Completed
Push — master ( a5c4b8...28cb8b )
by Baptiste
02:14
created

Dependencies::extract()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 3
dl 0
loc 11
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
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 36
                return $dependencies->put(
34 36
                    (string) $dependency->name(),
35 36
                    $dependency
36
                );
37 212
            }
38
        );
39 212
        $this->assertNoCircularDependency();
40 212
    }
41
42 7
    public function feed(Name $name, Services $services): self
43
    {
44 7
        $self = clone $this;
45 7
        $self->dependencies = $self->dependencies->put(
46 7
            (string) $name,
47 7
            $self->dependencies->get((string) $name)->bind($services)
48
        );
49
50 7
        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) use ($services): bool {
59 1
                if ($a->need($services)) {
60
                    return true;
61
                }
62
63 1
                return $a->dependsOn($b);
64 37
            })
65 37
            ->reduce(
66 37
                $services,
67 37
                static function(Services $services, Dependency $dependency): Services {
68 5
                    return $services->feed($dependency->name());
69 37
                }
70
            );
71
    }
72
73 31
    public function lazy(Name $name): Lazy
74
    {
75
        try {
76
            return $this
77 31
                ->get($name)
78 15
                ->lazy($name->withoutRoot());
79 24
        } catch (ReferenceNotFound $e) {
80 24
            throw new ReferenceNotFound((string) $name, 0, $e);
81
        }
82
    }
83
84 15
    public function build(Name $name): object
85
    {
86 15
        return $this->lazy($name)->load();
87
    }
88
89 13
    public function decorate(
90
        Name $decorator,
91
        Name $decorated,
92
        Name $newName = null
93
    ): Service {
94
        try {
95
            return $this
96 13
                ->get($decorator)
97 11
                ->decorate($decorator->withoutRoot(), $decorated, $newName);
98 3
        } catch (ReferenceNotFound $e) {
99 3
            throw new ReferenceNotFound((string) $decorator, 0, $e);
100
        }
101
    }
102
103
    /**
104
     * @param StreamInterface<mixed> $arguments
105
     *
106
     * @return StreamInterface<mixed>
107
     */
108 7
    public function extract(
109
        Name $name,
110
        StreamInterface $arguments,
111
        Argument $argument
112
    ): StreamInterface {
113
        try {
114
            return $this
115 7
                ->get($name)
116 5
                ->extract($name->withoutRoot(), $arguments, $argument);
117 4
        } catch (ReferenceNotFound $e) {
118 4
            throw new ReferenceNotFound((string) $name, 0, $e);
119
        }
120
    }
121
122 47
    private function get(Name $name): Dependency
123
    {
124
        try {
125 47
            $root = $name->root();
126 20
        } catch (NameNotNamespaced $e) {
127 20
            throw new ReferenceNotFound((string) $name);
128
        }
129
130 33
        if (!$this->dependencies->contains((string) $root)) {
131 7
            throw new ReferenceNotFound((string) $name);
132
        }
133
134 29
        return $this->dependencies->get((string) $root);
135
    }
136
137
    /**
138
     * Return the list of exposed services per dependency
139
     *
140
     * @return MapInterface<Name, MapInterface<Name, Constructor>>
141
     */
142 2
    public function exposed(): MapInterface
143
    {
144 2
        return $this->dependencies->reduce(
145 2
            new Map(Name::class, MapInterface::class),
146 2
            static function(Map $exposed, string $name, Dependency $dependency): Map {
147 2
                return $exposed->put(
148 2
                    $dependency->name(),
149 2
                    $dependency->exposed()
150
                );
151 2
            }
152
        );
153
    }
154
155 8
    public function compile(): CompiledDependencies
156
    {
157 8
        return new CompiledDependencies(
158 8
            ...$this->dependencies->values()
159
        );
160
    }
161
162
    private function assertNoCircularDependency(): void
163
    {
164 212
        $this->dependencies->foreach(function(string $name, Dependency $dependency): void {
165
            $this
166 36
                ->dependencies
167 36
                ->remove($name)
168 36
                ->foreach(static function(string $name, Dependency $other) use ($dependency): void {
169 4
                    if (!$dependency->dependsOn($other)) {
170 4
                        return;
171
                    }
172
173 2
                    if (!$other->dependsOn($dependency)) {
174 1
                        return;
175
                    }
176
177 1
                    throw new CircularDependency(sprintf(
178 1
                        '%s -> %s -> %s',
179 1
                        $dependency->name(),
180 1
                        $other->name(),
181 1
                        $dependency->name()
182
                    ));
183 36
                });
184 212
        });
185 212
    }
186
}
187