json_encode()
如何转化一个对象?
使用json_encode()
将数组array
转化成json
字符串我们都已经很熟悉了那么使用
json_encode()
转化一个对象是什么样的过程呢?
初步测试
我们需要新建一个具有多种属性的对象
新建 JsonTest
1 | class JsonTest |
执行
打印结果
1 | echo json_encode(new JsonTest()); |
输出
1 | { "a": "a" } |
可以看得出,只有公开非静态的属性被打印出来了,其他东西(常量、私有变量、方法等等)都丢失了。
思考
在实际的应用中,多数情况下,我们的属性都是非公开的,但是我们又想在执行 json_encode()
的时候将它打印出来,该怎么办呢?
JsonSerializable
JsonSerializable 是一个 PHP 的 JSON 序列化接口
官方定义
JSON 序列化接口
(PHP 5 >= 5.4.0, PHP 7)
简介
实现 JsonSerializable 的类可以 在 json_encode() 时定制他们的 JSON 表示法。
接口摘要
1 | JsonSerializable { |
可以看出 php
版本低于 5.4
是没有这个接口的
修改 JsonTest
继续测试
修改
JsonTest
让它实现JsonSerializable
,并为其写一个jsonSerialize
方法
1 | class JsonTest implements JsonSerializable |
执行
打印结果
1 | echo json_encode(new JsonTest()); |
输出
1 | { "a": "a", "e": "e", "d": "d" } |
可以看得出,公开属性和私有属性都被打印出来了,方法,常量以及静态变量没有打印出来(这是因为类(class
)中静态变量和常量的实现方式是所有对象共享的,并不具体属于某个类)
源码分析
这部分源码较多,我会按照源码中的
function
来一个一个进行分析,注意看代码块中的注释里边对应有一些
option
的位运算,我先贴出来每个option
常量对应的值,<<
是左移
1 | /* json_encode() options */ |
函数本身
1 | PHP_JSON_API int php_json_encode(smart_str *buf, zval *val, int options) /* {{{ */ |
可以看出真正的编码函数是 php_json_encode_zval()
php_json_encode_zval()
smart_str_appendl()
是一个拼接字符串的函数,第三个参数是字符串的长度
buf
就是最终要返回的json
字符串
1 | int php_json_encode_zval(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */ |
php_json_encode_array()
这个函数递归编码数组及没有实现
JsonSerializable()
的对象(只编码对象的公开属性)
1 | static int php_json_encode_array(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */ |
分析
看了源码,我得出了一些结论。
- 只有 null,布尔值,浮点数,整数,字符串才会被直接编码
- 对象要么实现
JsonSerializable
并定义一个jsonSerialize()
,要么就被当成一个数组,只会被处理公开非静态属性 json_encode()
并不会直接编码数组和对象,而是会递归遍历出所有可遍历的元素,并处理 key- 源码中
php_json_encode_zval()
和php_json_encode_array()
的相互调用,实现了数组和对象遍历的闭环 - 引用不会被编码
另外,关于 json_encode()
的 options
,我觉得这里处理的技巧非常有趣,巧妙利用位运算来区别多个常量,有兴趣的慢慢看看研究研究。(提示,将 options
每个常量转成二进制来看,json_encode()
接受多个 option
是按位或 |
)
Demo
1 | >>> $a = [1,2,3,4]; |