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