
Исходник теста:
<?
header("Content-type: text/html; charset=utf-8");
class Cache {
protected $module; // Ссылка на модуль
protected $action; // Название действия
public function setModule($module) {
$this->module = $module;
}
public function setAction($action) {
$this->action = $action;
}
public function getCacheFileName() {
return "cache/".get_class($this)."_".$this->module->getName()."_".$this->action.".txt";
}
public function isCacheValid() {
clearstatcache();
return file_exists($this->getCacheFileName()); // тупо проверяем, что файл существует
}
}
// Будем тестировать 3 способа кэширования:
class docCache extends Cache {
// 1. Кэшируем документ, загружаем/выгружаем данные через importNode
public function getNode($doc, $module, $action) {
if ($this->isCacheValid()) {
$cache_doc = new DOMDocument('1.0', 'utf-8');
$cache_doc->load($this->getCacheFileName());
$node = $doc->importNode($cache_doc->documentElement, true);
return $node;
}
}
public function saveNode($node) {
$doc = new DOMDocument('1.0', 'utf-8');
$cache_node = $doc->importNode($node, true);
$doc->appendChild($cache_node);
$doc->save($this->getCacheFileName());
}
}
class fragmentCache extends Cache {
// Кэшируем documentFragment
// Вот тут не совсем все точно - кэш возвращает documentFragment вместо DOMElement. Ну да не важно :)
public function getNode($doc) {
if ($this->isCacheValid()) {
$f = $doc->createDocumentFragment();
$xml_data = file_get_contents($this->getCacheFileName());
$f->appendXML($xml_data);
return $f;
}
}
public function saveNode($node) {
$node_xml = $node->ownerDocument->saveXML($node);
$file = fopen($this->getCacheFileName(), 'w');
fputs($file, $node_xml);
fclose($file);
}
}
class structureCache extends Cache {
// Кэшируем собранную структуру через serialize();
// При восстановлении перегоняем обратно в XML, используя соотв. метод модуля
public function getNode($doc) {
if ($this->isCacheValid()) {
$structure = unserialize(file_get_contents($this->getCacheFileName()));
$node = $doc->createElement($this->module->getName());
call_user_func(array($this->module, $this->action."_structure2XML"), $structure, $node);
return $node;
}
}
public function saveStructure($structure) {
$file = fopen($this->getCacheFileName(), 'w');
fputs($file, serialize($structure));
fclose($file);
}
}
class testModule {
private $Cache = false;
// Эти данные как-будто берутся из БД :)
// Предполагается, что кэширование актуально
private $menu = array(
'about' => array(
'name' => 'О фирме',
'id' => 1,
'children' => array(
'policy' => array('name' => 'Политика компании', 'id' => 2),
'history' => array('name' => 'Наша история', 'id' => 3),
'staff' => array(
'name' => 'Сотрудники',
'id' => 4,
'children' => array(
'drivers' => array('name' => 'Водители', 'id' => 5),
'managers' => array('name' => 'Менеджеры', 'id' => 6)
)
)
)
),
'service' => array(
'name' => 'Услуги',
'id' => 7,
'children' => array(
'rent' => array(
'name' => 'Аренда автобуса',
'id' => 8
),
'support' => array(
'name' => 'Транспортная поддержка',
'id' => 9
),
'meeting' => array(
'name' => 'Встреча в аэропорту',
'id' => 10
)
)
),
'press' => array(
'name' => 'Пресс-центр',
'id' => 11,
'children' => array(
'news' => array('name' => 'Новости компании', 'id' => 12),
'industry-news' => array('name' => 'Новости индустрии', 'id' => 13)
)
),
'buses' => array(
'name' => 'Наши автобусы',
'id' => 14,
'children' => array(
'lux' => array(
'name' => 'Автобусы люкс-класса',
'id' => 15,
'children' => array(
'mercedes-1' => array('name' => "Mercedes Benz 1", 'id' => 16),
'mercedes-2' => array('name' => "Mercedes Benz 2", 'id' => 17),
)
),
'busines' => array(
'name' => 'Автобусы бизнес-класса',
'id' => 18,
'children' => array(
'man-1' => array('name' => "Man 1", 'id' => 19),
'man-2' => array('name' => "Man 2", 'id' => 20),
)
)
)
),
'about1' => array(
'name' => 'О фирме',
'id' => 1,
'children' => array(
'policy' => array('name' => 'Политика компании', 'id' => 2),
'history' => array('name' => 'Наша история', 'id' => 3),
'staff' => array(
'name' => 'Сотрудники',
'id' => 4,
'children' => array(
'drivers' => array('name' => 'Водители', 'id' => 5),
'managers' => array('name' => 'Менеджеры', 'id' => 6)
)
)
)
),
'about2' => array(
'name' => 'О фирме',
'id' => 1,
'children' => array(
'policy' => array('name' => 'Политика компании', 'id' => 2),
'history' => array('name' => 'Наша история', 'id' => 3),
'staff' => array(
'name' => 'Сотрудники',
'id' => 4,
'children' => array(
'drivers' => array('name' => 'Водители', 'id' => 5),
'managers' => array('name' => 'Менеджеры', 'id' => 6)
)
)
)
),
'about3' => array(
'name' => 'О фирме',
'id' => 1,
'children' => array(
'policy' => array('name' => 'Политика компании', 'id' => 2),
'history' => array('name' => 'Наша история', 'id' => 3),
'staff' => array(
'name' => 'Сотрудники',
'id' => 4,
'children' => array(
'drivers' => array('name' => 'Водители', 'id' => 5),
'managers' => array('name' => 'Менеджеры', 'id' => 6)
)
)
)
),
'about4' => array(
'name' => 'О фирме',
'id' => 1,
'children' => array(
'policy' => array('name' => 'Политика компании', 'id' => 2),
'history' => array('name' => 'Наша история', 'id' => 3),
'staff' => array(
'name' => 'Сотрудники',
'id' => 4,
'children' => array(
'drivers' => array('name' => 'Водители', 'id' => 5),
'managers' => array('name' => 'Менеджеры', 'id' => 6)
)
)
)
)
);
public function getName() {
return "menu";
}
public function __construct() {
$this->doc = new DOMDocument('1.0', 'utf-8');
$this->root = $this->doc->appendChild($this->doc->createElement('root'));
}
public function setCache($Cache) {
if ($Cache !== false) {
$this->Cache = $Cache;
$this->Cache->setModule($this);
}
}
public function show_menu_getStructure() {
// Вот тут должны были бы быть нагрузочные махинации с БД
// Но нам лень их кодить.
// Для наглядности посчитаем факториал 5000 - это где-то 0.002 сек., но не забываем про 1000 итераций!
$int = 5000;
for($f=2;$int-1>1;$f*=$int--);
return $this->menu;
}
public function run($action) {
if ($this->Cache !== false) {
$this->Cache->setAction($action);
}
if ($this->Cache !== false && ($node = $this->Cache->getNode($this->doc, $this, $action))) {
// В нашем тесте кэш всегда возвращает нод
// файлы кэша уже записаны и актуальность не проверяется
$this->root->appendChild($node);
} else {
// А это как-будто проход без кэша (он уже сделан)
$node = $this->root->appendChild($this->doc->createElement($action));
// Сначала вытаскиваем данные, собираем структуру массивов/объектов
$structure = call_user_func(array($this, $action.'_getStructure'));
// А потом перегоняем ее в DOM
call_user_func(array($this, $action."_structure2XML"), $structure, $node);
// И кэшируем
if ($this->Cache !== false) {
if (method_exists($this->Cache, "saveNode")) { // для docCache и fragmentCache
$this->Cache->saveNode($node);
} else {
$this->Cache->saveStructure($structure); // для structureCache
}
}
}
}
// Эта штука рекурсивно превращает структуру массивов в XML
// при кэшировании через structureCache вызывается каждый раз
function show_menu_structure2XML($arr, $node) {
$doc = $node->ownerDocument;
foreach ($arr as $code => $props) {
$item = $node->appendChild($doc->createElement('item'));
$item->setAttribute('code', $code);
$item->setAttribute('name', $props['name']);
$item->setAttribute('id', $props['id']);
if (isset($props['children'])) {
$this->show_menu_structure2XML($props['children'], $item);
}
}
}
}
// Тест о 1000 прогонах
class Test {
private $names = array();
private $times = array();
private $max_time = 0;
function run($Cache) {
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
$m = new testModule();
$m->setCache($Cache);
$m->run('show_menu');
}
$elapsed = microtime(true) - $start;
$test_name = $Cache !== false ? get_class($Cache) : "noCache";
if ($elapsed > $this->max_time) {
$this->max_time = $elapsed;
}
$this->names []= $test_name;
$this->times []= $elapsed;
}
public function drawChart() {
// Эта магическая штука юзает гуглевый API для создания графиков
// См. http://code.google.com/apis/chart/
$proportion = 80 / $this->max_time;
$chart_url = array('names' => array(), 'data' => array());
foreach ($this->names as $key => $name) {
$time = $this->times[$key];
$chart_url['names'] []= urlencode($name. ", ".(round($time * 100) / 100)) . " s";
$chart_url['data'] []= round($time * $proportion * 10) / 10;
}
$colors = array('009900', '00cc66', 'ffcc00', 'cc0000');
$url = "http://chart.apis.google.com/chart?cht=bvg&chd=t:".join("|", $chart_url['data']);
$url .= "&chs=300x100&chdl=".join("|", $chart_url['names'])."&chco=".join(",", $colors);
?>
<img src="<?=$url?>" /><br />
<?
}
}
$max_time = 0;
// Запускаем тесты
$Test = new Test();
$Test->run(new fragmentCache());
$Test->run(new docCache());
$Test->run(new structureCache());
$Test->run(false);
// Показываем табличку с результатом
$Test->drawChart();
// Показываем исходник :)
echo "<b>Исходник теста:</b><br />";
$file_source = file_get_contents(__FILE__);
highlight_string($file_source);
// Типа все :)
?>