2009-06-08 12 views
30

Sto scrivendo un modulo per un php cms. In una funzione (un callback) posso accedere a un oggetto che proviene dal codice framework.forzare l'accesso alle proprietà dell'oggetto __PHP_Incomplete_Class

Questo oggetto è di tipo __PHP_Incomplete_Class perché il file di intestazione necessario non è incluso prima dell'avvio della sessione. Non riesco a includerlo senza aver hackerato il codice del core cms.

Mi chiedo se sia possibile accedere comunque alle proprietà dell'oggetto (il casting sull'array non funziona). Lo chiedo perché posso vedere i valori con var_dump() ma usando $object->var ottengo sempre i null.

risposta

57

Questo problema si verifica quando si serializza un oggetto di una classe che non è ancora stato incluso. Ad esempio, se si chiama session_start prima di includere la classe.

Un oggetto PHPIncompleteClass non è accessibile direttamente, ma è ok con foreach, serialize e gettype. La chiamata a is_object con un oggetto PHPIncompleteClass risulta falsa.

Quindi, se si trova un oggetto '' __PHP_Incomplete_Class nella sessione e hai incluso la classe dopo la session_load, è possibile utilizzare questa funzione:

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

Questo sarà risultato un oggetto utilizzabile:

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

Su quel secondo pezzo di codice, intendevi fixObject invece di fixclass? Potrebbe essere fonte di confusione. – Cyprus106

+2

potresti anche usare il caricatore automatico per caricare la classe, il che farebbe scomparire l'intero problema. – StasM

+0

dopo unserialized, non riesco ancora ad accedere alle proprietà, ma foreach è ok per me. – Sithu

18

Ho trovato questo hack che vi permetterà di lanciare un oggetto:

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

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

Così si può fare:

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

e poi proprietà di accesso come normale.


Si potrebbe anche definire un unserialize_callback_func in un file di configurazione .htaccess/Apache. In questo modo non avresti bisogno di hackerare alcun PHP ma potresti includere il file su richiesta.

+0

Neat. Adoro fare cose hacky del genere;) – n3rd

+0

intelligente ma "hackish":] All'inizio pensavo a eval (qualcosa). – gpilotino

+0

Grazie per aver postato! Nota: dovrebbe essere casttoclass ('stdClass', $ incompleteObject). – Langdon

0

Se avete solo bisogno di accedere ai dati grezzi (come variabili di classe) da un oggetto PHP_Incomplete_Class, è possibile utilizzare l'hack foreach, oppure si può anche fare:

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

casting non funziona in questo caso – gpilotino

+1

è un'osservazione generale, forse avrei dovuto aggiungerlo solo come commento. Ho lavorato per una grande azienda che ha utilizzato quanto sopra per estrarre i vars semplici. potrebbe aiutare gli altri a sapere. – Eric

+0

è utile quando è necessario migrare il database e sono necessari solo i dati semplici memorizzati nell'oggetto seriealizzato. –

3

Come una aggiunta, ecco la mia versione della funzione fix_object(): La modifica principale è il passaggio 3 nel codice: Rendi pubbliche tutte le proprietà.

Quando PHP serializza un oggetto, tutte le proprietà private e protette sono precedute da due null-byte! Questi null-byte rappresentano la ragione effettiva, perché non è possibile accedere alla proprietà tramite $obj->key perché in realtà è qualcosa come $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 non visualizzerà questi prefissi NULL byte a voi, ma è possibile vederli con questo codice:

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

Ci si vede:

  • La proprietà privata è preceduto da NULL + classname + NULL
  • La prope protetta rty è preceduto da NULL + "*" + NULL
0

Ho letto un sacco di suggerimenti su come risolvere classobjects incompleti e in realtà ho bisogno di risolvere questi problemi me stesso, in una e-commerce-progetto.

Un suggerimento che ho trovato è semplicemente utilizzare json_decode/json_encode per convertire classi incomplete senza precaricare nulla. Tuttavia, non volevo correre il rischio usando questo, se ci sono versioni PHP precedenti che dipendono ad esempio da PECL, che è descritto allo http://php.net/manual/en/function.json-encode.php - così finalmente sono riuscito a fare la mia soluzione.

Tuttavia, il codice è un modo per estrarre correttamente i dati dall'oggetto, quindi potrebbe non essere adatto a tutte le esigenze e, in primo luogo, utilizzare innanzitutto la soluzione json, se è disponibile nell'ambiente e non riesce oltre alla gestione manuale se necessario.

Funziona anche in modo ricorsivo, che nel mio caso è richiesto, per salvare l'intero array.

/** 
* 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

Mettere il session_start() dopo il vostro richiedono alla classe dell'oggetto che si sta tentando di leggere dalla sessione

Problemi correlati