#php #array #serde #key #hash-map #string #integer

php_serde

对PHP序列化格式的Serde支持

1个不稳定版本

0.6.0 2021年8月20日

#1382 in 编码

Download history 2729/week @ 2024-04-07 2580/week @ 2024-04-14 2791/week @ 2024-04-21 3275/week @ 2024-04-28 1877/week @ 2024-05-05 1527/week @ 2024-05-12 1862/week @ 2024-05-19 1556/week @ 2024-05-26 1465/week @ 2024-06-02 1830/week @ 2024-06-09 2255/week @ 2024-06-16 1707/week @ 2024-06-23 1755/week @ 2024-06-30 1636/week @ 2024-07-07 1862/week @ 2024-07-14 2272/week @ 2024-07-21

7,578 每月下载量

MIT/Apache

63KB
1.5K SLoC

为serde提供PHP序列化支持

允许对PHP的 serialize()/unserialize() 格式进行序列化和反序列化。有关详细信息,请参阅文档:docs.rs/php_serde.


lib.rs:

为serde提供PHP序列化格式支持

PHP通过其 serialize()unserialize() 方法使用自定义序列化格式。这个crate使用 serde 添加了对该格式的部分支持。

该格式的概述可以在 https://stackoverflow.com/questions/14297926/structure-of-a-serialized-php-string 中看到,详细信息请见 http://www.phpinternalsbook.com/php5/classes_objects/serialization.html

支持什么?

  • 基本和复合类型

    PHP类型 Rust类型
    布尔值 bool
    整数 i64 (自动转换为其他类型支持)
    浮点数 f64 (自动转换为 f32 支持)
    字符串 Vec<u8> (PHP字符串不是UTF8)
    空值 解码为 None
    数组(非关联数组) 元组结构体或 Vec<_>
    数组(关联数组) 常规 结构体HashMap<_, _>
  • Rust String 会自动转换为 PHP 字节数组。

无序数组

PHP 数组可以“无序”创建,因为它们将每个数组索引存储为显式的整数。因此以下代码

$arr = array();
$arr[0] = "zero";
$arr[3] = "three";
$arr[2] = "two";
$arr[1] = "one";

会生成一个等价于 ["zero", "one", "two", "three"] 的数组,至少在迭代时是这样。

因为反序列化不缓冲值,所以这些数组不能直接序列化为 Vec。相反,它们应该被反序列化为映射,然后可以(如果需要的话)将其转换为 Vec

第二个问题是数组中的“空隙”,例如如果缺少键 1 的条目。如何填充这些通常取决于用户。

辅助函数 deserialize_unordered_array 可以与 serde 的 deserialize_with 装饰器一起使用,以自动缓冲和排序,同时通过关闭任何间隙来填充空隙。

缺少什么?

  • PHP 对象
  • 非字符串/数字数组键,除非在反序列化为 HashMap
  • 混合数组。假设数组键始终具有相同的键类型(注意:如果需要,请考虑通过扩展此库来使用变体类型)。

示例使用

给定一个示例数据结构,使用以下 PHP 代码存储会话令牌

<?php
$serialized = serialize(array("user", "", array()));
echo($serialized);

因此以下输出

a:3:{i:0;s:4:"user";i:1;s:0:"";i:2;a:0:{}}

,可以使用以下 rust 代码重新构造数据

use serde::Deserialize;
use php_serde::from_bytes;

#[derive(Debug, Deserialize, Eq, PartialEq)]
struct Data(Vec<u8>, Vec<u8>, SubData);

#[derive(Debug, Deserialize, Eq, PartialEq)]
struct SubData();

let input = br#"a:3:{i:0;s:4:"user";i:1;s:0:"";i:2;a:0:{}}"#;
assert_eq!(
    from_bytes::<Data>(input).unwrap(),
    Data(b"user".to_vec(), b"".to_vec(), SubData())
);

同样,如果 PHP 数组使用键,则也支持结构体

<?php
$serialized = serialize(
    array("foo" => true,
          "bar" => "xyz",
          "sub" => array("x" => 42))
);
echo($serialized);

在 Rust 中

#[derive(Debug, Deserialize, Eq, PartialEq)]
struct Outer {
    foo: bool,
    bar: String,
    sub: Inner,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
struct Inner {
    x: i64,
}

let input = br#"a:3:{s:3:"foo";b:1;s:3:"bar";s:3:"xyz";s:3:"sub";a:1:{s:1:"x";i:42;}}"#;
let expected = Outer {
    foo: true,
    bar: "xyz".to_owned(),
    sub: Inner { x: 42 },
};

let deserialized: Outer = from_bytes(input).expect("deserialization failed");

assert_eq!(deserialized, expected);

可选值

缺失的值可以设置为可选,如下例所示

<?php
$location_a = array();
$location_b = array("province" => "Newfoundland and Labrador, CA");
$location_c = array("postalcode" => "90002",
                    "country" => "United States of America");
echo(serialize($location_a) . "\n");
echo(serialize($location_b) . "\n");
# -> a:1:{s:8:"province";s:29:"Newfoundland and Labrador, CA";}
echo(serialize($location_c) . "\n");
# -> a:2:{s:10:"postalcode";s:5:"90002";s:7:"country";
#         s:24:"United States of America";}

以下对 Location 的声明将能够解析所有三个示例输入。

#[derive(Debug, Deserialize, Eq, PartialEq)]
struct Location {
    province: Option<String>,
    postalcode: Option<String>,
    country: Option<String>,
}

完整往返示例

use serde::{Deserialize, Serialize};
use php_serde::{to_vec, from_bytes};

#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
struct UserProfile {
    id: u32,
    name: String,
    tags: Vec<String>,
}

let orig = UserProfile {
    id: 42,
    name: "Bob".to_owned(),
    tags: vec!["foo".to_owned(), "bar".to_owned()],
};

let serialized = to_vec(&orig).expect("serialization failed");
let expected = br#"a:3:{s:2:"id";i:42;s:4:"name";s:3:"Bob";s:4:"tags";a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}}"#;
assert_eq!(serialized, &expected[..]);

let profile: UserProfile = from_bytes(&serialized).expect("deserialization failed");
assert_eq!(profile, orig);

依赖关系

~200–490KB
~11K SLoC