Файл cache.php в Cotonti

Основное назначение файла cache.php в системной папке Cotonti CMF:

Файл cache.php в Cotonti CMF (Content Management Framework) предназначен для управления кэшированием данных в различных типах хранилищ. Он реализует механизм многоуровневого кэширования, позволяя сохранять данные в разных хранилищах, таких как файловая система, база данных, память и статические кэши. Основной задачей этого файла является управление кэшированием объектов и событий, улучшение производительности за счёт кеширования и управления хранением часто используемых данных.

Суть содержания кода:

  1. Многоуровневое кэширование: В коде реализована архитектура, которая использует несколько типов кэширования: файловое, базовое (для работы с базой данных), память и статические кэши. Каждый тип кэша имеет свой драйвер, и система автоматически управляет ими в зависимости от настроек.

  2. Привязка событий к кэшу: Одной из ключевых особенностей кода является возможность привязки событий к кэшированию. Когда событие происходит, связанный с ним кэш может быть очищен. Это обеспечивается методами для привязки (bind, bind_array) и очистки кэша по событиям.

  3. Поддержка различных типов хранилищ:

    • Файловое хранилище: используется для кэширования больших объектов, которые не стоит держать в памяти.
    • База данных: используется для кэширования меньших объектов.
    • Память: используется для быстрого доступа к кэшированным данным (например, с использованием таких драйверов, как XCache, Memcached и других).
    • Статические кэши: для кэширования целых страниц или других статических данных.
  4. Интерфейсы для различных драйверов кэширования: Код позволяет добавлять драйверы кэширования, проверять их доступность, а также очищать, получать и обновлять кэшированные данные. Методы классов взаимодействуют с внешними драйверами (например, xcache, memcache, db), обеспечивая гибкость системы.

Список классов в файле:

  1. Cache — основной класс для управления всеми типами кэширования.
  2. Xcache_driver — драйвер для работы с XCache (если расширение XCache доступно).
  3. Page_cache — класс для кэширования целых страниц.

Список методов и функций в файле:

  1. 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() — Перезагрузка привязок из базы данных.
  2. 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) — Сохранение данных в кэш.
  3. Page_cache

    • __construct($cache_dir, $permissions) — Инициализация кэша страницы.

Характеристика стиля PHP:

  1. Объектно-ориентированное программирование (ООП): Код написан в объектно-ориентированном стиле с использованием классов и методов. Например, класс Cache управляет различными драйверами кэширования, а методы внутри классов позволяют работать с кэшированием на разных уровнях.

  2. Использование глобальных переменных: В коде используется множество глобальных переменных (например, $cfg, $cot_cache_autoload, $cot_cache_drivers). Это позволяет динамически настраивать систему, но может затруднить понимание и тестирование кода.

  3. Чистота и читаемость кода: Код достаточно читаемый и использует ясные имена переменных и методов. Применяются такие практики, как использование описательных имён (например, clear(), trigger(), bind()), что облегчает понимание функционала.

  4. Отсутствие строгой типизации: Хотя в некоторых местах используются явные приведения типов (например, (int) $type), в целом отсутствует строгая типизация переменных и методов.

  5. Использование функций обработки данных: Код активно использует методы для обработки и подготовки данных перед их использованием (например, $db->quote() для экранирования данных, $db->prep() для подготовки значений).

  6. Обработка ошибок и исключений: В коде не используется явная обработка исключений с помощью 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('&amp;', '&', $parsedUrl['query']); // Заменяем '&amp;' на '&'
            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())));
    }

Объяснение:

  1. Проверка состояния кеша: Метод сначала проверяет, включен ли кеш и задано ли имя файла кеша. Если кеш не активен или имя файла не установлено, метод завершает выполнение.
  2. Создание директории: Далее метод проверяет, существует ли директория для кеша. Если её нет, она создается с соответствующими правами доступа.
  3. Запись в файл: Затем содержимое текущего буфера вывода (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;
    }
}

Объяснение:

  1. Проверка директории: Метод сначала проверяет, является ли переданный путь директорией с помощью is_dir.
  2. Чтение содержимого: Используется opendir для открытия директории и readdir для перебора файлов и поддиректорий.
  3. Рекурсивное удаление: Если элемент является поддиректорией, метод рекурсивно вызывает себя для удаления содержимого. Если элемент — файл, он удаляется с помощью unlink.
  4. Закрытие директории: После завершения работы с директорией вызывается closedir.
  5. Удаление самой директории: Когда все файлы и поддиректории удалены, вызывается 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();
    }
}

Объяснение:

  1. Комментарии к классу MySQL_cache: Класс предоставляет решение для кэширования, где данные хранятся в базе данных MySQL. Он, как правило, медленнее, чем кэширование на файловой системе (File_cache), но может быть более надёжным из-за использования базы данных.

  2. $buffer: Это приватная переменная, которая используется для хранения предварительно загруженных данных. Это помогает избежать повторных запросов к базе данных, тем самым улучшая производительность.

  3. __construct(): Конструктор класса, в котором есть случайный вызов сборщика мусора (GC) с вероятностью 10%. Этот сборщик мусора, вероятно, очищает устаревшие или ненужные кэшированные данные.

  4. __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); // Выполняем запрос на вставку или обновление данных
    }
}

Объяснение работы метода:

  1. Удаление данных из кэша:

    • Если в массиве $this->removed_data есть элементы, то формируется запрос на удаление этих данных из таблицы кэша $db_cache.
    • Для каждого элемента создаётся условие удаления по имени (c_name) и области (c_realm).
    • После формирования запроса на удаление, массив $this->removed_data очищается, и запрос выполняется с помощью метода $db->query().
  2. Запись данных в кэш:

    • Если в массиве $this->writeback_data есть данные для записи, формируется запрос на вставку в таблицу кэша.
    • Каждое значение сериализуется, и вычисляется время истечения срока действия (c_expire), если оно задано.
    • После формирования запроса, массив $this->writeback_data очищается.
    • Запрос завершён конструкцией ON DUPLICATE KEY UPDATE, которая позволяет обновлять уже существующие записи с таким же именем и областью, если они присутствуют в базе.

Ключевые моменты:

  • Оптимизация с помощью одного запроса: Метод выполняет удаление и запись данных в одном запросе для улучшения производительности.
  • Использование сериализации: Данные сериализуются перед записью в базу данных, чтобы сохранить сложные структуры данных.
  • Обработка обновлений: Используется ON DUPLICATE KEY UPDATE, чтобы обновлять запись, если она уже существует в базе.

Этот метод является важным элементом в реализации кэширования данных с использованием MySQL, так как он эффективно обрабатывает изменения в кэше, минимизируя количество запросов к базе данных.


28 минут чтения Administrator

Комментарии (0)

Комментарии отсутствуют
Добавление комментариев доступно только зарегистрированным пользователям

Автор контента

Administrator

Оффлайн

Administrator

Последняя авторизация: 05.07.2026 18:32

  • Страница размещена: 27.01.2025 22:12
  • Последнее обновление: 27.01.2025 23:17

Похожие страницы

Файл admin.cache.disk.php в Cotonti CMF
1 Файл admin.cache.disk.php в движке Cotonti CMF является частью административной панели, предназначенной для управления
Файл admin.cache.php в Cotonti
2 Основное назначение файла: Файл admin.cache.php в движке Cotonti CMF предназначен для управления внутренним кешем
HTML-шаблон admin.cache.tpl в Cotonti
3 Основное назначение файла и его место в системе Cotonti Файл шаблона admin.cache.tpl, расположенный в директории
HTML-шаблон admin.cache.disk.tpl в Cotonti
4 Файл шаблона admin.cache.disk.tpl является частью системы администрирования фреймворка Cotonti. Он выполняет роль