PHP APC Tutorial ? cz.3: User Variable Cache ? Czyli optymalizacja skryptów PHP

W pierwszym odcinku niniejszej serii poznaliśmy się z przeznaczeniem oraz sposobem instalacji APC na platformach Windows oraz Linux. W drugim odcinku poznaliśmy panel administracyjny APC. W tej części zapoznamy się z User Cache, czyli cache’owaniem zmiennych w kodzie skryptów PHP.

Zacznijmy od fragmentu kodu.

<?php
if( $quote = apc_fetch( 'zmienna1' )) {
    echo $quote;
} else {
    $quote = "Hello World";
    echo $quote;
    apc_add( 'zmienna1', $quote, 60 );
}
?>

Kod jest dosyć prosty. Najpierw za pomocą funkcji apc_fetch sprawdzamy, czy dana zmienna znajduje się w cache’u. Jeżeli znajduje się, to ją wyświetlamy. Jeżeli pożądanej przez nas zmiennej tam nie ma, to nadajemy jej wartość, wyświetlamy oraz za pomocą funkcji apc_add umieszczamy ją w cache’u na czas 60 sekund. Obie funkcje wykorzystują identyfikator do nazwania zmiennej – u nas zmienna1.

Czas na jaki umieszczamy daną zmienną w cache’u (zwany TTL (Time To Live) lub Timeout) ma czasami decydujące znaczenie. Nie widać tego w powyższym przykładzie, gdyż wartość zmiennej jest wpisywana tam na sztywno, ale gdyby owa zmienna ulegała zmianom (np. była pobierana z bazy danych etc.) to nowa wartość byłaby pozyskiwana co czas określony w TTL, gdyż w pozostałych przypadkach byłaby pobierana nie aktualna wartość, ale wartość umieszczona w cache’u.

Wspomnieć jeszcze należy, że powyższy przykład pokazuje jedynie zastosowanie funkcji apc_fetch oraz apc_add. Nie cache’ujemy zmiennych, którym nadajemy wartość wprost z kodu. Do cache’owania nadają się zmienne, których wartość jest obliczana (z pomocą jakiegoś algorytmu) lub z zewnętrznych źródeł (np. z bazy danych) i w ten sposób przyśpieszamy pracę skryptu.

W panelu administracyjnym możemy obejrzeć statystyki dotyczące tej zmiennej, co zostało opisane w poprzedniej części tutoriala. W podobny sposób możemy cache’ować tablice.

<?php
if( $quote = apc_fetch( 'tablica1' ));
else {
    $quote = array( 'a', 'b', 'c', 'd' );
    apc_add('tablica1', $quote, 60);
}
echo $quote[0];
echo $quote[1];
echo $quote[2];
echo $quote[3];
?>

Możemy również cache’ować obiekty. W poniższym przykładzie użyta jest nowa funkcja apc_exists, która sprawdza, czy dana zmienna o danym identyfikatorze występuje w cache’u.

<?php
class User {
    private $name;
 
    function setName($value) {
        $this->name = $value;
    }
 
    function getName() {
        return $this->name;
    }
}
 
if( apc_exists( 'oUser' )) {
    $obj = apc_fetch( 'oUser' );
} else {
    $obj = new User();
    $obj->setName( 'Adam' );
    apc_add( 'oUser', $obj, 60 );
}
 
echo $obj->getName();
?>

Alternatywnym do powyższego sposobem cache’owania obiektów jest ich wcześniejsze zserialiowanie za pomocą funkcji serialize oraz późniejsze odserializowanie za pomocą funkcji unserialize. Możemy również oczywiście cache’ować referencje.

<?php
class Date {
    public $year = 2000;
    public function &getYear() {
    return $this->year;
}
 
$obj = new Date;
$o = &$obj->getYear();    // 2000
$obj->year = 2011;    // 2011
apc_add( 'ref', $o, 60);    //2011
$ref = apc_fetch( 'ref' );    //2011
$ref++;    //2012
echo $ref;    // 2012
?>

W powyżej zaprezentowane sposoby nie można jednak cache’ować funkcji anonimowych (tzw. closures), gdyż powoduje to błąd. Innymi słowy, nie możemy wykonać poniższego kodu.

$area = function($length, $width) {
    return $length * $width;
};
apc_store('area', $area);

Powyższy przykład przedstawia nową funkcję apc_store. W odróżnieniu od apc_add, która nie zastępowała zmiennej, gdy ta już istniała w cahe’u, apc_store zastępuje zmienną, gdy ta już tam jest. Aby poradzić sobie z problemem niemożności zserializowania funkcji anonimowej, należy zastosować specjalną klasę opisaną przez Pana co się zowie Jeremy Lindblom opisaną tutaj, a dostępną do ściągnięcia tutaj. Wtedy można tego dokonać w następujący sposób.

<?php
include 'SuperClosure.class.php';
 
if( !apc_exists( 'area' )) {
    $area = new SuperClosure(
        function($length, $width) {
            return $length * $width;
        }
    );
    apc_store( 'area', $area );
} else {
    $area = apc_fetch('area');
}
 
echo $area(6,5);
?>