当前位置:   article > 正文

沉睡的木乃伊:var_export() 与可解析字符串

var_export

参考

环境

项目描述
PHP5.5.05.6.87.0.07.2.57.4.98.0.08.2.9
PHP 编辑器PhpStorm 2023.1.1(专业版)

var_export()

概念

var_export() 是 PHP 中用于 将数据转换为合法的 PHP 可解析字符串的一个内置函数var_export() 函数的主要目的是生成一个字符串,该字符串包含了一个 数据及该数据所属的数据类型的表示,以便后续可以通过 eval() 函数重新恢复该数据。

应用场景

数据持久化

var_export()eval() 可以用于实现数据持久化,因为它们共同提供了一种 将数据结构序列化为字符串,然后在需要时重新创建该数据结构的方法。这个过程可以用于将数据保存在文件或数据库中,然后在以后重新加载和使用它。

调试

var_export() 生成的输出易于阅读,它以有效的 PHP 代码形式表示变量的 值和类型,使开发人员能够快速理解变量的内容。

函数 var_export()

函数 var_export() 的大致定义如下:

function var_export(     
	mixed $value,     
	bool $return = false 
): null|string
  • 1
  • 2
  • 3
  • 4

其中:

属性描述
$value该参数用于指定 需要被转化为合法的 PHP 可解析字符串的变量或数据
$return该参数用于指定 是否需要将转化结果作为函数的返回值进行返回。如果为 false(默认值),则转化结果将 直接输出至终端,至于函数的返回值将被设置为 NULL

举个栗子

<?php


$arr = [1, 2, 3, 4, 5];
print("数组:\n");
var_export($arr);

print("\n\n字符串:\n");
var_export("Hello World");

print("\n\n整型:\n");
var_export(1);

print("\n\n布尔型:\n");
$result = var_export(TRUE, true);
print($result);

print("\n\nPHP 提供的内置通用空类的实例化对象:\n");
var_export(new stdClass());

print("\n\n自定义空类的实例化对象:\n");
class MyClass {};
var_export(new MyClass());

print("\n\n资源类型:\n");
$fp = fopen('./path/to/file', 'r');
var_export($fp);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

执行效果

数组:
array (
  0 => 1,
  1 => 2,
  2 => 3,
  3 => 4,
  4 => 5,
)

字符串:
'Hello World'

整型:
1

布尔型:
true

PHP 提供的内置通用空类的实例化对象:
(object) array(
)

自定义空类的实例化对象:
MyClass::__set_state(array(
))

资源类型:
NULL
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

自定义类

__set_state() 魔术方法

var_export() 函数在处理自定义对象时,会尝试将对象转化为形如

被处理对象所属的类::__set_state(array(
   '属性1' => '属性值1',
   '属性2' => '属性值2',
))
  • 1
  • 2
  • 3
  • 4

的字符串。在通过 eval() 函数复原对象时,字符串将被视为 PHP 代码进行执行,静态魔术方法 __set_state() 将被调用。属性与属性值组成的关联数组将被作为 __set_state() 方法的参数 用于规定复原对象的逻辑。若尝试通过 eval() 函数将一个未定义 __set_state() 方法的对象的可解析字符串还原为对象,则 PHP 将为此抛出 Fatal Error 异常。对此,请参考如下示例:

<?php


class MyClass
{
    public $name = "RedHeart";
    public $age = 18;
    public $nation = "China";
}

$indicate_text = var_export(new MyClass(), true);
print($indicate_text . "\n");

# 尝试通过 eval 将可解析字符串还原为对象
$result = eval('return ' . $indicate_text . ';');
print_r($result);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在上述示例代码中,为了将 $inidicate_text 的执行结果作为 eval 函数的返回值进行返回,我们将 $indicate_textreturn 语句进行拼接,又由于 eval() 函数的参数必须为 合法的可解析的 PHP 代码,我们又在 $indicate_text 的末尾拼接了 分号

执行效果

使用 eval() 函数将 var_export() 函数产生的可解析字符串复原为原始对象需要该对象的所属类实现了 __set_state() 魔术方法。否则,PHP 将抛出 Fatal Error 异常并立即终止。

MyClass::__set_state(array(
   'name' => 'RedHeart',
   'age' => 18,
   'nation' => 'China',
))
PHP Fatal error:  Uncaught Error: Call to undefined method MyClass::__set_state() in C:\test.php(15) : eval()'d code:1
Stack trace:
#0 C:\test.php(15): eval()
#1 {main}
  thrown in C:\test.php(15) : eval()'d code on line 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

设置 __set_state 魔术方法的逻辑以复原对象

__set_state() 魔术方法通过 var_export() 提供的 原始对象的属性信息 将能够对原始对象进行还原。对此,请参考如下示例:

<?php


class MyClass
{
    static function __set_state($obj_details) {
        # 将自生实例化为一个对象
        $obj = new self();
        # 循环遍历由原始对象的属性信息组成的数组
        foreach($obj_details as $key => $value) {
            # 将关联数组中的键作为对象的属性,
            # 值作为相应属性的属性值。
            $obj -> $key = $value;
        }
        # 将复原对象作为返回值进行返回
        return $obj;
    }
}

$myClass = new MyClass();

$myClass -> name = "RedHeart";
$myClass -> age = 19;
$myClass -> nation = "China";

# 通过 var_export(?, true) 获取对象的可解析字符串并将其输出至终端
$indicate_text = var_export($myClass, true);
print($indicate_text . "\n");

# 复原原始对象并将对象的结构输出至终端
$result = eval('return ' . $indicate_text . ";");
var_dump($result);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

执行效果

MyClass::__set_state(array(
   'name' => 'RedHeart',
   'age' => 19,
   'nation' => 'China',
))
object(MyClass)#2 (3) {
  ["name"]=>
  string(8) "RedHeart"
  ["age"]=>
  int(19)
  ["nation"]=>
  string(5) "China"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

注意事项

var_export()eval() 两者的配合进行对象的数据持久化与复原的过程中,var_export() 函数并不是必须存在的,若对 var_export 函数的输出信息较为了解,则 可以自行构造所需的字符串以实现对象的数据持久化
而在 eval() 函数使用原始对象的属性信息复原对象时,需要调用所属类的 __set_state() 方法,这就意味着被复原对象的所属类必须在尝试复原对象前进行定义。
调用 __set_state() 方法复原对象与类的实例对象无关(可以在类不被实例化任何对象时进行),故需要将 __set_state() 方法设置为静态方法。否则,PHP 将抛出 Fatal Error 异常并立即停止运行。

对于 var_export() 与 eval() 两者的配合进行对象的数据持久化与复原的过程中 需要注意的事项整理如下:

  1. var_export() 函数并不是获取对象可解析字符串的 唯一方式
  2. 被复原对象的所属类必须 在尝试复原对象前进行定义
  3. __set_state() 魔术方法必须被定义为 静态方法

举个栗子

<?php


class MyClass
{
    static function __set_state($obj_details) {
        $obj = new self();
        foreach($obj_details as $key => $value) {
            $obj -> $key = $value;
        }
        return $obj;
    }
}

# 自定义对象的可解析字符串
$indicate_text = "MyClass::__set_state(array(
                    'name' => 'RedHeart',
                    'age' => 19,
                    'nation' => 'China',
                   ))";

$result = eval('return ' . $indicate_text . ";");
var_dump($result);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

执行效果

object(MyClass)#1 (3) {
  ["name"]=>
  string(8) "RedHeart"
  ["age"]=>
  int(19)
  ["nation"]=>
  string(5) "China"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

通用内置空类 stdClass

stdClass

stdClass 是 PHP 提供的一个 基础类,用于创建一个 没有预定义结构的空对象(stdClass 中没有定义任何成员)stdClass 的结构大致如此:

class stdClass {}
  • 1

stdClass 提供了一种方式,允许开发者 在运行时动态地创建和管理对象属性,而不必预先定义一个完整的类。对此,请参考如下示例:

<?php


// stdClass 属于 PHP 的基础类,
// 是 PHP 的一部分,可以直接进行使用。
$myClass = new stdClass();

// 在 PHP 运行过程中动态的对属性进行创建与管理
$myClass -> name = "RedHeart";
$myClass -> nation = "BinaryMoon";
$myClass -> nation = "China";

print_r($myClass);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

执行效果

stdClass Object
(
    [name] => RedHeart
    [nation] => China
)
  • 1
  • 2
  • 3
  • 4
  • 5

对 __set_state() 的天然支持

PHP 的内置类 stdClass 尽管是一个空类但却对 __set_state() 魔术方法存在 天然的支持(PHP 内部已为 stdClass 实例对象提供了 __set_state 实现)。对此,请参考如下示例:

<?php


$myClass = new stdClass();

# 获取 stdClass 实例对像的方法列表并将其输出
print_r(get_class_methods($myClass));

# 定义 stdClass 实例对象的可解析字符串
$indicate_text = "(object)array(
                    'name' => 'RedHeart',
                    'age' => 19,
                    'nation' => 'China',
                   )";

# 尝试通过 eval() 函数还原 stdClass 对象
$result = eval('return ' . $indicate_text . ';');
print_r($result);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

执行效果

由上述示例代码的执行结果可知,即使 stdClass 未定义 __set_state 方法,该方法也能被成功被调用并执行还原对象的功能

Array
(
)
stdClass Object
(
    [name] => RedHeart
    [age] => 19
    [nation] => China
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/244089
推荐阅读
相关标签
  

闽ICP备14008679号