Файл cache.php в Cotonti
Основное назначение файла cache.php в системной папке Cotonti CMF:
Файл cache.php в Cotonti CMF (Content Management Framework) предназначен для управления кэшированием данных в различных типах хранилищ. Он реализует механизм многоуровневого кэширования, позволяя сохранять данные в разных хранилищах, таких как файловая система, база данных, память и статические кэши. Основной задачей этого файла является управление кэшированием объектов и событий, улучшение производительности за счёт кеширования и управления хранением часто используемых данных.
Суть содержания кода:
-
Многоуровневое кэширование: В коде реализована архитектура, которая использует несколько типов кэширования: файловое, базовое (для работы с базой данных), память и статические кэши. Каждый тип кэша имеет свой драйвер, и система автоматически управляет ими в зависимости от настроек.
-
Привязка событий к кэшу: Одной из ключевых особенностей кода является возможность привязки событий к кэшированию. Когда событие происходит, связанный с ним кэш может быть очищен. Это обеспечивается методами для привязки (
bind,bind_array) и очистки кэша по событиям. -
Поддержка различных типов хранилищ:
- Файловое хранилище: используется для кэширования больших объектов, которые не стоит держать в памяти.
- База данных: используется для кэширования меньших объектов.
- Память: используется для быстрого доступа к кэшированным данным (например, с использованием таких драйверов, как
XCache,Memcachedи других). - Статические кэши: для кэширования целых страниц или других статических данных.
-
Интерфейсы для различных драйверов кэширования: Код позволяет добавлять драйверы кэширования, проверять их доступность, а также очищать, получать и обновлять кэшированные данные. Методы классов взаимодействуют с внешними драйверами (например,
xcache,memcache,db), обеспечивая гибкость системы.
Список классов в файле:
- Cache — основной класс для управления всеми типами кэширования.
- Xcache_driver — драйвер для работы с XCache (если расширение XCache доступно).
- Page_cache — класс для кэширования целых страниц.
Список методов и функций в файле:
-
Cache
__construct()— Инициализация компонента кэширования.__destruct()— Действия при завершении работы объекта (например, синхронизация привязок).__get($name)— Обработчик свойств для получения информации о драйверах.init()— Инициализация всех компонентов кэширования.clear($type)— Очистка всех или определённых типов кэша.clear_realm($realm, $type)— Очистка кэша для конкретной области.get_info()— Получение информации о текущем использовании памяти для кэширования.trigger($event)— Очищение кэшированных данных, привязанных к событию.bind($event, $id, $realm, $type)— Привязка события к кэшированию.bind_array($bindings)— Привязка множества событий к кэшированию.unbind($realm, $id)— Удаление привязки события от кэширования.resync_bindings()— Перезагрузка привязок из базы данных.
-
Xcache_driver
clear($realm = '')— Очистка кэша в XCache для указанной области.exists($id, $realm = COT_DEFAULT_REALM)— Проверка существования кэшированного объекта.dec($id, $realm = COT_DEFAULT_REALM, $value = 1)— Уменьшение значения кэшированного объекта.get($id, $realm = COT_DEFAULT_REALM)— Получение данных из кэша.get_info()— Получение информации о текущем использовании XCache.inc($id, $realm = COT_DEFAULT_REALM, $value = 1)— Увеличение значения кэшированного объекта.remove($id, $realm = COT_DEFAULT_REALM)— Удаление данных из кэша.store($id, $data, $realm = COT_DEFAULT_REALM, $ttl = COT_DEFAULT_TTL)— Сохранение данных в кэш.
-
Page_cache
__construct($cache_dir, $permissions)— Инициализация кэша страницы.
Характеристика стиля PHP:
-
Объектно-ориентированное программирование (ООП): Код написан в объектно-ориентированном стиле с использованием классов и методов. Например, класс
Cacheуправляет различными драйверами кэширования, а методы внутри классов позволяют работать с кэшированием на разных уровнях. -
Использование глобальных переменных: В коде используется множество глобальных переменных (например,
$cfg,$cot_cache_autoload,$cot_cache_drivers). Это позволяет динамически настраивать систему, но может затруднить понимание и тестирование кода. -
Чистота и читаемость кода: Код достаточно читаемый и использует ясные имена переменных и методов. Применяются такие практики, как использование описательных имён (например,
clear(),trigger(),bind()), что облегчает понимание функционала. -
Отсутствие строгой типизации: Хотя в некоторых местах используются явные приведения типов (например,
(int) $type), в целом отсутствует строгая типизация переменных и методов. -
Использование функций обработки данных: Код активно использует методы для обработки и подготовки данных перед их использованием (например,
$db->quote()для экранирования данных,$db->prep()для подготовки значений). -
Обработка ошибок и исключений: В коде не используется явная обработка исключений с помощью
try-catch. Вместо этого ошибки, такие как ошибки базы данных, могут быть обработаны через возвращаемые значения или проверку количества затронутых строк ($db->affectedRows).
Заключение:
Файл cache.php реализует мощную систему многоуровневого кэширования для Cotonti CMF, поддерживая разные типы кэширования и возможности привязки событий. Стиль PHP в коде можно охарактеризовать как объектно-ориентированный с элементами функционального программирования (например, работа с глобальными переменными). Код чётко структурирован, но может быть улучшен с точки зрения использования строгой типизации и обработки ошибок.
/**
* Cache subsystem library
* Библиотека подсистемы кэширования
*
* @package API - Cache
* @copyright (c) Cotonti Team
* @license https://github.com/Cotonti/Cotonti/blob/master/License.txt
* Лицензия: https://github.com/Cotonti/Cotonti/blob/master/License.txt
*/
defined('COT_CODE') or die('Wrong URL');
// Проверка, что файл был загружен через правильный вход (COT_CODE должен быть определен). Если нет — выводится сообщение "Wrong URL".
/**
* Stores the list of advanced cachers provided by the host
* Хранит список расширенных кэшеров, предоставленных хостом
* @var array
*/
$cot_cache_drivers = [];
// Инициализация пустого массива для хранения драйверов кэширования.
/**
* Default cache realm
* Стандартная область кэширования
*/
const COT_DEFAULT_REALM = 'cot';
// Константа для задания области кэширования по умолчанию (значение 'cot').
/**
* Default time to live for temporary cache objects
* Стандартное время жизни временных объектов в кэше
*/
const COT_DEFAULT_TTL = 3600;
// Константа для задания времени жизни объектов кэша (по умолчанию 3600 секунд, т.е. 1 час).
/**
* Default cache type, uneffective
* Стандартный тип кэша, неэффективен
*/
const COT_CACHE_TYPE_ALL = 0;
// Константа для определения типа кэша "все", но она не имеет действия.
/**
* Disk cache type
* Тип кэша — диск
*/
const COT_CACHE_TYPE_DISK = 1;
// Константа для определения типа кэша как "диск" (значение 1).
/**
* Database cache type
* Тип кэша — база данных
*/
const COT_CACHE_TYPE_DB = 2;
// Константа для определения типа кэша как "база данных" (значение 2).
/**
* Shared memory cache type
* Тип кэша — общая память
*/
const COT_CACHE_TYPE_MEMORY = 3;
// Константа для определения типа кэша как "общая память" (значение 3).
/**
* Static cache type
* Тип кэша — статичный
*/
const COT_CACHE_TYPE_STATIC = 4;
// Константа для определения типа кэша как "статичный" (значение 4).
/**
* Default cache type
* Стандартный тип кэша
*/
const COT_CACHE_TYPE_DEFAULT = COT_CACHE_TYPE_DB;
// Константа для задания типа кэша по умолчанию (используется тип "база данных").
/**
* Абстрактный класс, содержащий код, общий для всех драйверов кэширования
*/
abstract class Cache_driver
{
/**
* Очищает все элементы кэша, обслуживаемые текущим драйвером
* @param string $realm Название области кэша, чтобы очистить только конкретную область
* @return bool
*/
abstract public function clear($realm = COT_DEFAULT_REALM);
// Абстрактный метод для очистки всех записей кэша, обслуживаемых текущим драйвером.
// Принимает необязательный параметр $realm, чтобы очистить только конкретную область кэша.
/**
* Проверяет, находится ли объект в кэше
* @param string $id Идентификатор объекта
* @param string $realm Область кэша
* @return bool
*/
abstract public function exists($id, $realm = COT_DEFAULT_REALM);
// Абстрактный метод для проверки, хранится ли объект в кэше.
// Принимает идентификатор объекта $id и необязательный параметр $realm (область кэша).
/**
* Возвращает значение кэшированного объекта
* @param string $id Идентификатор объекта
* @param string $realm Название области
* @return mixed Значение кэшированного элемента или NULL, если элемент не найден
*/
abstract public function get($id, $realm = COT_DEFAULT_REALM);
// Абстрактный метод для получения значения кэшированного объекта.
// Принимает идентификатор объекта $id и необязательный параметр $realm (область кэша).
// Возвращает значение кэшированного элемента или NULL, если элемент не найден.
/**
* Удаляет объект из кэша
* @param string $id Идентификатор объекта
* @param string $realm Название области
* @return bool
*/
abstract public function remove($id, $realm = COT_DEFAULT_REALM);
// Абстрактный метод для удаления объекта из кэша.
// Принимает идентификатор объекта $id и необязательный параметр $realm (область кэша).
}
/**
* Статический кэш используется для хранения больших объемов данных, которые редко изменяются
*/
abstract class Static_cache_driver extends Cache_driver
{
/**
* Хранит данные как объект в кэше
* @param string $id Идентификатор объекта
* @param mixed $data Значение объекта
* @param string $realm Название области
* @return bool
*/
abstract public function store($id, $data, $realm = COT_DEFAULT_REALM);
// Абстрактный метод для хранения данных в кэше как объектного изображения.
// Принимает идентификатор объекта $id, данные объекта $data и необязательный параметр $realm (область кэша).
}
/**
* Динамический кэш используется для хранения данных, которые не слишком велики
* и изменяются более или менее часто
*/
abstract class Dynamic_cache_driver extends Cache_driver
{
/**
* Хранит данные как объект в кэше
* @param string $id Идентификатор объекта
* @param mixed $data Значение объекта
* @param string $realm Название области
* @param int $ttl Время жизни, 0 — для неограниченного времени
* @return bool
*/
abstract public function store($id, $data, $realm = COT_DEFAULT_REALM, $ttl = COT_DEFAULT_TTL);
// Абстрактный метод для хранения данных в кэше как объектного изображения.
// Принимает идентификатор объекта $id, данные объекта $data, необязательные параметры $realm (область кэша)
// и $ttl (время жизни кэшируемого объекта).
}
/**
* Постоянный кэш-драйвер, который записывает все элементы обратно при завершении скрипта.
* Постоянные кэш-драйверы работают медленнее, но гарантируют долгосрочную консистентность данных.
*/
abstract class Writeback_cache_driver extends Dynamic_cache_driver
{
/**
* Значения для отложенной записи в постоянный кэш
* @var array
*/
protected $writeback_data = array(); // Массив для хранения данных, которые будут записаны обратно в кэш
/**
* Ключи, которые должны быть удалены
*/
protected $removed_data = array(); // Массив для хранения данных, которые должны быть удалены
/**
* Записывает изменённые элементы обратно в постоянное хранилище
*/
abstract public function flush(); // Абстрактный метод для записи данных в постоянное хранилище
/**
* Удаляет кэш-изображение объекта из базы данных
* @param string $id Идентификатор объекта
* @param string $realm Название области
*/
public function remove($id, $realm = COT_DEFAULT_REALM)
{
$this->removed_data[] = array('id' => $id, 'realm' => $realm); // Добавляет данные для удаления в массив
}
/**
* Удаляет элемент немедленно, избегая записи обратно.
* @param string $id Идентификатор элемента
* @param string $realm Область кэша
* @return bool
* @see Cache_driver::remove()
*/
abstract public function remove_now($id, $realm = COT_DEFAULT_REALM); // Абстрактный метод для немедленного удаления элемента
/**
* Сохраняет данные как изображение объекта в кэше
* @param string $id Идентификатор объекта
* @param mixed $data Значение объекта
* @param string $realm Название области
* @param int $ttl Время жизни, 0 — для неограниченного времени
* @return bool
* @see Cache_driver::store()
*/
public function store($id, $data, $realm = COT_DEFAULT_REALM, $ttl = 0)
{
$this->writeback_data[] = array('id' => $id, 'data' => $data, 'realm' => $realm, 'ttl' => $ttl); // Добавляет данные для записи обратно в массив
return true; // Возвращает true, что означает успешное выполнение
}
/**
* Немедленно записывает элемент в кэш, избегая записи обратно.
* @param string $id Идентификатор объекта
* @param mixed $data Значение объекта
* @param string $realm Название области
* @param int $ttl Время жизни, 0 — для неограниченного времени
* @return bool
* @see Cache_driver::store()
*/
abstract public function store_now($id, $data, $realm = COT_DEFAULT_REALM, $ttl = COT_DEFAULT_TTL); // Абстрактный метод для немедленного сохранения элемента в кэш
}
/**
* Кэш-драйверы запросов управляются с помощью базы данных
*/
abstract class Db_cache_driver extends Writeback_cache_driver
{
/**
* Загружает все переменные из указанной области(ей) в глобальную область
* @param mixed $realm Название области или массив названий областей
* @return int Количество загруженных элементов
*/
abstract public function get_all($realm = COT_DEFAULT_REALM); // Абстрактный метод для загрузки всех элементов из указанной области
}
/**
* Временный кэш-драйвер — это быстрый кэш в памяти. Обычно он работает быстрее и предоставляет
* автоматическую сборку мусора, но не сохраняет данные, если PHP останавливается по какой-либо причине.
* Используйте его для отдельных часто изменяемых переменных.
*/
abstract class Temporary_cache_driver extends Dynamic_cache_driver
{
/**
* Увеличивает значение счётчика
* @param string $id Идентификатор счётчика
* @param string $realm Название области
* @param int $value Значение для увеличения
* return int Результат
*/
public function inc($id, $realm = COT_DEFAULT_REALM, $value = 1)
{
$res = $this->get($id, $realm); // Получает текущее значение счётчика из кэша
$res += $value; // Увеличивает значение на указанную величину
$this->store($id, $res, $realm); // Сохраняет обновлённое значение в кэш
return $res; // Возвращает результат
}
/**
* Уменьшает значение счётчика
* @param string $id Идентификатор счётчика
* @param string $realm Название области
* @param int $value Значение для уменьшения
* return int Результат
*/
public function dec($id, $realm = COT_DEFAULT_REALM, $value = 1)
{
$res = $this->get($id, $realm); // Получает текущее значение счётчика из кэша
$res -= $value; // Уменьшает значение на указанную величину
$this->store($id, $res, $realm); // Сохраняет обновлённое значение в кэш
return $res; // Возвращает результат
}
/**
* Возвращает информацию о памяти, если доступно.
* Возможные ключи: available, occupied, max.
* Если драйвер не может предоставить значение, оно устанавливается в -1.
* @return array Ассоциативный массив с информацией
*/
abstract public function get_info(); // Абстрактный метод для получения информации о памяти
/**
* Получает размер лимита из php.ini
* @param string $name Имя настройки INI
* @return int Количество байт
*/
protected function get_ini_size($name)
{
$ini = ini_get($name); // Получает значение настройки INI
$suffix = strtoupper(substr($ini, -1)); // Извлекает суффикс (K, M, G)
$prefix = substr($ini, 0, -1); // Извлекает числовое значение
switch ($suffix)
{
case 'K':
return ((int) $prefix) * 1024; // Преобразует значение в байты (Килобайты)
break;
case 'M':
return ((int) $prefix) * 1048576; // Преобразует значение в байты (Мегабайты)
break;
case 'G':
return ((int) $prefix) * 1073741824; // Преобразует значение в байты (Гигабайты)
break;
default:
return (int) $ini; // Возвращает значение в байтах без суффикса
}
}
}
/**
* Постоянный кэш, использующий локальную файловую систему. Он не использует многоуровневую структуру
* или лексикографический поиск, поэтому может замедлиться, когда кэш становится очень большим.
* Но обычно он очень быстрый для чтения.
*/
class File_cache extends Static_cache_driver
{
/**
* Корневая директория кэша
* @var string
*/
private $dir; // Переменная для хранения пути к директории кэша
}
/**
* Конструктор объекта хранилища кэша
* @param string $dir Корневая директория кэша. Если не указана, используется системная настройка по умолчанию.
* @throws Exception
* @return File_cache
*/
public function __construct($dir = '')
{
global $cfg; // Доступ к глобальной переменной конфигурации
if (empty($dir)) $dir = $cfg['cache_dir']; // Если директория не указана, использовать системный путь кэша из конфигурации
if (!empty($dir) && !file_exists($dir)) mkdir($dir, 0755, true); // Если директория не существует, создать её с правами 0755
if (file_exists($dir) && is_writeable($dir)) // Если директория существует и доступна для записи
{
$this->dir = $dir; // Присваиваем директорию в свойство объекта
}
else
{
throw new Exception('Cache directory '.$dir.' is not writeable!'); // Генерируем исключение, если директория недоступна для записи
}
}
/**
* @see Cache_driver::clear()
*/
public function clear($realm = '', $exceptRealms = ['assets', 'static', 'htmlpurifier', 'templates'])
{
if (!empty($realm)) { // Если указан конкретный realm
$directory = $this->dir . '/' . $realm; // Формируем путь к директории
if (is_dir($this->dir . '/' . $realm)) { // Проверяем, существует ли директория
return $this->doClear($directory, true, true); // Очищаем кэш в указанной директории
}
return true; // Если директория не существует, возвращаем true
}
if (is_dir($this->dir)) { // Если корневая директория существует
$this->doClear(Cot::$cfg['cache_dir'], false); // Очищаем кэш в основной директории
$directory = opendir($this->dir); // Открываем директорию для чтения
while ($f = readdir($directory)) { // Проходим по файлам и поддиректориям
$dname = $this->dir . '/' . $f; // Формируем путь к файлу/директории
if (
is_dir($dname) // Проверяем, что это директория
&& $f[0] !== '.' // Игнорируем скрытые директории (начинаются с '.')
&& (empty($exceptRealms) || !in_array($f, $exceptRealms)) // Проверяем, что данная директория не исключена
) {
$this->clear($f); // Рекурсивно очищаем кэш в поддиректории
}
}
closedir($directory); // Закрываем директорию
}
return true; // Возвращаем true, если очистка прошла успешно
}
/**
* Clears disk cache directory
* Очищает директорию кэша на диске.
* @param string $directory Directory name Путь к директории
* @param bool $clearSubDirectories true, если нужно очистить поддиректории, иначе false
* @param bool $deleteDirectory true, если нужно удалить саму директорию, иначе false
* @return bool
*/
private function doClear($directory, $clearSubDirectories = true, $deleteDirectory = false)
{
if (!is_dir($directory) || !is_writable($directory)) { // Проверяем, существует ли директория и доступна ли она для записи
return false; // Если нет, возвращаем false
}
$glob = glob("$directory/*"); // Получаем все файлы и папки в директории
if (is_array($glob)) { // Если получен массив
foreach ($glob as $f) { // Проходим по каждому элементу в директории
if (
is_file($f) // Если это файл
&& !in_array($f, [$this->dir . '/index.html', $this->dir . '/.htaccess']) // Исключаем index.html и .htaccess
) {
@unlink($f); // Удаляем файл
} elseif (is_dir($f) && $clearSubDirectories) { // Если это директория и нужно очистить поддиректории
$this->doClear($f, true, true); // Рекурсивно очищаем поддиректорию
}
}
}
if ($this->dir !== $directory && $deleteDirectory) { // Если текущая директория не является корневой и нужно удалить саму директорию
@rmdir($directory); // Удаляем директорию
}
return true; // Возвращаем true, если очистка прошла успешно
}
/**
* Checks if an object is stored in disk cache
* Проверяет, существует ли объект в кэше на диске
* @param string $id Object identifier Идентификатор объекта
* @param string $realm Cache realm Область кэша
* @param int $ttl Lifetime in seconds, 0 means unlimited Время жизни в секундах, 0 — неограниченно
* @return bool
*/
public function exists($id, $realm = COT_DEFAULT_REALM, $ttl = 0)
{
$filename = $this->dir.'/'.$realm.'/'.$id; // Формируем путь к файлу с кэшированным объектом
return file_exists($filename) && ($ttl == 0 || time() - filemtime($filename) < $ttl); // Проверяем, существует ли файл и не истекло ли время жизни
}
/**
* Gets an object directly from disk
* Получает объект напрямую с диска
* @param string $id Object identifier Идентификатор объекта
* @param string $realm Realm name Название области
* @param int $ttl Lifetime in seconds, 0 means unlimited Время жизни в секундах, 0 — неограниченно
* @return mixed Cached item value or NULL if the item was not found in cache Возвращает значение кэшированного объекта или NULL, если объект не найден в кэше
*/
public function get($id, $realm = COT_DEFAULT_REALM, $ttl = 0)
{
if ($this->exists($id, $realm, $ttl)) // Если объект существует в кэше
{
return unserialize(file_get_contents($this->dir.'/'.$realm.'/'.$id)); // Возвращаем содержимое объекта, десериализовав его
}
else
{
return NULL; // Если объект не найден, возвращаем NULL
}
}
/**
* Removes cache image of the object from disk
* Удаляет объект из кэша на диске
* @param string $id Object identifier Идентификатор объекта
* @param string $realm Realm name Название области
*/
public function remove($id, $realm = COT_DEFAULT_REALM)
{
if ($this->exists($id, $realm)) // Если объект существует в кэше
{
unlink($this->dir.'/'.$realm.'/'.$id); // Удаляем файл с объектом
return true; // Возвращаем true, если объект успешно удален
}
else return false; // Если объект не найден, возвращаем false
}
/**
* Stores disk cache entry
* Сохраняет объект в кэш на диске
* @param string $id Object identifier Идентификатор объекта
* @param mixed $data Object value Значение объекта
* @param string $realm Realm name Название области
* @return bool
*/
public function store($id, $data, $realm = COT_DEFAULT_REALM)
{
if (!file_exists($this->dir.'/'.$realm)) // Если директория области кэша не существует
{
mkdir($this->dir.'/'.$realm); // Создаем директорию для области кэша
}
file_put_contents($this->dir.'/'.$realm.'/'.$id, serialize($data)); // Сохраняем объект в файл, сериализуя его
return true; // Возвращаем true, если объект успешно сохранен
}
}
/**
* A cache that stores entire page outputs. Disk-based.
* Кэш, который сохраняет полные выводы страниц. Основан на диске.
*/
class Page_cache
{
/**
* Cache root
* Корневая директория для кэша
* @var string
*/
private $dir;
/**
* Relative page (item) path
* Относительный путь к странице (элементу)
* @var string
*/
private $path;
/**
* Short file name
* Короткое имя файла
* @var string
*/
private $name;
/**
* Parameters to exclude
* Параметры, которые следует исключить
* @var string[]
*/
private $excl;
/**
* Filename extension
* Расширение файла
* @var string
*/
private $ext;
/**
* Full path to page cache image
* Полный путь к файлу кэша страницы
* @var string
* @todo relative to $this->dir
*/
private $filename = null;
/**
* Directory permissions
* Права доступа к директории
* @var string
*/
private $perms;
/**
* @var bool
*/
private $enabled = false;
/**
* Constructs controller object and sets basic configuration
* Конструктор объекта контроллера и установка базовой конфигурации
* @param string $dir Cache directory Директория для кэша
* @param int $perms Octal permission mask for cache directories Маска прав доступа в восьмеричной системе для директорий кэша
*/
public function __construct($dir, $perms = 0777)
{
$this->dir = rtrim($dir, '\\/'); // Убираем завершающие символы / или \ из пути к директории
$this->perms = $perms; // Устанавливаем права доступа для директорий
}
}
/**
* Initializes actual page cache
* Инициализирует кэш страницы
* @param string $path Page path string Путь страницы
* @param string $name Short name for the cache file Короткое имя для файла кэша
* @param array $exclude A list of GET params to be excluded from consideration Список GET параметров, которые следует исключить из рассмотрения
* @param string $ext File extension Расширение файла
*/
public function init($path, $name, $exclude = [], $ext = '')
{
$this->path = $path; // Устанавливаем путь страницы
$this->name = $name; // Устанавливаем короткое имя для файла кэша
$this->excl = $exclude; // Устанавливаем список параметров для исключения
$this->ext = $ext; // Устанавливаем расширение файла
$this->enabled = true; // Включаем кэширование
$filename = $this->path . '/' . $this->name; // Формируем базовое имя файла с путем
$args = []; // Массив для параметров запроса
foreach ($_GET as $key => $val) { // Проходим по всем GET параметрам
if (!in_array($key, $this->excl)) { // Если параметр не в списке исключений
$args[$key] = $val; // Добавляем параметр в массив
}
}
ksort($args); // Сортируем параметры по ключу
if (count($args) > 0) { // Если есть параметры
$hashkey = serialize($args); // Сериализуем параметры
$filename .= '_' . md5($hashkey) . sha1($hashkey); // Добавляем хэшированные параметры к имени файла
}
if (!empty($this->ext)) { // Если указано расширение файла
$filename .= '.' . $this->ext; // Добавляем расширение к имени файла
}
$this->filename = $filename; // Устанавливаем полное имя файла для кэша
}
/**
* Initializes actual page cache by given page uri
* Инициализирует кэш страницы по заданному URI страницы
* @param string $uri Relative page uri (relative to $sys['abs_url']) Относительный URI страницы (относительно $sys['abs_url'])
* @param string $name Short name for the cache file Короткое имя для файла кэша
* @param array $exclude A list of GET params to be excluded from consideration Список GET параметров, которые следует исключить из рассмотрения
* @param string $ext File extension Расширение файла
*/
public function initByUri($uri, $name, $exclude = [], $ext = '')
{
$path = $this->getPathByUri($uri); // Получаем путь страницы по URI
if (empty($path)) { // Если путь пустой
return; // Прерываем выполнение
}
$this->init($path, $name, $exclude, $ext); // Инициализируем кэш с полученным путем
}
/**
* @param string $uri Relative page uri (relative to $sys['abs_url']) Относительный URI страницы (относительно $sys['abs_url'])
* @return string Возвращает строку с путем
*/
protected function getPathByUri($uri)
{
$parsedUrl = cot_parse_url($uri); // Разбираем URL с помощью cot_parse_url
$get = []; // Массив для параметров запроса
if (!empty($parsedUrl['query'])) { // Если в URL есть параметры запроса
$parsedUrl['query'] = str_replace('&', '&', $parsedUrl['query']); // Заменяем '&' на '&'
parse_str($parsedUrl['query'], $get); // Преобразуем строку запроса в массив
}
if (function_exists('cot_staticCacheGetPathByUri')) { // Если существует функция cot_staticCacheGetPathByUri
$path = cot_staticCacheGetPathByUri($parsedUrl); // Получаем путь с помощью этой функции
return !empty($path) ? $path : 'index'; // Если путь найден, возвращаем его, иначе 'index'
}
if (!empty($get['e'])) { // Если параметр 'e' есть в запросе
$path = preg_replace('#\W#', '', $get['e']); // Убираем все неалфавитные символы
} elseif ($parsedUrl['path'] !== '/') { // Если путь не является корнем
$parsedUrl['path'] = rawurldecode($parsedUrl['path']); // Декодируем путь
$path = trim($parsedUrl['path'], '/'); // Убираем начальные и конечные слэши
// Убираем последнюю часть пути. Это может быть псевдоним страницы или идентификатор
// $lastPosition = mb_strrpos($path, '/'); // Находим последнюю позицию слэша
// if ($lastPosition > 0) { // Если слэш найден
// $path = mb_substr($path, 0, $lastPosition); // Обрезаем путь до последнего слэша
// }
}
if (!empty($path)) { // Если путь не пустой
$c = isset($get['c']) ? trim($get['c']) : null; // Получаем параметр 'c', если он есть
if (!empty($c)) { // Если параметр 'c' не пустой
$path .= '/' . $c; // Добавляем параметр 'c' к пути
}
} else { // Если путь пустой
$path = 'index'; // Используем 'index' как путь по умолчанию
}
return $path; // Возвращаем полученный путь
}
/**
* Проверяет, включен ли кеш.
* @return bool Возвращает true, если кеш включен, иначе false
*/
public function isEnabled()
{
return $this->enabled; // Возвращаем состояние флага включения кеша
}
/**
* Получает имя файла кеша.
* @return ?string Возвращает имя файла кеша или null, если кеш не инициализирован
*/
public function getFileName()
{
return $this->filename; // Возвращаем имя файла кеша
}
/**
* Отключает кеш.
*/
public function disable()
{
$this->enabled = false; // Устанавливаем флаг отключения кеша
}
Метод getFileName() возвращает имя файла кеша или null, если кеш не был инициализирован. Метод disable() отключает кеш, установив флаг enabled в false.
/**
* Удаляет элемент и/или все содержащиеся элементы и файлы кеша
* @param string $path Путь к элементу
* @param bool $withSubDirectories Удалить все содержащиеся элементы (подкаталоги)
* @return int Количество удалённых файлов
*/
public function clear($path, $withSubDirectories = false)
{
$directory = $this->dir; // Получаем основной каталог кеша
if (!empty($path)) {
$directory .= '/' . rtrim($path, '\\/'); // Добавляем путь к элементу
}
return $this->removeDir($directory, $withSubDirectories); // Удаляем указанный каталог и его содержимое
}
/**
* Удаляет элемент и все содержащиеся элементы и файлы кеша по заданному URI страницы
* @param string $uri Относительный URI страницы (относительно $sys['abs_url'])
* @param bool $withSubDirectories Удалить все содержащиеся элементы (подкаталоги)
* @return int Количество удалённых файлов
*/
public function clearByUri($uri, $withSubDirectories = false)
{
$path = $this->getPathByUri($uri); // Получаем путь по URI
if (empty($path)) {
return 0; // Если путь пустой, возвращаем 0
}
return $this->clear($path, $withSubDirectories); // Вызываем метод clear для удаления
}
Методы clear и clearByUri служат для удаления кеша и его содержимого. В первом методе удаляется элемент и/или подкаталоги по указанному пути, во втором — по URI страницы.
/**
* Читает объект кеша страницы с диска и отправляет его в вывод.
* Если объект кеша не существует, то просто вычисляет путь
* для последующего вызова метода write().
*/
public function read()
{
$fileFullName = $this->dir . '/' . $this->filename; // Полное имя файла кеша
if (file_exists($fileFullName)) { // Проверяем, существует ли файл кеша
// Заголовки для кеширования в браузере
$filemtime = filemtime($fileFullName); // Время последнего изменения файла
$etag = md5($fileFullName . filesize($fileFullName) . $filemtime); // Генерация ETag для файла
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { // Проверяем заголовок If-Modified-Since
// Преобразуем в метку времени UNIX
$if_modified_since = strtotime(preg_replace('#;.*$#', '',
stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE'])));
} else {
$if_modified_since = false;
}
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) { // Проверяем заголовок If-None-Match
$if_none_match = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
if ($if_none_match == $etag && $if_modified_since >= $filemtime) { // Если кеш актуален
// Отправляем заголовки с кодом 304 Not Modified
$protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
header($protocol . ' 304 Not Modified');
header("Etag: $etag");
exit;
}
}
// Заголовки с информацией о времени последнего изменения и ETag
header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', $filemtime));
header("ETag: $etag");
header('Expires: Mon, 01 Apr 1974 00:00:00 GMT'); // Устанавливаем дату истечения срока действия
header('Cache-Control: must-revalidate, proxy-revalidate'); // Устанавливаем необходимость повторной валидации кеша
// Выводим страницу
header('Content-Type: text/html; charset=UTF-8');
if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE) { // Если не поддерживается сжатие
readgzfile($fileFullName); // Читаем и выводим файл без сжатия
} else {
header('Content-Encoding: gzip'); // Устанавливаем заголовок для сжатия
echo file_get_contents($fileFullName); // Читаем и выводим сжатый файл
}
exit;
}
}
Этот метод читает файл кеша страницы с диска, проверяет, актуален ли кеш (посредством заголовков If-Modified-Since и If-None-Match), и если он актуален, отправляет заголовки с кодом 304 (Not Modified). В противном случае он отправляет саму страницу в нужном формате (сжато или нет).
/**
* Записывает содержимое буфера вывода в файл кеша
*/
public function write()
{
// Проверяем, активирован ли кеш и задан ли файл кеша
if (!$this->enabled || empty($this->filename)) {
return; // Если кеш не активирован или имя файла не задано, прекращаем выполнение
}
// Проверяем, существует ли директория для хранения кеша, если нет - создаем её
if (!file_exists($this->dir . '/' . $this->path)) {
mkdir($this->dir . '/' . $this->path, $this->perms, true); // Создаем директорию с необходимыми правами
}
// Сохраняем содержимое буфера вывода в сжатый файл
file_put_contents($this->dir . '/' . $this->filename, gzencode(cot_outputFilters(ob_get_contents())));
}
Объяснение:
- Проверка состояния кеша: Метод сначала проверяет, включен ли кеш и задано ли имя файла кеша. Если кеш не активен или имя файла не установлено, метод завершает выполнение.
- Создание директории: Далее метод проверяет, существует ли директория для кеша. Если её нет, она создается с соответствующими правами доступа.
- Запись в файл: Затем содержимое текущего буфера вывода (
ob_get_contents()) обрабатывается через функциюcot_outputFilters, сжимается с помощьюgzencodeи сохраняется в файл.
Этот метод обеспечивает сохранение сгенерированного контента в кеш, чтобы использовать его в дальнейшем, что повышает производительность сайта.
/**
* Рекурсивно удаляет директорию со всеми её содержимым
* @param string $path Путь к директории
* @param bool $withSubDirectories Удалять ли поддиректории
* @return int Количество удалённых элементов
*/
private function removeDir($path, $withSubDirectories = false)
{
$cnt = 0;
// Проверяем, существует ли директория
if (is_dir($path)) {
// Открываем директорию для чтения
$dp = opendir($path);
// Читаем содержимое директории
while ($f = readdir($dp)) {
$fpath = $path . '/' . $f;
// Если это поддиректория и она не является текущей или родительской директорией
if (is_dir($fpath) && $f != '.' && $f != '..' && $withSubDirectories) {
// Рекурсивно удаляем её содержимое
$cnt += $this->removeDir($fpath, true);
} elseif (is_file($fpath)) {
// Если это файл, удаляем его
unlink($fpath);
$cnt++;
}
}
// Закрываем директорию
closedir($dp);
// Удаляем саму директорию
rmdir($path);
}
// Возвращаем количество удалённых элементов (директорий и файлов)
return ++$cnt;
}
}
Объяснение:
- Проверка директории: Метод сначала проверяет, является ли переданный путь директорией с помощью
is_dir. - Чтение содержимого: Используется
opendirдля открытия директории иreaddirдля перебора файлов и поддиректорий. - Рекурсивное удаление: Если элемент является поддиректорией, метод рекурсивно вызывает себя для удаления содержимого. Если элемент — файл, он удаляется с помощью
unlink. - Закрытие директории: После завершения работы с директорией вызывается
closedir. - Удаление самой директории: Когда все файлы и поддиректории удалены, вызывается
rmdirдля удаления самой директории.
Метод возвращает количество удалённых элементов, включая файлы и директории.
Class MySQL_cache
/**
* Очень популярное решение для кэширования с использованием MySQL в качестве хранилища.
* Оно относительно медленнее, чем File_cache, но может считаться более надёжным.
*/
class MySQL_cache extends Db_cache_driver
{
/**
* Буфер предварительно загруженных данных для предотвращения дублирующих запросов
* @var array
*/
private $buffer = array();
/**
* Выполняет действия по предварительной загрузке
*/
public function __construct()
{
// 10% вероятность запуска GC (сборщик мусора)
if (mt_rand(1, 10) == 5)
{
$this->gc();
}
}
/**
* Осуществляет flush() при уничтожении объекта
*/
public function __destruct()
{
$this->flush();
}
}
Объяснение:
-
Комментарии к классу
MySQL_cache: Класс предоставляет решение для кэширования, где данные хранятся в базе данных MySQL. Он, как правило, медленнее, чем кэширование на файловой системе (File_cache), но может быть более надёжным из-за использования базы данных. -
$buffer: Это приватная переменная, которая используется для хранения предварительно загруженных данных. Это помогает избежать повторных запросов к базе данных, тем самым улучшая производительность. -
__construct(): Конструктор класса, в котором есть случайный вызов сборщика мусора (GC) с вероятностью 10%. Этот сборщик мусора, вероятно, очищает устаревшие или ненужные кэшированные данные. -
__destruct(): Метод, который вызывается при уничтожении объекта. Он вызывает методflush(), который, скорее всего, очищает или сохраняет кэш в базе данных.
/**
* Сохраняет все изменённые данные с помощью одного запроса
*/
public function flush()
{
global $db, $db_cache, $sys;
// Если есть данные, которые нужно удалить из кэша
if (count($this->removed_data) > 0)
{
$q = "DELETE FROM $db_cache WHERE";
$i = 0;
// Формируем запрос на удаление
foreach ($this->removed_data as $entry)
{
$c_name = $db->quote($entry['id']);
$c_realm = $db->quote($entry['realm']);
$or = $i == 0 ? '' : ' OR';
$q .= $or." (c_name = $c_name AND c_realm = $c_realm)";
$i++;
}
$this->removed_data = array(); // Очищаем список удалённых данных
$db->query($q); // Выполняем запрос на удаление
}
// Если есть данные, которые нужно записать в кэш
if (count($this->writeback_data) > 0)
{
$q = "INSERT INTO $db_cache (c_name, c_realm, c_expire, c_value) VALUES ";
$i = 0;
// Формируем запрос на вставку данных
foreach ($this->writeback_data as $entry)
{
$c_name = $db->quote($entry['id']);
$c_realm = $db->quote($entry['realm']);
$c_expire = $entry['ttl'] > 0 ? $sys['now'] + $entry['ttl'] : 0;
$c_value = $db->quote(serialize($entry['data']));
$comma = $i == 0 ? '' : ',';
$q .= $comma."($c_name, $c_realm, $c_expire, $c_value)";
$i++;
}
$this->writeback_data = array(); // Очищаем список данных на запись
$q .= " ON DUPLICATE KEY UPDATE c_value=VALUES(c_value), c_expire=VALUES(c_expire)"; // Обновляем данные, если уже есть записи с таким ключом
$db->query($q); // Выполняем запрос на вставку или обновление данных
}
}
Объяснение работы метода:
-
Удаление данных из кэша:
- Если в массиве
$this->removed_dataесть элементы, то формируется запрос на удаление этих данных из таблицы кэша$db_cache. - Для каждого элемента создаётся условие удаления по имени (
c_name) и области (c_realm). - После формирования запроса на удаление, массив
$this->removed_dataочищается, и запрос выполняется с помощью метода$db->query().
- Если в массиве
-
Запись данных в кэш:
- Если в массиве
$this->writeback_dataесть данные для записи, формируется запрос на вставку в таблицу кэша. - Каждое значение сериализуется, и вычисляется время истечения срока действия (
c_expire), если оно задано. - После формирования запроса, массив
$this->writeback_dataочищается. - Запрос завершён конструкцией
ON DUPLICATE KEY UPDATE, которая позволяет обновлять уже существующие записи с таким же именем и областью, если они присутствуют в базе.
- Если в массиве
Ключевые моменты:
- Оптимизация с помощью одного запроса: Метод выполняет удаление и запись данных в одном запросе для улучшения производительности.
- Использование сериализации: Данные сериализуются перед записью в базу данных, чтобы сохранить сложные структуры данных.
- Обработка обновлений: Используется
ON DUPLICATE KEY UPDATE, чтобы обновлять запись, если она уже существует в базе.
Этот метод является важным элементом в реализации кэширования данных с использованием MySQL, так как он эффективно обрабатывает изменения в кэше, минимизируя количество запросов к базе данных.
Комментарии (0)
Автор контента
Оффлайн
Administrator
Последняя авторизация: 05.07.2026 18:32
- Страница размещена: 27.01.2025 22:12
- Последнее обновление: 27.01.2025 23:17