ReflectionService   F
last analyzed

Complexity

Total Complexity 89

Size/Duplication

Total Lines 592
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
wmc 89
lcom 1
cbo 9
dl 0
loc 592
rs 2
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A injectObjectManager() 0 4 1
A injectConfigurationManager() 0 4 1
A setDataCache() 0 4 1
A initialize() 0 10 2
A isInitialized() 0 4 1
A shutdown() 0 7 2
A getClassTagsValues() 0 10 4
A getClassTagValues() 0 10 4
A getClassPropertyNames() 0 7 3
A getClassSchema() 0 8 3
A hasMethod() 0 13 4
A getMethodTagsValues() 0 13 4
A getMethodParameters() 0 11 3
A getPropertyTagsValues() 0 10 4
A getPropertyTagValues() 0 10 4
A isClassReflected() 0 4 1
A isClassTaggedWith() 0 13 4
A isPropertyTaggedWith() 0 13 4
B reflectClass() 0 33 10
B buildClassSchema() 0 31 10
B convertParameterReflectionToArray() 0 37 11
A getMethodReflection() 0 8 2
A loadFromCache() 0 9 3
A saveToCache() 0 23 3

How to fix   Complexity   

Complex Class

Complex classes like ReflectionService 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 ReflectionService, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Romm\ConfigurationObject\Legacy\Reflection;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use Exception;
18
use TYPO3\CMS\Core\Utility\ClassNamingUtility;
19
use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
20
21
/**
22
 * A backport of the TYPO3.Flow reflection service for acquiring reflection based information.
23
 * Most of the code is based on the TYPO3.Flow reflection service.
24
 *
25
 * @api
26
 */
27
class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface
28
{
29
    /**
30
     * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
31
     */
32
    protected $objectManager;
33
34
    /**
35
     * Whether this service has been initialized.
36
     *
37
     * @var bool
38
     */
39
    protected $initialized = false;
40
41
    /**
42
     * @var \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend
43
     */
44
    protected $dataCache;
45
46
    /**
47
     * Whether class alterations should be detected on each initialization.
48
     *
49
     * @var bool
50
     */
51
    protected $detectClassChanges = false;
52
53
    /**
54
     * All available class names to consider. Class name = key, value is the
55
     * UNIX timestamp the class was reflected.
56
     *
57
     * @var array
58
     */
59
    protected $reflectedClassNames = [];
60
61
    /**
62
     * Array of tags and the names of classes which are tagged with them.
63
     *
64
     * @var array
65
     */
66
    protected $taggedClasses = [];
67
68
    /**
69
     * Array of class names and their tags and values.
70
     *
71
     * @var array
72
     */
73
    protected $classTagsValues = [];
74
75
    /**
76
     * Array of class names, method names and their tags and values.
77
     *
78
     * @var array
79
     */
80
    protected $methodTagsValues = [];
81
82
    /**
83
     * Array of class names, method names, their parameters and additional
84
     * information about the parameters.
85
     *
86
     * @var array
87
     */
88
    protected $methodParameters = [];
89
90
    /**
91
     * Array of class names and names of their properties.
92
     *
93
     * @var array
94
     */
95
    protected $classPropertyNames = [];
96
97
    /**
98
     * Array of class names and names of their methods.
99
     *
100
     * @var array
101
     */
102
    protected $classMethodNames = [];
103
104
    /**
105
     * Array of class names, property names and their tags and values.
106
     *
107
     * @var array
108
     */
109
    protected $propertyTagsValues = [];
110
111
    /**
112
     * List of tags which are ignored while reflecting class and method annotations.
113
     *
114
     * @var array
115
     */
116
    protected $ignoredTags = ['package', 'subpackage', 'license', 'copyright', 'author', 'version', 'const'];
117
118
    /**
119
     * Indicates whether the Reflection cache needs to be updated.
120
     *
121
     * This flag needs to be set as soon as new Reflection information was
122
     * created.
123
     *
124
     * @see reflectClass()
125
     * @see getMethodReflection()
126
     * @var bool
127
     */
128
    protected $dataCacheNeedsUpdate = false;
129
130
    /**
131
     * Local cache for Class schemata
132
     *
133
     * @var array
134
     */
135
    protected $classSchemata = [];
136
137
    /**
138
     * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
139
     */
140
    protected $configurationManager;
141
142
    /**
143
     * @var string
144
     */
145
    protected $cacheIdentifier;
146
147
    /**
148
     * Internal runtime cache of method reflection objects
149
     *
150
     * @var array
151
     */
152
    protected $methodReflections = [];
153
154
    /**
155
     * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
156
     */
157
    public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
158
    {
159
        $this->objectManager = $objectManager;
160
    }
161
162
    /**
163
     * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
164
     */
165
    public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager)
166
    {
167
        $this->configurationManager = $configurationManager;
168
    }
169
170
    /**
171
     * Sets the data cache.
172
     *
173
     * The cache must be set before initializing the Reflection Service.
174
     *
175
     * @param \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend $dataCache Cache for the Reflection service
176
     */
177
    public function setDataCache(\TYPO3\CMS\Core\Cache\Frontend\VariableFrontend $dataCache)
178
    {
179
        $this->dataCache = $dataCache;
180
    }
181
182
    /**
183
     * Initializes this service
184
     *
185
     * @throws Exception
186
     */
187
    public function initialize()
188
    {
189
        if ($this->initialized) {
190
            throw new Exception('The Reflection Service can only be initialized once.', 1232044696);
191
        }
192
        $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
193
        $this->cacheIdentifier = 'ReflectionData_' . $frameworkConfiguration['extensionName'];
194
        $this->loadFromCache();
195
        $this->initialized = true;
196
    }
197
198
    /**
199
     * Returns whether the Reflection Service is initialized.
200
     *
201
     * @return bool true if the Reflection Service is initialized, otherwise false
202
     */
203
    public function isInitialized()
204
    {
205
        return $this->initialized;
206
    }
207
208
    /**
209
     * Shuts the Reflection Service down.
210
     */
211
    public function shutdown()
212
    {
213
        if ($this->dataCacheNeedsUpdate) {
214
            $this->saveToCache();
215
        }
216
        $this->initialized = false;
217
    }
218
219
    /**
220
     * Returns all tags and their values the specified class is tagged with
221
     *
222
     * @param string $className Name of the class
223
     * @return array An array of tags and their values or an empty array if no tags were found
224
     */
225
    public function getClassTagsValues($className)
226
    {
227
        if (!isset($this->reflectedClassNames[$className])) {
228
            $this->reflectClass($className);
229
        }
230
        if (!isset($this->classTagsValues[$className])) {
231
            return [];
232
        }
233
        return isset($this->classTagsValues[$className]) ? $this->classTagsValues[$className] : [];
234
    }
235
236
    /**
237
     * Returns the values of the specified class tag
238
     *
239
     * @param string $className Name of the class containing the property
240
     * @param string $tag Tag to return the values of
241
     * @return array An array of values or an empty array if the tag was not found
242
     */
243
    public function getClassTagValues($className, $tag)
244
    {
245
        if (!isset($this->reflectedClassNames[$className])) {
246
            $this->reflectClass($className);
247
        }
248
        if (!isset($this->classTagsValues[$className])) {
249
            return [];
250
        }
251
        return isset($this->classTagsValues[$className][$tag]) ? $this->classTagsValues[$className][$tag] : [];
252
    }
253
254
    /**
255
     * Returns the names of all properties of the specified class
256
     *
257
     * @param string $className Name of the class to return the property names of
258
     * @return array An array of property names or an empty array if none exist
259
     */
260
    public function getClassPropertyNames($className)
261
    {
262
        if (!isset($this->reflectedClassNames[$className])) {
263
            $this->reflectClass($className);
264
        }
265
        return isset($this->classPropertyNames[$className]) ? $this->classPropertyNames[$className] : [];
266
    }
267
268
    /**
269
     * Returns the class schema for the given class
270
     *
271
     * @param mixed $classNameOrObject The class name or an object
272
     * @return ClassSchema
273
     */
274
    public function getClassSchema($classNameOrObject)
275
    {
276
        $className = is_object($classNameOrObject) ? get_class($classNameOrObject) : $classNameOrObject;
277
        if (isset($this->classSchemata[$className])) {
278
            return $this->classSchemata[$className];
279
        }
280
        return $this->buildClassSchema($className);
281
    }
282
283
    /**
284
     * Wrapper for method_exists() which tells if the given method exists.
285
     *
286
     * @param string $className Name of the class containing the method
287
     * @param string $methodName Name of the method
288
     * @return bool
289
     */
290
    public function hasMethod($className, $methodName)
291
    {
292
        try {
293
            if (!array_key_exists($className, $this->classMethodNames) || !array_key_exists($methodName, $this->classMethodNames[$className])) {
294
                $this->getMethodReflection($className, $methodName);
295
                $this->classMethodNames[$className][$methodName] = true;
296
            }
297
        } catch (\ReflectionException $e) {
298
            // Method does not exist. Store this information in cache.
299
            $this->classMethodNames[$className][$methodName] = null;
300
        }
301
        return isset($this->classMethodNames[$className][$methodName]);
302
    }
303
304
    /**
305
     * Returns all tags and their values the specified method is tagged with
306
     *
307
     * @param string $className Name of the class containing the method
308
     * @param string $methodName Name of the method to return the tags and values of
309
     * @return array An array of tags and their values or an empty array of no tags were found
310
     */
311
    public function getMethodTagsValues($className, $methodName)
312
    {
313
        if (!isset($this->methodTagsValues[$className][$methodName])) {
314
            $method = $this->getMethodReflection($className, $methodName);
315
            $this->methodTagsValues[$className][$methodName] = [];
316
            foreach ($method->getTagsValues() as $tag => $values) {
317
                if (array_search($tag, $this->ignoredTags) === false) {
318
                    $this->methodTagsValues[$className][$methodName][$tag] = $values;
319
                }
320
            }
321
        }
322
        return $this->methodTagsValues[$className][$methodName];
323
    }
324
325
    /**
326
     * Returns an array of parameters of the given method. Each entry contains
327
     * additional information about the parameter position, type hint etc.
328
     *
329
     * @param string $className Name of the class containing the method
330
     * @param string $methodName Name of the method to return parameter information of
331
     * @return array An array of parameter names and additional information or an empty array of no parameters were found
332
     */
333
    public function getMethodParameters($className, $methodName)
334
    {
335
        if (!isset($this->methodParameters[$className][$methodName])) {
336
            $method = $this->getMethodReflection($className, $methodName);
337
            $this->methodParameters[$className][$methodName] = [];
338
            foreach ($method->getParameters() as $parameterPosition => $parameter) {
339
                $this->methodParameters[$className][$methodName][$parameter->getName()] = $this->convertParameterReflectionToArray($parameter, $parameterPosition, $method);
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
340
            }
341
        }
342
        return $this->methodParameters[$className][$methodName];
343
    }
344
345
    /**
346
     * Returns all tags and their values the specified class property is tagged with
347
     *
348
     * @param string $className Name of the class containing the property
349
     * @param string $propertyName Name of the property to return the tags and values of
350
     * @return array An array of tags and their values or an empty array of no tags were found
351
     */
352
    public function getPropertyTagsValues($className, $propertyName)
353
    {
354
        if (!isset($this->reflectedClassNames[$className])) {
355
            $this->reflectClass($className);
356
        }
357
        if (!isset($this->propertyTagsValues[$className])) {
358
            return [];
359
        }
360
        return isset($this->propertyTagsValues[$className][$propertyName]) ? $this->propertyTagsValues[$className][$propertyName] : [];
361
    }
362
363
    /**
364
     * Returns the values of the specified class property tag
365
     *
366
     * @param string $className Name of the class containing the property
367
     * @param string $propertyName Name of the tagged property
368
     * @param string $tag Tag to return the values of
369
     * @return array An array of values or an empty array if the tag was not found
370
     */
371
    public function getPropertyTagValues($className, $propertyName, $tag)
372
    {
373
        if (!isset($this->reflectedClassNames[$className])) {
374
            $this->reflectClass($className);
375
        }
376
        if (!isset($this->propertyTagsValues[$className][$propertyName])) {
377
            return [];
378
        }
379
        return isset($this->propertyTagsValues[$className][$propertyName][$tag]) ? $this->propertyTagsValues[$className][$propertyName][$tag] : [];
380
    }
381
382
    /**
383
     * Tells if the specified class is known to this reflection service and
384
     * reflection information is available.
385
     *
386
     * @param string $className Name of the class
387
     * @return bool If the class is reflected by this service
388
     */
389
    public function isClassReflected($className)
390
    {
391
        return isset($this->reflectedClassNames[$className]);
392
    }
393
394
    /**
395
     * Tells if the specified class is tagged with the given tag
396
     *
397
     * @param string $className Name of the class
398
     * @param string $tag Tag to check for
399
     * @return bool TRUE if the class is tagged with $tag, otherwise FALSE
400
     */
401
    public function isClassTaggedWith($className, $tag)
402
    {
403
        if ($this->initialized === false) {
404
            return false;
405
        }
406
        if (!isset($this->reflectedClassNames[$className])) {
407
            $this->reflectClass($className);
408
        }
409
        if (!isset($this->classTagsValues[$className])) {
410
            return false;
411
        }
412
        return isset($this->classTagsValues[$className][$tag]);
413
    }
414
415
    /**
416
     * Tells if the specified class property is tagged with the given tag
417
     *
418
     * @param string $className Name of the class
419
     * @param string $propertyName Name of the property
420
     * @param string $tag Tag to check for
421
     * @return bool TRUE if the class property is tagged with $tag, otherwise FALSE
422
     */
423
    public function isPropertyTaggedWith($className, $propertyName, $tag)
424
    {
425
        if (!isset($this->reflectedClassNames[$className])) {
426
            $this->reflectClass($className);
427
        }
428
        if (!isset($this->propertyTagsValues[$className])) {
429
            return false;
430
        }
431
        if (!isset($this->propertyTagsValues[$className][$propertyName])) {
432
            return false;
433
        }
434
        return isset($this->propertyTagsValues[$className][$propertyName][$tag]);
435
    }
436
437
    /**
438
     * Reflects the given class and stores the results in this service's properties.
439
     *
440
     * @param string $className Full qualified name of the class to reflect
441
     */
442
    protected function reflectClass($className)
443
    {
444
        $class = new ClassReflection($className);
445
        $this->reflectedClassNames[$className] = time();
446
        foreach ($class->getTagsValues() as $tag => $values) {
447
            if (array_search($tag, $this->ignoredTags) === false) {
448
                $this->taggedClasses[$tag][] = $className;
449
                $this->classTagsValues[$className][$tag] = $values;
450
            }
451
        }
452
        foreach ($class->getProperties() as $property) {
453
            $propertyName = $property->getName();
454
            $this->classPropertyNames[$className][] = $propertyName;
455
            foreach ($property->getTagsValues() as $tag => $values) {
456
                if (array_search($tag, $this->ignoredTags) === false) {
457
                    $this->propertyTagsValues[$className][$propertyName][$tag] = $values;
458
                }
459
            }
460
        }
461
        foreach ($class->getMethods() as $method) {
462
            $methodName = $method->getName();
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
463
            foreach ($method->getTagsValues() as $tag => $values) {
464
                if (array_search($tag, $this->ignoredTags) === false) {
465
                    $this->methodTagsValues[$className][$methodName][$tag] = $values;
466
                }
467
            }
468
            foreach ($method->getParameters() as $parameterPosition => $parameter) {
469
                $this->methodParameters[$className][$methodName][$parameter->getName()] = $this->convertParameterReflectionToArray($parameter, $parameterPosition, $method);
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
470
            }
471
        }
472
        ksort($this->reflectedClassNames);
473
        $this->dataCacheNeedsUpdate = true;
474
    }
475
476
    /**
477
     * Builds class schemata from classes annotated as entities or value objects
478
     *
479
     * @param string $className
480
     * @throws Exception
481
     * @return ClassSchema The class schema
482
     */
483
    protected function buildClassSchema($className)
484
    {
485
        if (!class_exists($className)) {
486
            throw new Exception('The classname "' . $className . '" was not found and thus can not be reflected.', 1278450972);
487
        }
488
        $classSchema = $this->objectManager->get(ClassSchema::class, $className);
489
        if (is_subclass_of($className, \TYPO3\CMS\Extbase\DomainObject\AbstractEntity::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 \TYPO3\CMS\Extbase\Domai...t\AbstractEntity::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
490
            $classSchema->setModelType(ClassSchema::MODELTYPE_ENTITY);
491
            $possibleRepositoryClassName = ClassNamingUtility::translateModelNameToRepositoryName($className);
492
            if (class_exists($possibleRepositoryClassName)) {
493
                $classSchema->setAggregateRoot(true);
494
            }
495
        } elseif (is_subclass_of($className, \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject::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 \TYPO3\CMS\Extbase\Domai...tractValueObject::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
496
            $classSchema->setModelType(ClassSchema::MODELTYPE_VALUEOBJECT);
497
        }
498
        foreach ($this->getClassPropertyNames($className) as $propertyName) {
499
            if (!$this->isPropertyTaggedWith($className, $propertyName, 'transient') && $this->isPropertyTaggedWith($className, $propertyName, 'var')) {
500
                $cascadeTagValues = $this->getPropertyTagValues($className, $propertyName, 'cascade');
501
                $classSchema->addProperty($propertyName, implode(' ', $this->getPropertyTagValues($className, $propertyName, 'var')), $this->isPropertyTaggedWith($className, $propertyName, 'lazy'), $cascadeTagValues[0] ?? null);
502
            }
503
            if ($this->isPropertyTaggedWith($className, $propertyName, 'uuid')) {
504
                $classSchema->setUuidPropertyName($propertyName);
505
            }
506
            if ($this->isPropertyTaggedWith($className, $propertyName, 'identity')) {
507
                $classSchema->markAsIdentityProperty($propertyName);
508
            }
509
        }
510
        $this->classSchemata[$className] = $classSchema;
511
        $this->dataCacheNeedsUpdate = true;
512
        return $classSchema;
513
    }
514
515
    /**
516
     * Converts the given parameter reflection into an information array
517
     *
518
     * @param ParameterReflection $parameter The parameter to reflect
519
     * @param int $parameterPosition
520
     * @param MethodReflection|null $method
521
     * @return array Parameter information array
522
     */
523
    protected function convertParameterReflectionToArray(ParameterReflection $parameter, $parameterPosition, MethodReflection $method = null)
524
    {
525
        $parameterInformation = [
526
            'position' => $parameterPosition,
527
            'byReference' => $parameter->isPassedByReference(),
528
            'array' => $parameter->isArray(),
529
            'optional' => $parameter->isOptional(),
530
            'allowsNull' => $parameter->allowsNull()
531
        ];
532
        $parameterClass = $parameter->getClass();
533
        $parameterInformation['class'] = $parameterClass !== null ? $parameterClass->getName() : null;
0 ignored issues
show
Bug introduced by
Consider using $parameterClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
534
        if ($parameter->isDefaultValueAvailable()) {
535
            $parameterInformation['defaultValue'] = $parameter->getDefaultValue();
536
        }
537
        if ($parameterClass !== null) {
538
            $parameterInformation['type'] = $parameterClass->getName();
0 ignored issues
show
Bug introduced by
Consider using $parameterClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
539
        } elseif ($method !== null) {
540
            $methodTagsAndValues = $this->getMethodTagsValues($method->getDeclaringClass()->getName(), $method->getName());
0 ignored issues
show
introduced by
Consider using $method->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
541
            if (isset($methodTagsAndValues['param']) && isset($methodTagsAndValues['param'][$parameterPosition])) {
542
                $explodedParameters = explode(' ', $methodTagsAndValues['param'][$parameterPosition]);
543
                if (count($explodedParameters) >= 2) {
544
                    if (TypeHandlingUtility::isSimpleType($explodedParameters[0])) {
545
                        // ensure that short names of simple types are resolved correctly to the long form
546
                        // this is important for all kinds of type checks later on
547
                        $typeInfo = TypeHandlingUtility::parseType($explodedParameters[0]);
548
                        $parameterInformation['type'] = $typeInfo['type'];
549
                    } else {
550
                        $parameterInformation['type'] = $explodedParameters[0];
551
                    }
552
                }
553
            }
554
        }
555
        if (isset($parameterInformation['type']) && $parameterInformation['type'][0] === '\\') {
556
            $parameterInformation['type'] = substr($parameterInformation['type'], 1);
557
        }
558
        return $parameterInformation;
559
    }
560
561
    /**
562
     * Returns the Reflection of a method.
563
     *
564
     * @param string $className Name of the class containing the method
565
     * @param string $methodName Name of the method to return the Reflection for
566
     * @return MethodReflection the method Reflection object
567
     */
568
    protected function getMethodReflection($className, $methodName)
569
    {
570
        $this->dataCacheNeedsUpdate = true;
571
        if (!isset($this->methodReflections[$className][$methodName])) {
572
            $this->methodReflections[$className][$methodName] = new MethodReflection($className, $methodName);
573
        }
574
        return $this->methodReflections[$className][$methodName];
575
    }
576
577
    /**
578
     * Tries to load the reflection data from this service's cache.
579
     */
580
    protected function loadFromCache()
581
    {
582
        $data = $this->dataCache->get($this->cacheIdentifier);
583
        if ($data !== false) {
584
            foreach ($data as $propertyName => $propertyValue) {
585
                $this->{$propertyName} = $propertyValue;
586
            }
587
        }
588
    }
589
590
    /**
591
     * Exports the internal reflection data into the ReflectionData cache.
592
     *
593
     * @throws Exception
594
     */
595
    protected function saveToCache()
596
    {
597
        if (!is_object($this->dataCache)) {
598
            throw new Exception('A cache must be injected before initializing the Reflection Service.', 1232044697);
599
        }
600
        $data = [];
601
        $propertyNames = [
602
            'reflectedClassNames',
603
            'classPropertyNames',
604
            'classMethodNames',
605
            'classTagsValues',
606
            'methodTagsValues',
607
            'methodParameters',
608
            'propertyTagsValues',
609
            'taggedClasses',
610
            'classSchemata'
611
        ];
612
        foreach ($propertyNames as $propertyName) {
613
            $data[$propertyName] = $this->{$propertyName};
614
        }
615
        $this->dataCache->set($this->cacheIdentifier, $data);
616
        $this->dataCacheNeedsUpdate = false;
617
    }
618
}
619