mirror of
https://github.com/Respect/Validation.git
synced 2026-03-16 15:25:45 +01:00
All validators related to object properties only consider the properties of the object being validated, not those of its parent object. That's because PHP reflection only gets the properties visible to the current class (public or protected). The problem with that is that when there's a private property in the parent class, we're completely unaware of it. This commit will modify those rules by retrieving properties from the parent class, ensuring we capture all properties that require validation.
94 lines
2.8 KiB
PHP
94 lines
2.8 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Respect\Validation\Rules;
|
|
|
|
use Attribute;
|
|
use ReflectionAttribute;
|
|
use ReflectionClass;
|
|
use ReflectionObject;
|
|
use ReflectionProperty;
|
|
use Respect\Validation\Id;
|
|
use Respect\Validation\Result;
|
|
use Respect\Validation\Rule;
|
|
use Respect\Validation\Rules\Core\Reducer;
|
|
|
|
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
|
|
final class Attributes implements Rule
|
|
{
|
|
public function evaluate(mixed $input): Result
|
|
{
|
|
$id = new Id('attributes');
|
|
$objectType = (new ObjectType())->evaluate($input);
|
|
if (!$objectType->hasPassed) {
|
|
return $objectType->withId($id);
|
|
}
|
|
|
|
$reflection = new ReflectionObject($input);
|
|
$rules = [...$this->getClassRules($reflection), ...$this->getPropertyRules($reflection)];
|
|
if ($rules === []) {
|
|
return (new AlwaysValid())->evaluate($input)->withId($id);
|
|
}
|
|
|
|
return (new Reducer(...$rules))->evaluate($input)->withId($id);
|
|
}
|
|
|
|
/** @return array<Rule> */
|
|
private function getClassRules(ReflectionObject $reflection): array
|
|
{
|
|
$rules = [];
|
|
while ($reflection instanceof ReflectionClass) {
|
|
foreach ($reflection->getAttributes(Rule::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
|
|
$rules[] = $attribute->newInstance();
|
|
}
|
|
|
|
$reflection = $reflection->getParentClass();
|
|
}
|
|
|
|
return $rules;
|
|
}
|
|
|
|
/** @return array<Rule> */
|
|
private function getPropertyRules(ReflectionObject $reflection): array
|
|
{
|
|
$rules = [];
|
|
foreach ($this->getProperties($reflection) as $propertyName => $property) {
|
|
$propertyRules = [];
|
|
foreach ($property->getAttributes(Rule::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
|
|
$propertyRules[] = $attribute->newInstance();
|
|
}
|
|
|
|
if ($propertyRules === []) {
|
|
continue;
|
|
}
|
|
|
|
$allowsNull = $property->getType()?->allowsNull() ?? false;
|
|
|
|
$childRule = new Reducer(...$propertyRules);
|
|
$rules[] = new Property($propertyName, $allowsNull ? new NullOr($childRule) : $childRule);
|
|
}
|
|
|
|
return $rules;
|
|
}
|
|
|
|
/** @return array<ReflectionProperty> */
|
|
private function getProperties(ReflectionObject $reflection): array
|
|
{
|
|
$properties = [];
|
|
while ($reflection instanceof ReflectionClass) {
|
|
foreach ($reflection->getProperties() as $property) {
|
|
$properties[$property->name] = $property;
|
|
}
|
|
|
|
$reflection = $reflection->getParentClass();
|
|
}
|
|
|
|
return $properties;
|
|
}
|
|
}
|