我最近接受了采访,我提供的代码具有魔术功能来获取和设置变量。我的代码如下:
public function __get($name){
try {
return $this->$name;
} catch (Exception $e) {
throw new Exception('Trying to get a variable "'.$name.'" that does not exist.');
}
}
在采访中,这个人告诉我关于变量的可见性,我设置了私有变量,但现在可以通过使用魔术函数来访问它们。基本上我在这一点上没有通过采访,所以我想了解更多。我正在按照PHP Master的一个教程找到一个不同的__get
,我试图打破它,但它似乎有效,但是以一种奇怪的方式。
我致电__get('test')
获取我的变量_test
,但如果将其设置为私有,则会再次调用自己,并告诉我它无法访问__test
。我真的不明白为什么它再次自称。
public function __get($name)
{
$field = '_' . strtolower($name);
if (!property_exists($this, $field)){
throw new \InvalidArgumentException(
"Getting the field '$field' is not valid for this entity"
);
}
$accessor = 'get' . ucfirst(strtolower($name));
return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ?
$this->$accessor() : $this->$field;
}
当在类中使用可见性以及为什么这个函数会再次调用自己时,是否可以给我一些关于正确使用__get和__set的指示。
我已阅读其他帖子,但我仍在努力解决这个问题。
答案 0 :(得分:5)
我刚刚提出这个问题,有一件事可能值得澄清:
我真的不明白为什么它再次自称。
代码不是再次调用自己,而是尝试执行自定义getter(如果有一个定义的)。让我分解方法执行:
public function __get($name)
{
正如在其他答案和here中已经解释的那样,当您尝试访问未在调用范围中声明或不可见的属性时,会调用__get()魔术方法。
$field = '_' . strtolower($name);
if (!property_exists($this, $field)){
throw new \InvalidArgumentException(
"Getting the field '$field' is not valid for this entity"
);
}
这里只检查类定义中是否存在预先附加下划线的属性。如果没有,则抛出异常。
$accessor = 'get' . ucfirst(strtolower($name));
这里创建要调用的getter的名称(如果存在)。因此,如果您尝试访问名为email
的属性并且存在名为_email
的私有成员,则$accessor
变量现在将保留'getEmail'
字符串。
return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ?
$this->$accessor() : $this->$field;
最后一部分有点神秘,因为很多事情都发生在一行:
method_exists($this, $accessor)
。检查接收方($this
)是否有$accessor
名称的方法(在我们的示例中为getEmail
)。is_callable(array($this, $accessor))
。检查getter can be called。$this->$accessor()
)。如果不是,则返回属性内容($this->$field
)。作为示例,请考虑此类定义:
class AccessorsExample
{
private $_test1 = "One";
private $_test2 = "Two";
public function getTest2()
{
echo "Calling the getter\n";
return $this->_test2;
}
public function __get($name)
{
$field = '_' . strtolower($name);
if (!property_exists($this, $field)){
throw new \InvalidArgumentException(
"Getting the field '$field' is not valid for this entity"
);
}
$accessor = 'get' . ucfirst(strtolower($name));
return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ?
$this->$accessor() : $this->$field;
}
}
然后运行:
$example = new AccessorsExample();
echo $example->test1 . "\n";
echo $example->test2 . "\n";
您应该看到:
One
Calling the getter
Two
HTH
答案 1 :(得分:1)
我发现在允许通过__get()
访问属性时要明确一点。通过这种方式,您仍然可以拥有真正的私人成员,并且不会冒着意外暴露您稍后添加的内容的风险。
class Foo
{
// readonly
private $foo;
private $bar;
// truly private
private $baz;
public function __get($var)
{
switch ($var)
{
// readonly access to foo and bar, but not baz
case 'foo':
case 'bar':
return $this->$var;
// readonly dynamically generated property
case 'buzz':
return $this->buzz();
default:
throw new InvalidPropertyException($var);
}
}
public function __isset($var)
{
switch ($var)
{
// return true for foo, bar and buzz so functions like isset()
// and empty() work as expected
case 'foo':
case 'bar':
case 'buzz':
return true;
default:
return false;
}
}
// dynamic readonly property implementation
private function buzz()
{
// calculate and return something which depends on other private properties
}
}
答案 2 :(得分:0)
我没有得到完整的问题,但是例如这段代码可以运行
<?php
class foo {
private $_test = "my";
public function __get($name)
{
$field = '_' . strtolower($name);
if (!property_exists($this, $field)){
throw new InvalidArgumentException(
"Getting the field '$field' is not valid for this entity"
);
}
$accessor = 'get' . ucfirst(strtolower($name));
return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ?
$this->$accessor() : $this->$field;
}
}
$foo = new foo();
echo $foo->test;
你可以在这里查看(http://codepad.org/jmkvHiDe)。
当您尝试访问私有属性时,将调用__get()
等魔术方法,就像调用它们以访问非现有属性一样,当然如果您将属性设置为“私有”,然后用户可以通过魔术方法访问变量,为什么首先将属性设置为私有?
答案 3 :(得分:0)
而不是$this->$name;
使用$this->protected_values[$name];
答案 4 :(得分:0)
public function __get($name){
try {
return $this->$name;
} catch (Exception $e) {
throw new Exception('Trying to get a variable "'.$name.'" that does not exist.');
}
}
这个方法存在一些问题,没有看到其余的代码:
它允许对类中的所有私有和受保护属性进行无限制的公共读取访问。除非在特殊情况下,这通常是不受欢迎的,因为它确实击败了类成员可见性的对象。如前所述,访问应该受到限制,要么通过检查允许列表(如Rob Agar的答案),要么检查已定义的getter(如OP的问题中所示)。
访问未定义的属性时通常不会抛出异常(除非您设置了自定义错误处理程序)。访问未定义的属性通常会触发E_NOTICE,因此您的方法不会捕获它。您应首先验证$name
确实存在。