Completed
Push — develop ( dd2af7...2c61cc )
by Baptiste
09:23 queued 01:14
created

Definitions::__construct()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 26
c 0
b 0
f 0
ccs 21
cts 21
cp 1
rs 8.8571
cc 2
eloc 17
nc 2
nop 2
crap 2
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\AtLeastOneServiceMustBeExposed
13
};
14
use Innmind\Immutable\{
15
    Sequence,
16
    MapInterface,
17
    Map,
18
    Pair
19
};
20
21
final class Definitions
22
{
23
    private $definitions;
24
    private $exposed;
25
    private $arguments;
26
27 12
    public function __construct(Arguments $arguments, Service ...$definitions)
28
    {
29 12
        $this->arguments = $arguments;
30 12
        $this->definitions = Sequence::of(...$definitions)->reduce(
31 12
            new Map('string', Service::class),
32 12
            static function(Map $definitions, Service $definition): Map {
33 11
                return $definitions->put(
34 11
                    (string) $definition->name(),
35 11
                    $definition
36
                );
37 12
            }
38
        );
39 12
        $this->exposed = $this
40 12
            ->definitions
41 12
            ->filter(static function(string $name, Service $definition): bool {
42 11
                return $definition->exposed();
43 12
            })
44 12
            ->map(static function(string $name, Service $definition): Pair {
45 11
                return new Pair(
46 11
                    (string) $definition->exposedAs(),
47 11
                    $definition
48
                );
49 12
            });
50
51 12
        if ($this->exposed->size() === 0) {
52 1
            throw new AtLeastOneServiceMustBeExposed;
53
        }
54 11
    }
55
56
    /**
57
     * @param MapInterface<string, mixed> $arguments
58
     */
59 9
    public function inject(MapInterface $arguments): self
60
    {
61 9
        $self = clone $this;
62 9
        $self->arguments = $self->arguments->bind($arguments);
63
64 9
        return $self;
65
    }
66
67 4
    public function build(Name $name): object
68
    {
69 4
        $definition = $this->get($name);
70 4
        $arguments = $this->buildArguments($definition);
71 4
        $construct = $definition->constructor();
72
73 4
        return $construct(...$arguments);
74
    }
75
76 2
    public function arguments(): Arguments
77
    {
78 2
        return $this->arguments;
79
    }
80
81 6
    public function has(Name $name): bool
82
    {
83 6
        if ($this->definitions->contains((string) $name)) {
84 4
            return true;
85
        }
86
87 4
        return $this->exposed->contains((string) $name);
88
    }
89
90 8
    public function get(Name $name): Service
91
    {
92
        try {
93 8
            return $this->definitions->get((string) $name);
94 4
        } catch (\Exception $e) {
95 4
            return $this->exposed->get((string) $name);
96
        }
97
    }
98
99 4
    public function buildArguments(Service $service): Sequence
100
    {
101
        return $service
102 4
            ->arguments()
103 4
            ->reduce(
104 4
                new Sequence,
105 4
                function(Sequence $arguments, Argument $argument): Sequence {
106
                    // @todo: handle the decoration
107 3
                    $value = $this->fetchArgumentValue($argument);
108
109 3
                    if ($argument->toUnwind()) {
110 1
                        return $arguments->append(new Sequence(...$value ?? []));
111
                    }
112
113 3
                    return $arguments->add($value);
114 4
                }
115
            );
116
    }
117
118 3
    public function fetchArgumentValue(Argument $argument)
119
    {
120
        try {
121 3
            return $this->arguments->get($argument->reference());
122 3
        } catch (ArgumentNotProvided $e) {
123 1
            if ($e->argument()->hasDefault()) {
124 1
                return $this->build($e->argument()->default());
125
            }
126 2
        } catch (ArgumentNotDefined $e) {
127 2
            return $this->build($argument->reference());
128
        }
129
130
        //null as the argument must be optional here, requirement as been
131
        //checked earlier
132
133 1
        return null;
134
    }
135
}
136