日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Swoft 源码剖析 - Swoft 中的注解机制

發(fā)布時間:2025/4/16 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Swoft 源码剖析 - Swoft 中的注解机制 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

作者:bromine
鏈接:https://www.jianshu.com/p/ef7...
來源:簡書
著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進行了重新的排版。
Swoft Github: https://github.com/swoft-clou...

PHP中的注解

注解(Annotations)是Swoft里面很多重要功能特別是AOP,IoC容器的基礎(chǔ)。
注解的定義是:“附加在數(shù)據(jù)/代碼上的元數(shù)據(jù)(metadata)。”框架可以基于這些元信息為代碼提供各種額外功能。

以另一個框架PHPUnit為例,注解@dataProvider聲明一個方法作為測試用例方法的數(shù)據(jù)提供器。當(dāng)PHPUnit框架執(zhí)行到某一個測試用例方法時,會迭代該數(shù)據(jù)提供器,并將其返回的數(shù)據(jù)作為參數(shù)傳入測試用例方法,為測試用例方法提供一套用例所需的測試數(shù)據(jù)。

//摘自phpseclib庫的單元測試 public function formatLogDataProvider() {return array(array(//該參數(shù)會作為$message_log參數(shù)傳到testFormatLog()測試用例方法中array('hello world'), array('<--'), //$message_number_log "<--\r\n00000000 68:65:6c:6c:6f:20:77:6f:72:6c:64 hello world\r\n\r\n"//$expected),array(array('hello', 'world'),array('<--', '<--'),"<--\r\n00000000 68:65:6c:6c:6f hello\r\n\r\n" ."<--\r\n00000000 77:6f:72:6c:64 world\r\n\r\n"),); }/*** @dataProvider formatLogDataProvider*/ public function testFormatLog(array $message_log, array $message_number_log, $expected) {$ssh = $this->createSSHMock();$result = $ssh->_format_log($message_log, $message_number_log);$this->assertEquals($expected, $result); }

一般而言,在編程屆中注解是一種和注釋平行的概念。
注釋提供對可執(zhí)行代碼的說明,單純用于開發(fā)人員閱讀,不影響代碼的執(zhí)行;而注解往往充當(dāng)著對代碼的聲明和配置的作用,為可執(zhí)行代碼提供機器可用的額外信息,在特定的環(huán)境下會影響程序的執(zhí)行。

但是由于官方對PHP的Annotation方案遲遲沒有達成一致(最新進展可以在 PHP: rfc 看到),目前PHP沒有對注解的官方實現(xiàn)。主流的PHP框架中使用的注解都是借用T_DOC_COMMENT型注釋塊(/*型注釋/)中的@Tag,定義自己的注解機制。

想對PHP注解的發(fā)展史要有更多了解的朋友可以參考Rafael Dohms的這個PPT:https://www.slideshare.net/rd...

Doctrine注解引擎

Swoft沒有重新造輪子,搞一個新的的注解方案,而是選擇使用 Doctrine的注解引擎

Doctrine的注解方案也是基于T_DOC_COMMENT型注釋的,Doctrine使用反射獲取代碼的T_DOC_COMMENT型注釋,并將注釋中的特定類型@Tag映射到對應(yīng)注解類。為此,Swoft首先要為每一個框架自定義的注解定義注解類。

注解定義

@Breaker注解的注解類定義如下。

<?php //Swoft\Sg\Bean\Annotation\Breaker.php namespace Swoft\Sg\Bean\Annotation;/*** @Annotation //聲明這是一個注解類* @Target("CLASS")//聲明這個注解只可用在class級別的注釋中*/ class Breaker {/*** @var string //@var是PHPDoc標準的常用的tag,定義了屬性的類型\* Doctrine會根據(jù)該類型額外對注解參數(shù)進行檢查*/private $name = "";/*** 若注解類提供構(gòu)造器,Doctrine會調(diào)用,一般會在此處對注解類對象的private屬性進行賦值* Breaker constructor.** @param array $values //Doctrine注解使用處的參數(shù)數(shù)組,*/public function __construct(array $values){if (isset($values['value'])) {$this->name = $values['value'];}if (isset($values['name'])) {$this->name = $values['name'];}}//按需寫的getter setter code.... }

簡單幾行,一個@Breaker的注解類的定義工作就完成了。

注解類加載器的注冊

在框架的bootstap階段,swoft會掃描所有的PHP源碼文件獲取并解析注解信息。

使用Doctrine首先需要提供一個類的自動加載方法,這里直接使用了swoft當(dāng)前的類加載器。Swoft的類加載器由Composer自動生成,這意味著注解類只要符合PSR-4規(guī)范即可自動加載。

//Swoft\Bean\Resource\AnnotationResource.php /*** 注冊加載器和掃描PHP文件** @return array*/ protected function registerLoaderAndScanBean() {// code code....AnnotationRegistry::registerLoader(function ($class) {if (class_exists($class) || interface_exists($class)) {return true;}return false;});// code code....return array_unique($phpClass); }

使用Doctrine獲取注解對象

掃描各源碼目錄獲取PHP類后,Sworft會遍歷類列表加載類,獲取類級別,方法級別,屬性級別的所有注解對象。結(jié)果存放在AnnotationResource的$annotations成員中。

//Swoft\Bean\Resource\AnnotationResource.php /*** 解析bean注解** @param string $className* @return null*/ public function parseBeanAnnotations(string $className) {if (!class_exists($className) && !interface_exists($className)) {return null;}// 注解解析器$reader = new AnnotationReader();$reader = $this->addIgnoredNames($reader);//跳過Swoft內(nèi)部注解$reflectionClass = new \ReflectionClass($className);$classAnnotations = $reader->getClassAnnotations($reflectionClass);// 沒有類注解不解析其它注解if (empty($classAnnotations)) {return;}foreach ($classAnnotations as $classAnnotation) {$this->annotations[$className]['class'][get_class($classAnnotation)] = $classAnnotation;}// 解析屬性$properties = $reflectionClass->getProperties();foreach ($properties as $property) {if ($property->isStatic()) {continue;}$propertyName = $property->getName();$propertyAnnotations = $reader->getPropertyAnnotations($property);foreach ($propertyAnnotations as $propertyAnnotation) {$this->annotations[$className]['property'][$propertyName][get_class($propertyAnnotation)] = $propertyAnnotation;}}// 解析方法$publicMethods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC);foreach ($publicMethods as $method) {if ($method->isStatic()) {continue;}$methodName = $method->getName();// 解析方法注解$methodAnnotations = $reader->getMethodAnnotations($method);foreach ($methodAnnotations as $methodAnnotation) {$this->annotations[$className]['method'][$methodName][get_class($methodAnnotation)][] = $methodAnnotation;}} }

注解的解析

doctrine完成的功能僅僅是將注解映射到將用@Annotation聲明的注解類。swoft需要自行處理注解對象獲取注解中的信息。這一步有兩個重要功能:

  • 掃描搜集Bean的所有信息包括Bean名,類名以及該Bean各個需要注入的屬性信息等,存放到ObjectDefinition數(shù)組中。
//Swoft\Bean\Wrapper\AbstractWrapper.php /*** 封裝注解** @param string $className* @param array $annotations 注解3劍客,包含了類級別,方法級別,屬性級別的注解對象,注解解析流程你會一直看到他** @return array|null*/ public function doWrapper(string $className, array $annotations) {$reflectionClass = new \ReflectionClass($className);// 解析類級別的注解$beanDefinition = $this->parseClassAnnotations($className, $annotations['class']);//code...// parser bean annotationlist($beanName, $scope, $ref) = $beanDefinition;// 初始化Bean結(jié)構(gòu),并填充該Bean的相關(guān)信息$objectDefinition = new ObjectDefinition();$objectDefinition->setName($beanName);$objectDefinition->setClassName($className);$objectDefinition->setScope($scope);$objectDefinition->setRef($ref);if (!$reflectionClass->isInterface()) {// 解析屬性,并獲取屬性相關(guān)依賴注入的信息$properties = $reflectionClass->getProperties();$propertyAnnotations = $annotations['property']??[];$propertyInjections = $this->parseProperties($propertyAnnotations, $properties, $className);$objectDefinition->setPropertyInjections($propertyInjections);//PropertyInjection對象}// 解析方法$publicMethods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC);$methodAnnotations = $annotations['method'] ??[];$this->parseMethods($methodAnnotations, $className, $publicMethods);return [$beanName, $objectDefinition]; }
  • 在注解解析時Parser會調(diào)用相關(guān)的Collector搜集功能所需的信息,譬如進行事件注冊。

舉個例子,BootstrapParser的解析僅僅就是搜集注解。Collector在Swoft中是注解信息的最終裝載容器。一般而言@XXXX注解對應(yīng)的Parser和Collect就是XXXXParser和XXXXCollect,知道這個慣例會大大方便你對Swoft源碼的閱讀。

//Swoft\Bean\Parser\BootstrapParser.php class BootstrapParser extends AbstractParser {/*** @param string $className* @param Bootstrap $objectAnnotation* @param string $propertyName* @param string $methodName* @param mixed $propertyValue** @return array*/public function parser(string $className, $objectAnnotation = null, string $propertyName = "", string $methodName = "", $propertyValue = null){$beanName = $className;$scope = Scope::SINGLETON;BootstrapCollector::collect($className, $objectAnnotation, $propertyName, $methodName, $propertyValue);return [$beanName, $scope, ""];} }

由于框架執(zhí)行前必須完整的獲取各種注解到Collertor和生成Bean定義集合,所以Swoft是不進行l(wèi)azyload的。

注解的使用

現(xiàn)在我們終于可以用一個的例子來講解注解是如何運行。InitMbFunsEncoding是一個實現(xiàn)了Bootable的類,他的作用是在應(yīng)用啟動時候設(shè)定系統(tǒng)的編碼。但是僅僅實現(xiàn)了Bootable接口并不會讓框架在啟動時自動調(diào)用他。
因此我們需要InitMbFunsEncoding為添加一個@Bootstrap(order=1)類注解,讓他成為一個Bootstrap型的Bean。

//Swoft\Bootstrap\Boots.InitMbFunsEncoding.php <?phpnamespace Swoft\Bootstrap\Boots; use Swoft\Bean\Annotation\Bootstrap;/*** @Bootstrap(order=1)*/ class InitMbFunsEncoding implements Bootable {/*** bootstrap*/public function bootstrap(){mb_internal_encoding("UTF-8");} }

我們在上文已經(jīng)提過框架啟動時會掃描PHP源碼

  • 將Bean的定義信息存放到ObjectDefinition數(shù)組中
  • 將注解信息存放到各個Collector中

因此在框架的Bootstrap階段,可以從BootstrapCollector中直接獲取所有@Bootstrap型的Bean,實例化并Bean執(zhí)行。

<?php\\Swoft\Bootstrap\Bootstrap.php;//code .../*** bootstrap*/ public function bootstrap() {$bootstraps = BootstrapCollector::getCollector();//根據(jù)注解類型的不同,注解中的屬性會有不同的作用,譬如@Bootstrap的order就影響各個Bean的執(zhí)行順序。array_multisort(array_column($bootstraps, 'order'), SORT_ASC, $bootstraps);foreach ($bootstraps as $bootstrapBeanName => $name){//使用Bean的ObjectDefinition信息構(gòu)造實例或獲取現(xiàn)有實例/* @var Bootable $bootstrap*/$bootstrap = App::getBean($bootstrapBeanName);$bootstrap->bootstrap();} }//code ...

以上就是Swoft注解機制的整體實現(xiàn)了。

Swoft源碼剖析系列目錄:https://segmentfault.com/a/11...

總結(jié)

以上是生活随笔為你收集整理的Swoft 源码剖析 - Swoft 中的注解机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。