PHP __set和__get在继承中重载

时间:2014-10-21 14:51:04

标签: php oop magic-methods

我正在使用PHP实现OOP设计。我想知道PHP如何处理其__get和__set等神奇方法的继承。

class Foo
{
    protected $property1;

    public function __get($name)
    {
        if ($name == "property1")
        {
            // do some logic
            return $result; // may be null
        }
        return;
    }

    public function __set($name, $value)
    {
        if ($name == "property1")
        {
            // do some logic
            return $result; // may be null
        }
        result;
    }
}

现在扩展Foo

class Bar extends Foo
{
    protected $property2;

    public function __get($name)
    {
        if (($result = parent::__get($name)) !== null)
            return $result; // may be null
        if ($name == "property2")
        {
            // do some logic
            return $result; // may be null
        }
        return;
    }

    public function __set($name, $value)
    {
        if (($result = parent::__set($name, $value)) !== null)
            return $result; // may be null
        if ($name == "property2")
        {
            // do some logic
            return $result; // may be null
        }
        return;
    }
}

由于PHP返回null作为函数的结果而没有返回...这可能会导致parent::__get()parent::__set()真实地返回null或返回no时不明确值;并导致开销。

现在,如果PHP首先考虑static::_get()static::__set()并在失败时回退到parent版本,则可以简化为:

class Bar extends Foo
{
    protected $property2;

    public function __get($name)
    {
        if ($name = "property2")
        {
            // do some logic
            return $result; // may be null
        }
        return;
    }

    public function __set($name, $value)
    {
        if ($name = "property2")
        {
            // do some logic
            return $result; // may be null
        }
        return;
    }
}

我无法在当前实现上对其进行测试,因为上下文中的类操作生产,实时数据库。哪个是正确的实施?

谢谢!

2 个答案:

答案 0 :(得分:2)

仅将魔术方法用作代理方法。您的代码将更加清晰,您不必处理由继承引起的问题。

简单示例:

class Foo
{
    protected $property1;

    public function setProperty1($property1)
    {
        // do some logic
        $this->property1 = $property1;
        return $this;
    }

    public function getProperty1()
    {
        // do some logic
        return $this->property1;
    }

    public function __get($name)
    {
        $method = 'get' . ucfirst($name);
        if (method_exists($this, $method)) {
            return $this->$method();
        }
    }

    public function __set($name, $value)
    {
        $method = 'set' . ucfirst($name);
        if (method_exists($this, $method)) {
            $this->$method($value);
        }
    }
}

class Bar extends Foo
{
    protected $property2;

    public function setProperty2($property2)
    {
        // do some logic
        $this->property2 = $property2;
        return $this;
    }

    public function getProperty2()
    {
        // do some logic
        return $this->property2;
    }
}

示例电话:

$bar = new Bar;
$bar->property1 = 'foo';
$bar->property2 = 'bar';
var_dump($bar);

答案 1 :(得分:1)

我设法创建了一个隔离环境并测试了这两种方法。

方法1

class Foo
{
    protected $property1;

    public function __get($name)
    {
        if ($name == "name")
        {
            echo "been here @line(" . __LINE__ . ") Foo::__get('$name') <br/>";
            $result = $this->property1;
            return $result; // may be null
        }
        return;
    }

    public function __set($name, $value)
    {
        if ($name == "name")
        {
            echo "been here @line(" . __LINE__ . ") Foo::__set('$name', '$value') <br/>";
            $result = $this->property1 = $value;
            return $result; // may be null
        }
        return;
    }
}

class Bar extends Foo
{
    protected $property2;

    public function __get($name)
    {
        if ($name == "place")
        {
            echo "been here @line(" . __LINE__ . ") Bar::__get('$name') <br/>";
            $result = $this->property2;
            return $result; // may be null
        }
        return;
    }

    public function __set($name, $value)
    {
        if ($name == "place")
        {
            echo "been here @line(" . __LINE__ . ") Bar::__set('$name', '$value') <br/>";
            $result = $this->property2 = $value;
            return $result; // may be null
        }
        return;
    }
}

$bar = new Bar;

$bar->name = 'Alice';
$bar->place = 'Wonderland';

echo "done that: '{$bar->name} in {$bar->place}'";

上面的代码结果是:

been here @line(59) Bar::__set('place', 'Wonderland') 
been here @line(43) Bar::__get('place') 
done that: ' in Wonderland'

这显然不是答案。

方法2

class Foo
{
    protected $property1;

    public function __get($name)
    {
        if ($name == "name")
        {
            echo "been here @line(" . __LINE__ . ") Foo::__get('$name') <br/>";
            $result = $this->property1;
            return $result; // may be null
        }
        return;
    }

    public function __set($name, $value)
    {
        if ($name == "name")
        {
            echo "been here @line(" . __LINE__ . ") Foo::__set('$name', '$value') <br/>";
            $result = $this->property1 = $value;
            return $result; // may be null
        }
        return;
    }
}

class Bar extends Foo
{
    protected $property2;

    public function __get($name)
    {
        if (($result = parent::__get($name)) !== null)
        {
            echo "been here @line(" . __LINE__ . ") parent::__get('$name') <br/>";
            return $result; // may be null
        }
        if ($name == "place")
        {
            echo "been here @line(" . __LINE__ . ") Bar::__get('$name') <br/>";
            $result = $this->property2;
            return $result; // may be null
        }
        return;
    }

    public function __set($name, $value)
    {
        if (($result = parent::__set($name, $value)) !== null)
        {
            echo "been here @line(" . __LINE__ . ") parent::__set('$name', '$value') <br/>";
            return $result; // may be null
        }
        if ($name == "place")
        {
            echo "been here @line(" . __LINE__ . ") Bar::__set('$name', '$value') <br/>";
            $result = $this->property2 = $value;
            return $result; // may be null
        }
        return;
    }
}

$bar = new Bar;

$bar->name = 'Alice';
$bar->place = 'Wonderland';

echo "done that: '{$bar->name} in {$bar->place}'";

结果是:

been here @line(22) Foo::__set('name', 'Alice') 
been here @line(54) parent::__set('name', 'Alice') 
been here @line(59) Bar::__set('place', 'Wonderland') 
been here @line(11) Foo::__get('name') 
been here @line(38) parent::__get('name') 
been here @line(43) Bar::__get('place') 
done that: 'Alice in Wonderland'

这是在处理继承时重载__get和__set魔术函数的正确答案,但这会产生开销。这是一个解决方案,但还有更好的解决方案吗?