Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[VarExporter] add Instantiator::instantiate() to create+populate objects without calling their constructor nor any other methods#28417

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
fabpot merged 1 commit intosymfony:masterfromnicolas-grekas:ve-instantiator
Sep 26, 2018

Conversation

@nicolas-grekas
Copy link
Member

@nicolas-grekasnicolas-grekas commentedSep 9, 2018
edited
Loading

QA
Branch?master
Bug fix?no
New feature?yes
BC breaks?no
Deprecations?no
Tests pass?yes
Fixed tickets-
LicenseMIT
Doc PR-

A blend of features also provided byhttps://github.com/doctrine/instantiator andhttps://github.com/Ocramius/GeneratedHydrator in one simple method. Because it's just a few more lines on top of the existing code infrastructure in the component :)

For example, from the docblock:

// creates an empty instance of FooInstantiator::instantiate(Foo::class);// creates a Foo instance and sets one of its public, protected or private propertiesInstantiator::instantiate(Foo::class, ['propertyName' =>$propertyValue]);// creates a Foo instance and sets a private property defined on its parent Bar classInstantiator::instantiate(Foo::class, [], [    Bar::class => ['privateBarProperty' =>$propertyValue],]);

Instances of ArrayObject, ArrayIterator and SplObjectHash can be created
by using the special"\0" property name to define their internal value:

// creates an SplObjectHash where $info1 is attached to $obj1, etc.Instantiator::instantiate(SplObjectStorage::class, ["\0" => [$obj1,$info1,$obj2,$info2...]]);// creates an ArrayObject populated with $inputArrayInstantiator::instantiate(ArrayObject::class, ["\0" => [$inputArray,$optionalFlag]]);

Misses some tests for now, but reuses the existing code infrastructure used to "unserialize" objects.

useSymfony\Component\VarExporter\Internal\Registry;

/**
* A utility class to create objects without calling their constructor.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Most likely a good idea to keep this@internal until proven to be stable for usage within the component

Copy link
MemberAuthor

@nicolas-grekasnicolas-grekasSep 10, 2018
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The proposed method borrows from the existing infrastructure in the component: if there are any issues withInstantiator::instantiate(), it's also an issue forVarExporter::export(). It would make no sense to mark this method as internal, and not the other.

And if we mark the other internal, then the component would have zero public interfaces. It wouldn't make sense :)

* // creates an empty instance of Foo
* Instantiator::instantiate([Foo::class => []]);
*
* // creates a Foo instance and sets one of its public, protected or private properties
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Thepublic/private/protected case should likely be always distinguished, because of inheritance and possible property overrides.

* Instantiator::instantiate([Foo::class => ['propertyName' => $propertyValue]]);
*
* // creates a Foo instance and sets a private property defined on its parent Bar class
* Instantiator::instantiate([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This syntax is very confusing: the object type should be the firststring parameter. What's the reasoning behind putting the first type inside the array?

* Instances of ArrayObject, ArrayIterator and SplObjectHash can be created
* by using the special "\0" property name to define their internal value:
*
* // creates an SplObjectHash where $info1 is attached to $obj1, etc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Strongly suggest keeping anything coming from php core or extensions out of the scope of the component, as well as its documentation: there's an infinity of wrongness in classed that are not defined in PHP userland, and we really should rather tell users to not rely on them.

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Same as above: this is already dealt with forVarExporter::export(), so any issue found there should be spotted and fixed. There are already tests for these btw (which doesn't mean it's yet bulletproof of course.)

@javiereguiluz
Copy link
Member

javiereguiluz commentedSep 10, 2018
edited
Loading

Thanks for the detailed description! Looking at the end-user API, I have two comments/questions:

  1. Do we really need to support an array as the argument ofinstantiate()? Is that common to instantiate more than one object at a time?
// BeforeInstantiator::instantiate([    Foo::class => [],    Bar::class => ['propertyName' =>$propertyValue],]);// AfterInstantiator::instantiate(Foo::class, []);Instantiator::instantiate(Bar::class, ['propertyName' =>$propertyValue]);
  1. The use of\0 for some special objects looks like an internal detail which complicates the learning curve when exposing it publicly. Why not "hardcode" this detail internally and let the end-user not know about this?
// BeforeInstantiator::instantiate([SplObjectStorage::class => ["\0" => [$obj1,$info1,$obj2,$info2]]]);Instantiator::instantiate([ArrayObject::class => ["\0" =>$inputArray]]);// AfterInstantiator::instantiate(SplObjectStorage::class, [$obj1 =>$info1,$obj2 =>$info2]);Instantiator::instantiate(ArrayObject::class,$inputArray);
stloyd, Kocal, and DavidBadura reacted with thumbs up emoji

@nicolas-grekas
Copy link
MemberAuthor

nicolas-grekas commentedSep 10, 2018
edited
Loading

@Ocramius@javiereguiluz thanks for the review+comments, I addressed some inline, let's address the remaining ones:

I added a 2nd commit to change the signature toinstantiate(string $class, array $properties, array $privateProperties);. See updated examples in the PR. If you think it's better for DX, fine to me.

The use of \0 for some special objects looks like an internal detail

It's not actually: e.g. you can also add dynamic properties to ArrayObject instances, that arenot stored in the internal array structure. Theinstantiate() method has to provide a way to set both these properties and the internal array.\0 is specifically chosen because no dynamic properties can start with the nul character, so it is safe to use.

This doesn't mean we cannot do what you propose@javiereguiluz, but we still need a way to set dynamic properties when needed.

Actually, there is a trivial way that works already: using anstdClass key in the$privateProperties: this populates public (thus dynamic) properties already.

So, let's say we want to create something like that using instantiate:

$a =newArrayObject($input);$a->foo =123;

The current code allows doing so using:

Instantiator::instantiate(ArrayObject::class, ['foo' =>123,"\0" => [$input]]);

And wecould make it do the same using:

Instantiator::instantiate(ArrayObject::class, [$input], ['stdClass' => ['foo' =>123]]);

Please advise.
(note that I have a preference for the 1st variant because it makes it less an edge case, so would be easier to work with in generic code - at least that's how I feel about it.)

@javiereguiluz
Copy link
Member

Thanks for the detailed explanation. I hadn't thought about this use case:

$a =newArrayObject($input);$a->foo =123;

I thought this utility was only for instantiating ... but that code instantiates and then initializes, so it's doing two different things :)

What if we use this signature?

public static function instantiate(    string $classFqcn,    array $constructorArguments = [],    array $propertyValues = [])

Then, this example would look as:

Instantiator::instantiate(ArrayObject::class, [$input], ['foo' =>123])

But most of the times you'd simply use something like this:

Instantiator::instantiate(ArrayObject::class, [$input])

@nicolas-grekas
Copy link
MemberAuthor

@javiereguiluz wouldn't make sense: the specific property of instantiators is tonot call the constructor nor any other methods...

@javiereguiluz
Copy link
Member

I just followed your last example: why pass$input if it's never used?

@javiereguiluz
Copy link
Member

After talking with Nico on Slack about this ... it's clear to me that I don't fully understand this 😅 So let's forget about my previous examples and proposals. Sorry!

@nicolas-grekas
Copy link
MemberAuthor

Good news: shaking the code always helps to spot new edge cases.
PR is ready, with test cases and fixes found meanwhile.

@nicolas-grekasnicolas-grekasforce-pushed theve-instantiator branch 4 times, most recently from5b16b53 toc18f056CompareSeptember 10, 2018 17:19
nicolas-grekas added a commit that referenced this pull requestSep 11, 2018
This PR was merged into the 4.2-dev branch.Discussion----------[VarExporter] fix more edge cases| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | yes| New feature?  | no| BC breaks?    | no| Deprecations? | no| Tests pass?   | yes| Fixed tickets | -| License       | MIT| Doc PR        | -As found while preparing#28417Commits-------443bd11 [VarExporter] fix more edge cases
@nicolas-grekas
Copy link
MemberAuthor

Fixes split in PR#28437. This PR is now rebased on top of it.


class InstantiatorTestextends TestCase
{
use VarDumperTestTrait;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

not sure im missing it.. but this can be removed right?

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

good catch, removed thanks

…cts without calling their constructor nor any other methods
@fabpot
Copy link
Member

@nicolas-grekas We needs good docs on that one (and the whole component). Can you create a PR for that in the docs repo? I think it won't take too much of your time as you're the best to write something smart about this :)

@fabpot
Copy link
Member

Thank you@nicolas-grekas.

@fabpotfabpot merged commitd9bade0 intosymfony:masterSep 26, 2018
fabpot added a commit that referenced this pull requestSep 26, 2018
…e+populate objects without calling their constructor nor any other methods (nicolas-grekas)This PR was merged into the 4.2-dev branch.Discussion----------[VarExporter] add Instantiator::instantiate() to create+populate objects without calling their constructor nor any other methods| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | yes| BC breaks?    | no| Deprecations? | no| Tests pass?   | yes| Fixed tickets | -| License       | MIT| Doc PR        | -A blend of features also provided byhttps://github.com/doctrine/instantiator andhttps://github.com/Ocramius/GeneratedHydrator in one simple method. Because it's just a few more lines on top of the existing code infrastructure in the component :)For example, from the docblock:```php// creates an empty instance of FooInstantiator::instantiate(Foo::class);// creates a Foo instance and sets one of its public, protected or private propertiesInstantiator::instantiate(Foo::class, ['propertyName' => $propertyValue]);// creates a Foo instance and sets a private property defined on its parent Bar classInstantiator::instantiate(Foo::class, [], [    Bar::class => ['privateBarProperty' => $propertyValue],]);```Instances of ArrayObject, ArrayIterator and SplObjectHash can be createdby using the special `"\0"` property name to define their internal value:```php// creates an SplObjectHash where $info1 is attached to $obj1, etc.Instantiator::instantiate(SplObjectStorage::class, ["\0" => [$obj1, $info1, $obj2, $info2...]]);// creates an ArrayObject populated with $inputArrayInstantiator::instantiate(ArrayObject::class, ["\0" => [$inputArray, $optionalFlag]]);```Misses some tests for now, but reuses the existing code infrastructure used to "unserialize" objects.Commits-------d9bade0 [VarExporter] add Instantiator::instantiate() to create+populate objects without calling their constructor nor any other methods
@nicolas-grekasnicolas-grekas deleted the ve-instantiator branchSeptember 26, 2018 05:47
@nicolas-grekasnicolas-grekas modified the milestones:next,4.2Nov 1, 2018
This was referencedNov 3, 2018
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@stofstofstof left review comments

@fabpotfabpotfabpot approved these changes

@dunglasdunglasdunglas approved these changes

+2 more reviewers

@ro0NLro0NLro0NL left review comments

@OcramiusOcramiusOcramius requested changes

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Projects

None yet

Milestone

4.2

Development

Successfully merging this pull request may close these issues.

8 participants

@nicolas-grekas@javiereguiluz@fabpot@dunglas@Ocramius@stof@ro0NL@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp