diff --git a/src/Symfony/Component/PropertyAccess/CHANGELOG.md b/src/Symfony/Component/PropertyAccess/CHANGELOG.md index d733c41481..7a545752b5 100644 --- a/src/Symfony/Component/PropertyAccess/CHANGELOG.md +++ b/src/Symfony/Component/PropertyAccess/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +5.1.0 +----- + +* Linking to PropertyInfo extractor to remove a lot of duplicate code + 4.4.0 ----- diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 9fc955efd3..a082dde2db 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -76,19 +76,14 @@ class PropertyAccessor implements PropertyAccessorInterface * Should not be used by application code. Use * {@link PropertyAccess::createPropertyAccessor()} instead. */ - public function __construct(bool $magicCall = false, bool $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null, bool $throwExceptionOnInvalidPropertyPath = true) + public function __construct(bool $magicCall = false, bool $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null, bool $throwExceptionOnInvalidPropertyPath = true, PropertyReadInfoExtractorInterface $readInfoExtractor = null, PropertyWriteInfoExtractorInterface $writeInfoExtractor = null) { $this->magicCall = $magicCall; $this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex; $this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool; // Replace the NullAdapter by the null value $this->ignoreInvalidProperty = !$throwExceptionOnInvalidPropertyPath; - $this->readInfoExtractor = $this->writeInfoExtractor = new ReflectionExtractor( - ['set'], - ['get', 'is', 'has', 'can'], - ['add', 'remove'], - false, - ReflectionExtractor::ALLOW_PUBLIC - ); + $this->readInfoExtractor = $readInfoExtractor ?? new ReflectionExtractor([], null, null, false); + $this->writeInfoExtractor = $writeInfoExtractor ?? new ReflectionExtractor(['set'], null, null, false); } /** @@ -391,34 +386,25 @@ class PropertyAccessor implements PropertyAccessorInterface $access = $this->getReadInfo($class, $property); if (null !== $access) { - if (PropertyReadInfo::TYPE_METHOD === $access->getType()) { - $result[self::VALUE] = $object->{$access->getName()}(); - } + $name = $access->getName(); + $type = $access->getType(); - if (PropertyReadInfo::TYPE_PROPERTY === $access->getType()) { - $result[self::VALUE] = $object->{$access->getName()}; + if (PropertyReadInfo::TYPE_METHOD === $type) { + $result[self::VALUE] = $object->$name(); + } elseif (PropertyReadInfo::TYPE_PROPERTY === $type) { + $result[self::VALUE] = $object->$name; if (isset($zval[self::REF]) && $access->canBeReference()) { - $result[self::REF] = &$object->{$access->getName()}; + $result[self::REF] = &$object->$name; } } } elseif ($object instanceof \stdClass && property_exists($object, $property)) { - // Needed to support \stdClass instances. We need to explicitly - // exclude $access[self::ACCESS_HAS_PROPERTY], otherwise if - // a *protected* property was found on the class, property_exists() - // returns true, consequently the following line will result in a - // fatal error. - $result[self::VALUE] = $object->$property; if (isset($zval[self::REF])) { $result[self::REF] = &$object->$property; } } elseif (!$ignoreInvalidProperty) { - throw new NoSuchPropertyException(sprintf( - 'Can get a way to read the property "%s" in class "%s".', - $property, - $class - )); + throw new NoSuchPropertyException(sprintf('Can\'t get a way to read the property "%s" in class "%s".', $property, $class)); } // Objects are always passed around by reference @@ -494,39 +480,29 @@ class PropertyAccessor implements PropertyAccessorInterface $class = \get_class($object); $mutator = $this->getWriteInfo($class, $property, $value); - if (null !== $mutator) { - if (PropertyWriteInfo::TYPE_METHOD === $mutator->getType()) { + if (PropertyWriteInfo::TYPE_NONE !== $mutator->getType()) { + $type = $mutator->getType(); + + if (PropertyWriteInfo::TYPE_METHOD === $type) { $object->{$mutator->getName()}($value); - } - - if (PropertyWriteInfo::TYPE_PROPERTY === $mutator->getType()) { + } elseif (PropertyWriteInfo::TYPE_PROPERTY === $type) { $object->{$mutator->getName()} = $value; - } - - if (PropertyWriteInfo::TYPE_ADDER_AND_REMOVER === $mutator->getType()) { + } elseif (PropertyWriteInfo::TYPE_ADDER_AND_REMOVER === $type) { $this->writeCollection($zval, $property, $value, $mutator->getAdderInfo(), $mutator->getRemoverInfo()); } } elseif ($object instanceof \stdClass && property_exists($object, $property)) { - // Needed to support \stdClass instances. We need to explicitly - // exclude $access[self::ACCESS_HAS_PROPERTY], otherwise if - // a *protected* property was found on the class, property_exists() - // returns true, consequently the following line will result in a - // fatal error. - $object->$property = $value; - } else { + } elseif (!$this->ignoreInvalidProperty) { + if ($mutator->hasErrors()) { + throw new NoSuchPropertyException(implode('. ', $mutator->getErrors()).'.'); + } + throw new NoSuchPropertyException(sprintf('Could not determine access type for property "%s" in class "%s".', $property, \get_class($object))); } } /** * Adjusts a collection-valued property by calling add*() and remove*() methods. - * - * @param array $zval The array containing the object to write to - * @param string $property The property to write - * @param iterable $collection The collection to write - * @param PropertyWriteInfo $addMethod The add*() method - * @param PropertyWriteInfo $removeMethod The remove*() method */ private function writeCollection(array $zval, string $property, iterable $collection, PropertyWriteInfo $addMethod, PropertyWriteInfo $removeMethod) { @@ -534,6 +510,9 @@ class PropertyAccessor implements PropertyAccessorInterface $previousValue = $this->readProperty($zval, $property); $previousValue = $previousValue[self::VALUE]; + $removeMethodName = $removeMethod->getName(); + $addMethodName = $addMethod->getName(); + if ($previousValue instanceof \Traversable) { $previousValue = iterator_to_array($previousValue); } @@ -544,7 +523,7 @@ class PropertyAccessor implements PropertyAccessorInterface foreach ($previousValue as $key => $item) { if (!\in_array($item, $collection, true)) { unset($previousValue[$key]); - $zval[self::VALUE]->{$removeMethod->getName()}($item); + $zval[self::VALUE]->$removeMethodName($item); } } } else { @@ -553,12 +532,12 @@ class PropertyAccessor implements PropertyAccessorInterface foreach ($collection as $item) { if (!$previousValue || !\in_array($item, $previousValue, true)) { - $zval[self::VALUE]->{$addMethod->getName()}($item); + $zval[self::VALUE]->$addMethodName($item); } } } - private function getWriteInfo(string $class, string $property, $value): ?PropertyWriteInfo + private function getWriteInfo(string $class, string $property, $value): PropertyWriteInfo { $useAdderAndRemover = \is_array($value) || $value instanceof \Traversable; $key = str_replace('\\', '.', $class).'..'.$property.'..'.(int) $useAdderAndRemover; @@ -601,13 +580,13 @@ class PropertyAccessor implements PropertyAccessorInterface $mutatorForArray = $this->getWriteInfo(\get_class($object), $property, []); - if (null !== $mutatorForArray || ($object instanceof \stdClass && property_exists($object, $property))) { + if (PropertyWriteInfo::TYPE_NONE !== $mutatorForArray->getType() || ($object instanceof \stdClass && property_exists($object, $property))) { return true; } $mutator = $this->getWriteInfo(\get_class($object), $property, ''); - return null !== $mutator || ($object instanceof \stdClass && property_exists($object, $property)); + return PropertyWriteInfo::TYPE_NONE !== $mutator->getType() || ($object instanceof \stdClass && property_exists($object, $property)); } /** diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php index 09aebab87b..18e51f33f2 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php @@ -188,7 +188,7 @@ abstract class PropertyAccessorCollectionTest extends PropertyAccessorArrayAcces public function testSetValueFailsIfAdderAndRemoverExistButValueIsNotTraversable() { $this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException'); - $this->expectExceptionMessageRegExp('/Could not determine access type for property "axes" in class "Symfony\\\\Component\\\\PropertyAccess\\\\Tests\\\\PropertyAccessorCollectionTest_Car[^"]*": The property "axes" in class "Symfony\\\\Component\\\\PropertyAccess\\\\Tests\\\\PropertyAccessorCollectionTest_Car[^"]*" can be defined with the methods "addAxis\(\)", "removeAxis\(\)" but the new value must be an array or an instance of \\\\Traversable, "string" given./'); + $this->expectExceptionMessageRegExp('/The property "axes" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\PropertyAccessorCollectionTest_Car" can be defined with the methods "addAxis\(\)", "removeAxis\(\)" but the new value must be an array or an instance of \\\Traversable\./'); $car = new PropertyAccessorCollectionTest_Car(); $this->propertyAccessor->setValue($car, 'axes', 'Not an array or Traversable'); diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index 218f18730f..70c3b681b7 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -760,7 +760,7 @@ class PropertyAccessorTest extends TestCase public function testAdderAndRemoveNeedsTheExactParametersDefined() { $this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException'); - $this->expectExceptionMessageRegExp('/.*The method "addFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 0 arguments, but should accept only 1\. The method "removeFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 2 arguments, but should accept only 1\./'); + $this->expectExceptionMessageRegExp('/.*The method "addFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 0 arguments, but should accept only 1\./'); $object = new TestAdderRemoverInvalidArgumentLength(); $this->propertyAccessor->setValue($object, 'foo', [1, 2]); } diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index a423c79e30..411f8121d5 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^7.2.5", "symfony/inflector": "^4.4|^5.0", - "symfony/property-info": "^4.4|^5.0" + "symfony/property-info": "^5.1" }, "require-dev": { "symfony/cache": "^4.4|^5.0" diff --git a/src/Symfony/Component/PropertyInfo/CHANGELOG.md b/src/Symfony/Component/PropertyInfo/CHANGELOG.md index 19120c9f60..2925a37a94 100644 --- a/src/Symfony/Component/PropertyInfo/CHANGELOG.md +++ b/src/Symfony/Component/PropertyInfo/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +5.1.0 +----- + +* Add support for extracting accessor and mutator via PHP Reflection + 4.3.0 ----- diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 33d77c3ef6..29e4327e82 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -233,28 +233,28 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp if ($reflClass->hasMethod($methodName) && ($reflClass->getMethod($methodName)->getModifiers() & $this->methodReflectionFlags)) { $method = $reflClass->getMethod($methodName); - return PropertyReadInfo::forMethod($methodName, $this->getReadVisiblityForMethod($method), $method->isStatic()); + return new PropertyReadInfo(PropertyReadInfo::TYPE_METHOD, $methodName, $this->getReadVisiblityForMethod($method), $method->isStatic(), false); } } if ($allowGetterSetter && $reflClass->hasMethod($getsetter) && ($reflClass->getMethod($getsetter)->getModifiers() & $this->methodReflectionFlags)) { $method = $reflClass->getMethod($getsetter); - return PropertyReadInfo::forMethod($getsetter, $this->getReadVisiblityForMethod($method), $method->isStatic()); + return new PropertyReadInfo(PropertyReadInfo::TYPE_METHOD, $getsetter, $this->getReadVisiblityForMethod($method), $method->isStatic(), false); } if ($hasProperty && ($reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags)) { $reflProperty = $reflClass->getProperty($property); - return PropertyReadInfo::forProperty($property, $this->getReadVisiblityForProperty($reflProperty), $reflProperty->isStatic(), true); + return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, $this->getReadVisiblityForProperty($reflProperty), $reflProperty->isStatic(), true); } if ($reflClass->hasMethod('__get') && ($reflClass->getMethod('__get')->getModifiers() & $this->methodReflectionFlags)) { - return PropertyReadInfo::forProperty($property, PropertyReadInfo::VISIBILITY_PUBLIC, false, false); + return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, PropertyReadInfo::VISIBILITY_PUBLIC, false, false); } if ($allowMagicCall && $reflClass->hasMethod('__call') && ($reflClass->getMethod('__call')->getModifiers() & $this->methodReflectionFlags)) { - return PropertyReadInfo::forMethod('get'.$camelProp, PropertyReadInfo::VISIBILITY_PUBLIC, false); + return new PropertyReadInfo(PropertyReadInfo::TYPE_METHOD, 'get'.$camelProp, PropertyReadInfo::VISIBILITY_PUBLIC, false, false); } return null; @@ -263,7 +263,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp /** * {@inheritdoc} */ - public function getWriteInfo(string $class, string $property, array $context = []): ?PropertyWriteInfo + public function getWriteInfo(string $class, string $property, array $context = []): PropertyWriteInfo { try { $reflClass = new \ReflectionClass($class); @@ -278,64 +278,92 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp $camelized = $this->camelize($property); $constructor = $reflClass->getConstructor(); + $singulars = (array) Inflector::singularize($camelized); + $errors = []; if (null !== $constructor && $allowConstruct) { foreach ($constructor->getParameters() as $parameter) { if ($parameter->getName() === $property) { - return PropertyWriteInfo::forConstructor($property); + return new PropertyWriteInfo(PropertyWriteInfo::TYPE_CONSTRUCTOR, $property); } } } - if ($allowAdderRemover && null !== $methods = $this->findAdderAndRemover($reflClass, (array) Inflector::singularize($camelized))) { - [$adderAccessName, $removerAccessName] = $methods; - + [$adderAccessName, $removerAccessName, $adderAndRemoverErrors] = $this->findAdderAndRemover($reflClass, $singulars); + if ($allowAdderRemover && null !== $adderAccessName && null !== $removerAccessName) { $adderMethod = $reflClass->getMethod($adderAccessName); $removerMethod = $reflClass->getMethod($removerAccessName); - return PropertyWriteInfo::forAdderAndRemover( - PropertyWriteInfo::forMethod($adderAccessName, $this->getWriteVisiblityForMethod($adderMethod), $adderMethod->isStatic()), - PropertyWriteInfo::forMethod($removerAccessName, $this->getWriteVisiblityForMethod($removerMethod), $removerMethod->isStatic()) - ); + $mutator = new PropertyWriteInfo(PropertyWriteInfo::TYPE_ADDER_AND_REMOVER); + $mutator->setAdderInfo(new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $adderAccessName, $this->getWriteVisiblityForMethod($adderMethod), $adderMethod->isStatic())); + $mutator->setRemoverInfo(new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $removerAccessName, $this->getWriteVisiblityForMethod($removerMethod), $removerMethod->isStatic())); + + return $mutator; + } else { + $errors = array_merge($errors, $adderAndRemoverErrors); } foreach ($this->mutatorPrefixes as $mutatorPrefix) { $methodName = $mutatorPrefix.$camelized; - if (!$this->isMethodAccessible($reflClass, $methodName, 1)) { + [$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, $methodName, 1); + if (!$accessible) { + $errors = array_merge($errors, $methodAccessibleErrors); continue; } $method = $reflClass->getMethod($methodName); if (!\in_array($mutatorPrefix, $this->arrayMutatorPrefixes, true)) { - return PropertyWriteInfo::forMethod($methodName, $this->getWriteVisiblityForMethod($method), $method->isStatic()); + return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $methodName, $this->getWriteVisiblityForMethod($method), $method->isStatic()); } } $getsetter = lcfirst($camelized); - if ($allowGetterSetter && $this->isMethodAccessible($reflClass, $getsetter, 1)) { + [$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, $getsetter, 1); + if ($allowGetterSetter && $accessible) { $method = $reflClass->getMethod($getsetter); - return PropertyWriteInfo::forMethod($getsetter, $this->getWriteVisiblityForMethod($method), $method->isStatic()); + return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $getsetter, $this->getWriteVisiblityForMethod($method), $method->isStatic()); + } else { + $errors = array_merge($errors, $methodAccessibleErrors); } if ($reflClass->hasProperty($property) && ($reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags)) { $reflProperty = $reflClass->getProperty($property); - return PropertyWriteInfo::forProperty($property, $this->getWriteVisiblityForProperty($reflProperty), $reflProperty->isStatic()); + return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, $this->getWriteVisiblityForProperty($reflProperty), $reflProperty->isStatic()); } - if ($this->isMethodAccessible($reflClass, '__set', 2)) { - return PropertyWriteInfo::forProperty($property, PropertyWriteInfo::VISIBILITY_PUBLIC, false); + [$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, '__set', 2); + if ($accessible) { + return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, PropertyWriteInfo::VISIBILITY_PUBLIC, false); + } else { + $errors = array_merge($errors, $methodAccessibleErrors); } - if ($allowMagicCall && $this->isMethodAccessible($reflClass, '__call', 2)) { - return PropertyWriteInfo::forMethod('set'.$camelized, PropertyWriteInfo::VISIBILITY_PUBLIC, false); + [$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, '__call', 2); + if ($allowMagicCall && $accessible) { + return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, 'set'.$camelized, PropertyWriteInfo::VISIBILITY_PUBLIC, false); + } else { + $errors = array_merge($errors, $methodAccessibleErrors); } - return null; + if (!$allowAdderRemover && null !== $adderAccessName && null !== $removerAccessName) { + $errors = array_merge($errors, [sprintf( + 'The property "%s" in class "%s" can be defined with the methods "%s()" but '. + 'the new value must be an array or an instance of \Traversable', + $property, + $reflClass->getName(), + implode('()", "', [$adderAccessName, $removerAccessName]) + )]); + } + + $noneProperty = new PropertyWriteInfo(); + $noneProperty->setErrors($errors); + + return $noneProperty; } /** @@ -575,45 +603,57 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp * @param \ReflectionClass $reflClass The reflection class for the given object * @param array $singulars The singular form of the property name or null * - * @return array|null An array containing the adder and remover when found, null otherwise + * @return array An array containing the adder and remover when found and errors */ - private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars) + private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars): array { if (!\is_array($this->arrayMutatorPrefixes) && 2 !== \count($this->arrayMutatorPrefixes)) { return null; } [$addPrefix, $removePrefix] = $this->arrayMutatorPrefixes; + $errors = []; foreach ($singulars as $singular) { $addMethod = $addPrefix.$singular; $removeMethod = $removePrefix.$singular; - $addMethodFound = $this->isMethodAccessible($reflClass, $addMethod, 1); - $removeMethodFound = $this->isMethodAccessible($reflClass, $removeMethod, 1); + [$addMethodFound, $addMethodAccessibleErrors] = $this->isMethodAccessible($reflClass, $addMethod, 1); + [$removeMethodFound, $removeMethodAccessibleErrors] = $this->isMethodAccessible($reflClass, $removeMethod, 1); + $errors = array_merge($errors, $addMethodAccessibleErrors, $removeMethodAccessibleErrors); if ($addMethodFound && $removeMethodFound) { - return [$addMethod, $removeMethod]; + return [$addMethod, $removeMethod, []]; + } elseif ($addMethodFound && !$removeMethodFound) { + $errors[] = sprintf('The add method "%s" in class "%s" was found, but the corresponding remove method "%s" was not found', $addMethod, $reflClass->getName(), $removeMethod); + } elseif (!$addMethodFound && $removeMethodFound) { + $errors[] = sprintf('The remove method "%s" in class "%s" was found, but the corresponding add method "%s" was not found', $removeMethod, $reflClass->getName(), $addMethod); } } + + return [null, null, $errors]; } /** - * Returns whether a method is public and has the number of required parameters. + * Returns whether a method is public and has the number of required parameters and errors. */ - private function isMethodAccessible(\ReflectionClass $class, string $methodName, int $parameters): bool + private function isMethodAccessible(\ReflectionClass $class, string $methodName, int $parameters): array { + $errors = []; + if ($class->hasMethod($methodName)) { $method = $class->getMethod($methodName); - if (($method->getModifiers() & $this->methodReflectionFlags) - && $method->getNumberOfRequiredParameters() <= $parameters - && $method->getNumberOfParameters() >= $parameters) { - return true; + if (\ReflectionMethod::IS_PUBLIC === $this->methodReflectionFlags && !$method->isPublic()) { + $errors[] = sprintf('The method "%s" in class "%s" was found but does not have public access.', $methodName, $class->getName()); + } elseif ($method->getNumberOfRequiredParameters() > $parameters || $method->getNumberOfParameters() < $parameters) { + $errors[] = sprintf('The method "%s" in class "%s" requires %d arguments, but should accept only %d.', $methodName, $class->getName(), $method->getNumberOfRequiredParameters(), $parameters); + } else { + return [true, $errors]; } } - return false; + return [false, $errors]; } /** diff --git a/src/Symfony/Component/PropertyInfo/PropertyReadInfo.php b/src/Symfony/Component/PropertyInfo/PropertyReadInfo.php index 4ec0f3ef76..ae10352444 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyReadInfo.php +++ b/src/Symfony/Component/PropertyInfo/PropertyReadInfo.php @@ -37,8 +37,13 @@ final class PropertyReadInfo private $byRef; - private function __construct() + public function __construct(string $type, string $name, string $visibility, bool $static, bool $byRef) { + $this->type = $type; + $this->name = $name; + $this->visibility = $visibility; + $this->static = $static; + $this->byRef = $byRef; } /** @@ -74,28 +79,4 @@ final class PropertyReadInfo { return $this->byRef; } - - public static function forProperty(string $propertyName, string $visibility, bool $static, bool $byRef): self - { - $accessor = new self(); - $accessor->type = self::TYPE_PROPERTY; - $accessor->name = $propertyName; - $accessor->visibility = $visibility; - $accessor->static = $static; - $accessor->byRef = $byRef; - - return $accessor; - } - - public static function forMethod(string $methodName, string $visibility, bool $static): self - { - $accessor = new self(); - $accessor->type = self::TYPE_METHOD; - $accessor->name = $methodName; - $accessor->visibility = $visibility; - $accessor->static = $static; - $accessor->byRef = false; - - return $accessor; - } } diff --git a/src/Symfony/Component/PropertyInfo/PropertyReadInfoExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyReadInfoExtractorInterface.php index 2c152c0f78..816b2825d5 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyReadInfoExtractorInterface.php +++ b/src/Symfony/Component/PropertyInfo/PropertyReadInfoExtractorInterface.php @@ -15,15 +15,11 @@ namespace Symfony\Component\PropertyInfo; * Extract read information for the property of a class. * * @author Joel Wurtz - * - * @internal */ interface PropertyReadInfoExtractorInterface { /** * Get read information object for a given property of a class. - * - * @internal */ public function getReadInfo(string $class, string $property, array $context = []): ?PropertyReadInfo; } diff --git a/src/Symfony/Component/PropertyInfo/PropertyWriteInfo.php b/src/Symfony/Component/PropertyInfo/PropertyWriteInfo.php index 4a3f8d380d..207003ea15 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyWriteInfo.php +++ b/src/Symfony/Component/PropertyInfo/PropertyWriteInfo.php @@ -20,6 +20,7 @@ namespace Symfony\Component\PropertyInfo; */ final class PropertyWriteInfo { + public const TYPE_NONE = 'none'; public const TYPE_METHOD = 'method'; public const TYPE_PROPERTY = 'property'; public const TYPE_ADDER_AND_REMOVER = 'adder_and_remover'; @@ -35,9 +36,14 @@ final class PropertyWriteInfo private $static; private $adderInfo; private $removerInfo; + private $errors = []; - private function __construct() + public function __construct(string $type = self::TYPE_NONE, string $name = null, string $visibility = null, bool $static = null) { + $this->type = $type; + $this->name = $name; + $this->visibility = $visibility; + $this->static = $static; } public function getType(): string @@ -54,6 +60,11 @@ final class PropertyWriteInfo return $this->name; } + public function setAdderInfo(self $adderInfo): void + { + $this->adderInfo = $adderInfo; + } + public function getAdderInfo(): self { if (null === $this->adderInfo) { @@ -63,6 +74,11 @@ final class PropertyWriteInfo return $this->adderInfo; } + public function setRemoverInfo(self $removerInfo): void + { + $this->removerInfo = $removerInfo; + } + public function getRemoverInfo(): self { if (null === $this->removerInfo) { @@ -90,44 +106,18 @@ final class PropertyWriteInfo return $this->static; } - public static function forMethod(string $methodName, string $visibility, bool $static): self + public function setErrors(array $errors): void { - $mutator = new self(); - $mutator->type = self::TYPE_METHOD; - $mutator->name = $methodName; - $mutator->visibility = $visibility; - $mutator->static = $static; - - return $mutator; + $this->errors = $errors; } - public static function forProperty(string $propertyName, string $visibility, bool $static): self + public function getErrors(): array { - $mutator = new self(); - $mutator->type = self::TYPE_PROPERTY; - $mutator->name = $propertyName; - $mutator->visibility = $visibility; - $mutator->static = $static; - - return $mutator; + return $this->errors; } - public static function forAdderAndRemover(self $adder, self $remover): self + public function hasErrors(): bool { - $mutator = new self(); - $mutator->type = self::TYPE_ADDER_AND_REMOVER; - $mutator->adderInfo = $adder; - $mutator->removerInfo = $remover; - - return $mutator; - } - - public static function forConstructor(string $propertyName): self - { - $mutator = new self(); - $mutator->type = self::TYPE_CONSTRUCTOR; - $mutator->name = $propertyName; - - return $mutator; + return (bool) \count($this->errors); } } diff --git a/src/Symfony/Component/PropertyInfo/PropertyWriteInfoExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyWriteInfoExtractorInterface.php index ed1b1c860b..f113463818 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyWriteInfoExtractorInterface.php +++ b/src/Symfony/Component/PropertyInfo/PropertyWriteInfoExtractorInterface.php @@ -15,15 +15,11 @@ namespace Symfony\Component\PropertyInfo; * Extract write information for the property of a class. * * @author Joel Wurtz - * - * @internal */ interface PropertyWriteInfoExtractorInterface { /** * Get write information object for a given property of a class. - * - * @internal */ public function getWriteInfo(string $class, string $property, array $context = []): ?PropertyWriteInfo; } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index aa2e6c8405..4f01159be2 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -378,9 +378,9 @@ class ReflectionExtractorTest extends TestCase $bazMutator = $this->extractor->getWriteInfo(Dummy::class, 'baz'); $this->assertNull($barAcessor); - $this->assertNull($barMutator); + $this->assertEquals(PropertyWriteInfo::TYPE_NONE, $barMutator->getType()); $this->assertNull($bazAcessor); - $this->assertNull($bazMutator); + $this->assertEquals(PropertyWriteInfo::TYPE_NONE, $bazMutator->getType()); } /** @@ -439,7 +439,7 @@ class ReflectionExtractorTest extends TestCase ]); if (!$found) { - $this->assertNull($writeMutator); + $this->assertEquals(PropertyWriteInfo::TYPE_NONE, $writeMutator->getType()); return; }