Completed
Push — develop ( 9f68ce...fc6f83 )
by Baptiste
01:57
created

Services::dependencies()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
declare(strict_types = 1);
3
4
namespace Innmind\Compose;
5
6
use Innmind\Compose\{
7
    Definition\Name,
8
    Definition\Service,
9
    Definition\Service\Argument,
10
    Exception\ArgumentNotProvided,
11
    Exception\ArgumentNotDefined,
12
    Exception\CircularDependency
13
};
14
use Innmind\Immutable\{
15
    Sequence,
16
    MapInterface,
17
    Map,
18
    Pair,
19
    Stream
20
};
21
22
final class Services
23
{
24
    private $definitions;
25
    private $exposed;
26
    private $arguments;
27
    private $dependencies;
28
    private $building;
29
    private $instances;
30
31 144
    public function __construct(
32
        Arguments $arguments,
33
        Dependencies $dependencies,
34
        Service ...$definitions
35
    ) {
36 144
        $this->arguments = $arguments;
37 144
        $this->dependencies = $dependencies;
38 144
        $this->definitions = Sequence::of(...$definitions)->reduce(
39 144
            new Map('string', Service::class),
40 144
            static function(Map $definitions, Service $definition): Map {
41 142
                return $definitions->put(
42 142
                    (string) $definition->name(),
43 142
                    $definition
44
                );
45 144
            }
46
        );
47 144
        $this->exposed = $this
48 144
            ->definitions
49 144
            ->filter(static function(string $name, Service $definition): bool {
50 142
                return $definition->exposed();
51 144
            })
52 144
            ->map(static function(string $name, Service $definition): Pair {
53 39
                return new Pair(
54 39
                    (string) $definition->exposedAs(),
55 39
                    $definition
56
                );
57 144
            });
58
59 144
        $this->building = Stream::of('string');
60 144
        $this->instances = new Map('string', 'object');
61 144
    }
62
63 3
    public function expose(Name $name, Name $as): self
64
    {
65 3
        $service = $this->get($name)->exposeAs($as);
66
67 3
        $self = clone $this;
68 3
        $self->definitions = $self->definitions->put(
69 3
            (string) $name,
70 3
            $service
71
        );
72 3
        $self->exposed = $self->exposed->put(
73 3
            (string) $as,
74 3
            $service
75
        );
76
77 3
        return $self;
78
    }
79
80 3
    public function stack(Name $name, Name $highest, Name $lower, Name ...$rest): self
81
    {
82 3
        $stack = Sequence::of($lower, ...$rest)->reverse();
83 3
        $stacked = Sequence::of(
84 3
            $this->get($stack->first())
85
        );
86
87 3
        $stacked = $stack->drop(1)->reduce(
88 3
            $stacked,
89 3
            function(Sequence $stacked, Name $decorator): Sequence {
90 3
                return $stacked->add(
91
                    $this
92 3
                        ->get($decorator)
93 3
                        ->decorate($stacked->last()->name())
94
                );
95 3
            }
96
        );
97 3
        $stacked = $stacked->add(
98
            $this
99 3
                ->get($highest)
100 3
                ->decorate($stacked->last()->name(), $name)
101
        );
102
103 3
        $self = clone $this;
104 3
        $self->definitions = $stacked->reduce(
105 3
            $self->definitions,
106 3
            static function(Map $definitions, Service $service): Map {
107 3
                return $definitions->put(
108 3
                    (string) $service->name(),
109 3
                    $service
110
                );
111 3
            }
112
        );
113
114 3
        return $self;
115
    }
116
117
    /**
118
     * @param MapInterface<string, mixed> $arguments
119
     */
120 27
    public function inject(MapInterface $arguments): self
121
    {
122 27
        $self = clone $this;
123 27
        $self->arguments = $self->arguments->bind($arguments);
124 27
        $self->building = $self->building->clear();
125 27
        $self->instances = $self->instances->clear();
126 27
        $self->dependencies = $self->dependencies->bind($self);
127
128 27
        return $self;
129
    }
130
131 98
    public function build(Name $name): object
132
    {
133 98
        $definition = $this->get($name);
134 98
        $name = $definition->name();
135
136 98
        if ($this->instances->contains((string) $name)) {
137 79
            return $this->instances->get((string) $name);
138
        }
139
140
        try {
141 98
            if ($this->building->contains((string) $name)) {
142 2
                throw new CircularDependency(
143
                    (string) $this
144 2
                        ->building
145 2
                        ->add((string) $name)
146 2
                        ->join(' -> ')
147
                );
148
            }
149
150 98
            $this->building = $this->building->add((string) $name);
151
152 98
            $service = $definition->build($this);
153
154 97
            $this->instances = $this->instances->put((string) $name, $service);
155 97
            $this->building = $this->building->dropEnd(1);
156 4
        } catch (\Throwable $e) {
157 4
            $this->building = $this->building->clear();
158
159 4
            throw $e;
160
        }
161
162 97
        return $service;
163
    }
164
165 30
    public function arguments(): Arguments
166
    {
167 30
        return $this->arguments;
168
    }
169
170 15
    public function dependencies(): Dependencies
171
    {
172 15
        return $this->dependencies;
173
    }
174
175 109
    public function has(Name $name): bool
176
    {
177 109
        if ($this->definitions->contains((string) $name)) {
178 107
            return true;
179
        }
180
181 7
        return $this->exposed->contains((string) $name);
182
    }
183
184 106
    public function get(Name $name): Service
185
    {
186
        try {
187 106
            return $this->definitions->get((string) $name);
188 17
        } catch (\Exception $e) {
189 17
            return $this->exposed->get((string) $name);
190
        }
191
    }
192
}
193