PHP pro pokročilé – znovu třídy a objekty

16. dubna 2004

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ů.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Předchozí článek fyzika1
Další článek listy.cz
Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *