aura/signal

SignalSlots/EventHandler 实现方案;通过它,我们可以在对象向信号管理器发送信号(通知或事件)时调用处理程序(槽或钩子)。

安装数量: 54,619

依赖项: 3

建议者: 0

安全性: 0

星星: 33

关注者: 10

分支: 7

开放问题: 0

类型:aura-package

1.0.4 2014-08-24 00:00 UTC

This package is auto-updated.

Last update: 2024-09-11 04:53:52 UTC


README

Build Status

Aura Signal 包是 PHP 的 SignalSlots/EventHandler 实现。通过它,我们可以在对象向信号管理器发送信号(通知或事件)时调用处理程序(槽或钩子)。

此包符合 PSR-0PSR-1PSR-2 规范。如果您发现不符合规范的地方,请通过 pull request 发送补丁。

基本用法

实例化信号管理器

首先,实例化信号 Manager 类。最简单的方法是调用 Aura.Signal/scripts/instance.php 脚本。

<?php
$signal = require '/path/to/Aura.Signal/scripts/instance.php';

添加信号处理程序

在我们向 Manager 发送信号之前,我们需要为其添加一个处理程序。要添加处理程序,指定以下内容:

  1. 预期发送信号的类。这可以是 “*” 表示 “任何类”,或完整的类名。

  2. 信号名称。

  3. 处理信号的闭包或回调函数。

例如,要添加一个闭包,每次 Vendor\Package\Example 类的对象发送名为 'example_signal' 的信号时都会执行。

<?php
$signal->handler(
    'Vendor\Package\Example',
    'example_signal',
    function ($arg) { echo $arg; }
);

按类发送信号

要发送信号,发送类必须有一个 Manager 实例。该类应调用 send() 方法,传入源对象(自身)、正在发送的信号以及传递给信号处理程序的参数。

例如,我们将定义 Vendor\Package\Example 类,并使其向 Manager 发送信号。

<?php
namespace Vendor\Package;
use Aura\Signal\Manager as SignalManager;

class Example
{
    protected $signal;
    
    public function __construct(SignalManager $signal)
    {
        $this->signal = $signal;
    }
    
    public function doSomething($text)
    {
        echo $text;
        $this->signal->send($this, 'example_signal', $text);
    }
}

现在,每次我们调用 doSomething() 方法时,它都会向 Manager 发送 'example_signal' 信号,并且 Manager 会调用该信号的处理器。

信号继承

如果一个类发送信号,但没有为它设置处理程序,那么 Manager 不会做任何操作。然而,如果已为父类设置了处理程序,并且其子类发送了父类已处理的信号,则 Manager 也会为该子类处理该信号。

例如,如果我们有两个这样的类,并且对它们每个都调用 doSomethingElse()...

<?php
namespace Vendor\Package;
use Aura\Signal\Manager as SignalManager;

class ExampleChild extends Example
{
    public function doSomethingElse($text)
    {
        echo $text . $text . $text;
        $this->signal->send($this, 'example_signal', $text);
    }
}

class ExampleOther
{
    protected $signal;
    
    public function __construct(SignalManager $signal)
    {
        $this->signal = $signal;
    }
    
    public function doSomethingElse($text)
    {
        echo $text . $text . $text;
        $this->signal->send($this, 'example_signal', $text)
    }
}

... 那么 Manager处理 来自 ExampleChild 的信号,因为其父类有一个处理程序。对于 ExampleOtherManager 不会处理其信号,因为没有为其或其父类添加处理程序到 Manager

按对象发送信号

可以将处理程序绑定到对象实例,这样只有从该特定对象发送的信号才会被处理。为此,将对象实例作为处理程序的 $sender 传递。

<?php
/**
 * @var Aura\Signal\Manager $signal
 */
$object = new Vendor\Package\ExampleChild($signal);

$signal->handler(
    $object,
    'example_signal',
    function ($arg) { echo "$arg!!!";}
);

如果特定的对象实例发送了example_signal,则将触发处理器,但其他任何ExampleChild实例发送相同的信号时,都不会触发处理器。这对于在包含其自身回调的对象中设置信号处理器很有用;例如

<?php
namespace Vendor\Package;
use Aura\Signal\Manager as SignalManager;

class ExampleAnotherChild extends Example
{
    public function __construct(SignalManager $signal)
    {
        parent::__construct();
        $this->signal = $signal;
        $this->signal->handler($this, 'preAction', [$this, 'preAction']);
        $this->signal->handler($this, 'postAction', [$this, 'postAction']);
    }
    
    public function action()
    {
        $this->signal->send($this, 'preAction');
        $this->doSomething( __METHOD__ );
        $this->signal->send($this, 'postAction');
    }
    
    public function preAction()
    {
        // happens before the main action() logic
    }
    
    public function postAction()
    {
        // happens after the main action() logic
    }
}

当调用ExampleAnotherChild::action()时,代码

  1. Manager发送一个'preAction'信号,然后Manager反过来调用对象上的preAction()方法

  2. 调用对象上的doSomething()方法(请注意,doSomething()方法会向Manager发送它自己的'example_signal'

  3. Manager发送一个'postAction'信号,然后Manager反过来调用对象上的postAction()方法。

如果存在ExampleAnotherChild类或其父类的基于类的处理器,那些也会被执行。这意味着我们可以设置应用于类的总体组合处理器,以及与特定对象相关的处理器。

高级用法

处理器位置组

默认情况下,所有Handler对象都将附加到Manager堆栈,并按添加的顺序进行处理。有时您可能需要将Handler以不同的顺序处理;例如,在所有其他处理器之前或之后。如果是这样,您可以在将Handler添加到Manager时传递一个$position值。(Handler对象的默认$position为5000。)

<?php
// add a closure at position 1000, which means it will be processed
// before all handlers at the default position 5000.
$closure = function() { 
    echo "Before all others."; 
    return "First closure";
};
$signal->handler('Vendor\Package\ExampleChild', 'example_signal', $closure, 1000);

// add a closure at position 9000, which means it will be processed
// after all handlers at the default position 5000.
$closure = function() { 
    echo "After all others."; 
    return "Second closure";
};
$signal->handler('Vendor\Package\ExampleChild', 'example_signal', $closure, 1000);

添加到位置的Handler对象仍然会附加在该位置组内。

结果检查

在发送信号后,我们可以审查每个处理器返回的结果。

<?php
// send a signal
$this->signal->send($this, 'example_signal');

// get the result collection
$results =  $this->signal->getResults();

// go through each result ...
foreach ($results as $result) {
    
    // ... and echo the value returned by the Handler callback
    echo $result->value;
}

getResults()方法返回一个包含Result对象的ResultCollection,每个对象都具有以下属性

  • $origin:发送信号的对象。

  • $sender:处理器预期的发送者。

  • $signal:原始发送的信号。

  • $value:处理器回调返回的值。

如果您只需要最后一个结果,您可以在ResultCollection对象上调用getLast()

<?php
// send a signal and retain the results from each Handler
$results = $this->signal->send($this, 'example_signal');

// get the last result
$result = $results->getLast();

// and echo the value returned by the last Handler callback
echo $result->value;

停止信号处理

有时将需要停止处理信号处理器。如果一个处理器的回调返回了Aura\Signal\Manager::STOP常量,则不会处理该信号的任何其他处理器。

首先我们定义处理器;注意第二个返回了STOP常量

<?php
// add signal handlers
$signal->handler(
    'Vendor\Package\Example',
    'mock_signal',
    function() { return 'first'; }
);

$signal->handler(
    'Vendor\Package\Example',
    'mock_signal',
    function() { return \Aura\Signal\Manager::STOP; }
);

$signal->handler(
    'Vendor\Package\Example',
    'mock_signal',
    function() { return 'third'; }
);

然后,从对象内部发送信号

<?php
$results = $this->signal->send($this, 'mock_signal');
// Or you can get via 
// $results = $this->signal->getResults();

通常,$results会有三个条目。在这种情况下,它只有两个,因为第二个处理器返回了\aura\signal\Manager::STOP。因此,第三个处理器从未被执行。您可以通过调用ResultCollection::isStopped()来查看Manager是否以这种方式停止了处理器处理。

<?php
if ($results->isStopped()) {
    $result = $results->getLast();
    echo "Processing for signal 'mock_signal' stopped "
       . "by handler for " . $result->sender;
}

在构造时设置处理器

我们可以在构造时为Manager设置Handler定义。这允许我们使用一个或多个配置文件来定义ManagerHandler堆栈。

给定位于/path/to/signal_handlers.php的此文件...

<?php
return [
    // first handler, with a closure
    [
        'Vendor\Package\Example',
        'mock_signal',
        function() { return 'foo'; },
    ],
    // second handler, with a static callback
    [
        'Vendor\Package\Example',
        'mock_signal',
        ['Vendor\Package\SomeClass', 'someMethod'],
    ],
    // third handler, with a closure and position
    [
        'Vendor\Package\Example',
        'mock_signal',
        function() { return 'baz'; },
        1000,
    ],
];

...我们可以这样配置一个Manager

<?php
namespace Aura\Signal;
$handlers = require '/path/to/signal_handlers.php';
$signal = new Manager(
    new HandlerFactory,
    new ResultFactory,
    new ResultCollection,
    $handlers
);

这相当于调用$signal->handler()三次以添加每个处理器。

谢谢

感谢Richard "Cyberlot" Thomas的原始建议,Galactic Void将其重新提出来,以及Matthew Weier O'Phinney