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

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

这些都是简单的例子,但是假设你的class级里有更多的属性。

最好的做法是什么?

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)使用传统的setter和getters

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

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

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

首先,当时使用PHP的魔术function(__get,__call等)非常stream行。 他们从一开始就没有错,但他们其实是非常危险的。 他们使API不清楚,自动完成不可能,最重要的是他们很慢。 他们的用例是破解PHP去做一些它不想做的事情。 它的工作。 但是糟糕的事情发生了。

但我想听到更多的意见。

过去我一直都是这样。 我去了魔术的方法。

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

  • 这比getters / setters
  • 没有自动完成 (实际上这是一个主要的问题),并通过IDE进行typespipe理以进行重构和代码浏览(在Zend Studio / PhpStorm下,可以使用@property phpdoc注释来处理,但需要维护它们:挺痛苦的)
  • 文档 (phpdoc)与您的代码应该如何使用不匹配,并且查看您的类不会带来太多答案。 这很混乱。
  • 在编辑之后添加:具有属性的getters 与“真实”方法更加一致,其中getXXX()不仅返回私有属性,而且还执行真正的逻辑。 你有相同的命名。 例如你有$user->getName() (返回私有属性)和$user->getToken($key) (计算)。 当你的获得者获得的不仅仅是一个吸气者,而且需要做一些逻辑的时候,一切都是一致的。

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

吸气剂和二硫化碳是一个痛苦的写(我恨他们),但他们是值得的。

如果对象确实是“神奇的”,你只需要使用魔法。 如果你有一个固定属性的经典对象,然后使用setter和getters,他们工作正常。

如果你的对象具有dynamic属性 ,例如它是数据库抽象层的一部分,并且它的参数是在运行时设置的,那么你确实需要神奇的方法来方便使用。

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

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

 echo $user->name; 

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

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

一次访问多个属性时,两者之间的差别尤其明显。

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

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

是否“$ a-> b”应该真的什么或者只是返回一个值是被调用者的责任。 对于调用者来说,“$ user-> name”和“$ user-> accountBalance”应该看起来相同,尽pipe后者可能涉及复杂的计算。 在我的数据类中,我使用以下小方法:

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

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

底线:PHP是一种dynamic的脚本语言,使用它,不要假装你在做Java或C#。

我做了一个edem的答案和你的第二个代码的组合。 通过这种方式,我可以获得共同的getter / setter(在IDE中完成代码)的好处,如果需要,可以轻松编码,由于不存在的属性导致的exception(用于发现拼写错误: $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"); } } } } 

我投了第三个解决scheme。 我在我的项目中使用这个,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); } } 

这样你就有了自动获取器(你也可以写setters),而且如果成员variables存在特殊情况,你只需要编写新的方法。

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

最好的做法是使用传统的getter和setter,因为内省或反思。 PHP中有一种方法(就像在Java中一样)获取方法或所有方法的名称。 这样的事情会在第一种情况下返回“__get”,在第二种情况下返回“getFirstField”,“getSecondField”(加上setters)。

更多关于: http : //php.net/manual/en/book.reflection.php

我现在正在回到制定者和获得者的位置,但是我也把吸引者和制定者放在了魔术的方法__get和__set中。 这样我有一个默认行为,当我这样做

$类 – > VAR;

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

第二个代码示例是更正确的方法来执行此操作,因为您正在对给予class的数据进行完全控制。 有些情况下__set__get是有用的,但不是在这种情况下。