PHP __get 和 __set 魔术方法
除非我完全错误,否则 __get
和 __set
方法应该允许重载 → get
和 set
>。
例如,以下语句应调用 __get
方法:
echo $foo->bar;
$var = $foo->bar;
以下语句应使用 __set
方法:
$foo->bar = 'test';
这在我的代码中不起作用,但可以通过这个简单的示例重现:
class foo {
public $bar;
public function __get($name) {
echo "Get:$name";
return $this->$name;
}
public function __set($name, $value) {
echo "Set:$name to $value";
$this->$name = $value;
}
}
$foo = new foo();
echo $foo->bar;
$foo->bar = 'test';
echo "[$foo->bar]";
这只会导致:
[test]
将一些 die()
调用放在那里表明它根本没有命中它。
现在,我只是说搞砸了,并且在目前需要的地方手动使用 __get ,但这不是很动态,并且需要知道“重载”代码实际上不会被调用,除非专门调用。我想知道这是否不应该按照我理解的方式发挥作用,或者为什么这不起作用。
它在 php 5.3.3
上运行。
Unless I'm completely mistaken, the __get
and __set
methods are supposed to allow overloading of the → get
and set
.
For example, the following statements should invoke the __get
method:
echo $foo->bar;
$var = $foo->bar;
And the following should use the __set
method:
$foo->bar = 'test';
This was not working in my code, and is reproducible with this simple example:
class foo {
public $bar;
public function __get($name) {
echo "Get:$name";
return $this->$name;
}
public function __set($name, $value) {
echo "Set:$name to $value";
$this->$name = $value;
}
}
$foo = new foo();
echo $foo->bar;
$foo->bar = 'test';
echo "[$foo->bar]";
This only results in:
[test]
Putting some die()
calls in there shows that it is not hitting it at all.
For now, I just said screw it, and am manually using __get
where it's needed for now, but that's not very dynamic and requires knowledge that the 'overloaded' code is in fact not being called unless specifically called. I'd like to know if this is either not supposed to function the way I've understood that it should or why this is not working.
This is running on php 5.3.3
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
当方法或属性不可访问时,将调用
__get
、__set
、__call
和__callStatic
。您的$bar
是公开的,因此并非不可访问。请参阅手册中有关属性重载的部分:
魔术方法不能替代 getter 和 setter。它们只允许您处理方法调用或属性访问,否则会导致错误。因此,还有更多与错误处理相关的内容。另请注意,它们比使用正确的 getter 和 setter 或直接方法调用要慢得多。
__get
,__set
,__call
and__callStatic
are invoked when the method or property is inaccessible. Your$bar
is public and therefor not inaccessible.See the section on Property Overloading in the manual:
The magic methods are not substitutes for getters and setters. They just allow you to handle method calls or property access that would otherwise result in an error. As such, there are much more related to error handling. Also note that they are considerably slower than using proper getter and setter or direct method calls.
我建议使用数组通过 __set() 存储所有值。
这样您就可以确保无法以其他方式访问变量(请注意,
$values
受到保护),以避免冲突。I'd recommend to use an array for storing all values via
__set()
.This way you make sure, that you can't access the variables in another way (note that
$values
is protected), to avoid collisions.从 PHP 手册来看:
仅在读取/写入不可访问属性时调用此方法。然而,您的财产是公共的,这意味着它是可以访问的。将访问修饰符更改为 protected 可以解决该问题。
From the PHP manual:
This is only called on reading/writing inaccessible properties. Your property however is public, which means it is accessible. Changing the access modifier to protected solves the issue.
为了扩展 Berry 的答案,将访问级别设置为 protected 允许 __get 和 __set 与显式声明的属性一起使用(至少在类外部访问时),并且速度要慢得多,我将引用另一个问题的评论关于这个主题并提出使用它的理由:
我同意 __get 对于自定义 get 函数(做同样的事情)来说更慢,这是 __get() 的时间 0.0124455,这个 0.0024445 用于自定义 get () 10000 次循环后。 – 梅尔西,2012 年 11 月 23 日 22:32 最佳实践:PHP 魔术方法 __set 和 __get
根据 Melsi 的测试,相当慢大约慢 5 倍。这肯定要慢得多,但还要注意,测试表明您仍然可以使用此方法访问属性 10,000 次(计算循环迭代的时间),大约需要 1/100 秒。与实际定义的 get 和 set 方法相比,它要慢得多,这是轻描淡写的说法,但从总体上看,即使慢 5 倍实际上也不会慢。
在 99% 的现实应用中,该操作的计算时间仍然可以忽略不计,不值得考虑。唯一真正应该避免的情况是当您实际上要在单个请求中访问属性超过 10,000 次时。如果高流量站点无法承担更多服务器来保持应用程序运行的费用,那么它们确实在做错事。在访问率成为问题的高流量网站的页脚上放置单行文本广告可能可以支付包含该行文本的 1,000 台服务器的费用。最终用户永远不会敲击手指,想知道是什么导致页面加载时间这么长,因为应用程序的属性访问需要百万分之一秒。
我是作为一名具有 .NET 背景的开发人员这么说的,但对消费者不可见的 get 和 set 方法并不是 .NET 的发明。没有它们,它们根本就不是属性,这些神奇的方法是 PHP 开发人员的可取之处,甚至可以将其版本的属性称为“属性”。另外,我认为,PHP 的 Visual Studio 扩展确实支持具有受保护属性的智能感知,考虑到这一技巧。我认为,如果有足够多的开发人员以这种方式使用神奇的 __get 和 __set 方法,PHP 开发人员就会调整执行时间来迎合开发人员社区。
编辑:理论上,受保护的属性似乎在大多数情况下都有效。在实践中,事实证明,在访问类定义和扩展类中的属性时,很多时候您会想要使用 getter 和 setter。更好的解决方案是在扩展其他类时使用基类和接口,因此您只需将几行代码从基类复制到实现类中即可。我正在对我的项目的基类做更多的事情,所以我现在没有可以提供的接口,但这里是未经测试的精简类定义,其中具有神奇的属性获取和设置,使用反射来删除和移动属性受保护的数组:
如果代码中有任何错误,我深表歉意。
To expand on Berry's answer, that setting the access level to protected allows __get and __set to be used with explicitly declared properties (when accessed outside the class, at least) and the speed being considerably slower, I'll quote a comment from another question on this topic and make a case for using it anyway:
I agree that __get is more slow to a custom get function (doing the same things), this is 0.0124455 the time for __get() and this 0.0024445 is for custom get() after 10000 loops. – Melsi Nov 23 '12 at 22:32 Best practice: PHP Magic Methods __set and __get
According to Melsi's tests, considerably slower is about 5 times slower. That is definitely considerably slower, but also note that the tests show that you can still access a property with this method 10,000 times, counting time for loop iteration, in roughly 1/100 of a second. It is considerably slower in comparison with actual get and set methods defined, and that is an understatement, but in the grand scheme of things, even 5 times slower is never actually slow.
The computing time of the operation is still negligible and not worth considering in 99% of real world applications. The only time it should really be avoided is when you're actually going to be accessing the properties over 10,000 times in a single request. High traffic sites are doing something really wrong if they can't afford throwing a few more servers up to keep their applications running. A single line text ad on the footer of a high traffic site where the access rate becomes an issue could probably pay for a farm of 1,000 servers with that line of text. The end user is never going to be tapping their fingers wondering what is taking the page so long to load because your application's property access takes a millionth of a second.
I say this speaking as a developer coming from a background in .NET, but invisible get and set methods to the consumer is not .NET's invention. They simply aren't properties without them, and these magic methods are PHP's developer's saving grace for even calling their version of properties "properties" at all. Also, the Visual Studio extension for PHP does support intellisense with protected properties, with that trick in mind, I'd think. I would think with enough developers using the magic __get and __set methods in this way, the PHP developers would tune up the execution time to cater to the developer community.
Edit: In theory, protected properties seemed like it'd work in most situation. In practice, it turns out that there's a lot of times you're going to want to use your getters and setters when accessing properties within the class definition and extended classes. A better solution is a base class and interface for when extending other classes, so you can just copy the few lines of code from the base class into the implementing class. I'm doing a bit more with my project's base class, so I don't have an interface to provide right now, but here is the untested stripped down class definition with magic property getting and setting using reflection to remove and move the properties to a protected array:
My apologies if there are any bugs in the code.
这是因为 $bar 是公共财产。
运行上面的时候不需要调用魔术方法。
从你的类中删除
public $bar;
应该可以纠正这个问题。It's because $bar is a public property.
There is no need to call the magic method when running the above.
Deleting
public $bar;
from your class should correct this.最好将魔术设置/获取方法与预定义的自定义设置/获取方法一起使用,如下例所示。这样您就可以结合两个世界的优点。就速度而言,我同意它们慢一点,但你能感觉到差异吗?下面的示例还根据预定义的设置器验证数据数组。
这就是为什么我们应该同时使用两者。
类项目示例
索引.PHP
输出
Best use magic set/get methods with predefined custom set/get Methods as in example below. This way you can combine best of two worlds. In terms of speed I agree that they are a bit slower but can you even feel the difference. Example below also validate the data array against predefined setters.
This is why we should use both.
CLASS ITEM EXAMPLE
INDEX.PHP
OUTPUT
删除
public $bar;
声明,它应该按预期工作。Drop the
public $bar;
declaration and it should work as expected.意图:
Intenta con: