Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed behavior of the yii\base\BaseModel::attributes() method in 2.0.46 #19546

Closed
flight643 opened this issue Sep 7, 2022 · 16 comments
Closed

Comments

@flight643
Copy link
Contributor

flight643 commented Sep 7, 2022

It looks like there was a breaking change in yii\base\Model::attributes() method. In 2.0.45 it returned uninitialized properties, but in 2.0.46 such properties are not returned.

What steps will reproduce the problem?

use yii\base\Model;

class TestModel extends Model
{
    public string $id;
}

$model = new TestModel();
var_dump($model->attributes());

What is the expected result?

array(1) {
  [0]=>
  string(2) "id"
}

What do you get instead?

array(0) {
}

Additional info

This change came after PR #19309

Q A
Yii version 2.0.46
PHP version 8.1
Operating system Linux
@samdark samdark added this to the 2.0.47 milestone Sep 7, 2022
@samdark
Copy link
Member

samdark commented Sep 7, 2022

@WinterSilence would you please take a look?

@schmunk42
Copy link
Contributor

The only place where attributes() itself is tested:
https://github.com/yiisoft/yii2/blob/master/tests/framework/base/ModelTest.php#L524

We could reuse Speaker https://github.com/yiisoft/yii2/blob/161526cd41beee62d0910333c3bd96034cf73186/tests/data/base/Speaker.php for testing this, by checking for firstName, lastName on a newly created instance.

@schmunk42
Copy link
Contributor

I wonder if gii works with 2.0.46, since the bug described here would render it completely unusable - untested, it's just a thought.

@WinterSilence
Copy link
Contributor

@samdark this code generates error because property not defined by default or in constructor.

@rob006
Copy link
Contributor

rob006 commented Sep 10, 2022

It should be reverted in that case.

I would also be more cautious with such "optimization" PRs in the future, since they often produces BC breaks without any noticeable performance boost.

@WinterSilence
Copy link
Contributor

WinterSilence commented Sep 10, 2022

@rob006 i mean @flight643 example

without any noticeable performance boost

do you test it? public your benchmarks

@rob006
Copy link
Contributor

rob006 commented Sep 10, 2022

do you test it? public your benchmarks

It is your "optimization", you should provide benchmark in #19546. Although it that case it does make much since new behavior looks incorrect, so it should be reverted even if it is significantly faster.

@WinterSilence
Copy link
Contributor

@rob006

It is your "optimization", you should provide benchmark

I would like to understand on the basis of what do you draw such conclusions if you don't have any benchmarks?

it does make much since new behavior looks incorrect

what behavioral changes are you talking about? can you post correct example?

@markch123
Copy link
Contributor

My Two cents,

Firstly kudos for wintersilence for working on optimisations, this is always a great thing and shows the base is moving forward. Its just in this case some unintended consequences have been hit. If we look at the attributes() function description, it states...

 * Returns the list of attribute names.
 *
 * By default, this method returns all public non-static properties of the class.
 * You may override this method to change the default behavior.

Notice the word "all".

Now with the new code we are moving from using Reflections which I agree with wintersilence is historically something one wants to move away from (although one could argue nowadays its prob been optimized so much its not so bad). To using Yii getObjectVars() which uses PHP core get_object_vars() and this function states....

 * @return array an associative array of defined object accessible non-static properties
 * for the specified <i>object</i> in scope. If a property have
 * not been assigned a value, it will be returned with a null value.  

I suspect the line we are interested in is "If a property have not been assigned a value... it will be returned with a null value"

This is me just doing a quick 5 min look, so could be very wrong :-) But if right then need to either

  • change the comment in the attributes() function by removing "all" and stating "properties that have been assigned a value"
  • update Yii getObjectVars() with say a flag indicating give me all object vars including ones that dont have a value.
  • or just revert but add a comment in the attributes function saying this can be optimised at a later stage?

As this is probably being used in thousands of installations I think best to revert. Again my two cents :-)

@WinterSilence
Copy link
Contributor

@markch123 typed properties must be initialized before access i.e. (new TestModel())->id generates error

@markch123
Copy link
Contributor

Was there some consensus on if this is a bug or not or something that needs extra documentation?

@brandonkelly
Copy link
Contributor

brandonkelly commented Oct 26, 2022

We’re seeing Craft plugins that have broken as a result of this change, as they had typed properties w/out default values, which were relying on attributes() to set themselves. I would consider it a breaking change that should be fixed ASAP.

@schmunk42
Copy link
Contributor

Should be reverted IMHO.

@samdark
Copy link
Member

samdark commented Oct 26, 2022

@bizley would you please handle reverting it?

@bizley
Copy link
Member

bizley commented Oct 26, 2022

Sure, this was only #19309 right?

@samdark
Copy link
Member

samdark commented Oct 26, 2022

Yes, I think so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants