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

serde_php

对PHP序列化格式的serde支持

7个不稳定版本 (3个重大更改)

0.5.0 2020年10月7日
0.4.1 2020年2月17日
0.3.0 2019年11月4日
0.2.2 2019年10月29日

#1728 in 编码

Download history 26/week @ 2024-03-31 1/week @ 2024-04-07 5/week @ 2024-04-14 35/week @ 2024-04-21 1/week @ 2024-04-28 5/week @ 2024-05-12 1/week @ 2024-05-19 6/week @ 2024-05-26 31/week @ 2024-06-02 79/week @ 2024-06-09 20/week @ 2024-06-16 27/week @ 2024-06-23 40/week @ 2024-06-30 25/week @ 2024-07-07

每月115次下载

MIT/Apache

56KB
1.5K SLoC

PHP序列化对serde的支持

允许将数据序列化和反序列化到PHP的 serialize()/unserialize() 格式。请参阅文档以获取详细信息: docs.rs/serde_php.


lib.rs:

PHP序列化格式对serde的支持

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)
    null 解码为 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 serde_php::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 serde_php::{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);

依赖项

~2MB
~42K SLoC