Почему unserialize_callback_func необходим, когда spl_autoload_register уже используется?

ini_set('unserialize_callback_func', 'spl_autoload_call');

spl_autoload_register(array(self::getInstance(), 'autoload'));

Почему установленный 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);

Вывод:

212

Кто-то может объяснить это?

5
задан user198729 24 February 2010 в 13:18
поделиться

2 ответа

unserialize () требует загрузки определения класса перед фактической десериализацией данных. unserialize_callback_func вызывается, когда определение класса не загружено, а spl_autoload_call пытается использовать все автозагрузчики, зарегистрированные для загрузки класса, необходимого для unserialize ().

-1
ответ дан 18 December 2019 в 11:55
поделиться

Я провел несколько тестов, и вот записи, которые я сделал (надеюсь, они будут понятны ^^ ;; и что я не слишком заблудился в своих собственных мыслях ^^ )
Примечание: я провел свои тесты на PHP 5.3.2-dev, если это имеет значение.


Прежде всего, давайте определим файл temp-2.php, который будет содержать только это :

<?php

class a {

}

т.е. определение класса, соответствующего объекту, который мы будем пытаться десериализовать.

А все остальные части кода, которые я буду размещать, будут содержаться в файле под названием 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);

На выходе получаем вот это :

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)

Что означает, что :

  • Была вызвана функция автозагрузки 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);

И мы получаем вот такой результат :

string 'callback_spl : a' (length=16)

object(a)[1]
  public 'value' => string '100' (length=3)

Что означает:

  • Функция автозагрузки, зарегистрированная 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);

И на выходе мы получаем :

string 'callback_no_spl : a' (length=19)

object(a)[1]
  public 'value' => string '100' (length=3)

Итак, все работает нормально :

  • Вызывается 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);

А на выходе мы получим :

string 'callback_spl : a' (length=16)

object(a)[1]
  public 'value' => string '100' (length=3)

Что означает:

  • была вызвана только функция автозагрузки, зарегистрированная в 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);

Мы получим точно такой же результат, как и раньше :

string 'callback_spl : a' (length=16)

object(a)[1]
  public 'value' => string '100' (length=3)

Что, похоже, указывает на то, что автозагрузчик, определенный через 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);

На этот раз вот какой результат мы получим :

string 'callback_spl : a' (length=16)

string 'callback_no_spl : a' (length=19)

object(a)[1]
  public 'value' => string '100' (length=3)

В основном :

  • Функция автозагрузки, зарегистрированная 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);

И, на этот раз, я получаю то же самое, что и вы :

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)

И, на этот раз :

  • Вызывается функция, зарегистрированная spl_autoload_register
    • И не загружает определение класса
  • Затем вызывается функция, зарегистрированная под unserialize_callback_func
    • Она тоже не загружает определение класса...
  • Как по волшебству, снова вызывается функция, зарегистрированная под spl_autoload_register!
    • Она по-прежнему не загружает определение класса
  • И бум, мы получаем предупреждение о том, что функция, зарегистрированная под unserialize_callback_func, не загрузила определение класса.
    • Обратите внимание, что это происходит только после того, как callback_spl была вызвана во второй раз!
    • Что, похоже, указывает на то, что происходит некая автозагрузка, даже если функция, определенная через unserialize_callback_func, не загрузила то, что должна была...

Должен признать, что это и приятно, и сложно - и я совершенно не представляю, почему это происходит, поскольку это не имеет особого смысла...


Я полагаю, что это странное поведение связано с тем, что :

Поведение "стек / очередь" spl_autoload_register, я полагаю, может иметь некоторые помехи со старым поведением unserialize_callback_func...

16
ответ дан 18 December 2019 в 11:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: