Add doc
This commit is contained in:
parent
0ac247550b
commit
7bfc3c0ae0
1 changed files with 149 additions and 2 deletions
|
@ -5,8 +5,155 @@ Friendica strategy and decorator Hooks
|
|||
|
||||
## Strategy hooks
|
||||
|
||||
This type of hook is based on the [Strategy Design Pattern](https://refactoring.guru/design-patterns/strategy).
|
||||
|
||||
A strategy class defines a possible implementation of a given interface based on a unique name.
|
||||
Every name is possible as long as it's unique and not `null`.
|
||||
Using an empty name (`''`) is possible as well and should be used as the "default" implementation.
|
||||
To register a strategy, use the [`ICanRegisterInstance`](../src/Core/Hooks/Capabilities/ICanRegisterInstances.php) interface.
|
||||
|
||||
After registration, a caller can automatically create this instance with the [`ICanCreateInstances`](../src/Core/Hooks/Capabilities/ICanCreateInstances.php) interface and the chosen name.
|
||||
|
||||
This is useful in case there are different, possible implementations for the same purpose, like for logging, locking, caching, ...
|
||||
|
||||
Normally, a config entry is used to choose the right implementation at runtime.
|
||||
And if no config entry is set, the "default" implementation should be used.
|
||||
|
||||
### Example
|
||||
|
||||
```php
|
||||
interface ExampleInterface
|
||||
{
|
||||
public function testMethod();
|
||||
}
|
||||
|
||||
public class ConcreteClassA implements ExampleInterface
|
||||
{
|
||||
public function testMethod()
|
||||
{
|
||||
echo "concrete class A";
|
||||
}
|
||||
}
|
||||
|
||||
public class ConcreteClassB implements ExampleInterface
|
||||
{
|
||||
public function testMethod()
|
||||
{
|
||||
echo "concrete class B";
|
||||
}
|
||||
}
|
||||
|
||||
/** @var \Friendica\Core\Hooks\Capabilities\ICanRegisterInstances $instanceRegister */
|
||||
$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassA::class, 'A');
|
||||
$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassB::class, 'B');
|
||||
|
||||
/** @var \Friendica\Core\Hooks\Capabilities\ICanCreateInstances $instanceManager */
|
||||
/** @var ConcreteClassA $concreteClass */
|
||||
$concreteClass = $instanceManager->createWithName(ExampleInterface::class, 'A');
|
||||
|
||||
$concreteClass->testMethod();
|
||||
// output:
|
||||
// "concrete class A";
|
||||
```
|
||||
|
||||
## Decorator hooks
|
||||
|
||||
## hook.config.php
|
||||
This type of hook is based on the [Decorator Design Pattern](https://refactoring.guru/design-patterns/decorator).
|
||||
|
||||
## Create new hooks
|
||||
A decorator class extends a given strategy instance (see [Strategy hooks](#strategy-hooks)]).
|
||||
To register a decorator, use the [`ICanRegisterInstance`](../src/Core/Hooks/Capabilities/ICanRegisterInstances.php) interface.
|
||||
|
||||
After registration, a caller can automatically create an instance with the [`ICanCreateInstances`](../src/Core/Hooks/Capabilities/ICanCreateInstances.php) interface and the decorator will wrap its logic around the call.
|
||||
|
||||
This is useful in case you want to extend a given class but the given class isn't responsible for these business logic. Or you want to extend an interface without knowing the concrete implementation.
|
||||
For example profiling logger calls, Friendica is using a [`ProfilerLogger`](../src/Core/Logger/Type/ProfilerLogger.php), which wraps all other logging implementations and traces each log call.
|
||||
|
||||
Normally, a config entry is used to enable/disable decorator.
|
||||
|
||||
### Example
|
||||
|
||||
```php
|
||||
interface ExampleInterface
|
||||
{
|
||||
public function testMethod();
|
||||
}
|
||||
|
||||
public class ConcreteClassA implements ExampleInterface
|
||||
{
|
||||
public function testMethod()
|
||||
{
|
||||
echo "concrete class A";
|
||||
}
|
||||
}
|
||||
|
||||
public class DecoratorClassA implements ExampleInterface
|
||||
{
|
||||
/** @var ExampleInterface */
|
||||
protected $example;
|
||||
|
||||
public function __construct(ExampleInterface $example)
|
||||
{
|
||||
$this->example = $example;
|
||||
}
|
||||
|
||||
public function testMethod()
|
||||
{
|
||||
echo "decorated!\n";
|
||||
$this->example->testMethod();
|
||||
}
|
||||
}
|
||||
|
||||
/** @var \Friendica\Core\Hooks\Capabilities\ICanRegisterInstances $instanceRegister */
|
||||
$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassA::class, 'A');
|
||||
$instanceRegister->registerDecorator(ExampleInterface::class, DecoratorClassA::class);
|
||||
|
||||
/** @var \Friendica\Core\Hooks\Capabilities\ICanCreateInstances $instanceManager */
|
||||
/** @var ConcreteClassA $concreteClass */
|
||||
$concreteClass = $instanceManager->createWithName(ExampleInterface::class, 'A');
|
||||
|
||||
$concreteClass->testMethod();
|
||||
// output:
|
||||
// "decorated!"
|
||||
// "concrete class A";
|
||||
```
|
||||
|
||||
## hooks.config.php
|
||||
|
||||
To avoid registering all strategies and decorators manually inside the code, Friendica introduced the [`hooks.config.php`](../static/hooks.config.php) file.
|
||||
|
||||
There, you can register all kind of strategies and decorators in one file.
|
||||
|
||||
### [`HookType::STRATEGY`](../src/Core/Hooks/Capabilities/HookType.php)
|
||||
|
||||
For each given interface, a list of key-value pairs can be set, where the key is the concrete implementation class and the value is an array of unique names.
|
||||
|
||||
### [`HookType::DECORATOR`](../src/Core/Hooks/Capabilities/HookType.php)
|
||||
|
||||
For each given interface, a list of concrete decorator classes can be set.
|
||||
|
||||
### Example
|
||||
|
||||
```php
|
||||
use Friendica\Core\Hooks\Capabilities\HookType as H;
|
||||
|
||||
return [
|
||||
H::STRATEGY => [
|
||||
ExampleInterface::class => [
|
||||
ConcreteClassA::class => ['A'],
|
||||
ConcreteClassB::class => ['B'],
|
||||
],
|
||||
],
|
||||
H::DECORATOR => [
|
||||
ExampleInterface::class => [
|
||||
DecoratorClassA::class,
|
||||
],
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
## Addons
|
||||
|
||||
The hook logic is useful for decoupling the Friendica core logic, but its primary goal is to modularize Friendica in creating addons.
|
||||
|
||||
Therefor you can either use the interfaces directly as shown above, or you can place your own `hooks.config.php` file inside a `static` directory directly under your addon core directory.
|
||||
Friendica will automatically search these config files for each **activated** addon and register the given hooks.
|
||||
|
|
Loading…
Reference in a new issue