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 ( 3961cd...cbeb5e )
by Cees-Jan
12s
created

Hydrator::buildAsyncFromSync()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 10
Ratio 100 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 10
loc 10
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 2
crap 1
1
<?php declare(strict_types=1);
2
3
namespace ApiClients\Foundation\Hydrator;
4
5
use ApiClients\Foundation\Hydrator\Annotation\EmptyResource;
6
use ApiClients\Foundation\Resource\EmptyResourceInterface;
7
use ApiClients\Foundation\Resource\ResourceInterface;
8
use ApiClients\Tools\CommandBus\CommandBusInterface;
9
use Doctrine\Common\Annotations\AnnotationReader;
10
use Doctrine\Common\Annotations\CachedReader;
11
use Doctrine\Common\Annotations\Reader;
12
use Doctrine\Common\Cache\Cache;
13
use GeneratedHydrator\Configuration;
14
use React\EventLoop\LoopInterface;
15
use RecursiveDirectoryIterator;
16
use RecursiveIteratorIterator;
17
use ReflectionClass;
18
use Zend\Hydrator\HydratorInterface;
19
20
class Hydrator
21
{
22
    /**
23
     * @var LoopInterface
24
     */
25
    protected $loop;
26
27
    /**
28
     * @var CommandBusInterface
29
     */
30
    protected $commandBus;
31
32
    /**
33
     * @var array
34
     */
35
    protected $options;
36
37
    /**
38
     * @var array
39
     */
40
    protected $hydrators = [];
41
42
    /**
43
     * @var array
44
     */
45
    protected $annotations = [];
46
47
    /**
48
     * @var HandlerInterface[]
49
     */
50
    protected $annotationHandlers = [];
51
52
    /**
53
     * @var Reader
54
     */
55
    protected $annotationReader;
56
57
    /**
58
     * @var array
59
     */
60
    protected $classProperties = [];
61
62
    /**
63
     * @param LoopInterface $loop
64
     * @param CommandBusInterface $commandBus
65
     * @param array $options
66
     */
67 10
    public function __construct(LoopInterface $loop, CommandBusInterface $commandBus, array $options)
68
    {
69 10
        $this->loop = $loop;
70 10
        $this->commandBus = $commandBus;
71 10
        $this->options = $options;
72
73 10
        $reader = new AnnotationReader();
74 10
        if (isset($this->options[Options::ANNOTATION_CACHE]) &&
75 1
            $this->options[Options::ANNOTATION_CACHE] instanceof Cache
76
        ) {
77 1
            $reader = new CachedReader(
78 1
                $reader,
79 1
                $this->options[Options::ANNOTATION_CACHE]
80
            );
81
        }
82 10
        $this->annotationReader = $reader;
83
84 10
        $this->setUpAnnotations();
85 10
    }
86
87 10
    protected function setUpAnnotations()
88
    {
89 10
        if (!isset($this->options[Options::ANNOTATIONS])) {
90
            return;
91
        }
92
93 10
        foreach ($this->options[Options::ANNOTATIONS] as $annotationClass => $handler) {
94 10
            $this->annotationHandlers[$annotationClass] = new $handler($this);
95
        }
96 10
    }
97
98 2
    public function preheat(string $scanTarget, string $namespace)
99
    {
100 2
        $directory = new RecursiveDirectoryIterator($scanTarget);
101 2
        $directory = new RecursiveIteratorIterator($directory);
102
103 2
        foreach ($directory as $node) {
104 2
            if (!is_file($node->getPathname())) {
105 2
                continue;
106
            }
107
108 2
            $file = substr($node->getPathname(), strlen($scanTarget));
109 2
            $file = ltrim($file, DIRECTORY_SEPARATOR);
110 2
            $file = rtrim($file, '.php');
111
112 2
            $class = $namespace . '\\' . str_replace(DIRECTORY_SEPARATOR, '\\', $file);
113
114 2
            if (!class_exists($class)) {
115
                continue;
116
            }
117
118 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...
119
                continue;
120
            }
121
122 2
            $this->getHydrator($class);
123 2
            $this->annotationReader->getClassAnnotations(new ReflectionClass($class));
124
        }
125 2
    }
126
127
    /**
128
     * @param string $class
129
     * @param array $json
130
     * @return ResourceInterface
131
     */
132 5 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...
133
    {
134 5
        $fullClassName = implode(
135 5
            '\\',
136
            [
137
                $this->options[Options::NAMESPACE],
138 5
                $this->options[Options::NAMESPACE_SUFFIX],
139 5
                $class,
140
            ]
141
        );
142 5
        return $this->hydrateFQCN($fullClassName, $json);
143
    }
144
145
    /**
146
     * @param string $class
147
     * @param array $json
148
     * @return ResourceInterface
149
     */
150 7
    public function hydrateFQCN(string $class, array $json): ResourceInterface
151
    {
152 7
        $class = $this->getEmptyOrResource($class, $json);
153 7
        $hydrator = $this->getHydrator($class);
154 6
        $object = new $class(
155 6
            $this->loop,
156 6
            $this->commandBus
157
        );
158 6
        $json = $this->hydrateApplyAnnotations($json, $object);
159 6
        $json = $this->ensureMissingValuesAreNull($json, $class);
160 6
        $resource = $hydrator->hydrate($json, $object);
161 6
        return $resource;
162
    }
163
164
    /**
165
     * @param array $json
166
     * @param ResourceInterface $object
167
     * @return array
168
     */
169 6 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...
170
    {
171 6
        foreach ($this->annotationHandlers as $annotationClass => $handler) {
172 6
            $annotation = $this->getAnnotation($object, $annotationClass);
173 6
            if ($annotation === null) {
174 5
                continue;
175
            }
176
177 6
            $json = $handler->hydrate($annotation, $json, $object);
178
        }
179
180 6
        return $json;
181
    }
182
183
    /**
184
     * Ensure all properties expected by resource are available
185
     *
186
     * @param array $json
187
     * @param string $class
188
     * @return array
189
     */
190 6
    protected function ensureMissingValuesAreNull(array $json, string $class): array
191
    {
192 6
        foreach ($this->getReflectionClassProperties($class) as $key) {
193 6
            if (isset($json[$key])) {
194 6
                continue;
195
            }
196
197 2
            $json[$key] = null;
198
        }
199
200 6
        return $json;
201
    }
202
203
    /**
204
     * @param string $class
205
     * @return string[]
206
     */
207 6
    protected function getReflectionClassProperties(string $class): array
208
    {
209 6
        if (isset($this->classProperties[$class])) {
210 5
            return $this->classProperties[$class];
211
        }
212
213 6
        $this->classProperties[$class] = [];
214 6
        foreach ((new ReflectionClass($class))->getProperties() as $property) {
215 6
            $this->classProperties[$class][] = (string)$property->getName();
216
        }
217 6
        return $this->classProperties[$class];
218
    }
219
220 7
    protected function getEmptyOrResource(string $class, array $json): string
221
    {
222 7
        if (count($json) > 0) {
223 7
            return $class;
224
        }
225
226 5
        $annotation = $this->getAnnotation(
227 5
            new $class(
228 5
                $this->loop,
229 5
                $this->commandBus
230
            ),
231 5
            EmptyResource::class
232
        );
233
234 5
        if (!($annotation instanceof EmptyResource)) {
235
            return $class;
236
        }
237
238
        $emptyClass = $this->options[Options::NAMESPACE] .
239 5
            '\\' .
240 5
            $this->options[Options::NAMESPACE_SUFFIX] .
241 5
            '\\' .
242
            $annotation->getEmptyReplacement();
243
244 5
        if (!class_exists($emptyClass)) {
245
            return $class;
246
        }
247
248 5
        return $emptyClass;
249
    }
250
251
    /**
252
     * @param string $class
253
     * @param ResourceInterface $object
254
     * @return array
255
     */
256 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...
257
    {
258 2
        $fullClassName = implode(
259 2
            '\\',
260
            [
261
                $this->options[Options::NAMESPACE],
262 2
                $this->options[Options::NAMESPACE_SUFFIX],
263 2
                $class,
264
            ]
265
        );
266 2
        return $this->extractFQCN($fullClassName, $object);
267
    }
268
269
    /**
270
     * Takes a fully qualified class name and extracts the data for that class from the given $object
271
     * @param string $class
272
     * @param ResourceInterface $object
273
     * @return array
274
     */
275 3
    public function extractFQCN(string $class, ResourceInterface $object): array
276
    {
277 3
        if ($object instanceof EmptyResourceInterface) {
278 2
            return [];
279
        }
280
281 3
        $json = $this->getHydrator($class)->extract($object);
282 3
        $json = $this->extractApplyAnnotations($object, $json);
283 3
        return $json;
284
    }
285
286
    /**
287
     * @param array $json
288
     * @param ResourceInterface $object
289
     * @return array
290
     */
291 3 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...
292
    {
293 3
        foreach ($this->annotationHandlers as $annotationClass => $handler) {
294 3
            $annotation = $this->getAnnotation($object, $annotationClass);
295 3
            if ($annotation === null) {
296 2
                continue;
297
            }
298
299 3
            $json = $handler->extract($annotation, $object, $json);
300
        }
301
302 3
        return $json;
303
    }
304
305
    /**
306
     * @param ResourceInterface $object
307
     * @param string $annotationClass
308
     * @return null|AnnotationInterface
309
     */
310 6
    protected function getAnnotation(ResourceInterface $object, string $annotationClass)
311
    {
312 6
        $class = get_class($object);
313 6
        if (isset($this->annotations[$class][$annotationClass])) {
314 4
            return $this->annotations[$class][$annotationClass];
315
        }
316
317 6
        if (!isset($this->annotations[$class])) {
318 6
            $this->annotations[$class] = [];
319
        }
320
321 6
        $this->annotations[$class][$annotationClass] = $this->recursivelyGetAnnotation($class, $annotationClass);
322 6
        return $this->annotations[$class][$annotationClass];
323
    }
324
325
    /**
326
     * @param string $class
327
     * @param string $annotationClass
328
     * @return null|AnnotationInterface
329
     */
330 6
    protected function recursivelyGetAnnotation(string $class, string $annotationClass)
331
    {
332 6
        if (!class_exists($class)) {
333
            return null;
334
        }
335
336 6
        $annotation = $this->annotationReader
337 6
            ->getClassAnnotation(
338 6
                new ReflectionClass($class),
339 6
                $annotationClass
340
            )
341
        ;
342
343 6
        if ($annotation !== null &&
344 6
            get_class($annotation) === $annotationClass
345
        ) {
346 6
            return $annotation;
347
        }
348
349 6
        $parentClass = get_parent_class($class);
350
351 6
        if ($parentClass === false || !class_exists($parentClass)) {
352 5
            return null;
353
        }
354
355 6
        return $this->recursivelyGetAnnotation($parentClass, $annotationClass);
356
    }
357
358
    /**
359
     * @param string $resource
360
     * @param ResourceInterface $object
361
     * @return ResourceInterface
362
     */
363 1 View Code Duplication
    public function buildAsyncFromSync(string $resource, ResourceInterface $object): 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...
364
    {
365 1
        return $this->hydrateFQCN(
366
            $this->options[Options::NAMESPACE] . '\\Async\\' . $resource,
367 1
            $this->extractFQCN(
368
                $this->options[Options::NAMESPACE] . '\\Sync\\' . $resource,
369 1
                $object
370
            )
371
        );
372
    }
373
374
    /**
375
     * @param string $resource
376
     * @param ResourceInterface $object
377
     * @return ResourceInterface
378
     */
379 View Code Duplication
    public function buildSyncFromAsync(string $resource, ResourceInterface $object): 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...
380
    {
381
        return $this->hydrateFQCN(
382
            $this->options[Options::NAMESPACE] . '\\Sync\\' . $resource,
383
            $this->extractFQCN(
384
                $this->options[Options::NAMESPACE] . '\\Async\\' . $resource,
385
                $object
386
            )
387
        );
388
    }
389
390
    /**
391
     * @param string $class
392
     * @return HydratorInterface
393
     */
394 9
    protected function getHydrator(string $class): HydratorInterface
395
    {
396 9
        if (isset($this->hydrators[$class])) {
397 6
            return $this->hydrators[$class];
398
        }
399
400
        if (strpos($class, $this->options[Options::NAMESPACE]) !== 0) {
401
            throw OutsideNamespaceException::create($class, $this->options[Options::NAMESPACE]);
402
        }
403
404 8
        $config = new Configuration($class);
405 8
        if (isset($this->options[Options::RESOURCE_CACHE_DIR])) {
406 8
            $config->setGeneratedClassesTargetDir($this->options[Options::RESOURCE_CACHE_DIR]);
407
        }
408 8
        if (isset($this->options[Options::RESOURCE_NAMESPACE])) {
409 8
            $config->setGeneratedClassesNamespace($this->options[Options::RESOURCE_NAMESPACE]);
410
        }
411 8
        $hydrator = $config->createFactory()->getHydratorClass();
412 8
        $this->hydrators[$class] = new $hydrator;
413
414 8
        return $this->hydrators[$class];
415
    }
416
}
417