2009-06-08 11 views
30

Estoy escribiendo un módulo para un php cms. En una función (una devolución de llamada) puedo acceder a un objeto que proviene del código de la estructura.forzando el acceso a __PHP_Incomplete_Class propiedades del objeto

Este objeto es de tipo __PHP_Incomplete_Class porque el archivo de encabezado necesario no está incluido antes de que comience la sesión. No puedo incluirlo sin hackear el código core cms.

Me pregunto si es posible acceder a las propiedades del objeto de todos modos (la conversión a una matriz no funciona). Pregunto esto porque puedo ver los valores con var_dump() pero usando $object->var siempre obtengo nulos.

Respuesta

57

Este problema se agrega cuando usted serializa un objeto de una clase que aún no se ha incluido. Por ejemplo, si llama a session_start antes de incluir la clase.

No se puede acceder directamente a un objeto PHPIncompleteClass, pero está bien con foreach, serialize y gettype. Llamar a is_object con un objeto PHPIncompleteClass resultará falso.

Por lo tanto, si usted encuentra un objeto '' __PHP_Incomplete_Class en la sesión y ha incluido su clase después de la session_load, puede utilizar esta función:

function fixObject (&$object) 
{ 
    if (!is_object ($object) && gettype ($object) == 'object') 
    return ($object = unserialize (serialize ($object))); 
    return $object; 
} 

Esto da como resultado un objeto utilizable hará lo siguiente:

fixObject($_SESSION['member']); 
+0

En ese segundo trozo de código, ¿quiso decir fixObject en lugar de fixclass? Podría ser confuso – Cyprus106

+2

también podría usar el autocargador para cargar la clase, lo que haría que todo el problema desaparezca. – StasM

+0

después de haber sido deserializado, todavía no puedo acceder a las propiedades, pero foreach está bien para mí. – Sithu

18

me encontré con este truco que le permitirá convertir un objeto:

function casttoclass($class, $object) 
{ 
    return unserialize(preg_replace('/^O:\d+:"[^"]++"/', 'O:' . strlen($class) . ':"' . $class . '"', serialize($object))); 
} 

De http://blog.adaniels.nl/articles/a-dark-corner-of-php-class-casting/

Así que usted puede hacer:

$obj = casttoclass('stdClass', $incompleteObject); 

y luego acceder a las propiedades de la forma habitual.


También puede definir un unserialize_callback_func en un archivo de configuración .htaccess/Apache. De esa forma no necesitarás piratear ningún PHP, pero podrías incluir el archivo a pedido.

+0

aseado. Me encanta hacer hacky cosas así;) – n3rd

+0

inteligente pero "hackish":] Pensé acerca de eval (algo) al principio. – gpilotino

+0

¡Gracias por publicar! Nota: debe ser casttoclass ('stdClass', $ incompleteObject). – Langdon

0

Si sólo necesita acceder a los datos en bruto (como variables de clase) de un objeto PHP_Incomplete_Class, podrá utilizar el hack foreach o también puedes hacer:

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

de fundición no funciona en este caso – gpilotino

+1

es una observación general, tal vez debería haberlo agregado solo como un comentario. Trabajé para una gran compañía que utilizó lo anterior para extraer vars simples. podría ayudar a otros a saber. – Eric

+0

es útil cuando necesitas migrar la base de datos y solo necesitas datos simples almacenados en objetos serializados. –

3

Como una adición aquí está mi versión de la función fix_object(): El cambio principal es el paso 3 en el código: Haga que todas las propiedades sean públicas.

Cuando PHP serializa un objeto, todas las propiedades privadas y protegidas tienen el prefijo dos bytes nulos. Estos bytes nulos son la razón real por la cual no se puede acceder a la propiedad a través del $obj->key porque en realidad es algo así como $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 no mostrará estos prefijos byte nulo para usted, pero se puede ver con este código:

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

Hay que ver:

  • La propiedad privada está prefijado con NULL + classname + NULL
  • El producto protegido rty tiene el prefijo NULL + "*" + NULL
0

He leído un montón de sugerencias sobre cómo solucionar classobjects incompletas y que en realidad tenía que solucionar esos problemas a mí mismo, en un proyecto de comercio electrónico.

Una sugerencia que he encontrado es simplemente usar json_decode/json_encode para convertir clases incompletas sin precargar nada. Sin embargo, no quería correr el riesgo de usar esto, si hay versiones anteriores de PHP que son dependientes, por ejemplo, PECL, que se describe en http://php.net/manual/en/function.json-encode.php, así que finalmente tuve éxito en hacer mi propia solución.

Sin embargo, el código es una forma de sacar los datos del objeto correctamente, por lo que puede no ajustarse a todas las necesidades, y utilizará primero la solución json, si está disponible en el entorno y falla a la manipulación manual si es necesario.

También funciona recursivamente, que en mi caso es necesario, para guardar toda la matriz.

/** 
* 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; 
} 
0

Coloque el session_start() después de su requieren para la clase del objeto que está intentando leer de la sesión

Cuestiones relacionadas