Dependencies   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 159
Duplicated Lines 0 %

Test Coverage

Coverage 98.73%

Importance

Changes 0
Metric Value
wmc 18
eloc 75
dl 0
loc 159
ccs 78
cts 79
cp 0.9873
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A build() 0 3 1
A decorate() 0 11 2
A __construct() 0 12 1
A feed() 0 9 1
A get() 0 13 3
A extract() 0 11 2
A exposed() 0 8 1
A lazy() 0 8 2
A bind() 0 12 1
A assertNoCircularDependency() 0 20 3
A compile() 0 8 1
1
<?php
2
declare(strict_types = 1);
3
4
namespace Innmind\Compose;
5
6
use Innmind\Compose\{
7
    Definition\Dependency,
8
    Definition\Name,
9
    Definition\Service,
10
    Definition\Service\Argument,
11
    Exception\ReferenceNotFound,
12
    Exception\NameNotNamespaced,
13
    Exception\CircularDependency,
14
    Exception\LogicException,
15
    Compilation\Dependencies as CompiledDependencies
16
};
17
use Innmind\Immutable\{
18
    Sequence,
19
    MapInterface,
20
    Map,
21
    StreamInterface
22
};
23
24
final class Dependencies
25
{
26
    private $dependencies;
27
28 212
    public function __construct(Dependency ...$dependencies)
29
    {
30 212
        $this->dependencies = Sequence::of(...$dependencies)->reduce(
31 212
            new Map('string', Dependency::class),
32 212
            static function(Map $dependencies, Dependency $dependency): Map {
33 38
                return $dependencies->put(
34 38
                    (string) $dependency->name(),
35 38
                    $dependency
36
                );
37 212
            }
38
        );
39 212
        $this->assertNoCircularDependency();
40 212
    }
41
42 8
    public function feed(Name $name, Services $services): self
43
    {
44 8
        $self = clone $this;
45 8
        $self->dependencies = $self->dependencies->put(
46 8
            (string) $name,
47 8
            $self->dependencies->get((string) $name)->bind($services)
48
        );
49
50 8
        return $self;
51
    }
52
53 37
    public function bind(Services $services): Services
54
    {
55
        return $this
56 37
            ->dependencies
57 37
            ->values()
58 37
            ->sort(static function(Dependency $a, Dependency $b): bool {
59 1
                return $a->dependsOn($b);
60 37
            })
61 37
            ->reduce(
62 37
                $services,
63 37
                static function(Services $services, Dependency $dependency): Services {
64 6
                    return $services->feed($dependency->name());
65 37
                }
66
            );
67
    }
68
69 31
    public function lazy(Name $name): Lazy
70
    {
71
        try {
72
            return $this
73 31
                ->get($name)
74 15
                ->lazy($name->withoutRoot());
75 24
        } catch (ReferenceNotFound $e) {
76 24
            throw new ReferenceNotFound((string) $name, 0, $e);
77
        }
78
    }
79
80 15
    public function build(Name $name): object
81
    {
82 15
        return $this->lazy($name)->load();
83
    }
84
85 13
    public function decorate(
86
        Name $decorator,
87
        Name $decorated,
88
        Name $newName = null
89
    ): Service {
90
        try {
91
            return $this
92 13
                ->get($decorator)
93 11
                ->decorate($decorator->withoutRoot(), $decorated, $newName);
94 3
        } catch (ReferenceNotFound $e) {
95 3
            throw new ReferenceNotFound((string) $decorator, 0, $e);
96
        }
97
    }
98
99
    /**
100
     * @param StreamInterface<mixed> $arguments
101
     *
102
     * @return StreamInterface<mixed>
103
     */
104 7
    public function extract(
105
        Name $name,
106
        StreamInterface $arguments,
107
        Argument $argument
108
    ): StreamInterface {
109
        try {
110
            return $this
111 7
                ->get($name)
112 5
                ->extract($name->withoutRoot(), $arguments, $argument);
113 4
        } catch (ReferenceNotFound $e) {
114 4
            throw new ReferenceNotFound((string) $name, 0, $e);
115
        }
116
    }
117
118 47
    private function get(Name $name): Dependency
119
    {
120
        try {
121 47
            $root = $name->root();
122 20
        } catch (NameNotNamespaced $e) {
123 20
            throw new ReferenceNotFound((string) $name);
124
        }
125
126 33
        if (!$this->dependencies->contains((string) $root)) {
127 7
            throw new ReferenceNotFound((string) $name);
128
        }
129
130 29
        return $this->dependencies->get((string) $root);
131
    }
132
133
    /**
134
     * Return the list of exposed services per dependency
135
     *
136
     * @return MapInterface<Name, MapInterface<Name, Constructor>>
137
     */
138 2
    public function exposed(): MapInterface
139
    {
140 2
        return $this->dependencies->reduce(
141 2
            new Map(Name::class, MapInterface::class),
142 2
            static function(Map $exposed, string $name, Dependency $dependency): Map {
143 2
                return $exposed->put(
144 2
                    $dependency->name(),
145 2
                    $dependency->exposed()
146
                );
147 2
            }
148
        );
149
    }
150
151 8
    public function compile(): CompiledDependencies
152
    {
153 8
        return new CompiledDependencies(
154
            ...$this
155 8
                ->dependencies
156 8
                ->values()
157 8
                ->sort(static function(Dependency $a, Dependency $b): bool {
158
                    return $a->dependsOn($b);
159 8
                })
160
        );
161
    }
162
163
    private function assertNoCircularDependency(): void
164
    {
165 212
        $this->dependencies->foreach(function(string $name, Dependency $dependency): void {
166
            $this
167 38
                ->dependencies
168 38
                ->remove($name)
169 38
                ->foreach(static function(string $name, Dependency $other) use ($dependency): void {
170 4
                    if (!$dependency->dependsOn($other)) {
171 4
                        return;
172
                    }
173
174 2
                    if (!$other->dependsOn($dependency)) {
175 1
                        return;
176
                    }
177
178 1
                    throw new CircularDependency(sprintf(
179 1
                        '%s -> %s -> %s',
180 1
                        $dependency->name(),
181 1
                        $other->name(),
182 1
                        $dependency->name()
183
                    ));
184 38
                });
185 212
        });
186 212
    }
187
}
188