Navigace

Hlavnφ menu

 

PHP pro pokroΦilΘ - znovu t°φdy a objekty

V tomto Φlßnku o t°φdßch a objektech si ukß₧eme dalÜφ zajφmavΘ mo₧nosti p°i prßci s OOP v PHP. Budeme se zab²vat vyu₧itφm takzvanΘ serializace a magick²ch funkcφ __sleep() a __wakeup() - vytvo°φme instanci t°φdy, ulo₧φme ji do souboru a nakonec cel² objekt p°edßme v session.

Serializace a unserializace

VÜe zajiÜ¥ujφ funkce serialize() a unserialize(), kterΘ p°evedou do takzvanΘho byte °et∞zce prom∞nnΘ objektu. Proces tohoto "p°evodu" se naz²vß serializace a opaΦn² postup unserializace. Na vstupu funkce serialize() je objekt a na v²stupu ji₧ zmi≥ovan² byte °et∞zec, u funkce unserialize() je tomu naopak.

Pro dalÜφ experimentovßnφ si nadefinujeme pokusnou t°φdu do souboru "trida.php" se kterou budeme dßle pracovat:

<?
/*
Soubor trida.php ve kterΘm je definice pokusnΘ t°φdy
*/

class clsTrida {
//pomocφ konstruktoru nastavφme hodnoty vlastnostφ
function clsTrida($vl1, $vl2) {
 $this->vlastnost1=$vl1;
 $this->vlastnost1=$vl2;
}
//funkce vypis() vypφÜe hodnoty obou vlastnostφ
function vypis() {
 echo "1. vlastnost: ".$this->vlastnost1."<br />";
 echo "2. vlastnost: ".$this->vlastnost2."<br />";
 }
}
?>

Nejprve si ukß₧eme postup ulo₧enφ objektu do souboru:

<?
/*
Soubor "a.php"
V prvnφm souboru nßÜ objekt zeserializujeme a ulo₧φme ho do souboru "data"
*/

//naincludujeme soubor s pokusnou t°φdou
include ('trida.php');
//vytvo°φme instanci pokusnΘ t°φdy a nastavφme vlastnostφ, kterΘ se zeserializujφ
$objekt = new clsTrida ("Vlastnost 1", "Vlastnost 2");
//zde vytvo°φme byte °et∞zec
$bytestring=serialize($objekt);
//otev°eme soubor a byte °et∞zec do n∞j ulo₧φme
$f = fopen("data", "w"); //vytvo°enφ a otev°enφ souboru pro zßpis
fputs($f, $bytestring); //ulo₧enφ bytestringu do souboru
fclose($f); //zav°enφ souboru
?>

V souboru s unserializacφ se ji₧ nevytvß°φ ₧ßdnß instance, proto₧e unserializovan² objekt je schopen sßm poznat svoji t°φdu. Ta ale ve skriptu definovßna b²t musφ.

<?
/*
Soubor "b.php"
V druhΘm souboru otev°eme soubor "data", kde je serializovan² objekt a unserializujeme ho
*/

//naincludujeme soubor s pokusnou t°φdou
include ("trida.php");
//otev°enφ souboru a p°eΦtenφ jeho obsahu
$bytestring = implode("", @file("data"));
$UnserializovanyObjekt=unserialize($bytestring);
//nechßme si vlastnosti objektu vypsat, abyste vid∞li, ₧e ulo₧enφ do souboru "p°e₧ili"
$UnserializovanyObjekt->Vypis();
?>

P°edßnφ objektu pomocφ session je v podstat∞ stejnΘ jako p°i uklßdßnφ do souboru, jen zde mφsto funkcφ pro ulo₧enφ souboru pou₧ijeme funkce pro p°edßvßnφ session:

<?
/*
Soubor "sesstrida1.php"
*/

session_start(); //nastartovani session
//naincludujeme soubor s pokusnou t°φdou
include ("trida.php");
//vytvo°φme instanci pokusnΘ t°φdy
$objekt = new clsTrida ("Vlastnost 1", "Vlastnost 2");
//zde vytvo°φme byte °et∞zec
$bytestring=serialize($objekt);
//vytvo°enφ session
$_SESSION["serializovanyobjekt"]=$bytestring;
//napφÜeme odkaz na dalÜφ strßnku
echo '<a href="sesstrida2.php">DalÜφ strßnka</a>';
?>
<?
/*
Soubor "sesstrida2.php"
*/

session_start(); //nastartovani session
//naincludujeme soubor s pokusnou t°φdou
include ("trida.php");
//zde unserializujeme byte °et∞zec
$UnserializovanyObjekt=unserialize($_SESSION["serializovanyobjekt"]);
//op∞t si vypφÜeme hodnoty obou vlastnostφ
$UnserializovanyObjekt->vypis();
?>

MagickΘ funkce __sleep() and __wakeup()

P°i (un)serializaci mohou b²t velmi u₧iteΦnΘ "magickΘ" funkce __sleep() a __wakeup(), kterΘ se umis¥ujφ do definice t°φdy. Funkce __sleep() se spustφ na zaΦßtku serializace. M∙₧eme do nφ vlo₧it k≤d nap°φklad pro ukonΦenφ spojenφ s databßzφ. D∙le₧itΘ je, aby funkce __sleep() vrßtila pole, kterΘ obsahuje prom∞nnΘ objektu, ze kter²ch se vytvo°φ byte °et∞zec. Naproti tomu funkce __wakeup() se spustφ na zaΦßtku unserializace. Lze ji vyu₧φt nap°φklad pro obnovenφ spojenφ s databßzφ.

V nßsledujφcφm p°φkladu si vytvo°φme t°φdu, kterß si pamatuje datum svΘ serializace a p°i unserializaci tuto hodnotu vypφÜe. Byte °et∞zec v tomto p°φkladu budeme uklßdat do souboru. Funkce get_object_vars() byla popsßna v p°edchozφm Φlßnku.

<?
/*
Soubor trida.php ve kterΘm je definice pokusnΘ t°φdy
*/
class clsTrida {

 //funkce vypis() vypφÜe hodnoty obou vlastnostφ
 function vypis() {
  echo "1. vlastnost: ".$this->vlastnost1."<br />";
  echo "2. vlastnost: ".$this->vlastnost2."<br />";
 }

 function __sleep() {

  //do vlastnosti "datum" ulo₧φme aktußlnφ datum = datum serializace
  $this->datum=Date("Y-m-d");

  //vytvo°φme pole vlastnostφ objektu. Jestli₧e n∞jakou vlastnost vynechßme, nebude serializovanß.
  $vlastnosti = get_object_vars($this);

  foreach ($vlastnosti as $klic => $hodnota) {
   $pole[] = $klic;
  }

  //vrßcenφ pole vlastnostφ t°φdy
  return $pole;
 }

 function __wakeup() {

  //jestli₧e jsme ulo₧ili datum, tak ho vypφÜeme
  if (isset($this->datum)) {
    echo "Datum serializace: ".$this->datum."<br />";
  }
 }

}
?>

V²Üe uveden² p°φklad je pouze jednoduchou ukßzkou magick²ch funkcφ a velkΘ praktickΘ vyu₧itφ asi nemß. V nßsledujφcφm p°φkladu si tedy vytvo°φme t°φdu, kterß vyu₧φvß databßzi. Funkce __sleep() v tomto p°φpad∞ spojenφ s nφ ukonΦφ a vrßtφ ·daje pot°ebnΘ k p°ihlßÜenφ do databßze, funkce __wakeup() spojenφ op∞tovn∞ navß₧e. V²hody tohoto postupu jsou z°ejmΘ.

<?
/*
Soubor trida.php ve kterΘm je definice pokusnΘ t°φdy
*/
class clsTrida {

 //konstruktor t°φdy
 function clsTrida($server,$uzivatel,$heslo,$databaze){

  $this->datserver=$server;
  $this->datuzivatel=$uzivatel;
  $this->datheslo=$heslo;
  $this->datjmeno=$databaze;

   $this->Pripojeni();
 }

 //navß₧e spojeni s databßzφ
 function Pripojeni() {
  MySQL_Connect($this->datserver,$this->datuzivatel,$this->datheslo);
  MySQL_Select_DB($this->datjmeno); //vybrßnφ databßze
 }

 //funkce vypis() vypφÜe obsah tabulky; je zde samoz°ejm∞ pouze pro demonstraci prßce s databßzφ
 function Vypis() {
  $dotaz=MySQL_Query("SELECT sloupec1, sloupec2 FROM tabulka") or Die(MySQL_Error());
  while ($data = MySQL_Fetch_Array($dotaz)){
   echo $data[sloupec1]." ".$data[sloupec2];
   echo "<br />";
  }
 }

 //ukonΦφme spojenφ s databßzφ a vrßtφme p°ihlaÜovacφ ·daje
 function __sleep() {

  MySQL_Close();

  //vytvo°φme pole vlastnostφ objektu
  $vlastnosti = get_object_vars($this);

  foreach ($vlastnosti as $klic => $hodnota) {
   $pole[] = $klic;
  }

  //vrßcenφ pole vlastnostφ t°φdy
  return $pole;
 }

 //op∞t navß₧eme spojenφ s databßzφ
 function __wakeup() {
  $this->Pripojeni();
 }

}
?>
<?
/*
Soubor databaze1.php, kde dochßzφ k serializaci
*/
include("trida.php");

$objekt = new clsTrida("Localhost", "MojeJmeno", "TajneHeslo", "PokusnaDAT");
//vypφÜeme obsah tabulky
$objekt->vypis();

$bytestring=serialize($objekt);
//otev°eme soubor a byte °et∞zec do n∞j ulo₧φme
$f = fopen("data", "w"); //vytvo°enφ a otev°enφ souboru pro zßpis
fputs($f, $bytestring); //ulo₧enφ bytestringu do souboru
fclose($f); //zav°enφ souboru
?>
<?
/*
Soubor databaze2.php, kde dochßzφ k unserializaci
*/
//naincludujeme soubor s pokusnou t°φdou
include ("trida.php");
//otev°enφ souboru a p°eΦtenφ jeho obsahu
$bytestring = implode("", @file("data"));
$UnserializovanyObjekt=unserialize($bytestring);
//vypφÜeme obsah tabulky
$UnserializovanyObjekt->Vypis();
?>

JeÜt∞ se musφm zmφnit o jednom bezpeΦnostnφm nedostatku zmφn∞nΘho p°φkladu. Pokud p°edßvßte byte °et∞zec v session, data jsou zde v nezaÜifrovanΘ form∞, tak₧e je m∙₧e p°φpadn² ·toΦnφk velmi jednoduÜe odchytit a pokud znß strukturu byte °et∞zce (kterß nenφ nijak slo₧itß, zkuste si otev°φt soubor, do kterΘho uklßdßte byte °et∞zec, v libovolnΘm textovΘm editoru), nebude problΘm si je p°eΦφst a dostat se tak do naÜφ databßze. Podmφnkou je tedy pou₧itφ nap°φklad zabezpeΦenΘho protokolu SSL.

VÜechny p°φklady z Φlßnku si m∙₧ete stßhnout. A₧ na p°φklad s databßzφ, kde si musφte upravit p°ihlaÜovacφ ·daje, budou fungovat bez problΘm∙.

Heller, Petr (16. 4. 2004)
webmaster PeHe.net