Hand in hand notes collection

Time:2020-9-17

preface

With the continuous iteration of swoole, some corresponding coroutine frameworks have gradually come into people’s view, such as: hyperf, swoft, etc.; the implementation of resident memory makes the performance of PHP better than the framework of traditional php-fpm mode. According to the swoole open-source framework, it provides all-round development components. All partners who have seen or used the hyperf and swott frameworks should know that Some frameworks have flexible annotations similar to the spring cloud framework. In this paper, we use a simple demo to implement an annotation to facilitate us to understand the annotation more quickly.

What is annotation

Annotation is defined as metadata attached to data / code.

The framework can provide various additional functions for the code based on the meta information. In essence, annotation is to understand that annotation is just another way to display configuration.

How do annotations work

How are annotations recognized in the code, and how are they called? With questions, let’s look at the code https://github.com/LoyaltyLu/annotation_ Demo this is a demo I wrote. It contains annotations and a simple example of implementing the container for your reference. Before looking at the article, we suggest you take a look at these two documents first
Doctrine Annotations
PHP reflection

Code analysis

Annotation collection

Let’s take you through the demo to understand how annotations are collected.

Class annotation collection

index.php


<?php

$loader = require __DIR__ . "/vendor/autoload.php";

Core\Application::init($loader);

......

var_dump(\Core\Route::dispatch('/index/test'));

First, in the index.php Use composer to automatically load the input encapsulatedApplicationA processor class; callsinitMethod initialization.

Core\Application.php

<?php

namespace Core;

use Annotation\Parser\RequestMappingParser;

use Doctrine\Common\Annotations\AnnotationRegistry;

use \Doctrine\Common\Annotations;

/**

*Equivalent to a processor, do some initialization work

* Class Application

*

* @package Core

*/

class Application

{

    public static $beans = [];

    public static function init($loader)

    {

        AnnotationRegistry::registerLoader([$loader, 'loadClass']);

        self::loadAnnotationRoute();

        self::loadAnnotationBean();

    }

......

}

Here, we refer to the doctrine / annotations package. For more information, please refer to doctrine annotations

init()The method first automatically loads, and then calls the static method.self::loadAnnotationRoute();

Notice that theself::loadAnnotationBean();Method this is a way to simulate a container. Let’s first look at the implementation logic of annotations

Core\Application.php

......

public static function loadAnnotationRoute()

{

    //Automatically load annotation classes (rules) into components

    $reader = new Annotations\AnnotationReader();

    //Here we use manual instantiation class, and we can use glob () to traverse the file

    $obj = new \App\Http\Controller\HomeController();

    $re = new \ReflectionClass($obj);

    //Get class annotation

    $class_annos = $reader->getClassAnnotations($re);

    foreach ($class_annos as $class_anno) {

        $routePrefix = $class_anno->getPrefix();

        //Get all the methods by reflection

        $refMethods = $re->getMethods();

        foreach ($refMethods as $method) {

            $methodAnnos = $reader->getMethodAnnotations($method);

            foreach ($methodAnnos as $methodAnno) {

                $routePath = $methodAnno->getRoute();

                //Put some logic in a parsing class to process logic

                //$re - > newinstance(); reflection instantiation

                (new RequestMappingParser())->parse($routePrefix,$routePath, $re->newInstance(), $method->name);
            }
       }

}

......

How to customize rules for browsing documents

Rules are stored in:./Annotation/Mapping/In the catalogue;

AnnotationReaderMethod of getting class annotation in classgetClassAnnotations($re)Method needs a reflection class. For more reflection, please refer to PHP reflection;

Manually instantiate the class:$obj = new \App\Http\Controller\HomeController();

Then get the reflection class:$re = new \ReflectionClass($obj);

Because the demo is not too complicated, we can continue to improve here

callgetClassAnnotations($re)Method to get all annotations of the class

$class_annos = $reader->getClassAnnotations($re);

Printing$class_annosThe results are as follows:


array(1) {

[0]=>

    object(Annotation\Mapping\Controller)#15 (1) {

        ["prefix":"Annotation\Mapping\Controller":private]=>

        string(6) "/index"

    }

}

Where are the parameters obtained from this result? Next, take a look at the instantiatedHomeControllerAnd defined annotation rule classesAnnotation\Mapping\Controller;

Annotation\Mapping\Controller.php


<?php declare(strict_types=1);

namespace Annotation\Mapping;

use Doctrine\Common\Annotations\Annotation\Attribute;

use Doctrine\Common\Annotations\Annotation\Attributes;

use Doctrine\Common\Annotations\Annotation\Required;

use Doctrine\Common\Annotations\Annotation\Target;

/**

* Class Controller

* @Annotation

* @Target("CLASS")

* @Attributes({

* @Attribute("prefix", type="string"),

* })

* @since 2.0

*/

final class Controller

{

    private $prefix = '';

    public function __construct(array $values)

    {

        if (isset($values['value'])) {
            $this->prefix = $values['value'];
        }

        if (isset($values['prefix'])) {
            $this->prefix = $values['prefix'];
        }
    }

    public function getPrefix(): string

    {

        return $this->prefix;
    }

}

In order to save space, some useless code and comments are deleted here. You can check it in GitHub

The class annotation in the annotation class is where the rules are set:


/**
* Class Controller
* @Annotation
* @Target("CLASS")
* @Attributes({
* @Attribute("prefix", type="string"),
* })
* @since 2.0
*/

@Target indicates the kind of component whose annotation type is applicable. You can then define one or more goals:

  • Class allows you to use the
  • Property allows the
  • Method allows the
  • All allows docblocks for classes, properties, and methods
  • Annotation allows other annotations

For more information, please move to doctrine annotations

HomeController.php


<?php declare(strict_types=1);

namespace App\Http\Controller;

use Annotation\Mapping\Controller;

use Annotation\Mapping\RequestMapping;

/**
* Class HomeController
* @Controller(prefix="/index")
*/
class HomeController
{
......
}

According to the annotation class, we can get the properties set by the class rules in theHomeControllerSet in@Controller(prefix="/index"), declare what we want to get, so print it$class_annosWe can see that the annotations have been collected from the array we got


array(1) {
[0]=>
    object(Annotation\Mapping\Controller)#15 (1) {
        ["prefix":"Annotation\Mapping\Controller":private]=>
            string(6) "/index"
    }
}

The collection of this class annotation is completed. The method annotation acquisition logic is basically the same as the class annotation acquisition logic.

Method note collection:

After the collection of class annotations is completed, we can continue to use the reflection class to obtain methods from all classes. The specific operations are as follows:

$obj = new \App\Http\Controller\HomeController();

$re = new \ReflectionClass($obj);

//Get class annotation
$class_annos = $reader->getClassAnnotations($re);

foreach ($class_annos as $class_anno) {

    $routePrefix = $class_ Anno - > getprefix(); // get all annotations
    
    //Get all the methods by reflection
    $refMethods = $re->getMethods();
    
    foreach ($refMethods as $method) {
    
        $methodAnnos = $reader->getMethodAnnotations($method);
        foreach ($methodAnnos as $methodAnno) {
        
            $routePath = $methodAnno->getRoute();
            
            //Put some logic in a parsing class to process logic
            //$re - > newinstance(); reflection instantiation
            
            (new RequestMappingParser())->parse($routePrefix,$routePath, $re->newInstance(), $method->name)
        }
   }
}

By reflecting thegetMethodsMethod to get all the methods in the class,var_dump($refMethods)The data are as follows:


array(2) {

    [0]=>object(ReflectionMethod)#16 (2) {
            ["name"]=>string(5) "index"
            ["class"]=>string(34) "App\Http\Controller\HomeController"
        }
    [1]=>object(ReflectionMethod)#13 (2) {
        ["name"]=>string(2) "demo"
        ["class"]=>string(34) "App\Http\Controller\HomeController"
      }
}

Using annotation classgetMethodAnnotations($method);Methods the annotation rules can be obtained according to the set methods to read and retrieve the annotations set by each method


array(1) {
    [0]=>object(Annotation\Mapping\RequestMapping)#18 (1) {
    
        ["route":"Annotation\Mapping\RequestMapping":private]=>
                string(5)"/test"
    }
}

array(1) {

    [0]=>object(Annotation\Mapping\RequestMapping)#14 (1) {
         ["route":"Annotation\Mapping\RequestMapping":private]=>
            string(5)"/demo"
    }
}

Method annotation collection rule configuration:

/**
* HTTP action method annotation
* @Annotation
* @Target("METHOD")
*
* @since 2.0
*/
class RequestMapping
{

    ......

}

At this point, all the collection of annotations has been completed. The following is the business processing logic, how to distribute the routing, etc. you can first look at the source code in the demo, and then we will write a note call.

Conclusion

This is a little summary in my own learning process. If there are mistakes in this article, please help to point out and correct them in time. I hope it can help you. This is the first article in 2020. I believe that this is just the beginning. The Spring Festival is coming soon. I wish you a happy new year, good health and double your salary. Thank you~