Passed
Branch develop (e0de4e)
by Baptiste
01:53
created

Definitions::stack()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 35
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 1

Importance

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