phata/hook

This package is not installable via Composer 1.x, please make sure you upgrade to Composer 2+. Read more about our Composer 1.x deprecation policy.

A minimalistic hook library based on PHP's Interface feature and PSR-11 container.

v1.1.0 2018-09-19 09:14 UTC

This package is auto-updated.

Last update: 2021-09-13 22:13:55 UTC


README

build status latest stable version total downloads total downloads

A minimalistic hook library based on PHP's Interface feature and PSR-11 container.

Why

Many CMS (like Drupal, Wordpress) are based on hook system for their modules. They usually work like this:

  1. The CMS declare the hooks somewhere in their documentations.
  2. And provide a way for modules to declare that they implemented any hook.
  3. The modules implement the hook(s) as documented.
  4. Somewhere in the CMS, when the hook is "invoked", all implementations from (3) are drawn out and run against certain data. Returned values are collected.

They usually also allow modules to declare their own hook. But they need to implement (4) on their own. CMS would provide helper API for invoking the hooks.

This library provide a way to do so in a modern manner.

Installation

The recommended method is using composer:

composer require "phata/hook"

Then you should be able to use it with composer's autoloading.

Example Usage

Basic invoker map and reduce ```php // path to composer's autoloader require_once '../vendor/autoload.php'; $container = new DI\Container; // or other PHP-11 compliant container $registry = new Phata\Hook\Registry; $invoker = new Phata\Hook\Invoker($registry, $container); // declare existing interfaces as hook. $registry->declareHooks([ App\MyInterface::class, SomeAwesomeLibrary\Interface::class, ]); // add class to the registry, which would // automatically index against the declared // hooks. $registry->add([ App\MyModule1::class, App\MyModule2::class, FooBar\External\Module::class, ]); // use $invoker to invoke a hook // Use 1: map results $array = $invoker->map( App\MyInterface::class, function (App\MyInterface $module) { return $module->doSomething(); } ); // Use 2: reduce $result = $invoker->reduce( App\MyInterface::class, function ($carry, App\MyInterface $module) { return $module->process($carry); }, $someInitialValue ); ```

Advanced Examples

Ordered modules with SortingInvoker ```php nameapace App; use Phata\Hook\SortableInterface; class MyModule1 implements SortableInterface, MyInterface { public function myProcess($input) { // some processsing of $input // ... return $output; } public function getWeight():int { // some logics to get the module's weight // perhapse from database operations // ... return $weight; } } ``` ```php // path to composer's autoloader require_once '../vendor/autoload.php'; $container = new DI\Container; // or other PHP-11 compliant container $registry = new Phata\Hook\Registry; $invoker = new Phata\Hook\SortingInvoker($registry, $container); // declare existing interfaces as hook. $registry->declareHooks([ Psr\Http\Server\MiddlewareInterface::class, ]); // add class to the registry, which would // automatically index against the declared // hooks. $registry->add([ App\MyModule1::class, App\MyModule2::class, FooBar\External\Module::class, ]); // handling the request // // Note: sorting invoker will sort the modules by their weight // before invoking. $results = $invoker->map( App\MyInterface::class, function (App\MyInterface $module) use ($input) { return $module->process($input); } ); // use the $results //... ```
Consuming PSR-7 request with PSR-15 middleware ```php // path to composer's autoloader require_once '../vendor/autoload.php'; $container = new DI\Container; // or other PHP-11 compliant container $registry = new Phata\Hook\Registry; $invoker = new Phata\Hook\Invoker($registry, $container); // declare existing interfaces as hook. $registry->declareHooks([ Psr\Http\Server\MiddlewareInterface::class, ]); // add class to the registry, which would // automatically index against the declared // hooks. $registry->add([ App\MyModule1::class, App\MyModule2::class, FooBar\External\Module::class, ]); // handling the request $request = $invoker->reduce( Psr\Http\Server\MiddlewareInterface::class, function ($request, Psr\Http\Server\MiddlewareInterface::class $middleware) { return $middleware->process($request); }, GuzzleHttp\Psr7\ServerRequest::fromGlobals() ); // handler handles the request //... ```
Reduce PSR-7 Request with `$carry` aggregation ```php // path to composer's autoloader require_once '../vendor/autoload.php'; $container = new DI\Container; // or other PHP-11 compliant container $registry = new Phata\Hook\Registry; $invoker = new Phata\Hook\Invoker($registry, $container); // declare existing interfaces as hook. $registry->declareHooks([ App\Middleware\Interface::class, ]); // add class to the registry, which would // automatically index against the declared // hooks. $registry->add([ App\MyModule1::class, App\MyModule2::class, FooBar\External\Module::class, ]); // handling the request $request = GuzzleHttp\Psr7\ServerRequest::fromGlobals(); $result = $invoker->reduce( App\Middleware\Interface::class, function ($carry, App\Middleware\Interface::class $middleware) use ($request) { $middleware->process($carry, $request); return $carry; }, null ); ```

Documentation

Class and type documentation of Phata\Hook can be found here.

License

Phata\Hook is distributed under the MIT License. You should have received a copy of the GNU Lesser General Public License along with Phata/Hook. If not, see https://opensource.org/licenses/MIT.