GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 851585...c4fac5 )
by Cees-Jan
02:38
created

Hydrator::getEmptyOrResource()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4.0466

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 12
cts 14
cp 0.8571
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 16
nc 4
nop 2
crap 4.0466
1
<?php declare(strict_types=1);
2
3
namespace ApiClients\Foundation\Hydrator;
4
5
use ApiClients\Foundation\Hydrator\Annotations\EmptyResource;
6
use ApiClients\Foundation\Resource\EmptyResourceInterface;
7
use ApiClients\Tools\CommandBus\CommandBus;
8
use Doctrine\Common\Annotations\AnnotationReader;
9
use Doctrine\Common\Annotations\CachedReader;
10
use Doctrine\Common\Annotations\Reader;
11
use Doctrine\Common\Cache\Cache;
12
use GeneratedHydrator\Configuration;
13
use Interop\Container\ContainerInterface;
14
use React\EventLoop\LoopInterface;
15
use ReflectionClass;
16
use RecursiveDirectoryIterator;
17
use RecursiveIteratorIterator;
18
use ApiClients\Foundation\Resource\ResourceInterface;
19
use Zend\Hydrator\HydratorInterface;
20
21
class Hydrator
22
{
23
    /**
24
     * @var ContainerInterface
25
     */
26
    protected $container;
27
28
    /**
29
     * @var array
30
     */
31
    protected $options;
32
33
    /**
34
     * @var array
35
     */
36
    protected $hydrators = [];
37
38
    /**
39
     * @var array
40
     */
41
    protected $annotations = [];
42
43
    /**
44
     * @var HandlerInterface[]
45
     */
46
    protected $annotationHandlers = [];
47
48
    /**
49
     * @var Reader
50
     */
51
    protected $annotationReader;
52
53
    /**
54
     * @param ContainerInterface $container
55
     * @param array $options
56
     */
57 7
    public function __construct(ContainerInterface $container, array $options)
58
    {
59 7
        $this->container = $container;
60 7
        $this->options = $options;
61
62 7
        $reader = new AnnotationReader();
63 7
        if (isset($this->options[Options::ANNOTATION_CACHE]) &&
64 7
            $this->options[Options::ANNOTATION_CACHE] instanceof Cache
65
        ) {
66 1
            $reader = new CachedReader(
67
                $reader,
68 1
                $this->options[Options::ANNOTATION_CACHE]
69
            );
70
        }
71 7
        $this->annotationReader = $reader;
72
73 7
        $this->setUpAnnotations();
74 7
    }
75
76 7
    protected function setUpAnnotations()
77
    {
78 7
        if (!isset($this->options[Options::ANNOTATIONS])) {
79
            return;
80
        }
81
82 7
        foreach ($this->options[Options::ANNOTATIONS] as $annotationClass => $handler) {
83 7
            $this->annotationHandlers[$annotationClass] = new $handler($this);
84
        }
85 7
    }
86
87 2
    public function preheat(string $scanTarget, string $namespace)
88
    {
89 2
        $directory = new RecursiveDirectoryIterator($scanTarget);
90 2
        $directory = new RecursiveIteratorIterator($directory);
91
92 2
        foreach ($directory as $node) {
93 2
            if (!is_file($node->getPathname())) {
94 2
                continue;
95
            }
96
97 2
            $file = substr($node->getPathname(), strlen($scanTarget));
98 2
            $file = ltrim($file, DIRECTORY_SEPARATOR);
99 2
            $file = rtrim($file, '.php');
100
101 2
            $class = $namespace . '\\' . str_replace(DIRECTORY_SEPARATOR, '\\', $file);
102
103 2
            if (!class_exists($class)) {
104
                continue;
105
            }
106
107 2
            if (!is_subclass_of($class, ResourceInterface::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \ApiClients\Foundation\R...esourceInterface::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
108
                continue;
109
            }
110
111 2
            $this->getHydrator($class);
112 2
            $this->annotationReader->getClassAnnotations(new ReflectionClass($class));
113
        }
114 2
    }
115
116
    /**
117
     * @param string $class
118
     * @param array $json
119
     * @return ResourceInterface
120
     */
121 4 View Code Duplication
    public function hydrate(string $class, array $json): ResourceInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
122
    {
123 4
        $fullClassName = implode(
124 4
            '\\',
125
            [
126
                $this->options[Options::NAMESPACE],
127 4
                $this->options[Options::NAMESPACE_SUFFIX],
128 4
                $class,
129
            ]
130
        );
131 4
        return $this->hydrateFQCN($fullClassName, $json);
132
    }
133
134
    /**
135
     * @param string $class
136
     * @param array $json
137
     * @return ResourceInterface
138
     */
139 4
    public function hydrateFQCN(string $class, array $json): ResourceInterface
140
    {
141 4
        $class = $this->getEmptyOrResource($class, $json);
142 4
        $hydrator = $this->getHydrator($class);
143 4
        $object = new $class($this->container->get(LoopInterface::class), $this->container->get(CommandBus::class));
144 4
        $json = $this->hydrateApplyAnnotations($json, $object);
145 4
        $resource = $hydrator->hydrate($json, $object);
146 4
        return $resource;
147
    }
148
149
    /**
150
     * @param array $json
151
     * @param ResourceInterface $object
152
     * @return array
153
     */
154 4 View Code Duplication
    protected function hydrateApplyAnnotations(array $json, ResourceInterface $object): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
155
    {
156 4
        foreach ($this->annotationHandlers as $annotationClass => $handler) {
157 4
            $annotation = $this->getAnnotation($object, $annotationClass);
158 4
            if ($annotation === null) {
159 4
                continue;
160
            }
161
162 4
            $json = $handler->hydrate($annotation, $json, $object);
163
        }
164
165 4
        return $json;
166
    }
167
168 4
    protected function getEmptyOrResource(string $class, array $json): string
169
    {
170 4
        if (count($json) > 0) {
171 4
            return $class;
172
        }
173
174 4
        $annotation = $this->getAnnotation(
175 4
            new $class($this->container->get(LoopInterface::class), $this->container->get(CommandBus::class)),
176 4
            EmptyResource::class
177
        );
178
179 4
        if (!($annotation instanceof EmptyResource)) {
180
            return $class;
181
        }
182
183
        $emptyClass = $this->options[Options::NAMESPACE] .
184 4
            '\\' .
185 4
            $this->options[Options::NAMESPACE_SUFFIX] .
186 4
            '\\' .
187
            $annotation->getEmptyReplacement();
188
189 4
        if (!class_exists($emptyClass)) {
190
            return $class;
191
        }
192
193 4
        return $emptyClass;
194
    }
195
196
    /**
197
     * @param string $class
198
     * @param ResourceInterface $object
199
     * @return array
200
     */
201 2 View Code Duplication
    public function extract(string $class, ResourceInterface $object): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
202
    {
203 2
        $fullClassName = implode(
204 2
            '\\',
205
            [
206
                $this->options[Options::NAMESPACE],
207 2
                $this->options[Options::NAMESPACE_SUFFIX],
208 2
                $class,
209
            ]
210
        );
211 2
        return $this->extractFQCN($fullClassName, $object);
212
    }
213
214
    /**
215
     * Takes a fully qualified class name and extracts the data for that class from the given $object
216
     * @param string $class
217
     * @param ResourceInterface $object
218
     * @return array
219
     */
220 2
    public function extractFQCN(string $class, ResourceInterface $object): array
221
    {
222 2
        if ($object instanceof EmptyResourceInterface) {
223 2
            return [];
224
        }
225
226 2
        $json = $this->getHydrator($class)->extract($object);
227 2
        $json = $this->extractApplyAnnotations($object, $json);
228 2
        return $json;
229
    }
230
231
    /**
232
     * @param array $json
233
     * @param ResourceInterface $object
234
     * @return array
235
     */
236 2 View Code Duplication
    protected function extractApplyAnnotations(ResourceInterface $object, array $json): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
237
    {
238 2
        foreach ($this->annotationHandlers as $annotationClass => $handler) {
239 2
            $annotation = $this->getAnnotation($object, $annotationClass);
240 2
            if ($annotation === null) {
241 2
                continue;
242
            }
243
244 2
            $json = $handler->extract($annotation, $object, $json);
245
        }
246
247 2
        return $json;
248
    }
249
250
    /**
251
     * @param ResourceInterface $object
252
     * @param string $annotationClass
253
     * @return null|AnnotationInterface
254
     */
255 4
    protected function getAnnotation(ResourceInterface $object, string $annotationClass)
256
    {
257 4
        $class = get_class($object);
258 4
        if (isset($this->annotations[$class][$annotationClass])) {
259 3
            return $this->annotations[$class][$annotationClass];
260
        }
261
262 4
        if (!isset($this->annotations[$class])) {
263 4
            $this->annotations[$class] = [];
264
        }
265
266 4
        $this->annotations[$class][$annotationClass] = $this->recursivelyGetAnnotation($class, $annotationClass);
267 4
        return $this->annotations[$class][$annotationClass];
268
    }
269
270
    /**
271
     * @param string $class
272
     * @param string $annotationClass
273
     * @return null|AnnotationInterface
274
     */
275 4
    protected function recursivelyGetAnnotation(string $class, string $annotationClass)
276
    {
277 4
        if (!class_exists($class)) {
278
            return null;
279
        }
280
281 4
        $annotation = $this->annotationReader
282 4
            ->getClassAnnotation(
283 4
                new ReflectionClass($class),
284
                $annotationClass
285
            )
286
        ;
287
288 4
        if ($annotation !== null &&
289 4
            get_class($annotation) === $annotationClass
290
        ) {
291 4
            return $annotation;
292
        }
293
294 4
        $parentClass = get_parent_class($class);
295
296 4
        if ($parentClass === false || !class_exists($parentClass)) {
297 4
            return null;
298
        }
299
300 4
        return $this->recursivelyGetAnnotation($parentClass, $annotationClass);
301
    }
302
303
    /**
304
     * @param string $resource
305
     * @param ResourceInterface $object
306
     * @return ResourceInterface
307
     */
308 1
    public function buildAsyncFromSync(string $resource, ResourceInterface $object): ResourceInterface
309
    {
310 1
        return $this->hydrateFQCN(
311
            $this->options[Options::NAMESPACE] . '\\Async\\' . $resource,
312 1
            $this->extractFQCN(
313
                $this->options[Options::NAMESPACE] . '\\Sync\\' . $resource,
314
                $object
315
            )
316
        );
317
    }
318
319
    /**
320
     * @param string $class
321
     * @return HydratorInterface
322
     */
323 6
    protected function getHydrator(string $class): HydratorInterface
324
    {
325 6
        if (isset($this->hydrators[$class])) {
326 4
            return $this->hydrators[$class];
327
        }
328
329 6
        $config = new Configuration($class);
330 6
        if (isset($this->options[Options::RESOURCE_CACHE_DIR])) {
331 6
            $config->setGeneratedClassesTargetDir($this->options[Options::RESOURCE_CACHE_DIR]);
332
        }
333 6
        if (isset($this->options[Options::RESOURCE_NAMESPACE])) {
334 6
            $config->setGeneratedClassesNamespace($this->options[Options::RESOURCE_NAMESPACE]);
335
        }
336 6
        $hydrator = $config->createFactory()->getHydratorClass();
337 6
        $this->hydrators[$class] = new $hydrator;
338
339 6
        return $this->hydrators[$class];
340
    }
341
}
342