【问题标题】:PHP __PHP_Incomplete_Class Object with my $_SESSION dataPHP __PHP_Incomplete_Class 对象和我的 $_SESSION 数据
【发布时间】:2011-01-01 21:33:06
【问题描述】:

我有一个站点设置,在页面加载时,将所有用户提交的字符串转换为 SafeString 对象。对于那些不熟悉 SafeString 的人来说,它基本上会强制用户回显已清理过的数据,以防止 XSS 之类的......

总之,有问题。我的 $_SESSION 数组被__PHP_Incomplete_Class Object 填充。根据我的阅读,这是由于没有在会话之前初始化类,然后在会话中存储类对象。

这是我的代码:

require_once __WEBROOT__ . '/includes/safestring.class.php'; 

$temp = array
(
   &$_SERVER, &$_GET, &$_POST, &$_COOKIE,
   &$_SESSION, &$_ENV, &$_REQUEST, &$_FILES,
   &$HTTP_SERVER_VARS, &$HTTP_GET_VARS,
   &$HTTP_POST_VARS, &$HTTP_COOKIE_VARS,
   &$HTTP_POST_FILES, &$HTTP_ENV_VARS
); 

function StringsToSafeString(&$array)
{
   foreach ($array as $key => $value)
   {
      if (is_string($array[$key]))
      {
         $array[$key] = new SafeString($value);
      } 

      if (is_array($array[$key]))
      {
         StringsToSafeString($array[$key]);
      }
   }
}

StringsToSafeString($temp);

unset($temp);

我想不出一种可以解决问题的重写方法:/

有什么想法吗?

【问题讨论】:

    标签: php class session object


    【解决方案1】:

    在尝试对我的应用进行身份验证和访问照片 API 时,我遇到了与 Google Photo API 相同的问题。

    只需在include 和所有use 语句之后使用session_start() 即可解决此问题。 这是我的完整代码:

        include "./vendor/autoload.php";
        use Google\Auth\Credentials\UserRefreshCredentials;
        use Google\Photos\Library\V1\PhotosLibraryClient;
        use Google\Photos\Library\V1\PhotosLibraryResourceFactory;
        use Google\Auth\OAuth2;
        session_start();
        //rest of code comes here
    

    【讨论】:

      【解决方案2】:

      我的错误是将用户发送到一个 PHP 页面,而没有在该页面中包含该类,而只在原始页面中。

      看起来像这样:

      index.php

      include __DIR__.'AirInfo.php';
      
      session_start();
      
      $plan = new Plan();
      
      header('Location: session.php');
      

      session.php

      // Should have put include __DIR__.'AirInfo.php' here
      session_start();
      

      【讨论】:

        【解决方案3】:

        我遇到了同样的问题,解决方案的灵感来自@bobince 回答

        为了能够做到这一点,您必须在调用之前定义有问题的类 session_start

        首先,我的会话设置如下:

        $_SESSION["customer"] = $customerObj;
        

        然后在调用session_start()之前,我必须先通过导入来加载或定义类,然后再调用session_start()

        require 'entity/Customer.php';
        
        ob_start();
        session_start();
        $customer = new Customer();
        if (isset($_SESSION["customer"]))
        {
            $customer = $_SESSION["customer"];
            echo $customer->getCustomerName();
        }
        

        【讨论】:

          【解决方案4】:

          我知道这是一个非常古老的问题,但我遇到了这个问题。经过更多的研究和试验后,我想出了一个我认为可以接受的替代在会话中存储类的方法。这可能有点骇人听闻,但适用于我当前的项目。

          注意:此解决方法对我有用,因为我在用户登录时启动会话,并且不想包含用户在会话期间可能遇到或可能不会遇到的每个可能的类。包括所有的类似乎并不实用或有效(但也许这并没有更好???)。

          首先,我的基类包含以下代码,该代码自动填充给定数组中的对象属性。

          class BaseClass {
          
              public function __construct($properties=[]){
                  if (!empty($properties)) {
                      array_walk($properties, function ($val, $key) {
                          $this->fromArray($key, $val);
                      });
                  }
              }
          
              public function fromArray($property, $value){
                  return (property_exists($this, $property)) ? $this->$property = $value : null;
              }
          
              public function toArray(){
                  return get_object_vars($this);
              }
          
          }
          

          解决方法: 我使用 toArray() 方法在类实例进入会话之前将其转换为数组,然后在从会话中获取该类的新实例时创建它。

          $_SESSION['user'] = $userInstance->toArray();
          
          // ... do stuff ...
          
          $userInstance = new User($_SESSION['user']);
          

          这对于将类写入数据库并转换为 JSON 也非常方便。使用 PHP 数组时,这两者都变得更容易。

          就像我上面所说的,这可能是也可能不是处理此问题的最有效方法。它还提出了一个问题,“如果我只是要转换为数组,我应该使用 PHP 类吗?”

          【讨论】:

            【解决方案5】:

            我的错误是我将session.auto_start 设置为on。然后将在调用任何代码行(包括自动加载器)之前初始化会话。

            【讨论】:

              【解决方案6】:

              我知道这个问题已经有好几年了,但我发布了我的答案,因为上面的答案都没有真正向 OP 解释什么是真正的错误。

              PHP 使用内置的 serializeunserialize 方法序列化其会话。 PHP 的serialize 能够序列化 PHP 对象(又名类实例)并将它们转换为字符串。当您unserialize 那些字符串时,它会将它们转换回具有这些值的相同类。具有一些私有属性并希望对其进行编码/解码或在其序列化/反序列化中执行复杂操作的类实现 Serializable 类并将 serializeunserialize 方法添加到该类。

              当 PHP 的 unserialize 试图反序列化一个类对象,但没有声明/要求类名时,它不会给出警告或抛出 Exception,而是将其转换为 __PHP_Incomplete_Class 的对象。

              如果您不希望会话对象转换为 __PHP_Incomplete_Class,您可以通过在调用 session_start 之前要求类文件或注册自动加载函数来实现。

              【讨论】:

              • 专业提示: 如果该解决方案似乎不起作用,请记住清除会话/会话 cookie/在隐私浏览窗口中尝试。我浪费了调试时间,因为我忘记了 __PHP_Incomplete_Class 对象已经存储在会话中,doh!
              • 如果使用 SPL,它会起作用吗? (仍未实施,但最终会实施)
              【解决方案7】:

              我使用json_encodejson_decode 函数解决了这个问题。

              这是我想为会话分配值的地方。

              $user_json              = json_encode($user);
              $_SESSION['user']           = $user_json;
              

              这是我解码 json 后显示给用户的地方

              session_start();
              
              $user_json= $_SESSION['user'];
              $user = json_decode($user_json);
              

              这解决了我的问题,但我不确定性能或安全性。我没有检查它们。

              【讨论】:

              • 谢谢,这个解决方案似乎是最简单的,尤其是对于我的小类/对象。有人可以告诉我这是否有任何安全问题吗?
              【解决方案8】:

              我通过在我的 php 文件顶部包含 __autoload 函数解决了这个问题。所以它看起来像这样:

              <?php 
              require_once("path/to/include.inc");
              
              //Needed for serialization/deserialization
              function __autoload($class_name) {
                  include "path/to/". $class_name . '.php';
              }
              

              在 PHP 5 中,不需要此函数,但在我使用此函数之前我一直卡住。希望这对其他人有帮助!

              【讨论】:

              • 当一个类从$_SESSION 数据中反序列化时,总是需要自动加载(或显式加载,这在现代 PHP 中是不需要的)——这与所使用的 PHP 版本无关...如果无法(或尚未)加载该类,则您无法取消序列化该类,因此对于无法自动加载的任何内容,您都会得到 __PHP_Incomplete_Class
              【解决方案9】:

              你可能只是在打电话,

              session_start();
              session_start();
              

              在您的代码中两次。调用一次。检查所需的 php 类是否有重复。这就是我的解决方案。

              【讨论】:

              • 调用session_start 两次不会导致问题。没有加载所需的类会导致问题。
              【解决方案10】:

              我刚刚处理过这样的事情。我花了几个小时才终于找到我的订单是如何搞砸的。

              我有一个文件被异步调用。

              我的文件.php

              该文件包含以下内容..

              $result = include ("myOtherFile.php");
              return $result;
              

              Myotherfile.php 有这样的东西

              require_once "lib/myClassLibs.php";
              require_once "webconfig.php";
              

              webconfig.php 中有 session_start() 调用。

              lib/myClassLibs 包含所有类信息 init。如果您在 webconfig 调用之前检查,您可以看到该类可用。

              如果您在调用 webconfig 之前进行检查,您还会看到会话已经开始。如果你在 lib/myClassLibs.php 之前检查,你会看到会话已经开始了。

              在包含 MyOtherFile.php 之前检查 myFile.php,您会发现会话尚未开始。

              这代表了过去 8 年来一直有效的遗留代码,而我却没有摆弄它。我从“MyOtherFile.php”中提取了包含。现在我的会话正在正确同步。

              【讨论】:

                【解决方案11】:

                卢克曼的回答是正确的。但是你已经在你的问题中提到了这一点,所以显然你不能在会话开始之前实例化这个类,出于某种原因。

                您可能想检查会话是否在 php 配置中自动启动: http://www.php.net/manual/en/session.configuration.php#ini.session.auto-start

                如果它们是并且您无法帮助,您可能需要检查是否可以在此之前自动加载您的类: http://php.net/manual/en/language.oop5.autoload.php

                如果所有其他方法都失败了,您仍然可以在将对象存储在会话中之前对其进行序列化,并在每次检索它们时对它们进行反序列化: http://php.net/manual/en/function.serialize.php

                我没有在您的代码中看到您存储变量的位置,但它会类似于

                $mystuff = unserialize($_SESSION["mystuff"]);
                $mystuff->dostuff();
                $_SESSION["mystuff"] = serialize($mystuff);
                

                确保在反序列化变量之前加载类定义

                $2c, *-派克

                【讨论】:

                  【解决方案12】:

                  当您访问$_SESSION 时,您不仅会更改当前脚本的从会话中读取的数据副本,还会将 SafeString 对象写回到活动会话中。

                  但是在会话中放置自定义对象是不可靠的,我通常会尽量避免。为了能够做到这一点,您必须在调用 session_start 之前定义相关类;如果你不这样做,PHP 的会话处理程序将不知道如何反序列化该类的实例,你最终会得到 __PHP_Incomplete_Class Object

                  所以避免破坏会话。如果您必须采用这种方法,请将$_SESSION 中的数据复制到本地$mysession 数组中。但是,我不得不说,我认为 SafeString 的整个想法是危险且不可行的。我认为这种方法永远不会是无懈可击的。一串原始文本是否“安全”与它的来源无关,它是您如何针对目标上下文对其进行编码的属性。

                  如果您从不同的来源(例如数据库、文件)或在脚本本身中计算得到另一个文本字符串,它需要与来自用户的字符串完全相同的处理方式:它需要是 @987654326 @编辑。无论如何,您将不得不编写逃逸;安全绳对你一无所获。如果您需要将字符串发送到不同的目标格式,则需要不同的转义。

                  你不能把所有的字符串处理问题都封装到一个方便的盒子里,然后再也不去想它们;字符串不是这样工作的。

                  【讨论】:

                  • 很好的答案。 +1。我喜欢 PHP :D 我的问题是一个旧的缓存文件。假设出现这样的错误消息:“无法反序列化对象,因为找不到它”...
                  • "在调用 session_start() 之前需要类"
                  【解决方案13】:

                  当您想从 $_SESSION 变量中读取 SafeString 对象时,您只需在调用 session_start() 之前包含 safestring.class.php

                  <?php
                  
                  require_once __WEBROOT__ . '/includes/safestring.class.php';    
                  session_start();
                  
                  print_r($_SESSION);
                  

                  是的,如果您使用的 PHP 框架(很可能)在内部调用 session_start(),请确保您事先 require_once 类文件(使用挂钩或框架提供的任何机制)。

                  【讨论】:

                    猜你喜欢
                    • 2014-02-05
                    • 2015-05-26
                    • 2010-09-13
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多