Passed
Push — develop ( 7d2249...4ccadf )
by Baptiste
02:09
created

Dependencies   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 147
ccs 72
cts 72
cp 1
rs 10
c 0
b 0
f 0
wmc 17

10 Methods

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