我有一个对象,它有一些受保护的属性,我想获取并设置。该对象看起来像
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在自动完成时无法识别它们。