Подтвердить что ты не робот

Принудительный доступ к свойствам объекта __PHP_Incomplete_Class

Я пишу модуль для php cms. В функции (обратном вызове) я могу получить доступ к объекту, который поступает из кода рамки.

Этот объект имеет тип __PHP_Incomplete_Class, потому что необходимый заголовочный файл не включается до начала сеанса. Я не могу включить его без взлома кода cms cms.

Интересно, возможно ли вообще получить доступ к свойствам объекта (приведение в массив не работает). Я спрашиваю об этом, потому что я вижу значения с помощью var_dump(), но используя $object->var, я всегда получаю null.

4b9b3361

Ответ 1

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

Объект PHPIncompleteClass не может быть доступен напрямую, но это нормально с foreach, serialize и gettype. Вызов is_object с объектом PHPIncompleteClass приведет к ошибке.

Итак, если вы обнаружите объект __PHP_Incomplete_Class в своем сеансе, и вы включили свой класс после session_load, вы можете использовать эту функцию:

function fixObject (&$object)
{
  if (!is_object ($object) && gettype ($object) == 'object')
    return ($object = unserialize (serialize ($object)));
  return $object;
}

Это приведет к использованию полезного объекта:

fixObject($_SESSION['member']);

Ответ 2

Я нашел этот хак, который позволит вам создать объект:

function casttoclass($class, $object)
{
  return unserialize(preg_replace('/^O:\d+:"[^"]++"/', 'O:' . strlen($class) . ':"' . $class . '"', serialize($object)));
}

От http://blog.adaniels.nl/articles/a-dark-corner-of-php-class-casting/

Итак, вы можете сделать:

$obj = casttoclass('stdClass', $incompleteObject);

а затем доступ к свойствам как обычно.


Вы также можете определить unserialize_callback_func в файле конфигурации .htaccess/Apache. Таким образом, вам не нужно будет взломать любой PHP, но вы можете включить файл по требованию.

Ответ 3

В качестве дополнения здесь представлена ​​моя версия функции fix_object(): Основное изменение - это шаг 3 в коде: Сделать все свойства общедоступными.

Когда PHP сериализует объект, все частные и защищенные свойства имеют префикс с двумя нулевыми байтами! Эти нуль-байты являются фактической причиной, почему свойство не может быть доступно через $obj->key, потому что на самом деле это что-то вроде $obj->{NULL*NULL}key.

/**
 * Takes an __PHP_Incomplete_Class and casts it to a stdClass object.
 * All properties will be made public in this step.
 *
 * @since  1.1.0
 * @param  object $object __PHP_Incomplete_Class
 * @return object
 */
function fix_object( $object ) {
    // preg_replace_callback handler. Needed to calculate new key-length.
    $fix_key = create_function(
        '$matches',
        'return ":" . strlen( $matches[1] ) . ":\"" . $matches[1] . "\"";'
    );

    // 1. Serialize the object to a string.
    $dump = serialize( $object );

    // 2. Change class-type to 'stdClass'.
    $dump = preg_replace( '/^O:\d+:"[^"]++"/', 'O:8:"stdClass"', $dump );

    // 3. Make private and protected properties public.
    $dump = preg_replace_callback( '/:\d+:"\0.*?\0([^"]+)"/', $fix_key, $dump );

    // 4. Unserialize the modified object again.
    return unserialize( $dump );
}

var_dump не отобразит эти NULL байтовые префиксы, но вы можете увидеть их с помощью этого кода:

class Test {
    private $AAA = 1;
    protected $BBB = 2;
    public $CCC = 3;
}

$test = new Test();
echo json_encode( serialize( $test ) );

// Output:
// "O:4:\"Test\":3:{s:9:\"\u0000Test\u0000AAA\";i:1;s:6:\"\u0000*\u0000BBB\";i:2;s:3:\"CCC\";i:3;}"

$test2 = fix_object( $test );
echo json_encode( serialize( $test2 ) );

// Output:
// "O:8:\"stdClass\":3:{s:3:\"AAA\";i:1;s:3:\"BBB\";i:2;s:3:\"CCC\";i:3;}"

Там вы видите:

  • Частная собственность имеет префикс NULL + classname + NULL
  • Защищенное свойство имеет префикс NULL + "*" + NULL

Ответ 4

Если вам просто нужно получить доступ к необработанным данным (например, переменным класса) из объекта PHP_Incomplete_Class, вы можете использовать foreach hack, или вы также можете:

$result_array = (array)$_SESSION['incomplete_object_index'];
echo $result_array['desired_item'];

Ответ 5

Я прочитал много предложений о том, как исправить неполные классные объекты, и мне действительно нужно было самостоятельно решить эти проблемы в проекте электронной коммерции.

Одно из предложений, которое я нашел, - просто использовать json_decode/json_encode для преобразования неполных классов без предварительной загрузки. Тем не менее, я не хотел рисковать этим, если есть более старые версии PHP, которые зависят, например, от PECL, описанного в http://php.net/manual/en/function.json-encode.php - поэтому мне, наконец, удалось сделать свое собственное решение.

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

Он также работает рекурсивно, что в моем случае необходимо, чтобы сохранить весь массив.

/**
 * Convert a object to a data object (used for repairing __PHP_Incomplete_Class objects)
 * @param array $d
 * @return array|mixed|object
 */
function arrayObjectToStdClass($d = array())
{
    /**
     * If json_decode and json_encode exists as function, do it the simple way.
     * http://php.net/manual/en/function.json-encode.php
     */
    if (function_exists('json_decode') && function_exists('json_encode')) {
        return json_decode(json_encode($d));
    }
    $newArray = array();
    if (is_array($d) || is_object($d)) {
        foreach ($d as $itemKey => $itemValue) {
            if (is_array($itemValue)) {
                $newArray[$itemKey] = (array)$this->arrayObjectToStdClass($itemValue);
            } elseif (is_object($itemValue)) {
                $newArray[$itemKey] = (object)(array)$this->arrayObjectToStdClass($itemValue);
            } else {
                $newArray[$itemKey] = $itemValue;
            }
        }
    }
    return $newArray;
}

Ответ 6

Поместите session_start() после вашего требования к классу объекта, который вы пытаетесь прочитать из СЕССИИ