基本__get实现的原因

时间:2011-12-03 01:26:12

标签: php oop overloading

  

其他类似问题:

     

Why I should use __get() and __set()-magic methods in php?
  When do/should I use __construct(), __get(), __set(), and __call() in PHP?

首先,我完全理解如何在PHP中实现__get__set方法,并且遇到了使用这些方法是有益的方案。

但是,我遇到并编写了如下所示的代码:

class SomeObject {

    protected $data = array();

    public function __set($key, $val) {
        $this->data[$key] = $val;
    }

    public function __get($key) {
        return $this->data[$key];
    }

}

为什么我们需要这样做?我使用了没有定义__get__set方法的对象,仍然可以添加未定义的属性,检索与这些属性关联的值,在这些属性上调用isset()unset()

当PHP中的对象已经表现出类似的行为时,我们是否真的需要使用上面的代码来实现这些魔术方法?

注意,我不是指在__set__get方法中使用更多代码来处理所请求数据的特殊处理,而是基本实现,如上面的代码。

6 个答案:

答案 0 :(得分:4)

如果您正在使用它们作为阵列上的美化包装(即绝对没有额外的逻辑),我不相信它们会带来任何好处。但它们确实提供了一个缺点,因为你需要为它们编写代码。而且该代码可能有缺陷或不完整。

为简洁起见,你遗漏了__isset吗?因为如果您确实使用__get__set但未提供__isset,那么您自己就写了一个错误。

See what I did there

答案 1 :(得分:2)

在您给出的示例中,使用新数组覆盖$data非常容易。如果您正在处理属性,则必须迭代一个数组并执行$this->$key = $data;(可能会导致数据泄漏)。

更详细:

protected function setData($arr)
{
   $this->data = $arr;
}

VS

protected function setData($arr)
{
    foreach($arr as $key => $data)
    {
        $this->$key => $data;
    }
}

通过上面的示例,当前通话中未显示在$arr但以前通话中的键不会被覆盖,这可能会导致意外结果。

最后,它取决于对你最有意义的东西。


作为旁注,在您提供的示例代码中,您应该确保在__isset()时实施__get()。如果不这样做,则在使用isset()empty()等功能时会出现意外结果。

答案 2 :(得分:2)

对我来说,示例代码看起来像“我们希望对象的所有缺点与数组的所有误用潜力相结合”

我可以看到这样的事情的唯一原因是当你开始从数组重构为对象时。

您将所有['']访问权限替换为->,但一旦再次编译,您就不会破坏任何内容。

在下一步中,您可以开始为特定属性添加规则。

public function __set($key, $val) {
    if($key == "money") { throw new Exception("Can't touch this"); }
    $this->data[$key] = $val;
}

public function __get($key, $val) {
    if($key == "money") { return $this->x * $this->y; }
    $this->data[$key] = $val;
}

但总的来说,我认为在这方面没有任何用处。为OO数据结构创建真正的“价值对象”似乎更有用。

为了“我喜欢->优于['']而做这样的事情不是我书中的有效用例。”


对于大多数使用“对象作为数据结构”的用例,我更愿意:

class SomeObject {

    protected $a, $b, $c;

    public function __construct(TypeA $a, TypeB $b, TypeC $c) {
        $this->a = $a;
        $this->b = $b;
        $this->c = $c;
    }

    /** @return TypeA */
    public function getA($key) {
        return $this->a;
    }
    // an so on

}

记录预期值。使用原生类型提示,而不需要写下你的自我和我作为“数据结构”的预期。

如果有一个很好的理由让一个类应该像一个数组一样,那么ArrayAccess对我来说似乎是一个更好的选择。

答案 3 :(得分:1)

您可能想要使用此原因的原因有很多,这一切都取决于您希望如何控制您的课程。几个例子:

1。)您的对象上有一些与键相关的属性 - >价值数据库。如果添加了更多值,您希望使用这些值更新数据库。所以在这个方法中,你会得到某种标志,表明有一个$new_data数组需要与数据库同步

2。)你想严格强制一个对象只附加你想要的变量。如果不存在,则可能会抛出异常。

3。)您已经抽象了一个数据库,以便获取/设置将对其中的相应值产生直接影响。

还有很多其他人。

答案 4 :(得分:0)

在你的例子中,不,你没有。

然而,当我们进入更复杂的类时,您可能想要定义setter和getter。然后样本__set将是:

function __set($key, $val) {
    if (method_exists($this, 'set'.ucfirst($key))) {
        $this -> {'set'.ucfirst($key)}($val);
    } else {
        $this -> data[$key] = $val;
    }
}

使用此代码,您只需定义一个函数setTestKey即可将$this->data['testKey']设置为$value中的值,并通过$storage->testKey = 1;调用

答案 5 :(得分:0)

我和你在一起,我从未成为魔术方法的粉丝,因为我觉得它破坏了封装。如果允许调用代码来指示对象不知道的对象行为,则会产生不期望的行为风险,与静态setter / getter访问已定义的成员变量相比,这更难以跟踪。

话虽如此,我已经看到一些聪明的例子,其中魔术方法似乎是有用的。一个实例是调用记录器并定义要在其中显示消息的日志级别。

考虑以下内容(我在我的生产代码中使用它):

// Only works with php 5.3 or greater (uses __callstatic)
class MyCustom_Api_Log
{
    /**
     * Wrapper method for getting a symfony logger object
     *
     * @return object
     */
    public static function getLogger()
    {
        return sfContext::getInstance()->getLogger();
    }

    /**
     * Magic method for logging all supported log levels
     *
     * @param string
     * @param array
     */
     public static function __callStatic($level, $params)
     {
         // Only concerned with message at this time
         // However any number of params can be passed in
         $message = shift($params);

        self::getLogger()->$level($message);
     }
}

// All these calls are made to __callStatic, so no need for individual methods
MyCustom_Api_Log::err(array('Oh noes something went wrong'));
MyCustom_Api_Log::warn(array('Oh noes something went wrong'));
MyCustom_Api_Log::notice(array('Oh noes something went wrong'));

正如您所看到的,我可以使用错误,警告或通知来调用魔术方法,并在该日志级别记录我的消息。