-
-
Notifications
You must be signed in to change notification settings - Fork 69
With PHP 7.4, the hydrator breaks with required typed property #118
Comments
I'd say that this needs to be started from integration test additions: properties not being initialized (yet being typed) seems to be a consumer-side bug though |
Maybe, maybe not, you could imagine a scenario where objects are created using |
An object should be in valid state after its instantiation: whether that instantiation is |
Talking about partial initialization specifically, any access to un-initialized object state should case initialization before the actual read operation happens (see |
Yes, I agree, I was thinking about using this hydrator as being part of the instantiation mechanism, in my use cases. Where data is pre-validated before being sent to the hydrator. |
Sorry, keyboard oops. |
I would very much like avoiding using proxies, it would be over-engineering in most of the use cases I encounter on a daily basis. My goal is not to proceed to partial initialization. When we allow this in our API's, we documented the properties as not being required (and will type them as not being required as soon as we will use PHP 7.4). Does it make sense to enforce this hydrator being more resilient, robust and avoid it crashing on such as easy fix ? I agree then that wrongly initialising the object by missing required properties is a domain related problem, not this hydrator's concern; nevertheless in the end, would it be valid to use this hydrator as being part of the factory that is supposed to correctly instantiate objects ? |
I think not, reason being that the process of hydration would return partial/incorrect data, while type information declares that the data should be there. I think adding typed properties only highlighted the presence of a bug there. |
That's the whole goal of typed properties, I can only agree. But let's consider the following code: class Foo {
private int $a = 0;
private ?int $b = 0;
private int $c;
private ?int $d;
}
$callback = \Closure::bind(function (Foo $object, array $values) {
if (isset($values['a']) || $object->a !== null && \array_key_exists('a', $values)) {
$object->a = $values['a'];
}
if (isset($values['b']) || $object->b !== null && \array_key_exists('b', $values)) {
$object->b = $values['b'];
}
if (isset($values['c']) || $object->c !== null && \array_key_exists('c', $values)) {
$object->c = $values['c'];
}
if (isset($values['d']) || $object->d !== null && \array_key_exists('d', $values)) {
$object->d = $values['d'];
}
}, null, Foo::class);
$foo = (new ReflectionClass(Foo::class))->newInstanceWithoutConstructor();
$callback($foo, [
'c' => 12,
]); This should be valid, because:
In this scenario, as a user of this hydrator, I expect this code to pass, and expect it to:
Whereas as of today (PHP 7.4.1) I got this: [pounard@guinevere] ~
> php -f pouet.php
PHP Fatal error: Uncaught Error: Typed property Foo::$d must not be accessed before initialization in /var/www/irpauto/aavant2.local/app/pouet.php:20
Stack trace:
#0 /var/www/irpauto/aavant2.local/app/pouet.php(29): Foo::{closure}(Object(Foo), Array)
#1 {main}
thrown in /var/www/irpauto/aavant2.local/app/pouet.php on line 20 Line 20 is for the 'd' value, which means that PHP did set the default values and handled Does it make sense ? |
Oh, I see what you mean: yes, it makes sense, sorry for the misunderstanding! |
Thanks, I'm sorry if wrongly expressed myself, I'm not a native english speaker (even worse, I'm french). |
I was mostly focusing on |
See also discussion regarding this issue on Cycle ORM repo where this library is considered and the issue is with |
…lt value in extract
…n check at runtime
For example, in hydrate methods, this:
will raise a "property cannot be accessed prior to initialization" if it's typed and required, because of the
object->current_revision !== null
statement.This means that the generated code must vary depending on how the property was declared, for typed and required properties, default value must be determined during compilation and code must be generated otherwise.
For example, let's consider that all other use case keep the same code, but for typed required properties, if property has a default value:
Or:
And if it has none:
Or:
Which also could be a runtime optimization (for the later) whereas it could be slower (for the former) but since now recent PHP versions have an optimised opcode for
\array_key_exists()
this probably wouldn't be visible.The text was updated successfully, but these errors were encountered: