【问题标题】:Is there a better way to define __get without using variable variables or an array for all member variables?有没有更好的方法来定义 __get 而不使用变量变量或所有成员变量的数组?
【发布时间】:2013-12-14 04:10:06
【问题描述】:

我正在创建一个 BaseModel 类,并希望使用魔术方法 __set 和 __get 而不是为每个属性定义 setter 和 getter。

我目前正在使用可变变量,因为我无法通过谷歌搜索找到另一种方法。变量变量是否被认为是不好的做法,还是我无所适从?

abstract class BaseModel implements \ArrayAccess {

/**
 * Don't allow these member variables to be written by __set
 *
 * @var array
 */
protected $noSet = array();

/**
 * Don't allow these member variables to be retrieved by __get
 *
 * @var array
 */
protected $noGet = array();

public function offsetExists( $offset )
{
    return property_exists($this, $offset);
}
public function offsetGet( $offset )
{
    return $this->__get($offset);
}
public function offsetSet( $offset , $value )
{
    return $this->__set($offset, $value);
}
public function offsetUnset( $offset )
{
    unset($this->$offset);
}

public function __get($member)
{
    if( $member == 'noSet' || $member == 'noGet')
    {
        throw new \InvalidArgumentException ("Tried to access a forbidden property", 1);
    }

    if( ! property_exists($this, $member))
    {
        throw new \InvalidArgumentException ("Tried to access a non-existent property", 1);
    }

    if( in_array($member, $this->noGet))
    {
        throw new \InvalidArgumentException ("Tried to access a forbidden property", 1);
    }

    return $this->$member;
}

public function __set($member, $value)
{
    if( $member == 'noSet' || $member == 'noGet')
    {
        throw new \DomainException ("Tried write to a non-writable property.", 1);
    }

    if( ! property_exists($this, $member))
    {
        throw new \InvalidArgumentException ("Tried to access a non-existent property", 1);
    }

    if( in_array($member, $this->noSet))
    {
        throw new \DomainException ("Tried write to a non-writable property.", 1);
    }

    return $this->$member = $value;
}

【问题讨论】:

    标签: php


    【解决方案1】:

    首先,您似乎认为protected 关键字使属性无法使用魔术方法设置/获取。不是这种情况。这只会使您无法从类范围之外直接访问/修改这些属性(即,您不能执行$object->foo = 'bar' 之类的操作)

    第二,你似乎对魔法方法有误解。他们实际上所做的是在用户尝试直接访问/修改属性时强制执行行为。因此,在我的示例中,如果用户尝试这样做:

    $object->foo = 'bar';
    

    这实际上是调用__set()方法,相当于:

    $object->__set('foo', 'bar');
    

    因此,使用 get/set 魔术方法的典型类实现可能如下所示:

    class some_class {
        protected $foo;
        protected $foo2;
        public $pub;
        public function __construct() {
            // maybe do something here
        }
        public function __get($prop) {
            if(!property_exists($this, $prop) {
                throw new Exception('Tried to get unknown property ' . $prop);
            } else {
                return $this->{$prop};
            }
        }
        public function __set($prop, $value) {
            if(!property_exists($this, $prop) {
                throw new Exception('Tried to set unknown property ' . $prop);
            } else {
                $this->{$prop} = $value;
                return true; // or whatever you want to return
            }
        }
    }
    

    用法如下:

    $object = new some_class();
    $object->foo = 'bar'; // sets 'bar'
    echo $object->foo; // echo 'bar;
    var_dump($object->foo2); // null
    $object->pub = 'something'; // does not call __set() as this property is available from global scope
    echo $object->pub; // echo 'something' does not call __get() as this property is available from global scope
    $object->no_prop; // throws Exception from __get() as property does not exist
    

    尝试在类中实际调用__get()__set() 似乎很奇怪。

    查看有关对象重载的 PHP 文档以获取更多信息:

    http://www.php.net/manual/en/language.oop5.overloading.php#object.get

    【讨论】:

    • offsetExists,而其他 offset* 方法是 ArrayAccess interface 的一部分。
    • @zzzzBov 我之前没有注意到implements 声明。删除了关于该方法的评论。
    【解决方案2】:

    变量变量确实是要走的路。

    【讨论】:

    • PS:查看__call。我们摆脱了模型中的很多杂物 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2017-06-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多