前言
本人認為學習一個框架正確的方式是先看框架的文件,再進入實戰動手寫程式碼寫一些demo,然後就是看框架的原始碼,深入瞭解框架是怎麼啟動執行的,整個框架的流程最好用debug跑一遍,看一下有哪些可以值得學習和擴充套件的地方,比如框架原始碼中用到的一些設計模式,思考為什麼用這個設計模式;比如框架原始碼中有一些可擴充套件的方法,是文件中沒有提及到的;比如想要寫框架的擴充套件庫,必須要深入瞭解框架原始碼和執行流程。
下面主要是對swoft這個框架註解器元件方面做一個流程剖析。
swoft註解
註解(Annotations) 是Swoft裡面很多重要功能的基礎。特別是AOP,IoC容器、事件監聽的基礎。
註解的定義是: “附加在資料/程式碼上的後設資料(metadata)”
swoft框架可以基於這些後設資料為程式碼提供各種額外功能。
註解 VS 註釋
一般而言,在程式設計屆中註解是一種和註釋平行的概念。
- 註釋提供對可執行程式碼的說明,單純用於開發人員閱讀,不影響程式碼的執行;
- 而註解往往充當著對程式碼的宣告和配置的作用,為可執行程式碼提供機器可用的額外資訊,在特定的環境下會影響程式的執行;
php註解與swoft註解
目前PHP沒有對註解的官方實現,主流的PHP框架中使用的註解都是借用T_DOC_COMMENT型註釋塊(/**型註釋*/)
中的@Tag,定義自己的註解機制。
Swoft沒有重新造輪子,搞一個新的的註解方案,而是選擇使用Doctrine的註解引擎
Doctrine的註解方案也是基於T_DOC_COMMENT型註釋的,Doctrine使用反射獲取程式碼的T_DOC_COMMENT型註釋,並將註釋中的特定型別@Tag對映到對應註解類
如果使用
在過原始碼流程之前,先說一下作為swoft核心之一的註解器是怎麼使用的,就像我們日常開發寫註解一樣,只需在類、方法或成員變數上方按規則新增註解即可,如定義一個監聽器:
namespace SwoftTest\Event;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
/**
* Class SendMessageListener
*
* @Listener("order.created")
*/
class SendMessageListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*/
public function handle(EventInterface $event): void
{
$pos = __METHOD__;
echo "handle the event '{$event->getName()}' on the: $pos\n";
}
}
和laravel必須要寫事件監聽類繫結不一樣,swoft只需要在類註解寫上@Listener這個註解就能將這個類定義為一個監聽器,並繫結到具體的事件。
order.created是這個監聽類繫結的具體事件,任何地方呼叫Swoft::trigger("order.created")
建立訂單事件能觸發到這個傳送訊息的監聽器,執行handle()方法。
實現註解
那麼怎麼使得這些註解附有意義呢?這時候要引出強大的ReflectionClass反射類,通過反射類的getDocComment()方法能獲取某個類的類註釋,方法和屬性的註釋。
/**
* Gets doc comment
* @link https://php.net/manual/en/reflectionproperty.getdoccomment.php
* @return string|bool The doc comment if it exists, otherwise <b>FALSE</b>
* @since 5.1.0
*/
public function getDocComment () {}
不妨設想一下,程式在開始執行的時候(php bin/swoft http start)應該是把全部檔案都掃描一遍,通過反射獲取類註解,如果類註解為Swoft\Event\Annotation\Mapping\Listener
就把該類(SwoftTest\Event\SendMessageListener
)和對應的事件名(“order.created”)繫結到事件管理器,然後觸發事件就能找到對應的監聽器。其實就是1、先識別註解 ==> 再到2、讓註解起作用兩個步驟。
下面來看一下程式啟動的執行流程,可以一邊看程式碼一邊看文章,看一下註解是怎麼起作用的。
先看命令啟動的入口檔案 bin/swoft
// Bootstrap
require_once __DIR__ . '/bootstrap.php';
Swoole\Coroutine::set([
'max_coroutine' => 300000,
]);
// 啟動App
(new \App\Application())->run();
主要就是程式的入口,例項化Application,並執行run()方法,我們知道Application是繼承與SwoftApplication,Application沒有構造方法,自然就會呼叫父類SwoftApplication的構造方法,父類的構造方法如下:
/**
* Class constructor.
*
* @param array $config
*/
public function __construct(array $config = [])
{
......
// Init application
$this->init();
......
}
前半部分主要是程式初始化的一些校驗,日誌器的初始化,省略掉,主要是$this->init()
初始化方法
protected function init(): void
{
......
$processors = $this->processors();
$this->processor = new ApplicationProcessor($this);
$this->processor->addFirstProcessor(...$processors);
}
......
/**
* swoft 六大處理器
* @return ProcessorInterface[]
*/
protected function processors(): array
{
return [
new EnvProcessor($this), // 處理環境變數
new ConfigProcessor($this), // 處理配置
new AnnotationProcessor($this), // 處理註解(本文章重點)
new BeanProcessor($this), // 處理容器物件
new EventProcessor($this), // 處理事件
new ConsoleProcessor($this), // 處理命令
];
}
初始化裡面主要是把swoft六大處理器新增到應用程式的主處理器ApplicationProcessor中,本文章主要解讀的是AnnotationProcessor註解處理器,通過這個處理器實現對類註解的解析。新增處理器後,Application初始化結束。下面再來呼叫的run()方法。
/**
* Run application
*/
public function run(): void
{
try {
if (!$this->beforeRun()) {
return;
}
$this->processor->handle();
} catch (Throwable $e) {
......
}
}
run()方法主要執行了應用程式的主處理器ApplicationProcessor的handle()方法,handle()方法把前面新增的六大處理器的handle()方法都執行了一遍(程式碼省略),下面主要看AnnotationProcessor註解處理器中的handle()方法。
/**
* Handle annotation
*
* @return bool
* @throws Exception
*/
public function handle(): bool
{
......
$app = $this->application;
AnnotationRegister::load([
'inPhar' => IN_PHAR,
'basePath' => $app->getBasePath(),
'notifyHandler' => [$this, 'notifyHandle'],
'disabledAutoLoaders' => $app->getDisabledAutoLoaders(),
'disabledPsr4Prefixes' => $app->getDisabledPsr4Prefixes(),
]);
......
}
註解處理器handle()方法中呼叫了load方法,AnnotationRegister::load()
方法呼叫了AnnotationResource例項化物件的load()方法,AnnotationResource類用途就是收集整合註解資源的,load方法的主要作用是:
- 查詢目錄中的AutoLoader.php檔案,如果沒有的話就不解析(所以swoft擴充套件庫的src目錄必須有AutoLoader.php檔案,否則註解器等功能不能生效)。
- 解析每個目錄下每個檔案並收集帶有解析的類或屬性方法。
/**
* 遍歷查詢目錄中的AutoLoader.php檔案,如果沒有就不解析,如果有就根據這個檔案指定的目錄檔案解析
*
* @throws AnnotationException
* @throws ReflectionException
*/
public function load(): void
{
// 獲取composer裡面autoload的psr-4對映目錄,包括了app目錄和vendor第三方庫的目錄
$prefixDirsPsr4 = $this->classLoader->getPrefixesPsr4();
foreach ($prefixDirsPsr4 as $ns => $paths) {
......
// 迴圈每個目錄,查詢Autoloader.php檔案
foreach ($paths as $path) {
$loaderFile = $this->getAnnotationClassLoaderFile($path);
.......
$loaderClass = $this->getAnnotationLoaderClassName($ns);
$isEnabled = true;
$autoLoader = new $loaderClass();
.......
// 如果Autoloader類沒有允許載入,則不能載入註解,通過isEnable()控制
if (isset($this->disabledAutoLoaders[$loaderClass]) || !$autoLoader->isEnable()) {
$isEnabled = false;
$this->notify('disabledLoader', $loaderFile);
} else {
AnnotationRegister::addAutoLoaderFile($loaderFile);
$this->notify('addLoaderClass', $loaderClass);
// 載入並收集註解類(核心)
$this->loadAnnotation($autoLoader);
}
// Storage autoLoader instance to register
AnnotationRegister::addAutoLoader($ns, $autoLoader, $isEnabled);
}
}
}
load()方法已經完成了Autoloader.php檔案的發現,這就找到了允許被swoft解析註解的目錄,有了目錄,接下來就是遍歷目錄中的每個檔案,收集並解析註解,這一核心流程交給了$this->loadAnnotation($autoLoader)
方法去實現。
/**
* 迴圈解析目錄下每個檔案的註解
*
* @param LoaderInterface $loader
*
* @throws AnnotationException
* @throws ReflectionException
*/
private function loadAnnotation(LoaderInterface $loader): void
{
// 獲取Autoloader類中設定的目錄
$nsPaths = $loader->getPrefixDirs();
foreach ($nsPaths as $ns => $path) {
// 迭代生成目錄下檔案迭代器,然後遍歷每個檔案
$iterator = DirectoryHelper::recursiveIterator($path);
foreach ($iterator as $splFileInfo) {
......
$suffix = sprintf('.%s', $this->loaderClassSuffix);
$pathName = str_replace([$path, '/', $suffix], ['', '\\', ''], $filePath);
$className = sprintf('%s%s', $ns, $pathName);
// 解析某個類,檢視某個類有沒有類註解,方法註解,屬性註解等。
$this->parseAnnotation($ns, $className);
}
}
}
/**
* 解析某個類的註解
*
* @param string $namespace
* @param string $className
*
* @throws AnnotationException
* @throws ReflectionException
*/
private function parseAnnotation(string $namespace, string $className): void
{
// **核心**:例項化某類的ReflectionClass類,比如說上面的SwoftTest\Event\SendMessageListener類
$reflectionClass = new ReflectionClass($className);
// Fix ignore abstract
if ($reflectionClass->isAbstract()) {
return;
}
// 根據反射類ReflectionClass解析某個類並查詢某個類註解,返回了某個類整合完的註解(陣列形式)
$oneClassAnnotation = $this->parseOneClassAnnotation($reflectionClass);
// 如果某個類整合完的註解不為空,就註冊到AnnotationRegister類中
if (!empty($oneClassAnnotation)) {
AnnotationRegister::registerAnnotation($namespace, $className, $oneClassAnnotation);
}
}
核心流程:例項化了某類的ReflectionClass類,比如說上面的SwoftTest\Event\SendMessageListener
類,然後把反射類傳遞給parseOneClassAnnotation()方法去處理。
/**
* 解析某個類的註解
*
* @param ReflectionClass $reflectionClass
*
* @return array
* @throws AnnotationException
* @throws ReflectionException
*/
private function parseOneClassAnnotation(ReflectionClass $reflectionClass): array
{
// Annotation reader 註解閱讀器
$reader = new AnnotationReader();
$className = $reflectionClass->getName();
$oneClassAnnotation = [];
// $reader獲取類註解
$classAnnotations = $reader->getClassAnnotations($reflectionClass);
// Register annotation parser 註冊註解的解析器,這裡的解析器不是getDocComment(),而是把上面例子中監聽器和時間繫結的解析器。通常在src\Annotation\Parser目錄中。
foreach ($classAnnotations as $classAnnotation) {
if ($classAnnotation instanceof AnnotationParser) {
// * 如果是AnnotationParser解析類,則把該類註冊到AnnotationRegister類的$parsers(解析器)屬性中,這個解析類後文作用重大,用來讓註解真正起作用的,一個註解類對應一個解析類
$this->registerParser($className, $classAnnotation);
return [];
}
}
// Class annotation
if (!empty($classAnnotations)) {
$oneClassAnnotation['annotation'] = $classAnnotations;
$oneClassAnnotation['reflection'] = $reflectionClass;
}
// 獲取類屬性 => 遍歷類屬性 => 獲取屬性註解
$reflectionProperties = $reflectionClass->getProperties();
foreach ($reflectionProperties as $reflectionProperty) {
$propertyName = $reflectionProperty->getName();
// $reader獲取屬性註解
$propertyAnnotations = $reader->getPropertyAnnotations($reflectionProperty);
if (!empty($propertyAnnotations)) {
$oneClassAnnotation['properties'][$propertyName]['annotation'] = $propertyAnnotations;
$oneClassAnnotation['properties'][$propertyName]['reflection'] = $reflectionProperty;
}
}
// 獲取類方法 => 遍歷類方法 => 獲取方法註解
$reflectionMethods = $reflectionClass->getMethods();
foreach ($reflectionMethods as $reflectionMethod) {
$methodName = $reflectionMethod->getName();
// $reader獲取方法註解
$methodAnnotations = $reader->getMethodAnnotations($reflectionMethod);
if (!empty($methodAnnotations)) {
$oneClassAnnotation['methods'][$methodName]['annotation'] = $methodAnnotations;
$oneClassAnnotation['methods'][$methodName]['reflection'] = $reflectionMethod;
}
}
$parentReflectionClass = $reflectionClass->getParentClass();
if ($parentReflectionClass !== false) {
$parentClassAnnotation = $this->parseOneClassAnnotation($parentReflectionClass);
if (!empty($parentClassAnnotation)) {
$oneClassAnnotation['parent'] = $parentClassAnnotation;
}
}
return $oneClassAnnotation;
}
AnnotationParser解析類,與註解類一一對應,有Swoft\Event\Annotation\Mapping\Listener
註解類就有對應的Swoft\Event\Annotation\Parser\ListenerParser
解析類,一般都存放在app或者庫src目錄下的Annotation目錄中。
$reader = new AnnotationReader()
註解閱讀器是引用了Doctrine註解引擎這個包裡面的類。
parseOneClassAnnotation()方法解析某個類的註解,並根據類、屬性、方法整合到了$oneClassAnnotation陣列中,並返回。AnnotationReader類能解析(類、屬性、方法)註解,下面看看這個類是怎麼通過getClassAnnotations()方法獲取註解的。
/**
* {@inheritDoc}
*/
public function getClassAnnotations(ReflectionClass $class)
{
$this->parser->setTarget(Target::TARGET_CLASS);
$this->parser->setImports($this->getClassImports($class));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
}
使用ReflectionClass的getDocComment()方法獲取類註釋,再傳遞給$this->parser,$this->parser->parse()
方法根據@關鍵字把註解格式化為陣列形式,陣列裡面是具體的註解類例項化物件例如:
/**
* Class SendMessageListener
*
* @since 2.0
*
* @Listener(DbEvent::MODEL_SAVED)
*/
class SendMessageListener{
......
}
經過parser()處理後變成如下形式,由於只有一個註解@Listener,所以陣列只有一個元素。
array(1) {
[0]=>
object(Swoft\Event\Annotation\Mapping\Listener)#1479 (2) {
["event":"Swoft\Event\Annotation\Mapping\Listener":private]=>
string(17) "swoft.model.saved"
["priority":"Swoft\Event\Annotation\Mapping\Listener":private]=>
int(0)
}
}
同樣的方式獲取屬性的註解,方法的註解,並組裝成$oneClassAnnotation陣列,然後通過AnnotationRegister::registerAnnotation()
方法,將類名(例中的SwoftTest\Event\SendMessageListener
)、名稱空間、和生成的註解陣列$oneClassAnnotation註冊到然後通過AnnotationRegister類的$annotations屬性中。
整個目錄檔案掃描的流程完成後,最終會把所有的註解(格式化後 => 註解類例項化物件)新增在AnnotationRegister類的$annotations屬性中。
/**
* Class AnnotationRegister
*
* @since 2.0.0
*/
final class AnnotationRegister
{
/**
* @var array
*
* @example
* [
* // 名稱空間 SwoftTest\Event
* 'loadNamespace' => [
* // 類名,例子中的SwoftTest\Event\SendMessageListener
* 'className' => [
* // 類註解
* 'annotation' => [
* // 例子中的Swoft\Event\Annotation\Mapping\Listener註解類物件
* new ClassAnnotation(),
* new ClassAnnotation(),
* new ClassAnnotation(),
* ]
* 'reflection' => new ReflectionClass(),
* // 屬性註解
* 'properties' => [
* 'propertyName' => [
* 'annotation' => [
* new PropertyAnnotation(),
* new PropertyAnnotation(),
* new PropertyAnnotation(),
* ]
* 'reflection' => new ReflectionProperty(),
* ]
* ],
* // 方法註解
* 'methods' => [
* 'methodName' => [
* 'annotation' => [
* new MethodAnnotation(),
* new MethodAnnotation(),
* new MethodAnnotation(),
* ]
* 'reflection' => new ReflectionFunctionAbstract(),
* ]
* ]
* ]
* ]
* ]
*/
private static $annotations = [];
}
一般來說,每個不同的註解類都會有不同的屬性,比如Swoft\Event\Annotation\Mapping\Listener
註解類就儲存了事件名和優先順序屬性,而Swoft\Bean\Annotation\Mapping\Bean
這個註解類就儲存了名稱、作用域、別名等屬性,這些屬性時在解析類中獲取的,解析類的作用下文說。
至此,AnnotationProcessor任務完成。但是有個疑問的是,上面的流程只是把註解格式化出來(步驟1識別註解),具體怎麼樣讓註解起作用好像是還沒有涉及到,那得繼續看下一個處理器BeanProcessor的handle()方法了。
先說一下BeanProcessor處理器的功能,這個處理器的功能如下:
- Bean 就是一個類的一個物件例項。 容器Container就是一個巨大的工廠,用於存放和管理 Bean 生命週期。所以這個處理器是用來生成Bean並放入容器中的。
- 把app目錄下的bean.php檔案的陣列和AutoLoader的beans()方法返回的陣列合併再例項化後放入容器Container中。
- 把用了
@Bean註解
的類例項化後放入Container中,所以必須要讓註解起作用後才能進行Bean的例項化。
/**
* Class BeanProcessor
*
* @since 2.0
*/
class BeanProcessor extends Processor
{
/**
* Handle bean
*
* @return bool
* @throws ReflectionException
* @throws AnnotationException
*/
public function handle(): bool
{
......
$handler = new BeanHandler();
// 獲取bean.php檔案的陣列和AutoLoader的beans()方法返回的陣列
$definitions = $this->getDefinitions();
// 獲取$parsers(解析類)
$parsers = AnnotationRegister::getParsers();
// 獲取$annotations(所有格式化後註解的結果,註解類)
$annotations = AnnotationRegister::getAnnotations();
// 把上面獲取到的都新增到BeanFactory的屬性中
BeanFactory::addDefinitions($definitions);
BeanFactory::addAnnotations($annotations);
BeanFactory::addParsers($parsers);
BeanFactory::setHandler($handler);
// Bean工廠初始化
BeanFactory::init();
......
}
......
}
/**
* Class BeanFactory
*
* @since 2.0
*/
class BeanFactory
{
/**
* Init bean container
*
* @return void
* @throws AnnotationException
* @throws ReflectionException
*/
public static function init(): void
{
// 呼叫容器的初始化方法
Container::getInstance()->init();
}
......
}
/**
* Class Container
*/
class Container implements ContainerInterface
{
/**
* Init
*
* @throws AnnotationException
* @throws ReflectionException
*/
public function init(): void
{
// 解析註解
$this->parseAnnotations();
// 解析Bean物件
$this->parseDefinitions();
// 例項化Bean物件
$this->initializeBeans();
}
}
我們重點看$this->parseAnnotations()
這個方法,這個方法是解析註解類的,讓註解起作用的(步驟2),下面的是解析和例項化Bean,和註解類怎麼起作用的無關,就不理它了。至於為什麼把解析註解類的流程放在BeanProcessor處理器上而不放在AnnotationProcessor處理器上,可能是解析類處理完要返回一個結果提供給Bean,為什麼這麼做,猜想是要相容很多解析類有關吧,具體我也不太明白,繼續看程式碼。
/**
* Class Container
*/
class Container implements ContainerInterface
{
/**
* Parse annotations
*
* @throws AnnotationException
*/
private function parseAnnotations(): void
{
$annotationParser = new AnnotationObjParser(
$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases
);
// 例項化AnnotationObjParser物件,用這個物件去解析註解類
$annotationData = $annotationParser->parseAnnotations($this->annotations, $this->parsers);
[$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases] = $annotationData;
}
}
/**
* Class AnnotationParser
*
* @since 2.0
*/
class AnnotationObjParser extends ObjectParser
{
/**
* Parse annotations
*
* @param array $annotations
* @param array $parsers
*
* @return array
* @throws AnnotationException
*/
public function parseAnnotations(array $annotations, array $parsers): array
{
$this->parsers = $parsers;
$this->annotations = $annotations;
foreach ($this->annotations as $loadNameSpace => $classes) {
foreach ($classes as $className => $classOneAnnotations) {
$this->parseOneClassAnnotations($className, $classOneAnnotations);
}
}
return [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases];
}
}
AnnotationObjParser類的parseAnnotations方法迴圈了所有的註解$annotations,呼叫了parseOneClassannotations(),並且把類名$className(SwoftTest\Event\SendMessageListener
)和這個類的所有註解類$classOneAnnotations (包括類註解,方法註解,屬性註解)作為引數傳遞給了這個方法。
/**
* 解析某類的所有註解
*
* @param string $className
* @param array $classOneAnnotations
*
* @throws AnnotationException
*/
private function parseOneClassAnnotations(string $className, array $classOneAnnotations): void
{
......
// Parse class annotations
$classAnnotations = $classOneAnnotations['annotation'];
$reflectionClass = $classOneAnnotations['reflection'];
$classAry = [
$className,
$reflectionClass,
$classAnnotations
];
// 解析類註解
$objectDefinition = $this->parseClassAnnotations($classAry);
// 解析屬性註解
$propertyInjects = [];
$propertyAllAnnotations = $classOneAnnotations['properties'] ?? [];
foreach ($propertyAllAnnotations as $propertyName => $propertyOneAnnotations) {
$proAnnotations = $propertyOneAnnotations['annotation'] ?? [];
$propertyInject = $this->parsePropertyAnnotations($classAry, $propertyName, $proAnnotations);
if ($propertyInject) {
$propertyInjects[$propertyName] = $propertyInject;
}
}
// 解析方法註解
$methodInjects = [];
$methodAllAnnotations = $classOneAnnotations['methods'] ?? [];
foreach ($methodAllAnnotations as $methodName => $methodOneAnnotations) {
$methodAnnotations = $methodOneAnnotations['annotation'] ?? [];
$methodInject = $this->parseMethodAnnotations($classAry, $methodName, $methodAnnotations);
if ($methodInject) {
$methodInjects[$methodName] = $methodInject;
}
}
......
}
我們只看怎麼解析類註解$this->parseClassAnnotations($classAry)
。
/**
* @param array $classAry
*
* @return ObjectDefinition|null
*/
private function parseClassAnnotations(array $classAry): ?ObjectDefinition
{
[, , $classAnnotations] = $classAry;
$objectDefinition = null;
foreach ($classAnnotations as $annotation) {
$annotationClass = get_class($annotation);
if (!isset($this->parsers[$annotationClass])) {
continue;
}
// 去解析類陣列裡面根據註解類名找到對應的解析類名(前面說了註解類和解析類一一對應的)
$parserClassName = $this->parsers[$annotationClass];
// 根據解析類名獲取示例化後的解析類(例子中是Swoft\Event\Annotation\Parser\ListenerParser)
$annotationParser = $this->getAnnotationParser($classAry, $parserClassName);
// 呼叫解析類的parse()
$data = $annotationParser->parse(Parser::TYPE_CLASS, $annotation);
......
}
return $objectDefinition;
}
呼叫解析類的parse()方法,例子中就是呼叫Swoft\Event\Annotation\Parser\ListenerParser
這個解析類的parse()方法,這個方法就是實現了步驟2讓註解起作用,具體看一下parse()方法。
class ListenerParser extends Parser
{
/**
* @param int $type
* @param Listener $annotation
*
* @return array
* @throws AnnotationException (註解類,例子中的Swoft\Event\Annotation\Mapping\Listener)
*/
public function parse(int $type, $annotation): array
{
if ($type !== self::TYPE_CLASS) {
throw new AnnotationException('`@Listener` must be defined on class!');
}
// 註冊監聽器,key為為SwoftTest\Event\SendMessageListener,值為["order.created" => 0]
ListenerRegister::addListener($this->className, [
// event name => listener priority
$annotation->getEvent() => $annotation->getPriority()
]);
return [$this->className, $this->className, Bean::SINGLETON, ''];
}
}
ListenerParser類的parse()方法把註解類(Swoft\Event\Annotation\Mapping\Listener
)例項化物件中儲存的事件名和優先順序註冊到了ListenerRegister類的$listeners屬性中,從而使得SwoftTest\Event\SendMessageListener和”order.created”繫結了關係,後續程式中觸發了order.created事件就能找到對應的監聽器了。
通過識別註解 ==> 到解析註解這麼一個流程,類註解成功繫結了事件和監聽器了。
自定義註解
swoft框架的註解流程講解完了,如果要自定義一個註解怎麼做應該也清晰多了
主要是編寫註解類和解析類:
註解類寫在App\Annotation\Mapping目錄,比如編寫要編寫一個門面Facades的註解類App\Annotation\Mapping\FacadesAnnotation
類,讓類變成門面類(不瞭解門面的可以看一下laravel門面的文件)。
namespace App\Annotation\Mapping;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
/**
* Class Facades
*
* @since 2.0
*
* @Annotation //宣告這是一個註解類
* @Target("CLASS") //宣告這個註解只可用在class級別的註釋中
* @Attributes({
* @Attribute("alias",type="string")
* })
*/
class Facades
{
/**
* @var string
*/
private $alias = '';
/**
* StringType constructor.
*
* @param array $values
*/
public function __construct(array $values)
{
if (isset($values['value'])) {
$this->message = $values['value'];
}
if (isset($values['alias'])) {
$this->alias = $values['alias'];
}
}
/**
* @return string
*/
public function getAlias(): string
{
return $this->alias;
}
}
在程式任何有Autoload.php檔案的目錄下的類都能在類註解上按格式寫上自定義的註解類(記得要use 註解類,phpstorm有註解的外掛可以直接引入)比如:
use App\Annotation\Mapping\Facades
/**
* Class Calculate
*
* @Facades()
*/
class Calculate{
/**
* 執行
*
* @return mixed
* @throws Exception
*/
public function execute()
{
......
}
}
解析類寫在App\Annotation\Parser目錄下,編寫App\Annotation\Parser\FacadesParser
類:
<?php declare(strict_types=1);
/**
* This file is part of Swoft.
*
* @link https://swoft.org
* @document https://swoft.org/docs
* @contact group@swoft.org
* @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
*/
namespace App\Annotation\Parser;
use ReflectionException;
use Swoft\Annotation\Annotation\Mapping\AnnotationParser;
use Swoft\Annotation\Annotation\Parser\Parser;
use App\Annotation\Mapping\Facades;
use Swoft\Validator\Exception\ValidatorException;
use Swoft\Validator\ValidatorRegister;
/**
* Class FacadesParser
*
* @AnnotationParser(annotation=Facades::class) // 引數值寫上註解類
*/
class FacadesParser extends Parser
{
/**
* @param int $type
* @param object $annotationObject
*
* @return array
* @throws ReflectionException
* @throws ValidatorException
*/
public function parse(int $type, $annotationObject): array
{
// 可以獲取到目標類Calculate,用$this->className獲取
// 可以獲取到註解類物件$annotationObject
// 這裡是把目標類Calculate怎麼變成門面的流程,我也沒有實現,有興趣的可以自己寫一個
return [];
}
}
以上就是Swoft註解器的原始碼流程剖析。
本作品採用《CC 協議》,轉載必須註明作者和本文連結