如何在PHP中获取对象的受保护属性


How to get protected property of object in PHP

我有一个对象,它有一些受保护的属性,我想获取并设置。该对象看起来像

Fields_Form_Element_Location Object
(
[helper] => formText
[_allowEmpty:protected] => 1
[_autoInsertNotEmptyValidator:protected] => 1
[_belongsTo:protected] => 

[_description:protected] => 
[_disableLoadDefaultDecorators:protected] => 
[_errorMessages:protected] => Array
    (
    )
[_errors:protected] => Array
    (
    )
[_isErrorForced:protected] => 
[_label:protected] => Current City

[_value:protected] => 93399
[class] => field_container field_19 option_1 parent_1
)

我想获取对象的value属性。当我尝试$obj->_value$obj->value时,它会产生错误。我搜索并找到了使用PHP Reflection Class的解决方案。它在我的本地工作,但在服务器上的PHP版本是5.2.17,所以我不能在那里使用这个函数。那么有什么解决方案可以得到这样的财产吗?

以下是关于如何使用ReflectionClass:的一个非常简单的示例(没有错误检查)

function accessProtected($obj, $prop) {
  $reflection = new ReflectionClass($obj);
  $property = $reflection->getProperty($prop);
  $property->setAccessible(true);
  return $property->getValue($obj);
}

我知道你说过你被限制在5.2,但那是两年前的事了,5.5是最古老的支持版本,我希望能帮助人们使用现代版本。

对象可以被类型化为(关联)数组,并且受保护的成员具有前缀为chr(0).'*'.chr(0)的键(请参阅@fardelian的注释)。使用这个未累积的功能,您可以编写一个";曝光器":

function getProtectedValue($obj, $name) {
  $array = (array)$obj;
  $prefix = chr(0).'*'.chr(0);
  return $array[$prefix.$name];
}

或者,您可以解析序列化字符串中的值,其中(似乎)受保护的成员具有相同的前缀。

这在PHP 5.2中工作,没有ReflectionClass的开销。但是,某些属性受到保护并对客户端代码隐藏是有原因的。读取或写入可能会使数据不一致,或者作者提供了一些其他方式来公开数据,以使界面尽可能精简。当有理由直接读取受保护的属性时,当时正确的方法是实现__get()魔术方法,所以总是检查是否有,看看它能做什么。这种反直觉的查找最终在PHP8.1中用只读属性解决了。

自PHP 8.0以来,ReflectionClass还可以访问属性元数据,请确保在尝试闯入受保护的成员之前也检查它们。被取代的属性";注释"1,所以也要检查它们。

1:注释对客户端程序员来说是一个非常令人讨厌的惊喜:它们解析注释以添加疯狂的花哨的黑盒无用的混淆功能,不应该再使用了,但它们仍然存在

这就是"受保护"的含义,正如可见性一章所解释的:

被声明为受保护的成员只能在类本身内以及由继承类和父类访问。

如果你需要从外面进入房产,请选择一个:

  • 不要将其声明为受保护,而是将其公开
  • 编写几个函数来获取和设置值(getter和setter)

如果你不想修改原始类(因为它是第三方库,你不想搞砸),请创建一个扩展原始类的自定义类:

class MyFields_Form_Element_Location extends Fields_Form_Element_Location{
}

并在那里添加你的getter/setter。

如果您想在不添加getter和setter的情况下修改类。。。。

PHP 7在闭包上添加了一个调用($obj)方法(比旧的bindTo更快),允许您调用函数,因此$this变量将像在类中一样发挥作用——具有完全权限。

 //test class with restricted properties
 class test{
    protected $bar="protected bar";
    private $foo="private foo";
    public function printProperties(){
        echo $this->bar."::".$this->foo;   
     }
 }
$testInstance=new test();
//we can change or read the restricted properties by doing this...
$change=function(){
    $this->bar="I changed bar";
    $this->foo="I changed foo";
};
$change->call($testInstance);
$testInstance->printProperties();
//outputs I changed bar::I changed foo in php 7.0 

对于PHP 7.4+,我们可以使用箭头函数和Closure::call来访问私有和受保护的成员,只需使用一小行:

PHP 7.4+

检索受保护/私有成员:

class Test {
  protected $data = 'Protected variable!';
}
// Will output "Protected variable!"
echo (fn() => $this->data)->call(new Test);

更改受保护/私人成员:

class Test {
  protected $data = 'Testing';
}
$test = new Test;
(fn() => $this->data = "New Data!")->call($test);
// Will output "New Data!"
echo (fn() => $this->data)->call($test);

当然,如果我们想更改/使用多个成员,我们可以使用普通的Closure函数:

class Test {
  protected $data = 'Data!';
}
$test = new Test;
(function() {
  $this->new_data = "New {$this->data}";
})->call($test);
// Will output "New Data!"
echo (fn() => $this->new_data)->call($test);

如果您不能修改原始类,并且扩展它也不是一个选项,那么您可以使用ReflectionProperty接口。

phptoolcase库有一个方便的方法:

$value = PtcHandyMan::getProperty($your_object , 'propertyName');

来自singleton类的静态属性:

$value = PtcHandyMan::getProperty('myCLassName', 'propertyName');

您可以在此处找到该工具:http://phptoolcase.com/guides/ptc-hm-guide.html

$a=json_encode((array)$obj);
$b=(array)json_decode(str_replace(''u0000*'u0000','',$a));
echo($b['value']);

我想做的是将从外部可写的每个属性声明为public。想要对外部可见但不可写的属性,您应该声明为protected并编写__get()魔术方法,以便您可以读取它们。示例:

/**
 * Class Test
 *
 * @property int $protected
 *
 */
class Test
{
    
    private const READABLE = ['protected'];
    
    protected $protected = 1;
    
    public $public = 2;
    
    public function __get($property)
    {
        //if you want to read every protected or private
        return $this->$property ?? null;
    
        //if you want only some protected and private values to be readable
        if (in_array($property, self::READABLE)) {
            return $this->$property;
        }
    }
}
$test = new Test();
echo $test->protected; //outputs 1
echo $test->public; //outputs 2
$test->protected = 3; //outputs error - protected property

最好的方法是进行属性声明,如:

public readonly $protected = 1; //only readable from the outside
public  $public = 2; //readable and writable from the outside

但目前还不存在这样的语法(或者……至少我不知道)P.S.您应该声明protected/private属性,这些属性将在Class DockBlock中可读,如图所示,这样您就可以自动完成它们,否则您可以访问它们,但在编写代码时,IDE在自动完成时无法识别它们。