23 февраля 2013 г.

Использование кэша в 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, то мы готовы предложить со своей стороны помощь.

3 комментария:

  1. Обнаружена неприятная особенность работы кэша OpenCart на сайтах с высокой посещаемостью и медленной дисковой системой (операции ввода/вывода).

    Может настать такой момент, когда под оним процессом происходит удаление файлов устаревшего кэша, а под другим идёт их чтение. Если на стороне дисковой системы будут задержки, то это может привести к коллизиям или ошибкам при сериализации/десериализации данных.

    ОтветитьУдалить
  2. новая функция 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);

    ОтветитьУдалить
    Ответы
    1. Это отмечено в комментарии выше. Какой бы функция кеширования для OpenCart не была, PHP всегда будет выдавать подобные предупреждения, если файловая система на хостинге медленная или плохо оптимизирована.

      Чтобы подобные записи не беспокоили, можно добавить перед функциями работы с дисковой системой знак @ (http://www.php.net//manual/en/language.operators.errorcontrol.php)

      Удалить

  • RSS
  • Twitter
  • Youtube