parameter-resolvers.md
110 lines
| 1 | # Parameter resolvers |
| 2 | |
| 3 | Extending the behavior of the `Invoker` is easy and is done by implementing a [](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ParameterResolver.php`ParameterResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ParameterResolver.php](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ParameterResolver.php): |
| 4 | |
| 5 | ```php |
| 6 | interface ParameterResolver |
| 7 | { |
| 8 | public function getParameters( |
| 9 | ReflectionFunctionAbstract $reflection, |
| 10 | array $providedParameters, |
| 11 | array $resolvedParameters |
| 12 | ); |
| 13 | } |
| 14 | ``` |
| 15 | |
| 16 | - `$providedParameters` contains the parameters provided by the user when calling `$invoker->call($callable, $parameters)` |
| 17 | - `$resolvedParameters` contains parameters that have already been resolved by other parameter resolvers |
| 18 | |
| 19 | An `Invoker` can chain multiple parameter resolvers to mix behaviors, e.g. you can mix "named parameters" support with "dependency injection" support. This is why a `ParameterResolver` should skip parameters that are already resolved in `$resolvedParameters`. |
| 20 | |
| 21 | Here is an implementation example for dumb dependency injection that creates a new instance of the classes type-hinted: |
| 22 | |
| 23 | ```php |
| 24 | class MyParameterResolver implements ParameterResolver |
| 25 | { |
| 26 | public function getParameters( |
| 27 | ReflectionFunctionAbstract $reflection, |
| 28 | array $providedParameters, |
| 29 | array $resolvedParameters |
| 30 | ) { |
| 31 | foreach ($reflection->getParameters() as $index => $parameter) { |
| 32 | if (array_key_exists($index, $resolvedParameters)) { |
| 33 | // Skip already resolved parameters |
| 34 | continue; |
| 35 | } |
| 36 | |
| 37 | $class = $parameter->getClass(); |
| 38 | |
| 39 | if ($class) { |
| 40 | $resolvedParameters[$index] = $class->newInstance(); |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | return $resolvedParameters; |
| 45 | } |
| 46 | } |
| 47 | ``` |
| 48 | |
| 49 | To use it: |
| 50 | |
| 51 | ```php |
| 52 | $invoker = new Invoker\Invoker(new MyParameterResolver); |
| 53 | |
| 54 | $invoker->call(function (ArticleManager $articleManager) { |
| 55 | $articleManager->publishArticle('Hello world', 'This is the article content.'); |
| 56 | }); |
| 57 | ``` |
| 58 | |
| 59 | A new instance of `ArticleManager` will be created by our parameter resolver. |
| 60 | |
| 61 | ## Chaining parameter resolvers |
| 62 | |
| 63 | The fun starts to happen when we want to add support for many things: |
| 64 | |
| 65 | - named parameters |
| 66 | - dependency injection for type-hinted parameters |
| 67 | - ... |
| 68 | |
| 69 | This is where we should use the [](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ResolverChain.php`ResolverChain`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ResolverChain.php](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ResolverChain.php). This resolver implements the [](http://en.wikipedia.org/wiki/Chain-of-responsibility_patternChain of responsibility](http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern](http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) design pattern. |
| 70 | |
| 71 | For example the default chain is: |
| 72 | |
| 73 | ```php |
| 74 | $parameterResolver = new ResolverChain([ |
| 75 | new NumericArrayResolver, |
| 76 | new AssociativeArrayResolver, |
| 77 | new DefaultValueResolver, |
| 78 | ]); |
| 79 | ``` |
| 80 | |
| 81 | It allows to support even the weirdest use cases like: |
| 82 | |
| 83 | ```php |
| 84 | $parameters = []; |
| 85 | |
| 86 | // First parameter will receive "Welcome" |
| 87 | $parameters[] = 'Welcome'; |
| 88 | |
| 89 | // Parameter named "content" will receive "Hello world!" |
| 90 | $parameters['content'] = 'Hello world!'; |
| 91 | |
| 92 | // $published is not defined so it will use its default value |
| 93 | $invoker->call(function ($title, $content, $published = true) { |
| 94 | // ... |
| 95 | }, $parameters); |
| 96 | ``` |
| 97 | |
| 98 | We can put our custom parameter resolver in the list and created a super-duper invoker that also supports basic dependency injection: |
| 99 | |
| 100 | ```php |
| 101 | $parameterResolver = new ResolverChain([ |
| 102 | new MyParameterResolver, // Our resolver is at the top for highest priority |
| 103 | new NumericArrayResolver, |
| 104 | new AssociativeArrayResolver, |
| 105 | new DefaultValueResolver, |
| 106 | ]); |
| 107 | |
| 108 | $invoker = new Invoker\Invoker($parameterResolver); |
| 109 | ``` |
| 110 |