Service   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 145
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 60
dl 0
loc 145
ccs 71
cts 71
cp 1
rs 10
c 0
b 0
f 0
wmc 20

13 Methods

Rating   Name   Duplication   Size   Complexity  
A exposed() 0 3 1
A constructor() 0 3 1
A isExposedAs() 0 3 1
A exposedAs() 0 3 1
A arguments() 0 3 1
A decorates() 0 3 1
A name() 0 3 1
A decorate() 0 20 3
A exposeAs() 0 6 1
A build() 0 6 1
A tunnel() 0 23 4
A __construct() 0 19 3
A compile() 0 9 1
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
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...
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