惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
MyScale Blog
MyScale Blog
Jina AI
Jina AI
爱范儿
爱范儿
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
I
Intezer
The Cloudflare Blog
T
Threat Research - Cisco Blogs
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Docker
AI
AI
Scott Helme
Scott Helme
Attack and Defense Labs
Attack and Defense Labs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
L
LangChain Blog
Recent Announcements
Recent Announcements
Security Latest
Security Latest
Hugging Face - Blog
Hugging Face - Blog
W
WeLiveSecurity
Last Week in AI
Last Week in AI
Security Archives - TechRepublic
Security Archives - TechRepublic
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
S
Securelist
S
Security Affairs
Project Zero
Project Zero
博客园 - 叶小钗
Google DeepMind News
Google DeepMind News
T
Tor Project blog
A
About on SuperTechFans
V2EX - 技术
V2EX - 技术
宝玉的分享
宝玉的分享
T
Tenable Blog
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
Simon Willison's Weblog
Simon Willison's Weblog
Forbes - Security
Forbes - Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V
V2EX
AWS News Blog
AWS News Blog
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
P
Privacy & Cybersecurity Law Blog
阮一峰的网络日志
阮一峰的网络日志
I
InfoQ
C
CXSECURITY Database RSS Feed - CXSecurity.com
H
Hacker News: Front Page
美团技术团队

流动

对口型视频合成方案对比:Wav2Lip、VideoReTalking 与 MuseTalk 2026 音色克隆方案对比:IndexTTS-2、CosyVoice、GPT-SoVITS、Fish Speech、VoxCPM 部署与实测 AI Agent折腾记(OpenClaw / Hermes Agent) 我的2025年 大连之行 回家收麦 六一儿童节爬长城 Golang database/sql 数据库断线自动重连机制解析 Golang默认Http Client导致的cannot assign requested address错误 清明踏春,爬山看海 购入小牛G400T电动车 北京的三月飞雪 wrenAI本地LLM模型部署 天津一日游 2024年终总结 停止使用staticfile.org服务 使用 ImageMagick 自动添加水印,保护图片版权 如何注册一个.sol域名 奥森公园半日游 昌平42公里骑行绿道打卡 十月一日爬慕田峪长城 当Hugo遇上AVIF,优化图片加载 博客被恶意镜像 Github Pages 部署流程解析 搭建个人锻炼页面 你好 Follow 中秋爬山 Google Adsense的审核之旅 让你的IPFS站点持久在线:接入Filebase的Names(IPNS)服务 一次简短的青岛之行 解决 "undeclared name: any (requires version go1.18 or later)" 编译错误 搭建自托管IPFS Gateway服务,替代Cloudflare的IPFS Gateway 302跳转的跨域问题(CORS) GORM增加sqlcommenter特性 源码分析:GORM是如何生成sql的 工银亚洲网银密码重置 加速Cloudflare访问 2023年终总结 2023年12月北京暴雪记录 使用Hugo实现响应式和优化的图片 加速Google Analytics 使用Google Indexing API加速博客收录 在Netlify上部署Twikoo评论系统 利用Github Actions定时抓取微博 北大口腔牙周刮治记录 故乡回忆之旅 解决Golang使用go get安装包后找不到可执行文件的问题 修正Hugo的JSON Feed格式 我的学车之路 将博客部署到星际文件系统(IPFS) 新冠疫情后的第一个春节 第一次清理键盘 2022年终总结 去掉Cloudflare烦人的email-decode.min.js请求 累计布局偏移修复方案改进 —— 自动生成图片宽高 优化博客的累计布局偏移(CLS)问题 将博客部署到Cloudflare Pages 奥林匹克公园向日葵之旅 记第二次洗牙 记录2022年海淀幼升小 Golang解析json的一个问题 疫情下的生活 整理下博客的一些调整 疫情下的五一假期 自己动手,更换thinkpad x1硬盘 二刷百望山 带娃游颐和园 博客架构说明 难得的清明假期 十一年的等待,终于拿到了liudon.com域名 被隔离的一周 mysql中字符串和整型自动转换的问题 一次惊心动魄的Mysql更新操作 如何在北京公积金网站上修改婚姻状况 PHP7.2编译安装后没有php.ini文件的问题 检测网站支持的SSL/TLS协议版本 记一次难忘的手术经历 十一假期经历 Swoft 框架运行分析(五) —— ConsoleProcessor模块分析 一个git submodule update引发的问题 一个Curl的耗时长的问题 Swoft 框架运行分析(三) —— BeanProcessor模块分析 Swoft 框架运行分析(二) —— AnnotationProcessor模块分析 Swoft 框架运行分析(一) BCMath 与 科学计数 Flink Could Not Resolve Resourcemanager Address 解决Sublime Text安装包时"There Are No Packages Available for Installation"的报错 关于本站 2019,新开始
Swoft 框架运行分析(四) —— EventProcessor模块分析
Liudon · 2019-09-26 · via 流动

今天我们来看一下EventProcessor的实现。

/**
    * Handle event register
    * @return bool
    */
public function handle(): bool
{
    if (!$this->application->beforeEvent()) {
        CLog::warning('Stop event processor by beforeEvent return false');
        return false;
    }

    /** @var EventManager $eventManager */
    $eventManager = bean('eventManager');
    [$count1, $count2] = ListenerRegister::register($eventManager);

    CLog::info('Event manager initialized(%d listener, %d subscriber)', $count1, $count2);

    // Trigger a app init event
    Swoft::trigger(SwoftEvent::APP_INIT_COMPLETE);

    return $this->application->afterEvent();
}

获取eventManagerBean实例,对应为Swoft\Event\Manager\EventManager类。

[$count1, $count2] = ListenerRegister::register($eventManager);

调用ListenerRegister类的register方法。

/**
    * @param EventManager $em
    *
    * @return array
    */
public static function register(EventManager $em): array
{
    foreach (self::$listeners as $className => $eventInfo) {
        $listener = Swoft::getSingleton($className);

        if (!$listener instanceof EventHandlerInterface) {
            throw new RuntimeException("The event listener class '{$className}' must be instanceof EventHandlerInterface");
        }

        $em->addListener($listener, $eventInfo);
    }

    foreach (self::$subscribers as $className) {
        $subscriber = Swoft::getSingleton($className);
        if (!$subscriber instanceof EventSubscriberInterface) {
            throw new RuntimeException("The event subscriber class '{$className}' must be instanceof EventSubscriberInterface");
        }

        $em->addSubscriber($subscriber);
    }

    $count1 = count(self::$listeners);
    $count2 = count(self::$subscribers);
    // Clear data
    self::$listeners = self::$subscribers = [];

    return [$count1, $count2];
}

遍历ListenerRegister类下的$listeners$subscribers属性,绑定事件到eventManagerBean实例上。

这里的$listeners$subscribers是从哪里来的呢?

这里以http-server为例。

swoft-component-2.0.5\src\http-server\src\Listener目录下,存在下面三个文件。

AfterRequestListener.php
AppInitCompleteListener.php
BeforeRequestListener.php

这里我们以AppInitCompleteListener.php为例。

<?php

namespace Swoft\Http\Server\Listener;

use function bean;
use ReflectionException;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Http\Server\Exception\HttpServerException;
use Swoft\Http\Server\Middleware\MiddlewareRegister;
use Swoft\Http\Server\Router\Router;
use Swoft\Http\Server\Router\RouteRegister;
use Swoft\SwoftEvent;

/**
 * Class AppInitCompleteListener
 * @since 2.0
 *
 * @Listener(SwoftEvent::APP_INIT_COMPLETE)
 */
class AppInitCompleteListener implements EventHandlerInterface
{
    /**
     * @param EventInterface $event
     *
     * @throws ContainerException
     * @throws ReflectionException
     * @throws HttpServerException
     */
    public function handle(EventInterface $event): void
    {
        /** @var Router $router Register HTTP routes */
        $router = bean('httpRouter');

        RouteRegister::registerRoutes($router);

        // Register middleware
        MiddlewareRegister::register();
    }
}

可以看到这里通过@Listener(SwoftEvent::APP_INIT_COMPLETE),使用了Swoft\Event\Annotation\Mapping\Listener类注解,对应的注解解析类为Swoft\Event\Annotation\Parser\ListenerParser

<?php declare(strict_types=1);

namespace Swoft\Event\Annotation\Parser;

use Doctrine\Common\Annotations\AnnotationException;
use Swoft\Annotation\Annotation\Mapping\AnnotationParser;
use Swoft\Annotation\Annotation\Parser\Parser;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\ListenerRegister;

/**
 * Class ListenerParser
 *
 * @since 2.0
 *
 * @AnnotationParser(Listener::class)
 */
class ListenerParser extends Parser
{
    /**
     * @param int      $type
     * @param Listener $annotation
     *
     * @return array
     * @throws AnnotationException
     */
    public function parse(int $type, $annotation): array
    {
        if ($type !== self::TYPE_CLASS) {
            throw new AnnotationException('`@Listener` must be defined on class!');
        }

        // collect listeners
        ListenerRegister::addListener($this->className, [
            // event name => listener priority
            $annotation->getEvent() => $annotation->getPriority()
        ]);

        return [$this->className, $this->className, Bean::SINGLETON, ''];
    }
}
/**
    * @param string $className
    * @param array  $definition [event name => listener priority]
    */
public static function addListener(string $className, array $definition = []): void
{
    // Collect listeners
    self::$listeners[$className] = $definition;
}

可以看到这里通过ListenerRegister::addListener方法,往ListenerRegister上注册了$listeners属性。

属性$listeners$subscribers的值,都是通过注解解析得来。

这里我们回到EventProcessor类的handle方法。

// Trigger a app init event
Swoft::trigger(SwoftEvent::APP_INIT_COMPLETE);

trigger的方法定义如下。

/**
    * Trigger an swoft application event
    *
    * @param string|EventInterface $event eg: 'app.start' 'app.stop'
    * @param null|mixed            $target
    * @param array                 $params
    *
    * @return EventInterface
    */
public static function trigger($event, $target = null, ...$params): EventInterface
{
    /** @see EventManager::trigger() */
    return BeanFactory::getSingleton('eventManager')->trigger($event, $target, $params);
}

这里调用了eventManager这个Bean实例的trigger方法。

/**
    * Trigger an event. Can accept an EventInterface or will create one if not passed
    *
    * @param string|EventInterface $event  'app.start' 'app.stop'
    * @param mixed|string          $target It is object or string.
    * @param array|mixed           $args
    *
    * @return EventInterface
    * @throws InvalidArgumentException
    */
public function trigger($event, $target = null, array $args = []): EventInterface
{
    if ($isString = is_string($event)) {
        $name = trim($event);
    } elseif ($event instanceof EventInterface) {
        $name = trim($event->getName());
    } else {
        throw new InvalidArgumentException('Invalid event params for trigger event handler');
    }

    $shouldCall = [];

    // Have matched listener
    if (isset($this->listenedEvents[$name])) {
        $shouldCall[$name] = '';
    }

    // Like 'app.db.query' => prefix: 'app.db'
    if ($pos = strrpos($name, '.')) {
        $prefix = substr($name, 0, $pos);

        // Have a wildcards listener. eg 'app.db.*'
        $wildcardEvent = $prefix . '.*';
        if (isset($this->listenedEvents[$wildcardEvent])) {
            $shouldCall[$wildcardEvent] = substr($name, $pos + 1);
        }
    }

    // Not found listeners
    if (!$shouldCall) {
        return $isString ? $this->basicEvent : $event;
    }

    /** @var EventInterface $event */
    if ($isString) {
        $event = $this->events[$name] ?? $this->basicEvent;
    }

    // Initial value
    $event->setName($name);
    $event->setParams($args);
    $event->setTarget($target);
    $event->stopPropagation(false);

    // Notify event listeners
    foreach ($shouldCall as $name => $method) {
        $this->triggerListeners($this->listeners[$name], $event, $method);

        if ($event->isPropagationStopped()) {
            return $event;
        }
    }

    // Have global wildcards '*' listener.
    if (isset($this->listenedEvents['*'])) {
        $this->triggerListeners($this->listeners['*'], $event);
    }

    return $event;
}

如果存在对应的事件,调用triggerListeners方法。

/**
    * @param array|ListenerQueue $listeners
    * @param EventInterface      $event
    * @param string              $method
    */
protected function triggerListeners($listeners, EventInterface $event, string $method = ''): void
{
    // $handled = false;
    $name     = $event->getName();
    $callable = false === strpos($name, '.');

    // 循环调用监听器,处理事件
    foreach ($listeners as $listener) {
        if ($event->isPropagationStopped()) {
            break;
        }

        if (is_object($listener)) {
            if ($listener instanceof EventHandlerInterface) {
                $listener->handle($event);
            } elseif ($method && method_exists($listener, $method)) {
                $listener->$method($event);
            } elseif ($callable && method_exists($listener, $name)) {
                $listener->$name($event);
            } elseif (method_exists($listener, '__invoke')) {
                $listener($event);
            }
        } elseif (is_callable($listener)) {
            $listener($event);
        }
    }
}

遍历事件回调,执行对应方法。

回到EventProcessor类的handle方法。

// Trigger a app init event
Swoft::trigger(SwoftEvent::APP_INIT_COMPLETE);

这里的事件为SwoftEvent::APP_INIT_COMPLETE,所以这里会执行这个事件下的所有回调。

这里以Swoft\Http\Server\Listener\AppInitCompleteListener为例。

<?php

namespace Swoft\Http\Server\Listener;

use function bean;
use ReflectionException;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Http\Server\Exception\HttpServerException;
use Swoft\Http\Server\Middleware\MiddlewareRegister;
use Swoft\Http\Server\Router\Router;
use Swoft\Http\Server\Router\RouteRegister;
use Swoft\SwoftEvent;

/**
 * Class AppInitCompleteListener
 * @since 2.0
 *
 * @Listener(SwoftEvent::APP_INIT_COMPLETE)
 */
class AppInitCompleteListener implements EventHandlerInterface
{
    /**
     * @param EventInterface $event
     *
     * @throws ContainerException
     * @throws ReflectionException
     * @throws HttpServerException
     */
    public function handle(EventInterface $event): void
    {
        /** @var Router $router Register HTTP routes */
        $router = bean('httpRouter');

        RouteRegister::registerRoutes($router);

        // Register middleware
        MiddlewareRegister::register();
    }
}

这里使用了Swoft\Event\Annotation\Mapping\Listener注解,对应的事件为SwoftEvent::APP_INIT_COMPLETE

按照上面的分析,这里会调用到AppInitCompleteListenerhandle方法,获取httpRouterBean实例,注册http服务的路由信息和中间件。

到这里,我们大概清楚了EventProcessor这个模块的作用,注册了所有事件的回调。