Passed
Branch master (6c65a4)
by Christian
16:31
created

ClassSchema   F

Complexity

Total Complexity 91

Size/Duplication

Total Lines 725
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 725
rs 1.263
c 0
b 0
f 0
wmc 91

31 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 27 4
A hasConstructor() 0 3 1
A getInjectProperties() 0 8 2
A getTags() 0 3 1
A isAggregateRoot() 0 3 1
A isValueObject() 0 3 1
A setModelType() 0 10 3
A isSingleton() 0 3 1
A isEntity() 0 3 1
A getConstructorArguments() 0 7 2
A hasMethod() 0 3 1
F reflectProperties() 0 136 25
F reflectMethods() 0 116 21
A getUuidPropertyName() 0 7 1
A hasProperty() 0 3 1
A hasInjectMethodName() 0 14 4
A addProperty() 0 12 1
A getMethod() 0 3 1
A hasInjectMethods() 0 3 1
A getMethods() 0 3 1
A getClassName() 0 3 1
A getModelType() 0 7 1
A getProperty() 0 3 2
A markAsIdentityProperty() 0 13 3
A getIdentityProperties() 0 7 1
A hasInjectProperties() 0 3 1
A getProperties() 0 3 1
A isModel() 0 3 2
A getInjectMethods() 0 8 2
A setAggregateRoot() 0 3 1
A setUuidPropertyName() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like ClassSchema 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 ClassSchema, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace TYPO3\CMS\Extbase\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 Doctrine\Common\Annotations\AnnotationReader;
18
use TYPO3\CMS\Core\SingletonInterface;
19
use TYPO3\CMS\Core\Utility\ClassNamingUtility;
20
use TYPO3\CMS\Extbase\Annotation\IgnoreValidation;
21
use TYPO3\CMS\Extbase\Annotation\Inject;
22
use TYPO3\CMS\Extbase\Annotation\ORM\Cascade;
23
use TYPO3\CMS\Extbase\Annotation\ORM\Lazy;
24
use TYPO3\CMS\Extbase\Annotation\ORM\Transient;
25
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
26
use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
27
use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
28
29
/**
30
 * A class schema
31
 *
32
 * @internal
33
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
34
 */
35
class ClassSchema
36
{
37
    /**
38
     * Available model types
39
     */
40
    const MODELTYPE_ENTITY = 1;
41
    const MODELTYPE_VALUEOBJECT = 2;
42
43
    /**
44
     * Name of the class this schema is referring to
45
     *
46
     * @var string
47
     */
48
    protected $className;
49
50
    /**
51
     * Model type of the class this schema is referring to
52
     *
53
     * @var int
54
     */
55
    protected $modelType = self::MODELTYPE_ENTITY;
56
57
    /**
58
     * Whether a repository exists for the class this schema is referring to
59
     *
60
     * @var bool
61
     */
62
    protected $aggregateRoot = false;
63
64
    /**
65
     * The name of the property holding the uuid of an entity, if any.
66
     *
67
     * @var string
68
     */
69
    protected $uuidPropertyName;
70
71
    /**
72
     * Properties of the class which need to be persisted
73
     *
74
     * @var array
75
     */
76
    protected $properties = [];
77
78
    /**
79
     * The properties forming the identity of an object
80
     *
81
     * @var array
82
     */
83
    protected $identityProperties = [];
84
85
    /**
86
     * Indicates if the class is a singleton or not.
87
     *
88
     * @var bool
89
     */
90
    private $isSingleton;
91
92
    /**
93
     * @var array
94
     */
95
    private $methods;
96
97
    /**
98
     * @var array
99
     */
100
    private $tags = [];
101
102
    /**
103
     * @var array
104
     */
105
    private $injectProperties = [];
106
107
    /**
108
     * @var array
109
     */
110
    private $injectMethods = [];
111
112
    /**
113
     * Constructs this class schema
114
     *
115
     * @param string $className Name of the class this schema is referring to
116
     * @throws \TYPO3\CMS\Extbase\Reflection\Exception\UnknownClassException
117
     * @throws \ReflectionException
118
     */
119
    public function __construct($className)
120
    {
121
        $this->className = $className;
122
123
        $reflectionClass = new \ReflectionClass($className);
124
125
        $this->isSingleton = $reflectionClass->implementsInterface(SingletonInterface::class);
126
127
        if ($reflectionClass->isSubclassOf(AbstractEntity::class)) {
128
            $this->modelType = static::MODELTYPE_ENTITY;
129
130
            $possibleRepositoryClassName = ClassNamingUtility::translateModelNameToRepositoryName($className);
131
            if (class_exists($possibleRepositoryClassName)) {
132
                $this->setAggregateRoot(true);
133
            }
134
        }
135
136
        if ($reflectionClass->isSubclassOf(AbstractValueObject::class)) {
137
            $this->modelType = static::MODELTYPE_VALUEOBJECT;
138
        }
139
140
        $docCommentParser = new DocCommentParser(true);
141
        $docCommentParser->parseDocComment($reflectionClass->getDocComment());
142
        $this->tags = $docCommentParser->getTagsValues();
143
144
        $this->reflectProperties($reflectionClass);
145
        $this->reflectMethods($reflectionClass);
146
    }
147
148
    /**
149
     * @param \ReflectionClass $reflectionClass
150
     */
151
    protected function reflectProperties(\ReflectionClass $reflectionClass)
152
    {
153
        $annotationReader = new AnnotationReader();
154
155
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
156
            $propertyName = $reflectionProperty->getName();
157
158
            $this->properties[$propertyName] = [
159
                'default'     => $reflectionProperty->isDefault(),
160
                'private'     => $reflectionProperty->isPrivate(),
161
                'protected'   => $reflectionProperty->isProtected(),
162
                'public'      => $reflectionProperty->isPublic(),
163
                'static'      => $reflectionProperty->isStatic(),
164
                'type'        => null, // Extbase
165
                'elementType' => null, // Extbase
166
                'annotations' => [],
167
                'tags'        => []
168
            ];
169
170
            $docCommentParser = new DocCommentParser(true);
171
            $docCommentParser->parseDocComment($reflectionProperty->getDocComment());
172
            foreach ($docCommentParser->getTagsValues() as $tag => $values) {
173
                $this->properties[$propertyName]['tags'][strtolower($tag)] = $values;
174
            }
175
176
            $this->properties[$propertyName]['annotations']['inject'] = false;
177
            $this->properties[$propertyName]['annotations']['lazy'] = false;
178
            $this->properties[$propertyName]['annotations']['transient'] = false;
179
            $this->properties[$propertyName]['annotations']['type'] = null;
180
            $this->properties[$propertyName]['annotations']['cascade'] = null;
181
            $this->properties[$propertyName]['annotations']['dependency'] = null;
182
            $this->properties[$propertyName]['annotations']['validators'] = [];
183
184
            if ($docCommentParser->isTaggedWith('validate')) {
185
                $this->properties[$propertyName]['annotations']['validators'] = $docCommentParser->getTagValues('validate');
186
            }
187
188
            if ($annotationReader->getPropertyAnnotation($reflectionProperty, Lazy::class) instanceof Lazy) {
189
                $this->properties[$propertyName]['annotations']['lazy'] = true;
190
            }
191
192
            if ($docCommentParser->isTaggedWith('lazy')) {
193
                $this->properties[$propertyName]['annotations']['lazy'] = true;
194
                trigger_error(
195
                    'Tagging properties with @lazy is deprecated and will be removed in TYPO3 v10.0.',
196
                    E_USER_DEPRECATED
197
                );
198
            }
199
200
            if ($annotationReader->getPropertyAnnotation($reflectionProperty, Transient::class) instanceof Transient) {
201
                $this->properties[$propertyName]['annotations']['transient'] = true;
202
            }
203
204
            if ($docCommentParser->isTaggedWith('transient')) {
205
                $this->properties[$propertyName]['annotations']['transient'] = true;
206
                trigger_error(
207
                    'Tagging properties with @transient is deprecated and will be removed in TYPO3 v10.0.',
208
                    E_USER_DEPRECATED
209
                );
210
            }
211
212
            if ($propertyName !== 'settings'
213
                && ($annotationReader->getPropertyAnnotation($reflectionProperty, Inject::class) instanceof Inject)
214
            ) {
215
                try {
216
                    $varValue = ltrim($docCommentParser->getTagValues('var')[0], '\\');
217
                    $this->properties[$propertyName]['annotations']['inject'] = true;
218
                    $this->properties[$propertyName]['annotations']['type'] = $varValue;
219
                    $this->properties[$propertyName]['annotations']['dependency'] = $varValue;
220
221
                    $this->injectProperties[] = $propertyName;
222
                } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
223
                }
224
            }
225
226
            if ($propertyName !== 'settings' && $docCommentParser->isTaggedWith('inject')) {
227
                trigger_error(
228
                    'Tagging properties with @inject is deprecated and will be removed in TYPO3 v10.0.',
229
                    E_USER_DEPRECATED
230
                );
231
                try {
232
                    $varValues = $docCommentParser->getTagValues('var');
233
                    $this->properties[$propertyName]['annotations']['inject'] = true;
234
                    $this->properties[$propertyName]['annotations']['type'] = ltrim($varValues[0], '\\');
235
                    $this->properties[$propertyName]['annotations']['dependency'] = ltrim($varValues[0], '\\');
236
237
                    if (!$reflectionProperty->isPublic()) {
238
                        trigger_error(
239
                            'Using @inject with non-public properties is deprecated since TYPO3 v9.0 and will stop working in TYPO3 v10.0.',
240
                            E_USER_DEPRECATED
241
                        );
242
                    }
243
244
                    $this->injectProperties[] = $propertyName;
245
                } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
246
                }
247
            }
248
249
            if ($docCommentParser->isTaggedWith('var') && !$docCommentParser->isTaggedWith('transient')) {
250
                try {
251
                    $cascadeAnnotationValues = $docCommentParser->getTagValues('cascade');
252
                    $this->properties[$propertyName]['annotations']['cascade'] = $cascadeAnnotationValues[0];
253
                } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
254
                }
255
256
                if ($this->properties[$propertyName]['annotations']['cascade'] !== null) {
257
                    trigger_error(
258
                        'Tagging properties with @cascade is deprecated and will be removed in TYPO3 v10.0.',
259
                        E_USER_DEPRECATED
260
                    );
261
                }
262
263
                if (($annotation = $annotationReader->getPropertyAnnotation($reflectionProperty, Cascade::class)) instanceof Cascade) {
264
                    /** @var Cascade $annotation */
265
                    $this->properties[$propertyName]['annotations']['cascade'] = $annotation->value;
266
                }
267
268
                try {
269
                    $type = TypeHandlingUtility::parseType(implode(' ', $docCommentParser->getTagValues('var')));
270
                } catch (\Exception $e) {
271
                    $type = [
272
                        'type' => null,
273
                        'elementType' => null
274
                    ];
275
                }
276
277
                $this->properties[$propertyName]['type'] = $type['type'] ? ltrim($type['type'], '\\') : null;
278
                $this->properties[$propertyName]['elementType'] = $type['elementType'] ? ltrim($type['elementType'], '\\') : null;
279
            }
280
281
            if ($docCommentParser->isTaggedWith('uuid')) {
282
                $this->setUuidPropertyName($propertyName);
0 ignored issues
show
Deprecated Code introduced by
The function TYPO3\CMS\Extbase\Reflec...::setUuidPropertyName() has been deprecated. ( Ignorable by Annotation )

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

282
                /** @scrutinizer ignore-deprecated */ $this->setUuidPropertyName($propertyName);
Loading history...
283
            }
284
285
            if ($docCommentParser->isTaggedWith('identity')) {
286
                $this->markAsIdentityProperty($propertyName);
0 ignored issues
show
Deprecated Code introduced by
The function TYPO3\CMS\Extbase\Reflec...arkAsIdentityProperty() has been deprecated. ( Ignorable by Annotation )

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

286
                /** @scrutinizer ignore-deprecated */ $this->markAsIdentityProperty($propertyName);
Loading history...
287
            }
288
        }
289
    }
290
291
    /**
292
     * @param \ReflectionClass $reflectionClass
293
     */
294
    protected function reflectMethods(\ReflectionClass $reflectionClass)
295
    {
296
        $annotationReader = new AnnotationReader();
297
298
        foreach ($reflectionClass->getMethods() as $reflectionMethod) {
299
            $methodName = $reflectionMethod->getName();
300
301
            $this->methods[$methodName] = [];
302
            $this->methods[$methodName]['private']      = $reflectionMethod->isPrivate();
303
            $this->methods[$methodName]['protected']    = $reflectionMethod->isProtected();
304
            $this->methods[$methodName]['public']       = $reflectionMethod->isPublic();
305
            $this->methods[$methodName]['static']       = $reflectionMethod->isStatic();
306
            $this->methods[$methodName]['abstract']     = $reflectionMethod->isAbstract();
307
            $this->methods[$methodName]['params']       = [];
308
            $this->methods[$methodName]['tags']         = [];
309
            $this->methods[$methodName]['annotations']  = [];
310
311
            $docCommentParser = new DocCommentParser(true);
312
            $docCommentParser->parseDocComment($reflectionMethod->getDocComment());
313
314
            $this->methods[$methodName]['annotations']['validators'] = [];
315
316
            foreach ($docCommentParser->getTagsValues() as $tag => $values) {
317
                if ($tag === 'ignorevalidation') {
318
                    trigger_error(
319
                        'Tagging methods with @ignorevalidation is deprecated and will be removed in TYPO3 v10.0.',
320
                        E_USER_DEPRECATED
321
                    );
322
                }
323
                if ($tag === 'validate') {
324
                    $this->methods[$methodName]['annotations']['validators'] = $values;
325
                }
326
                $this->methods[$methodName]['tags'][$tag] = array_map(function ($value) use ($tag) {
327
                    // not stripping the dollar sign for @validate annotations is just
328
                    // a quick fix for a regression introduced in 9.0.0.
329
                    // This exception to the rules will vanish once the resolving of
330
                    // validators will take place inside this class and not in the
331
                    // controller during runtime.
332
                    return $tag === 'validate' ? $value : ltrim($value, '$');
333
                }, $values);
334
            }
335
336
            foreach ($annotationReader->getMethodAnnotations($reflectionMethod) as $annotation) {
337
                if ($annotation instanceof IgnoreValidation) {
338
                    $this->methods[$methodName]['tags']['ignorevalidation'][] = $annotation->argumentName;
339
                }
340
            }
341
342
            $this->methods[$methodName]['description'] = $docCommentParser->getDescription();
343
344
            foreach ($reflectionMethod->getParameters() as $parameterPosition => $reflectionParameter) {
345
                /* @var $reflectionParameter \ReflectionParameter */
346
347
                $parameterName = $reflectionParameter->getName();
348
349
                $this->methods[$methodName]['params'][$parameterName] = [];
350
                $this->methods[$methodName]['params'][$parameterName]['position'] = $parameterPosition; // compat
351
                $this->methods[$methodName]['params'][$parameterName]['byReference'] = $reflectionParameter->isPassedByReference(); // compat
352
                $this->methods[$methodName]['params'][$parameterName]['array'] = $reflectionParameter->isArray(); // compat
353
                $this->methods[$methodName]['params'][$parameterName]['optional'] = $reflectionParameter->isOptional();
354
                $this->methods[$methodName]['params'][$parameterName]['allowsNull'] = $reflectionParameter->allowsNull(); // compat
355
                $this->methods[$methodName]['params'][$parameterName]['class'] = null; // compat
356
                $this->methods[$methodName]['params'][$parameterName]['type'] = null;
357
                $this->methods[$methodName]['params'][$parameterName]['nullable'] = $reflectionParameter->allowsNull();
358
                $this->methods[$methodName]['params'][$parameterName]['default'] = null;
359
                $this->methods[$methodName]['params'][$parameterName]['hasDefaultValue'] = $reflectionParameter->isDefaultValueAvailable();
360
                $this->methods[$methodName]['params'][$parameterName]['defaultValue'] = null; // compat
361
                $this->methods[$methodName]['params'][$parameterName]['dependency'] = null; // Extbase DI
362
363
                if ($reflectionParameter->isDefaultValueAvailable()) {
364
                    $this->methods[$methodName]['params'][$parameterName]['default'] = $reflectionParameter->getDefaultValue();
365
                    $this->methods[$methodName]['params'][$parameterName]['defaultValue'] = $reflectionParameter->getDefaultValue(); // compat
366
                }
367
368
                if (($reflectionType = $reflectionParameter->getType()) instanceof \ReflectionType) {
369
                    $this->methods[$methodName]['params'][$parameterName]['type'] = (string)$reflectionType;
370
                    $this->methods[$methodName]['params'][$parameterName]['nullable'] = $reflectionType->allowsNull();
371
                }
372
373
                if (($parameterClass = $reflectionParameter->getClass()) instanceof \ReflectionClass) {
374
                    $this->methods[$methodName]['params'][$parameterName]['class'] = $parameterClass->getName();
375
                    $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($parameterClass->getName(), '\\');
376
                } else {
377
                    $methodTagsAndValues = $this->methods[$methodName]['tags'];
378
                    if (isset($methodTagsAndValues['param'], $methodTagsAndValues['param'][$parameterPosition])) {
379
                        $explodedParameters = explode(' ', $methodTagsAndValues['param'][$parameterPosition]);
380
                        if (count($explodedParameters) >= 2) {
381
                            if (TypeHandlingUtility::isSimpleType($explodedParameters[0])) {
382
                                // ensure that short names of simple types are resolved correctly to the long form
383
                                // this is important for all kinds of type checks later on
384
                                $typeInfo = TypeHandlingUtility::parseType($explodedParameters[0]);
385
386
                                $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($typeInfo['type'], '\\');
387
                            } else {
388
                                $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($explodedParameters[0], '\\');
389
                            }
390
                        }
391
                    }
392
                }
393
394
                // Extbase DI
395
                if ($reflectionParameter->getClass() instanceof \ReflectionClass
396
                    && ($reflectionMethod->isConstructor() || $this->hasInjectMethodName($reflectionMethod))
397
                ) {
398
                    $this->methods[$methodName]['params'][$parameterName]['dependency'] = $reflectionParameter->getClass()->getName();
399
                }
400
            }
401
402
            // Extbase
403
            $this->methods[$methodName]['injectMethod'] = false;
404
            if ($this->hasInjectMethodName($reflectionMethod)
405
                && count($this->methods[$methodName]['params']) === 1
406
                && reset($this->methods[$methodName]['params'])['dependency'] !== null
407
            ) {
408
                $this->methods[$methodName]['injectMethod'] = true;
409
                $this->injectMethods[] = $methodName;
410
            }
411
        }
412
    }
413
414
    /**
415
     * Returns the class name this schema is referring to
416
     *
417
     * @return string The class name
418
     */
419
    public function getClassName(): string
420
    {
421
        return $this->className;
422
    }
423
424
    /**
425
     * Adds (defines) a specific property and its type.
426
     *
427
     * @param string $name Name of the property
428
     * @param string $type Type of the property
429
     * @param bool $lazy Whether the property should be lazy-loaded when reconstituting
430
     * @param string $cascade Strategy to cascade the object graph.
431
     * @deprecated
432
     */
433
    public function addProperty($name, $type, $lazy = false, $cascade = '')
434
    {
435
        trigger_error(
436
            'This method will be removed in TYPO3 v10.0, properties will be automatically added on ClassSchema construction.',
437
            E_USER_DEPRECATED
438
        );
439
        $type = TypeHandlingUtility::parseType($type);
440
        $this->properties[$name] = [
441
            'type' => $type['type'],
442
            'elementType' => $type['elementType'],
443
            'lazy' => $lazy,
444
            'cascade' => $cascade
445
        ];
446
    }
447
448
    /**
449
     * Returns the given property defined in this schema. Check with
450
     * hasProperty($propertyName) before!
451
     *
452
     * @param string $propertyName
453
     * @return array
454
     */
455
    public function getProperty($propertyName)
456
    {
457
        return is_array($this->properties[$propertyName]) ? $this->properties[$propertyName] : [];
458
    }
459
460
    /**
461
     * Returns all properties defined in this schema
462
     *
463
     * @return array
464
     */
465
    public function getProperties()
466
    {
467
        return $this->properties;
468
    }
469
470
    /**
471
     * Sets the model type of the class this schema is referring to.
472
     *
473
     * @param int $modelType The model type, one of the MODELTYPE_* constants.
474
     * @throws \InvalidArgumentException
475
     * @deprecated
476
     */
477
    public function setModelType($modelType)
478
    {
479
        trigger_error(
480
            'This method will be removed in TYPO3 v10.0, modelType will be automatically set on ClassSchema construction.',
481
            E_USER_DEPRECATED
482
        );
483
        if ($modelType < self::MODELTYPE_ENTITY || $modelType > self::MODELTYPE_VALUEOBJECT) {
484
            throw new \InvalidArgumentException('"' . $modelType . '" is an invalid model type.', 1212519195);
485
        }
486
        $this->modelType = $modelType;
487
    }
488
489
    /**
490
     * Returns the model type of the class this schema is referring to.
491
     *
492
     * @return int The model type, one of the MODELTYPE_* constants.
493
     * @deprecated
494
     */
495
    public function getModelType()
496
    {
497
        trigger_error(
498
            'This method will be removed in TYPO3 v10.0.',
499
            E_USER_DEPRECATED
500
        );
501
        return $this->modelType;
502
    }
503
504
    /**
505
     * Marks the class if it is root of an aggregate and therefore accessible
506
     * through a repository - or not.
507
     *
508
     * @param bool $isRoot TRUE if it is the root of an aggregate
509
     */
510
    public function setAggregateRoot($isRoot)
511
    {
512
        $this->aggregateRoot = $isRoot;
513
    }
514
515
    /**
516
     * Whether the class is an aggregate root and therefore accessible through
517
     * a repository.
518
     *
519
     * @return bool TRUE if it is managed
520
     */
521
    public function isAggregateRoot(): bool
522
    {
523
        return $this->aggregateRoot;
524
    }
525
526
    /**
527
     * If the class schema has a certain property.
528
     *
529
     * @param string $propertyName Name of the property
530
     * @return bool
531
     */
532
    public function hasProperty($propertyName): bool
533
    {
534
        return array_key_exists($propertyName, $this->properties);
535
    }
536
537
    /**
538
     * Sets the property marked as uuid of an object with @uuid
539
     *
540
     * @param string $propertyName
541
     * @throws \InvalidArgumentException
542
     * @deprecated
543
     */
544
    public function setUuidPropertyName($propertyName)
545
    {
546
        trigger_error(
547
            'Tagging properties with @uuid is deprecated and will be removed in TYPO3 v10.0.',
548
            E_USER_DEPRECATED
549
        );
550
        if (!array_key_exists($propertyName, $this->properties)) {
551
            throw new \InvalidArgumentException('Property "' . $propertyName . '" must be added to the class schema before it can be marked as UUID property.', 1233863842);
552
        }
553
        $this->uuidPropertyName = $propertyName;
554
    }
555
556
    /**
557
     * Gets the name of the property marked as uuid of an object
558
     *
559
     * @return string
560
     * @deprecated
561
     */
562
    public function getUuidPropertyName()
563
    {
564
        trigger_error(
565
            'Tagging properties with @uuid is deprecated and will be removed in TYPO3 v10.0.',
566
            E_USER_DEPRECATED
567
        );
568
        return $this->uuidPropertyName;
569
    }
570
571
    /**
572
     * Marks the given property as one of properties forming the identity
573
     * of an object. The property must already be registered in the class
574
     * schema.
575
     *
576
     * @param string $propertyName
577
     * @throws \InvalidArgumentException
578
     * @deprecated
579
     */
580
    public function markAsIdentityProperty($propertyName)
581
    {
582
        trigger_error(
583
            'Tagging properties with @identity is deprecated and will be removed in TYPO3 v10.0.',
584
            E_USER_DEPRECATED
585
        );
586
        if (!array_key_exists($propertyName, $this->properties)) {
587
            throw new \InvalidArgumentException('Property "' . $propertyName . '" must be added to the class schema before it can be marked as identity property.', 1233775407);
588
        }
589
        if ($this->properties[$propertyName]['annotations']['lazy'] === true) {
590
            throw new \InvalidArgumentException('Property "' . $propertyName . '" must not be makred for lazy loading to be marked as identity property.', 1239896904);
591
        }
592
        $this->identityProperties[$propertyName] = $this->properties[$propertyName]['type'];
593
    }
594
595
    /**
596
     * Gets the properties (names and types) forming the identity of an object.
597
     *
598
     * @return array
599
     * @see markAsIdentityProperty()
600
     * @deprecated
601
     */
602
    public function getIdentityProperties()
603
    {
604
        trigger_error(
605
            'Tagging properties with @identity is deprecated and will be removed in TYPO3 v10.0.',
606
            E_USER_DEPRECATED
607
        );
608
        return $this->identityProperties;
609
    }
610
611
    /**
612
     * @return bool
613
     */
614
    public function hasConstructor(): bool
615
    {
616
        return isset($this->methods['__construct']);
617
    }
618
619
    /**
620
     * @param string $name
621
     * @return array
622
     */
623
    public function getMethod(string $name): array
624
    {
625
        return $this->methods[$name] ?? [];
626
    }
627
628
    /**
629
     * @return array
630
     */
631
    public function getMethods(): array
632
    {
633
        return $this->methods;
634
    }
635
636
    /**
637
     * @param \ReflectionMethod $reflectionMethod
638
     * @return bool
639
     */
640
    protected function hasInjectMethodName(\ReflectionMethod $reflectionMethod): bool
641
    {
642
        $methodName = $reflectionMethod->getName();
643
        if ($methodName === 'injectSettings' || !$reflectionMethod->isPublic()) {
644
            return false;
645
        }
646
647
        if (
648
            strpos($reflectionMethod->getName(), 'inject') === 0
649
        ) {
650
            return true;
651
        }
652
653
        return false;
654
    }
655
656
    /**
657
     * @return bool
658
     * @internal
659
     */
660
    public function isModel(): bool
661
    {
662
        return $this->isEntity() || $this->isValueObject();
663
    }
664
665
    /**
666
     * @return bool
667
     * @internal
668
     */
669
    public function isEntity(): bool
670
    {
671
        return $this->modelType === static::MODELTYPE_ENTITY;
672
    }
673
674
    /**
675
     * @return bool
676
     * @internal
677
     */
678
    public function isValueObject(): bool
679
    {
680
        return $this->modelType === static::MODELTYPE_VALUEOBJECT;
681
    }
682
683
    /**
684
     * @return bool
685
     */
686
    public function isSingleton(): bool
687
    {
688
        return $this->isSingleton;
689
    }
690
691
    /**
692
     * @param string $methodName
693
     * @return bool
694
     */
695
    public function hasMethod(string $methodName): bool
696
    {
697
        return isset($this->methods[$methodName]);
698
    }
699
700
    /**
701
     * @return array
702
     */
703
    public function getTags(): array
704
    {
705
        return $this->tags;
706
    }
707
708
    /**
709
     * @return bool
710
     */
711
    public function hasInjectProperties(): bool
712
    {
713
        return count($this->injectProperties) > 0;
714
    }
715
716
    /**
717
     * @return bool
718
     */
719
    public function hasInjectMethods(): bool
720
    {
721
        return count($this->injectMethods) > 0;
722
    }
723
724
    /**
725
     * @return array
726
     */
727
    public function getInjectMethods(): array
728
    {
729
        $injectMethods = [];
730
        foreach ($this->injectMethods as $injectMethodName) {
731
            $injectMethods[$injectMethodName] = reset($this->methods[$injectMethodName]['params'])['dependency'];
732
        }
733
734
        return $injectMethods;
735
    }
736
737
    /**
738
     * @return array
739
     */
740
    public function getInjectProperties(): array
741
    {
742
        $injectProperties = [];
743
        foreach ($this->injectProperties as $injectPropertyName) {
744
            $injectProperties[$injectPropertyName] = $this->properties[$injectPropertyName]['annotations']['dependency'];
745
        }
746
747
        return $injectProperties;
748
    }
749
750
    /**
751
     * @return array
752
     */
753
    public function getConstructorArguments(): array
754
    {
755
        if (!$this->hasConstructor()) {
756
            return [];
757
        }
758
759
        return $this->methods['__construct']['params'];
760
    }
761
}
762