Completed
Pull Request — master (#4)
by Woody
04:10 queued 02:20
created

ServiceConfig::createDelegator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
crap 1
1
<?php
2
3
namespace Northwoods\Container\Config;
4
5
use ArrayObject;
6
use Auryn\Injector;
7
use Northwoods\Container\InjectorConfig;
8
use Psr\Container\ContainerInterface;
9
10
class ServiceConfig implements InjectorConfig
11
{
12
    /**
13
     * @var array
14
     */
15
    private $config;
16
17 1
    public function __construct(array $config)
18
    {
19 1
        $this->config = $config;
20 1
    }
21
22
    /**
23
     * @param Injector $injector
24
     * @return void
25
     */
26 1
    public function apply(Injector $injector)
27
    {
28
        // Aliases are exactly the same as aliases. Natch.
29 1
        if (isset($this->config['aliases'])) {
30 1
            $this->applyAliases($injector, $this->config['aliases']);
31
        }
32
33 1
        if (isset($this->config['delegators'])) {
34
            // Delegators are effectively a chain of prepare() statements.
35 1
            $this->applyDelegators($injector, $this->config['delegators']);
36
        }
37
38
        // Factories are exactly the same thing as a delegate.
39 1
        if (isset($this->config['factories'])) {
40 1
            $this->applyDelegates($injector, $this->config['factories']);
41
        }
42
43
        // Invokables are references to classes that have no constructor parameters.
44
        // This means nothing in Auryn so we just alias the reference.
45 1
        if (isset($this->config['invokables'])) {
46 1
            $this->applyAliases($injector, $this->config['invokables']);
47
        }
48
49
        // Services are already constructed instances of something. To handle this,
50
        // we simply wrap the instance in a callable that returns the instance.
51 1
        if (isset($this->config['services'])) {
52 1
            $this->applyDelegates($injector, $this->kAll($this->config['services']));
53
        }
54 1
    }
55
56
    /**
57
     * @param array $services
58
     * @return void
59
     */
60 1
    private function applyAliases(Injector $injector, array $services)
61
    {
62 1
        foreach ($services as $name => $object) {
63 1
            $injector->alias($name, $object);
64
        }
65 1
    }
66
67
    /**
68
     * @param array $services
69
     * @return void
70
     */
71 1
    private function applyDelegates(Injector $injector, array $services)
72
    {
73 1
        foreach ($services as $name => $object) {
74 1
            $injector->delegate($name, $object);
75
        }
76 1
    }
77
78
    /**
79
     * @param array $services
0 ignored issues
show
Bug introduced by
There is no parameter named $services. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
80
     * @return void
81
     */
82 1
    private function applyDelegators(Injector $injector, array $delegators)
83
    {
84
        // https://github.com/rdlowrey/auryn#prepares-and-setter-injection
85 1
        foreach ($delegators as $service => $prepares) {
86 1
            $injector->prepare($service, $this->createDelegator($service, $prepares));
87
        }
88 1
    }
89
90
    /**
91
     * Create a chained prepare()
92
     *
93
     * @param string $service
94
     * @param string[] $delegators
95
     * @return callable
96
     */
97 1
    private function createDelegator($service, array $delegators)
98
    {
99
        // Prepare the service by calling each delegator with the result of the previous.
100
        return function ($instance, $injector) use ($service, $delegators) {
101 1
            return array_reduce($delegators, $this->delegatorReducer($injector, $service), $instance);
102 1
        };
103
    }
104
105
    /**
106
     * Create a reducer for a chained prepare()
107
     *
108
     * @param string $service
109
     * @return callable
110
     */
111 1
    private function delegatorReducer(Injector $injector, $service)
112
    {
113
        // https://docs.zendframework.com/zend-expressive/features/container/delegator-factories/
114
        return function ($instance, $delegator) use ($injector, $service) {
115 1
            if (!is_callable($delegator)) {
116 1
                $delegator = $injector->make($delegator);
117
            }
118 1
            $callable = $this->k($instance);
119 1
            return $injector->execute($this->curryDelegator($delegator, $service, $callable));
1 ignored issue
show
Documentation introduced by
$delegator is of type *, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
120 1
        };
121
    }
122
123
    /**
124
     * Curry the delegator to only require a container
125
     *
126
     * @param callable $delegator that will be ultimately called
127
     * @param string $service name of service being prepared
128
     * @param callable $callable that returns the instance
129
     * @return callable
130
     */
131 1
    private function curryDelegator(callable $delegator, $service, callable $callable)
132
    {
133
        return static function (ContainerInterface $container) use ($delegator, $service, $callable) {
134 1
            return $delegator($container, $service, $callable);
135 1
        };
136
    }
137
138
    /**
139
     * Returns a function that always returns the same value
140
     *
141
     * Also known as a "kestrel" or "k combinator".
142
     *
143
     * @param mixed $x
144
     * @return callable
145
     */
146 1
    private function k($x)
147
    {
148
        return static function () use ($x) {
149 1
            return $x;
150 1
        };
151
    }
152
153
    /**
154
     * @param array $values
155
     * @return callable[]
156
     */
157 1
    private function kAll(array $values)
158
    {
159 1
        return array_map(
160 1
            function ($x) {
161 1
                return $this->k($x);
162 1
            },
163 1
            $values
164
        );
165
    }
166
}
167