На днях я получил задание реализовать кэширование в моделях. В обсуждениях с коллегами родилась довольно интересная, на мой взгляд, идея, которую я бы хотел выставить на ваш суд.
Реализация идеи на Zend Framework:
1. Все модели наследуются от нашего класса My_Db_Table_Abstract:
2. Который в свою очередь наследуется от Zend_Db_Table_Abstract:
3. В базовом для всех моделей классе My_Db_Table_Abstract описываем магический метод __call():
Теперь у нас появилась возможность использовать методы моделей двумя способами:
1. Не кэшируя, просто обратившись к методу:
2. Кэшируя, дописав к имени метода префикс «cached_»:
Во втором случае, обращаясь к несуществующему методу, срабатывает метод __call(), в котором проверяется наличие закэшированного результата. Если результат выполнения метода закеширован — используется кэш, если нет — вызывается метод и полученный результат кэшируется.
Некоторые нюансы:
1. Для того чтобы кэш результата выполнения метода был разный при отличающихся параметрах метода я сделал хеширование параметров:
2. Имя кэшируемого файла имеет вид model_ИмяКласса_ИмяМетода_ХэшПараметров, исключая возможность совпадения.
3. Кэш помечается тегами 'model', 'className' и 'methodName', которые позволяют легко манипулировать очисткой. Вот для примера очистка кэша всех методов модели My_Model_ModuleSettings:
Очень хотелось бы услышать ваши комментарии… Какие вы видите недостатки этого способа? Имеет ли он право на жизнь?
Реализация идеи на Zend Framework:
1. Все модели наследуются от нашего класса My_Db_Table_Abstract:
class My_Model_ModuleSettings extends My_Db_Table_Abstract
2. Который в свою очередь наследуется от Zend_Db_Table_Abstract:
class My_Db_Table_Abstract extends Zend_Db_Table_Abstract
3. В базовом для всех моделей классе My_Db_Table_Abstract описываем магический метод __call():
public function __call($name, $arguments)
{
/** If call cached method */
if (preg_match('/^cached_(.+)$/', $name, $methodName)&&method_exists($this,$methodName[1])) {
/** Get cache instance */
$cache = My_Cache::getInstance();
/** Get arguments hash */
$argHash = md5(print_r($arguments, true));
/** Get model class name */
$className = get_class($this);
/** If method result don't cached */
if (!$result = $cache->load('model_'.$className.'_'.$methodName[1].'_'.$argHash)) {
$result = call_user_method_array($methodName[1], $this, $arguments);
$cache->save($result,
'model_'.$className.'_'.$methodName[1].'_'.$argHash,
array('model',$className,$methodName[1]));
}
return $result;
} else {
/** Generate exception */
throw new Exception('Call to undefined method '.$name);
}
}
Теперь у нас появилась возможность использовать методы моделей двумя способами:
1. Не кэшируя, просто обратившись к методу:
$result = $this->_life->getAll('Now!!');
2. Кэшируя, дописав к имени метода префикс «cached_»:
$result = $this->_life->cached_getAll('Now!!');
Во втором случае, обращаясь к несуществующему методу, срабатывает метод __call(), в котором проверяется наличие закэшированного результата. Если результат выполнения метода закеширован — используется кэш, если нет — вызывается метод и полученный результат кэшируется.
Некоторые нюансы:
1. Для того чтобы кэш результата выполнения метода был разный при отличающихся параметрах метода я сделал хеширование параметров:
$argHash = md5(print_r($arguments));
Это, пожалуй, самый неоднозначный момент, т.к. я не могу точно сказать, как это может повлиять на производительность (при тестировании увеличение нагрузки замечено не было). При этом можно использовать разные функции хеширования(md5(),sha1()..) и разные способы приведения массива переменных к строковому типу (print_r(), var_dump(), implode()).2. Имя кэшируемого файла имеет вид model_ИмяКласса_ИмяМетода_ХэшПараметров, исключая возможность совпадения.
3. Кэш помечается тегами 'model', 'className' и 'methodName', которые позволяют легко манипулировать очисткой. Вот для примера очистка кэша всех методов модели My_Model_ModuleSettings:
$cache->clean(
Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG,
array('My_Model_ModuleSettings')
);
Очень хотелось бы услышать ваши комментарии… Какие вы видите недостатки этого способа? Имеет ли он право на жизнь?