PHP获取类私有属性的3种方法

今天在推上看到一条获取PHP类私有属性的推文,感觉很有意思:

顺着推文联想,还有其他方式吗?经过自己的测试及网上答案,总结出三种方法:

1. 反射

反射可以获取类的详细信息,要获取私有属性的值,只需将对应属性的ReflectionProperty实例设置为可访问再取值即可。示例代码如下:

namespace tlanyan;

class Foo {
 private $bar = "Foo bar!";
}

// 获取反射类及反射属性
$class = new \ReflectionClass(Foo::class);
$property = $class->getProperty("bar");
// 设置属性可访问
$property->setAccessible(true);

$foo = new Foo;
// 获取对象属性值
// 注意:只能通过 ReflectionProperty 实例的 getValue 方法访问
// 不能这样直接访问: $foo->bar;
echo $property->getValue($foo), PHP_EOL:
// 输出: Foo bar!

本人之前写过“PHP回顾之反射”一文,比较详细的介绍了反射及用法,有兴趣的阅读参考。

2. 转换成数组

这种方法用将对象强制转换成数组,再通过键获取其值。示例代码如下:

class Foo {
 private $bar = "Foo bar!";
}

$foo = new Foo;
// 强制转型
$attrs = (array)$foo;
// 拼接key,注意 "\0" 不能改成单引号!
$key = "\0" . Foo::class . "\0" . "bar";
echo $attrs[$key], PHP_EOL;
// 输出: Foo bar!

上述代码中key的拼接方式比较诡异,key规则如下:

  1. public属性, key是 属性名;
  2. protected属性,key是 \0*\0属性名;
  3. private属性, key是 \0类名\0属性名。

注意 \0 是一个字符(不是两个),对应的ASCII码是数字0。编程时要用双引号将其引起来。不能使用单引号,否则转义失效,那就是两个字符。如果你有C语言基础,应该知道 \0 就是字符串的结束符。这个符号直接输出不会显示,但可以通过strlen或者ord让其现形:

foreach ($attrs as $key => $value) {
 echo "key:$key", ", key length:", strlen($key), ", ascii: ";
 for ($i = 0; $i < strlen($key); ++ $i) {
 echo ord($key[$i]), " ";
 }
 echo PHP_EOL;
}
// 输出
// key:Foobar, key length:8, ascii: 0 70 111 111 0 98 97 114
// Foobar 有6个字符,加上两个不显示字符,所以长度是8

还需要注意拼接private属性时类名应该是 “完全限定类名” ,建议通过Foo::class的方式获取。

与强制转换成数组类似的另一种方法是serialize,但是serialize比较慢,并且序列化后的字符串更难辨认结构和处理,不建议使用。

3. 闭包

文章开头的推特截图已经展示了闭包的用法,其中call方法在PHP7中引入,另一个是PHP5.4引入的bindTocallbindTo的用法示例如下:

namespace tlanyan;

class Foo {
 private $bar = "Foo bar!";
}

$foo = new Foo;
// 闭包(匿名函数)是PHP5.3引入的功能
$closure = function() { return $this->bar; };
// PHP5.4起支持bindTo方法
$method = $closure->bindTo($foo, Foo::class);
echo $method(), PHP_EOL;

// PHP7引入call方法,可绑定this直接执行
echo $closure->call($foo), PHP_EOL;

bindTo方法的第二个参数注意传入对象的 “完全限定类名”,指示函数应该放置在该类的作用域下,从而可以访问私有属性。

总结

性能: 数组 > 反射 > 闭包

易用性: 闭包 > 数组 > 反射

推荐: 闭包 > 反射 > 数组

以上就是PHP获取类私有属性的3种方法的详细内容,更多关于PHP获取类私有属性的资料请关注我们其它相关文章!

时间: 2020-09-10

PHPUnit测试私有属性和方法功能示例

本文实例讲述了PHPUnit测试私有属性和方法功能.分享给大家供大家参考,具体如下: 一.测试类中的私有方法: class Sample { private $a = 0; private function run() { echo $a; } } 上面只是简单的写了一个类包含,一个私有变量和一个私有方法.对于protected和private方法,由于无法像是用public方法一样直接调用,所以在使用phpunit进行单测的时候,多有不便,特别是当一个类中,对外只提供少量接口,内部使用了大量p

PHP实现在对象之外访问其私有属性private及保护属性protected的方法

本文实例讲述了PHP实现在对象之外访问其私有属性private及保护属性protected的方法.分享给大家供大家参考,具体如下: public 表示全局的访问权限,类内部外部子类都可以访问: private表示私有的访问权限,只有本类内部可以使用: protected表示受保护的访问权限,只有本类或子类或父类中可以访问: 比较经典的用法示例如下: <?php //父类 class father{ public function a(){ echo "function a<br/&g

Server.CreateObject的调用失败拒绝对此对象的访问的解决方法

今天把一个网站的数据从win2000服务器转移到了win2003服务器上,然后调试,发现在用aspjpeg组件上传图片的时候,提示出错: 其实很多时候是因为你安装的插件权限设置不当造成的.应该坚持你最近安装了什么组件. 最终找到了解决"检查权限时,对Server.CreateObject的调用失败,拒绝对此对象的访问"的方法: windows server 2003默认的安全级别太高了,找到你组件所在的安装目录,右键/属性/安全,添加everyone(我用的是IUSR_WD)的可执行权

讲解Java中如何构造内部类对象以及访问对象

通过反射构造内部类对象 首先在 javalang 包下写一个包含内部类的类: package javalang; public class Outer { public static class Inner1{} } 注意这个类是 public static,后面我们慢慢把这些修饰符去掉. 要想通过反射来创建 Inner1 对象,首先要获得 Inner1 的 Class 对象.我们在 Outer 中写上 main 方法: public class Outer { public static cl

Javascript类定义语法,私有成员、受保护成员、静态成员等介绍

其实通俗的讲类就是对象的模板,为了增强JS的OO特性,受mootoos框架启发我们可以使用一个JSON对象来描述这个对象的模板.在这个模板中我们可以模拟实现私有成员,受保护成员,静态成员. 这是一个在JS中模拟的类定义语法,代码中Class是一个自定义函数,它接受两个参数,第一个参数是类名.第二个参数是一个JSON用来一个对象的模板.在这个JSON对象中其中字段 "extend",,"initialize","static" 为一些预定义关键字,

遍历json 对象的属性并且动态添加属性的实现

昨天因为公司的一个需求,所以就研究了一下json对象的属性的遍历和动态修改: var person= { name: 'zhangsan', pass: '123' , 'sni.ni' : 'sss', hello:function (){ for(var i=0;i<arguments.length;i++){ //在不知参数个数情况下可通过for循环遍历 // arguments这个是js 默认提供 alert("arr["+i+"]="+argumen

js对象内部访问this修饰的成员函数示例

用wrapper封装这样在对象内外都可以访问 复制代码 代码如下: function MapPool(){ function createMarker(name, lat, lng, state){  var marker = new AMap.Marker({   position : new AMap.LngLat(lng, lat),        });  //the function mapMoveTo is not accessible here too        AMap.ev

js 对象外部访问或者调用问题

以下是我的代码: <script> abc = function(){ this.a; this.b; } abc.prototype = { getData:function(){ var c = function(num){ alert(num); this.b = num; } c('12345'); }, clearData:function(){ this.getData(); alert(this.b); } } var d = new abc(); d.clearData();

MS-sql 2005拒绝了对对象 'xxx' (数据库 'xxx',架构 'dbo')的 SELECT 权限的解决方法

问题:Sql server 2005 默认设置下不允许远程登陆 1 外围设置 tcp/ip 和 named pipe 同时启用 2 服务器 --〉属性 --〉安全性 --〉sql server 和windows 认证模式 问题: 拒绝了对对象 'xxx' (数据库 'xxx',架构 'dbo')的 SELECT 权限. 答案: 数据库(xxx) --->安全性---->架构---->dbo(属性)--->权限--->添加--->浏览--> [public]----

javascript 对象属性property与元素属性attribute的浏览器支持

var div = document.getElementById('myId'); div.userProperty = 'test2'; alert(div.attributes.length); // IE6/7/8 -> 4 , [id,class,userAttribute,userProperty] // IE9/FF -> 3, [id,class,userAttribute] alert(div.userAttribute); // IE6/7/8 -> 'test1'

php采用curl访问域名返回405 method not allowed提示的解决方法

/** * http测试 * 注:PHP版本5.2以上才支持CURL_IPRESOLVE_V4 * @param $url 网站域名 * @param $type 网站访问协议 * @param $ipresolve 解析方式 */ public function web_http($url,$type,$ipresolve) { //设置Header头 $header[] = "Accept: application/json"; $header[] = "Accept-E