强制访问__PHP_Incomplete_Class对象属性

时间:2009-06-08 15:51:47

标签: php

我正在为php cms编写一个模块。在函数(回调)中,我可以访问来自框架代码的对象。

此对象的类型为__PHP_Incomplete_Class,因为在会话开始之前不包含所需的头文件。我不能在不破解核心cms代码的情况下包含它。

我想知道是否有可能访问对象属性(转换为数组不起作用)。我问这个是因为我可以看到var_dump()的值,但使用$object->var我总是得到空值。

8 个答案:

答案 0 :(得分:59)

当您取消序列化尚未包含的类的对象时,会附加此问题。 例如,如果在包含类之前调用​​session_start。

无法直接访问PHPIncompleteClass对象,但foreach,serialize和gettype都可以。 使用PHPIncompleteClass对象调用is_object将导致错误。

因此,如果您在会话中找到'__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']);

答案 1 :(得分:18)

我发现这个黑客会让你施放一个物体:

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,但你可以按需包含文件。

答案 2 :(得分: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
  • 为前缀

答案 3 :(得分:0)

如果您只需要从PHP_Incomplete_Class对象访问原始数据(如类变量),您可以使用foreach hack,或者您也可以这样做:

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

答案 4 :(得分:0)

我已经阅读了很多关于如何修复不完整的类对象的建议,我实际上需要在电子商务项目中自行解决这些问题。

我发现的一个建议是简单地使用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;
}

答案 5 :(得分:0)

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

答案 6 :(得分:0)

除了以下解决方案外,以上答案均未对我真正起作用:

$ object =反序列化(serialize($ object));

$ object-> function();

希望它对某人有帮助

答案 7 :(得分:0)

我在这里尝试过answer of Tom Haigh,但是发现了2个问题。

  1. 当您具有其他“ Incomplete_Class”对象作为顶级类的属性时,它们保持不变,__PHP_Incomplete_Class Object
  2. 如果您具有私有属性,则它们仍将在您的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