[TASK] Deprecate Extbase CommandControllers and @cli annotation
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Reflection / ClassSchema.php
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\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Core\Utility\StringUtility;
22 use TYPO3\CMS\Extbase\Annotation\IgnoreValidation;
23 use TYPO3\CMS\Extbase\Annotation\Inject;
24 use TYPO3\CMS\Extbase\Annotation\ORM\Cascade;
25 use TYPO3\CMS\Extbase\Annotation\ORM\Lazy;
26 use TYPO3\CMS\Extbase\Annotation\ORM\Transient;
27 use TYPO3\CMS\Extbase\Annotation\Validate;
28 use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
29 use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
30 use TYPO3\CMS\Extbase\Mvc\Controller\ControllerInterface;
31 use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
32 use TYPO3\CMS\Extbase\Validation\Exception\InvalidTypeHintException;
33 use TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationConfigurationException;
34 use TYPO3\CMS\Extbase\Validation\ValidatorResolver;
35
36 /**
37 * A class schema
38 *
39 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
40 */
41 class ClassSchema
42 {
43 /**
44 * Available model types
45 */
46 const MODELTYPE_ENTITY = 1;
47 const MODELTYPE_VALUEOBJECT = 2;
48
49 /**
50 * Name of the class this schema is referring to
51 *
52 * @var string
53 */
54 protected $className;
55
56 /**
57 * Model type of the class this schema is referring to
58 *
59 * @var int
60 */
61 protected $modelType = self::MODELTYPE_ENTITY;
62
63 /**
64 * Whether a repository exists for the class this schema is referring to
65 *
66 * @var bool
67 */
68 protected $aggregateRoot = false;
69
70 /**
71 * The name of the property holding the uuid of an entity, if any.
72 *
73 * @var string
74 */
75 protected $uuidPropertyName;
76
77 /**
78 * Properties of the class which need to be persisted
79 *
80 * @var array
81 */
82 protected $properties = [];
83
84 /**
85 * The properties forming the identity of an object
86 *
87 * @var array
88 */
89 protected $identityProperties = [];
90
91 /**
92 * Indicates if the class is a singleton or not.
93 *
94 * @var bool
95 */
96 private $isSingleton;
97
98 /**
99 * @var bool
100 */
101 private $isController;
102
103 /**
104 * @var array
105 */
106 private $methods;
107
108 /**
109 * @var array
110 */
111 private $tags;
112
113 /**
114 * @var array
115 */
116 private $injectProperties = [];
117
118 /**
119 * @var array
120 */
121 private $injectMethods = [];
122
123 /**
124 * Constructs this class schema
125 *
126 * @param string $className Name of the class this schema is referring to
127 * @throws \TYPO3\CMS\Extbase\Reflection\Exception\UnknownClassException
128 * @throws \ReflectionException
129 */
130 public function __construct($className)
131 {
132 $this->className = $className;
133
134 $reflectionClass = new \ReflectionClass($className);
135
136 $this->isSingleton = $reflectionClass->implementsInterface(SingletonInterface::class);
137 $this->isController = $reflectionClass->implementsInterface(ControllerInterface::class);
138
139 if ($reflectionClass->isSubclassOf(AbstractEntity::class)) {
140 $this->modelType = static::MODELTYPE_ENTITY;
141
142 $possibleRepositoryClassName = ClassNamingUtility::translateModelNameToRepositoryName($className);
143 if (class_exists($possibleRepositoryClassName)) {
144 $this->setAggregateRoot(true);
145 }
146 }
147
148 if ($reflectionClass->isSubclassOf(AbstractValueObject::class)) {
149 $this->modelType = static::MODELTYPE_VALUEOBJECT;
150 }
151
152 $docCommentParser = new DocCommentParser(true);
153 $docCommentParser->parseDocComment($reflectionClass->getDocComment());
154 $this->tags = $docCommentParser->getTagsValues();
155
156 $this->reflectProperties($reflectionClass);
157 $this->reflectMethods($reflectionClass);
158 }
159
160 /**
161 * @param \ReflectionClass $reflectionClass
162 */
163 protected function reflectProperties(\ReflectionClass $reflectionClass)
164 {
165 $annotationReader = new AnnotationReader();
166
167 foreach ($reflectionClass->getProperties() as $reflectionProperty) {
168 $propertyName = $reflectionProperty->getName();
169
170 $this->properties[$propertyName] = [
171 'default' => $reflectionProperty->isDefault(),
172 'private' => $reflectionProperty->isPrivate(),
173 'protected' => $reflectionProperty->isProtected(),
174 'public' => $reflectionProperty->isPublic(),
175 'static' => $reflectionProperty->isStatic(),
176 'type' => null, // Extbase
177 'elementType' => null, // Extbase
178 'annotations' => [],
179 'tags' => [],
180 'validators' => []
181 ];
182
183 $docCommentParser = new DocCommentParser(true);
184 $docCommentParser->parseDocComment($reflectionProperty->getDocComment());
185 foreach ($docCommentParser->getTagsValues() as $tag => $values) {
186 $this->properties[$propertyName]['tags'][strtolower($tag)] = $values;
187 }
188
189 $this->properties[$propertyName]['annotations']['inject'] = false;
190 $this->properties[$propertyName]['annotations']['lazy'] = false;
191 $this->properties[$propertyName]['annotations']['transient'] = false;
192 $this->properties[$propertyName]['annotations']['type'] = null;
193 $this->properties[$propertyName]['annotations']['cascade'] = null;
194 $this->properties[$propertyName]['annotations']['dependency'] = null;
195
196 $annotations = $annotationReader->getPropertyAnnotations($reflectionProperty);
197
198 /** @var array|Validate[] $validateAnnotations */
199 $validateAnnotations = array_filter($annotations, function ($annotation) {
200 return $annotation instanceof Validate;
201 });
202
203 if (count($validateAnnotations) > 0) {
204 $validatorResolver = GeneralUtility::makeInstance(ValidatorResolver::class);
205
206 foreach ($validateAnnotations as $validateAnnotation) {
207 $validatorObjectName = $validatorResolver->resolveValidatorObjectName($validateAnnotation->validator);
208
209 $this->properties[$propertyName]['validators'][] = [
210 'name' => $validateAnnotation->validator,
211 'options' => $validateAnnotation->options,
212 'className' => $validatorObjectName,
213 ];
214 }
215 }
216
217 if ($docCommentParser->isTaggedWith('validate')) {
218 trigger_error(
219 sprintf(
220 'Property %s::%s is tagged with @validate which is deprecated and will be removed in TYPO3 v10.0.',
221 $reflectionClass->getName(),
222 $reflectionProperty->getName()
223 ),
224 E_USER_DEPRECATED
225 );
226
227 $validatorResolver = GeneralUtility::makeInstance(ValidatorResolver::class);
228
229 $validateValues = $docCommentParser->getTagValues('validate');
230 foreach ($validateValues as $validateValue) {
231 $validatorConfiguration = $validatorResolver->parseValidatorAnnotation($validateValue);
232
233 foreach ($validatorConfiguration['validators'] ?? [] as $validator) {
234 $validatorObjectName = $validatorResolver->resolveValidatorObjectName($validator['validatorName']);
235
236 $this->properties[$propertyName]['validators'][] = [
237 'name' => $validator['validatorName'],
238 'options' => $validator['validatorOptions'],
239 'className' => $validatorObjectName,
240 ];
241 }
242 }
243 }
244
245 if ($annotationReader->getPropertyAnnotation($reflectionProperty, Lazy::class) instanceof Lazy) {
246 $this->properties[$propertyName]['annotations']['lazy'] = true;
247 }
248
249 if ($docCommentParser->isTaggedWith('lazy')) {
250 $this->properties[$propertyName]['annotations']['lazy'] = true;
251 trigger_error(
252 sprintf(
253 'Property %s::%s is tagged with @lazy which is deprecated and will be removed in TYPO3 v10.0.',
254 $reflectionClass->getName(),
255 $reflectionProperty->getName()
256 ),
257 E_USER_DEPRECATED
258 );
259 }
260
261 if ($annotationReader->getPropertyAnnotation($reflectionProperty, Transient::class) instanceof Transient) {
262 $this->properties[$propertyName]['annotations']['transient'] = true;
263 }
264
265 if ($docCommentParser->isTaggedWith('transient')) {
266 $this->properties[$propertyName]['annotations']['transient'] = true;
267 trigger_error(
268 sprintf(
269 'Property %s::%s is tagged with @transient which is deprecated and will be removed in TYPO3 v10.0.',
270 $reflectionClass->getName(),
271 $reflectionProperty->getName()
272 ),
273 E_USER_DEPRECATED
274 );
275 }
276
277 if ($propertyName !== 'settings'
278 && ($annotationReader->getPropertyAnnotation($reflectionProperty, Inject::class) instanceof Inject)
279 ) {
280 try {
281 $varValue = ltrim($docCommentParser->getTagValues('var')[0], '\\');
282 $this->properties[$propertyName]['annotations']['inject'] = true;
283 $this->properties[$propertyName]['annotations']['type'] = $varValue;
284 $this->properties[$propertyName]['annotations']['dependency'] = $varValue;
285
286 $this->injectProperties[] = $propertyName;
287 } catch (\Exception $e) {
288 }
289 }
290
291 if ($propertyName !== 'settings' && $docCommentParser->isTaggedWith('inject')) {
292 trigger_error(
293 sprintf(
294 'Property %s::%s is tagged with @inject which is deprecated and will be removed in TYPO3 v10.0.',
295 $reflectionClass->getName(),
296 $reflectionProperty->getName()
297 ),
298 E_USER_DEPRECATED
299 );
300 try {
301 $varValues = $docCommentParser->getTagValues('var');
302 $this->properties[$propertyName]['annotations']['inject'] = true;
303 $this->properties[$propertyName]['annotations']['type'] = ltrim($varValues[0], '\\');
304 $this->properties[$propertyName]['annotations']['dependency'] = ltrim($varValues[0], '\\');
305
306 if (!$reflectionProperty->isPublic()) {
307 trigger_error(
308 sprintf(
309 'Property %s::%s is not public and tagged with @inject which is deprecated and will stop working in TYPO3 v10.0.',
310 $reflectionClass->getName(),
311 $reflectionProperty->getName()
312 ),
313 E_USER_DEPRECATED
314 );
315 }
316
317 $this->injectProperties[] = $propertyName;
318 } catch (\Exception $e) {
319 }
320 }
321
322 if ($docCommentParser->isTaggedWith('var') && !$docCommentParser->isTaggedWith('transient')) {
323 try {
324 $cascadeAnnotationValues = $docCommentParser->getTagValues('cascade');
325 $this->properties[$propertyName]['annotations']['cascade'] = $cascadeAnnotationValues[0];
326 } catch (\Exception $e) {
327 }
328
329 if ($this->properties[$propertyName]['annotations']['cascade'] !== null) {
330 trigger_error(
331 sprintf(
332 'Property %s::%s is tagged with @cascade which is deprecated and will be removed in TYPO3 v10.0.',
333 $reflectionClass->getName(),
334 $reflectionProperty->getName()
335 ),
336 E_USER_DEPRECATED
337 );
338 }
339
340 if (($annotation = $annotationReader->getPropertyAnnotation($reflectionProperty, Cascade::class)) instanceof Cascade) {
341 /** @var Cascade $annotation */
342 $this->properties[$propertyName]['annotations']['cascade'] = $annotation->value;
343 }
344
345 try {
346 $type = TypeHandlingUtility::parseType(implode(' ', $docCommentParser->getTagValues('var')));
347 } catch (\Exception $e) {
348 $type = [
349 'type' => null,
350 'elementType' => null
351 ];
352 }
353
354 $this->properties[$propertyName]['type'] = $type['type'] ? ltrim($type['type'], '\\') : null;
355 $this->properties[$propertyName]['elementType'] = $type['elementType'] ? ltrim($type['elementType'], '\\') : null;
356 }
357
358 if ($docCommentParser->isTaggedWith('uuid')) {
359 $this->setUuidPropertyName($propertyName);
360 }
361
362 if ($docCommentParser->isTaggedWith('identity')) {
363 $this->markAsIdentityProperty($propertyName);
364 }
365 }
366 }
367
368 /**
369 * @param \ReflectionClass $reflectionClass
370 */
371 protected function reflectMethods(\ReflectionClass $reflectionClass)
372 {
373 $annotationReader = new AnnotationReader();
374
375 foreach ($reflectionClass->getMethods() as $reflectionMethod) {
376 $methodName = $reflectionMethod->getName();
377
378 $this->methods[$methodName] = [];
379 $this->methods[$methodName]['private'] = $reflectionMethod->isPrivate();
380 $this->methods[$methodName]['protected'] = $reflectionMethod->isProtected();
381 $this->methods[$methodName]['public'] = $reflectionMethod->isPublic();
382 $this->methods[$methodName]['static'] = $reflectionMethod->isStatic();
383 $this->methods[$methodName]['abstract'] = $reflectionMethod->isAbstract();
384 $this->methods[$methodName]['params'] = [];
385 $this->methods[$methodName]['tags'] = [];
386 $this->methods[$methodName]['annotations'] = [];
387 $this->methods[$methodName]['isAction'] = StringUtility::endsWith($methodName, 'Action');
388
389 $docCommentParser = new DocCommentParser(true);
390 $docCommentParser->parseDocComment($reflectionMethod->getDocComment());
391
392 $argumentValidators = [];
393
394 $annotations = $annotationReader->getMethodAnnotations($reflectionMethod);
395
396 /** @var array|Validate[] $validateAnnotations */
397 $validateAnnotations = array_filter($annotations, function ($annotation) {
398 return $annotation instanceof Validate;
399 });
400
401 if ($this->isController && $this->methods[$methodName]['isAction'] && count($validateAnnotations) > 0) {
402 $validatorResolver = GeneralUtility::makeInstance(ValidatorResolver::class);
403
404 foreach ($validateAnnotations as $validateAnnotation) {
405 $validatorName = $validateAnnotation->validator;
406 $validatorObjectName = $validatorResolver->resolveValidatorObjectName($validatorName);
407
408 $argumentValidators[$validateAnnotation->param][] = [
409 'name' => $validatorName,
410 'options' => $validateAnnotation->options,
411 'className' => $validatorObjectName,
412 ];
413 }
414 }
415
416 foreach ($docCommentParser->getTagsValues() as $tag => $values) {
417 if ($tag === 'cli') {
418 trigger_error(
419 sprintf(
420 'Method %s::%s is tagged with @cli which is deprecated and will be removed in TYPO3 v10.0.',
421 $reflectionClass->getName(),
422 $reflectionMethod->getName()
423 ),
424 E_USER_DEPRECATED
425 );
426 }
427 if ($tag === 'ignorevalidation') {
428 trigger_error(
429 sprintf(
430 'Method %s::%s is tagged with @ignorevalidation which is deprecated and will be removed in TYPO3 v10.0.',
431 $reflectionClass->getName(),
432 $reflectionMethod->getName()
433 ),
434 E_USER_DEPRECATED
435 );
436 }
437 if ($tag === 'validate' && $this->isController && $this->methods[$methodName]['isAction']) {
438 trigger_error(
439 sprintf(
440 'Method %s::%s is tagged with @validate which is deprecated and will be removed in TYPO3 v10.0.',
441 $reflectionClass->getName(),
442 $reflectionMethod->getName()
443 ),
444 E_USER_DEPRECATED
445 );
446
447 $validatorResolver = GeneralUtility::makeInstance(ValidatorResolver::class);
448
449 foreach ($values as $validate) {
450 $methodValidatorDefinition = $validatorResolver->parseValidatorAnnotation($validate);
451
452 foreach ($methodValidatorDefinition['validators'] as $validator) {
453 $validatorObjectName = $validatorResolver->resolveValidatorObjectName($validator['validatorName']);
454
455 $argumentValidators[$methodValidatorDefinition['argumentName']][] = [
456 'name' => $validator['validatorName'],
457 'options' => $validator['validatorOptions'],
458 'className' => $validatorObjectName,
459 ];
460 }
461 }
462 }
463 $this->methods[$methodName]['tags'][$tag] = array_map(function ($value) use ($tag) {
464 // not stripping the dollar sign for @validate annotations is just
465 // a quick fix for a regression introduced in 9.0.0.
466 // This exception to the rules will vanish once the resolving of
467 // validators will take place inside this class and not in the
468 // controller during runtime.
469 return $tag === 'validate' ? $value : ltrim($value, '$');
470 }, $values);
471 }
472 unset($methodValidatorDefinition);
473
474 foreach ($annotations as $annotation) {
475 if ($annotation instanceof IgnoreValidation) {
476 $this->methods[$methodName]['tags']['ignorevalidation'][] = $annotation->argumentName;
477 }
478 }
479
480 $this->methods[$methodName]['description'] = $docCommentParser->getDescription();
481
482 foreach ($reflectionMethod->getParameters() as $parameterPosition => $reflectionParameter) {
483 /* @var $reflectionParameter \ReflectionParameter */
484
485 $parameterName = $reflectionParameter->getName();
486
487 $this->methods[$methodName]['params'][$parameterName] = [];
488 $this->methods[$methodName]['params'][$parameterName]['position'] = $parameterPosition; // compat
489 $this->methods[$methodName]['params'][$parameterName]['byReference'] = $reflectionParameter->isPassedByReference(); // compat
490 $this->methods[$methodName]['params'][$parameterName]['array'] = $reflectionParameter->isArray(); // compat
491 $this->methods[$methodName]['params'][$parameterName]['optional'] = $reflectionParameter->isOptional();
492 $this->methods[$methodName]['params'][$parameterName]['allowsNull'] = $reflectionParameter->allowsNull(); // compat
493 $this->methods[$methodName]['params'][$parameterName]['class'] = null; // compat
494 $this->methods[$methodName]['params'][$parameterName]['type'] = null;
495 $this->methods[$methodName]['params'][$parameterName]['nullable'] = $reflectionParameter->allowsNull();
496 $this->methods[$methodName]['params'][$parameterName]['default'] = null;
497 $this->methods[$methodName]['params'][$parameterName]['hasDefaultValue'] = $reflectionParameter->isDefaultValueAvailable();
498 $this->methods[$methodName]['params'][$parameterName]['defaultValue'] = null; // compat
499 $this->methods[$methodName]['params'][$parameterName]['dependency'] = null; // Extbase DI
500 $this->methods[$methodName]['params'][$parameterName]['validators'] = [];
501
502 if ($reflectionParameter->isDefaultValueAvailable()) {
503 $this->methods[$methodName]['params'][$parameterName]['default'] = $reflectionParameter->getDefaultValue();
504 $this->methods[$methodName]['params'][$parameterName]['defaultValue'] = $reflectionParameter->getDefaultValue(); // compat
505 }
506
507 if (($reflectionType = $reflectionParameter->getType()) instanceof \ReflectionType) {
508 $this->methods[$methodName]['params'][$parameterName]['type'] = (string)$reflectionType;
509 $this->methods[$methodName]['params'][$parameterName]['nullable'] = $reflectionType->allowsNull();
510 }
511
512 if (($parameterClass = $reflectionParameter->getClass()) instanceof \ReflectionClass) {
513 $this->methods[$methodName]['params'][$parameterName]['class'] = $parameterClass->getName();
514 $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($parameterClass->getName(), '\\');
515 } else {
516 $methodTagsAndValues = $this->methods[$methodName]['tags'];
517 if (isset($methodTagsAndValues['param'][$parameterPosition])) {
518 $explodedParameters = explode(' ', $methodTagsAndValues['param'][$parameterPosition]);
519 if (count($explodedParameters) >= 2) {
520 if (TypeHandlingUtility::isSimpleType($explodedParameters[0])) {
521 // ensure that short names of simple types are resolved correctly to the long form
522 // this is important for all kinds of type checks later on
523 $typeInfo = TypeHandlingUtility::parseType($explodedParameters[0]);
524
525 $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($typeInfo['type'], '\\');
526 } else {
527 $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($explodedParameters[0], '\\');
528 }
529 }
530 }
531 }
532
533 // Extbase DI
534 if ($reflectionParameter->getClass() instanceof \ReflectionClass
535 && ($reflectionMethod->isConstructor() || $this->hasInjectMethodName($reflectionMethod))
536 ) {
537 $this->methods[$methodName]['params'][$parameterName]['dependency'] = $reflectionParameter->getClass()->getName();
538 }
539
540 // Extbase Validation
541 if (isset($argumentValidators[$parameterName])) {
542 if ($this->methods[$methodName]['params'][$parameterName]['type'] === null) {
543 throw new InvalidTypeHintException(
544 'Missing type information for parameter "$' . $parameterName . '" in ' . $this->className . '->' . $methodName . '(): Either use an @param annotation or use a type hint.',
545 1515075192
546 );
547 }
548
549 $this->methods[$methodName]['params'][$parameterName]['validators'] = $argumentValidators[$parameterName];
550 unset($argumentValidators[$parameterName]);
551 }
552 }
553
554 // Extbase Validation
555 foreach ($argumentValidators as $parameterName => $validators) {
556 $validatorNames = array_column($validators, 'name');
557
558 throw new InvalidValidationConfigurationException(
559 'Invalid validate annotation in ' . $this->className . '->' . $methodName . '(): The following validators have been defined for missing param "$' . $parameterName . '": ' . implode(', ', $validatorNames),
560 1515073585
561 );
562 }
563
564 // Extbase
565 $this->methods[$methodName]['injectMethod'] = false;
566 if ($this->hasInjectMethodName($reflectionMethod)
567 && count($this->methods[$methodName]['params']) === 1
568 && reset($this->methods[$methodName]['params'])['dependency'] !== null
569 ) {
570 $this->methods[$methodName]['injectMethod'] = true;
571 $this->injectMethods[] = $methodName;
572 }
573 }
574 }
575
576 /**
577 * Returns the class name this schema is referring to
578 *
579 * @return string The class name
580 */
581 public function getClassName(): string
582 {
583 return $this->className;
584 }
585
586 /**
587 * Adds (defines) a specific property and its type.
588 *
589 * @param string $name Name of the property
590 * @param string $type Type of the property
591 * @param bool $lazy Whether the property should be lazy-loaded when reconstituting
592 * @param string $cascade Strategy to cascade the object graph.
593 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
594 */
595 public function addProperty($name, $type, $lazy = false, $cascade = '')
596 {
597 trigger_error(
598 'This method will be removed in TYPO3 v10.0, properties will be automatically added on ClassSchema construction.',
599 E_USER_DEPRECATED
600 );
601 $type = TypeHandlingUtility::parseType($type);
602 $this->properties[$name] = [
603 'type' => $type['type'],
604 'elementType' => $type['elementType'],
605 'lazy' => $lazy,
606 'cascade' => $cascade
607 ];
608 }
609
610 /**
611 * Returns the given property defined in this schema. Check with
612 * hasProperty($propertyName) before!
613 *
614 * @param string $propertyName
615 * @return array
616 */
617 public function getProperty($propertyName)
618 {
619 return isset($this->properties[$propertyName]) && is_array($this->properties[$propertyName])
620 ? $this->properties[$propertyName]
621 : [];
622 }
623
624 /**
625 * Returns all properties defined in this schema
626 *
627 * @return array
628 */
629 public function getProperties()
630 {
631 return $this->properties;
632 }
633
634 /**
635 * Sets the model type of the class this schema is referring to.
636 *
637 * @param int $modelType The model type, one of the MODELTYPE_* constants.
638 * @throws \InvalidArgumentException
639 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
640 */
641 public function setModelType($modelType)
642 {
643 trigger_error(
644 'This method will be removed in TYPO3 v10.0, modelType will be automatically set on ClassSchema construction.',
645 E_USER_DEPRECATED
646 );
647 if ($modelType < self::MODELTYPE_ENTITY || $modelType > self::MODELTYPE_VALUEOBJECT) {
648 throw new \InvalidArgumentException('"' . $modelType . '" is an invalid model type.', 1212519195);
649 }
650 $this->modelType = $modelType;
651 }
652
653 /**
654 * Returns the model type of the class this schema is referring to.
655 *
656 * @return int The model type, one of the MODELTYPE_* constants.
657 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
658 */
659 public function getModelType()
660 {
661 trigger_error(
662 'This method will be removed in TYPO3 v10.0.',
663 E_USER_DEPRECATED
664 );
665 return $this->modelType;
666 }
667
668 /**
669 * Marks the class if it is root of an aggregate and therefore accessible
670 * through a repository - or not.
671 *
672 * @param bool $isRoot TRUE if it is the root of an aggregate
673 */
674 public function setAggregateRoot($isRoot)
675 {
676 $this->aggregateRoot = $isRoot;
677 }
678
679 /**
680 * Whether the class is an aggregate root and therefore accessible through
681 * a repository.
682 *
683 * @return bool TRUE if it is managed
684 */
685 public function isAggregateRoot(): bool
686 {
687 return $this->aggregateRoot;
688 }
689
690 /**
691 * If the class schema has a certain property.
692 *
693 * @param string $propertyName Name of the property
694 * @return bool
695 */
696 public function hasProperty($propertyName): bool
697 {
698 return array_key_exists($propertyName, $this->properties);
699 }
700
701 /**
702 * Sets the property marked as uuid of an object with @uuid
703 *
704 * @param string $propertyName
705 * @throws \InvalidArgumentException
706 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
707 */
708 public function setUuidPropertyName($propertyName)
709 {
710 trigger_error(
711 'Tagging properties with @uuid is deprecated and will be removed in TYPO3 v10.0.',
712 E_USER_DEPRECATED
713 );
714 if (!array_key_exists($propertyName, $this->properties)) {
715 throw new \InvalidArgumentException('Property "' . $propertyName . '" must be added to the class schema before it can be marked as UUID property.', 1233863842);
716 }
717 $this->uuidPropertyName = $propertyName;
718 }
719
720 /**
721 * Gets the name of the property marked as uuid of an object
722 *
723 * @return string
724 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
725 */
726 public function getUuidPropertyName()
727 {
728 trigger_error(
729 'Tagging properties with @uuid is deprecated and will be removed in TYPO3 v10.0.',
730 E_USER_DEPRECATED
731 );
732 return $this->uuidPropertyName;
733 }
734
735 /**
736 * Marks the given property as one of properties forming the identity
737 * of an object. The property must already be registered in the class
738 * schema.
739 *
740 * @param string $propertyName
741 * @throws \InvalidArgumentException
742 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
743 */
744 public function markAsIdentityProperty($propertyName)
745 {
746 trigger_error(
747 'Tagging properties with @identity is deprecated and will be removed in TYPO3 v10.0.',
748 E_USER_DEPRECATED
749 );
750 if (!array_key_exists($propertyName, $this->properties)) {
751 throw new \InvalidArgumentException('Property "' . $propertyName . '" must be added to the class schema before it can be marked as identity property.', 1233775407);
752 }
753 if ($this->properties[$propertyName]['annotations']['lazy'] === true) {
754 throw new \InvalidArgumentException('Property "' . $propertyName . '" must not be makred for lazy loading to be marked as identity property.', 1239896904);
755 }
756 $this->identityProperties[$propertyName] = $this->properties[$propertyName]['type'];
757 }
758
759 /**
760 * Gets the properties (names and types) forming the identity of an object.
761 *
762 * @return array
763 * @see markAsIdentityProperty()
764 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
765 */
766 public function getIdentityProperties()
767 {
768 trigger_error(
769 'Tagging properties with @identity is deprecated and will be removed in TYPO3 v10.0.',
770 E_USER_DEPRECATED
771 );
772 return $this->identityProperties;
773 }
774
775 /**
776 * @return bool
777 */
778 public function hasConstructor(): bool
779 {
780 return isset($this->methods['__construct']);
781 }
782
783 /**
784 * @param string $name
785 * @return array
786 */
787 public function getMethod(string $name): array
788 {
789 return $this->methods[$name] ?? [];
790 }
791
792 /**
793 * @return array
794 */
795 public function getMethods(): array
796 {
797 return $this->methods;
798 }
799
800 /**
801 * @param \ReflectionMethod $reflectionMethod
802 * @return bool
803 */
804 protected function hasInjectMethodName(\ReflectionMethod $reflectionMethod): bool
805 {
806 $methodName = $reflectionMethod->getName();
807 if ($methodName === 'injectSettings' || !$reflectionMethod->isPublic()) {
808 return false;
809 }
810
811 if (
812 strpos($reflectionMethod->getName(), 'inject') === 0
813 ) {
814 return true;
815 }
816
817 return false;
818 }
819
820 /**
821 * @return bool
822 * @internal
823 */
824 public function isModel(): bool
825 {
826 return $this->isEntity() || $this->isValueObject();
827 }
828
829 /**
830 * @return bool
831 * @internal
832 */
833 public function isEntity(): bool
834 {
835 return $this->modelType === static::MODELTYPE_ENTITY;
836 }
837
838 /**
839 * @return bool
840 * @internal
841 */
842 public function isValueObject(): bool
843 {
844 return $this->modelType === static::MODELTYPE_VALUEOBJECT;
845 }
846
847 /**
848 * @return bool
849 */
850 public function isSingleton(): bool
851 {
852 return $this->isSingleton;
853 }
854
855 /**
856 * @param string $methodName
857 * @return bool
858 */
859 public function hasMethod(string $methodName): bool
860 {
861 return isset($this->methods[$methodName]);
862 }
863
864 /**
865 * @return array
866 */
867 public function getTags(): array
868 {
869 return $this->tags;
870 }
871
872 /**
873 * @return bool
874 */
875 public function hasInjectProperties(): bool
876 {
877 return count($this->injectProperties) > 0;
878 }
879
880 /**
881 * @return bool
882 */
883 public function hasInjectMethods(): bool
884 {
885 return count($this->injectMethods) > 0;
886 }
887
888 /**
889 * @return array
890 */
891 public function getInjectMethods(): array
892 {
893 $injectMethods = [];
894 foreach ($this->injectMethods as $injectMethodName) {
895 $injectMethods[$injectMethodName] = reset($this->methods[$injectMethodName]['params'])['dependency'];
896 }
897
898 return $injectMethods;
899 }
900
901 /**
902 * @return array
903 */
904 public function getInjectProperties(): array
905 {
906 $injectProperties = [];
907 foreach ($this->injectProperties as $injectPropertyName) {
908 $injectProperties[$injectPropertyName] = $this->properties[$injectPropertyName]['annotations']['dependency'];
909 }
910
911 return $injectProperties;
912 }
913
914 /**
915 * @return array
916 */
917 public function getConstructorArguments(): array
918 {
919 if (!$this->hasConstructor()) {
920 return [];
921 }
922
923 return $this->methods['__construct']['params'];
924 }
925 }