带有我的 $_SESSION 数据的 PHP __PHP_Incomplete_Class 对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2010427/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 04:40:17  来源:igfitidea点击:

PHP __PHP_Incomplete_Class Object with my $_SESSION data

phpclasssessionobject

提问by dave

I've got a site setup that, on page load, turns all user submitted strings into SafeString objects. For those unfamiliar with SafeString, it basically forces the user to echo out sanitized data preventing XSS and whatnot..

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

Anyways, there's a problem. My $_SESSION array is being filled with __PHP_Incomplete_Class Object. From what I've read, this is due to not initializing the class before the session and then storing class objects in the session.

总之,有问题。我的 $_SESSION 数组被__PHP_Incomplete_Class Object. 从我读过的内容来看,这是因为没有在会话之前初始化类,然后在会话中存储类对象。

Here's my code:

这是我的代码:

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

I can't think of a way to rewrite this which would solve the problem :/

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

Any ideas?

有任何想法吗?

回答by bobince

When you're accessing $_SESSION, you're not just changing the current script's copy of the data read from the session, you're writing SafeString objects back into the active session.

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

But putting custom objects in the session is dodgy and something I would generally try to avoid. To be able to do it you have to have defined the class in question before calling session_start; if you don't, PHP's session handler won't know how to deserialise the instances of that class, and you'll end up with the __PHP_Incomplete_Class Object.

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

So avoid frobbing the session. If you must take this approach, make a copy of the data from $_SESSIONinto a local $mysessionarray. However, I have to say I think the whole idea of a SafeString is dangerous and unworkable; I don't think this approach is ever going to be watertight. Whether a string of raw text is ‘safe' is nothing to do with where it came from, it is a property of how you encode it for the target context.

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

If you get another text string from a different source such as the database, or a file, or calculated within the script itself, it needs exactly the same handling as a string that came from the user: it needs to be htmlspecialchars?ed. You're going to have to write that escape anyway; the safestring gains you nothing. If you need to send the string to a different destination format, you would need a different escape.

如果您从不同的来源(例如数据库或文件)获取另一个文本字符串,或者在脚本本身中计算,则它需要与来自用户的字符串完全相同的处理:它需要被htmlspecialchars?ed。无论如何,您将不得不编写该逃逸;安全字符串对你没有任何好处。如果您需要将字符串发送到不同的目标格式,则需要不同的转义。

You cannot encapsulate all string processing problems into one handy box and never think about them again; that's just not how strings work.

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

回答by Steel Brain

I know it's been years since this was asked, but I'm posting my answer because none of the answers above actually explain to the OP what is actually wrong.

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

PHP serializes its sessions using the built-in serializeand unserializemethods. serializeof PHP has the ability to serialize PHP objects (aka class instances) and convert them to string. When you unserializethose strings, It converts them back those same classes with those values. Classes who have some private properties and want to encode/decode that or do something complex in their serialization/deserialization implement the Serializableclass and add serializeand unserializemethods to the class.

PHP 使用内置的serializeunserialize方法序列化其会话。serializePHP 能够序列化 PHP 对象(又名类实例)并将它们转换为字符串。当您使用unserialize这些字符串时,它会将它们转换回具有这些值的相同类。谁拥有一些私人性质,并希望编码/解码是或做复杂的东西在自己的序列化/反序列化类实现Serializable类,并添加serializeunserialize方法的类。

When PHP's unserializetries to unserialize a class object, but the class name isn't declared/required, instead of giving a warning or throwing an Exception, it converts it to an object of __PHP_Incomplete_Class.

当 PHPunserialize尝试对类对象进行反序列化,但未声明/需要类名时,它不会发出警告或抛出Exception,而是将其转换为__PHP_Incomplete_Class.

If you don't want your session objects to convert to __PHP_Incomplete_Class, You can do it by either requiring the class files before you invoke session_start, or by registering an autoload function.

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

回答by Lukman

You just have to include the safestring.class.phpbefore you call session_start()when you want to read the SafeString objects from $_SESSIONvariable:

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

<?php

require_once __WEBROOT__ . '/includes/safestring.class.php';    
session_start();

print_r($_SESSION);

and yeah, if you are using PHP framework that (most probably) calls session_start()internally, make sure you require_oncethe class file beforehand (use hooks or whatever mechanisms that the framework provides).

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

回答by commonpike

Lukman's answer is correct. But you already mention that in your question, so apparently you can't instantiate the class before the session starts, for some reason.

卢克曼的回答是正确的。但是您已经在问题中提到了这一点,因此显然由于某种原因,您无法在会话开始之前实例化该类。

You may want to check if sessions start automatically in the php config: http://www.php.net/manual/en/session.configuration.php#ini.session.auto-start

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

If they are and yu cant help that, you may want to check if you can have your classes autoloaded prior to that: http://php.net/manual/en/language.oop5.autoload.php

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

If all else fails, you can still serialize the objects before you store them in a session, and unserialize them each them you retrieve them: http://php.net/manual/en/function.serialize.php

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

I dont see in your code where you store your variables, but it would be something like

我没有在你的代码中看到你存储变量的位置,但它会像

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

Be sure to load the class definition before you unserialize your variables

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

$2c, *-pike

2 美分,*-派克

回答by brad tittle

I just dealt with something like this. Took me hours to finally find how my order was screwed.

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

I had a file being called asynchronously.

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

myFile.php

我的文件

that file contained the following..

该文件包含以下内容..

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

Myotherfile.php has something like this

Myotherfile.php 有这样的东西

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

the webconfig.php had the session_start() call in it.

webconfig.php 中有 session_start() 调用。

The lib/myClassLibs has all the class info init. If you check before the webconfig call, you can see that the class is available.

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

If you check before the webconfig call, you will also see that the session has started already. If you check before the lib/myClassLibs.php, you will see the session is already started.

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

Checking in myFile.php before you include MyOtherFile.php, you find the session has not started.

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

This represented legacy code that has worked for the last 8 years without me fiddling with it. I pulled the includes out of the "MyOtherFile.php". Now my sessions are synching properly.

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

回答by Ariful Haque

I solved the problem using json_encodeand json_decodefunction.

我解决了使用json_encodejson_decode功能的问题。

This is where I wanted to assign the value to session.

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

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

This is where I show the user after decoding the json

这是我在解码 json 后向用户展示的地方

session_start();

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

This solves my problem but I am not sure about performance or security. I haven't checked them.

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

回答by CheddarMonkey

I know this is a really old question but I ran into this problem. After more research and experimenting I came up with a what I think is an acceptable alternative to storing classes in the session. It might be a bit hackish, but works for my current project.

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

NOTE: this work-around works for me because I start a session when a user logs in and don't want to include every possible class the user might, or might not encounter during the session. Including all the classes doesn't seem practical or efficient (but maybe this isn't any better ???).

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

First, my base class contains the following code that populates the object attributes from a given array, automatically.

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

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

}

The Work-Around:I use the toArray() method to convert a class instance to an array before it goes into the session, then create a new instance of the class when fetching it from the session.

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

$_SESSION['user'] = $userInstance->toArray();

// ... do stuff ...

$userInstance = new User($_SESSION['user']);

This is also really handy for writing classes to a database and converting to JSON. Both of which are made easier when working with a PHP array.

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

Like I said above, this may or may not be the most efficient way to handle this problem. It also raises the question, "should I be using PHP classes if I'm just going to convert to arrays?"

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

回答by Storage Lenovo

I run into the same problem and the solution was inspired by @bobince answer

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

To be able to do it you have to have defined the class in question before calling session_start

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

First, my session was set like this:

首先,我的会话是这样设置的:

$_SESSION["customer"] = $customerObj;

Then before calling the session_start(), I have to load or defined the class first by importing it and then call session_start()right after

然后在调用之前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();
}

回答by janoulle

I solved this problem by including the __autoloadfunction at the top of my php file. So it looks like this:

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

<?php 
require_once("path/to/include.inc");

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

In PHP 5, this function isn't be needed but I was stuck until I used this function. Hope this helps someone else!

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

回答by Tim

My mistake here was that I had set the session.auto_startsetting to on. The session would then be initialized before any line of code (including the autoloader) will be called.

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