Hi internals,
I have a feature request related to upcoming typed properties, that I'd
like to discuss here first.
I'm building a data mapper that will target PHP 7.4, and will make heavy
use of typed properties.
One of the features that I want to have available, is allowing to load a
partial object, by giving a list of properties to load:
$user = $userRepo->load(123, ['status', 'followerCount']);
Under the hood, load() will create a new User object using
ReflectionClass:newInstanceWithoutConstructor(), get the data from the
database and populate the requested fields using
ReflectionProperty::setValue(). Now when I do:
$userRepo->save($user);
The repo will use the new ReflectionProperty::isInitialized() method to
locate which fields are initialized, and only save these to the database.
The problem is, newInstanceWithoutConstructor() initializes properties to
their default value, so save() has no way to know whether initialized
properties have been explicitly set during load(), or are just default
value (note: repos are stateless).
To overcome this problem, I can think of 2 possible options:
Option 1: add an optional parameter to
ReflectionClass::newInstanceWithoutConstructor(), to not initialize
properties to their default value:
function newInstanceWithoutConstructor(bool $initializeProperties = true)
Option 2: add a ReflectionProperty::unset() method:
function unset(string $property)
This way, I could explicitly unset() every property that was not requested
during load().
Currently, there is no way to unset() a protected property from outside the
object (i.e. in the data mapper).
Note that Option 1 would have a slight performance advantage in my case,
while Option 2 may have more use cases outside of mine.
What do you think?
Cheers,
Ben
Hi Ben,
Such logic is already possible by casting the object to (array)
, and
therefore receiving a list of all properties that are explicitly set.
Also, this seems like something I've already started from Doctrine 3 (but
couldn't pursue further due to time constraints).
Marco Pivetta
On Thu, Dec 13, 2018 at 3:04 PM Benjamin Morel benjamin.morel@gmail.com
wrote:
Hi internals,
I have a feature request related to upcoming typed properties, that I'd
like to discuss here first.
I'm building a data mapper that will target PHP 7.4, and will make heavy
use of typed properties.One of the features that I want to have available, is allowing to load a
partial object, by giving a list of properties to load:$user = $userRepo->load(123, ['status', 'followerCount']);
Under the hood, load() will create a new User object using
ReflectionClass:newInstanceWithoutConstructor(), get the data from the
database and populate the requested fields using
ReflectionProperty::setValue(). Now when I do:$userRepo->save($user);
The repo will use the new ReflectionProperty::isInitialized() method to
locate which fields are initialized, and only save these to the database.
The problem is, newInstanceWithoutConstructor() initializes properties to
their default value, so save() has no way to know whether initialized
properties have been explicitly set during load(), or are just default
value (note: repos are stateless).To overcome this problem, I can think of 2 possible options:
Option 1: add an optional parameter to
ReflectionClass::newInstanceWithoutConstructor(), to not initialize
properties to their default value:function newInstanceWithoutConstructor(bool $initializeProperties = true)
Option 2: add a ReflectionProperty::unset() method:
function unset(string $property)
This way, I could explicitly unset() every property that was not requested
during load().
Currently, there is no way to unset() a protected property from outside the
object (i.e. in the data mapper).Note that Option 1 would have a slight performance advantage in my case,
while Option 2 may have more use cases outside of mine.What do you think?
Cheers,
Ben
Benjamin said:
(...) Regarding your suggestion, unfortunately not, (array) also returns
default values for properties:
Marco said:
Yes, but that's where you should unset all properties upfront:
class A {
protected $a = 1;
}
$r = new ReflectionClass('A');
$a = $r->newInstanceWithoutConstructor();
(function (A $a) { unset($a->a); })->bindTo(null, A::class)($a);
print_r((array) $a);
Array
(
)
Ah, genius! I did not think we could already use Closures for this.
Scratch that then, there is no need for an extra feature ?
https://emojipedia.org/thumbs-up-sign/
Thanks Marco.
Hi Ben,
Such logic is already possible by casting the object to
(array)
, and
therefore receiving a list of all properties that are explicitly set.Also, this seems like something I've already started from Doctrine 3 (but
couldn't pursue further due to time constraints).Marco Pivetta
On Thu, Dec 13, 2018 at 3:04 PM Benjamin Morel benjamin.morel@gmail.com
wrote:Hi internals,
I have a feature request related to upcoming typed properties, that I'd
like to discuss here first.
I'm building a data mapper that will target PHP 7.4, and will make heavy
use of typed properties.One of the features that I want to have available, is allowing to load a
partial object, by giving a list of properties to load:$user = $userRepo->load(123, ['status', 'followerCount']);
Under the hood, load() will create a new User object using
ReflectionClass:newInstanceWithoutConstructor(), get the data from the
database and populate the requested fields using
ReflectionProperty::setValue(). Now when I do:$userRepo->save($user);
The repo will use the new ReflectionProperty::isInitialized() method to
locate which fields are initialized, and only save these to the database.
The problem is, newInstanceWithoutConstructor() initializes properties to
their default value, so save() has no way to know whether initialized
properties have been explicitly set during load(), or are just default
value (note: repos are stateless).To overcome this problem, I can think of 2 possible options:
Option 1: add an optional parameter to
ReflectionClass::newInstanceWithoutConstructor(), to not initialize
properties to their default value:function newInstanceWithoutConstructor(bool $initializeProperties = true)
Option 2: add a ReflectionProperty::unset() method:
function unset(string $property)
This way, I could explicitly unset() every property that was not requested
during load().
Currently, there is no way to unset() a protected property from outside
the
object (i.e. in the data mapper).Note that Option 1 would have a slight performance advantage in my case,
while Option 2 may have more use cases outside of mine.What do you think?
Cheers,
Ben