我开始一个新的web应用程序在PHP和这一次,我想创建一些东西,人们可以通过使用插件接口扩展。

如何在他们的代码中编写“钩子”,以便插件可以附加到特定的事件?


当前回答

钩子和监听器方法是最常用的方法,但您还可以做其他事情。取决于你的应用程序的大小,以及你允许谁看到代码(这将是一个自由/开源软件脚本,还是内部的东西)将极大地影响你想要如何允许插件。

Kdeloach有一个很好的例子,但是他的实现和钩子函数有点不安全。我想请你给更多的信息,php应用程序的性质,你写的,以及你如何看待插件适合。

+1给我。

其他回答

雅虎的Matt Zandstra有一个叫做Stickleback的项目,它处理了很多PHP插件的处理工作。

它加强了插件类的接口,支持命令行接口,并且安装和运行起来并不太难——特别是如果您阅读了PHP架构师杂志上关于它的封面故事。

这是我使用的一种方法,它试图复制Qt信号/槽机制,一种观察者模式。 物体可以发射信号。 每个信号在系统中都有一个ID——它由发送者的ID +对象名称组成 每个信号都可以绑定到接收器,这就是一个“可调用的”。 您使用总线类将信号传递给任何有兴趣接收它们的人 当某事发生时,你“发送”一个信号。 下面是一个示例实现

    <?php

class SignalsHandler {


    /**
     * hash of senders/signals to slots
     *
     * @var array
     */
    private static $connections = array();


    /**
     * current sender
     *
     * @var class|object
     */
    private static $sender;


    /**
     * connects an object/signal with a slot
     *
     * @param class|object $sender
     * @param string $signal
     * @param callable $slot
     */
    public static function connect($sender, $signal, $slot) {
        if (is_object($sender)) {
            self::$connections[spl_object_hash($sender)][$signal][] = $slot;
        }
        else {
            self::$connections[md5($sender)][$signal][] = $slot;
        }
    }


    /**
     * sends a signal, so all connected slots are called
     *
     * @param class|object $sender
     * @param string $signal
     * @param array $params
     */
    public static function signal($sender, $signal, $params = array()) {
        self::$sender = $sender;
        if (is_object($sender)) {
            if ( ! isset(self::$connections[spl_object_hash($sender)][$signal])) {
                return;
            }
            foreach (self::$connections[spl_object_hash($sender)][$signal] as $slot) {
                call_user_func_array($slot, (array)$params);
            }

        }
        else {
            if ( ! isset(self::$connections[md5($sender)][$signal])) {
                return;
            }
            foreach (self::$connections[md5($sender)][$signal] as $slot) {
                call_user_func_array($slot, (array)$params);
            }
        }

        self::$sender = null;
    }


    /**
     * returns a current signal sender
     *
     * @return class|object
     */
    public static function sender() {
        return self::$sender;
    }

}   

class User {

    public function login() {
        /**
         * try to login
         */
        if ( ! $logged ) {
            SignalsHandler::signal(this, 'loginFailed', 'login failed - username not valid' );
        }
    }

}

class App {
    public static function onFailedLogin($message) {
        print $message;
    }
}


$user = new User();
SignalsHandler::connect($user, 'loginFailed', array($Log, 'writeLog'));
SignalsHandler::connect($user, 'loginFailed', array('App', 'onFailedLogin'));

$user->login();

?>

好的建议是看看其他项目是如何做到这一点的。许多人要求安装插件,并为服务注册插件的“名称”(就像wordpress那样),这样在代码中就有了“点”,可以调用一个函数来标识已注册的侦听器并执行它们。一个标准的OO设计模式是观察者模式,在真正面向对象的PHP系统中实现它是一个很好的选择。

Zend Framework使用了许多钩子方法,并且架构非常好。这是一个很好的系统。

钩子和监听器方法是最常用的方法,但您还可以做其他事情。取决于你的应用程序的大小,以及你允许谁看到代码(这将是一个自由/开源软件脚本,还是内部的东西)将极大地影响你想要如何允许插件。

Kdeloach有一个很好的例子,但是他的实现和钩子函数有点不安全。我想请你给更多的信息,php应用程序的性质,你写的,以及你如何看待插件适合。

+1给我。

我很惊讶,这里的大多数答案似乎都是关于web应用程序的本地插件,即运行在本地web服务器上的插件。

如果你想让插件在另一个远程服务器上运行呢?最好的方法是提供一个表单,允许您定义不同的url,当应用程序中发生特定事件时将调用这些url。

不同的事件会根据刚刚发生的事件发送不同的信息。

这样,您只需对提供给应用程序的URL(例如通过https)执行cURL调用,远程服务器可以根据应用程序发送的信息执行任务。

这提供了两个好处:

您不必在本地服务器上托管任何代码(安全性) 代码可以在远程服务器上(可扩展性),使用PHP以外的不同语言(可移植性)