强制访问__PHP_Incomplete_Class对象属性

swvgeqrz  于 2023-02-15  发布在  PHP
关注(0)|答案(8)|浏览(88)

我正在为php cms编写一个模块,在一个函数(回调函数)中我可以访问来自框架代码的对象。
这个对象的类型是__PHP_Incomplete_Class,因为在会话开始之前没有包含所需的头文件。如果不修改cms的核心代码,我就不能包含它。
我想知道是否有可能访问对象属性(强制转换为数组不起作用)。我问这个问题是因为我可以看到var_dump()的值,但使用$object->var我总是得到null。

kx7yvsdv

kx7yvsdv1#

当你反序列化一个还没有包含的类的对象时,这个问题就会出现。例如,如果你在包含这个类之前调用session_start。
不能直接访问PHPIncompleteClass对象,但可以使用foreach,serialize和gettype.使用PHPIncompleteClass对象调用is_object将导致false.
因此,如果您在会话中发现了一个'__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']);
smdncfj3

smdncfj32#

我发现这个黑客可以让你投一个对象:

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);

然后照常访问属性。
你也可以在一个. htaccess/Apache配置文件中定义一个unserialize_callback_func,这样你就不需要破解任何PHP,但是你可以根据需要包含这个文件。

0sgqnhkj

0sgqnhkj3#

下面是我的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
  • protected属性的前缀为NULL + "*" + NULL
vbkedwbf

vbkedwbf4#

上面的答案没有一个对我有效,除了这个解决方案:
$object =反序列化(序列化($object));
$对象-〉函数();
希望能帮到什么人

fkaflof6

fkaflof65#

我在这里试用了answer of Tom Haigh,但发现了两个问题。
1.当您将其他“Incomplete_Class”对象作为顶级类的属性时,它们将保持不变,即__PHP_Incomplete_Class Object
1.如果你有私有属性,它们在你的stdClass对象中仍然是私有的
所以我重写了函数句柄:

/**
 * @see: https://stackoverflow.com/a/965704/2377961
 *
 * @param  object  $object  The object that should be casted
 * @param  String  $class   The name of the class
 * @return mixed   The new created object
 */
function casttoclass($object, $class = 'stdClass')
{
  $ser_data = serialize($object);
  # preg_match_all('/O:\d+:"([^"]++)"/', $ser_data, $matches); // find all classes

  /*
   * make private and protected properties public
   *   privates  is stored as "s:14:\0class_name\0property_name")
   *   protected is stored as "s:14:\0*\0property_name")
   */
  $ser_data = preg_replace_callback('/s:\d+:"\0([^\0]+)\0([^"]+)"/',
    function($prop_match) {
      list($old, $classname, $propname) = $prop_match;
      return 's:'.strlen($propname) . ':"' . $propname . '"';
  }, $ser_data);

  // replace object-names
  $ser_data = preg_replace('/O:\d+:"[^"]++"/', 'O:' . strlen($class) . ':"' . $class . '"', $ser_data);
  return unserialize($ser_data);
}

我也切换了函数参数,这样您就可以使用

$obj = casttoclass($incompleteObject);

你得到一个只有公共属性的stdClass对象,即使你在子类中有对象,它们也会被转换成有公共属性的stdClass

fsi0uk1n

fsi0uk1n6#

如果你只需要访问PHP_Incomplete_Class对象中的原始数据(比如类变量),你可以使用foreach hack,或者你也可以:

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

bf1o4zei7#

将require之后的session_start()放到您试图从SESSION读取的对象的类中

u0sqgete

u0sqgete8#

我读了很多关于如何修复不完整类对象的建议,实际上我需要自己修复这些问题,在一个电子商务项目中。
我发现的一个建议是简单地使用json_decode/json_encode来转换不完整的类,而不需要预加载任何东西。然而,我不想冒险使用这个,如果有旧的PHP版本依赖于例如PECL,这在http://php.net/manual/en/function.json-encode.php中有描述-所以我最终成功地制作了我自己的解决方案。
然而,代码是一种从对象中正确获取数据的方法,因此它可能无法满足所有需求--而且它将首先使用json-solution(如果环境中有可用的话),然后在需要时故障转移到手动处理。
它还可以递归地工作,这在我自己的情况下是必需的,以保存整个数组。

/**
 * 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;
}

相关问题