Registry   B
last analyzed

Complexity

Total Complexity 44

Size/Duplication

Total Lines 203
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 79
dl 0
loc 203
ccs 89
cts 89
cp 1
rs 8.8798
c 0
b 0
f 0
wmc 44

14 Methods

Rating   Name   Duplication   Size   Complexity  
A checkInterface() 0 4 2
A register() 0 9 1
A createService() 0 11 2
A getType() 0 13 3
A checkNameExists() 0 5 3
A getPossibleNames() 0 10 3
A getEntry() 0 10 5
A __construct() 0 3 1
A checkParentType() 0 24 6
A checkClassAlreadyExists() 0 5 2
A checkExtensionInterface() 0 4 2
A registerExtension() 0 10 1
A checkExtendedTypeAvailable() 0 12 6
B getExtensions() 0 16 7

How to fix   Complexity   

Complex Class

Complex classes like Registry often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Registry, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * AbstractCollector.php
5
 *
6
 * @since 29/05/16
7
 * @author gseidel
8
 */
9
10
namespace Enhavo\Component\Type;
11
12
use Enhavo\Component\Type\Exception\TypeNotFoundException;
13
use Enhavo\Component\Type\Exception\TypeNotValidException;
14
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
15
16
class Registry implements RegistryInterface
17
{
18
    use ContainerAwareTrait;
19
20
    /** @var string */
21
    private string $namespace;
22
23
    /** @var RegistryEntry[] */
24
    private array $entries = [];
25
26
    /** @var RegistryExtension[] */
27
    private array $extensions = [];
28
29
30 14
    public function __construct(string $namespace)
31
    {
32 14
        $this->namespace = $namespace;
33
    }
34
35 12
    public function register(string $class, string $id)
36
    {
37 12
        $this->checkInterface($class);
38
        /** @var string|TypeInterface $class */
39 11
        $this->checkClassAlreadyExists($class);
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type Enhavo\Component\Type\TypeInterface; however, parameter $class of Enhavo\Component\Type\Re...eckClassAlreadyExists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

39
        $this->checkClassAlreadyExists(/** @scrutinizer ignore-type */ $class);
Loading history...
40 11
        $this->checkNameExists($class::getName());
41 11
        $this->checkParentType($class);
42
43 7
        $this->entries[] = new RegistryEntry($id, $class, $class::getName());
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type Enhavo\Component\Type\TypeInterface; however, parameter $class of Enhavo\Component\Type\RegistryEntry::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

43
        $this->entries[] = new RegistryEntry($id, /** @scrutinizer ignore-type */ $class, $class::getName());
Loading history...
44
    }
45
46
    /**
47
     * @param $class
48
     * @throws TypeNotValidException
49
     */
50 12
    private function checkInterface($class): void
51
    {
52 12
        if (!in_array(TypeInterface::class, class_implements($class))) {
53 1
            throw TypeNotValidException::invalidInterface($class);
54
        }
55
    }
56
57
    /**
58
     * @param $name
59
     * @throws TypeNotValidException
60
     */
61 11
    private function checkNameExists($name)
62
    {
63 11
        $entry = $this->getEntry($name);
64 11
        if ($name !== null && $entry !== null) {
65 1
            throw TypeNotValidException::nameExists($name, $this->namespace, $entry->getClass());
66
        }
67
    }
68
69
    /**
70
     * @param string $class
71
     * @throws TypeNotValidException
72
     */
73 11
    private function checkClassAlreadyExists($class)
74
    {
75 11
        $entry = $this->getEntry($class);
76 11
        if ($entry !== null) {
77 1
            throw TypeNotValidException::classExists($class, $this->namespace);
78
        }
79
    }
80
81
    /**
82
     * @param string|TypeInterface $class
83
     * @throws TypeNotValidException
84
     */
85 11
    private function checkParentType($class)
86
    {
87
        /** @var string|TypeInterface $parentClass */
88 11
        $parentClass = $class::getParentType();
89 11
        $classes = [$class];
90 11
        while ($parentClass !== null) {
91 8
            if (in_array($parentClass, $classes)) {
92 2
                $classes[] = $parentClass;
93 2
                throw TypeNotValidException::circleReferences($classes);
94
            }
95
96 7
            if ($parentClass !== null && !class_exists($parentClass)) {
97 1
                throw TypeNotValidException::parentNotFound($class, $parentClass);
98
            }
99
100 6
            $reflection = new \ReflectionClass($parentClass);
101 6
            if (!$reflection->implementsInterface(TypeInterface::class)) {
102 1
                throw TypeNotValidException::parentInvalidInterface($class, $parentClass);
103
            }
104
105 5
            $classes[] = $parentClass;
106
107 5
            $class = $parentClass;
108 5
            $parentClass = $parentClass::getParentType();
109
        }
110
    }
111
112 11
    private function getEntry($name): ?RegistryEntry
113
    {
114 11
        foreach($this->entries as $entry) {
115 5
            if ($entry->getName() !== null && $entry->getName() === $name) {
116 2
                return $entry;
117 4
            } elseif($entry->getClass() === $name)  {
118 2
                return $entry;
119
            }
120
        }
121 11
        return null;
122
    }
123
124
    /**
125
     * @param string $name
126
     * @throws TypeNotFoundException
127
     * @return TypeInterface
128
     */
129 3
    public function getType(string $name): TypeInterface
130
    {
131 3
        $entry = $this->getEntry($name);
132
133 3
        if ($entry === null) {
134 1
            throw TypeNotFoundException::notFound($name, $this->namespace, $this->getPossibleNames());
135
        }
136
137 2
        if ($entry->getService() === null) {
138 2
            $entry->setService($this->createService($entry));
139
        }
140
141 2
        return clone $entry->getService();
142
    }
143
144 2
    private function createService(RegistryEntry $entry): TypeInterface
145
    {
146
        /** @var TypeInterface $service */
147 2
        $service = $this->container->get($entry->getId());
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

147
        /** @scrutinizer ignore-call */ 
148
        $service = $this->container->get($entry->getId());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
148
149 2
        $parent = $service::getParentType();
150 2
        if($parent !== null) {
151 1
            $service->setParent($this->getType($parent));
152
        }
153
154 2
        return $service;
155
    }
156
157 1
    private function getPossibleNames()
158
    {
159 1
        $names = [];
160 1
        foreach($this->entries as $entry) {
161 1
            if($entry->getName() !== null) {
162 1
                $names[] = $entry->getName();
163
            }
164 1
            $names[] = $entry->getClass();
165
        }
166 1
        return $names;
167
    }
168
169 3
    public function registerExtension(string $class, string $id, int $priority = 10)
170
    {
171 3
        $this->checkExtensionInterface($class);
172
        /** @var string|TypeExtensionInterface $class */
173 2
        $this->checkExtendedTypeAvailable($class);
174
175 1
        $this->extensions[] = new RegistryExtension($id, $class, $class::getExtendedTypes(), $priority);
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type Enhavo\Component\Type\TypeExtensionInterface; however, parameter $class of Enhavo\Component\Type\Re...xtension::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

175
        $this->extensions[] = new RegistryExtension($id, /** @scrutinizer ignore-type */ $class, $class::getExtendedTypes(), $priority);
Loading history...
176
177 1
        usort($this->extensions, function (RegistryExtension $a, RegistryExtension $b) {
178 1
            return $b->getPriority() - $a->getPriority();
179 1
        });
180
    }
181
182 3
    private function checkExtensionInterface($class): void
183
    {
184 3
        if (!in_array(TypeExtensionInterface::class, class_implements($class))) {
185 1
            throw TypeNotValidException::invalidExtensionInterface($class);
186
        }
187
    }
188
189 2
    private function checkExtendedTypeAvailable($extensionClass)
190
    {
191 2
        $types = $extensionClass::getExtendedTypes();
192 2
        foreach ($this->entries as $entry) {
193 1
            foreach ($types as $type) {
194 1
                if ($entry->getClass() === $type || ($entry->getName() && $entry->getName() === $type)) {
195 1
                    return;
196
                }
197
            }
198
        }
199
200 1
        throw TypeNotValidException::extendedTypeNotExists($extensionClass, $types);
201
    }
202
203 1
    public function getExtensions(TypeInterface $type): array
204
    {
205 1
        $extensions = [];
206 1
        foreach ($this->extensions as $extension) {
207 1
            foreach ($extension->getExtendedTypes() as $extendedType) {
208 1
                if ($type::class === $extendedType || ($type::getName() && $type::getName() === $extendedType)) {
209 1
                    if ($extension->getService() === null) {
210 1
                        $service = $this->container->get($extension->getId());
211 1
                        $extension->setService($service);
212
                    }
213 1
                    $extensionService = clone $extension->getService();
214 1
                    $extensions[] = $extensionService;
215
                }
216
            }
217
        }
218 1
        return $extensions;
219
    }
220
}
221