Serializacja danych w PHP

MatheW

Serializacja danych w PHP

1 Serializacja danych w PHP
     1.1 Serializacja obiektów
          1.1.1 Magiczne metody __sleep() i __wakeup()
     1.2 Serializacja a sesje
     1.3 Gdzie możemy wykorzystać serializację?

Na początku standardowa, książkowa definicja serializacji (oczywiście z Wikipedii):

Serializacja - w programowaniu komputerów proces przekształcania obiektów, tj. instancji określonych klas, do postaci szeregowej, czyli w strumień bajtów, z zachowaniem aktualnego stanu obiektu. Serializowany obiekt może zostać utrwalony w pliku dyskowym, przesłany do innego procesu lub innego komputera poprzez sieć. Procesem odwrotnym do serializacji jest deserializacja. Proces ten polega na odczytaniu wcześniej zapisanego strumienia danych i odtworzeniu na tej podstawie obiektu klasy wraz z jego stanem bezpośrednio sprzed serializacji.

Serializacja jest bardzo przydatna - dzięki niej możemy zapisać stan obiektu, tablicy czy innej zmiennej do specjalnego ciagu , a następnie przykładowo do bazy danych, czy pliku, lub nawet sesji - a w przyszłości z ów zmiennej skorzystać, co przydaje się np. przy cachowaniu. Innymi słowy jest to zamiana elementu języka programowania w gołe bajty.

Do serializacji możemy użyć naszego własnego mechanizmu, biblioteki do serializacji, lub skorzystać z mechanizmu PHP. W tym artykule zaprezentowana jest metoda dostarczona przez PHP. Możesz napisać swój system serializacji lub skorzystać z innej biblioteki.

Dzięki temu zapisowi możemy w innym skrypcie za pomocą deserializacji wczytać ten obiekt, posiadający wszystkie właściwości, które nadaliśmy mu przed zapisem do zmiennej tekstowej. Niestety serializacja i deserializacja domyślna w PHP zadziała tylko w PHP. Do serializacji pomiędzy językami i systemami użyj innych, bardziej okrojonych w możliwości formatów, np JSON, XML.

Funkcjami za to odpowiedzialnymi są serialize() oraz unserialize(), ich użycie prezentuje przykład:

$array= ['value1', 'value2'];
$serializedArray = serialize($array); # serializacja tablicy
$unserializedArray = unserialize($serializedArray); # deserializaja
var_dump($unserializedArray);

Na początku deklarujemy tablicę posiadającą dwa elementy. Następnie serializujemy tablicę funkcją serialize() i przypisujemy do zmiennej $serializedArray. Gdybyśmy ją wyświetlili, ujrzelibyśmy:

a:2:{i:0;s:6:"value1";i:1;s:6:"value2";}

Deserializacja nastepuje za pomocą funkcji unserialize(). Odzyskaną tablicę wyświetlamy i oto co widzimy:

Array
(
    [0] => value1
    [1] => value2
)

Struktura zmiennej się nie zmieniła - jest to praktycznie ta sama zmienna!

Serializacja obiektów

Serializacja obiektów nie różni się w zasadzie od serializacji innych zmiennych:

class Serialization {
    private $field;

    /**
     * Zapisuje zmienna
     */
    public function setField($value) {
        $this->field= $value;
    }

    /**
     * Zwraca zmienna
     */
    public function getField() {
        return $this->field;
    }
}

$serialization= new Serialization(); #tworzymy obiekt
$serialization->setField('value'); #przypisujemy polu wartość value
$serializedObject= serialize($serialization); #serializujemy
$unserializedObject= unserialize($serializedObject); #deserializujemy

echo $unserializedObject->getField(); #wyswietlamy pole deserializowanego obiektu

Jak widzimy mamy tu klasę Serialization, z polem $field. Po utworzeniu obiektu zapisujemy polu wartosc value, następnie poddajemy serializacji i deserializacji i wyświetlamy pole - jest to ta sama wartość!

Jednak gdy chcemy użyć serializowanej zmiennej w innym skrypcie należy pamiętać, że musi być w nim zawarty kod klasy tego obiektu!

Magiczne metody __sleep() i __wakeup()

Obiekty mają tą dogodność, że mogą dowolnie zmieniać własne dane podczas serializacji i deserializacji, za pomocą metod magicznych.

Pierwszą z nich jest metoda o nazwie __sleep(). Wywoływana jest ona automatycznie podczas serializacji obiektu i może służyć do manipulowania danymi, które muszą zostać zmodyfikowane przed poddaniem obiektu serializacji. Funkcja powinna zwrócić tablicę, której elementy będą nazwami pól obiektu, które mają zostać w nim zachowane.

Funkcja __wakeup() jest wywoływana przy deserializacji obiektu. Może być przydatna np. do ponownego połączenia z bazą, otwarcia pliku itd.

class Serialization {
    private $time;

    public function __sleep() {
        $this->time= time(); #zapis aktualnego czasu
        return array_keys(get_object_vars($this));
    }

    public function __wakeup(){
        echo $this->time; #wyswietlamy czas serializacji
    }
}

$serialization= new Serialization(); #tworzymy obiekt
$serializedObject= serialize($serialization); #serializujemy
$unserializedObject= unserialize($serializedObject); #deserializujemy

Efektem kodu jest wyświetlenie uniksowego znacznika czasu, w którym nastąpiła serializacja. W przykładzie podczas serializacji zapisujemy czas, w którym obiekt został jej poddany, a podczas deserializacji jest on wyświetlany.

Warto zwrócić uwagę na linię

return array_keys(get_object_vars($this));

get_object_vars() zwraca nam tablice składająca sie z nazw pól obiektu i ich wartości, a array_keys zwraca tejże tablicy indeksy, przez co dostajemy nazwy wszystkich pól obiektu - są one niezbedne do poprawnej serializacji.

Serializacja a sesje

Sesje są ściśle związane z serializacją - podczas zapisu zmiennej do sesji, jest ona przechowywana właśnie jako serializowany string. Podczas rozpoczecia sesji obiekty są deserializowane, a podczas zakończenia parsowania kodu zmienne sesyjne są poddawane serializacji.

Najlepiej zobrazuje to przykład, który w końcu zaprezentuje praktyczne wykorzystanie serializacji:

session_start();

class DBConnector {
    public $connection, $user, $pass, $host;

    public function connect() {
        if (!$this->connected)
            $this->db= new mysqli($this->host, $this->user, $this->pass); #polaczenie sie z baza danych
    }
    /**
     * Stwier    dza czy jestesmy juz polaczeni z baza danych
     */
    public function connected() {
        return $this->db instanceof mysqli; #jezeli zmienna jest implementacja klasy mysqli
    }

    public function __sleep() {
        $this->db->close(); #rozlaczenie
        $this->db= null; #wyczyszczenie zmiennej
        echo 'Rozłączono z bazą danych';
        return array_keys(get_object_vars($this));
    }

    public function __wakeup() {
        echo $this->connect(); #laczymy
    }
}

$connector= new DBConnector();
$connector->host= 'localhost';
$connector->user= 'user';
$connector->pass= 'password';
$connector->connect();

$_SESSION['connector']=$connector;

W przykładzie mamy klasę łączącą się z bazą danych, oraz przechowującą identyfikator połączenia. Po zapisie jej do zmiennej sesyjnej oraz zakończeniu skryptu zostaje ona poddana serializacji, czego efektem będzie rozłączenie się z bazą danych oraz wyświetlenie komunikatu.

Teraz gdy odpalimy inny plik, będziemy mogli skorzystać z tych danych i korzystać z bazy danych:

require_once('DBConnector.class.php');
session_start();
if($_SESSION['connector']->connected()) echo 'Jest połączenie z bazą danych';

Pamiętajmy by załączyć klasę, której implementacją jest obiekt przed rozpoczęciem sesji - inaczej wyświetli nam się błąd.

Gdzie możemy wykorzystać serializację?

Serializacja przydaje się między innymi podczas cachowania danych - wielokrotny pobór danych z bazy danych,a następnie ich przetwarzanie jest powolne, a wraz z obciążeniem skryptu przez użytkowników wzrasta jeszcze bardziej. Zapisując serializowane dane do pliku i korzystanie z nich znacznie usprawni prace skryptu. Serializacja przyda się pewnie też przy rozbudowanych formularzach, składających się z kilku stron - wtedy korzystamy z obiektów w sesji.

PHP

0 komentarzy