<p>I am starting a new web application in PHP and this time around I want to create something that people can extend by using a plugin interface. </p>

<p>How does one go about writing 'hooks' into their code so that plugins can attach to specific events?</p>

[{'answer_id': 76, 'author': 'helloandre', 'author_id': 50, 'author_profile': 'https://Stackoverflow.com/users/50', 'pm_score': 4, 'selected': False, 'text': "<p>I believe the easiest way would be to follow Jeff's own advice and have a look around the existing code. Try looking at WordPress, Drupal, Joomla, and other well-known PHP-based CMS to see how their API hooks look and feel. This way you can even get ideas you may have not thought of previously to make things a little more robust.</p>\n<p>A more direct answer would be to write general files that they would &quot;include_once&quot; into their file that would provide the usability they would need. This would be broken up into categories and NOT provided in one MASSIVE &quot;hooks.php&quot; file. Be careful though, because what ends up happening is that files that they include end up having more and more dependencies and functionality improves. Try to keep API dependencies low. I.E fewer files for them to include.</p>\n"}, {'answer_id': 77, 'author': 'Kevin', 'author_id': 40, 'author_profile': 'https://Stackoverflow.com/users/40', 'pm_score': 8, 'selected': True, 'text': '<p>You could use an Observer pattern.  A simple functional way to accomplish this:</p>\n\n<pre><code>&lt;?php\n\n/** Plugin system **/\n\n$listeners = array();\n\n/* Create an entry point for plugins */\nfunction hook() {\n    global $listeners;\n\n    $num_args = func_num_args();\n    $args = func_get_args();\n\n    if($num_args &lt; 2)\n        trigger_error("Insufficient arguments", E_USER_ERROR);\n\n    // Hook name should always be first argument\n    $hook_name = array_shift($args);\n\n    if(!isset($listeners[$hook_name]))\n        return; // No plugins have registered this hook\n\n    foreach($listeners[$hook_name] as $func) {\n        $args = $func($args); \n    }\n    return $args;\n}\n\n/* Attach a function to a hook */\nfunction add_listener($hook, $function_name) {\n    global $listeners;\n    $listeners[$hook][] = $function_name;\n}\n\n/////////////////////////\n\n/** Sample Plugin **/\nadd_listener(\'a_b\', \'my_plugin_func1\');\nadd_listener(\'str\', \'my_plugin_func2\');\n\nfunction my_plugin_func1($args) {\n    return array(4, 5);\n}\n\nfunction my_plugin_func2($args) {\n    return str_replace(\'sample\', \'CRAZY\', $args[0]);\n}\n\n/////////////////////////\n\n/** Sample Application **/\n\n$a = 1;\n$b = 2;\n\nlist($a, $b) = hook(\'a_b\', $a, $b);\n\n$str  = "This is my sample application\\n";\n$str .= "$a + $b = ".($a+$b)."\\n";\n$str .= "$a * $b = ".($a*$b)."\\n";\n\n$str = hook(\'str\', $str);\necho $str;\n?&gt;\n</code></pre>\n\n<p><strong>Output:</strong></p>\n\n<pre><code>This is my CRAZY application\n4 + 5 = 9\n4 * 5 = 20\n</code></pre>\n\n<p><strong>Notes:</strong></p>\n\n<p>For this example source code, you must declare all your plugins before the actual source code that you want to be extendable.  I\'ve included an example of how to handle single or multiple values being passed to the plugin.  The hardest part of this is writing the actual documentation which lists what arguments get passed to each hook.</p>\n\n<p>This is just one method of accomplishing a plugin system in PHP.  There are better alternatives, I suggest you check out the WordPress Documentation for more information.</p>\n'}, {'answer_id': 147, 'author': 'w-ll', 'author_id': 146637, 'author_profile': 'https://Stackoverflow.com/users/146637', 'pm_score': 5, 'selected': False, 'text': '<p>The <em>hook</em> and <em>listener</em> method is the most commonly used, but there are other things you can do.  Depending on the size of your app, and who your going to allow see the code (is this going to be a FOSS script, or something in house) will influence greatly how you want to allow plugins.</p>\n\n<p>kdeloach has a nice example, but his implementation and hook function is a little unsafe.  I would ask for you to give more information of the nature of php app your writing, And how you see plugins fitting in.  </p>\n\n<p>+1 to kdeloach from me.</p>\n'}, {'answer_id': 86433, 'author': 'julz', 'author_id': 16536, 'author_profile': 'https://Stackoverflow.com/users/16536', 'pm_score': 4, 'selected': False, 'text': '<p>There\'s a neat project called <a href="http://developer.yahoo.net/blog/archives/2007/10/r3_and_stickleb.html" rel="noreferrer">Stickleback</a> by Matt Zandstra at Yahoo that handles much of the work for handling plugins in PHP.</p>\n\n<p>It enforces the interface of a plugin class, supports a command line interface and isn\'t too hard to get up and running - especially if you read the cover story about it in the <a href="http://www.phparch.com" rel="noreferrer">PHP architect magazine</a>.</p>\n'}, {'answer_id': 86796, 'author': 'THEMike', 'author_id': 7106, 'author_profile': 'https://Stackoverflow.com/users/7106', 'pm_score': 4, 'selected': False, 'text': '<p>Good advice is to look how other projects have done it. Many call for having plugins installed and their "name" registered for services (like wordpress does) so you have "points" in your code where you call a function that identifies registered listeners and executes them. A standard OO design patter is the <a href="http://www.phppatterns.com/docs/design/observer_pattern" rel="noreferrer">Observer Pattern</a>, which would be a good option to implement in a truly object oriented PHP system.</p>\n\n<p>The <a href="http://framework.zend.com" rel="noreferrer">Zend Framework</a> makes use of many hooking methods, and is very nicely architected. That would be a good system to look at.</p>\n'}, {'answer_id': 136273, 'author': 'andy.gurin', 'author_id': 22388, 'author_profile': 'https://Stackoverflow.com/users/22388', 'pm_score': 5, 'selected': False, 'text': '<p>Here is an approach I\'ve used, it\'s an attempt to copy from Qt signals/slots mechanism, a kind of Observer pattern.\nObjects can emit signals.\nEvery signal has an ID in the system - it\'s composed by sender\'s id + object name\nEvery signal can be binded to the receivers, which simply is a "callable"\nYou use a bus class to pass the signals to anybody interested in receiving them\nWhen something happens, you "send" a signal. \nBelow is and example implementation</p>\n\n<pre><code>    &lt;?php\n\nclass SignalsHandler {\n\n\n    /**\n     * hash of senders/signals to slots\n     *\n     * @var array\n     */\n    private static $connections = array();\n\n\n    /**\n     * current sender\n     *\n     * @var class|object\n     */\n    private static $sender;\n\n\n    /**\n     * connects an object/signal with a slot\n     *\n     * @param class|object $sender\n     * @param string $signal\n     * @param callable $slot\n     */\n    public static function connect($sender, $signal, $slot) {\n        if (is_object($sender)) {\n            self::$connections[spl_object_hash($sender)][$signal][] = $slot;\n        }\n        else {\n            self::$connections[md5($sender)][$signal][] = $slot;\n        }\n    }\n\n\n    /**\n     * sends a signal, so all connected slots are called\n     *\n     * @param class|object $sender\n     * @param string $signal\n     * @param array $params\n     */\n    public static function signal($sender, $signal, $params = array()) {\n        self::$sender = $sender;\n        if (is_object($sender)) {\n            if ( ! isset(self::$connections[spl_object_hash($sender)][$signal])) {\n                return;\n            }\n            foreach (self::$connections[spl_object_hash($sender)][$signal] as $slot) {\n                call_user_func_array($slot, (array)$params);\n            }\n\n        }\n        else {\n            if ( ! isset(self::$connections[md5($sender)][$signal])) {\n                return;\n            }\n            foreach (self::$connections[md5($sender)][$signal] as $slot) {\n                call_user_func_array($slot, (array)$params);\n            }\n        }\n\n        self::$sender = null;\n    }\n\n\n    /**\n     * returns a current signal sender\n     *\n     * @return class|object\n     */\n    public static function sender() {\n        return self::$sender;\n    }\n\n}   \n\nclass User {\n\n    public function login() {\n        /**\n         * try to login\n         */\n        if ( ! $logged ) {\n            SignalsHandler::signal(this, \'loginFailed\', \'login failed - username not valid\' );\n        }\n    }\n\n}\n\nclass App {\n    public static function onFailedLogin($message) {\n        print $message;\n    }\n}\n\n\n$user = new User();\nSignalsHandler::connect($user, \'loginFailed\', array($Log, \'writeLog\'));\nSignalsHandler::connect($user, \'loginFailed\', array(\'App\', \'onFailedLogin\'));\n\n$user-&gt;login();\n\n?&gt;\n</code></pre>\n'}, {'answer_id': 933700, 'author': 'Volomike', 'author_id': 105539, 'author_profile': 'https://Stackoverflow.com/users/105539', 'pm_score': 6, 'selected': False, 'text': '<p>So let\'s say you don\'t want the Observer pattern because it requires that you change your class methods to handle the task of listening, and want something generic. And let\'s say you don\'t want to use <code>extends</code> inheritance because you may already be inheriting in your class from some other class. Wouldn\'t it be great to have a generic way to make <em>any class pluggable without much effort</em>? Here\'s how:</p>\n\n<pre><code>&lt;?php\n\n////////////////////\n// PART 1\n////////////////////\n\nclass Plugin {\n\n    private $_RefObject;\n    private $_Class = \'\';\n\n    public function __construct(&amp;$RefObject) {\n        $this-&gt;_Class = get_class(&amp;$RefObject);\n        $this-&gt;_RefObject = $RefObject;\n    }\n\n    public function __set($sProperty,$mixed) {\n        $sPlugin = $this-&gt;_Class . \'_\' . $sProperty . \'_setEvent\';\n        if (is_callable($sPlugin)) {\n            $mixed = call_user_func_array($sPlugin, $mixed);\n        }   \n        $this-&gt;_RefObject-&gt;$sProperty = $mixed;\n    }\n\n    public function __get($sProperty) {\n        $asItems = (array) $this-&gt;_RefObject;\n        $mixed = $asItems[$sProperty];\n        $sPlugin = $this-&gt;_Class . \'_\' . $sProperty . \'_getEvent\';\n        if (is_callable($sPlugin)) {\n            $mixed = call_user_func_array($sPlugin, $mixed);\n        }   \n        return $mixed;\n    }\n\n    public function __call($sMethod,$mixed) {\n        $sPlugin = $this-&gt;_Class . \'_\' .  $sMethod . \'_beforeEvent\';\n        if (is_callable($sPlugin)) {\n            $mixed = call_user_func_array($sPlugin, $mixed);\n        }\n        if ($mixed != \'BLOCK_EVENT\') {\n            call_user_func_array(array(&amp;$this-&gt;_RefObject, $sMethod), $mixed);\n            $sPlugin = $this-&gt;_Class . \'_\' . $sMethod . \'_afterEvent\';\n            if (is_callable($sPlugin)) {\n                call_user_func_array($sPlugin, $mixed);\n            }       \n        } \n    }\n\n} //end class Plugin\n\nclass Pluggable extends Plugin {\n} //end class Pluggable\n\n////////////////////\n// PART 2\n////////////////////\n\nclass Dog {\n\n    public $Name = \'\';\n\n    public function bark(&amp;$sHow) {\n        echo "$sHow&lt;br /&gt;\\n";\n    }\n\n    public function sayName() {\n        echo "&lt;br /&gt;\\nMy Name is: " . $this-&gt;Name . "&lt;br /&gt;\\n";\n    }\n\n\n} //end class Dog\n\n$Dog = new Dog();\n\n////////////////////\n// PART 3\n////////////////////\n\n$PDog = new Pluggable($Dog);\n\nfunction Dog_bark_beforeEvent(&amp;$mixed) {\n    $mixed = \'Woof\'; // Override saying \'meow\' with \'Woof\'\n    //$mixed = \'BLOCK_EVENT\'; // if you want to block the event\n    return $mixed;\n}\n\nfunction Dog_bark_afterEvent(&amp;$mixed) {\n    echo $mixed; // show the override\n}\n\nfunction Dog_Name_setEvent(&amp;$mixed) {\n    $mixed = \'Coco\'; // override \'Fido\' with \'Coco\'\n    return $mixed;\n}\n\nfunction Dog_Name_getEvent(&amp;$mixed) {\n    $mixed = \'Different\'; // override \'Coco\' with \'Different\'\n    return $mixed;\n}\n\n////////////////////\n// PART 4\n////////////////////\n\n$PDog-&gt;Name = \'Fido\';\n$PDog-&gt;Bark(\'meow\');\n$PDog-&gt;SayName();\necho \'My New Name is: \' . $PDog-&gt;Name;\n</code></pre>\n\n<p>In Part 1, that\'s what you might include with a <code>require_once()</code> call at the top of your PHP script. It loads the classes to make something pluggable.</p>\n\n<p>In Part 2, that\'s where we load a class. Note I didn\'t have to do anything special to the class, which is significantly different than the Observer pattern.</p>\n\n<p>In Part 3, that\'s where we switch our class around into being "pluggable" (that is, supports plugins that let us override class methods and properties). So, for instance, if you have a web app, you might have a plugin registry, and you could activate plugins here. Notice also the <code>Dog_bark_beforeEvent()</code> function. If I set <code>$mixed = \'BLOCK_EVENT\'</code> before the return statement, it will block the dog from barking and would also block the Dog_bark_afterEvent because there wouldn\'t be any event.</p>\n\n<p>In Part 4, that\'s the normal operation code, but notice that what you might think would run does not run like that at all. For instance, the dog does not announce it\'s name as \'Fido\', but \'Coco\'. The dog does not say \'meow\', but \'Woof\'. And when you want to look at the dog\'s name afterwards, you find it is \'Different\' instead of \'Coco\'. All those overrides were provided in Part 3.</p>\n\n<p>So how does this work? Well, let\'s rule out <code>eval()</code> (which everyone says is "evil") and rule out that it\'s not an Observer pattern. So, the way it works is the sneaky empty class called Pluggable, which does not contain the methods and properties used by the Dog class. Thus, since that occurs, the magic methods will engage for us. That\'s why in parts 3 and 4 we mess with the object derived from the Pluggable class, not the Dog class itself. Instead, we let the Plugin class do the "touching" on the Dog object for us. (If that\'s some kind of design pattern I don\'t know about -- please let me know.)</p>\n'}, {'answer_id': 16141818, 'author': 'Tim Groeneveld', 'author_id': 2143004, 'author_profile': 'https://Stackoverflow.com/users/2143004', 'pm_score': 3, 'selected': False, 'text': "<p>I am surprised that most of the answers here seem to be geared about plugins that are local to the web application, ie, plugins that run on the local web server.</p>\n\n<p>What about if you wanted the plugins to run on a different - remote - server? The best way to do this would be to provide a form that allows you to define different URLs that would be called when particular events occur in your application.</p>\n\n<p>Different events would send different information based on the event that just occurred.</p>\n\n<p>This way, you would just perform a cURL call to the URL that has been provided to your application (eg over https) where remote servers can perform tasks based on information that has been sent by your application.</p>\n\n<p>This provides two benefits:</p>\n\n<ol>\n<li>You don't have to host any code on your local server (security)</li>\n<li>The code can be on remote servers (extensibility) in different languages other then PHP (portability)</li>\n</ol>\n"}]
['https://Stackoverflow.com/questions/42', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/37/']