如何使用SplObserver for Hook系统?

时间:2017-05-16 14:19:54

标签: php

我为Hook系统编写了一个类。但这已经过时了。我想用splObserver来编码它。

<?php
class Event
{
    private static $filters = [];
    private static $actions = [];

    public static function addAction($name, $callback, $priority = 10)
    {
        if (! isset(static::$actions[$name])) {
            static::$actions[$name] = [];
        }

        static::$actions[$name][] = [
            'priority' => (int)$priority,
            'callback' => $callback,
        ];
    }

    public function doAction($name, ...$args)
    {
        $actions = isset(static::$actions[$name]) ? static::$actions[$name] : false;

        if (! $actions) {
            return;
        }

        // sort actions by priority
        $sortArr = array_map(function ($action) {
            return $action['priority'];
        }, $actions);

        \array_multisort($sortArr, $actions);

        foreach ($actions as $action) {
            \call_user_func_array($action['callback'], $args);
        }
    }
}

Event::addAction('action1', function(){
   echo 'balabala1';
});
Event::addAction('action1', function(){
   echo 'balabala2';
});
Event::doAction('action1');

输出:balabala1 balabala2

效果很好。 我想使用SplObserver重新编码并尝试编码,但不知道。

1 个答案:

答案 0 :(得分:1)

我真的不知道 whether this implementation could be useful in a real life application 与否,但为了回答您的问题,我们开始...

假设我们有一个 User 类,我们希望将其与我们的自定义函数挂钩。

首先,我们创建了一个包含主题逻辑的可重用 trait,能够管理我们可以将我们的操作挂钩到的“事件名称”。

trait SubjectTrait {

    private $observers = [];

    // this is not a real __construct() (we will call it later)
    public function construct()
    {
        $this->observers["all"] = [];
    }

    private function initObserversGroup(string $name = "all")
    {
        if (!isset($this->observers[$name])) {
            $this->observers[$name] = [];
        }
    }

    private function getObservers(string $name = "all")
    {
        $this->initObserversGroup($name);
        $group = $this->observers[$name];
        $all = $this->observers["all"];

        return array_merge($group, $all);
    }

    public function attach(\SplObserver $observer, string $name = "all")
    {
        $this->initObserversGroup($name);
        $this->observers[$name][] = $observer;
    }

    public function detach(\SplObserver $observer, string $name = "all")
    {
        foreach ($this->getObservers($name) as $key => $o) {
            if ($o === $observer) {
                unset($this->observers[$name][$key]);
            }
        }
    }

    public function notify(string $name = "all", $data = null)
    {
        foreach ($this->getObservers($name) as $observer) {
            $observer->update($this, $name, $data);
        }
    }
}

接下来,我们在 SplSubject User 类中使用 trait:

class User implements \SplSubject
{

    // It's necessary to alias construct() because it
    // would conflict with other methods.
    use SubjectTrait {
        SubjectTrait::construct as protected constructSubject;
    }

    public function __construct()
    {
        $this->constructSubject();
    }

    public function create()
    {
        // User creation code...

        $this->notify("User:created");
    }

    public function update()
    {
        // User update code...

        $this->notify("User:updated");
    }

    public function delete()
    {
        // User deletion code...

        $this->notify("User:deleted");
    }
}

最后一步是实现可重用的 SplObserver。此观察者能够将自己绑定到 Closure(匿名函数)。

class MyObserver implements SplObserver
{
    protected $closure;

    public function __construct(Closure $closure)
    {
        $this->closure = $closure->bindTo($this, $this);
    }

    public function update(SplSubject $subject, $name = null, $data = null)
    {
        $closure = $this->closure;
        $closure($subject, $name, $data);
    }
}

现在,测试:

$user = new User;

// our custom functions (Closures)
$function1 = function(SplSubject $subject, $name, $data) {
    echo $name . ": function1\n"; // we could also use $data here
};

$function2 = function(SplSubject $subject, $name, $data) {
    echo $name . ": function2\n";
};

// subscribe the first function to all events
$user->attach(new MyObserver($function1), 'all');
// subscribe the second function to user creations only
$user->attach(new MyObserver($function2), 'User:created');

// run a couple of methods to see what happens
$user->create();
$user->update();

输出将是:

User:created: function2
User:created: function1
User:updated: function1

注意:我们可以使用 SplObjectStorage 代替数组,将观察者存储在特征中。