简体   繁体   English

魔术方法__get和__set的PHP可见性

[英]PHP Visibility with Magic Methods __get & __set

I recently went to an interview and my code I supplied had magic functions to get and set variables. 我最近去了一次采访,提供的代码具有魔术函数来获取和设置变量。 My code was as follows: 我的代码如下:

public function __get($name){
    try { 
        return $this->$name;
    } catch (Exception $e) { 
        throw new Exception('Trying to get a variable "'.$name.'" that does not exist.'); 
    }
}

In the interview the guy asked me about the visibility on my variables, I had private ones set but these were now accessible by using magic functions. 在采访中,那个家伙问我变量的可见性,我设置了私有变量,但是现在可以使用魔术函数访问这些变量。 Essentially I failed the interview on this point, so I wanted to understand more. 从本质上讲,我在这一点上没有通过面试,所以我想了解更多。 I was following a tutorial from PHP Master and found a different __get , I have tried to break it but it seems to work, but in a strange way. 我正在研究PHP Master的教程,发现了另一个__get ,我试图打破它,但它似乎可以工作,但是以一种奇怪的方式。

I call __get('test') to get my variable _test but if it is set to private it calls itself again and tells me that it cannot access __test . 我调用__get('test')来获取我的变量_test但是如果将其设置为private,它将再次调用自身并告诉我它无法访问__test I do not really understand why it calls itself again. 我真的不明白为什么它会再次自我称呼。

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;

}

Can anyone give me some pointers on proper use of __get and __set when using visibility in a class and why this function would call itself again. 在类中使用可见性时,谁能给我一些有关正确使用__get和__set的指示,以及为什么该函数会再次调用自身。

I have read the other posts here but I am still struggling with this concept. 我在这里阅读了其他文章,但我仍在为这个概念而苦苦挣扎。

I just bumped into this question and there is a little thing that may be worth clarifying: 我只是碰到了这个问题,可能有一点事情需要澄清:

I do not really understand why it calls itself again. 我真的不明白为什么它会再次自我称呼。

The code is not calling itself again but trying to execute a custom getter if there is one defined. 该代码不会再次调用自身,而是尝试执行自定义getter(如果已定义)。 Let me break down the method execution: 让我分解一下方法执行:

public function __get($name)
{

As already explained in the other answers and here , the __get() magic method is called when you are trying to access a property that is not declared or not visible in the calling scope. 正如其他答案和此处已解释的那样,当您尝试访问未声明或在调用范围中不可见的属性时,将调用__get()魔术方法。

$field = '_' . strtolower($name);

if (!property_exists($this, $field)){
    throw new \InvalidArgumentException(
        "Getting the field '$field' is not valid for this entity"
    );
}

Here it just checks that a property with an underscore pre-appended exists in the class definition. 在这里,它只是检查类定义中是否存在带下划线的属性。 If it doesn't, an exception is thrown. 如果不是,则抛出异常。

$accessor = 'get' . ucfirst(strtolower($name));

Here it creates the name of the getter to call if it exists. 如果存在,它将在这里创建要调用的吸气剂的名称。 Thus, if you try to access a property named email and there is a private member called _email the $accessor variable will now hold the 'getEmail' string. 因此,如果您尝试访问名为email的属性,并且有一个名为_email的私有成员,则$accessor变量现在将保留'getEmail'字符串。

return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ?
    $this->$accessor() : $this->$field;

The final part is a bit cryiptic, since many things are happening in one line: 最后一部分有点讽刺,因为许多事情正在同一行中发生:

  • method_exists($this, $accessor) . method_exists($this, $accessor) Checks if the receiver ( $this ) has a method with $accessor name (in our example, getEmail ). 检查接收方( $this )是否具有带有$accessor名称的方法(在我们的示例中为getEmail )。
  • is_callable(array($this, $accessor)) . is_callable(array($this, $accessor)) Checks that the getter can be called . 检查可以调用 getter。
  • If both conditions are met, the custom getter is called and its return value is returned ( $this->$accessor() ). 如果两个条件都满足,则调用自定义getter并返回其返回值( $this->$accessor() )。 If not, the property contents are returned ( $this->$field ). 如果不是,则返回属性内容( $this->$field )。

As an example consider this class definition: 例如,考虑以下类定义:

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;

}
}

and then run: 然后运行:

$example = new AccessorsExample();
echo $example->test1 . "\n";
echo $example->test2 . "\n";

You should see: 您应该看到:

One
Calling the getter
Two

HTH 高温超导

I find it's better to be explicit when allowing access to properties via __get() . 我发现在允许通过__get()访问属性时最好明确一些。 This way you can still have truly private members, and you don't run the risk of accidentally exposing things you add later. 这样,您仍然可以拥有真正的私人成员,并且不会冒意外暴露您以后添加的内容的风险。

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
  }
}
public function __get($name){
    try { 
        return $this->$name;
    } catch (Exception $e) { 
        throw new Exception('Trying to get a variable "'.$name.'" that does not exist.'); 
    }
}

A couple of problems with this method as it stands, without seeing the rest of the code: 这种方法目前存在两个问题,而没有看到其余的代码:

  • It allows unrestricted public read access to all private and protected properties inside the class. 它允许对类内所有私有和受保护属性的不受限制的公共读取访问。 Except in special cases this is usually undesirable as it does defeat the object of class member visibility. 除非在特殊情况下,否则这通常是不希望的,因为它确实使类成员可见性的对象无效。 As mentioned earlier, access should be restricted, either by checking against an allowed list (as in Rob Agar's answer) or by checking for a defined getter (as in the OP's question). 如前所述,应该通过检查允许的列表(如Rob Agar的回答)或检查定义的getter(如OP的问题)来限制访问。

  • An exception is not normally thrown when accessing an undefined property (unless you have a custom error handler that is set to do so). 访问未定义的属性时,通常不会引发异常(除非您已将自定义错误处理程序设置为这样做)。 Accessing an undefined property normally triggers an E_NOTICE, so your method would not trap this. 访问未定义的属性通常会触发E_NOTICE,因此您的方法不会捕获此错误。 You should first validate that $name does actually exist. 您应该首先验证$name确实存在。

I don't get exactl what is your problem, but for example this code works 我不知道您的问题是什么,但是例如此代码有效

<?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;

as you can check here (http://codepad.org/jmkvHiDe). 您可以在此处查看(http://codepad.org/jmkvHiDe)。

Magic methods like __get() will be called, when you try to access a private property exactly as they would be called to access a non existing property, of course if you set a property as "private" and then the user can access the variable through a magic method, why set the property as private in the first place? 当您尝试完全像访问不存在的属性一样调用私有属性时,将调用诸如__get()类的魔术方法,当然,如果将属性设置为“私有”,则用户可以访问变量通过魔术方法,为什么首先将属性设置为私有?

Instead of $this->$name; 代替$this->$name;

Use something like $this->protected_values[$name]; 使用类似$this->protected_values[$name];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM