Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jorgecolonconsulting/hooky
Easily and reliably allow your classes to be extendable without allowing people to touch your core code
https://github.com/jorgecolonconsulting/hooky
Last synced: 24 days ago
JSON representation
Easily and reliably allow your classes to be extendable without allowing people to touch your core code
- Host: GitHub
- URL: https://github.com/jorgecolonconsulting/hooky
- Owner: jorgecolonconsulting
- License: mit
- Created: 2014-11-07T07:56:02.000Z (almost 10 years ago)
- Default Branch: master
- Last Pushed: 2014-11-12T22:32:57.000Z (almost 10 years ago)
- Last Synced: 2024-04-05T18:22:02.113Z (7 months ago)
- Language: PHP
- Size: 199 KB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
[![Build Status](https://travis-ci.org/2upmedia/hooky.svg)](https://travis-ci.org/2upmedia/hooky)
# Hooky
Easily and reliably allow your classes to be hooked into without allowing people to touch your core code. Some inspiration came from Aspect Oriented Programming.
## Audience
Authors of packages or developers extending existing packages and anyone that needs to allow hooks for core code they can modify.
## Uses
- Logging, triggering events, analytics
- When you need to do something consistently before or after a method call: custom authenticating of an HTTP call, transforming data after it's been received
- Whatever you make of it## Features
- Hooking to specific instances
- Global hooks for all instances of a specific class
- Hooking to constructors
- Dynamic analysis to catch bugs before they happen: method name checks, mismatched parameters, restricted methods
- PSR2 and PSR4
- 99% code coverage thanks to TDD
- Perfectly-spaced, hand-crafted PHPDocs
- Semantic Versioning (currently in initial development phase)## Installing via Composer
The recommended way to install Hooky is through
[Composer](http://getcomposer.org).```bash
# Install Composer
curl -sS https://getcomposer.org/installer | php
```Next, run the Composer command to install the latest stable version of Hooky:
```bash
composer require 2upmedia/hooky
```After installing, you need to require Composer's autoloader:
```php
require 'vendor/autoload.php';
```## Examples
```php
require 'vendor/autoload.php';class HelloWorld
{
use _2UpMedia\Hooky\HooksTrait;public function sayIt()
{
$this->callBeforeHooks($this, __METHOD__);
}
}HelloWorld::globalBeforeAllHook(function() {
echo 'hello world';
});(new HelloWorld())->sayIt(); // echos hello world
```
## Check out demos with well-known packageshttps://github.com/2upmedia/hooky-demos
## Documentation
### Making your classes hooky-compatible
#### Hook executors
There's two core actions: before and after. You choose what if want to allow one, both, or either/or.
`$this->callBeforeHooks()` allows ALL before-type hooks: beforeAll, onceBeforeAll, before{Method}, and onceBefore{Method}
`$this->callAfterHooks()` does the same but for after-type hooks
### Controlling actions
You can control the types of hooks you will allow per method by using the specific call method for the action you want to allow.
For instance, if you don't want to allow beforeAll hooks for your method, but you want to allow people to hook before your method you can call `$this->callAfterMethodHooks()`.#### Allowing return values from callables
Early returns are possible using the following format right before your core method code:
```php
public function foo($methodParameter){
if (($hookReturn = $this->callBeforeHooks($this, __METHOD__, [$methodParameter])) !== null) {
return $hookReturn;
}
```
**Note:** The first applicable registered hook that returns a value will cancel all successively registered hooks for that method.#### Allowing parameters to be overridden
**NOTE:** use with care
Just send parameters as references
```php
// method hook
public function foo($methodParameter){
if (($hookReturn = $this->callBeforeHooks($this, __METHOD__, [&$methodParameter])) !== null) {
return $hookReturn;
}// in your callable you also mark the parameter as a reference
$bar->afterFooHook(function (&$methodParameter) {
$methodParameter = 'Some other value';
});
```
#### Handling returned null valuesReturn the special NULL constant and also make sure to use `hookReturn()` when you set up your hooks.
```php
// method hook
public function foo($methodParameter){
if (($hookReturn = $this->callBeforeHooks($this, __METHOD__, [&$methodParameter])) !== null) {
return $this->hookReturn($hookReturn);
}// in your callable
use _2UpMedia\Hooky\Constants;$bar->afterFooHook(function ($methodParameter) {
return Constants::NULL;
});
```
#### Allowing original return value to be manipulated in your after hooks**NOTE:** use with care
```php
// method hook
public function foo($methodParameter){
// do some awesome stuff with $methodParameter;
$return = $this->wowwow($methodParameter); // split core return value into a variable
if (($hookReturn = $this->callAfterHooks($this, __METHOD__, [$methodParameter, $return])) !== null) {
return $this->hookReturn($hookReturn);
}
return $return; // very important in case you don't have any hooks returning values
```#### Cancelling propagation
```php
// in your callable
use _2UpMedia\Hooky\CancelPropagationException;$bar->afterFooHook(function ($methodParameter) {
throw new CancelPropagationException('buahahahaha!');
});$bar->afterFooHook(function ($methodParameter) {
// this one never gets called
});
```### Default behavior
To reduce the risk of hooks breaking because of the underlying libraries being refactored, the decision was made to restrict hooking to concrete public methods of interface methods or abstract methods as the default. Interfaces and abstracts are usually well thought out and usually don't change as often as they're supposed to be contracts, and thus any hooks relying on those methods would break much less frequently, if not at all.
This default behavior can be changed. See the Changing Default Behavior section.
### Changing Default BehaviorYou can restrict hooking to the following types of methods using `setDefaultAccessibility()`: public, protected, private, and/or abstract/interface method.
```php
use _2UpMedia\Hooky\Constants;// allow hooking to public methods
$this->setDefaultAccessibility(Constants::PUBLIC_ACCESSIBLE);// allow hooking to public and protected methods
$this->setDefaultAccessibility(Constants::PUBLIC_ACCESSIBLE | Constants::PROTECTED_ACCESSIBLE);// allow hooking ONLY to public and protected abstract methods
$this->setDefaultAccessibility(Constants::PUBLIC_ACCESSIBLE | Constants::PROTECTED_ACCESSIBLE | Constants::ABSTRACT_ONLY );
```
### Class-wide hooksGlobal hooks are called statically by the name of the class that's using the HooksTrait. Global hooks are called for all instances.
The format is: `{ClassName}::global{Action}[ {Method} ]Hook`
- `{ClassName}::globalBeforeAllHook`
- `{ClassName}::globalOnceBeforeAllHook`
- `{ClassName}::globalBefore{Method}Hook`
- `{ClassName}::globalOnceBefore{Method}Hook`
- `{ClassName}::globalAfterAllHook`
- `{ClassName}::globalOnceAfterAllHook`
- `{ClassName}::globalAfter{Method}Hook`
- `{ClassName}::globalOnceAfter{Method}Hook````php
Bar::globalBeforeAllHook(function ($) {
var_dump(
});$bar->foo();
```### Instance-specific hooks
Instance-specific hooks are called as methods of an instance and these hooks are not called globally.
The format is: `{$instanceVariable}->{action}[ {Method} ]Hook`
- `{$instanceVariable}->beforeAllHook`
- `{$instanceVariable}->onceBeforeAllHook`
- `{$instanceVariable}->before{Method}Hook`
- `{$instanceVariable}->onceBefore{Method}Hook`
- `{$instanceVariable}->afterAllHook`
- `{$instanceVariable}->onceAfterAllHook`
- `{$instanceVariable}->after{Method}Hook`
- `{$instanceVariable}->onceAfter{Method}Hook`### Check potential bugs before going out to production
The `{ClassName}::$checkCallableParameters` flag triggers PHP Notices on different issues where method parameters don't match up with the callables set up through hooks. This is handy during development to catch potential bugs. By default it is turned on to reduce bugs immediately. Turn it off in a production environment.
### Restricting hooking to specific methods
In the `__construct()` of your concrete class call `$this->setHookableMethods(['methodName1', 'methodName2'])`.
### Working with existing `__destruct()`
Because of the nature of Traits, `__destruct()` will be overridden if the implemented class already has them. The workaround is to use an alias.
```php
use \_2UpMedia\Hooky\HooksTrait { __destruct as traitDestruct; }public function __destruct()
{
$this->traitDestruct();
```keywords: aop, hook, event, pub, sub