first commit
This commit is contained in:
commit
1218dd1517
12716 files changed
+989233
No files matched your search
@@ -0,0 +1,944 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Reflection;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\ClassNamingUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\StringUtility;
|
||||
use TYPO3\CMS\Extbase\Annotation\IgnoreValidation;
|
||||
use TYPO3\CMS\Extbase\Annotation\Inject;
|
||||
use TYPO3\CMS\Extbase\Annotation\ORM\Cascade;
|
||||
use TYPO3\CMS\Extbase\Annotation\ORM\Lazy;
|
||||
use TYPO3\CMS\Extbase\Annotation\ORM\Transient;
|
||||
use TYPO3\CMS\Extbase\Annotation\Validate;
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ControllerInterface;
|
||||
use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
|
||||
use TYPO3\CMS\Extbase\Validation\Exception\InvalidTypeHintException;
|
||||
use TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationConfigurationException;
|
||||
use TYPO3\CMS\Extbase\Validation\ValidatorResolver;
|
||||
|
||||
/**
|
||||
* A class schema
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
class ClassSchema
|
||||
{
|
||||
/**
|
||||
* Available model types
|
||||
*/
|
||||
const MODELTYPE_ENTITY = 1;
|
||||
const MODELTYPE_VALUEOBJECT = 2;
|
||||
|
||||
/**
|
||||
* Name of the class this schema is referring to
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $className;
|
||||
|
||||
/**
|
||||
* Model type of the class this schema is referring to
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $modelType = 0;
|
||||
|
||||
/**
|
||||
* Whether a repository exists for the class this schema is referring to
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $aggregateRoot = false;
|
||||
|
||||
/**
|
||||
* The name of the property holding the uuid of an entity, if any.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $uuidPropertyName;
|
||||
|
||||
/**
|
||||
* Properties of the class which need to be persisted
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $properties = [];
|
||||
|
||||
/**
|
||||
* The properties forming the identity of an object
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $identityProperties = [];
|
||||
|
||||
/**
|
||||
* Indicates if the class is a singleton or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $isSingleton;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $methods;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $tags;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $injectProperties = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $injectMethods = [];
|
||||
|
||||
/**
|
||||
* Constructs this class schema
|
||||
*
|
||||
* @param string $className Name of the class this schema is referring to
|
||||
* @throws \TYPO3\CMS\Extbase\Reflection\Exception\UnknownClassException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function __construct($className)
|
||||
{
|
||||
$this->className = $className;
|
||||
|
||||
$reflectionClass = new \ReflectionClass($className);
|
||||
|
||||
$this->isSingleton = $reflectionClass->implementsInterface(SingletonInterface::class);
|
||||
$this->isController = $reflectionClass->implementsInterface(ControllerInterface::class);
|
||||
|
||||
if ($reflectionClass->isSubclassOf(AbstractEntity::class)) {
|
||||
$this->modelType = static::MODELTYPE_ENTITY;
|
||||
|
||||
$possibleRepositoryClassName = ClassNamingUtility::translateModelNameToRepositoryName($className);
|
||||
if (class_exists($possibleRepositoryClassName)) {
|
||||
$this->setAggregateRoot(true);
|
||||
}
|
||||
}
|
||||
|
||||
if ($reflectionClass->isSubclassOf(AbstractValueObject::class)) {
|
||||
$this->modelType = static::MODELTYPE_VALUEOBJECT;
|
||||
}
|
||||
|
||||
$docCommentParser = new DocCommentParser(true);
|
||||
$docCommentParser->parseDocComment($reflectionClass->getDocComment());
|
||||
$this->tags = $docCommentParser->getTagsValues();
|
||||
|
||||
$this->reflectProperties($reflectionClass);
|
||||
$this->reflectMethods($reflectionClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionClass $reflectionClass
|
||||
*/
|
||||
protected function reflectProperties(\ReflectionClass $reflectionClass)
|
||||
{
|
||||
$annotationReader = new AnnotationReader();
|
||||
|
||||
foreach ($reflectionClass->getProperties() as $reflectionProperty) {
|
||||
$propertyName = $reflectionProperty->getName();
|
||||
|
||||
$this->properties[$propertyName] = [
|
||||
'default' => $reflectionProperty->isDefault(),
|
||||
'private' => $reflectionProperty->isPrivate(),
|
||||
'protected' => $reflectionProperty->isProtected(),
|
||||
'public' => $reflectionProperty->isPublic(),
|
||||
'static' => $reflectionProperty->isStatic(),
|
||||
'type' => null, // Extbase
|
||||
'elementType' => null, // Extbase
|
||||
'annotations' => [],
|
||||
'tags' => [],
|
||||
'validators' => []
|
||||
];
|
||||
|
||||
$docCommentParser = new DocCommentParser(true);
|
||||
$docCommentParser->parseDocComment($reflectionProperty->getDocComment());
|
||||
foreach ($docCommentParser->getTagsValues() as $tag => $values) {
|
||||
$this->properties[$propertyName]['tags'][strtolower($tag)] = $values;
|
||||
}
|
||||
|
||||
$this->properties[$propertyName]['annotations']['inject'] = false;
|
||||
$this->properties[$propertyName]['annotations']['lazy'] = false;
|
||||
$this->properties[$propertyName]['annotations']['transient'] = false;
|
||||
$this->properties[$propertyName]['annotations']['type'] = null;
|
||||
$this->properties[$propertyName]['annotations']['cascade'] = null;
|
||||
$this->properties[$propertyName]['annotations']['dependency'] = null;
|
||||
|
||||
$annotations = $annotationReader->getPropertyAnnotations($reflectionProperty);
|
||||
|
||||
/** @var array|Validate[] $validateAnnotations */
|
||||
$validateAnnotations = array_filter($annotations, function ($annotation) {
|
||||
return $annotation instanceof Validate;
|
||||
});
|
||||
|
||||
if (count($validateAnnotations) > 0) {
|
||||
$validatorResolver = GeneralUtility::makeInstance(ValidatorResolver::class);
|
||||
|
||||
foreach ($validateAnnotations as $validateAnnotation) {
|
||||
$validatorObjectName = $validatorResolver->resolveValidatorObjectName($validateAnnotation->validator);
|
||||
|
||||
$this->properties[$propertyName]['validators'][] = [
|
||||
'name' => $validateAnnotation->validator,
|
||||
'options' => $validateAnnotation->options,
|
||||
'className' => $validatorObjectName,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($docCommentParser->isTaggedWith('validate')) {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Property %s::%s is tagged with @validate which is deprecated and will be removed in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionProperty->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
$validatorResolver = GeneralUtility::makeInstance(ValidatorResolver::class);
|
||||
|
||||
$validateValues = $docCommentParser->getTagValues('validate');
|
||||
foreach ($validateValues as $validateValue) {
|
||||
$validatorConfiguration = $validatorResolver->parseValidatorAnnotation($validateValue);
|
||||
|
||||
foreach ($validatorConfiguration['validators'] ?? [] as $validator) {
|
||||
$validatorObjectName = $validatorResolver->resolveValidatorObjectName($validator['validatorName']);
|
||||
|
||||
$this->properties[$propertyName]['validators'][] = [
|
||||
'name' => $validator['validatorName'],
|
||||
'options' => $validator['validatorOptions'],
|
||||
'className' => $validatorObjectName,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($annotationReader->getPropertyAnnotation($reflectionProperty, Lazy::class) instanceof Lazy) {
|
||||
$this->properties[$propertyName]['annotations']['lazy'] = true;
|
||||
}
|
||||
|
||||
if ($docCommentParser->isTaggedWith('lazy')) {
|
||||
$this->properties[$propertyName]['annotations']['lazy'] = true;
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Property %s::%s is tagged with @lazy which is deprecated and will be removed in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionProperty->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
|
||||
if ($annotationReader->getPropertyAnnotation($reflectionProperty, Transient::class) instanceof Transient) {
|
||||
$this->properties[$propertyName]['annotations']['transient'] = true;
|
||||
}
|
||||
|
||||
if ($docCommentParser->isTaggedWith('transient')) {
|
||||
$this->properties[$propertyName]['annotations']['transient'] = true;
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Property %s::%s is tagged with @transient which is deprecated and will be removed in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionProperty->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
|
||||
if ($propertyName !== 'settings'
|
||||
&& ($annotationReader->getPropertyAnnotation($reflectionProperty, Inject::class) instanceof Inject)
|
||||
) {
|
||||
try {
|
||||
$varValue = ltrim($docCommentParser->getTagValues('var')[0], '\\');
|
||||
$this->properties[$propertyName]['annotations']['inject'] = true;
|
||||
$this->properties[$propertyName]['annotations']['type'] = $varValue;
|
||||
$this->properties[$propertyName]['annotations']['dependency'] = $varValue;
|
||||
|
||||
$this->injectProperties[] = $propertyName;
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
if ($propertyName !== 'settings' && $docCommentParser->isTaggedWith('inject')) {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Property %s::%s is tagged with @inject which is deprecated and will be removed in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionProperty->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
try {
|
||||
$varValues = $docCommentParser->getTagValues('var');
|
||||
$this->properties[$propertyName]['annotations']['inject'] = true;
|
||||
$this->properties[$propertyName]['annotations']['type'] = ltrim($varValues[0], '\\');
|
||||
$this->properties[$propertyName]['annotations']['dependency'] = ltrim($varValues[0], '\\');
|
||||
|
||||
if (!$reflectionProperty->isPublic()) {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Property %s::%s is not public and tagged with @inject which is deprecated and will stop working in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionProperty->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
|
||||
$this->injectProperties[] = $propertyName;
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
if ($docCommentParser->isTaggedWith('var') && !$docCommentParser->isTaggedWith('transient')) {
|
||||
try {
|
||||
$cascadeAnnotationValues = $docCommentParser->getTagValues('cascade');
|
||||
$this->properties[$propertyName]['annotations']['cascade'] = $cascadeAnnotationValues[0];
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
if ($this->properties[$propertyName]['annotations']['cascade'] !== null) {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Property %s::%s is tagged with @cascade which is deprecated and will be removed in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionProperty->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
|
||||
if (($annotation = $annotationReader->getPropertyAnnotation($reflectionProperty, Cascade::class)) instanceof Cascade) {
|
||||
/** @var Cascade $annotation */
|
||||
$this->properties[$propertyName]['annotations']['cascade'] = $annotation->value;
|
||||
}
|
||||
|
||||
try {
|
||||
$type = TypeHandlingUtility::parseType(implode(' ', $docCommentParser->getTagValues('var')));
|
||||
} catch (\Exception $e) {
|
||||
$type = [
|
||||
'type' => null,
|
||||
'elementType' => null
|
||||
];
|
||||
}
|
||||
|
||||
$this->properties[$propertyName]['type'] = $type['type'] ? ltrim($type['type'], '\\') : null;
|
||||
$this->properties[$propertyName]['elementType'] = $type['elementType'] ? ltrim($type['elementType'], '\\') : null;
|
||||
}
|
||||
|
||||
if ($docCommentParser->isTaggedWith('uuid')) {
|
||||
$this->setUuidPropertyName($propertyName);
|
||||
}
|
||||
|
||||
if ($docCommentParser->isTaggedWith('identity')) {
|
||||
$this->markAsIdentityProperty($propertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionClass $reflectionClass
|
||||
*/
|
||||
protected function reflectMethods(\ReflectionClass $reflectionClass)
|
||||
{
|
||||
$annotationReader = new AnnotationReader();
|
||||
|
||||
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
|
||||
$methodName = $reflectionMethod->getName();
|
||||
|
||||
$this->methods[$methodName] = [];
|
||||
$this->methods[$methodName]['private'] = $reflectionMethod->isPrivate();
|
||||
$this->methods[$methodName]['protected'] = $reflectionMethod->isProtected();
|
||||
$this->methods[$methodName]['public'] = $reflectionMethod->isPublic();
|
||||
$this->methods[$methodName]['static'] = $reflectionMethod->isStatic();
|
||||
$this->methods[$methodName]['abstract'] = $reflectionMethod->isAbstract();
|
||||
$this->methods[$methodName]['params'] = [];
|
||||
$this->methods[$methodName]['tags'] = [];
|
||||
$this->methods[$methodName]['annotations'] = [];
|
||||
$this->methods[$methodName]['isAction'] = StringUtility::endsWith($methodName, 'Action');
|
||||
|
||||
$docCommentParser = new DocCommentParser(true);
|
||||
$docCommentParser->parseDocComment($reflectionMethod->getDocComment());
|
||||
|
||||
$argumentValidators = [];
|
||||
|
||||
$annotations = $annotationReader->getMethodAnnotations($reflectionMethod);
|
||||
|
||||
/** @var array|Validate[] $validateAnnotations */
|
||||
$validateAnnotations = array_filter($annotations, function ($annotation) {
|
||||
return $annotation instanceof Validate;
|
||||
});
|
||||
|
||||
if ($this->isController && $this->methods[$methodName]['isAction'] && count($validateAnnotations) > 0) {
|
||||
$validatorResolver = GeneralUtility::makeInstance(ValidatorResolver::class);
|
||||
|
||||
foreach ($validateAnnotations as $validateAnnotation) {
|
||||
$validatorName = $validateAnnotation->validator;
|
||||
$validatorObjectName = $validatorResolver->resolveValidatorObjectName($validatorName);
|
||||
|
||||
$argumentValidators[$validateAnnotation->param][] = [
|
||||
'name' => $validatorName,
|
||||
'options' => $validateAnnotation->options,
|
||||
'className' => $validatorObjectName,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($docCommentParser->getTagsValues() as $tag => $values) {
|
||||
if ($tag === 'cli') {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Method %s::%s is tagged with @cli which is deprecated and will be removed in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionMethod->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
if ($tag === 'internal' && $reflectionClass->isSubclassOf(\TYPO3\CMS\Extbase\Mvc\Controller\CommandController::class)) {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Command method %s::%s is tagged with @internal which is deprecated and will be removed in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionMethod->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
if ($tag === 'ignorevalidation') {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Method %s::%s is tagged with @ignorevalidation which is deprecated and will be removed in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionMethod->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
if ($tag === 'flushesCaches') {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Method %s::%s is tagged with @flushesCaches which is deprecated and will be removed in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionMethod->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
if ($tag === 'validate' && $this->isController && $this->methods[$methodName]['isAction']) {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'Method %s::%s is tagged with @validate which is deprecated and will be removed in TYPO3 v10.0.',
|
||||
$reflectionClass->getName(),
|
||||
$reflectionMethod->getName()
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
$validatorResolver = GeneralUtility::makeInstance(ValidatorResolver::class);
|
||||
|
||||
foreach ($values as $validate) {
|
||||
$methodValidatorDefinition = $validatorResolver->parseValidatorAnnotation($validate);
|
||||
|
||||
foreach ($methodValidatorDefinition['validators'] as $validator) {
|
||||
$validatorObjectName = $validatorResolver->resolveValidatorObjectName($validator['validatorName']);
|
||||
|
||||
$argumentValidators[$methodValidatorDefinition['argumentName']][] = [
|
||||
'name' => $validator['validatorName'],
|
||||
'options' => $validator['validatorOptions'],
|
||||
'className' => $validatorObjectName,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->methods[$methodName]['tags'][$tag] = array_map(function ($value) use ($tag) {
|
||||
// not stripping the dollar sign for @validate annotations is just
|
||||
// a quick fix for a regression introduced in 9.0.0.
|
||||
// This exception to the rules will vanish once the resolving of
|
||||
// validators will take place inside this class and not in the
|
||||
// controller during runtime.
|
||||
return $tag === 'validate' ? $value : ltrim($value, '$');
|
||||
}, $values);
|
||||
}
|
||||
unset($methodValidatorDefinition);
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
if ($annotation instanceof IgnoreValidation) {
|
||||
$this->methods[$methodName]['tags']['ignorevalidation'][] = $annotation->argumentName;
|
||||
}
|
||||
}
|
||||
|
||||
$this->methods[$methodName]['description'] = $docCommentParser->getDescription();
|
||||
|
||||
foreach ($reflectionMethod->getParameters() as $parameterPosition => $reflectionParameter) {
|
||||
/* @var \ReflectionParameter $reflectionParameter */
|
||||
|
||||
$parameterName = $reflectionParameter->getName();
|
||||
|
||||
$this->methods[$methodName]['params'][$parameterName] = [];
|
||||
$this->methods[$methodName]['params'][$parameterName]['position'] = $parameterPosition; // compat
|
||||
$this->methods[$methodName]['params'][$parameterName]['byReference'] = $reflectionParameter->isPassedByReference(); // compat
|
||||
$this->methods[$methodName]['params'][$parameterName]['array'] = $reflectionParameter->isArray(); // compat
|
||||
$this->methods[$methodName]['params'][$parameterName]['optional'] = $reflectionParameter->isOptional();
|
||||
$this->methods[$methodName]['params'][$parameterName]['allowsNull'] = $reflectionParameter->allowsNull(); // compat
|
||||
$this->methods[$methodName]['params'][$parameterName]['class'] = null; // compat
|
||||
$this->methods[$methodName]['params'][$parameterName]['type'] = null;
|
||||
$this->methods[$methodName]['params'][$parameterName]['nullable'] = $reflectionParameter->allowsNull();
|
||||
$this->methods[$methodName]['params'][$parameterName]['default'] = null;
|
||||
$this->methods[$methodName]['params'][$parameterName]['hasDefaultValue'] = $reflectionParameter->isDefaultValueAvailable();
|
||||
$this->methods[$methodName]['params'][$parameterName]['defaultValue'] = null; // compat
|
||||
$this->methods[$methodName]['params'][$parameterName]['dependency'] = null; // Extbase DI
|
||||
$this->methods[$methodName]['params'][$parameterName]['validators'] = [];
|
||||
|
||||
if ($reflectionParameter->isDefaultValueAvailable()) {
|
||||
$this->methods[$methodName]['params'][$parameterName]['default'] = $reflectionParameter->getDefaultValue();
|
||||
$this->methods[$methodName]['params'][$parameterName]['defaultValue'] = $reflectionParameter->getDefaultValue(); // compat
|
||||
}
|
||||
|
||||
if (($reflectionType = $reflectionParameter->getType()) instanceof \ReflectionNamedType) {
|
||||
$this->methods[$methodName]['params'][$parameterName]['type'] = $reflectionType->getName();
|
||||
$this->methods[$methodName]['params'][$parameterName]['nullable'] = $reflectionType->allowsNull();
|
||||
}
|
||||
|
||||
if (($parameterClass = $reflectionParameter->getClass()) instanceof \ReflectionClass) {
|
||||
$this->methods[$methodName]['params'][$parameterName]['class'] = $parameterClass->getName();
|
||||
$this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($parameterClass->getName(), '\\');
|
||||
} else {
|
||||
$methodTagsAndValues = $this->methods[$methodName]['tags'];
|
||||
if (isset($methodTagsAndValues['param'][$parameterPosition])) {
|
||||
$explodedParameters = explode(' ', $methodTagsAndValues['param'][$parameterPosition]);
|
||||
if (count($explodedParameters) >= 2) {
|
||||
if (TypeHandlingUtility::isSimpleType($explodedParameters[0])) {
|
||||
// ensure that short names of simple types are resolved correctly to the long form
|
||||
// this is important for all kinds of type checks later on
|
||||
$typeInfo = TypeHandlingUtility::parseType($explodedParameters[0]);
|
||||
|
||||
$this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($typeInfo['type'], '\\');
|
||||
} else {
|
||||
$this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($explodedParameters[0], '\\');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extbase DI
|
||||
if ($reflectionParameter->getClass() instanceof \ReflectionClass
|
||||
&& ($reflectionMethod->isConstructor() || $this->hasInjectMethodName($reflectionMethod))
|
||||
) {
|
||||
$this->methods[$methodName]['params'][$parameterName]['dependency'] = $reflectionParameter->getClass()->getName();
|
||||
}
|
||||
|
||||
// Extbase Validation
|
||||
if (isset($argumentValidators[$parameterName])) {
|
||||
if ($this->methods[$methodName]['params'][$parameterName]['type'] === null) {
|
||||
throw new InvalidTypeHintException(
|
||||
'Missing type information for parameter "$' . $parameterName . '" in ' . $this->className . '->' . $methodName . '(): Either use an @param annotation or use a type hint.',
|
||||
1515075192
|
||||
);
|
||||
}
|
||||
|
||||
$this->methods[$methodName]['params'][$parameterName]['validators'] = $argumentValidators[$parameterName];
|
||||
unset($argumentValidators[$parameterName]);
|
||||
}
|
||||
}
|
||||
|
||||
// Extbase Validation
|
||||
foreach ($argumentValidators as $parameterName => $validators) {
|
||||
$validatorNames = array_column($validators, 'name');
|
||||
|
||||
throw new InvalidValidationConfigurationException(
|
||||
'Invalid validate annotation in ' . $this->className . '->' . $methodName . '(): The following validators have been defined for missing param "$' . $parameterName . '": ' . implode(', ', $validatorNames),
|
||||
1515073585
|
||||
);
|
||||
}
|
||||
|
||||
// Extbase
|
||||
$this->methods[$methodName]['injectMethod'] = false;
|
||||
if ($this->hasInjectMethodName($reflectionMethod)
|
||||
&& count($this->methods[$methodName]['params']) === 1
|
||||
&& reset($this->methods[$methodName]['params'])['dependency'] !== null
|
||||
) {
|
||||
$this->methods[$methodName]['injectMethod'] = true;
|
||||
$this->injectMethods[] = $methodName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class name this schema is referring to
|
||||
*
|
||||
* @return string The class name
|
||||
*/
|
||||
public function getClassName(): string
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds (defines) a specific property and its type.
|
||||
*
|
||||
* @param string $name Name of the property
|
||||
* @param string $type Type of the property
|
||||
* @param bool $lazy Whether the property should be lazy-loaded when reconstituting
|
||||
* @param string $cascade Strategy to cascade the object graph.
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function addProperty($name, $type, $lazy = false, $cascade = '')
|
||||
{
|
||||
trigger_error(
|
||||
'This method will be removed in TYPO3 v10.0, properties will be automatically added on ClassSchema construction.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
$type = TypeHandlingUtility::parseType($type);
|
||||
$this->properties[$name] = [
|
||||
'type' => $type['type'],
|
||||
'elementType' => $type['elementType'],
|
||||
'lazy' => $lazy,
|
||||
'cascade' => $cascade
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given property defined in this schema. Check with
|
||||
* hasProperty($propertyName) before!
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @return array
|
||||
*/
|
||||
public function getProperty($propertyName)
|
||||
{
|
||||
return isset($this->properties[$propertyName]) && is_array($this->properties[$propertyName])
|
||||
? $this->properties[$propertyName]
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all properties defined in this schema
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getProperties()
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model type of the class this schema is referring to.
|
||||
*
|
||||
* @param int $modelType The model type, one of the MODELTYPE_* constants.
|
||||
* @throws \InvalidArgumentException
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function setModelType($modelType)
|
||||
{
|
||||
trigger_error(
|
||||
'This method will be removed in TYPO3 v10.0, modelType will be automatically set on ClassSchema construction.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
if ($modelType < self::MODELTYPE_ENTITY || $modelType > self::MODELTYPE_VALUEOBJECT) {
|
||||
throw new \InvalidArgumentException('"' . $modelType . '" is an invalid model type.', 1212519195);
|
||||
}
|
||||
$this->modelType = $modelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the model type of the class this schema is referring to.
|
||||
*
|
||||
* @return int The model type, one of the MODELTYPE_* constants.
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getModelType()
|
||||
{
|
||||
trigger_error(
|
||||
'This method will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
return $this->modelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the class if it is root of an aggregate and therefore accessible
|
||||
* through a repository - or not.
|
||||
*
|
||||
* @param bool $isRoot TRUE if it is the root of an aggregate
|
||||
*/
|
||||
public function setAggregateRoot($isRoot)
|
||||
{
|
||||
$this->aggregateRoot = $isRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the class is an aggregate root and therefore accessible through
|
||||
* a repository.
|
||||
*
|
||||
* @return bool TRUE if it is managed
|
||||
*/
|
||||
public function isAggregateRoot(): bool
|
||||
{
|
||||
return $this->aggregateRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the class schema has a certain property.
|
||||
*
|
||||
* @param string $propertyName Name of the property
|
||||
* @return bool
|
||||
*/
|
||||
public function hasProperty($propertyName): bool
|
||||
{
|
||||
return array_key_exists($propertyName, $this->properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property marked as uuid of an object with @uuid
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @throws \InvalidArgumentException
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function setUuidPropertyName($propertyName)
|
||||
{
|
||||
trigger_error(
|
||||
'Tagging properties with @uuid is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
if (!array_key_exists($propertyName, $this->properties)) {
|
||||
throw new \InvalidArgumentException('Property "' . $propertyName . '" must be added to the class schema before it can be marked as UUID property.', 1233863842);
|
||||
}
|
||||
$this->uuidPropertyName = $propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the property marked as uuid of an object
|
||||
*
|
||||
* @return string
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getUuidPropertyName()
|
||||
{
|
||||
trigger_error(
|
||||
'Tagging properties with @uuid is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
return $this->uuidPropertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the given property as one of properties forming the identity
|
||||
* of an object. The property must already be registered in the class
|
||||
* schema.
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @throws \InvalidArgumentException
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function markAsIdentityProperty($propertyName)
|
||||
{
|
||||
trigger_error(
|
||||
'Tagging properties with @identity is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
if (!array_key_exists($propertyName, $this->properties)) {
|
||||
throw new \InvalidArgumentException('Property "' . $propertyName . '" must be added to the class schema before it can be marked as identity property.', 1233775407);
|
||||
}
|
||||
if ($this->properties[$propertyName]['annotations']['lazy'] === true) {
|
||||
throw new \InvalidArgumentException('Property "' . $propertyName . '" must not be makred for lazy loading to be marked as identity property.', 1239896904);
|
||||
}
|
||||
$this->identityProperties[$propertyName] = $this->properties[$propertyName]['type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the properties (names and types) forming the identity of an object.
|
||||
*
|
||||
* @return array
|
||||
* @see markAsIdentityProperty()
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getIdentityProperties()
|
||||
{
|
||||
trigger_error(
|
||||
'Tagging properties with @identity is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
return $this->identityProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasConstructor(): bool
|
||||
{
|
||||
return isset($this->methods['__construct']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public function getMethod(string $name): array
|
||||
{
|
||||
return $this->methods[$name] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMethods(): array
|
||||
{
|
||||
return $this->methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionMethod $reflectionMethod
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasInjectMethodName(\ReflectionMethod $reflectionMethod): bool
|
||||
{
|
||||
$methodName = $reflectionMethod->getName();
|
||||
if ($methodName === 'injectSettings' || !$reflectionMethod->isPublic()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
strpos($reflectionMethod->getName(), 'inject') === 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
public function isModel(): bool
|
||||
{
|
||||
return $this->isEntity() || $this->isValueObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
public function isEntity(): bool
|
||||
{
|
||||
return $this->modelType === static::MODELTYPE_ENTITY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
public function isValueObject(): bool
|
||||
{
|
||||
return $this->modelType === static::MODELTYPE_VALUEOBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSingleton(): bool
|
||||
{
|
||||
return $this->isSingleton;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $methodName
|
||||
* @return bool
|
||||
*/
|
||||
public function hasMethod(string $methodName): bool
|
||||
{
|
||||
return isset($this->methods[$methodName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTags(): array
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasInjectProperties(): bool
|
||||
{
|
||||
return count($this->injectProperties) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasInjectMethods(): bool
|
||||
{
|
||||
return count($this->injectMethods) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getInjectMethods(): array
|
||||
{
|
||||
$injectMethods = [];
|
||||
foreach ($this->injectMethods as $injectMethodName) {
|
||||
$injectMethods[$injectMethodName] = reset($this->methods[$injectMethodName]['params'])['dependency'];
|
||||
}
|
||||
|
||||
return $injectMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getInjectProperties(): array
|
||||
{
|
||||
$injectProperties = [];
|
||||
foreach ($this->injectProperties as $injectPropertyName) {
|
||||
$injectProperties[$injectPropertyName] = $this->properties[$injectPropertyName]['annotations']['dependency'];
|
||||
}
|
||||
|
||||
return $injectProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getConstructorArguments(): array
|
||||
{
|
||||
if (!$this->hasConstructor()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->methods['__construct']['params'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Reflection;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
/**
|
||||
* A little parser which creates tag objects from doc comments
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
class DocCommentParser
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $ignoredTags = ['package', 'subpackage', 'license', 'copyright', 'author', 'version', 'const'];
|
||||
|
||||
/**
|
||||
* @var string The description as found in the doc comment
|
||||
*/
|
||||
protected $description = '';
|
||||
|
||||
/**
|
||||
* @var array An array of tag names and their values (multiple values are possible)
|
||||
*/
|
||||
protected $tags = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $useIgnoredTags;
|
||||
|
||||
/**
|
||||
* @param bool $useIgnoredTags
|
||||
*/
|
||||
public function __construct($useIgnoredTags = false)
|
||||
{
|
||||
$this->useIgnoredTags = $useIgnoredTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given doc comment and saves the result (description and
|
||||
* tags) in the parser's object. They can be retrieved by the
|
||||
* getTags() getTagValues() and getDescription() methods.
|
||||
*
|
||||
* @param string $docComment A doc comment as returned by the reflection getDocComment() method
|
||||
*/
|
||||
public function parseDocComment($docComment)
|
||||
{
|
||||
$this->description = '';
|
||||
$this->tags = [];
|
||||
$lines = explode(LF, $docComment);
|
||||
foreach ($lines as $line) {
|
||||
if ($line !== '' && strpos($line, '@') !== false) {
|
||||
$this->parseTag(substr($line, strpos($line, '@')));
|
||||
} elseif (empty($this->tags)) {
|
||||
$this->description .= preg_replace('#\\s*/?[*/]*(.*)$#', '$1', $line) . LF;
|
||||
}
|
||||
}
|
||||
$this->description = trim($this->description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tags which have been previously parsed
|
||||
*
|
||||
* @return array Array of tag names and their (multiple) values
|
||||
*/
|
||||
public function getTagsValues()
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of the specified tag. The doc comment
|
||||
* must be parsed with parseDocComment() before tags are
|
||||
* available.
|
||||
*
|
||||
* @param string $tagName The tag name to retrieve the values for
|
||||
* @throws \RuntimeException
|
||||
* @return array The tag's values
|
||||
*/
|
||||
public function getTagValues($tagName)
|
||||
{
|
||||
if (!$this->isTaggedWith($tagName)) {
|
||||
throw new \RuntimeException('Tag "' . $tagName . '" does not exist.', 1169128255);
|
||||
}
|
||||
return $this->tags[$tagName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a tag with the given name exists
|
||||
*
|
||||
* @param string $tagName The tag name to check for
|
||||
* @return bool TRUE the tag exists, otherwise FALSE
|
||||
*/
|
||||
public function isTaggedWith($tagName)
|
||||
{
|
||||
return isset($this->tags[$tagName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description which has been previously parsed
|
||||
*
|
||||
* @return string The description which has been parsed
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a line of a doc comment for a tag and its value.
|
||||
* The result is stored in the interal tags array.
|
||||
*
|
||||
* @param string $line A line of a doc comment which starts with an @-sign
|
||||
*/
|
||||
protected function parseTag($line)
|
||||
{
|
||||
$tagAndValue = preg_split('/\\s/', $line, 2);
|
||||
$tag = substr($tagAndValue[0], 1);
|
||||
|
||||
if ($this->useIgnoredTags && in_array($tag, static::$ignoredTags, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (count($tagAndValue) > 1) {
|
||||
$this->tags[$tag][] = trim($tagAndValue[1]);
|
||||
} else {
|
||||
$this->tags[$tag] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Reflection;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
/**
|
||||
* A generic Reflection Exception
|
||||
*/
|
||||
class Exception extends \TYPO3\CMS\Extbase\Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Reflection\Exception;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
/**
|
||||
* An "Property not accessible" exception
|
||||
*/
|
||||
class PropertyNotAccessibleException extends \TYPO3\CMS\Extbase\Reflection\Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Reflection\Exception;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
/**
|
||||
* An "Unknown Class" exception
|
||||
*/
|
||||
class UnknownClassException extends \TYPO3\CMS\Extbase\Reflection\Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Reflection;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
|
||||
/**
|
||||
* Provides methods to call appropriate getter/setter on an object given the
|
||||
* property name. It does this following these rules:
|
||||
* - if the target object is an instance of ArrayAccess, it gets/sets the property
|
||||
* - if public getter/setter method exists, call it.
|
||||
* - if public property exists, return/set the value of it.
|
||||
* - else, throw exception
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
class ObjectAccess
|
||||
{
|
||||
const ACCESS_GET = 0;
|
||||
|
||||
const ACCESS_SET = 1;
|
||||
|
||||
const ACCESS_PUBLIC = 2;
|
||||
|
||||
/**
|
||||
* Get a property of a given object.
|
||||
* Tries to get the property the following ways:
|
||||
* - if the target is an array, and has this property, we call it.
|
||||
* - if super cow powers should be used, fetch value through reflection
|
||||
* - if public getter method exists, call it.
|
||||
* - if the target object is an instance of ArrayAccess, it gets the property
|
||||
* on it if it exists.
|
||||
* - if public property exists, return the value of it.
|
||||
* - else, throw exception
|
||||
*
|
||||
* @param mixed $subject Object or array to get the property from
|
||||
* @param string $propertyName name of the property to retrieve
|
||||
* @param bool $forceDirectAccess directly access property using reflection(!)
|
||||
*
|
||||
* @throws \InvalidArgumentException in case $subject was not an object or $propertyName was not a string
|
||||
* @return mixed Value of the property
|
||||
*/
|
||||
public static function getProperty($subject, $propertyName, $forceDirectAccess = false)
|
||||
{
|
||||
if (!is_object($subject) && !is_array($subject)) {
|
||||
throw new \InvalidArgumentException('$subject must be an object or array, ' . gettype($subject) . ' given.', 1237301367);
|
||||
}
|
||||
if (!is_string($propertyName) && (!is_array($subject) && !$subject instanceof \ArrayAccess)) {
|
||||
throw new \InvalidArgumentException('Given property name is not of type string.', 1231178303);
|
||||
}
|
||||
return self::getPropertyInternal($subject, $propertyName, $forceDirectAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property of a given object or array.
|
||||
* This is an internal method that does only limited type checking for performance reasons.
|
||||
* If you can't make sure that $subject is either of type array or object and $propertyName of type string you should use getProperty() instead.
|
||||
*
|
||||
* @see getProperty()
|
||||
*
|
||||
* @param mixed $subject Object or array to get the property from
|
||||
* @param string $propertyName name of the property to retrieve
|
||||
* @param bool $forceDirectAccess directly access property using reflection(!)
|
||||
*
|
||||
* @throws Exception\PropertyNotAccessibleException
|
||||
* @return mixed Value of the property
|
||||
* @internal
|
||||
*/
|
||||
public static function getPropertyInternal($subject, $propertyName, $forceDirectAccess = false)
|
||||
{
|
||||
// type check and conversion of iterator to numerically indexed array
|
||||
if ($subject === null || is_scalar($subject)) {
|
||||
return null;
|
||||
}
|
||||
if (!$forceDirectAccess && ($subject instanceof \SplObjectStorage || $subject instanceof ObjectStorage)) {
|
||||
$subject = iterator_to_array(clone $subject, false);
|
||||
}
|
||||
|
||||
// value get based on data type of $subject (possibly converted above)
|
||||
if (($subject instanceof \ArrayAccess && $subject->offsetExists($propertyName)) || is_array($subject)) {
|
||||
// isset() is safe; array_key_exists would only be needed to determine
|
||||
// if the value is NULL - and that's already what we return as fallback.
|
||||
if (isset($subject[$propertyName])) {
|
||||
return $subject[$propertyName];
|
||||
}
|
||||
} elseif (is_object($subject)) {
|
||||
if ($forceDirectAccess) {
|
||||
if (property_exists($subject, $propertyName)) {
|
||||
$propertyReflection = new \ReflectionProperty($subject, $propertyName);
|
||||
if ($propertyReflection->isPublic()) {
|
||||
return $propertyReflection->getValue($subject);
|
||||
}
|
||||
$propertyReflection->setAccessible(true);
|
||||
return $propertyReflection->getValue($subject);
|
||||
}
|
||||
throw new Exception\PropertyNotAccessibleException('The property "' . $propertyName . '" on the subject does not exist.', 1302855001);
|
||||
}
|
||||
$upperCasePropertyName = ucfirst($propertyName);
|
||||
$getterMethodName = 'get' . $upperCasePropertyName;
|
||||
if (is_callable([$subject, $getterMethodName])) {
|
||||
return $subject->{$getterMethodName}();
|
||||
}
|
||||
$getterMethodName = 'is' . $upperCasePropertyName;
|
||||
if (is_callable([$subject, $getterMethodName])) {
|
||||
return $subject->{$getterMethodName}();
|
||||
}
|
||||
$getterMethodName = 'has' . $upperCasePropertyName;
|
||||
if (is_callable([$subject, $getterMethodName])) {
|
||||
return $subject->{$getterMethodName}();
|
||||
}
|
||||
if (property_exists($subject, $propertyName)) {
|
||||
return $subject->{$propertyName};
|
||||
}
|
||||
throw new Exception\PropertyNotAccessibleException('The property "' . $propertyName . '" on the subject does not exist.', 1476109666);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property path from a given object or array.
|
||||
*
|
||||
* If propertyPath is "bla.blubb", then we first call getProperty($object, 'bla'),
|
||||
* and on the resulting object we call getProperty(..., 'blubb')
|
||||
*
|
||||
* For arrays the keys are checked likewise.
|
||||
*
|
||||
* @param mixed $subject Object or array to get the property path from
|
||||
* @param string $propertyPath
|
||||
*
|
||||
* @return mixed Value of the property
|
||||
*/
|
||||
public static function getPropertyPath($subject, $propertyPath)
|
||||
{
|
||||
$propertyPathSegments = explode('.', $propertyPath);
|
||||
try {
|
||||
foreach ($propertyPathSegments as $pathSegment) {
|
||||
$subject = self::getPropertyInternal($subject, $pathSegment);
|
||||
}
|
||||
} catch (Exception\PropertyNotAccessibleException $error) {
|
||||
return null;
|
||||
}
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a property for a given object.
|
||||
* Tries to set the property the following ways:
|
||||
* - if target is an array, set value
|
||||
* - if super cow powers should be used, set value through reflection
|
||||
* - if public setter method exists, call it.
|
||||
* - if public property exists, set it directly.
|
||||
* - if the target object is an instance of ArrayAccess, it sets the property
|
||||
* on it without checking if it existed.
|
||||
* - else, return FALSE
|
||||
*
|
||||
* @param mixed &$subject The target object or array
|
||||
* @param string $propertyName Name of the property to set
|
||||
* @param mixed $propertyValue Value of the property
|
||||
* @param bool $forceDirectAccess directly access property using reflection(!)
|
||||
*
|
||||
* @throws \InvalidArgumentException in case $object was not an object or $propertyName was not a string
|
||||
* @return bool TRUE if the property could be set, FALSE otherwise
|
||||
*/
|
||||
public static function setProperty(&$subject, $propertyName, $propertyValue, $forceDirectAccess = false)
|
||||
{
|
||||
if (is_array($subject) || ($subject instanceof \ArrayAccess && !$forceDirectAccess)) {
|
||||
$subject[$propertyName] = $propertyValue;
|
||||
return true;
|
||||
}
|
||||
if (!is_object($subject)) {
|
||||
throw new \InvalidArgumentException('subject must be an object or array, ' . gettype($subject) . ' given.', 1237301368);
|
||||
}
|
||||
if (!is_string($propertyName)) {
|
||||
throw new \InvalidArgumentException('Given property name is not of type string.', 1231178878);
|
||||
}
|
||||
$result = true;
|
||||
if ($forceDirectAccess) {
|
||||
if (property_exists($subject, $propertyName)) {
|
||||
$propertyReflection = new \ReflectionProperty($subject, $propertyName);
|
||||
$propertyReflection->setAccessible(true);
|
||||
$propertyReflection->setValue($subject, $propertyValue);
|
||||
} else {
|
||||
$subject->{$propertyName} = $propertyValue;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
$setterMethodName = self::buildSetterMethodName($propertyName);
|
||||
if (is_callable([$subject, $setterMethodName])) {
|
||||
$subject->{$setterMethodName}($propertyValue);
|
||||
} elseif (property_exists($subject, $propertyName)) {
|
||||
$reflection = new \ReflectionProperty($subject, $propertyName);
|
||||
if ($reflection->isPublic()) {
|
||||
$subject->{$propertyName} = $propertyValue;
|
||||
} else {
|
||||
$result = false;
|
||||
}
|
||||
} else {
|
||||
$result = false;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of properties which can be get with the getProperty()
|
||||
* method.
|
||||
* Includes the following properties:
|
||||
* - which can be get through a public getter method.
|
||||
* - public properties which can be directly get.
|
||||
*
|
||||
* @param object $object Object to receive property names for
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @return array Array of all gettable property names
|
||||
*/
|
||||
public static function getGettablePropertyNames($object)
|
||||
{
|
||||
if (!is_object($object)) {
|
||||
throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1237301369);
|
||||
}
|
||||
if ($object instanceof \stdClass) {
|
||||
$properties = array_keys((array)$object);
|
||||
sort($properties);
|
||||
return $properties;
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass($object);
|
||||
$declaredPropertyNames = array_map(
|
||||
function (\ReflectionProperty $property) {
|
||||
return $property->getName();
|
||||
},
|
||||
$reflection->getProperties(\ReflectionProperty::IS_PUBLIC)
|
||||
);
|
||||
foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
$methodParameters = $method->getParameters();
|
||||
if (!empty($methodParameters)) {
|
||||
foreach ($methodParameters as $parameter) {
|
||||
if (!$parameter->isOptional()) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$methodName = $method->getName();
|
||||
if (strpos($methodName, 'is') === 0) {
|
||||
$declaredPropertyNames[] = lcfirst(substr($methodName, 2));
|
||||
}
|
||||
if (strpos($methodName, 'get') === 0) {
|
||||
$declaredPropertyNames[] = lcfirst(substr($methodName, 3));
|
||||
}
|
||||
if (strpos($methodName, 'has') === 0) {
|
||||
$declaredPropertyNames[] = lcfirst(substr($methodName, 3));
|
||||
}
|
||||
}
|
||||
$propertyNames = array_unique($declaredPropertyNames);
|
||||
sort($propertyNames);
|
||||
|
||||
return $propertyNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of properties which can be set with the setProperty()
|
||||
* method.
|
||||
* Includes the following properties:
|
||||
* - which can be set through a public setter method.
|
||||
* - public properties which can be directly set.
|
||||
*
|
||||
* @param object $object Object to receive property names for
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @return array Array of all settable property names
|
||||
*/
|
||||
public static function getSettablePropertyNames($object)
|
||||
{
|
||||
if (!is_object($object)) {
|
||||
throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1264022994);
|
||||
}
|
||||
if ($object instanceof \stdClass) {
|
||||
$declaredPropertyNames = array_keys((array)$object);
|
||||
} else {
|
||||
$declaredPropertyNames = array_keys(get_class_vars(get_class($object)));
|
||||
}
|
||||
foreach (get_class_methods($object) as $methodName) {
|
||||
if (strpos($methodName, 'set') === 0 && is_callable([$object, $methodName])) {
|
||||
$declaredPropertyNames[] = lcfirst(substr($methodName, 3));
|
||||
}
|
||||
}
|
||||
$propertyNames = array_unique($declaredPropertyNames);
|
||||
sort($propertyNames);
|
||||
return $propertyNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the value of the specified property can be set by this Object Accessor.
|
||||
*
|
||||
* @param object $object Object containting the property
|
||||
* @param string $propertyName Name of the property to check
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPropertySettable($object, $propertyName)
|
||||
{
|
||||
if (!is_object($object)) {
|
||||
throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1259828920);
|
||||
}
|
||||
if ($object instanceof \stdClass && array_key_exists($propertyName, get_object_vars($object))) {
|
||||
return true;
|
||||
}
|
||||
if (array_key_exists($propertyName, get_class_vars(get_class($object)))) {
|
||||
return true;
|
||||
}
|
||||
return is_callable([$object, self::buildSetterMethodName($propertyName)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the value of the specified property can be retrieved by this Object Accessor.
|
||||
*
|
||||
* @param object $object Object containting the property
|
||||
* @param string $propertyName Name of the property to check
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPropertyGettable($object, $propertyName)
|
||||
{
|
||||
if (!is_object($object)) {
|
||||
throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1259828921);
|
||||
}
|
||||
if ($object instanceof \ArrayAccess && isset($object[$propertyName])) {
|
||||
return true;
|
||||
}
|
||||
if ($object instanceof \stdClass && isset($object->$propertyName)) {
|
||||
return true;
|
||||
}
|
||||
if (is_callable([$object, 'get' . ucfirst($propertyName)])) {
|
||||
return true;
|
||||
}
|
||||
if (is_callable([$object, 'has' . ucfirst($propertyName)])) {
|
||||
return true;
|
||||
}
|
||||
if (is_callable([$object, 'is' . ucfirst($propertyName)])) {
|
||||
return true;
|
||||
}
|
||||
if (property_exists($object, $propertyName)) {
|
||||
$propertyReflection = new \ReflectionProperty($object, $propertyName);
|
||||
return $propertyReflection->isPublic();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all properties (names and their current values) of the current
|
||||
* $object that are accessible through this class.
|
||||
*
|
||||
* @param object $object Object to get all properties from.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @return array Associative array of all properties.
|
||||
* @todo What to do with ArrayAccess
|
||||
*/
|
||||
public static function getGettableProperties($object)
|
||||
{
|
||||
if (!is_object($object)) {
|
||||
throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1237301370);
|
||||
}
|
||||
$properties = [];
|
||||
foreach (self::getGettablePropertyNames($object) as $propertyName) {
|
||||
$properties[$propertyName] = self::getPropertyInternal($object, $propertyName);
|
||||
}
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the setter method name for a given property by capitalizing the
|
||||
* first letter of the property, and prepending it with "set".
|
||||
*
|
||||
* @param string $propertyName Name of the property
|
||||
*
|
||||
* @return string Name of the setter method name
|
||||
*/
|
||||
public static function buildSetterMethodName($propertyName)
|
||||
{
|
||||
return 'set' . ucfirst($propertyName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Reflection;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Cache\CacheManager;
|
||||
use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
|
||||
/**
|
||||
* Reflection service for acquiring reflection based information.
|
||||
* Originally based on the TYPO3.Flow reflection service.
|
||||
*/
|
||||
class ReflectionService implements SingletonInterface
|
||||
{
|
||||
const CACHE_IDENTIFIER = 'extbase_reflection';
|
||||
const CACHE_ENTRY_IDENTIFIER = 'ClassSchematas';
|
||||
|
||||
/**
|
||||
* @var FrontendInterface
|
||||
*/
|
||||
protected $dataCache;
|
||||
|
||||
/**
|
||||
* Indicates whether the Reflection cache needs to be updated.
|
||||
*
|
||||
* This flag needs to be set as soon as new Reflection information was
|
||||
* created.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $dataCacheNeedsUpdate = false;
|
||||
|
||||
/**
|
||||
* Local cache for Class schemata
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $classSchemata = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $cachingEnabled = false;
|
||||
|
||||
/**
|
||||
* If not $cacheManager is injected, the reflection service does not
|
||||
* cache any data, useful for testing this service in unit tests.
|
||||
*
|
||||
* @param CacheManager $cacheManager
|
||||
*/
|
||||
public function __construct(CacheManager $cacheManager = null)
|
||||
{
|
||||
if ($cacheManager instanceof CacheManager) {
|
||||
try {
|
||||
$this->dataCache = $cacheManager->getCache(static::CACHE_IDENTIFIER);
|
||||
$this->cachingEnabled = true;
|
||||
} catch (NoSuchCacheException $ignoredException) {
|
||||
$this->cachingEnabled = false;
|
||||
}
|
||||
|
||||
if ($this->cachingEnabled) {
|
||||
if (($classSchemata = $this->dataCache->get(static::CACHE_ENTRY_IDENTIFIER)) !== false) {
|
||||
$this->classSchemata = $classSchemata;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->dataCacheNeedsUpdate && $this->cachingEnabled) {
|
||||
$this->dataCache->set(static::CACHE_ENTRY_IDENTIFIER, $this->classSchemata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all tags and their values the specified class is tagged with
|
||||
*
|
||||
* @param string $className Name of the class
|
||||
* @return array An array of tags and their values or an empty array if no tags were found
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getClassTagsValues($className): array
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
try {
|
||||
$classSchema = $this->getClassSchema($className);
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $classSchema->getTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of the specified class tag
|
||||
*
|
||||
* @param string $className Name of the class containing the property
|
||||
* @param string $tag Tag to return the values of
|
||||
* @return array An array of values or an empty array if the tag was not found
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getClassTagValues($className, $tag): array
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
try {
|
||||
$classSchema = $this->getClassSchema($className);
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $classSchema->getTags()[$tag] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of all properties of the specified class
|
||||
*
|
||||
* @param string $className Name of the class to return the property names of
|
||||
* @return array An array of property names or an empty array if none exist
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getClassPropertyNames($className): array
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
try {
|
||||
$classSchema = $this->getClassSchema($className);
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_keys($classSchema->getProperties());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class schema for the given class
|
||||
*
|
||||
* @param mixed $classNameOrObject The class name or an object
|
||||
* @return ClassSchema
|
||||
* @throws \TYPO3\CMS\Extbase\Reflection\Exception\UnknownClassException
|
||||
*/
|
||||
public function getClassSchema($classNameOrObject): ClassSchema
|
||||
{
|
||||
$className = is_object($classNameOrObject) ? get_class($classNameOrObject) : $classNameOrObject;
|
||||
if (isset($this->classSchemata[$className])) {
|
||||
return $this->classSchemata[$className];
|
||||
}
|
||||
|
||||
return $this->buildClassSchema($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for method_exists() which tells if the given method exists.
|
||||
*
|
||||
* @param string $className Name of the class containing the method
|
||||
* @param string $methodName Name of the method
|
||||
* @return bool
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function hasMethod($className, $methodName): bool
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
try {
|
||||
$classSchema = $this->getClassSchema($className);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $classSchema->hasMethod($methodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all tags and their values the specified method is tagged with
|
||||
*
|
||||
* @param string $className Name of the class containing the method
|
||||
* @param string $methodName Name of the method to return the tags and values of
|
||||
* @return array An array of tags and their values or an empty array of no tags were found
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getMethodTagsValues($className, $methodName): array
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
try {
|
||||
$classSchema = $this->getClassSchema($className);
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $classSchema->getMethod($methodName)['tags'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of parameters of the given method. Each entry contains
|
||||
* additional information about the parameter position, type hint etc.
|
||||
*
|
||||
* @param string $className Name of the class containing the method
|
||||
* @param string $methodName Name of the method to return parameter information of
|
||||
* @return array An array of parameter names and additional information or an empty array of no parameters were found
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getMethodParameters($className, $methodName): array
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
try {
|
||||
$classSchema = $this->getClassSchema($className);
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $classSchema->getMethod($methodName)['params'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all tags and their values the specified class property is tagged with
|
||||
*
|
||||
* @param string $className Name of the class containing the property
|
||||
* @param string $propertyName Name of the property to return the tags and values of
|
||||
* @return array An array of tags and their values or an empty array of no tags were found
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getPropertyTagsValues($className, $propertyName): array
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
try {
|
||||
$classSchema = $this->getClassSchema($className);
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $classSchema->getProperty($propertyName)['tags'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of the specified class property tag
|
||||
*
|
||||
* @param string $className Name of the class containing the property
|
||||
* @param string $propertyName Name of the tagged property
|
||||
* @param string $tag Tag to return the values of
|
||||
* @return array An array of values or an empty array if the tag was not found
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getPropertyTagValues($className, $propertyName, $tag): array
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
try {
|
||||
$classSchema = $this->getClassSchema($className);
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $classSchema->getProperty($propertyName)['tags'][$tag] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the specified class is tagged with the given tag
|
||||
*
|
||||
* @param string $className Name of the class
|
||||
* @param string $tag Tag to check for
|
||||
* @return bool TRUE if the class is tagged with $tag, otherwise FALSE
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function isClassTaggedWith($className, $tag): bool
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
try {
|
||||
$classSchema = $this->getClassSchema($className);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (array_keys($classSchema->getTags()) as $tagName) {
|
||||
if ($tagName === $tag) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the specified class property is tagged with the given tag
|
||||
*
|
||||
* @param string $className Name of the class
|
||||
* @param string $propertyName Name of the property
|
||||
* @param string $tag Tag to check for
|
||||
* @return bool TRUE if the class property is tagged with $tag, otherwise FALSE
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function isPropertyTaggedWith($className, $propertyName, $tag): bool
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
try {
|
||||
$classSchema = $this->getClassSchema($className);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$property = $classSchema->getProperty($propertyName);
|
||||
|
||||
if (empty($property)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isset($property['tags'][$tag]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds class schemata from classes annotated as entities or value objects
|
||||
*
|
||||
* @param string $className
|
||||
* @throws Exception\UnknownClassException
|
||||
* @return ClassSchema The class schema
|
||||
*/
|
||||
protected function buildClassSchema($className): ClassSchema
|
||||
{
|
||||
try {
|
||||
$classSchema = new ClassSchema($className);
|
||||
} catch (\ReflectionException $e) {
|
||||
throw new Exception\UnknownClassException($e->getMessage() . '. Reflection failed.', 1278450972, $e);
|
||||
}
|
||||
$this->classSchemata[$className] = $classSchema;
|
||||
$this->dataCacheNeedsUpdate = true;
|
||||
return $classSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function __sleep(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function __wakeup(): void
|
||||
{
|
||||
$this->dataCache = null;
|
||||
$this->dataCacheNeedsUpdate = false;
|
||||
$this->classSchemata = [];
|
||||
$this->cachingEnabled = false;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user