这些是简单的例子,但想象一下你的班级中有两个以上的属性。
什么是最佳做法?
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)使用传统的二传手和吸气剂
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的魔术函数(__get,__ call等)非常受欢迎。从初看起来没有任何问题,但它们实际上非常危险。它们使API不清楚,无法自动完成,最重要的是它们很慢。他们的用例是破解PHP来做他们不想做的事情。它奏效了。但是发生了坏事。
但我想听到更多关于此的意见。
答案 0 :(得分:148)
我过去一直都是你的情况。我选择了神奇的方法。
这是一个错误,你问题的最后一部分说明了一切:
@property
phpdoc注释处理,但这需要维护它们:非常痛苦)getXXX()
不仅返回私有属性,而且还执行真正的逻辑。你有相同的命名。例如,您有$user->getName()
(返回私有属性)和$user->getToken($key)
(已计算)。当你的吸气剂不仅仅是一个吸气剂并需要做一些逻辑时,一切都仍然是一致的。最后,这是IMO最大的问题:这很神奇。魔术非常非常糟糕,因为你必须知道魔法是如何正常使用它的。这是我在团队中遇到的一个问题:每个人都必须了解魔法,而不仅仅是你。
吸气剂和二传手很难写(我讨厌它们),但它们是值得的。
答案 1 :(得分:109)
如果对象确实是“神奇的”,你只需要使用魔法。如果你有一个具有固定属性的经典对象,那么使用setter和getter,它们可以正常工作。
如果您的对象具有动态属性,例如它是数据库抽象层的一部分,并且其参数是在运行时设置的,那么为了方便您确实需要魔术方法。
答案 2 :(得分:81)
我尽可能使用__get
(和公共属性),因为它们使代码更具可读性。比较:
这段代码明确地说明了我在做什么:
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
应该看起来相同,尽管后者可能涉及复杂的计算。在我的数据类中,我使用以下小方法:
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是一种动态脚本语言,以这种方式使用它,不要假装你在做Java或C#。
答案 3 :(得分:3)
我混合了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");
}
}
}
}
答案 4 :(得分:0)
我投票支持第三种解决方案。我在我的项目中使用它,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);
}
}
通过这种方式,您可以使用自动getter(也可以编写setter),如果成员变量有特殊情况,您只需编写新方法。
答案 5 :(得分:-2)
如果你想要魔术成员,你应该使用stdClass,如果你写一个类 - 定义它包含的内容。
答案 6 :(得分:-2)
最好的做法是使用传统的吸气剂和制定者,因为内省或反思。在PHP中有一种方法(与Java完全相同)来获取方法或所有方法的名称。这样的事情会在第一种情况下返回“__get”,在第二种情况下返回“getFirstField”,“getSecondField”(加上设置者)。
答案 7 :(得分:-3)
第二个代码示例是更恰当的方法,因为您正在完全控制赋予class
的数据。
在某些情况下,__set
和__get
很有用,但在这种情况下并非如此。
答案 8 :(得分:-3)
我现在回到了二传手和吸气者,但我也把吸气剂和制定者放在了神奇的方法__get和__set中。这样,当我这样做时,我有一个默认行为
$类 - &GT;变种;
这只会调用我在__get中设置的getter。通常我会直接使用getter,但仍有一些情况下这只是更简单。