Использование кэша в OpenCart позволяет сократить количество обращений к базе данных и увеличить быстродействие магазина, однако есть некоторые нюансы в работе самого кэша.
Управление кэшем происходит в файле ./system/library/cache.php, если обратить внимание на функцию get, то видно, что результат возвращается функцией php unserialize когда файл кэша есть и null, когда его нет. Это значит, что если в кэше сохранён результат пустой выборки в одной из моделей, то при очередном запросе кэша будет возвращён ноль.
Практически во всех моделях условие проверки кэша следующее:
$product_data = $this->cache->get( ...
if (!$product_data) { ...
что в корне неверно, так как содержание кэша, например, строка s:1:"0";, может сообщить модулю об его "отсутствии" и будет выполнено повторное обращение к базе.
Если создать новые категории, то это будет хорошо заметно. Товаров нет, каждый раз запрос из базы возвращает пустой результат и сохраняет его в кэше. В этом конкретном примере необходимо открыть файл ./catalog/model/catalog/product.php найти функцию getTotalProducts и в её теле заменить условие
if (!$product_data) { ...
на
if ($product_data === null) { ...
Теперь существование кэша будет правильно воспринято отдельно взятой функцией того или иного модуля. Исправления актуальны для всех версий OpenCart, включая последнюю v1.5.5.1.
Update
Работая с библиотекой кэша ./system/library/cache.php обратил внимание, что принцип выдачи кэшированной информации организован неверно. Модулю, запросившему кэш, может быть выдана устаревшая информация, если обратите внимание на код, то данные с файла считываются раньше проверки на время.
Дабы не создавать отдельную статью, приведу решение здесь. Функцию get() необходимо заменить на код ниже.
public function get($key) {
$data = null;
$files = glob(DIR_CACHE . 'cache.' . preg_replace('/[^A-Z0-9\._-]/i', '', $key) . '.*');
if ($files) {
for ($n=0, $lenght = count($files); $n < $lenght; $n++) {
$file = $files[$n];
$time = substr(strrchr($file, '.'), 1);
if ($time < time()) {
if (file_exists($file)) { unlink($file); }
} elseif (!$n) { $cache = file_get_contents($file); $data = unserialize($cache);
} } } return $data; }
Кстати, обновлённная функция работает немного быстрее, т.к. используется цикл for next (призываю использовать в PHP вместо foreach везде) и unserialize выполняется не по умолчанию, а только если кэш не устарел.
Если у Вас недостаточно опыта по внесению изменений в программный код OpenCart, то мы готовы предложить со своей стороны помощь.
Управление кэшем происходит в файле ./system/library/cache.php, если обратить внимание на функцию get, то видно, что результат возвращается функцией php unserialize когда файл кэша есть и null, когда его нет. Это значит, что если в кэше сохранён результат пустой выборки в одной из моделей, то при очередном запросе кэша будет возвращён ноль.
Практически во всех моделях условие проверки кэша следующее:
$product_data = $this->cache->get( ...
if (!$product_data) { ...
что в корне неверно, так как содержание кэша, например, строка s:1:"0";, может сообщить модулю об его "отсутствии" и будет выполнено повторное обращение к базе.
Если создать новые категории, то это будет хорошо заметно. Товаров нет, каждый раз запрос из базы возвращает пустой результат и сохраняет его в кэше. В этом конкретном примере необходимо открыть файл ./catalog/model/catalog/product.php найти функцию getTotalProducts и в её теле заменить условие
if (!$product_data) { ...
на
if ($product_data === null) { ...
Теперь существование кэша будет правильно воспринято отдельно взятой функцией того или иного модуля. Исправления актуальны для всех версий OpenCart, включая последнюю v1.5.5.1.
Update
Работая с библиотекой кэша ./system/library/cache.php обратил внимание, что принцип выдачи кэшированной информации организован неверно. Модулю, запросившему кэш, может быть выдана устаревшая информация, если обратите внимание на код, то данные с файла считываются раньше проверки на время.
Дабы не создавать отдельную статью, приведу решение здесь. Функцию get() необходимо заменить на код ниже.
public function get($key) {
$data = null;
$files = glob(DIR_CACHE . 'cache.' . preg_replace('/[^A-Z0-9\._-]/i', '', $key) . '.*');
if ($files) {
for ($n=0, $lenght = count($files); $n < $lenght; $n++) {
$file = $files[$n];
$time = substr(strrchr($file, '.'), 1);
if ($time < time()) {
if (file_exists($file)) { unlink($file); }
} elseif (!$n) { $cache = file_get_contents($file); $data = unserialize($cache);
} } } return $data; }
Кстати, обновлённная функция работает немного быстрее, т.к. используется цикл for next (призываю использовать в PHP вместо foreach везде) и unserialize выполняется не по умолчанию, а только если кэш не устарел.
Если у Вас недостаточно опыта по внесению изменений в программный код OpenCart, то мы готовы предложить со своей стороны помощь.
Обнаружена неприятная особенность работы кэша OpenCart на сайтах с высокой посещаемостью и медленной дисковой системой (операции ввода/вывода).
ОтветитьУдалитьМожет настать такой момент, когда под оним процессом происходит удаление файлов устаревшего кэша, а под другим идёт их чтение. Если на стороне дисковой системы будут задержки, то это может привести к коллизиям или ошибкам при сериализации/десериализации данных.
новая функция get часто выдаёт ошибку типа:
ОтветитьУдалить2014-06-24 12:58:41 - PHP Warning: file_get_contents(/****/system/cache/cache.category.104.1.0.1403607509): failed to open stream: No such file or directory in /***/system/library/cache.php on line 46
где строка 46:
} elseif (!$n) { $cache = file_get_contents($file); $data = unserialize($cache);
Это отмечено в комментарии выше. Какой бы функция кеширования для OpenCart не была, PHP всегда будет выдавать подобные предупреждения, если файловая система на хостинге медленная или плохо оптимизирована.
УдалитьЧтобы подобные записи не беспокоили, можно добавить перед функциями работы с дисковой системой знак @ (http://www.php.net//manual/en/language.operators.errorcontrol.php)