基于事件的架构和PHP中的钩子函数

15

我打算开发一个游戏,它有一个PHP后端用于与数据存储库通信。我考虑过后,得出结论,我们游戏应该遵循最佳的事件驱动设计范式。我希望有一个成就系统(类似这个网站的徽章系统),并且我想将这些“成就检查”挂钩到游戏中发生的多种不同事件中。例如:

当用户执行X操作时,hook Y被触发,并调用所有附加的函数来检查成就要求。

通过这样建立架构,我可以轻松添加新的成就,只需将检查函数添加到适当的钩子中,其他所有内容都会自动完成。

我不确定这是否是我打算做的事情的很好解释,但无论如何,我正在寻找以下内容:

  1. 如何编写事件驱动应用程序的相关参考资料
  2. 展示如何在PHP函数中放置“钩子”的代码片段
  3. 展示如何将函数附加到上述第2点提及的“钩子”的代码片段

我对如何完成2)和3)有一些想法,但我希望精通此事的人能够为我指点迷津,讲述最佳实践。

谢谢您的帮助!


我知道WordPress使用挂钩来管理其插件。您可以在这里查看:http://codex.wordpress.org/Plugin_API/Hooks - Maxime Fafard
1
我并不是在寻找 WordPress 的钩子 API 链接。 - MoarCodePlz
你可以查看WordPress如何使用钩子。有很好的文档和清晰的源代码。 - Maxime Fafard
4
WordPress的钩子实现并不是一个好的例子,它没有清晰的接口,实现存在漏洞,代码也没有很好地记录文档。但它展示了您可以将回调分配给变量,包括数组,您可以对这些数组进行排序,以及在调用回调和取消回调注册时可能出错的情况。然而,这些信息并没有以易于理解的格式呈现出来。 - hakre
4个回答

16

如何编写事件驱动应用的良好参考资料

您可以使用“愚蠢”的回调函数(伪类型)(演示):

class Hooks
{
    private $hooks;
    public function __construct()
    {
        $this->hooks = array();
    }
    public function add($name, $callback) {
        // callback parameters must be at least syntactically
        // correct when added.
        if (!is_callable($callback, true))
        {
            throw new InvalidArgumentException(sprintf('Invalid callback: %s.', print_r($callback, true)));
        }
        $this->hooks[$name][] = $callback;
    }
    public function getCallbacks($name)
    {
        return isset($this->hooks[$name]) ? $this->hooks[$name] : array();
    }
    public function fire($name)
    {
        foreach($this->getCallbacks($name) as $callback)
        {
            // prevent fatal errors, do your own warning or
            // exception here as you need it.
            if (!is_callable($callback))
                continue;

            call_user_func($callback);
        }
    }
}

$hooks = new Hooks;
$hooks->add('event', function() {echo 'morally disputed.';});
$hooks->add('event', function() {echo 'explicitly called.';});
$hooks->fire('event');

或者实现在事件驱动应用程序中经常使用的模式:观察者模式

展示如何在 PHP 函数中放置“钩子”的代码片段

上面的手册链接(回调函数可以存储到变量中)以及一些PHP代码示例,演示了观察者模式的使用


1
@MoarCodePlz:这个实现缺少事件/钩子上下文。例如,您可以将事件封装到它自己的类中,以便它们可以被触发(接口化)。但是这会增加另一层。在这种情况下,另一个有用的函数是is_callable - hakre
有什么好的方法可以“停止”下一个事件的运行,比如如果我要输出响应或其他内容? - John
@John:定义钩子不要打开像输出这样的侧通道。或者采用 PHP 风格,通过捕获所有输出并将其转换为致命错误来删除所有输出。钩子应该通过定义的结构返回,而不是通过侧通道返回,否则您会遇到通常试图防止的全局静态状态的常见问题。 - hakre
我该如何将东西注入事件中,例如对象? - John
1
@John:看一下call_user_func_array()而不是call_user_func() - 它允许您传递零个或多个参数的数组。还有func_get_args() - hakre
显示剩余2条评论

5

对于PHP,我经常集成Symfony事件组件:http://components.symfony-project.org/event-dispatcher/

下面是一个简短的例子,您可以在Symfony的菜谱部分中找到扩展内容。

<?php

class Foo
{
  protected $dispatcher = null;

    // Inject the dispatcher via the constructor
  public function __construct(sfEventDispatcher $dispatcher)
  {
    $this->dispatcher = $dispatcher;
  }

  public function sendEvent($foo, $bar)
  {
    // Send an event
    $event = new sfEvent($this, 'foo.eventName', array('foo' => $foo, 'bar' => $bar));
    $this->dispatcher->notify($event);
  }
}


class Bar
{
  public function addBarMethodToFoo(sfEvent $event)
  {
    // respond to event here.
  }
}


// Somewhere, wire up the Foo event to the Bar listener
$dispatcher->connect('foo.eventName', array($bar, 'addBarMethodToFoo'));

?>

这是我们将其集成到购物车中以创建类似游戏的购物体验的系统,将用户操作连接到游戏事件中。当用户执行特定操作时,php触发事件,从而触发奖励。
例如:如果用户点击了特定按钮10次,则会获得一颗星星。
例如2:当用户推荐朋友并且该朋友注册时,会触发一个事件,奖励原始推荐人积分。

1

请查看 CodeIgniter,因为它已经内置了钩子

只需启用钩子:

$config['enable_hooks'] = TRUE;

然后定义您的钩子:

 $hook['post_controller_constructor'] = array(
                                'class'    => 'Hooks',
                                'function' => 'session_check',
                                'filename' => 'hooks.php',
                                'filepath' => 'hooks',
                                'params'   => array()
                                ); 

然后在你的类中使用它:

<?php

    class Hooks {
        var $CI;

        function Hooks() {
            $this->CI =& get_instance();
        }

        function session_check() {
            if(!$this->CI->session->userdata("logged_in") && $this->CI->uri->uri_string != "/user/login")
                redirect('user/login', 'location');
        }
    }

?> 

1
虽然我很感谢您的反馈并一定要去查看CodeIgniter,但我更希望从头开始学习如何编写这种程序。我还在寻找有关开发事件驱动应用程序的良好参考资料,以便开发自己的事件驱动应用程序并使用自己制作的挂钩。 - MoarCodePlz
1
虽然我完全同意“不要重复造轮子”的论点,但我也认为,如果我只需要一个非常简单的事件驱动钩子系统,那么使用一个带有许多花哨功能的外部库比编写像hakre所展示的单个类更糟糕。如果我正在寻找更复杂和强大的东西,那么我认为使用外部库会更合适。 - MoarCodePlz
1
你正在开发一款游戏,不认为需要一个带有各种花哨功能的强大框架?好的 :) - AlienWebguy
2
没错,我了解那种感觉。需要考虑的一点是,社区驱动的开源框架总是比个人努力更好。学习如何做某事就像看源代码并进行逆向工程一样简单。这样你就可以在当前最佳实践上建立你的基础,并从那里开始。 - AlienWebguy
5
CodeIgniter远离事件驱动设计。 - sepehr
显示剩余2条评论

0
一个新的访问者可能会通过调查ReactPHP来找到一些关于O/P的问题的答案的见解。如果你今天(2023年)搜索短语“PHP中的事件驱动架构”,ReactPHP似乎是共同点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接