DefinitionContainer::findProcessor()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace DummyGenerator\Container;
6
7
use DummyGenerator\Definitions\Calculator\EanCalculatorInterface;
8
use DummyGenerator\Definitions\Calculator\IbanCalculatorInterface;
9
use DummyGenerator\Definitions\Calculator\IsbnCalculatorInterface;
10
use DummyGenerator\Definitions\Calculator\LuhnCalculatorInterface;
11
use DummyGenerator\Definitions\DefinitionInterface;
12
use DummyGenerator\Definitions\Extension\Awareness\EanCalculatorAwareExtensionInterface;
13
use DummyGenerator\Definitions\Extension\Awareness\IbanCalculatorAwareExtensionInterface;
14
use DummyGenerator\Definitions\Extension\Awareness\IsbnCalculatorAwareExtensionInterface;
15
use DummyGenerator\Definitions\Extension\Awareness\LuhnCalculatorAwareExtensionInterface;
16
use DummyGenerator\Definitions\Extension\Awareness\RandomizerAwareExtensionInterface;
17
use DummyGenerator\Definitions\Extension\Awareness\ReplacerAwareExtensionInterface;
18
use DummyGenerator\Definitions\Randomizer\RandomizerInterface;
19
use DummyGenerator\Definitions\Replacer\RandomizerAwareReplacerInterface;
20
use DummyGenerator\Definitions\Replacer\ReplacerInterface;
21
use DummyGenerator\Definitions\Transliterator\TransliteratorAwareReplacerInterface;
22
use DummyGenerator\Definitions\Transliterator\TransliteratorInterface;
23
24
final class DefinitionContainer implements DefinitionContainerInterface
25
{
26
    /** @var array<callable(): DefinitionInterface|DefinitionInterface|class-string<DefinitionInterface>> $definitions */
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<callable(): Defini...g<DefinitionInterface>> at position 2 could not be parsed: Expected '>' at position 2, but found 'callable'.
Loading history...
27
    private array $definitions;
28
29
    /** @var array<string, DefinitionInterface> */
30
    private array $services = [];
31
32
    /**
33
     * Create a container object with a set of definitions.
34
     * The array value MUST produce an object that implements DefinitionInterface.
35
     *
36
     * @param array<string, callable(): DefinitionInterface|class-string<DefinitionInterface>> $definitions
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, callable()...g<DefinitionInterface>> at position 4 could not be parsed: Expected '>' at position 4, but found 'callable'.
Loading history...
37
     */
38 164
    public function __construct(array $definitions = [])
39
    {
40 164
        $this->definitions = $definitions;
41
    }
42
43
    /**
44
     * Retrieve a definition from the container.
45
     *
46
     * @throws \InvalidArgumentException
47
     * @throws \RuntimeException
48
     * @throws ContainerException
49
     * @throws NotInContainerException
50
     */
51 148
    public function get(string $id): DefinitionInterface
52
    {
53 148
        if (array_key_exists($id, $this->services)) {
54 66
            return $this->services[$id];
55
        }
56
57 148
        if (!$this->has($id)) {
58 1
            throw new NotInContainerException(sprintf(
59 1
                'There is not service with id "%s" in the container.',
60 1
                $id,
61 1
            ));
62
        }
63
64 147
        $definition = $this->definitions[$id];
65
66 147
        return $this->services[$id] = $this->getService($id, $definition);
67
    }
68
69
    /**
70
     * Check if the container contains a given identifier.
71
     */
72 154
    public function has(string $id): bool
73
    {
74 154
        return array_key_exists($id, $this->definitions);
75
    }
76
77
    /**
78
     * Add new definition
79
     *
80
     * @param DefinitionInterface|class-string<DefinitionInterface>|callable(): DefinitionInterface $value
0 ignored issues
show
Documentation Bug introduced by
The doc comment DefinitionInterface|clas...(): DefinitionInterface at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in DefinitionInterface|class-string<DefinitionInterface>|callable(): DefinitionInterface.
Loading history...
81
     */
82 151
    public function add(string $name, callable|DefinitionInterface|string $value): void
83
    {
84 151
        $this->definitions[$name] = $value;
85
    }
86
87
    /**
88
     * Find proper Extension for given method
89
     */
90 149
    public function findProcessor(string $name): null|ResolvedDefinition
91
    {
92 149
        foreach ($this->definitions as $id => $definition) {
93 149
            $service = $this->getService($id, $definition);
94
95 149
            if (method_exists($service, $name)) {
96
                //return $service;
97 149
                return new ResolvedDefinition($name, $id, $service);
98
            }
99
        }
100
101 2
        return null;
102
    }
103
104
    /**
105
     * Get the service from a definition.
106
     *
107
     * @param callable(): DefinitionInterface|DefinitionInterface|class-string<DefinitionInterface> $definition
108
     */
109 155
    private function getService(string $id, callable|object|string $definition): DefinitionInterface
110
    {
111 155
        if (is_callable($definition)) {
112
            try {
113 4
                return $this->handleAwareness($definition());
114 1
            } catch (\Throwable $e) {
115 1
                throw new ContainerException(
116 1
                    sprintf('Error while invoking callable for "%s"', $id),
117 1
                    0,
118 1
                    $e,
119 1
                );
120
            }
121 151
        } elseif ($definition instanceof DefinitionInterface) {
122 14
            return $this->handleAwareness($definition);
123 145
        } elseif (is_string($definition)) {
124 145
            if (class_exists($definition)) {
125
                try {
126 144
                    $class = new $definition();
127
128 143
                    if ($class instanceof DefinitionInterface) {
129 142
                        return $this->handleAwareness($class);
130
                    }
131
132
                    // @phpstan-ignore-next-line
133 1
                    throw new ContainerException(sprintf('Class for "%s" is not implementing DefinitionInterface', $id));
134 2
                } catch (\Throwable $e) {
135 2
                    throw new ContainerException(sprintf('Could not instantiate class for "%s"', $id), 0, $e);
136
                }
137
            }
138
139 1
            throw new ContainerException(sprintf(
140 1
                'Could not instantiate class for "%s". Class was not found.',
141 1
                $id,
142 1
            ));
143
        }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return DummyGenerator\Definitions\DefinitionInterface. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
144
    }
145
146 151
    private function handleAwareness(DefinitionInterface $extension): DefinitionInterface
147
    {
148 151
        if ($extension instanceof TransliteratorAwareReplacerInterface) {
149
            /** @var TransliteratorInterface $transliterator */
150 63
            $transliterator = $this->get(TransliteratorInterface::class);
151 63
            $extension = $extension->withTransliterator($transliterator);
152
        }
153
154 151
        if ($extension instanceof RandomizerAwareReplacerInterface) {
155
            /** @var RandomizerInterface $randomizer */
156 63
            $randomizer = $this->get(RandomizerInterface::class);
157 63
            $extension = $extension->withRandomizer($randomizer);
158
        }
159
160 151
        if ($extension instanceof RandomizerAwareExtensionInterface) {
161
            /** @var RandomizerInterface $randomizer */
162 141
            $randomizer = $this->get(RandomizerInterface::class);
163 141
            $extension = $extension->withRandomizer($randomizer);
164
        }
165
166 151
        if ($extension instanceof ReplacerAwareExtensionInterface) {
167
            /** @var ReplacerInterface $replacer */
168 55
            $replacer = $this->get(ReplacerInterface::class);
169 55
            $extension = $extension->withReplacer($replacer);
170
        }
171
172 151
        if ($extension instanceof EanCalculatorAwareExtensionInterface) {
173
            /** @var EanCalculatorInterface $calculator */
174 5
            $calculator = $this->get(EanCalculatorInterface::class);
175 5
            $extension = $extension->withEanCalculator($calculator);
176
        }
177
178 151
        if ($extension instanceof IbanCalculatorAwareExtensionInterface) {
179
            /** @var IbanCalculatorInterface $calculator */
180 9
            $calculator = $this->get(IbanCalculatorInterface::class);
181 9
            $extension = $extension->withIbanCalculator($calculator);
182
        }
183
184 151
        if ($extension instanceof IsbnCalculatorAwareExtensionInterface) {
185
            /** @var IsbnCalculatorInterface $calculator */
186 5
            $calculator = $this->get(IsbnCalculatorInterface::class);
187 5
            $extension = $extension->withIsbnCalculator($calculator);
188
        }
189
190 151
        if ($extension instanceof LuhnCalculatorAwareExtensionInterface) {
191
            /** @var LuhnCalculatorInterface $calculator */
192 12
            $calculator = $this->get(LuhnCalculatorInterface::class);
193 12
            $extension = $extension->withLuhnCalculator($calculator);
194
        }
195
196 151
        return $extension;
197
    }
198
}
199