手把手走入註解之註解收集

前言

隨着Swoole的不斷的迭代,相應一些Swoole的協程框架也逐漸進入了你們的視野,好比:HyperfSwoft等;常駐內存的實現讓PHP性能比傳統PHP-FPM模式的框架有質的提高,依據Swoole開源的框架都提供了全面的開發組件,看過或使用過Hyperf、Swoft框架的小夥伴應該都知道,這些框架當中有相似SpringCloud框架靈活的註解,本文就以一個簡單的demo實現一個註解的實現,方便你們更快速的瞭解註解。php

什麼是註解

註解的定義是:附加在數據/代碼上的元數據(metadata)。html

框架能夠基於這些元信息爲代碼提供各類額外功能,本質上註解就是理解註解只是配置的另外一種展示方式。git

註解如何工做的

註解是如何在代碼裏面被識別,又是如何被調用的呢?帶着疑問我們一塊兒來看代碼,GitHubhttps://github.com/LoyaltyLu/annotation_demo這是我寫的一個demo,裏面包含了註解以及實現容器的一個簡單的示例方便你們參看,再看文章前建議你們先看下這兩個文檔:
Doctrine Annotations
PHP反射github

代碼解析

註解收集

帶你們經過demo簡單瞭解下註解是如何被收集的。數組

類註解收集

index.phpapp

<?php

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

Core\Application::init($loader);

......

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

首先在index.php中使用composer 自動加載傳入封裝好的Application一個處理器類;調用init方法初始化。composer

Core\Application.php框架

<?php

namespace Core;

use Annotation\Parser\RequestMappingParser;

use Doctrine\Common\Annotations\AnnotationRegistry;

use \Doctrine\Common\Annotations;

/**

* 至關於一個處理器,作一些初始化工做

* Class Application

*

* @package Core

*/

class Application

{

    public static $beans = [];

    public static function init($loader)

    {

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

        self::loadAnnotationRoute();

        self::loadAnnotationBean();

    }

......

}

這裏引用了doctrine/annotations包,更多參考Doctrine Annotations性能

init()方法首先自動加載,而後調用靜態方法self::loadAnnotationRoute();學習

注意這裏同時調用了 self::loadAnnotationBean();方法這是模擬容器的一個方法我們先看註解的實現邏輯

Core\Application.php

......

public static function loadAnnotationRoute()

{

    //自動加載註解類(規則)到組件當中

    $reader = new Annotations\AnnotationReader();

    //這裏採用手動實例化類、能夠利用 glob() 遍歷文件

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

    $re = new \ReflectionClass($obj);

    //獲取類註解

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

    foreach ($class_annos as $class_anno) {

        $routePrefix = $class_anno->getPrefix();

        //經過反射獲得全部的方法

        $refMethods = $re->getMethods();

        foreach ($refMethods as $method) {

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

            foreach ($methodAnnos as $methodAnno) {

                $routePath = $methodAnno->getRoute();

                //把某個邏輯放到在某個解析類當中處理邏輯

                //$re->newInstance();反射實例化

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

}

......

如何定製規則可翻閱文檔

規則存放在:./Annotation/Mapping/目錄中;

AnnotationReader類中的獲取類註解的方法getClassAnnotations($re)方法須要一個反射類,更多反射請參考PHP反射

手動實例化類:$obj = new \App\Http\Controller\HomeController();

而後獲取反射類:$re = new \ReflectionClass($obj);

由於是demo就沒有作過於複雜,這裏你們能夠繼續完善

調用getClassAnnotations($re)方法獲取類全部註解

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

打印$class_annos的結果以下:

array(1) {

[0]=>

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

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

        string(6) "/index"

    }

}

這個結果是獲取到的哪裏的參數呢?接下來分別看下實例化的HomeController和定義的註解規則類Annotation\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;
    }

}
爲了節省篇幅這裏刪除了一部分沒用的代碼和註釋,你們能夠去 GitHub中查看

註解類中的類註解是設置規則的地方:

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

@Target指示種類元件,其註釋類型是適用的。而後,你能夠定義一個或多個目標:

  • CLASS 容許在類的docblock
  • PROPERTY 容許在屬性的docblock
  • METHOD 容許在該方法的docblock
  • ALL 容許類,屬性和方法的docblock
  • ANNOTATION 容許其餘註釋裏面

更多介紹還請你們移步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
{
......
}

根據註解類獲取類規則設置的屬性,咱們能夠在HomeController中設置@Controller(prefix="/index"),聲明咱們要被獲取的內容,因此在打印$class_annos時咱們獲取到的數組中能夠看獲得註解已經被收集到:

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

到此類註解的收集工做就完成了,方法的註解獲取邏輯與類註解獲取邏輯基本相同。

方法註解收集:

當類註解收集完成以後,咱們能夠繼續利用反射類獲取全部類當中的方法,具體操做以下:

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

$re = new \ReflectionClass($obj);

//獲取類註解
$class_annos = $reader->getClassAnnotations($re);

foreach ($class_annos as $class_anno) {

    $routePrefix = $class_anno->getPrefix();//獲取全部註解
    
    //經過反射獲得全部的方法
    $refMethods = $re->getMethods();
    
    foreach ($refMethods as $method) {
    
        $methodAnnos = $reader->getMethodAnnotations($method);
        foreach ($methodAnnos as $methodAnno) {
        
            $routePath = $methodAnno->getRoute();
            
            //把某個邏輯放到在某個解析類當中處理邏輯
            //$re->newInstance();反射實例化
            
            (new RequestMappingParser())->parse($routePrefix,$routePath, $re->newInstance(), $method->name)
        }
   }
}

經過反射類中的getMethods方法獲取類中全部方法,var_dump($refMethods)數據以下:

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"
      }
}

利用註解類的getMethodAnnotations($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"
    }
}

方法註解收集規則配置:

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

    ......

}

至此註解的的全部收集工做所有完成,後面的話就是業務處理邏輯,如何分發路由等等,你們能夠先看下demo中的源碼,後面會在跟你們寫一個關於註解調用。

結束語

這是在本身學習過程當中的一點點總結,文中有錯誤的地方請你們加幫忙指出,及時改正,但願能幫助到你們,這是2020年的第一篇文章相信這只是一個開始,春節立刻來臨,在這裏提早預祝各位新年快樂,身體健康,薪資翻倍~謝謝~

相關文章
相關標籤/搜索