1 | <?php |
||
2 | declare(strict_types = 1); |
||
3 | |||
4 | namespace Innmind\Compose\Definition; |
||
5 | |||
6 | use Innmind\Compose\{ |
||
7 | Definition\Service\Constructor, |
||
8 | Definition\Service\Argument, |
||
9 | Definition\Service\Arguments, |
||
10 | Services, |
||
11 | Compilation, |
||
12 | Exception\ServiceCannotDecorateMultipleServices, |
||
13 | Exception\LogicException |
||
14 | }; |
||
15 | use Innmind\Immutable\{ |
||
16 | StreamInterface, |
||
17 | Stream, |
||
18 | Sequence |
||
19 | }; |
||
20 | |||
21 | final class Service |
||
22 | { |
||
23 | private $name; |
||
24 | private $construct; |
||
25 | private $arguments; |
||
26 | private $exposeName; |
||
27 | private $decorates = false; |
||
28 | |||
29 | 220 | public function __construct( |
|
30 | Name $name, |
||
31 | Constructor $constructor, |
||
32 | Argument ...$arguments |
||
33 | ) { |
||
34 | 220 | $this->name = $name; |
|
35 | 220 | $this->construct = $constructor; |
|
36 | 220 | $this->arguments = Stream::of(Argument::class, ...$arguments); |
|
37 | |||
38 | 220 | $decorates = $this->arguments->filter(static function(Argument $argument): bool { |
|
39 | 54 | return $argument instanceof Argument\Decorate; |
|
40 | 220 | }); |
|
41 | |||
42 | 220 | if ($decorates->size() > 0) { |
|
43 | 30 | $this->decorates = true; |
|
44 | } |
||
45 | |||
46 | 220 | if ($decorates->size() > 1) { |
|
47 | 1 | throw new ServiceCannotDecorateMultipleServices((string) $name); |
|
48 | } |
||
49 | 219 | } |
|
50 | |||
51 | 23 | public function decorates(): bool |
|
52 | { |
||
53 | 23 | return $this->decorates; |
|
54 | } |
||
55 | |||
56 | 96 | public function exposeAs(Name $name): self |
|
57 | { |
||
58 | 96 | $self = clone $this; |
|
59 | 96 | $self->exposeName = $name; |
|
60 | |||
61 | 96 | return $self; |
|
62 | } |
||
63 | |||
64 | 207 | public function exposed(): bool |
|
65 | { |
||
66 | 207 | return $this->exposeName instanceof Name; |
|
67 | } |
||
68 | |||
69 | 86 | public function exposedAs(): Name |
|
70 | { |
||
71 | 86 | return $this->exposeName; |
|
72 | } |
||
73 | |||
74 | 45 | public function isExposedAs(Name $name): bool |
|
75 | { |
||
76 | 45 | return (string) $this->exposeName === (string) $name; |
|
77 | } |
||
78 | |||
79 | 215 | public function name(): Name |
|
80 | { |
||
81 | 215 | return $this->name; |
|
82 | } |
||
83 | |||
84 | 13 | public function constructor(): Constructor |
|
85 | { |
||
86 | 13 | return $this->construct; |
|
87 | } |
||
88 | |||
89 | /** |
||
90 | * @return StreamInterface<Argument> |
||
91 | */ |
||
92 | 12 | public function arguments(): StreamInterface |
|
93 | { |
||
94 | 12 | return $this->arguments; |
|
95 | } |
||
96 | |||
97 | 114 | public function build(Services $services): object |
|
98 | { |
||
99 | 114 | return ($this->construct)(...$this->arguments->reduce( |
|
100 | 114 | Stream::of('mixed'), |
|
101 | 114 | static function(Stream $arguments, Argument $argument) use ($services): Stream { |
|
102 | 17 | return $argument->resolve($arguments, $services); |
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||
103 | 114 | } |
|
104 | )); |
||
105 | } |
||
106 | |||
107 | 13 | public function decorate(Name $service, Name $newName = null): self |
|
108 | { |
||
109 | 13 | if (!$this->decorates) { |
|
110 | 2 | throw new LogicException; |
|
111 | } |
||
112 | |||
113 | 12 | $self = clone $this; |
|
114 | 12 | $self->name = $newName ?? new Name( |
|
115 | 10 | $self->name.'.'.md5((string) $service) |
|
116 | ); |
||
117 | 12 | $self->decorates = false; |
|
118 | 12 | $self->arguments = $self->arguments->map(static function(Argument $argument) use ($service): Argument { |
|
119 | 12 | if ($argument instanceof Argument\Decorate) { |
|
120 | 12 | return new Argument\Reference($service); |
|
121 | } |
||
122 | |||
123 | 10 | return $argument; |
|
124 | 12 | }); |
|
125 | |||
126 | 12 | return $self; |
|
127 | } |
||
128 | |||
129 | 15 | public function tunnel( |
|
130 | Name $dependency, |
||
131 | Name $decorated, |
||
132 | Name $newName = null |
||
133 | ): self { |
||
134 | if ( |
||
135 | 15 | !$this->decorates() || |
|
136 | 15 | !$this->exposed() |
|
137 | ) { |
||
138 | 2 | throw new LogicException; |
|
139 | } |
||
140 | |||
141 | 13 | return new self( |
|
142 | 13 | $newName ?? new Name('tunnel_'.md5($dependency.'.'.$this->exposeName)), |
|
143 | 13 | $this->construct, |
|
144 | 13 | ...$this->arguments->map(function(Argument $argument) use ($dependency, $decorated): Argument { |
|
145 | 13 | if ($argument instanceof Argument\Decorate) { |
|
146 | 13 | return new Argument\Reference($decorated); |
|
147 | } |
||
148 | |||
149 | 12 | return new Argument\Tunnel( |
|
150 | 12 | $dependency->add($this->exposeName), |
|
151 | 12 | $argument |
|
152 | ); |
||
153 | 13 | }) |
|
154 | ); |
||
155 | } |
||
156 | |||
157 | 7 | public function compile(): Compilation\Service |
|
158 | { |
||
159 | 7 | return new Compilation\Service( |
|
160 | 7 | $this, |
|
161 | 7 | $this->construct->compile( |
|
162 | 7 | ...$this->arguments->reduce( |
|
163 | 7 | new Sequence, |
|
164 | 7 | static function(Sequence $arguments, Argument $argument): Sequence { |
|
165 | 4 | return $arguments->add($argument->compile()); |
|
166 | 7 | } |
|
167 | ) |
||
168 | ) |
||
169 | ); |
||
170 | } |
||
171 | } |
||
172 |