forcing access to __PHP_Incomplete_Class object properties
None of the above answers actually worked for me, except this solution:
$object = unserialize(serialize($object));
$object->function();
Hope it helps someone
I found this hack which will let you cast an object:
function casttoclass($class, $object)
{
return unserialize(preg_replace('/^O:\d+:"[^"]++"/', 'O:' . strlen($class) . ':"' . $class . '"', serialize($object)));
}
From http://blog.adaniels.nl/articles/a-dark-corner-of-php-class-casting/
So you can do:
$obj = casttoclass('stdClass', $incompleteObject);
and then access properties as normal.
You could also define an unserialize_callback_func
in a .htaccess/Apache configuration file. That way you wouldn't need to hack any PHP but you could include the file on demand.
As an addition here is my version of the fix_object() function: The main change is step 3 in the code: Make all properties public.
When PHP serializes an object, all private and protected properties are prefixed with two null-bytes! These null-bytes are the actual reason, why the property cannot be accessed via $obj->key
because actually it is something like $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
will not display these NULL byte prefixes to you, but you can see them with this code:
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;}"
There you see:
- The private property is prefixed with
NULL + classname + NULL
- The protected property is prefixed with
NULL + "*" + NULL
This issue appends when you un serialize an object of a class that hasn't been included yet. For exemple, if you call session_start before include the class.
A PHPIncompleteClass object can't be accessed directly, but it's ok with foreach, serialize and gettype. Calling is_object with an PHPIncompleteClass object will result false.
So, if you find a '__PHP_Incomplete_Class' object in your session and you've included your class after the session_load, you can use this function :
function fixObject (&$object)
{
if (!is_object ($object) && gettype ($object) == 'object')
return ($object = unserialize (serialize ($object)));
return $object;
}
This will results a usable object :
fixObject($_SESSION['member']);