最佳实践:PHP Magic Methods __set和__get

rfc*_*484 121 php magic-methods

可能重复:
魔术方法是PHP的最佳实践吗?

这些都是简单的例子,但想象一下你的班级中有两个以上的属性.

什么是最佳做法?

a)使用__get和__set

class MyClass {
    private $firstField;
    private $secondField;

    public function __get($property) {
            if (property_exists($this, $property)) {
                return $this->$property;
            }
    }

    public function __set($property, $value) {
        if (property_exists($this, $property)) {
            $this->$property = $value;
        }
    }
}

$myClass = new MyClass();

$myClass->firstField = "This is a foo line";
$myClass->secondField = "This is a bar line";

echo $myClass->firstField;
echo $myClass->secondField;

/* Output:
    This is a foo line
    This is a bar line
 */
Run Code Online (Sandbox Code Playgroud)

b)使用传统的二传手和吸气剂

class MyClass {

    private $firstField;
    private $secondField;

    public function getFirstField() {
        return $this->firstField;
    }

    public function setFirstField($firstField) {
        $this->firstField = $firstField;
    }

    public function getSecondField() {
        return $this->secondField;
    }

    public function setSecondField($secondField) {
        $this->secondField = $secondField;
    }

}

$myClass = new MyClass();

$myClass->setFirstField("This is a foo line");
$myClass->setSecondField("This is a bar line");

echo $myClass->getFirstField();
echo $myClass->getSecondField();

/* Output:
    This is a foo line
    This is a bar line
 */
Run Code Online (Sandbox Code Playgroud)

在本文中:http://blog.webspecies.co.uk/2011-05-23/the-new-era-of-php-frameworks.html

作者声称使用魔法不是一个好主意:

首先,当时使用PHP的魔术函数(__get,__ call等)非常受欢迎.从初看起来没有任何问题,但它们实际上非常危险.它们使API不清楚,无法自动完成,最重要的是它们很慢.他们的用例是破解PHP来做他们不想做的事情.它奏效了.但是发生了坏事.

但我想听听更多关于此的意见.

Mat*_*oli 154

我过去一直都在你的情况.然后我去寻找魔术方法.

这是一个错误,你问题的最后一部分说明了一切:

  • 这比较(比getters/setters)
  • 没有自动完成(这实际上是一个大问题),以及式管理由IDE的重构和代码浏览(在Zend Studio的/ PhpStorm这可能与处理@propertyPHPDoc的注解,但是需要保持他们:相当疼痛)
  • 文档(PHPDoc的)不符合您的代码应该如何使用,并且看着你的类并没有带来多大的答案为好.这令人困惑.
  • 在编辑后添加:具有属性的getter 与"真实"方法更加一致,其中getXXX()不仅返回私有属性而且执行真正的逻辑.你有相同的命名.例如,您有$user->getName()(返回私有属性)和$user->getToken($key)(计算).当你的吸气剂不仅仅是吸气剂并且需要做一些逻辑时,一切都仍然是一致的.

最后,这是IMO最大的问题:这很神奇.魔术非常非常糟糕,因为你必须知道魔法是如何正常使用它的.这是我在团队中遇到的一个问题:每个人都必须了解魔法,而不仅仅是你.

写作者和写作者都很难写(我讨厌他们),但他们是值得的.

  • 虽然我同意你的一般论点,即`__get`和`__set`不应该被滥用于懒惰访问器,但是你不能为它们获得自动完成.有关如何操作的信息,请参见http://stackoverflow.com/questions/3814733/code-completion-for-private-protected-member-variables-when-using-magic-get/3815198#3815198. (14认同)
  • 我认为神奇的方法是有原因的...... (8认同)
  • 好吧,拥有"$ user-> name"(普通)和"$ user-> token"(通过__get计算)更加一致,不是吗. (4认同)
  • 我在这里唯一真正同意的是他们可能比吸气者/制定者慢,但在许多情况下这并不重要; 如果不经常使用,那么毫秒是多少?您可以通过设置PHPDoc的@property来提供自动完成_(至少在PhpStorm中)_还提供文档,关于一致性的最后一点仅仅是意见和意见不同_(请参阅user187291的评论.)_使用`__get(有一个好处) )`没有提到AFAICT,那个属性可以嵌入HEREDOCs,但方法调用不能. (3认同)
  • @stereofrog:是的,这正是:p.但是我忘记提到的另一件事是:**拥有属性的getter与"真实"方法更加一致**其中getXXX不仅返回私有属性而且执行真正的逻辑.你有相同的命名.例如,你有`$ user-> getName()`(返回属性)和`$ user-> getToken()`(计算). (2认同)

vbe*_*nce 113

如果对象确实是"神奇的",你只需要使用魔法.如果你有一个具有固定属性的经典对象,那么使用setter和getter,它们工作正常.

如果您的对象具有动态属性,例如它是数据库抽象层的一部分,并且其参数在运行时设置,那么您确实需要魔术方法以方便使用.

  • 我同意.到目前为止最佳答案.不知道为什么它没有更多的选票.这样做:`$ user-> getFirstName()`并仅在真正需要时才使用魔法. (2认同)

use*_*291 85

__get尽可能地使用(和公共属性),因为它们使代码更具可读性.相比:

这段代码明确地说出了我在做什么:

echo $user->name;
Run Code Online (Sandbox Code Playgroud)

这段代码让我感到愚蠢,我不喜欢:

function getName() { return $this->_name; }
....

echo $user->getName();
Run Code Online (Sandbox Code Playgroud)

当您一次访问多个属性时,两者之间的差异尤为明显.

echo "
    Dear $user->firstName $user->lastName!
    Your purchase:
        $product->name  $product->count x $product->price
"
Run Code Online (Sandbox Code Playgroud)

echo "
    Dear " . $user->getFirstName() . " " . $user->getLastName() . "
    Your purchase: 
        " . $product->getName() . " " . $product->getCount() . "  x " . $product->getPrice() . " ";
Run Code Online (Sandbox Code Playgroud)

是否$a->b真的应该做的一件事或只是返回值是被调用者的责任.对于来电,$user->name并且$user->accountBalance应该看起来是一样的,尽管后者可能涉及复杂的计算.在我的数据类中,我使用以下小方法:

 function __get($p) { 
      $m = "get_$p";
      if(method_exists($this, $m)) return $this->$m();
      user_error("undefined property $p");
 }
Run Code Online (Sandbox Code Playgroud)

当有人调用$obj->xxx并且类已get_xxx定义时,将隐式调用此方法.因此,您可以根据需要定义一个getter,同时保持界面的统一和透明.作为额外奖励,这提供了记忆计算的优雅方式:

  function get_accountBalance() {
      $result = <...complex stuff...>
      // since we cache the result in a public property, the getter will be called only once
      $this->accountBalance = $result;
  }

  ....


   echo $user->accountBalance; // calculate the value
   ....
   echo $user->accountBalance; // use the cached value
Run Code Online (Sandbox Code Playgroud)

底线:php是一种动态脚本语言,以这种方式使用它,不要假装你在做Java或C#.

  • 最后一行是我的理念:让PHP成为PHP,让SQL成为SQL,让Javascript成为Javascript,让HTML成为HTML,让Java,C#或您选择的语言成为现实,并运行它的设计运作方式.固有的是,当你的团队*知道*如何做到这一点时,它主要是有效的,如何使用一种语言来发挥其最大的潜力,而不是将它变成另一种语言的风格,但这就是你将如何获得无论如何最好的工作,不是通过尝试使PHP成为Java等. (12认同)
  • ...此外,我觉得PHP中的很多"最佳实践"都是由开始使用PHP的Java程序员驱动的,因为他们需要或想要一个更好的"网页"语言,并且发现(当时)真正的在PHP世界中糟糕的编程卫生状态并强制执行他们的世界观只是为了获得*some*结构,并且一些结构比没有更好,这被认为这实际上是在PHP中做事的最佳方式.这可能就是语言的发展方向,但我不认为它代表了最终的真理,至少目前不是这样. (9认同)
  • @Matthieu:正如答案中所提到的,`$ foo-> bar`只会调用`$ this-> get_bar()`,****是**getter****可以**改变为你需要的任何东西它来. (5认同)
  • 你知道吗 - >亲爱的{$ user-> getFirstName()} ?? (3认同)
  • @MarkAmery没有`public`字段,是的.这就是我的建议:封装. (2认同)
  • -1,我不同意,魔术方法不是更易读,您的示例有缺陷,因为您可以轻松地对此类字符串使用sprintf并使用getter使代码保持可读性-__get和__set并非一对一的解决方案与物体互动 (2认同)