PHP __PHP_Incomplete_Class Object with my $_SESSION data

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..

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.

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?


Solution 1:

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.

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.

So avoid frobbing the session. If you must take this approach, make a copy of the data from $_SESSION into a local $mysession array. 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.

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.

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

Solution 2:

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.

PHP serializes its sessions using the built-in serialize and unserialize methods. serialize of PHP has the ability to serialize PHP objects (aka class instances) and convert them to string. When you unserialize those 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 Serializable class and add serialize and unserialize methods to the class.

When PHP's unserialize tries 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.

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.

Solution 3:

You just have to include the safestring.class.php before you call session_start() when you want to read the SafeString objects from $_SESSION variable:

<?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_once the class file beforehand (use hooks or whatever mechanisms that the framework provides).

Solution 4:

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

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

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

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