PHP __PHP_PHP_Incomplete_Class Obiect cu datele mele $_SESSION (Programare, Php, Clasa, Sesiune, Obiect)

dave a intrebat.

Am o configurație de site care, la încărcarea paginii, transformă toate șirurile trimise de utilizator în obiecte SafeString. Pentru cei care nu sunt familiarizați cu SafeString, practic forțează utilizatorul să trimită un ecou cu date igienizate, prevenind XSS și altele…

În orice caz, există o problemă. Matricea mea $_SESSION este umplută cu __PHP_Incomplete_Class Object. Din ce am citit, acest lucru se datorează faptului că nu inițializăm clasa înainte de sesiune și apoi stocăm obiectele clasei în sesiune.

Iată codul meu:

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

Nu mă pot gândi la o modalitate de a rescrie acest lucru care ar rezolva problema :/

Aveți vreo idee?

13 răspunsuri
bobince

Când accesezi $_SESSION, nu modificați doar copia scriptului curent a datelor citite din sesiune, ci scrieți obiecte SafeString înapoi în sesiunea activă.

Dar punerea obiectelor personalizate în sesiune este dubioasă și ceva ce aș încerca în general să evit. Pentru a putea face acest lucru, trebuie să definiți clasa în cauză înainte de a apela la session_start; dacă nu o faceți, gestionarul de sesiune PHP nu va ști cum să deserializeze instanțele acelei clase și veți sfârși cu __PHP_Incomplete_Class Object.

Prin urmare, evitați să faceți frobbing la sesiune. Dacă trebuie să adoptați această abordare, faceți o copie a datelor din $_SESSION într-un fișier local $mysession locală. Cu toate acestea, trebuie să spun că eu cred că întreaga idee a unui SafeString este periculoasă și nefuncțională; nu cred că această abordare va fi vreodată etanșă. Faptul că un șir de text brut este „sigur” nu are nimic de-a face cu locul de unde provine, ci este o proprietate a modului în care îl codificați pentru contextul țintă.

Dacă obțineți un alt șir de text dintr-o sursă diferită, cum ar fi baza de date, un fișier sau calculat în scriptul însuși, acesta are nevoie de exact aceeași manipulare ca și un șir care provine de la utilizator: trebuie să fie htmlspecialcharsed. Oricum va trebui să scrieți acel escape; safestring-ul nu vă aduce nimic. Dacă trebuie să trimiteți șirul către un format de destinație diferit, veți avea nevoie de un alt escape.

Nu puteți încapsula toate problemele de procesare a șirurilor de caractere într-o singură cutie la îndemână și să nu vă mai gândiți niciodată la ele; pur și simplu nu așa funcționează șirurile de caractere.

Comentarii

  • Răspuns excelent. +1. Iubesc PHP 😀 Problema mea a fost un fișier cache vechi. Ar presupune, un mesaj de eroare de genul ăsta: „Obiectul nu a putut fi deserializat, deoarece nu a putut fi găsit”… –  > Por Christian Gollhardt.
  • „necesită clasa ÎNAINTE de a apela session_start()” – –  > Por max4ever.
Steel Brain

Știu că au trecut ani de zile de când a fost întrebat acest lucru, dar postez răspunsul meu pentru că niciunul dintre răspunsurile de mai sus nu îi explică de fapt lui OP ce este de fapt greșit.

PHP își serializează sesiunile folosind funcția încorporată serialize și unserialize metode. serialize de PHP are capacitatea de a serializa obiectele PHP (aka instanțe de clasă) și de a le converti în șir de caractere. Atunci când unserialize acele șiruri, acesta le convertește înapoi aceleași clase cu acele valori. Clasele care au unele proprietăți private și doresc să codifice/decodifice acest lucru sau să facă ceva complex în serializarea/deserializarea lor implementează funcția Serializable clasă și adaugă serialize și unserialize metode la clasă.

Atunci când PHP unserialize încearcă să deserializeze un obiect de clasă, dar numele clasei nu este declarat/necesar, în loc să dea un avertisment sau să arunce o eroare de tip Exception, îl convertește într-un obiect de clasă __PHP_Incomplete_Class.

Dacă nu doriți ca obiectele sesiunii dvs. să fie convertite în __PHP_Incomplete_Class, puteți face acest lucru fie cerând fișierele de clasă înainte de a invoca session_start, fie prin înregistrarea unei funcții de încărcare automată.

Comentarii

  • ProTip: dacă soluția nu pare să funcționeze, nu uitați să ștergeți cookie-ul de sesiune/sesiune/încercați într-o fereastră de navigare privată. Am pierdut timp cu depanarea pentru că am uitat de __PHP_Incomplete_Class obiectele fuseseră deja stocate în sesiune, doh! –  > Por Pocketsand.
  • Dacă se folosește SPL, ar funcționa? (Încă nu a fost implementat, dar ar face-o în cele din urmă) – –  > Por Master DJon.
Lukman

Trebuie doar să includeți safestring.class.php înainte de a apela session_start() atunci când doriți să citiți obiectele SafeString din $_SESSION variabilă:

<?php

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

print_r($_SESSION);

și da, dacă folosiți un cadru PHP care (cel mai probabil) apelează session_start() în mod intern, asigurați-vă că require_once fișierul de clasă în prealabil (folosiți cârlige sau orice alte mecanisme pe care le oferă cadrul).

commonpike

Răspunsul lui Lukman este corect. Dar ați menționat deja acest lucru în întrebarea dvs., deci se pare că nu puteți instanția clasa înainte de începerea sesiunii, din anumite motive.

Poate doriți să verificați dacă sesiunile încep automat în configurația php:http://www.php.net/manual/en/session.configuration.php#ini.session.auto-start

Dacă sunt și nu puteți face nimic, poate doriți să verificați dacă puteți face ca clasele să fie încărcate automat înainte de acest lucru:http://php.net/manual/en/language.oop5.autoload.php

Dacă toate celelalte eșuează, puteți serializa obiectele înainte de a le stoca într-o sesiune și le puteți deserializa de fiecare dată când le recuperați:http://php.net/manual/en/function.serialize.php

Nu văd în codul dvs. unde vă stocați variabilele, dar ar trebui să fie ceva de genul

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

Asigurați-vă că încărcați definiția clasei înainte de a dez-serializa variabilele dvs.

$2c,*-pike

Brad Tittle

Tocmai m-am confruntat cu ceva de genul acesta. Mi-a luat ore întregi pentru a găsi în cele din urmă modul în care comanda mea a fost înșurubată.

Am avut un fișier care a fost apelat asincron.

myFile.php

că fișierul conținea următoarele…

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

Myotherfile.php are ceva de genul acesta

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

webconfig.php avea în el apelul session_start().

Lib/myClassLibs are toate informațiile despre clasele init. Dacă verificați înainte de apelul webconfig, puteți vedea că clasa este disponibilă.

Dacă verificați înainte de apelul webconfig, veți vedea, de asemenea, că sesiunea a început deja. Dacă verificați înainte de lib/myClassLibs.php, veți vedea că sesiunea este deja începută.

Dacă verificați în myFile.php înainte de a include MyOtherFile.php, veți constata că sesiunea nu a început.

Acest lucru a reprezentat un cod moștenit care a funcționat în ultimii 8 ani fără ca eu să mă joc cu el. Am scos includerile din „MyOtherFile.php”. Acum sesiunile mele se sincronizează corect.

Ariful Haque

Am rezolvat problema folosind json_encode și json_decode funcție.

Aici am vrut să atribui valoarea la sesiune.

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

Acesta este locul în care îi arăt utilizatorului după decodarea fișierului json

session_start();

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

Acest lucru îmi rezolvă problema, dar nu sunt sigur de performanță sau de securitate. Nu le-am verificat.

Comentarii

  • Mulțumesc, această soluție pare cea mai simplă, mai ales pentru clasa/obiectul meu mic. Poate cineva să-mi spună dacă există probleme de securitate cu acest lucru? –  > Por Shivang Saxena.
janoulle

Am rezolvat această problemă prin includerea instrucțiunii __autoload în partea de sus a fișierului meu php. Deci, arată astfel:

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

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

În PHP 5, această funcție nu este necesară, dar am fost blocat până când am folosit această funcție. Sper că acest lucru ajută pe altcineva!

Comentarii

  • Autoloading (sau încărcarea explicită, pe care nu o veți dori în PHP-ul modern) este întotdeauna necesară atunci când o clasă care este deserializată din $_SESSION date – acest lucru nu are nimic de-a face cu versiunea de PHP utilizată… nu puteți dezerializa o clasă, dacă acea clasă nu poate fi (sau nu este deja) încărcată, de aceea obțineți __PHP_Incomplete_Class pentru orice lucru care nu a reușit să se încarce automat. –  > Por mindplay.dk.
CheddarMonkey

Știu că aceasta este o întrebare foarte veche, dar m-am confruntat cu această problemă. După mai multe cercetări și experimente, am găsit ceea ce cred că este o alternativă acceptabilă la stocarea claselor în sesiune. S-ar putea să fie un pic hackish, dar funcționează pentru proiectul meu actual.

NOTĂ: această soluție alternativă funcționează pentru mine, deoarece încep o sesiune atunci când un utilizator se conectează și nu doresc să includ fiecare clasă posibilă pe care utilizatorul ar putea, sau nu, să o întâlnească în timpul sesiunii. Includerea tuturor claselor nu mi se pare practică sau eficientă (dar poate că acest lucru nu este mai bun ????).

În primul rând, clasa mea de bază conține următorul cod care completează automat atributele obiectului dintr-o matrice dată.

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

}

Metoda de lucru:Folosesc metoda toArray() pentru a converti o instanță de clasă într-un array înainte de a intra în sesiune, apoi creez o nouă instanță a clasei atunci când o extrag din sesiune.

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

// ... do stuff ...

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

Acest lucru este, de asemenea, foarte util pentru a scrie clasele într-o bază de date și pentru a le converti în JSON. Ambele sunt mai ușor de realizat atunci când se lucrează cu o matrice PHP.

După cum am spus mai sus, acesta poate fi sau nu cel mai eficient mod de a rezolva această problemă. De asemenea, ridică întrebarea: „ar trebui să folosesc clase PHP dacă am de gând doar să convertesc în array-uri?”.

Stocare Lenovo

Mă confrunt cu aceeași problemă, iar soluția a fost inspirată de răspunsul lui @bobince

Pentru a putea face acest lucru trebuie să fi definit clasa în cauză înainte de a apela session_start

În primul rând, sesiunea mea a fost setată astfel:

$_SESSION["customer"] = $customerObj;

Apoi, înainte de a apela clasa session_start(), trebuie să încarc sau să definesc clasa mai întâi prin import și apoi să sun session_start() imediat după

require 'entity/Customer.php';

ob_start();
session_start();
$customer = new Customer();
if (isset($_SESSION["customer"]))
{
    $customer = $_SESSION["customer"];
    echo $customer->getCustomerName();
}

Tim

Greșeala mea aici a fost că am setat session.auto_start la on. Sesiunea ar fi fost inițializată înainte ca orice linie de cod (inclusiv autoloaderul) să fie apelată.

RazerMoon

Greșeala mea a fost să trimit utilizatorul într-o pagină PHP fără să includ clasa în acea pagină, ci doar în pagina originală.

Arăta ceva de genul acesta:

index.php

include __DIR__.'AirInfo.php';

session_start();

$plan = new Plan();

header('Location: session.php');

session.php

// Should have put include __DIR__.'AirInfo.php' here
session_start();

Mostafa

Am aceeași problemă cu Google Photo API atunci când încerc să autentific aplicația mea și să accesez Photo API.

Rezolvați-l prin utilizarea doar session_start() după include și toate use aici codul meu complet:

    include "./vendor/autoload.php";
    use GoogleAuthCredentialsUserRefreshCredentials;
    use GooglePhotosLibraryV1PhotosLibraryClient;
    use GooglePhotosLibraryV1PhotosLibraryResourceFactory;
    use GoogleAuthOAuth2;
    session_start();
    //rest of code comes here

Dave None

S-ar putea fi doar apelarea,

session_start();
session_start();

de două ori în codul tău. Apelați-o o singură dată. Verificați clasele php necesare pentru repetiții. Aceasta a fost soluția pentru mine.

Comentarii

  • Apelarea session_start de două ori nu cauzează problema. Lipsa clasei necesare încărcate cauzează problema. –  > Por Charles.