Duże aplikacje napisane w oparciu o Zend Framework zazwyczaj zawierają duży plik konfiguracyjny, niejednokrotnie zapisany w postaci XML. Im większy plik konfiguracyjny, tym wolniej jest on tłumaczony do postaci rozumianej przez PHP. Z tego właśnie względu warto zastosować cache’owanie konfiguracji naszej aplikacji.
Wbrew pozorom nie jest to proces skomplikowany i wymaga jedynie kosmetycznych poprawek w projekcie. Wystarczy utworzyć klasę dziedziczącą po Zend_Application, a w niej przesłonić metodę odpowiedzialną za wczytanie pliku konfiguracyjnego.
class Batman_Application extends Zend_Application
{
/**
* @var null|Zend_Cache_Core
*/
private $_cache = null;
/**
* @var null|string
*/
private $_configFile = null;
/**
* @param string $file
*/
public function setConfigFile($file)
{
$this->_configFile = $file;
}
/**
* @return null|string
*/
public function getConfigFile()
{
if($this->_configFile === null) {
$this->_configFile = APPLICATION_PATH . '/configs/application.ini';
}
return $this->_configFile;
}
/**
* @param Zend_Cache_Core $cache
*/
public function setCache(Zend_Cache_Core $cache)
{
$this->_cache = $cache;
}
/**
* @return null|Zend_Cache_Core
*/
public function getCache()
{
if($this->_cache === null) {
$this->_cache = Zend_Cache::factory(
'File',
'File',
array(
'master_files' => array($this->getConfigFile()),
'automatic_serialization' => true,
'lifetime' => null
),
array(
'cache_dir' => APPLICATION_PATH . '/data/cache'
)
);
}
return $this->_cache;
}
protected function _loadConfig($file)
{
if($this->_configFile === null) {
$this->setConfigFile($file);
}
$cache = $this->getCache();
$config = $cache->load('config_cache');
if(!$config) {
$config = parent::_loadConfig($file);
$cache->save($config, 'config_cache');
}
return $config;
}
}
Powyższy przykład korzysta z cache’u File, który automatycznie się odświeża, gdy w pliku konfiguracyjnym zostaną wprowadzone zmiany. Nic nie stoi na przeszkodzie aby to zmienić na dowolny inny cache, podobnie z resztą jak backend. Jeśli mamy dostęp do Memcached lub APC, o wiele lepiej będzie tam przechowywać cache, niż w pliku.
Jedyną zmianę jaką należy wprowadzić w już istniejącym kodzie, to modyfikacja pliku index.php, w którym zmieniamy nazwę klasy Zend_Application, na klasę przez nas utworzoną.
// reszta pliku index.php
require_once 'Zend/Application.php';
require_once 'Batman/Application.php';
// Create application, bootstrap, and run
$application = new Batman_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap()
->run();
Zanim jednak zdecydujemy się na cache’owanie konfiguracji, lepiej upewnić się, że taka zmiana nam się opłaci. Może się bowiem tak zdarzyć, iż parsowanie pliku z konfiguracją jest szybsze niż sprawdzenie czy cache jest aktualny.
Pliki z kodem źródłowym znajdziecie na Githubie.
Aktualizacja
W komentarzach melkrom słusznie zauważył, iż plik konfiguracyjny powinien być pobierany ze zmiennej przekazanej do metody _loadConfig. Dodałem taką możliwość do klasy. Wpis oraz projekt na Githubie zostały zaktualizowane do nowej wersji.
Zgadzam się w 100%.
W projekcie nad którym pracuję, parsowanie konfiguracji aplikacji zajmowało ponad 10% czasu wykonania skryptu. Przerobienie aplikacji, aby config ten cachowany był w APC zajmuje pare minut.
Jak widać stosunek przyrostu wydajności, do nakładu pracy jest olbrzymi, dlatego warto się tym zainteresować.
BTW Trzeba też pamiętać o flush’owaniu cache’u przy deploy’u applikacji.
Ogólnie wolałbym skorzystać z cache managera zendowego – i zrobić to w zewnętrznej klasie by móc jej używać wszędzie i dla innych plików konfiguracyjnych – takie podejście jest oczywiście spoko ale przyczepię się do jednej rzeczy
'master_files' => array(APPLICATION_PATH . '/configs/application.ini'),
Skąd założenie że właśnie tak trzymam plik konfiguracyjny? :]
@Kamil Nowak
Usunięciem cache’u zajmuje się zazwyczaj skrypt do deploy’u aplikacji, więc na szczęście nie trzeba sobie tym głowy zawracać
@melkrom
Lokalizację pliku konfiguracyjnego przyjąłem domyślną, generowaną przez framework. Jeśli plik jest zlokalizowany w innym miejscu lub chcemy skorzystać z innego cache’u, wystarczy skorzystać z metody setCache (tutaj index.php nieco spuchnie).
@melkrom
Ponieważ tak masz w index.php. Jak sobie tam zmieniłes to musisz pamiętać, żeby gdziekolwiek indziej zmieniac i/lub dac to w jakąś zmienną.
@SebaZ
Nie muszę – ponieważ plik konfiguracyjny ładujesz raz, defaultowo w index.php.
@batman, a czy w metodzie getCache nie lepiej by było dać ścieżkę do pliku z _loadCache i byłoby po sprawie
?
@melkrom
W sumie racja. Zmienię to pod wieczór i uaktualnię wpis.
Jedna mała uwaga. W testach w testSetInvalidDataTemplate zamiast bloku try catch powinno być: $this->setExpectedException(‘Batman_Notification_Exception’);
Pozdrawiam
@melkrom
Wpis (i klasa) zaktualizowany.
@Darek
Niestety nie mam teraz jak tego sprawdzić, ale jestem prawie pewien,że zdecydowałem się na takie coś z jakiegoś konkretnego powodu.
Fajny pomysł na przyspieszenie, ale wydaje mi się, że kopa dostanie jeśli cache przeniesiemy do pamięci i wyłączymy sprawdzanie ważności (np: na produkcji).