Yii2: Allow to set undefined properties in yii\base\Object

Created on 7 Mar 2018  路  8Comments  路  Source: yiisoft/yii2

I have to come back to my closed issue #12021. It seems that this is still not fixed in Yii 2.1.

My description and use case there is still valid.

The outcome of #12021 was, that this should be implemented via a trait. But it seems this never happened in 2.1.

tl;dr: I want to use the magic getter/setter methods from yii\base\BaseObject to be available in my custom classes. But I don't want that this changes PHP's default behavior, where you can also set undefined properties on a class.

<?php
class X
{
}

$x = new X;
$x->demo = 'x';   // No error, PHP allows to have dynamic properties

echo $x->wtf;  // Error, because 'wtf' was not set yet.

Yii changes this default behavior of PHP:

<?php
class X extends yii\base\BaseObject
{
}

$x = new X;

// This will throw an error:
$x->demo = 'x';
under discussion

Most helpful comment

I'd rather see it as LooseGetSetInterface that may be applied to class to indicate that __get()/__set() methods should not check property existence.

All 8 comments

You can create your custom Object class that extends yii\base\Object and overrides the __set with your proposed code:

    public function __set($name, $value)
    {
        $setter = 'set' . $name;
        if (method_exists($this, $setter)) {
            $this->$setter($value);
        } elseif (method_exists($this, 'get' . $name)) {
            throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
        } else {
            // Instead of throwing exception:
            $this->$name = $value;
        }
    }

And your custom classes can extends your custom Object.

OK. Let's have another look at it. Yii indeed enhances default PHP objects behavior adding several features:

  • Getters/setters.
  • Strict errors when non-existing property is set.

On top if it, Component adds:

  • Events.
  • Behaviors.

Ideally we want to divide these into traits and be able to use any combination of these traits for any object.

The problem is that same __get(), __set() and other magic methods are used by all these features therefore I don't immediately see a good way to achieve that.

We could probably add a specialized trait that could be configured with an array of handlers for the purpose but I do not see any way for it to be as performant as current solution.

I don't know a perfect way either. Each thinkable solution has shortcomings. Still, my current favourite is having a (optional!!) class property like:

public $__strictGetSet = false;

Only if defined this would disable checks for existing properties. The default would be the same behavior we have now.

You can create your custom Object class that extends yii\base\Object and overrides the __set with your proposed code:

@berosoboy Sure, but I may also want to extend a more complex Yii class and still disable that behavior. With your suggestion I'd basically end up duplicating a lot of Yii classes in that case.

I'd rather see it as LooseGetSetInterface that may be applied to class to indicate that __get()/__set() methods should not check property existence.

@SilverFire You mean an empty interface (interface LooseGetSetInterface {}) that's only used to flag certain classes? I like that idea. It's also straightforward to check for that in the magic method.

This issue was moved by samdark to yiisoft/yii-core#41.

Was this page helpful?
0 / 5 - 0 ratings