Completed
Pull Request — master (#12)
by Woody
05:04
created

Config::apply()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 1
crap 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Northwoods\Container\Zend;
5
6
use ArrayObject;
7
use Auryn\Injector;
8
use Northwoods\Container\ContainerException;
9
use Northwoods\Container\InjectorConfig;
10
use Psr\Container\ContainerInterface;
11
12
class Config implements InjectorConfig
13
{
14
    /** @var array */
15
    private $config;
16
17
    /** @var bool */
18
    private $sharedByDefault = true;
19
20 235
    public function __construct(array $config)
21
    {
22 235
        $this->config = $config;
23 235
    }
24
25
    /**
26
     * Configure the injector using Zend Service Manager format
27
     */
28 235
    public function apply(Injector $injector): void
29
    {
30 235
        $dependencies = $this->config['dependencies'] ?? [];
31
32
        // Define the "config" service, accounting for the fact that Auryn
33
        // requires all returns are objects.
34 235
        $dependencies['services']['config'] = new ArrayObject($this->config, ArrayObject::ARRAY_AS_PROPS);
35
36 235
        $this->injectServices($injector, $dependencies);
37 235
        $this->injectFactories($injector, $dependencies);
38 235
        $this->injectInvokables($injector, $dependencies);
39 235
        $this->injectAliases($injector, $dependencies);
40 235
    }
41
42 235 View Code Duplication
    private function injectAliases(Injector $injector, array $dependencies): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
43
    {
44 235
        $aliases = $dependencies['aliases'] ?? [];
45 235
        foreach ($aliases as $alias => $target) {
46
            // Standard Auryn aliases do not work when chained. Work around by
47
            // lazily fetching the shared target from the container.
48 132
            $injector->share($alias)->share($target)->delegate($alias, $this->makeContainerGet($target));
49
        }
50 235
    }
51
52 235
    private function injectFactories(Injector $injector, array $dependencies): void
53
    {
54 235
        $factories = $dependencies['factories'] ?? [];
55 235
        foreach ($factories as $name => $factory) {
56 150
            $delegate = $this->makeFactory($injector, $name, $factory);
57 150 View Code Duplication
            if (isset($dependencies['delegators'][$name])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
58 96
                $delegate = $this->makeDelegator(
59 96
                    $injector,
60 96
                    $name,
61 96
                    $delegate,
62 96
                    $dependencies['delegators'][$name]
63
                );
64
            }
65 150
            $injector->share($name)->delegate($name, $delegate);
66
        }
67 235
    }
68
69 235
    private function injectInvokables(Injector $injector, array $dependencies): void
70
    {
71 235
        $invokables = $dependencies['invokables'] ?? [];
72 235
        foreach ($invokables as $alias => $invokable) {
73 77
            if (is_string($alias) && $alias !== $invokable) {
74 29
                $injector->alias($alias, $invokable);
75
            }
76 77
            $delegate = $this->makeLazyInvokable($invokable, $invokable);
77 77 View Code Duplication
            if (isset($dependencies['delegators'][$invokable])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
78 49
                $delegate = $this->makeDelegator(
79 49
                    $injector,
80 49
                    $invokable,
81 49
                    $delegate,
82 49
                    $dependencies['delegators'][$invokable]
83
                );
84
            }
85 77
            $injector->share($invokable)->delegate($invokable, $delegate);
86
        }
87 235
    }
88
89 235 View Code Duplication
    private function injectServices(Injector $injector, array $dependencies): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
90
    {
91 235
        $services = $dependencies['services'] ?? [];
92 235
        foreach ($services as $name => $service) {
93 235
            $injector->share($name)->delegate($name, $this->makeIdentity($service));
94
        }
95 235
    }
96
97
    private function makeDelegator(Injector $injector, string $name, callable $callback, array $delegators): callable
98
    {
99 145
        return /** @return object */ function () use ($injector, $name, $callback, $delegators) {
100 145
            foreach ($delegators as $delegator) {
101 127
                $container = $injector->make(ContainerInterface::class);
102 127
                $delegator = $this->makeInvokable($name, $delegator);
103 73
                $instance = $delegator($container, $name, $callback);
104 53
                $callback = $this->makeIdentity($instance);
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $callback, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
105
            }
106 71
            return $instance ?? $callback();
107 145
        };
108
    }
109
110
    private function makeContainerGet(string $name): callable
111
    {
112 132
        return /** @return object */ static function (ContainerInterface $container) use ($name) {
113 132
            return $container->get($name);
114 132
        };
115
    }
116
117
    /**
118
     * @param string|callable $factory
119
     */
120
    private function makeFactory(Injector $injector, string $name, $factory): callable
121
    {
122 150
        return /** @return object */ function () use ($injector, $name, $factory) {
123 102
            $container = $injector->make(ContainerInterface::class);
124 102
            $factory = $this->makeInvokable($name, $factory);
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $factory, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
125 78
            return $factory($container, $name);
126 150
        };
127
    }
128
129
    /**
130
     * @param object $object
131
     */
132
    private function makeIdentity($object): callable
133
    {
134 235
        return /** @return object */ static function () use ($object) {
135 27
            return $object;
136 235
        };
137
    }
138
139
    /**
140
     * @param string|callable $factory
141
     * @throws ContainerException if the factory cannot be made invokable
142
     */
143 223
    private function makeInvokable(string $name, $factory): callable
144
    {
145 223
        if ($this->isValidClass($factory)) {
146 159
            $factory = new $factory();
147
        }
148
149 201
        if (is_callable($factory)) {
150 153
            return $factory;
151
        }
152
153 62
        throw ContainerException::expectedInvokable($name);
154
    }
155
156
    /**
157
     * @param string|callable $factory
158
     */
159
    private function makeLazyInvokable(string $name, $factory): callable
160
    {
161 77
        return /** @return callable */ function () use ($name, $factory) {
162 47
            return $this->makeInvokable($name, $factory);
163 77
        };
164
    }
165
166
    /**
167
     * @param mixed $factory
168
     */
169 223
    private function isValidClass($factory): bool
170
    {
171 223
        return is_string($factory) && class_exists($factory);
172
    }
173
}
174