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

Service::tunnel()   B

Complexity

Conditions 4
Paths 2

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 13
cts 13
cp 1
rs 8.7972
c 0
b 0
f 0
cc 4
eloc 13
nc 2
nop 3
crap 4
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
    Exception\ServiceCannotDecorateMultipleServices,
12
    Exception\LogicException
13
};
14
use Innmind\Immutable\{
15
    StreamInterface,
16
    Stream
17
};
18
19
final class Service
20
{
21
    private $name;
22
    private $construct;
23
    private $arguments;
24
    private $exposeName;
25
    private $decorates = false;
26
27 189
    public function __construct(
28
        Name $name,
29
        Constructor $constructor,
30
        Argument ...$arguments
31
    ) {
32 189
        $this->name = $name;
33 189
        $this->construct = $constructor;
34 189
        $this->arguments = Stream::of(Argument::class, ...$arguments);
35
36 189
        $decorates = $this->arguments->filter(static function(Argument $argument): bool {
37 46
            return $argument instanceof Argument\Decorate;
38 189
        });
39
40 189
        if ($decorates->size() > 0) {
41 24
            $this->decorates = true;
42
        }
43
44 189
        if ($decorates->size() > 1) {
45 1
            throw new ServiceCannotDecorateMultipleServices((string) $name);
46
        }
47 188
    }
48
49 16
    public function decorates(): bool
50
    {
51 16
        return $this->decorates;
52
    }
53
54 73
    public function exposeAs(Name $name): self
55
    {
56 73
        $self = clone $this;
57 73
        $self->exposeName = $name;
58
59 73
        return $self;
60
    }
61
62 182
    public function exposed(): bool
63
    {
64 182
        return $this->exposeName instanceof Name;
65
    }
66
67 67
    public function exposedAs(): Name
68
    {
69 67
        return $this->exposeName;
70
    }
71
72 39
    public function isExposedAs(Name $name): bool
73
    {
74 39
        return (string) $this->exposeName === (string) $name;
75
    }
76
77 184
    public function name(): Name
78
    {
79 184
        return $this->name;
80
    }
81
82 2
    public function constructor(): Constructor
83
    {
84 2
        return $this->construct;
85
    }
86
87
    /**
88
     * @return StreamInterface<Argument>
89
     */
90 11
    public function arguments(): StreamInterface
91
    {
92 11
        return $this->arguments;
93
    }
94
95 105
    public function build(Services $services): object
96
    {
97 105
        return ($this->construct)(...$this->arguments->reduce(
98 105
            Stream::of('mixed'),
99 105
            static function(Stream $arguments, Argument $argument) use ($services): Stream {
100 16
                return $argument->resolve($arguments, $services);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $argument->resolve($arguments, $services) returns the type Innmind\Immutable\StreamInterface which includes types incompatible with the type-hinted return Innmind\Immutable\Stream.
Loading history...
101 105
            }
102
        ));
103
    }
104
105 7
    public function decorate(Name $service, Name $newName = null): self
106
    {
107 7
        if (!$this->decorates) {
108 2
            throw new LogicException;
109
        }
110
111 6
        $self = clone $this;
112 6
        $self->name = $newName ?? new Name(
113 4
            $self->name.'.'.md5((string) $service)
114
        );
115 6
        $self->decorates = false;
116 6
        $self->arguments = $self->arguments->map(static function(Argument $argument) use ($service): Argument {
117 6
            if ($argument instanceof Argument\Decorate) {
118 6
                return new Argument\Reference($service);
119
            }
120
121 4
            return $argument;
122 6
        });
123
124 6
        return $self;
125
    }
126
127 10
    public function tunnel(
128
        Name $dependency,
129
        Name $decorated,
130
        Name $newName = null
131
    ): self {
132
        if (
133 10
            !$this->decorates() ||
134 10
            !$this->exposed()
135
        ) {
136 2
            throw new LogicException;
137
        }
138
139 8
        return new self(
140 8
            $newName ?? new Name(md5($dependency.'.'.$this->exposeName)),
141 8
            $this->construct,
142 8
            ...$this->arguments->map(function(Argument $argument) use ($dependency, $decorated): Argument {
143 8
                if ($argument instanceof Argument\Decorate) {
144 8
                    return new Argument\Reference($decorated);
145
                }
146
147 7
                return new Argument\Tunnel(
148 7
                    $dependency->add($this->exposeName),
149 7
                    $argument
150
                );
151 8
            })
152
        );
153
    }
154
}
155