Config::makeContainerGet()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
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 235
    public function __construct(array $config)
18
    {
19 235
        $this->config = $config;
20 235
    }
21
22
    /**
23
     * Configure the injector using Zend Service Manager format
24
     */
25 235
    public function apply(Injector $injector): void
26
    {
27 235
        $dependencies = $this->config['dependencies'] ?? [];
28
29
        // Define the "config" service, accounting for the fact that Auryn
30
        // requires all returns are objects.
31 235
        $dependencies['services']['config'] = new ArrayObject($this->config, ArrayObject::ARRAY_AS_PROPS);
32
33 235
        $this->injectServices($injector, $dependencies);
34 235
        $this->injectFactories($injector, $dependencies);
35 235
        $this->injectInvokables($injector, $dependencies);
36 235
        $this->injectAliases($injector, $dependencies);
37 235
    }
38
39 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...
40
    {
41 235
        $aliases = $dependencies['aliases'] ?? [];
42 235
        foreach ($aliases as $alias => $target) {
43
            // Standard Auryn aliases do not work when chained. Work around by
44
            // lazily fetching the shared target from the container.
45 132
            $injector->share($alias)->share($target)->delegate($alias, $this->makeContainerGet($target));
46
        }
47 235
    }
48
49 235
    private function injectFactories(Injector $injector, array $dependencies): void
50
    {
51 235
        $factories = $dependencies['factories'] ?? [];
52 235
        foreach ($factories as $name => $factory) {
53 150
            $delegate = $this->makeFactory($injector, $name, $factory);
54 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...
55 96
                $delegate = $this->makeDelegator(
56 96
                    $injector,
57 96
                    $name,
58 96
                    $delegate,
59 96
                    $dependencies['delegators'][$name]
60
                );
61
            }
62 150
            $injector->share($name)->delegate($name, $delegate);
63
        }
64 235
    }
65
66 235
    private function injectInvokables(Injector $injector, array $dependencies): void
67
    {
68 235
        $invokables = $dependencies['invokables'] ?? [];
69 235
        foreach ($invokables as $alias => $invokable) {
70 77
            if (is_string($alias) && $alias !== $invokable) {
71 29
                $injector->alias($alias, $invokable);
72
            }
73 77
            $delegate = $this->makeLazyInvokable($invokable, $invokable);
74 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...
75 49
                $delegate = $this->makeDelegator(
76 49
                    $injector,
77 49
                    $invokable,
78 49
                    $delegate,
79 49
                    $dependencies['delegators'][$invokable]
80
                );
81
            }
82 77
            $injector->share($invokable)->delegate($invokable, $delegate);
83
        }
84 235
    }
85
86 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...
87
    {
88 235
        $services = $dependencies['services'] ?? [];
89 235
        foreach ($services as $name => $service) {
90 235
            $injector->share($name)->delegate($name, $this->makeIdentity($service));
91
        }
92 235
    }
93
94 145
    private function makeDelegator(Injector $injector, string $name, callable $callback, array $delegators): callable
95
    {
96
        return /** @return object */ function () use ($injector, $name, $callback, $delegators) {
97 145
            foreach ($delegators as $delegator) {
98 127
                $container = $injector->make(ContainerInterface::class);
99 127
                $delegator = $this->makeInvokable($name, $delegator);
100 73
                $instance = $delegator($container, $name, $callback);
101 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...
102
            }
103 71
            return $instance ?? $callback();
104 145
        };
105
    }
106
107 132
    private function makeContainerGet(string $name): callable
108
    {
109
        return /** @return object */ static function (ContainerInterface $container) use ($name) {
110 132
            return $container->get($name);
111 132
        };
112
    }
113
114
    /**
115
     * @param string|callable $factory
116
     */
117 150
    private function makeFactory(Injector $injector, string $name, $factory): callable
118
    {
119
        return /** @return object */ function () use ($injector, $name, $factory) {
120 102
            $container = $injector->make(ContainerInterface::class);
121 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...
122 78
            return $factory($container, $name);
123 150
        };
124
    }
125
126
    /**
127
     * @param object $object
128
     */
129 235
    private function makeIdentity($object): callable
130
    {
131
        return /** @return object */ static function () use ($object) {
132 27
            return $object;
133 235
        };
134
    }
135
136
    /**
137
     * @param string|callable $factory
138
     * @throws ContainerException if the factory cannot be made invokable
139
     */
140 223
    private function makeInvokable(string $name, $factory): callable
141
    {
142 223
        if (is_callable($factory)) {
143 65
            return $factory;
144
        }
145
146 178
        if ($this->isValidClass($factory)) {
147 150
            $factory = new $factory();
148
        }
149
150 156
        if (is_callable($factory) === false) {
151 62
            throw ContainerException::expectedInvokable($name);
152
        }
153
154 108
        return $factory;
155
    }
156
157
    /**
158
     * @param string|callable $factory
159
     */
160 77
    private function makeLazyInvokable(string $name, $factory): callable
161
    {
162
        return /** @return callable */ function () use ($name, $factory) {
163 47
            return $this->makeInvokable($name, $factory);
164 77
        };
165
    }
166
167
    /**
168
     * @param mixed $factory
169
     */
170 178
    private function isValidClass($factory): bool
171
    {
172 178
        return is_string($factory) && class_exists($factory);
173
    }
174
}
175