最佳实践:PHP 魔术方法 __set 和 __get

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6184337/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 23:35:59  来源:igfitidea点击:

Best practice: PHP Magic Methods __set and __get

phpmagic-methods

提问by rfc1484

Possible Duplicate:
Are Magic Methods Best practice in PHP?

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

These are simple examples, but imagine you have more properties than two in your class.

这些是简单的示例,但想象一下,您的类中的属性多于两个。

What would be best practice?

什么是最佳实践?

a) Using __get and __set

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
 */

b) Using traditional setters and getters

b) 使用传统的 setter 和 getter

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
 */

In this article: http://blog.webspecies.co.uk/2011-05-23/the-new-era-of-php-frameworks.html

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

The author claims that using magic methods is not a good idea:

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

First of all, back then it was very popular to use PHP's magic functions (__get, __call etc.). There is nothing wrong with them from a first look, but they are actually really dangerous. They make APIs unclear, auto-completion impossible and most importantly they are slow. The use case for them was to hack PHP to do things which it didn't want to. And it worked. But made bad things happen.

首先,当时非常流行使用 PHP 的魔法函数(__get、__call 等)。乍一看,它们并没有什么问题,但它们实际上非常危险。它们使 API 不清楚,无法自动完成,最重要的是它们很慢。他们的用例是破解 PHP 来做它不想做的事情。它奏效了。但是让不好的事情发生了。

But I would like to hear more opinions about this.

但我想听听更多关于这方面的意见。

回答by Matthieu Napoli

I have been exactly in your case in the past. And I went for magic methods.

过去我一直在你的情况下。我去了魔法方法。

This was a mistake, the last part of your question says it all :

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

  • this is slower(than getters/setters)
  • there is no auto-completion(and this is a major problem actually), and type managementby the IDE for refactoring and code-browsing (under Zend Studio/PhpStorm this can be handled with the @propertyphpdoc annotation but that requires to maintain them: quite a pain)
  • the documentation(phpdoc) doesn't match how your code is supposed to be used, and looking at your class doesn't bring much answers as well. This is confusing.
  • added after edit: having getters for properties is more consistent with "real" methodswhere getXXX()is not only returning a private property but doing real logic. You have the same naming. For example you have $user->getName()(returns private property) and $user->getToken($key)(computed). The day your getter gets more than a getter and needs to do some logic, everything is still consistent.
  • 这比 getter/setter
  • 没有自动完成(这是一个重大的问题,实际上),以及式管理由IDE的重构和代码浏览(在Zend Studio的/ PhpStorm这可能与处理@propertyPHPDoc的注解,但是需要保持他们:相当痛)
  • 文档(PHPDoc的)不符合您的代码应该如何使用,并且看着你的类并没有带来多大的答案为好。这令人困惑。
  • 编辑后添加:具有属性的 getter与“真实”方法更一致,其中getXXX()不仅返回私有属性,而且执行真实逻辑。你有相同的命名。例如你有$user->getName()(返回私有财产)和$user->getToken($key)(计算)。当你的 getter 得到的不仅仅是一个 getter 并且需要做一些逻辑的时候,一切仍然是一致的。

Finally, and this is the biggest problem IMO : this is magic. And magic is very very bad, because you have to know how the magic works to use it properly. That's a problem I've met in a team: everybody has to understand the magic, not just you.

最后,这是 IMO 最大的问题:这就是魔法。魔法非常非常糟糕,因为你必须知道魔法是如何工作的才能正确使用它。这是我在团队中遇到的一个问题:每个人都必须了解魔法,而不仅仅是你。

Getters and setters are a pain to write (I hate them) but they are worth it.

Getter 和 setter 编写起来很痛苦(我讨厌它们),但它们值得。

回答by vbence

You only need to use magic if the object is indeed "magical". If you have a classic object with fixed properties then use setters and getters, they work fine.

如果对象确实是“神奇的”,您只需要使用魔法。如果您有一个具有固定属性的经典对象,则使用 setter 和 getter,它们可以正常工作。

If your object have dynamic propertiesfor example it is part of a database abstraction layer, and its parameters are set at runtime then you indeed need the magic methods for convenience.

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

回答by user187291

I use __get(and public properties) as much as possible, because they make code much more readable. Compare:

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

this code unequivocally says what i'm doing:

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

echo $user->name;

this code makes me feel stupid, which i don't enjoy:

这段代码让我觉得很愚蠢,我不喜欢:

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

echo $user->getName();

The difference between the two is particularly obvious when you access multiple properties at once.

当您一次访问多个属性时,两者之间的区别尤为明显。

echo "
    Dear $user->firstName $user->lastName!
    Your purchase:
        $product->name  $product->count x $product->price
"

and

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

Whether $a->bshould really dosomething or just return a value is the responsibility of the callee. For the caller, $user->nameand $user->accountBalanceshould look the same, although the latter may involve complicated calculations. In my data classes i use the following small method:

到底$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");
 }

when someone calls $obj->xxxand the class has get_xxxdefined, this method will be implicitly called. So you can define a getter if you need it, while keeping your interface uniform and transparent. As an additional bonus this provides an elegant way to memorize calculations:

当有人调用$obj->xxxget_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

Bottom line: php is a dynamic scripting language, use it that way, don't pretend you're doing Java or C#.

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

回答by Carlos Campderrós

I do a mix of edem's answer and your second code. This way, I have the benefits of common getter/setters (code completion in your IDE), ease of coding if I want, exceptions due to inexistent properties (great for discovering typos: $foo->naeminstead of $foo->name), read only properties and compound properties.

我混合了 edem 的答案和您的第二个代码。通过这种方式,我拥有通​​用 getter/setter(IDE 中的代码完成)、易于编码(如果我愿意)、由于不存在的属性引起的异常(非常适合发现错别字:$foo->naem而不是$foo->name)、只读属性和复合属性的好处。

class Foo
{
    private $_bar;
    private $_baz;

    public function getBar()
    {
        return $this->_bar;
    }

    public function setBar($value)
    {
        $this->_bar = $value;
    }

    public function getBaz()
    {
        return $this->_baz;
    }

    public function getBarBaz()
    {
        return $this->_bar . ' ' . $this->_baz;
    }

    public function __get($var)
    {
        $func = 'get'.$var;
        if (method_exists($this, $func))
        {
            return $this->$func();
        } else {
            throw new InexistentPropertyException("Inexistent property: $var");
        }
    }

    public function __set($var, $value)
    {
        $func = 'set'.$var;
        if (method_exists($this, $func))
        {
            $this->$func($value);
        } else {
            if (method_exists($this, 'get'.$var))
            {
                throw new ReadOnlyException("property $var is read-only");
            } else {
                throw new InexistentPropertyException("Inexistent property: $var");
            }
        }
    }
}

回答by Adam Arold

I vote for a third solution. I use this in my projects and Symfony uses something like this too:

我投票赞成第三种解决方案。我在我的项目中使用它,Symfony 也使用类似的东西:

public function __call($val, $x) {
    if(substr($val, 0, 3) == 'get') {
        $varname = strtolower(substr($val, 3));
    }
    else {
        throw new Exception('Bad method.', 500);
    }
    if(property_exists('Yourclass', $varname)) {
        return $this->$varname;
    } else {
        throw new Exception('Property does not exist: '.$varname, 500);
    }
}

This way you have automated getters (you can write setters too), and you only have to write new methods if there is a special case for a member variable.

这样你就有了自动化的 getter(你也可以编写 setter),并且只有在成员变量有特殊情况时才需要编写新方法。

回答by Wesley van Opdorp

You should use stdClassif you want magic members, if you write a class - define what it contains.

如果你想要魔法成员,你应该使用stdClass,如果你写一个类 - 定义它包含的内容。

回答by SteeveDroz

The best practice would be to use traditionnal getters and setters, because of introspection or reflection. There is a way in PHP (exactly like in Java) to obtain the name of a method or of all methods. Such a thing would return "__get" in the first case and "getFirstField", "getSecondField" in the second (plus setters).

由于自省或反思,最佳实践是使用传统的 getter 和 setter。在 PHP 中有一种方法(就像在 Java 中一样)可以获取一个方法或所有方法的名称。这样的事情在第一种情况下会返回“__get”,在第二种情况下会返回“getFirstField”、“getSecondField”(加上 setter)。

More on that: http://php.net/manual/en/book.reflection.php

更多相关信息:http: //php.net/manual/en/book.reflection.php

回答by skowron-line

Second code example is much more proper way to do this because you are taking full control of data which are given to class. There are cases in which the __setand __getare useful but not in this case.

第二个代码示例是更合适的方法,因为您正在完全控制提供给class. 在某些情况下__set__get有用,但在这种情况下无效。

回答by AntonioCS

I am now returning to setters and getters but I am also putting the getters and setters in the magic methos __get and __set. This way I have a default behavior when I do this

我现在返回到 setter 和 getter,但我也将 getter 和 setter 放在魔术方法 __get 和 __set 中。这样,当我这样做时,我有一个默认行为

$class->var;

$class->var;

This will just call the getter I have set in the __get. Normally I will just use the getter directly but there are still some instances where this is just simpler.

这只会调用我在 __get 中设置的 getter。通常我会直接使用 getter,但仍有一些情况下这会更简单。