为什么在使用spl_autoload_register时需要unserialize_callback_func?

use*_*729 5 php spl-autoload-call

ini_set('unserialize_callback_func', 'spl_autoload_call');

spl_autoload_register(array(self::getInstance(), 'autoload'));
Run Code Online (Sandbox Code Playgroud)

为什么设置spl_autoload_call如上?

我做了一个测试:

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

ini_set('unserialize_callback_func','mycallback');

function mycallback($classname) {
    echo 1;
}

function func2() 
{
    echo 2;
}

spl_autoload_register('func2');
unserialize($serialized_object);
Run Code Online (Sandbox Code Playgroud)

输出是:

212
Run Code Online (Sandbox Code Playgroud)

有人可以解释一下吗?

Pas*_*TIN 16

我做了一些测试,这里是我所做的笔记(希望它是可以理解的^^ ;;并且我不会在我自己的想法中迷失^^)
注意:我已经在PHP 5.3上完成了我的测试.2-dev,万一重要.


首先,让我们定义一个temp-2.php文件,它只包含这个:

<?php

class a {

}
Run Code Online (Sandbox Code Playgroud)

即对应于我们将尝试反序列化的对象的类的定义.

我将发布的代码的所有其他部分将包含在一个名为temp.php- 的文件中,该文件必须包含,temp-2.php因此类的定义是已知的.


首先尝试:我们尝试反序列化字符串,而没有定义类a:

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

function callback_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
}

spl_autoload_register('callback_spl');
$data = unserialize($serialized_object);
var_dump($data);
Run Code Online (Sandbox Code Playgroud)

作为输出,我们得到这个:

string 'callback_spl : a' (length=16)

object(__PHP_Incomplete_Class)[1]
  public '__PHP_Incomplete_Class_Name' => string 'a' (length=1)
    public 'value' => string '100' (length=3)
Run Code Online (Sandbox Code Playgroud)

意思就是 :

  • callback_spl已调用 自动加载功能
    • 即使注册了 spl_autoload_register
    • 但它没有自动加载任何东西
  • 并且,由于类没有被自动加载,我们得到一个对象,它是一个实例 __PHP_Incomplete_Class


现在,让我们尝试使用spl_autoload_register注册实际自动加载类定义的自动加载功能:

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

function callback_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
    require dirname(__FILE__) . '/temp-2.php';
}

spl_autoload_register('callback_spl');
$data = unserialize($serialized_object);
var_dump($data);
Run Code Online (Sandbox Code Playgroud)

我们得到这个输出:

string 'callback_spl : a' (length=16)

object(a)[1]
  public 'value' => string '100' (length=3)
Run Code Online (Sandbox Code Playgroud)

意思是 :

  • 注册的自动加载功能spl_autoload_register已被调用
    • 而且,这一次,它确实需要包含类定义的文件
  • 非序列化已经成功
    • 即我们不再获得实例__PHP_Incomplete_Class,
    • 我们实际上得到了一个实例 a

所以,在这里,我想说使用它unserialize_callback_func时不需要spl_autoload_register.

我想,在这里,我有点回答了这个问题?但我会发布一些其他的测试,只是为了好玩^^



现在,如果我们尝试使用unserialize_callback_func,而不是使用spl_autoload_register怎么办?
我猜想代码看起来像他的代码:

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

ini_set('unserialize_callback_func', 'callback_no_spl');

function callback_no_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
    require dirname(__FILE__) . '/temp-2.php';
}

$data = unserialize($serialized_object);
var_dump($data);
Run Code Online (Sandbox Code Playgroud)

而且,作为输出,我们得到:

string 'callback_no_spl : a' (length=19)

object(a)[1]
  public 'value' => string '100' (length=3)
Run Code Online (Sandbox Code Playgroud)

所以,一切正常:

  • callback_no_spl通过注册的回调函数unserialize_callback_func被调用
    • 它加载类的定义
  • 并且数据被正确地反序列化
    • 即我们得到一个实例 a


走得更远,让我们尝试两者兼得的结果:

  • 设置自动加载的功能,称为callback_no_spl,用unserialize_callback_func
  • 并设置另一个自动加载功能,被称为callback_spl,用spl_autoload_register

代码如下所示:

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

ini_set('unserialize_callback_func', 'callback_no_spl');
function callback_no_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
    require dirname(__FILE__) . '/temp-2.php';
}

spl_autoload_register('callback_spl');
function callback_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
    require dirname(__FILE__) . '/temp-2.php';
}

$data = unserialize($serialized_object);
var_dump($data);
Run Code Online (Sandbox Code Playgroud)

我们得到的输出:

string 'callback_spl : a' (length=16)

object(a)[1]
  public 'value' => string '100' (length=3)
Run Code Online (Sandbox Code Playgroud)

意思是 :

  • 只有自动加载函数注册spl_autoload_register已被称为
  • 它确实加载了包含类定义的文件
  • 并且数据已正确地反序列化.


现在,只是为了好玩,如果我们尝试更改设置自动加载器的顺序怎么办?
即使用这部分代码:

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

spl_autoload_register('callback_spl');
function callback_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
    require dirname(__FILE__) . '/temp-2.php';
}

ini_set('unserialize_callback_func', 'callback_no_spl');
function callback_no_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
    require dirname(__FILE__) . '/temp-2.php';
}

$data = unserialize($serialized_object);
var_dump($data);
Run Code Online (Sandbox Code Playgroud)

我们得到与以前完全相同的输出:

string 'callback_spl : a' (length=16)

object(a)[1]
  public 'value' => string '100' (length=3)
Run Code Online (Sandbox Code Playgroud)

这似乎表明自动加载器定义的spl_autoload_register优先级高于定义的优先级unserialize_callback_func.


我还能测试什么?
哦,让我们测试设置两个自动加载功能,但是注册的spl_autoload_register 那个(即具有最高优先级的那个)实际上没有加载类的定义:

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

ini_set('unserialize_callback_func', 'callback_no_spl');
function callback_no_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
    require dirname(__FILE__) . '/temp-2.php';
}

spl_autoload_register('callback_spl');
function callback_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
    //require dirname(__FILE__) . '/temp-2.php';        // We don't load the class' definition
}

$data = unserialize($serialized_object);
var_dump($data);
Run Code Online (Sandbox Code Playgroud)

这一次,这是我们得到的输出:

string 'callback_spl : a' (length=16)

string 'callback_no_spl : a' (length=19)

object(a)[1]
  public 'value' => string '100' (length=3)
Run Code Online (Sandbox Code Playgroud)

基本上:

  • 注册的自动加载功能spl_autoload_register已被调用
    • 它没有加载类的定义
  • 因此unserialize_callback_func已经调用了 注册的自动加载功能
    • 它确实加载了类的定义
    • 因此,我们已经正确地获取了未序列化的数据.


现在,让我们回到您发布的代码示例 - 翻译成我的函数名称,它会给我们这样的东西,我想:

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

ini_set('unserialize_callback_func', 'callback_no_spl');
function callback_no_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
    //require dirname(__FILE__) . '/temp-2.php';        // We don't load the class' definition
}

spl_autoload_register('callback_spl');
function callback_spl($className)  {
    var_dump(__FUNCTION__ . ' : ' . $className);
    //require dirname(__FILE__) . '/temp-2.php';        // We don't load the class' definition
}

$data = unserialize($serialized_object);
var_dump($data);
Run Code Online (Sandbox Code Playgroud)

而且,这一次,我得到了和你一样的东西:

string 'callback_spl : a' (length=16)
string 'callback_no_spl : a' (length=19)
string 'callback_spl : a' (length=16)

( ! ) Warning: unserialize() [function.unserialize]: Function callback_no_spl() hasn't defined the class it was called for ...

object(__PHP_Incomplete_Class)[1]
  public '__PHP_Incomplete_Class_Name' => string 'a' (length=1)
  public 'value' => string '100' (length=3)
Run Code Online (Sandbox Code Playgroud)

而且,这一次:

  • 注册的函数spl_autoload_register被调用
    • 并且不加载类的定义
  • 然后,函数注册unserialize_callback_func被称为
    • 它也没有加载类的定义......
  • 像魔术一样,注册的功能spl_autoload_register再次被调用!
    • 它仍然没有加载类的定义
  • 繁荣时,我们得到一个警告,说注册的函数unserialize_callback_func没有加载类的定义
    • 请注意,这只发生在callback_spl第二次调用之后!
    • 这似乎表明即使定义的函数unserialize_callback_func没有加载应该具有的内容,也会发生某种自动加载...

我不得不承认,这既好又棘手 - 我不知道为什么会这样,因为它似乎没有多大意义......


我想这种奇怪的行为与以下事实有关:

spl_autoload_register我想,"堆栈/队列"行为会对unserialize_callback_func...... 的旧行为产生一些干扰.


Sam*_*ark -1

unserialize() 需要在实际反序列化数据之前加载类定义。当未加载类定义并且 spl_autoload_call 尝试使用所有已注册的自动加载器来加载 unserialize() 所需的类时,将调用 unserialize_callback_func。