Skip to content

Commit

Permalink
Rely on reflection to access null properties
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Oct 30, 2024
1 parent 494f010 commit c186115
Showing 1 changed file with 65 additions and 5 deletions.
70 changes: 65 additions & 5 deletions src/Extension/CoreExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ final class CoreExtension extends AbstractExtension
private $numberFormat = [0, '.', ','];
private $timezone = null;

private static $classReflectors = [];
private static $propertyReflectors = [];

/**
* Sets the default format to be used by the date filter.
*
Expand Down Expand Up @@ -1626,9 +1629,11 @@ public static function getAttribute(Environment $env, Source $source, $object, $
if (Template::METHOD_CALL !== $type) {
$arrayItem = \is_bool($item) || \is_float($item) ? (int) $item : $item;

if (((\is_array($object) || $object instanceof \ArrayObject) && (isset($object[$arrayItem]) || \array_key_exists($arrayItem, (array) $object)))
|| ($object instanceof \ArrayAccess && isset($object[$arrayItem]))
) {
if (match (true) {
\is_array($object) => \array_key_exists($arrayItem, $object),
$object instanceof \ArrayAccess => $object->offsetExists($arrayItem),
default => false,
}) {
if ($isDefinedTest) {
return true;
}
Expand Down Expand Up @@ -1697,7 +1702,12 @@ public static function getAttribute(Environment $env, Source $source, $object, $

// object property
if (Template::METHOD_CALL !== $type) {
if (isset($object->$item) || \array_key_exists((string) $item, (array) $object)) {
$propertyReflector = false;
if (match (true) {
isset($object->$item) => true,
($propertyReflector = self::$propertyReflectors[$object::class][$item] ??= self::getPropertyReflector($object, $item))->isInitialized($object) => true,
default => false,
}) {
if ($isDefinedTest) {
return true;
}
Expand All @@ -1706,7 +1716,7 @@ public static function getAttribute(Environment $env, Source $source, $object, $
$env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source);
}

return isset($object->$item) ? $object->$item : ((array) $object)[(string) $item];
return $propertyReflector ? $propertyReflector->getValue($object) : $object->$item;
}

if (\defined($object::class.'::'.$item)) {
Expand Down Expand Up @@ -2055,4 +2065,54 @@ public static function parseAttributeFunction(Parser $parser, Node $fakeNode, $a

return new GetAttrExpression($args[0], $args[1], $args[2] ?? null, Template::ANY_CALL, $line);
}

private static function getPropertyReflector(object $object, string $property): \ReflectionProperty
{
$class = self::$classReflectors[$object::class] ??= new \ReflectionClass($object::class);

if ($class->hasProperty($property)) {
$reflector = $class->getProperty($property);

if ($reflector->isPublic()) {
return $reflector;
}

return new class () extends \ReflectionProperty {
public function __construct()
{
}

public function isInitialized(?object $object = null): bool
{
return false;
}
};
}

return new class ($property) extends \ReflectionProperty {
private $value;

public function __construct(private string $property)
{
}

public function isInitialized(?object $object = null): bool
{
$arrayCast = (array) $object;

if (\array_key_exists($this->property, $arrayCast)) {
$this->value = $arrayCast[$this->property];

return true;
}

return false;
}

public function getValue(?object $object = null): mixed
{
return $this->value;
}
};
}
}

0 comments on commit c186115

Please sign in to comment.