其他类似问题:
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
方法中使用更多代码来处理所请求数据的特殊处理,而是基本实现,如上面的代码。
答案 0 :(得分:4)
如果您正在使用它们作为阵列上的美化包装(即绝对没有额外的逻辑),我不相信它们会带来任何好处。但它们确实提供了一个缺点,因为你需要为它们编写代码。而且该代码可能有缺陷或不完整。
为简洁起见,你遗漏了__isset
吗?因为如果您确实使用__get
和__set
但未提供__isset
,那么您自己就写了一个错误。
答案 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'));
正如您所看到的,我可以使用错误,警告或通知来调用魔术方法,并在该日志级别记录我的消息。