unserialize

(PHP 4、PHP 5、PHP 7、PHP 8)

unserialize从存储的表示创建 PHP 值

描述

unserialize(string $data, array $options = []): mixed

unserialize() 接受一个序列化后的变量,并将其转换回 PHP 值。

警告

不要将不可信的用户输入传递给 unserialize(),无论 options 值的 allowed_classes 是什么。反序列化可能导致代码被加载和执行,因为对象实例化和自动加载,恶意用户可能利用这一点。如果您需要将序列化数据传递给用户,请使用安全的标准数据交换格式,如 JSON(通过 json_decode()json_encode())。

如果您需要反序列化外部存储的序列化数据,请考虑使用 hash_hmac() 进行数据验证。确保数据未被除您以外的任何人修改。

参数

data

序列化字符串。

如果反序列化的变量是一个对象,在成功重建对象后,PHP 将自动尝试调用 __unserialize()__wakeup() 方法(如果存在)。

注意: unserialize_callback_func 指令

可以设置一个回调函数,如果在反序列化过程中应该实例化一个未定义的类,则会调用该函数。(以防止得到一个不完整的 object __PHP_Incomplete_Class。)使用您的 php.iniini_set().htaccess 定义 unserialize_callback_func。每次应该实例化一个未定义的类时,它都会被调用。要禁用此功能,只需清空此设置。

options

作为关联数组传递给 unserialize() 的任何选项。

有效选项
名称 类型 描述
allowed_classes 混合 要接受的类名数组、false 表示不接受任何类,或 true 表示接受所有类。如果定义了此选项,并且 unserialize() 遇到一个不应被接受的类的对象,那么该对象将被实例化为 __PHP_Incomplete_Class 省略此选项与将其定义为 true 相同:PHP 将尝试实例化任何类的对象。
max_depth int 反序列化期间允许的结构的最大深度,旨在防止堆栈溢出。默认深度限制为 4096,可以通过将 max_depth 设置为 0 来禁用。

返回值

返回转换后的值,可以是 boolintfloatstringarrayobject

如果传递的字符串不可反序列化,则返回 false 并发出 E_WARNING

错误/异常

对象可能在其反序列化处理程序中抛出 Throwable

变更日志

版本 描述
8.3.0 现在在传递的字符串不可反序列化时发出 E_WARNING;以前发出 E_NOTICE
7.4.0 添加了 max_depth 元素(在 options 中)以设置反序列化期间允许的结构的最大深度。
7.1.0 allowed_classes 元素(在 options 中)现在是严格类型化的,即,如果传递的不是 arraybool,则 unserialize() 返回 false 并发出 E_WARNING

示例

示例 #1 unserialize() 示例

<?php
// 在这里,我们使用 unserialize() 将从数据库中选定的字符串加载会话数据到 $session_data 数组中。

示例 #2 unserialize_callback_func 示例

<?php
$serialized_object
='O:1:"a":1:{s:5:"value";s:3:"100";}';

ini_set('unserialize_callback_func', 'mycallback'); // 设置您的回调函数

注释

警告

false 在出现错误的情况下以及在反序列化序列化后的 false 值时都会返回。可以通过将 dataserialize(false) 进行比较或捕获发出的 E_NOTICE 来捕获此特殊情况。

另请参阅

添加注释

用户贡献注释 24 个注释

me+phpnet at unreal4u dot com
6 年前
关于 `$options` 数组,只是一些可能节省别人时间的小提醒。

假设您想安全起见,不允许反序列化任何对象... 我的第一个想法是执行以下操作:

<?php
$lol
= unserialize($string, false);
// 这将生成:
// Warning: unserialize() expects parameter 2 to be array, boolean given
?>

执行此操作的正确方法如下所示:
<?php
$lol
= unserialize($string, ['allowed_classes' => false]);
?>

希望对某些人有帮助!
karsten at dambekalns dot de
3 年前
请记住,`allowed_classes` 不使用继承,即不允许接口,子类也不会通过检查。请参见 https://3v4l.org/tdHfl
ErnestV
11 年前
只是一点说明 - 如果序列化字符串包含对无法实例化的类的引用(例如,是抽象类),PHP 将立即出现致命错误并停止。如果 `unserialize()` 语句前面有 `@` 以避免日志中出现警告或提示,则将完全无法了解脚本停止工作的原因。这让我浪费了几小时...
daniel at fourstaples dot com
14 年前
这是一个简单的函数,用于获取序列化字符串的类(即,如果反序列化,将返回的对象类型)。

<?php
function get_serial_class($serial) {
$types = array('s' => 'string', 'a' => 'array', 'b' => 'bool', 'i' => 'int', 'd' => 'float', 'N;' => 'NULL');

$parts = explode(':', $serial, 4);
return isset(
$types[$parts[0]]) ? $types[$parts[0]] : trim($parts[2], '"');
}
?>

当我将序列化对象保存到 cookie 中时,我会使用此函数,以确保在反序列化时它具有正确的类型。

类型名称与您使用 `var_dump()` 时看到的格式/大小写相同。
bjd
7 年前
关于利用 PHP7 反序列化的讨论:https://media.ccc.de/v/33c3-7858-exploiting_php7_unserialize
hadley8899 at gmail dot com
4 年前
对于遇到以下错误的人:

PHP Notice: unserialize(): Error at offset 191 of 285 bytes in ...

并且从数据库中获取数据,请确保数据库设置了正确的编码,我的数据库设置为 `latin1_swedish_ci`,所有数据看起来都很好,事实上,当我将其复制到在线反序列化器中时,它工作正常。我将排序规则更改为 `utf8mb4_unicode_ci`,一切都正常工作了。
chris at pollett dot org
9 年前
当您序列化来自特定命名空间的类的对象时,命名空间将被记录为序列化的一部分。如果您决定更改此命名空间的名称,则可能难以读取旧的序列化对象。例如,假设您序列化了一个类型为 `foo\A` 的对象,您将项目的命名空间更改为 `goo`,但其他方面保持 `A` 的类定义不变。您希望能够将该对象反序列化为 `goo\A`,但反序列化只会创建一个部分对象。要解决此问题,如果您在类定义中没有嵌套对象,可以使用以下简单的重命名函数
/**
* 用于更改序列化 PHP 对象的命名空间(假设没有
* 嵌套子对象)
*
* @param string $class_name 新的带命名空间的完全限定名称
* @param string $object_string 序列化对象
*
* @return string 具有新名称的序列化对象
*/
function renameSerializedObject($class_name, $object_string)
{
/* 对象名称长度的位数需要
小于 12 位(可能更像 4 位)才能使此方法起作用。 */
*/
$name_length = intval(substr($object_string, 2, 14));
$name_space_info_length = strlen("O:".$name_length.":") +
$name_length + 2; // 2 用于引号;
$object_string = 'O:' .
strlen($class_name) . ':"'. $class_name.'"' .
substr($object_string, $name_space_info_length);
return $object_string;
}
Ray.Paseur often uses Gmail
11 年前
在类和对象文档中,有以下内容:为了能够反序列化() 对象,该对象的类需要被定义。

在 PHP 5.3 之前,这不是问题。但在 PHP 5.3 之后,由 `SimpleXML_Load_String()` 创建的对象无法被序列化。尝试这样做会导致运行时错误,抛出异常。如果您将此类对象存储在 `$_SESSION` 中,您将获得一个后执行错误,提示您

Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'SimpleXMLElement' is not allowed' in [no active file]:0 Stack trace: #0 {main} thrown in [no active file] on line 0

会话的全部内容将丢失。希望这能节省某些人一些时间!

<?php // RAY_temp_ser.php
error_reporting(E_ALL);
session_start();
var_dump($_SESSION);
$_SESSION['hello'] = 'World';
var_dump($_SESSION);

// 用于测试数据的 XML 字符串
$xml = '<?xml version="1.0"?>
<families>
<parent>
<child index="1" value="Category 1">Child One</child>
</parent>
</families>'
;

// 创建一个对象(返回 SimpleXMLElement)
$obj = SimpleXML_Load_String($xml);

// 将对象存储在会话中
$_SESSION['obj'] = $obj;
arbie samong
14 年前
`__PHP_Incomplete_Class` 对象解读

1. 首先注意输出。一个简单的示例

`__PHP_Incomplete_Class` Object (
`[__PHP_Incomplete_Class_Name]` => `SomeObject1`
`[obj1property1]` => `somevalue1` `[obj1property2]` => `__PHP_Incomplete_Class` Object ( `[__PHP_Incomplete_Class_Name]` => `SomeObject2` `[obj2property1]` => `somevalue1` `[obj2property2]` => Array (
`['key1']` => `somevalue3`, `['key2']` => `somevalue4` ) ) )

2. 我们分析并分解它。
`__PHP_Incomplete_Class` Object 告诉您有一个需要以某种方式声明的对象。
`__PHP_Incomplete_Class_Name` 只是告诉您预期的类名。它目前只是一个属性。

所以我们有
a) 一个未知对象,其类名为 `SomeObject1`(第一个类)
b) 它有两个属性,即 `obj1property1` 和 `obj2property2`
c) `obj2property2` 本身是一个对象,其类名为 `SomeObject2`(第二个类)
d) `SomeObject2` 有两个属性,`obj2property1` 和 `obj2property2`
e) `obj2property2` 是一个包含两个元素的数组

3. 现在我们已经了解了结构,我们将根据它创建类定义。我们现在只创建属性,方法不是必需的,因为它们是最低限度的。

<?php
class SomeObject1 {
public
$obj1property1;
public
$obj1property2;
}
class
SomeObject2 {
public
$obj2property1;
public
$obj2property2;
}
?>

4. 让您的脚本能够访问它,它将解决 `__PHP_Incomplete_Class` 对象问题,就输出而言。现在您将拥有

`SomeObject1` ( `[obj1property1]` => `somevalue1` `[obj1property2]` => `SomeObject2` ( `[obj2property1]` => `somevalue1` `[obj2property2]` => Array ( `['key1']` => `somevalue3`, `['key2']` => `somevalue4` ) ) )

正如您所看到的,`__PHP_Incomplete_Class` Object 已消失,并被类名替换。`__PHP_Incomplete_Class_Name` 属性也被删除了。

5. 至于数组属性 `obj2property2`,我们可以直接访问它,并假设它是一个数组并遍历它

<?php

// 这将是 SomeObject1
$data = unserialize($serialized_data);

// 这将是 SomeObject2
$data2 = $data->obj1property2();

foreach(
$data2->obj2property2 as $key => $value):
print
$key.' : '. $value .'<br>';
endforeach;

?>

输出
key1 : somevalue3
key2 : somevalue4

就是这样。 您可以为给定属性在类声明中添加更多方法,前提是您以原始输出为数据类型的基础。
m.m.j.kronenburg
7 年前
您可以使用以下代码在 PHP 5.3 及更高版本中使用 PHP 7 的 unserialize 函数。这将添加 $option 参数。

<?php

namespace
{

/**
* PHP 7 的 unserialize 函数,适用于 PHP 5.3 及更高版本。
* 添加了 $option 参数(allowed_classes)。
* 有关更多详细信息,请参阅 php unserialize 手册。
**/
function php7_unserialize($str, $options = array())
{
if(
version_compare(PHP_VERSION, '7.0.0', '>='))
{ return
unserialize($str, $options); }

$allowed_classes = isset($options['allowed_classes']) ?
$options['allowed_classes'] : true;
if(
is_array($allowed_classes) || !$allowed_classes)
{
$str = preg_replace_callback(
'/(?=^|:)(O|C):\d+:"([^"]*)":(\d+):{/',
function(
$matches) use ($allowed_classes)
{
if(
is_array($allowed_classes) &&
in_array($matches[2], $allowed_classes))
{ return
$matches[0]; }
else
{
return
$matches[1].':22:"__PHP_Incomplete_Class":'.
(
$matches[3] + 1).
':{s:27:"__PHP_Incomplete_Class_Name";'.
serialize($matches[2]);
}
},
$str
);
}
unset(
$allowed_classes);
return
unserialize($str);
}

}
// namespace

namespace my_name_space
{
/**
* 在您的命名空间中使用新的 php7 unserialize,无需
* 将所有 unserialize(...) 函数调用重命名为
* php7_unserialize(...).
**/
function unserialize($str, $options = array())
{ return
php7_unserialize($str, $options); }
}

?>
chris AT cmbuckley DOT co DOT uk
16 年前
如注释中所述,unserialize 在发生错误时以及对于布尔值 false 返回 false。 以下是第一个解决方案,不使用错误处理

<?php
function isSerialized($str) {
return (
$str == serialize(false) || @unserialize($str) !== false);
}

var_dump(isSerialized('s:6:"foobar";')); // bool(true)
var_dump(isSerialized('foobar')); // bool(false)
var_dump(isSerialized('b:0;')); // bool(true)
?>
chris at colourlovers dot com
13 年前
任何在对存储有 SimpleXMLElement 对象的数据进行序列化或反序列化时遇到问题的人,请查看以下内容

这将遍历 $data,查找任何是 SimpleXMLElement 实例的子元素,并对它们运行 ->asXML(),将其转换为字符串并使它们可序列化。 其他数据将保持不变。

<?php
function exportNestedSimpleXML($data) {
if (
is_scalar($data) === false) {
foreach (
$data as $k => $v) {
if (
$v instanceof SimpleXMLElement) {
$v = str_replace("&#13;","\r",$v->asXML());
} else {
$v = exportNestedSimpleXML($v);
}

if (
is_array($data)) {
$data[$k] = $v;
} else if (
is_object($data)) {
$data->$k = $v;
}
}
}

return
$data;
}

$data = array (
"baz" => array (
"foo" => new stdClass(),
"int" => 123,
"str" => "asdf",
"bar" => new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>'),
)
);

var_dump($data);
/*array(1) {
["baz"]=>
array(4) {
["foo"]=>
object(stdClass)#3 (0) {
}
["int"]=>
int(123)
["str"]=>
string(4) "asdf"
["bar"]=>
object(SimpleXMLElement)#4 (1) {
[0]=>
string(3) "bar"
}
}
}*/

var_dump(exportNestedSimpleXML($data));
/*array(1) {
["baz"]=>
array(4) {
["foo"]=>
object(stdClass)#3 (0) {
}
["int"]=>
int(123)
["str"]=>
string(4) "asdf"
["bar"]=>
string(54) "<?xml version="1.0" encoding="UTF-8"?>
<foo>bar</foo>
"
}
}
*/
?>
BenBE at omorphia dot de
17 年前
在尝试序列化或反序列化递归数组或其他链接数据时,您可能会发现未记录的 R 数据类型非常有用。

如果您想要一个类似于以下代码生成的数组
<?
$a = array();
$a[0] =& $a;
?>
序列化后,您可以使用类似于此的字符串存储它
<?
$a = unserialize("a:1:{i:0;R:1;}");
?>

这两个源代码将使 $a 保存一个在索引 0 中自引用的数组。

R 的参数是创建的序列化字符串子变量的索引,从 1 开始。
double at dumpit dot com
17 年前
这个小函数将检查序列化字符串是否格式良好。

PHP < 6,因为我听说在这个 php-intern 函数中将进行更改。
也许可以轻松地对其进行编辑。

<?php

function wd_check_serialization( $string, &$errmsg )
{

$str = 's';
$array = 'a';
$integer = 'i';
$any = '[^}]*?';
$count = '\d+';
$content = '"(?:\\\";|.)*?";';
$open_tag = '\{';
$close_tag = '\}';
$parameter = "($str|$array|$integer|$any):($count)" . "(?:[:]($open_tag|$content)|[;])";
$preg = "/$parameter|($close_tag)/";
if( !
preg_match_all( $preg, $string, $matches ) )
{
$errmsg = '不是序列化字符串';
return
false;
}
$open_arrays = 0;
foreach(
$matches[1] AS $key => $value )
{
if( !empty(
$value ) && ( $value != $array xor $value != $str xor $value != $integer ) )
{
$errmsg = '未定义的数据类型';
return
false;
}
if(
$value == $array )
{
$open_arrays++;
if(
$matches[3][$key] != '{' )
{
$errmsg = '预期打开标签';
return
false;
}
}
if(
$value == '' )
{
if(
$matches[4][$key] != '}' )
{
$errmsg = '预期关闭标签';
return
false;
}
$open_arrays--;
}
if(
$value == $str )
{
$aVar = ltrim( $matches[3][$key], '"' );
$aVar = rtrim( $aVar, '";' );
if(
strlen( $aVar ) != $matches[2][$key] )
{
$errmsg = '字符串长度不匹配';
return
false;
}
}
if(
$value == $integer )
{
if( !empty(
$matches[3][$key] ) )
{
$errmsg = '意外数据';
return
false;
}
if( !
is_integer( (int)$matches[2][$key] ) )
{
$errmsg = '预期整数';
return
false;
}
}
}
if(
$open_arrays != 0 )
{
$errmsg = '数组设置错误';
return
false;
}
return
true;
}

?>
Are Pedersen
18 年前
请注意,如果在同时使用 32 位和 64 位服务器的服务器场中使用 serialize/unserialize,您可能会得到意外的结果。

例如:如果您在 64 位系统上序列化值为 2147483648 的整数,然后在 32 位系统上对其进行反序列化,您将得到 -2147483648 的值。这是因为 32 位上的整数不能超过 2147483647,所以它会回绕。
w dot laurencine at teknoa dot net
15 年前
在处理包含“\r”的字符串时,似乎长度没有被正确评估。以下内容解决了我的问题

<?php
// 从 $unserialized 字符串中删除 \r 字符
$unserialized = str_replace("\r","",$unserialized);

// 然后反序列化()
unserialize($unserialized);
?>
suman dot jis at gmail dot com
12 年前
我遇到了 unserialize() 错误,偏移量错误。

如果您遇到类似问题,请使用以下步骤

$auctionDetails = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $dataArr[$i]['auction_details'] );
$auctionDetails = unserialize($auctionDetails);
OscarZarrus
1 年前
对于那些正在寻找处理有争议的“FALSE”的有效解决方案的人来说,他们可以使用此函数,该函数在遇到不可反序列化的字符串时,会抛出异常,而不是“FALSE”。反之,它将返回反序列化的变量。
<?php
/**
* @param string $serializedString
* @param array $options
* @return mixed
* @throws Exception
*/
function UnSerialize(string $serializedString, array $options = []) {
$_unserialized = @unserialize($serializedString, $options);
if (
$serializedString === serialize(false) || $_unserialized !== false){
return
$_unserialized;
}
throw new
Exception("不可反序列化的字符串");

}

?>
Chris Hayes (chris at hypersites dot com)
19 年前
关于必须在使用 unserialize 之前包含对象定义的早期帖子的回复。有一个解决方法。

当对象被序列化时,字符串的第一个部分实际上是类的名称。当未知对象被反序列化时,这将被保留为一个属性。所以如果您再次序列化它,您将获得与您序列化原始对象时完全相同的字符串。基本上,为了切中要害...

如果您使用

$_SESSION['my_object'] = unserialize(serialize($_SESSION['my_object']))

那么您将获得正确类型的对象,即使会话最初将其加载为 stdClass 类型的对象。
aderyn at nowhere dot tld
20 年前
一个简短的说明
如果您将序列化对象存储在会话中,您必须在初始化(session_start())会话之前包含该类。
Anonymous
5 年前
如果 serialize() 是答案,那么您几乎肯定问错了问题。

JSON 广泛可用。它唯一没有做的事情,就是使序列化极其危险的事情。只需要一个狡猾的黑客将精心制作的有效负载传递给一个据称是“安全”的序列化调用,例如,数据库驱动程序就可以被恶意代码覆盖。

重新创建对象。通常。使用实际数据和源文件,而不是使用序列化。这样做是懒惰,几乎等同于恶意。
MBa
13 年前
检查字符串是否已序列化

$blSerialized=(@unserialize($sText)||$sText=='b:0;');
walf
12 年前
unserialize 的替代方法,它返回是否成功,并通过引用填充反序列化的变量
<?php
function funserialize($serialized, &$into) {
static
$sfalse;
if (
$sfalse === null)
$sfalse = serialize(false);
$into = @unserialize($serialized);
return
$into !== false || rtrim($serialized) === $sfalse;//whitespace at end of serialized var is ignored by PHP
}

$s_foo = 'b:0;';
var_dump(funserialize($s_foo, $foo), $foo);

$s_bar = 'bar';
var_dump(funserialize($s_bar, $bar), $bar);

$s_foo = 'a:0:{};';
var_dump(funserialize($s_foo, $foo), $foo);

?>
gives

bool(true)
bool(false)

bool(false)
bool(false)

bool(true)
array(0) {
}
Fagzal
14 年前
对于在 MySQL 中存储序列化数据时遇到引号和斜杠问题的所有人:你可能做错了。

例如,使用带有占位符和 blob 列类型的 PDO,它就会正常工作。
To Top