Completed
Push — develop ( fe7998...59b49f )
by Baptiste
01:41
created

Dependencies::bind()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

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