Rebase, fix tests, review & update CHANGELOG
This commit is contained in:
parent
fc250863a8
commit
0a92dab753
@ -1,6 +1,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
* Linking to PropertyInfo extractor to remove a lot of duplicate code
|
||||
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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');
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -1,6 +1,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
* Add support for extracting accessor and mutator via PHP Reflection
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -15,15 +15,11 @@ namespace Symfony\Component\PropertyInfo;
|
||||
* Extract read information for the property of a class.
|
||||
*
|
||||
* @author Joel Wurtz <jwurtz@jolicode.com>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -15,15 +15,11 @@ namespace Symfony\Component\PropertyInfo;
|
||||
* Extract write information for the property of a class.
|
||||
*
|
||||
* @author Joel Wurtz <jwurtz@jolicode.com>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user