-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
Description
Symfony version(s) affected
6.0.5
Description
This may be either a documentation problem or a bug in the PropertyAccess itself (hoping for the later ;)). In essence I have a case where an object is designated to be used like an array and has no __set()
, relying on properties being just set on it. PropertyAccess throws NoSuchPropertyException
when a non-existent property is used in setValue()
and isExceptionOnInvalidPropertyPath() === true
:
PHP Fatal error: Uncaught Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException: Could not determine access type for property "foo" in class "stdClass". in /app/vendor/symfony/property-access/PropertyAccessor.php:537
Stack trace:
#0 /app/vendor/symfony/property-access/PropertyAccessor.php(136): Symfony\Component\PropertyAccess\PropertyAccessor->writeProperty(Array, 'foo', 'bar')
#1 /app/test.php(20): Symfony\Component\PropertyAccess\PropertyAccessor->setValue(Object(stdClass), 'foo', 'bar')
#2 {main}
thrown in /app/vendor/symfony/property-access/PropertyAccessor.php on line 537
When isExceptionOnInvalidPropertyPath() === false
the errors is swallowed quietly and no property is set on the object.
This behavior seems to contradict PropertyAccessorBuilder
:
//...
* This has no influence on writing non-existing indices with PropertyAccessorInterface::setValue()
* which are always created on-the-fly.
//...
public function enableExceptionOnInvalidPropertyPath(): static
The PropertyAccessorInterface::setValue()
however explicitly states that, upon chain of options being exhausted, "If neither is found, an exception is thrown.".
How to reproduce
<?php
use Symfony\Component\PropertyAccess\PropertyAccess;
require_once __DIR__ . '/vendor/autoload.php';
$obj = new stdClass();
$builder = PropertyAccess::createPropertyAccessorBuilder();
$builder->disableExceptionOnInvalidPropertyPath();
$builder->getPropertyAccessor()->setValue($obj, 'foo', 'bar');
var_dump($obj); //empty object results
$builder->enableExceptionOnInvalidPropertyPath();
$builder->getPropertyAccessor()->setValue($obj, 'foo', 'bar'); //exception
Possible Solution
For me the most logical solution will be to allow writing non-existent properties only when accessor was built with isExceptionOnInvalidPropertyPath() === false
and throw as currently is done with isExceptionOnInvalidPropertyPath() === true
.
Alternatively the docblock of the builder (with maybe some not in the docs itself?) can be improved to state the property will never be created. However, this imho creates a strange situation where an exception is silenced but the property is silently ignored.
Additional Context
I believe allowing for dynamic properties creation, when it's explicitly intended, should be supported. While controversial in the internals itself it will become a first-class citizen in PHP 8.2 where intended: https://3v4l.org/Vj7Zu/rfc#vgit.master