andreamk / jsonserialize
通过 JSON 改进对象序列化和反序列化的库
Requires
- php: >=5.4
Requires (Dev)
- composer/composer: >=1.5.6 <1.7.0 || ^1.7.1 || ^2.0.0
- mcaskill/composer-exclude-files: ^2.1
- phpstan/extension-installer: *
- phpstan/phpstan: *
- phpstan/phpstan-phpunit: *
- phpunit/phpunit: ^4 || ^6 || ^9
- squizlabs/php_codesniffer: ^3
- dev-main
- v1.30
- v1.2.9
- v1.2.8
- v1.2.7
- v1.2.6
- v1.2.5
- v1.2.4
- v1.2.3
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.0
- v1.0.0
- dev-20240315_update_for_php_-.3
- dev-20221128_AL_fix_json_sierialized_invalid_problems
- dev-20220803_AL_add_skip_magic_methods-to-unserialize
- dev-20220802_AL_add_mapping_fixed_val
- dev-20220726_AL_skip_magic_methods_option
- dev-20220725_AL_autoloader-improved
- dev-20220612_AL_JS_php_stan
- dev-20220713_AL_improve_unserilizeToObj
- dev-20220708_AL_sanitize_invalid_string
- dev-20220707_AL_fix_mapped_value_if_in_json_data_don't-exists
- dev-20220707_AL_fix_readm.md
- dev-20220706_AL_unserialize-mapping-added
- dev-20220517_AL_new_feathures
- dev-20211010_AL_add_multi_php_tests
- dev-20211009_AL_write_docs
- dev-20210926_AL_update_phpunit_tests
- dev-20210926_AL_fix_codesniffer
This package is auto-updated.
Last update: 2024-09-16 03:31:26 UTC
README
此库结合了原生 PHP 序列化的功能和 JSON 的可移植性,特别是它允许使用 JSON 对象的 受保护和私有属性 进行编码。当在类中定义时,魔法方法 __sleep , __serialize, __wakeup 和 __unserialize 与序列化时使用的方式相同。
使用此库序列化和反序列化的值将保留其类型,因此数组、关联数组和对象将保留其类型和类。
要求
PHP 5.4+
安装
通过 Composer
composer require andreamk/jsonserialize
可以通过 composer 自动加载器或库自动加载器包含此库
require_once PATH . '/jsonserialize/src/Autoloader.php'; \Amk\JsonSerialize\Autoloader::register();
基本用法
use Amk\JsonSerialize\JsonSerialize; $json = JsonSerialize::serialize($value); $value = JsonSerialize::unserialize($json);
public static JsonSerialize::serialize(mixed $value, int $flags = 0, int $depth = 512): string|false
serialize 函数将任何值转换为 JSON,就像 json_encode 函数一样,不同之处在于 JSON 中将包含对象的私有和受保护属性以及对应类的引用。
public static JsonSerialize::unserialize(string $json, int $depth = 512, int $flags = 0): mixed
接受一个 JSON 编码的字符串并将其转换为 PHP 变量,就像 json_decode。如果引用对象的类未定义,则对象将作为 stdClass 实例化,所有属性变为公共。
注意:当对象反序列化时,它将以不调用构造函数的方式实例化,就像 PHP 的 unserialize 函数一样。如果正在反序列化的变量是一个对象,在成功重建对象后,PHP 将自动尝试调用 __wakeup() 方法(如果存在)。
高级用法
方法 __sleep
public __sleep(): array
Amk/JsonSerialize 序列化函数检查类是否有名为 __sleep() 的魔法函数。如果有,则在该序列化之前执行该函数。它可以清理对象,并应返回一个包含该对象所有应序列化的变量名称的数组。如果没有返回数组,则抛出异常。
__sleep() 的预期用途是提交挂起数据或执行类似的清理任务。此外,如果非常大的对象不需要完全保存,此函数也很有用。
方法 __wakeup
public __wakeup(): void
Amk/JsonSerialize 反序列化函数检查是否存在名为 __wakeup() 的魔法函数。如果存在,此函数可以重建对象可能拥有的任何资源。
__wakeup() 的预期用途是在序列化过程中可能丢失的数据库连接重新建立,并执行其他重新初始化任务。
方法 __serialize
public __serialize(): array
Amk/JsonSerialize 序列化函数检查类是否有名为 __serialize() 的魔法函数。如果有,则在该序列化之前执行该函数。它必须构建并返回一个表示对象序列化形式的关联数组。如果没有返回数组,则抛出异常。
注意:如果在同一对象中同时定义了 __serialize() 和 __sleep() 方法,则只有 __serialize() 方法会被调用。__sleep() 方法将被忽略。
方法 __unserialize
public __unserialize(array $data): void
Amk/JsonSerialize 的 __unserialize() 方法检查是否存在具有魔法名称 __unserialize() 的函数。如果存在,此函数将传递从 __serialize() 返回的恢复后的数组。然后,它可以从该数组恢复对象的属性,具体取决于适当的操作。
注意:如果在同一对象中同时定义了 __unserialize() 和 __wakeup() 方法,则只有 __unserialize() 方法会被调用。__wakeup() 方法将被忽略。
抽象 JsonSerializable 类
class MyClass extends \Amk\JsonSerialize\AbstractJsonSerializable { } $obj = new MyClass(); $json = json_encode($obj);
通过扩展实现 JsonSerializable 接口的 AbstractJsonSerializable 类,可以使用 PHP 的正常 json_encode 函数,为扩展此类的对象获得使用 JsonSerialize::serialize 获得相同的结果。
标志
- JSON_SKIP_CLASS_NAME
在某些情况下,在 JSON 序列化对象时可以不暴露类。例如,如果我们想通过 AJAX 调用将对象的内 容发送到浏览器。在这些情况下,我们可以使用 JSON_SKIP_CLASS_NAME 标志,除了 json_encode 函数的正常标志外。
use Amk\JsonSerialize\JsonSerialize; $json = JsonSerialize::serialize( $value, JSON_PRETTY_PRINT | JsonSerialize::JSON_SKIP_CLASS_NAME );
- JSON_SKIP_MAGIC_METHODS
当此标志开启时,类的 __sleep 和 __serialize 方法将被忽略。
- JSON_SKIP_SANITIZE
默认情况下,如果 json_encode 由于无效字符而失败,则应用字符串清理。激活此标志将关闭清理。
方法 serializeToData
public static serializeToData( mixed $value, $flags = 0 ): mixed
在某些情况下,在将数据结构转换为 JSON 之前处理数据结构是有用的。使用 serializeToData 方法,您将获得传递给具有 serialize 方法的 json_encode 函数的值。
$data = JsonSerialize::serializeToData($obj, JsonSerialize::JSON_SKIP_CLASS_NAME); $data['extraProp'] = true; unset($data['prop']); $json = json_encode($data);
方法 unserializeToObj
public static JsonSerialize::unserializeToObj( string $json, object|string $obj, int $depth = 512, int $flags = 0 ) : object
在某些情况下,在已实例化的对象中反序列化 JSON 数据可能很有用。例如,如果我们正在处理带有 JSON_SKIP_CLASS_NAME 标志的序列化 JSON。
在这种情况下,我们没有关于引用类的信息,因此使用正常的反序列化函数,结果将是一个关联数组。使用 unserializeToObj 方法,我们强制使用参数传递的对象。
$obj = new MyClass(); $json = JsonSerialize::serialize($obj , JsonSerialize::JSON_SKIP_CLASS_NAME); $obj2 = new MyClass(); JsonSerialize::unserializeToObj($json, $obj2);
方法 unserializeWithMap
public static unserializeWithMap( string $json, JsonUnserializeMap $map, $depth = 512, $flags = 0 ): mixed
映射反序列化是一种非常强大的方法,可以将属性映射到更改反序列化中的类型。经典的用途是强制将通常为关联数组的对象进行对象反序列化。除了 nuti PHP 原生类型外,还有一些特殊类型,其中可以指定对象的类,以及为具有递归引用的对象引用另一个属性。
$map = new JsonUnserializeMap( [ '' => 'object'; 'prop1/prop11' => 'bool', 'prop2' => 'cl:MyClass' ] ); $val = JsonSerialize::unserializeWithMap($json, $map);
有关更多信息,请参阅映射部分。
反序列化映射
映射通过 JsonUnserializeMap
类定义。可以通过向构造函数传递项目数组来初始化它,并且可以使用 addProp
、removeProp
和 resetMap
方法在以后操作项目列表。
映射对象由键(属性标识符)值(属性类型)对组成。请注意,映射不需要定义结构中所有属性的定义,但只需需要强制转换为特定类型的属性
$map = new JsonUnserializeMap( [ 'prop' => 'object', 'prop/flag1' => 'int', 'prop/flag2' => 'bool' ] );
属性标识符
- 空标识符对应于根元素
- 属性级别由字符
/
分隔 - 字符
*
是通配符,如果您想映射数组中的所有元素,则很有用
一些示例
[ '' => type, // itendifies the root element 'prop' => type, // identifies the level 1 property ($val->prop or $val['prop']) 'v1/v2/v3' => type, // identifies the level 3 property ($val->v1->v2->v3 or $val['v1']['v2']['v3']) 'v1/*' => type, // identifies all the properties that are children of v1, 'v1/*/v3' => type, // identifies all v3 properties of the children of v1 ]
- 通配符属性但优先级低于特定属性,因此您可以定义所有属性的类型,但除少数属性外。在下一个示例中,元素的所有子属性都将为整数,而 flag 将为布尔值
[ 'element/*' => 'int', 'element/flag' => 'bool', ]
属性类型
- 类型是一个字符串,可以是 PHP 原生类型
bool
、boolean
、float
、int
、integer
、string
、array
、object
或null
。 - 如果类型以字符
?
开头,则它可以设置为可空的。这意味着如果 JSON 值为 null,则保持 null,否则取类型的值。 - 使用特殊类型
cl:
后跟类标识符,定义类型是该类定义的对象。 - 使用特殊类型
rf:
后跟属性标识符(不带通配符),定义了属性引用。这仅在属性已定义且是对象时才有意义。
$map = new JsonUnserializeMap( [ '' => 'cl:' . MyClass::class, // root val is an istance of MyClass 'items/*' => '?cl:' . MyItems::class, // all element of items are istances of MyItems or null 'items/*/parent' => 'rf:', // all prop parent of all items ar a reference to root value 'obj' => '?object' // obj can be null or an object ] );
注意:在定义类类型 cl:
时,如果存在,也会执行 __wakeup 和 unserialize 方法。在定义引用 rf:
时,JSON 中此对象的子值将被忽略,并且由于类已经初始化,所以不会执行 __wakeup 或 __unserialie 方法。
工作原理
serialize 和 unserialize 方法在标准 JSON 上工作,可以被任何编写/读取 json 文件的函数读取。特别是如果值是标量值或数组,使用标准函数 json_encode 和 json_decode 不会有区别。
如果您使用这些函数与对象一起使用,除了公共和私有属性外,还会添加数据所属的类。
此代码
namespace Test; class MyClass { public $publicProp = 'public'; protected $protectedProp = 'protected'; private $privateProp = 'private'; private $arrayProp = [1,2,3]; } $object = new MyClass(); $json = JsonSerialize::serialize($object, JSON_PRETTY_PRINT);
将生成此 JSON
{ "CL_-=_-=": "Tests\\MyClass", "publicProp": "public", "protectedProp": "protected", "privateProp": "private", "arrayProp": [ 1, 2, 3 ] }
在反序列化 JSON 时,如果数据是数组类型,并且存在 AbstractJsonSerializeObjData::CLASS_KEY_FOR_JSON_SERIALIZE (CL_-=_-=) 键,则此数组将转换为对象。
如果类存在,则实例化的对象将属于定义的类;否则,它将是一个 stdClass 对象。
如果对象是 stdClass 类型,则数组的所有项都将成为公共属性;否则,将使用数组的值更新类中定义且存在于数组中的每个属性。
如果您正在处理随着时间的推移而更改的序列化类的项目,了解此功能的不同之处非常重要。
假设我们有一个 WordPress 插件,其中有一个 Settings 类来描述插件设置。随着时间的推移,该类的属性可能被添加或删除,这是很常见的。在这种情况下,我们可能有序列化属性不存在于类中,以及存在于类中但尚未序列化的属性。
反序列化通过丢弃 JSON 中未定义在类中的所有属性,并保留定义在类中但不存在于 JSON 中的属性的默认值来处理此问题,除非它是 stdClass 类,其中所有 JSON 值都会被分配。
因此,在这种情况下,我们拥有此 JSON
{ "CL_-=_-=": "MyClass", "propA": "value A", "propB": "value B" }
以及以下方式定义的类
class MyClass { public $propA = 'init A'; public $propC = 'init C'; } $obj = JsonSerialize::unserialize($json); var_dump($obj);
结果将是
object(MyClass)#1 (2) {
["propA"]=>
string(7) "value A"
["propC"]=>
string(6) "init C"
}