【问题标题】:forcing access to __PHP_Incomplete_Class object properties强制访问 __PHP_Incomplete_Class 对象属性
【发布时间】:2009-06-08 15:51:46
【问题描述】:

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

此对象的类型为__PHP_Incomplete_Class,因为在会话开始之前未包含所需的头文件。如果不破解核心 cms 代码,我就无法包含它。

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

【问题讨论】:

    标签: php


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      除了这个解决方案之外,上述答案都没有真正对我有用:

      $object = unserialize(序列化($object));

      $object->函数();

      希望对某人有所帮助

      【讨论】:

        【解决方案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 为前缀

        【讨论】:

        • LOL 你提供了相同的改进来同时处理私有和公共,而我正在编写我的代码
        • 我很想看看 PHP >7.2 版本。 create_function 已弃用。
        【解决方案4】:

        我在这里尝试了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

        【讨论】:

          【解决方案5】:

          当您取消序列化尚未包含的类的对象时,会附加此问题。 例如,如果你在包含类之前调用​​ 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']);
          

          【讨论】:

          • 在第二段代码中,您是指 fixObject 而不是 fixclass?可能会令人困惑。
          • 你也可以使用自动加载器来加载类,这样就可以解决整个问题。
          • 反序列化后,我仍然无法访问属性,但 foreach 对我来说没问题。
          • 不知道为什么,但在我看来 unserialize(serialize($data)) 应该做任何事情都很愚蠢,但它确实有效。谢谢
          • 自 PHP 7.2 起这将不再有效,因为 is_object() 将返回不完整对象的 true。您需要使用不同的方法来检测对象是否不完整。 php.net/manual/en/…
          【解决方案6】:

          我发现这个 hack 可以让你投射一个对象:

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

          【讨论】:

          • 整洁。我只是喜欢做这样的骇人听闻的事情;)
          • 聪明但“hackish” :] 一开始我想到了 eval(something)。
          • 感谢发帖!注意:应该是 casttoclass('stdClass', $incompleteObject)。
          • 我遇到了这个问题,似乎无法实施此修复。我正在从 Ebay api 保存一个序列化的返回对象,现在无法访问子类。
          • 很好,但问题是,新创建的对象的属性仍然是私有的
          【解决方案7】:

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

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

          【讨论】:

          • 这是一个一般性的观察,也许我应该只将它作为评论添加。我曾在一家使用上述方法提取简单变量的大公司工作。可能有助于其他人了解。
          • 当您需要迁移数据库并且只需要存储在序列化对象中的简单数据时,它很有用。
          • 这很好用。我正在进行迁移并且情况非常复杂。这很容易解决!谢谢!
          【解决方案8】:

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

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

          【讨论】:

            猜你喜欢
            • 2011-07-04
            • 1970-01-01
            • 2021-03-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多