Файл extensions.php в Cotonti

Файл extensions.php в системной папке Cotonti CMF имеет основное назначение управлять расширениями системы. В частности, его функциональность охватывает операции, связанные с добавлением, удалением, обновлением и получением информации о расширениях (модулях и плагинах). Также он выполняет регистрационные действия для модулей и плагинов в ядре системы и работает с метаинформацией расширений, получаемой из их заголовков.

Основные действия, которые реализует код:

  1. Чтение и анализ заголовков расширений с помощью регулярных выражений, чтобы извлечь метаинформацию.
  2. Регистрация новых расширений в базе данных.
  3. Обновление и удаление расширений.
  4. Управление связями расширений с хуками (например, добавление или удаление привязок плагинов к определённым хук-пунктам).
  5. Обработка информации о категориях и зависимости расширений (модули и плагины).

Стиль PHP, который используется в данном файле, можно охарактеризовать как стандартный для программирования в Cotonti CMF:

  • Используются глобальные переменные, например, $db, для работы с базой данных.
  • Код имеет строгую структуру с ясным разделением функциональности на отдельные функции.
  • Активно используется обработка ошибок с использованием конструкций try-catch для работы с транзакциями в базе данных.
  • Применяются регулярные выражения и манипуляции с текстом для извлечения данных из файлов.
  • Используются стандартные средства работы с базой данных, такие как методы $db->insert(), $db->delete(), и $db->update().
  • Для улучшения читаемости и предотвращения ошибок работы с файлами используется конструкция @fopen() с проверкой на успешное открытие файла.

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

 

Вот список всех функций в файле extensions.php:

  1. cot_infoget
    Функция извлекает информацию из заголовков COT файлов.

  2. cot_extension_add
    Функция для регистрации расширения в базе данных.

  3. cot_extension_catcmp
    Функция сравнивает два расширения по коду категории.

  4. cot_extension_installed
    Функция проверяет, установлено ли расширение. (Помечена как устаревшая.)

  5. cot_extension_type
    Функция возвращает тип расширения (модуль или плагин). (Помечена как устаревшая.)

  6. cot_extension_list_info
    Функция возвращает массив с метаинформацией всех расширений в указанной директории.

  7. cot_extension_remove
    Функция удаляет расширение из базы данных.

  8. cot_extension_update
    Функция обновляет версию расширения в базе данных.

  9. cot_plugin_add
    Функция добавляет плагин или его части в реестр хуков.

  10. cot_plugin_remove
    Функция удаляет плагин или его части из реестра хуков.

Это все функции, которые реализованы в файле extensions.php.

 

/**
 * Plugin and Module Management API
 * 
 * @package API - Extensions
 * @copyright (c) Cotonti Team
 * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt
 */
// API для управления плагинами и модулями

use cot\extensions\ExtensionsControlService; // Подключение класса для управления расширениями
use cot\extensions\ExtensionsDictionary; // Подключение словаря расширений
use cot\extensions\ExtensionsService; // Подключение сервиса расширений

defined('COT_CODE') or die('Wrong URL'); // Проверка на правильность обращения (если не задан правильный код, выполнение прекращается)

// Requirements
require_once cot_incfile('auth'); // Подключение файла для работы с авторизацией
require_once cot_incfile('configuration'); // Подключение файла для работы с конфигурацией
require_once cot_langfile('admin', 'core'); // Подключение языкового файла для админки

 

/**
 * Значение, возвращаемое функцией cot_extension_install() при обновлении, 
 * если нечего обновлять
 */
const COT_EXT_NOTHING_TO_UPDATE = 2; // Значение для случая, когда обновление не требуется

/**
 * Приоритет выполнения части плагина по умолчанию
 */
const COT_PLUGIN_DEFAULT_ORDER = 10; // Стандартный приоритет выполнения частей плагина

/**
 * Эти части ($name.$part.php) являются зарезервированными обработчиками без хуков
 */
$cot_ext_ignore_parts = ['configure', 'install', 'setup', 'uninstall']; // Массив зарезервированных частей плагина

 

/**
 * Применяет пользовательские SQL и PHP патчи из указанной директории. Сообщения об ошибках и успехах
 * выводятся через стандартное API сообщений в процессе выполнения.
 *
 * @param string $directory Путь к директории
 * @param string $from_ver Текущая версия, с которой начинать патчинг
 * @param string $sql_pattern Шаблон имени файла для SQL патча (PCRE)
 * @param string $php_pattern Шаблон имени файла для PHP патча (PCRE)
 * @return string|bool Функция возвращает TRUE, если нет патчей для применения,
 * FALSE, если произошла ошибка при патчинге, или строку, содержащую номер версии последнего примененного патча, если патчинг прошел успешно.
 */
function cot_apply_patches(
    $directory,
    $from_ver,
	$sql_pattern = 'patch_([\w\.\-\_]+)\.(sql)', // Шаблон для файлов SQL патчей
	$php_pattern = 'patch_([\w\.\-\_]+)\.(inc)'  // Шаблон для файлов PHP патчей
)
{
    global $L, $Ls, $cfg, $R, $db;

	// Ищем новые патчи
	$dp = opendir($directory); // Открытие директории для чтения
	$delta = []; // Массив для хранения патчей
	while ($f = readdir($dp)) { // Чтение файлов из директории
		if (
            preg_match('#^' . $sql_pattern . '$#', $f, $mt) // Проверка на соответствие шаблону SQL
			|| preg_match('#^' . $php_pattern . '$#', $f, $mt) // Проверка на соответствие шаблону PHP
        ) {
			$type = $mt[2] == 'sql' ? 'sql' : 'php'; // Определение типа патча (sql или php)
			$ver = $mt[1]; // Извлечение версии из имени файла
			if (version_compare($ver, $from_ver) > 0 && !isset($delta[$ver][$type])) { // Проверка, что версия больше текущей
				$delta[$ver][$type] = $directory . '/' . $f; // Добавляем патч в массив
			}
		}
	}
	closedir($dp); // Закрытие директории
	if (count($delta) == 0) { // Если нет новых патчей
		return true; // Возвращаем TRUE, так как патчей для применения нет
	}

	// Применяем патчи по порядку версий
	uksort($delta, 'version_compare'); // Сортируем массив патчей по версии

	$max_ver = $from_ver; // Инициализируем максимальную версию как текущую
    static $executed = []; // Статическая переменная для хранения уже примененных патчей
	foreach ($delta as $key => $val) { // Проходим по всем найденным патчам
		if (isset($val['sql']) && !in_array($val['sql'], $executed)) { // Если есть SQL патч и он еще не применен
			$error = Cot::$db->runScript(file_get_contents($val['sql'])); // Выполняем SQL патч
            $executed[] = $val['sql']; // Добавляем патч в список примененных
			if (empty($error)) { // Если ошибки нет
				cot_message(cot_rc('ext_patch_applied',
					array('f' => $val['sql'], 'msg' => 'OK'))); // Выводим сообщение об успешном применении
			} else { // Если произошла ошибка
				cot_error(cot_rc('ext_patch_error', array('f' => $val['sql'], 'msg' => $error))); // Выводим сообщение об ошибке
				return false; // Возвращаем FALSE, так как произошла ошибка
			}
		}
		if (isset($val['php']) && !in_array($val['php'], $executed)) { // Если есть PHP патч и он еще не применен
			$ret = include $val['php']; // Подключаем PHP патч
            $executed[] = $val['php']; // Добавляем патч в список примененных
			if ($ret !== false) { // Если патч выполнен успешно
				$msg = $ret == 1 ? 'OK' : $ret; // Определяем сообщение (если ret равно 1 - успех)
				cot_message(cot_rc('ext_patch_applied', array('f' => $val['php'], 'msg' => $msg))); // Выводим сообщение об успешном применении
			} else { // Если патч не выполнен
				cot_error(cot_rc('ext_patch_error', array('f' => $val['php'], 'msg' => $L['Error']))); // Выводим сообщение об ошибке
				return false; // Возвращаем FALSE, так как произошла ошибка
			}
		}
		$max_ver = $key; // Обновляем максимальную версию
	}

	return $max_ver; // Возвращаем версию последнего примененного патча
}

 

/**
 * Проверяет, выполнены ли все зависимости для выбранного расширения. Это означает,
 * что все необходимые модули и плагины уже установлены или выбраны для установки.
 *
 * Сообщения о неудовлетворенных требованиях выводятся через систему ошибок и сообщений.
 *
 * @param string $name Код расширения
 * @param bool $is_module TRUE для модулей, FALSE для плагинов
 * @param array $selected_modules Список модулей, выбранных для установки
 * @param array $selected_plugins Список плагинов, выбранных для установки
 * @return bool TRUE, если все зависимости выполнены, или FALSE в противном случае
 */
function cot_extension_dependencies_statisfied($name, $is_module = false,
	$selected_modules = array(), $selected_plugins = array())
{
	global $cfg, $L;
	$path = $is_module ? $cfg['modules_dir'] . "/$name" : $cfg['plugins_dir'] . "/$name"; // Путь к расширению в зависимости от его типа
	$ret = true; // Инициализируем переменную для проверки

	// Получаем список зависимостей
	$info = cot_infoget("$path/$name.setup.php", 'COT_EXT'); // Загружаем информацию о расширении
	$required_modules = empty($info['Requires_modules']) ? array() // Если нет требуемых модулей, создаем пустой массив
		: explode(',', $info['Requires_modules']); // Разбиваем строку с модулями на массив
	$required_modules = array_map('trim', $required_modules); // Убираем лишние пробелы из элементов массива
	$required_plugins = empty($info['Requires_plugins']) ? array() // Если нет требуемых плагинов, создаем пустой массив
		: explode(',', $info['Requires_plugins']); // Разбиваем строку с плагинами на массив
	$required_plugins = array_map('trim', $required_plugins); // Убираем лишние пробелы из элементов массива

	// Проверяем каждую зависимость
	foreach ($required_modules as $req_ext) { // Для каждого требуемого модуля
		if (!empty($req_ext) && !in_array($req_ext, $selected_modules) // Если модуль не пустой и не выбран для установки
			&& !cot_extension_installed($req_ext)) { // И если модуль не установлен
			cot_error(cot_rc('ext_dependency_error', array( // Выводим ошибку о зависимости
				'name' => $name,
				'type' => $is_module ? $L['Module'] : $L['Plugin'], // Тип расширения (модуль или плагин)
				'dep_type' => $L['Module'], // Тип зависимости (модуль)
				'dep_name' => $req_ext // Имя требуемого модуля
			)));
			$ret = false; // Устанавливаем результат в false, так как зависимость не выполнена
		}
	}

	foreach ($required_plugins as $req_ext) { // Для каждого требуемого плагина
		if (!empty($req_ext) && !in_array($req_ext, $selected_plugins) // Если плагин не пустой и не выбран для установки
			&& !cot_extension_installed($req_ext)) { // И если плагин не установлен
			cot_error(cot_rc('ext_dependency_error', array( // Выводим ошибку о зависимости
				'name' => $name,
				'type' => $is_module ? $L['Module'] : $L['Plugin'], // Тип расширения (модуль или плагин)
				'dep_type' => $L['Plugin'], // Тип зависимости (плагин)
				'dep_name' => $req_ext // Имя требуемого плагина
			)));
			$ret = false; // Устанавливаем результат в false, так как зависимость не выполнена
		}
	}

	return $ret; // Возвращаем результат проверки (TRUE, если все зависимости выполнены)
}

 

/**
 * Устанавливает или обновляет расширение Cotonti: модуль или плагин.
 * Сообщения, выводимые во время установки, могут быть получены через стандартный
 * интерфейс сообщений Cotonti.
 * 
 * @param string $extensionCode Код модуля (или плагина)
 * @param bool $isModule TRUE для модулей, FALSE для плагинов
 * @param bool $update Выполнить обновление, а не новую установку
 * @param bool $force_update Принудительное обновление расширения, даже если версия не изменилась
 * @return bool Статус операции
 * @global Cache $cache
 *
 * @todo переместить в ExtensionControlService или создать отдельный ExtensionSetupService
 */
function cot_extension_install($extensionCode, $isModule = false, $update = false, $force_update = false)
{

 

global $cfg, $L, $R, $cache, $usr, $db_auth, $db_config, $db_users,
		$db_core, $cot_groups, $cot_ext_ignore_parts, $db, $db_x, $env;

/** @deprecated in 0.9.24 for backward compatibility */
$is_module = $isModule; // @deprecated в 0.9.24 для обеспечения совместимости с предыдущими версиями
/** @deprecated in 0.9.24 for backward compatibility */
$name = $extensionCode; // @deprecated в 0.9.24 для обеспечения совместимости с предыдущими версиями

$path = $isModule ? Cot::$cfg['modules_dir'] . "/$extensionCode" : Cot::$cfg['plugins_dir'] . "/$extensionCode"; // Определение пути к модулю или плагину в зависимости от типа расширения

// Отправка начального сообщения
if ($update) { // Если выполняется обновление
    cot_message(cot_rc('ext_updating', array(
        'type' => $isModule ? Cot::$L['Module'] : Cot::$L['Plugin'], // Указание типа расширения (модуль или плагин)
        'name' => $extensionCode, // Имя расширения
    )));
} else { // Если выполняется установка
    cot_message(cot_rc('ext_installing', array(
        'type' => $isModule ? $L['Module'] : $L['Plugin'], // Указание типа расширения (модуль или плагин)
        'name' => $extensionCode, // Имя расширения
    )));
}

// Проверка наличия файла настройки и тегов
$setup_file = $path . "/$extensionCode.setup.php"; // Путь к файлу настройки расширения
if (!file_exists($setup_file)) { // Если файл настройки не найден
    cot_error(cot_rc('ext_setup_not_found', array('path' => $setup_file))); // Вывод ошибки
    return false; // Возвращаем false, если файл не найден
}

$old_ext_format = false; // Переменная для проверки старого формата расширения

$info = cot_infoget($setup_file, 'COT_EXT'); // Попытка получить информацию о расширении в новом формате
if (!$info && cot_plugin_active('genoa')) { // Если информация не найдена и активирован плагин Genoa
    // Пытаемся загрузить информацию в старом формате
    $info = cot_infoget($setup_file, 'SED_EXTPLUGIN'); // Попытка получить информацию в старом формате
    if ($info) {
        $old_ext_format = true; // Если информация найдена в старом формате, устанавливаем флаг
    }
}

if ($info === false) { // Если информация не найдена
    cot_error('ext_invalid_format'); // Вывод ошибки о неверном формате расширения
    return false; // Возвращаем false, если формат неверен
}

Глобальные переменные:

  • $cfg: глобальный массив конфигурации, который содержит параметры конфигурации системы, такие как пути к директориям, настройки базы данных и прочее.
  • $L: глобальный массив, содержащий локализованные строки (переводы) для разных языков, используемых на сайте.
  • $R: глобальный массив, который может использоваться для хранения различных ресурсов, таких как шаблоны, изображения, стили и скрипты.
  • $cache: объект или массив для работы с кешем данных, используется для хранения и извлечения временных данных.
  • $usr: глобальный массив или объект, содержащий информацию о текущем пользователе, например, его идентификатор, статус и разрешения.
  • $db_auth: глобальная переменная для работы с данными аутентификации, например, для проверки учетных записей пользователей.
  • $db_config: глобальная переменная для работы с конфигурационными данными базы данных.
  • $db_users: глобальная переменная для работы с данными пользователей в базе данных.
  • $db_core: глобальная переменная для работы с основными данными, такими как таблицы пользователей, групп, и другие ключевые данные.
  • $cot_groups: глобальная переменная для работы с группами пользователей на сайте.
  • $cot_ext_ignore_parts: массив, содержащий части расширений, которые должны быть проигнорированы (например, установочные или конфигурационные файлы).
  • $db: глобальная переменная, представляющая подключение к базе данных.
  • $db_x: глобальная переменная для работы с таблицами базы данных, связана с функциональностью расширений.
  • $env: глобальная переменная, представляющая окружение текущего запроса или сеанса.

 

	// Проверка версии
	$res = Cot::$db->query('SELECT ct_version FROM ' . Cot::$db->core . ' WHERE ct_code = ?', $extensionCode);
	if ($res->rowCount() == 1) {
		$current_ver = $res->fetchColumn(); // Извлекаем текущую версию расширения из базы данных
		$res->closeCursor(); // Закрываем курсор, так как данные извлечены

		if ($update) {
			// Если выполняется обновление
			if (version_compare($current_ver, $info['Version']) == 0 && !$force_update) {
				// Если текущая версия уже совпадает с той, что указана в setup файле и не требуется принудительное обновление
				cot_message(cot_rc('ext_up2date', array(
					'type' => $isModule ? Cot::$L['Module'] :Cot::$L['Plugin'],
					'name' => $extensionCode
				))); // Отправляем сообщение, что расширение актуально

				return COT_EXT_NOTHING_TO_UPDATE; // Возвращаем константу, указывающую, что обновление не требуется
			}
		} else {
			// Если это не обновление, а новая установка
			cot_clear_messages(); // Очищаем все текущие сообщения
			cot_error(cot_rc('ext_already_installed', array('name' => $extensionCode))); // Выводим ошибку, что расширение уже установлено
			return false; // Возвращаем false, так как расширение уже установлено
		}
	}

 

$registeredParts = []; // Инициализация пустого массива для хранения зарегистрированных частей расширения
if ($update) { // Если выполняется обновление
    $query = $query = Cot::$db->query( // Выполнение SQL-запроса
        'SELECT * FROM ' . Cot::$db->plugins . ' WHERE pl_code = :code', // Запрос для получения данных расширения по коду
        ['code' => $extensionCode] // Параметр запроса с кодом расширения
    );
    while ($row = $query->fetch()) { // Проходим по всем строкам результата запроса
        if (!isset($registeredParts[$row['pl_part']]['hooks'])) { // Если для текущей части нет списка хуков
            $registeredParts[$row['pl_part']]['hooks'] = []; // Инициализируем пустой массив для хуков
        }
        $registeredParts[$row['pl_part']]['hooks'][$row['pl_hook']] = [ // Добавляем хук для текущей части
            'hook' => $row['pl_hook'], // Хук
            'order' => (int) $row['pl_order'], // Порядок хуков (приводим к целому числу)
        ];
        unset($row['pl_hook'], $row['pl_order']); // Удаляем элементы массива, которые не нужны дальше
        $registeredParts[$row['pl_part']] = array_merge($registeredParts[$row['pl_part']], $row); // Объединяем остальные данные с текущей частью
    }
    $query->closeCursor(); // Закрытие курсора после выполнения запроса

    // Безопасное удаление существующих привязок
    $bindings_cnt = cot_plugin_remove($extensionCode); // Удаление привязок плагина
    cot_message(cot_rc('ext_bindings_uninstalled', array('cnt' => $bindings_cnt))); // Отправка сообщения о том, сколько привязок было удалено
}

 

// Установка частей хука и привязок
$hookBindings = []; // Инициализация массива для хранения привязок хуков
$dp = opendir($path); // Открытие директории расширения
while ($f = readdir($dp)) { // Чтение файлов в директории
    if (
        preg_match("#^$extensionCode(\.([\w\.]+))?.php$#", $f, $mt) // Проверка, что имя файла соответствует шаблону расширения
        && (!isset($mt[2]) || !in_array($mt[2], $cot_ext_ignore_parts)) // Проверка, что часть не игнорируется
    ) {
        $part_info = cot_infoget($path . "/$f", 'COT_EXT'); // Получение информации о части из файла
        if (!$part_info && cot_plugin_active('genoa')) { // Если информация не получена, пытаемся загрузить в старом формате
            $part_info = cot_infoget($path . "/$f", 'SED_EXTPLUGIN');
        }
        if ($part_info) { // Если информация получена
            if (empty($part_info['Hooks'])) { // Если хук не задан, используем дефолтное значение
                $hooks = $isModule ? ['module'] : ['standalone'];
            } else {
                $hooks = explode(',', $part_info['Hooks']); // Разделяем хуки по запятой
                $hooks = is_array($hooks) ? array_map('trim', $hooks) : array(); // Очищаем пробелы
            }
            if (empty($part_info['Order'])) { // Если порядок не задан, используем дефолтный
                $order = COT_PLUGIN_DEFAULT_ORDER;
            } else {
                $order = array_map('trim', explode(',', $part_info['Order'])); // Разделяем порядок по запятой
                if (count($order) == 1 || count($order) < count($hooks)) { // Если порядок один, применяем его ко всем хукам
                    $order = (int) $order[0];
                }
            }

            $i = 0;
            foreach ($hooks as $hook) { // Для каждого хука
                $hookBinding = [ // Создаём привязку для хука
                    'part' => !isset($mt[2]) ? 'main' : $mt[2], // Определяем часть (основная или по имени)
                    'file' => $f, // Файл, к которому относится хук
                    'hook' => $hook, // Имя хука
                    'order' => isset($order[$i]) ? (int) $order[$i] : $order, // Порядок хука
                ];
                if (isset($registeredParts[$hookBinding['part']])) { // Если часть уже зарегистрирована
                    $hookBinding['active'] = $registeredParts[$hookBinding['part']]['pl_active']; // Устанавливаем активность
                }
                $hookBindings[] = $hookBinding; // Добавляем привязку к списку
                ++$i;
            }
        }
    }
}

closedir($dp); // Закрытие директории
$bindings_cnt = cot_plugin_add($hookBindings, $extensionCode, $info['Name'], $isModule); // Добавление привязок плагина
cot_message(cot_rc('ext_bindings_installed', ['cnt' => $bindings_cnt])); // Сообщение о количестве установленных привязок

 

// Установка конфигурации
$info_cfg = cot_infoget($setup_file, 'COT_EXT_CONFIG'); // Получение конфигурации расширения из setup файла
if (!$info_cfg && cot_plugin_active('genoa')) { // Если конфигурация не найдена и активен плагин 'genoa'
    // Пытаемся загрузить конфигурацию в старом формате
    $info_cfg = cot_infoget($setup_file, 'SED_EXTPLUGIN_CONFIG');
}
$options = cot_config_parse($info_cfg, $isModule); // Разбор конфигурации с учетом модуля или плагина

if ($update) { // Если обновление расширения
    // Получаем дифференциальную конфигурацию
    if (cot_config_update($extensionCode, $options, $isModule) > 0) { // Обновляем конфигурацию
        cot_message('ext_config_updated'); // Сообщение об успешном обновлении конфигурации
    }
} elseif (count($options) > 0) { // Если конфигурация не пуста
    if (cot_config_add($extensionCode, $options, $isModule)) { // Добавляем новую конфигурацию
        cot_message('ext_config_installed'); // Сообщение об успешной установке конфигурации
    } else { // Если произошла ошибка при установке конфигурации
        cot_error('ext_config_error'); // Сообщение об ошибке при установке конфигурации
        return false; // Возвращаем false, чтобы остановить дальнейшее выполнение
    }
}

 

// Установка конфигурации структуры, если она присутствует
$info_cfg = cot_infoget($setup_file, 'COT_EXT_CONFIG_STRUCTURE'); // Получение конфигурации структуры из setup файла
if ($info_cfg) { // Если конфигурация структуры найдена
    $options = cot_config_parse($info_cfg, $isModule); // Разбор конфигурации структуры с учетом модуля или плагина
    if ($update) { // Если обновление расширения
        if (cot_config_update($extensionCode, $options, $isModule, '__default') > 0) { // Обновляем конфигурацию структуры по умолчанию
            // Обновление всех вложенных категорий
            $type = $isModule ? 'module' : 'plug'; // Определяем тип: модуль или плагин
            $res = $db->query("SELECT DISTINCT config_subcat FROM $db_config
                WHERE config_owner = '$type' AND config_cat = '$extensionCode'
                    AND config_subcat != '' AND config_subcat != '__default'"); // Получаем список всех подкатегорий
            $cat_list = $res->fetchAll(PDO::FETCH_COLUMN, 0); // Извлекаем подкатегории
            foreach ($cat_list as $cat) { // Для каждой подкатегории
                cot_config_update($extensionCode, $options, $isModule, $cat); // Обновляем конфигурацию для подкатегории
            }
            cot_message('ext_config_struct_updated'); // Сообщение об успешном обновлении конфигурации структуры
        }
    } elseif (count($options) > 0) { // Если конфигурация не пуста
        if (cot_config_add($extensionCode, $options, $isModule, '__default')) { // Добавляем новую конфигурацию структуры
            cot_message('ext_config_struct_installed'); // Сообщение об успешной установке конфигурации структуры
        } else { // Если произошла ошибка при установке конфигурации структуры
            cot_error('ext_config_struct_error'); // Сообщение об ошибке при установке конфигурации структуры
            return false; // Возвращаем false, чтобы остановить дальнейшее выполнение
        }
    }
}

 

// Установка / Обновление аутентификации
if (!isset($info['Auth_guests'])) { // Если в информации не указана аутентификация для гостей
    $info['Auth_guests'] = ''; // Устанавливаем значение по умолчанию
}
if (!isset($info['Lock_guests'])) { // Если в информации не указаны блокировки для гостей
    $info['Lock_guests'] = ''; // Устанавливаем значение по умолчанию
}
if (!isset($info['Auth_members'])) { // Если в информации не указана аутентификация для пользователей
    $info['Auth_members'] = ''; // Устанавливаем значение по умолчанию
}
if (!isset($info['Lock_members'])) { // Если в информации не указаны блокировки для пользователей
    $info['Lock_members'] = ''; // Устанавливаем значение по умолчанию
}
if ($update) { // Если это обновление
    // Обновляем только блокировки аутентификации
    if ($isModule) { // Если это модуль
        $auth_code = $extensionCode; // Код аутентификации для модуля
        $auth_option = 'a'; // Опция для аутентификации
    } else { // Если это плагин
        $auth_code = 'plug'; // Код аутентификации для плагина
        $auth_option = $extensionCode; // Опция для аутентификации
    }

    $lock_guests = cot_auth_getvalue($info['Lock_guests']); // Получаем блокировку для гостей
    $db->update($db_auth, array('auth_rights_lock' => $lock_guests), "
        auth_code = '$auth_code' AND auth_option = '$auth_option'
        AND (auth_groupid = " . COT_GROUP_GUESTS
            . ' OR auth_groupid = ' . COT_GROUP_INACTIVE . ')'); // Обновляем блокировку для гостей и неактивных пользователей

    $lock_members = cot_auth_getvalue($info['Lock_members']); // Получаем блокировку для пользователей
    $ingore_groups = implode(',', array(
        COT_GROUP_GUESTS,
        COT_GROUP_INACTIVE,
        COT_GROUP_BANNED,
        COT_GROUP_SUPERADMINS
    )); // Игнорируем группы: гости, неактивные, заблокированные и супер администраторы
    $db->update($db_auth, array('auth_rights_lock' => $lock_members),
        "auth_code = '$auth_code' AND auth_option = '$auth_option' AND auth_groupid NOT IN ($ingore_groups)"); // Обновляем блокировку для остальных групп

    cot_message('ext_auth_locks_updated'); // Сообщение о том, что блокировки аутентификации обновлены
} else { // Если это установка
    // Устанавливаем аутентификацию
    $insert_rows = array(); // Массив для вставки данных аутентификации
    foreach ($cot_groups as $v) { // Для каждой группы
        $v['skiprights'] = isset($v['skiprights']) ? $v['skiprights'] : false; // Проверяем, нужно ли пропускать права
        if (!$v['skiprights']) { // Если не нужно пропускать права
            if ($v['id'] == COT_GROUP_GUESTS || $v['id'] == COT_GROUP_INACTIVE) { // Если группа - гости или неактивные пользователи
                $ins_auth = cot_auth_getvalue($info['Auth_guests']); // Получаем значение аутентификации для гостей
                $ins_lock = cot_auth_getvalue($info['Lock_guests']); // Получаем значение блокировки для гостей

                if ($ins_auth > 128 || $ins_lock < 128) { // Если аутентификация превышает 128 или блокировка меньше 128
                    $ins_auth = ($ins_auth > 127) ? $ins_auth - 128 : $ins_auth; // Корректируем значение аутентификации
                    $ins_lock = 128; // Устанавливаем значение блокировки для гостей
                }
            } elseif ($v['id'] == COT_GROUP_BANNED) { // Если группа - заблокированные пользователи
                $ins_auth = 0; // Аутентификация для заблокированных - 0
                $ins_lock = 255; // Блокировка для заблокированных - 255
            } elseif ($v['id'] == COT_GROUP_SUPERADMINS) { // Если группа - супер администраторы
                $ins_auth = 255; // Аутентификация для супер администраторов - 255
                $ins_lock = 255; // Блокировка для супер администраторов - 255
            } else { // Для остальных групп
                $ins_auth = cot_auth_getvalue($info['Auth_members']); // Получаем значение аутентификации для пользователей
                $ins_lock = cot_auth_getvalue($info['Lock_members']); // Получаем значение блокировки для пользователей
            }

            if ($isModule) { // Если это модуль
                $insert_rows[] = array( // Добавляем данные для вставки
                    'auth_groupid' => $v['id'],
                    'auth_code' => $extensionCode,
                    'auth_option' => 'a',
                    'auth_rights' => $ins_auth,
                    'auth_rights_lock' => $ins_lock,
                    'auth_setbyuserid' => $usr['id']
                );
            } else { // Если это плагин
                $insert_rows[] = array( // Добавляем данные для вставки
                    'auth_groupid' => $v['id'],
                    'auth_code' => 'plug',
                    'auth_option' => $extensionCode,
                    'auth_rights' => $ins_auth,
                    'auth_rights_lock' => $ins_lock,
                    'auth_setbyuserid' => $usr['id']
                );
            }
        }
    }

    if ($db->insert($db_auth, $insert_rows)) { // Если данные успешно вставлены в базу
        $db->update($db_users, array('user_auth' => ''), "user_auth != ''"); // Очищаем поле user_auth для всех пользователей, у которых оно не пустое
        cot_message('ext_auth_installed'); // Сообщение об успешной установке аутентификации
    }
}

 

$new_ver = ''; // Инициализация переменной для новой версии
if ($update) { // Если это обновление
    // Находим и применяем патчи
    if (file_exists("$path/setup")) { // Если существует папка с патчами
        $new_ver = cot_apply_patches("$path/setup", $current_ver); // Применяем патчи и получаем новую версию
    }
    if (version_compare($info['Version'], $new_ver) > 0 || $new_ver === true) { // Если новая версия больше текущей или патчи применены успешно
        $new_ver = $info['Version']; // Устанавливаем версию из информации об расширении
    }
} else { // Если это установка
    if (file_exists($path . "/setup/$extensionCode.install.sql")) { // Если существует SQL-скрипт для установки
        // Выполняем SQL-скрипт установки
        $sql_err = $db->runScript(
            file_get_contents("$path/setup/$extensionCode.install.sql")); // Запускаем скрипт
        if (empty($sql_err)) { // Если ошибок нет
            cot_message(cot_rc('ext_executed_sql', array('ret' => 'OK'))); // Сообщение об успешном выполнении SQL-скрипта
        } else { // Если есть ошибки
            cot_error(cot_rc('ext_executed_sql', array('ret' => $sql_err))); // Сообщение об ошибках
            return false; // Возвращаем false
        }
    }

    $install_handler = $old_ext_format ? $setup_file : $path . "/setup/$extensionCode.install.php"; // Определяем путь к PHP-обработчику установки

    if ($old_ext_format) { // Если используется старый формат
        global $action;
        $action = 'install'; // Устанавливаем действие как 'install'
    }

    if (file_exists($install_handler)) { // Если существует файл обработчика установки
        // Выполняем PHP-обработчик установки
        $envtmp = $env; // Сохраняем текущее состояние переменной $env
        $env = array( // Устанавливаем параметры окружения для установки
            'ext' => $extensionCode,
            'location' => $extensionCode,
            'type' => ($isModule) ? 'module' : 'plug' // Определяем тип расширения
        );
        $ret = include $install_handler; // Подключаем и выполняем файл обработчика
        $env = $envtmp; // Восстанавливаем исходное состояние переменной $env

        if ($ret !== false) { // Если выполнение обработчика прошло успешно
            $msg = $ret == 1 ? 'OK' : $ret; // Устанавливаем сообщение в зависимости от результата
            cot_message(cot_rc('ext_executed_php', array('ret' => $msg))); // Сообщение о выполнении PHP-обработчика
        } else { // Если выполнение обработчика завершилось с ошибкой
            cot_error(cot_rc('ext_executed_php',
                array('ret' => $msg ? $msg : $L['Error']))); // Сообщение об ошибке
            return false; // Возвращаем false
        }
    }
}

 

// Регистрируем информацию о версии
if ($update) { // Если это обновление
    cot_extension_update($extensionCode, $new_ver, !$isModule); // Обновляем информацию о расширении

    ExtensionsControlService::getInstance()->checkIsActive($extensionCode); // Проверяем, активировано ли расширение

    cot_message(cot_rc('ext_updated', array( // Отправляем сообщение об успешном обновлении
        'type' => $isModule ? $L['Module'] : $L['Plugin'], // Тип расширения (модуль или плагин)
        'name' => $extensionCode, // Имя расширения
        'ver' => $new_ver // Новая версия
    )));

    /* === Hook  === */
    foreach (cot_getextplugins('extension.update.done') as $pl) { // Применяем хуки после обновления расширения
        include $pl; // Подключаем и выполняем хук
    }
    /* ===== */

} else { // Если это установка
    cot_extension_add($extensionCode, $info['Name'], $info['Version'], !$isModule); // Регистрируем новое расширение

    /* === Hook  === */
    foreach (cot_getextplugins('extension.install.done') as $pl) { // Применяем хуки после установки расширения
        include $pl; // Подключаем и выполняем хук
    }
    /* ===== */
}

// Очистка
cot_auth_reorder(); // Перераспределение прав доступа
$cache && $cache->clear(); // Очистка кэша, если он активирован

конец функции cot_extension_install

	return true;
}

 

код ниже выполняет удаление расширения (модуля или плагина), включая очистку конфигураций, привязок, структуры, выполнение SQL-скриптов, запуск PHP-обработчиков, удаление расширения из основной таблицы и очистку кэша.

/**
 * Удаляет расширение и все его данные
 * @param string $code Код расширения
 * @param bool $isModule TRUE для модулей, FALSE для плагинов
 * @global CotDB $db
 * @global Cache $cache
 */
function cot_extension_uninstall($code, $isModule = false)
{
	global $cfg, $db_auth, $db_config, $db_users, $db_updates, $cache, $db, $db_x, $db_plugins, $cot_plugins,
           $cot_plugins_enabled, $cot_modules, $env, $structure, $db_structure, $L, $R;

	$path = $isModule ? $cfg['modules_dir'] . "/$code" : $cfg['plugins_dir'] . "/$code"; // Определяем путь к расширению

	// Отправляем начальное сообщение
	cot_message(cot_rc('ext_uninstalling', array(
		'type' => $isModule ? Cot::$L['Module'] : Cot::$L['Plugin'], // Тип расширения (модуль или плагин)
		'name' => $code // Имя расширения
	)));

	// Удаляем привязки плагина
	cot_plugin_remove($code);

	// Удаляем данные авторизации и конфигурации
	if ($isModule) { 
        Cot::$db->delete($db_config, "config_owner = 'module' AND config_cat = '$code'"); // Удаляем конфигурацию модуля
        Cot::$db->delete($db_auth, "auth_code = '$code'"); // Удаляем права доступа для модуля
	} else {
        Cot::$db->delete($db_config, "config_owner = 'plug' AND config_cat = '$code'"); // Удаляем конфигурацию плагина
        Cot::$db->delete($db_auth, "auth_code = 'plug' AND auth_option = '$code'"); // Удаляем права доступа для плагина
	}
	cot_message('ext_auth_uninstalled'); // Отправляем сообщение об удалении прав доступа
	cot_message('ext_config_uninstalled'); // Отправляем сообщение об удалении конфигурации

	// Удаляем структуру расширения
	if ($isModule && isset($structure[$code])) {
		$db->delete($db_structure, "structure_area = ?", $code); // Удаляем структуру для модуля
		unset($structure[$code]); // Удаляем структуру из массива
	}

	// Запускаем SQL-скрипт, если он есть
	if (file_exists($path . "/setup/$code.uninstall.sql")) {
		$sql_err = $db->runScript(
			file_get_contents("$path/setup/$code.uninstall.sql")); // Выполняем SQL-скрипт
		if (empty($sql_err)) {
			cot_message(cot_rc('ext_executed_sql', ['ret' => 'OK'])); // Отправляем сообщение об успешном выполнении SQL
		} else {
			cot_error(cot_rc('ext_executed_sql', ['ret' => $sql_err])); // Отправляем сообщение об ошибке в SQL
		}
	}

	// Запускаем обработчик удаления
	if (cot_plugin_active('genoa') && cot_infoget($path . "/$code.setup.php", 'SED_EXTPLUGIN')) {
		global $action;
		$action = 'uninstall'; // Устанавливаем действие как 'uninstall'
		$uninstall_handler = $path . "/$code.setup.php"; // Указываем путь к обработчику удаления
	} else {
		$uninstall_handler = $path . "/setup/$code.uninstall.php"; // Указываем путь к установочному скрипту для плагина
	}

	if (file_exists($uninstall_handler)) { 
		$envtmp = $env;
		$env = [
			'ext' => $code, // Устанавливаем переменные окружения для расширения
			'location' => $code,
			'type' => ($isModule) ? ExtensionsDictionary::TYPE_MODULE : ExtensionsDictionary::TYPE_PLUGIN,
		];
		$ret = include $uninstall_handler; // Выполняем обработчик
		$env = $envtmp;

		if ($ret !== false) {
			cot_message(cot_rc('ext_executed_php', ['ret' => $ret])); // Отправляем сообщение о выполнении PHP-скрипта
		} else {
			cot_error(cot_rc('ext_executed_php', ['ret' => Cot::$L['Error']])); // Отправляем сообщение об ошибке в PHP-скрипте
		}
	}

	// Убираем расширение из основной таблицы
	cot_extension_remove($code, !$isModule);

	$sql = $db->query("SELECT pl_code, pl_file, pl_hook, pl_module FROM $db_plugins
		WHERE pl_active = 1 ORDER BY pl_hook ASC, pl_order ASC"); // Получаем все активные плагины
	$cot_plugins = array();
	if ($sql->rowCount() > 0) {
		while ($row = $sql->fetch()) {
			$cot_plugins[$row['pl_hook']][] = $row; // Заполняем массив с плагинами по хук-ключу
		}
		$sql->closeCursor();
	}

	if (!$isModule) {
		unset($cot_plugins_enabled[$code]); // Удаляем плагин из списка активных плагинов
	} else {
		unset($cot_modules[$code]); // Удаляем модуль из списка активных модулей
	}

    /* === Hook  === */
    foreach (cot_getextplugins('extension.uninstall.done') as $pl) { // Выполняем хуки после удаления расширения
        include $pl;
    }
    /* ===== */

	// Очищаем кэш
	$db->update($db_users, array('user_auth' => ''), "user_auth != ''"); // Сбрасываем кэш прав доступа пользователей
	$cache && $cache->clear(); // Очищаем кэш, если он включен
}

 

/**
 * Парсит заголовок PHPDoc из файла в массив
 *
 * @param string $filename Путь к PHP файлу
 * @return array Ассоциативный массив, содержащий содержимое PHPDoc. Массив будет
 *  пустым, если PHPDoc не найден
 */
function cot_file_phpdoc($filename)
{
	$res = array(); // Инициализация пустого массива для хранения результатов

	$data = file_get_contents($filename); // Чтение содержимого файла в переменную $data

	// Проверка на наличие блока PHPDoc в файле с использованием регулярного выражения
	if (preg_match('#^/\*\*(.*?)^\s\*/#ms', $data, $mt)) 
	{
		$phpdoc = preg_split('#\r?\n\s\*\s@#', $mt[1]); // Разделение содержимого PHPDoc по строкам с использованием @ как разделителя
		$cnt = count($phpdoc); // Получение количества элементов в массиве $phpdoc

		if ($cnt > 0) // Если в массиве есть элементы
		{
			$res['description'] = trim(preg_replace('#\r?\n\s\*\s?#', '', $phpdoc[0])); 
			// Очищаем описание (первую строку) от лишних символов и сохраняем его в ключ 'description'

			for ($i = 1; $i < $cnt; $i++) // Начинаем цикл с 1, чтобы пропустить описание
			{
				$delim = mb_strpos($phpdoc[$i], ' '); // Находим позицию первого пробела в строке, это разделитель ключа и значения
				$key = mb_substr($phpdoc[$i], 0, $delim); // Извлекаем ключ (часть до пробела)

				// Извлекаем значение (всё, что после пробела) и очищаем от лишних символов
				$contents = trim(preg_replace('#\r?\n\s\*\s?#', '', mb_substr($phpdoc[$i], $delim + 1))); 
				$res[$key] = $contents; // Добавляем ключ и значение в ассоциативный массив $res
			}
		}
	}
	return $res; // Возвращаем ассоциативный массив с содержимым PHPDoc
}

Разбор:

  1. Функция cot_file_phpdoc — предназначена для парсинга комментариев в формате PHPDoc из указанного файла и возврата содержимого в виде ассоциативного массива.

  2. Чтение файла — содержимое файла считывается с помощью file_get_contents($filename) и сохраняется в переменной $data.

  3. Регулярное выражение — с помощью preg_match проверяется наличие блока PHPDoc в начале файла. Регулярное выражение ищет начало комментария с /** и конец с */. Весь текст между ними сохраняется в переменную $mt.

  4. Разбиение PHPDoc — содержимое блока PHPDoc разбивается на строки с использованием preg_split, разделяя строки, содержащие теги, такие как @param, @return и другие.

  5. Обработка каждой строки PHPDoc — для каждой строки:

    • Извлекается ключ (например, param, return), который находится до первого пробела.
    • С помощью регулярного выражения очищается описание от лишних символов и сохраняется в массив с соответствующим ключом.
  6. Возвращение результата — возвращается ассоциативный массив с ключами и значениями, соответствующими данным из блока PHPDoc.

Функция полезна для получения метаинформации о функциях, классах или методах, описанных в комментариях PHPDoc.

 

 

/**
 * Извлекает информацию из заголовков файлов COT
 *
 * @param string $file Путь к файлу
 * @param string $limiter Название тега
 * @param int $maxsize Максимальный размер заголовка
 * @return array Массив с данными блока или FALSE в случае ошибки
 */
function cot_infoget($file, $limiter = 'COT_EXT', $maxsize = 32768)
{
    $result = []; // Инициализация пустого массива для хранения результатов

    // Структура данных по умолчанию
    if ($limiter == 'COT_EXT' || $limiter == 'SED_EXTPLUGIN') {
        $result = [
            'Code' => '', // Код расширения
            'Name' => '', // Название расширения
            'Description' => '', // Описание расширения
            'Category' => '', // Категория расширения
            'Version' => '', // Версия расширения
            'Date' => '', // Дата создания расширения
            'Author' => '', // Автор расширения
            'Copyright' => '', // Авторские права
            'Notes' => '', // Заметки
            'Auth_members' => '', // Доступ для зарегистрированных пользователей
            'Lock_members' => '', // Ограничения для зарегистрированных пользователей
            'Auth_guests' => '', // Доступ для гостей
            'Lock_guests' => '', // Ограничения для гостей
        ];

        // Если тег COT_EXT, добавляем дополнительные поля
        if ($limiter == 'COT_EXT') {
            $result['Requires_modules'] = ''; // Модули, которые требуются
            $result['Requires_plugins'] = ''; // Плагины, которые требуются
            $result['Recommends_modules'] = ''; // Рекомендуемые модули
            $result['Recommends_plugins'] = ''; // Рекомендуемые плагины
        }
    }

	$fp = @fopen($file, 'r'); // Открытие файла на чтение в режиме r (без создания файла, если его нет)
	if ($fp) { // Если файл успешно открыт
		// Формирование ограничителей начала и конца блока на основе тега
		$limiter_begin = '[BEGIN_' . $limiter . ']'; 
		$limiter_end = '[END_' . $limiter . ']'; 
		$data = fread($fp, $maxsize); // Чтение данных из файла с ограничением по размеру $maxsize
		$begin = mb_strpos($data, $limiter_begin); // Нахождение позиции начала блока
		$end = mb_strpos($data, $limiter_end); // Нахождение позиции конца блока

		if ($end > $begin && $begin > 0) { // Если конец блока найден и его начало корректно
			// Извлекаем данные между ограничителями
			$lines = mb_substr($data, $begin + 8 + mb_strlen($limiter), 
				$end - $begin - mb_strlen($limiter) - 8); 
			$lines = explode("\n", $lines); // Разделяем данные на строки

			foreach ($lines as $line) { // Для каждой строки
				$line = ltrim($line, " */"); // Убираем начальные символы пробела, звездочки или косой черты
				$linex = preg_split('/\s*\=\s*/', trim($line), 2); // Разделяем строку по символу "=" на ключ и значение
				if ($linex[0]) { // Если есть ключ
					$result[$linex[0]] = isset($linex[1]) ? $linex[1] : ''; // Сохраняем ключ и его значение в массив
				}
			}

		} else { // Если блок не найден
			$result = false; // Возвращаем false
		}
	} else { // Если файл не открыт
		$result = false; // Возвращаем false
	}
	@fclose($fp); // Закрытие файла (с suppress оператором для игнорирования ошибок)

	return $result; // Возвращаем массив с данными или false в случае ошибки
}

Разбор:

  1. Функция cot_infoget — предназначена для извлечения данных из заголовков файлов Cotonti, которые находятся между тегами, указанными в параметре $limiter. Эта функция может быть полезна для получения информации о расширениях или плагинах.

  2. Параметры функции:

    • $file — путь к файлу, из которого нужно извлечь информацию.
    • $limiter — тег, который определяет начало и конец блока, который нужно извлечь (по умолчанию 'COT_EXT').
    • $maxsize — максимальный размер данных, которые могут быть прочитаны из файла.
  3. Структура данных по умолчанию — в зависимости от значения $limiter, создается структура массива для хранения данных о расширении.

  4. Чтение данных из файла — файл открывается для чтения, и данные считываются с ограничением по размеру, заданному параметром $maxsize.

  5. Поиск блоков данных — данные между тегами [BEGIN_COT_EXT] и [END_COT_EXT] извлекаются из файла. Если блок найден, данные обрабатываются.

  6. Обработка строк — каждая строка в блоке разбивается по символу "=", и результат сохраняется в ассоциативный массив.

  7. Закрытие файла — файл закрывается после чтения.

  8. Возвращение результатов — возвращается массив с данными, если блок найден, или false, если произошла ошибка.

 


/**
 * Регистрирует расширение в ядре
 *
 * @param string $name Название расширения (код)
 * @param string $title Название
 * @param string $version Номер версии в формате A.B.C
 * @param bool $is_plug Является ли это плагином
 * @return bool TRUE в случае успеха, FALSE в случае ошибки
 * @global CotDB $db Объект базы данных
 */
function cot_extension_add($name, $title, $version = '1.0.0', $is_plug = false)
{
	global $db, $db_core; // Использование глобальных переменных для доступа к объекту базы данных и к основной таблице базы данных

	// Выполняем вставку данных о расширении в таблицу базы данных
	$res = $db->insert($db_core, array(
		'ct_code' => $name, // Код расширения
		'ct_title' => $title, // Название расширения
		'ct_version' => $version, // Версия расширения
		'ct_plug' => (int) $is_plug // Преобразуем значение флага плагина в целое число (0 или 1)
	));

	// Возвращаем TRUE, если вставка данных в базу данных прошла успешно, иначе FALSE
	return $res > 0;
}

Разбор:

  1. Функция cot_extension_add — предназначена для регистрации нового расширения (или плагина) в ядре системы.

  2. Параметры функции:

    • $name — код расширения (уникальное название расширения).
    • $title — название расширения (например, "Мой плагин").
    • $version — версия расширения, по умолчанию задано значение '1.0.0'.
    • $is_plug — флаг, указывающий, является ли расширение плагином. Если это плагин, то значение true, если нет — false.
  3. Глобальные переменные:

    • $db — объект базы данных, через который выполняются операции с БД.
    • $db_core — имя таблицы, в которую будет произведена вставка данных.
  4. Вставка данных — функция вызывает метод insert объекта $db, который добавляет новую запись в таблицу базы данных $db_core. Вставляются данные о расширении (код, название, версия и флаг плагина).

  5. Возвращаемое значение — если операция вставки прошла успешно (результат больше 0), возвращается TRUE, иначе — FALSE.


/**
 * Сравнивает две записи информации об расширениях по коду категории.
 * Расширения, установленные после установки, всегда идут последними.
 *
 * @param array $ext1 Информация о расширении 1
 * @param array $ext2 Информация о расширении 2
 * @return int Результат сравнения: 0 — если элементы одинаковы, 1 или -1 — если элементы разные
 */
function cot_extension_catcmp($ext1, $ext2)
{
	global $L; // Глобальная переменная для доступа к локализованным строкам

	// Если категория первого расширения существует в локализованных строках, заменяем ее на локализованную версию
	if (isset($L['ext_cat_' . $ext1['Category']]))
	{
		$ext1['Category'] = $L['ext_cat_' . $ext1['Category']];
	}

	// Если категория второго расширения существует в локализованных строках, заменяем ее на локализованную версию
	if (isset($L['ext_cat_' . $ext2['Category']]))
	{
		$ext2['Category'] = $L['ext_cat_' . $ext2['Category']];
	}

	// Если категории двух расширений одинаковые
	if ($ext1['Category'] == $ext2['Category'])
	{
		// Если названия расширений одинаковые, возвращаем 0 (значения равны)
		if ($ext1['Name'] == $ext2['Name'])
		{
			return 0;
		}
		else
		{
			// Если названия расширений разные, возвращаем 1 или -1 в зависимости от лексикографического сравнения
			return ($ext1['Name'] > $ext2['Name']) ? 1 : -1;
		}
	}
	else
	{
		// Если категории разные, то расширение с "большей" категорией (или если категория 'post-install') идет позже
		return ($ext1['Category'] > $ext2['Category'] || $ext1['Category'] == 'post-install') ? 1 : -1;
	}
}

Разбор:

  1. Функция cot_extension_catcmp — предназначена для сравнения двух расширений по категории и имени. В результате этого сравнения расширения сортируются в нужном порядке.

  2. Параметры функции:

    • $ext1 и $ext2 — массивы с информацией о расширениях, которые будут сравниваться.
  3. Локализация категорий:

    • Переменная $L содержит локализованные строки для категорий расширений. Если категория расширения существует в этом массиве, она заменяется на локализованное название.
  4. Сравнение категорий:

    • Если категории расширений одинаковы, то сравниваются их имена. Если имена одинаковы, возвращается 0 (элементы равны).
    • Если имена разные, возвращается 1 или -1 в зависимости от того, какое имя лексикографически больше.
  5. Сравнение категорий, если они разные:

    • Если категории расширений разные, то расширение с "большей" категорией (в соответствии с лексикографическим порядком) или расширение с категорией 'post-install' будет располагаться после другого.

 


/**
 * Проверяет, установлено ли расширение
 *
 * @param string $extensionCode Код расширения
 * @return bool TRUE, если расширение установлено, иначе FALSE
 * @deprecated Эта функция устарела
 * @see ExtensionsService::isInstalled() Используйте этот метод вместо текущего
 */
function cot_extension_installed($extensionCode)
{
    // Если код расширения пустой, возвращаем FALSE
    if (empty($extensionCode)) {
        return false;
    }
    
    // Используем сервис ExtensionsService для проверки, установлено ли расширение
    return ExtensionsService::getInstance()->isInstalled($extensionCode);
}

Разбор:

  1. Функция cot_extension_installed — проверяет, установлено ли расширение с заданным кодом. Это устаревшая функция, и рекомендуется использовать метод ExtensionsService::isInstalled().

  2. Параметр:

    • $extensionCode — код расширения, которое нужно проверить на установку.
  3. Логика работы:

    • Если код расширения пустой, функция возвращает false, так как проверка не может быть выполнена.
    • Если код расширения не пустой, используется класс ExtensionsService, метод isInstalled() для выполнения проверки. Этот метод возвращает true, если расширение установлено, и false, если нет.
  4. Депрецированный статус:

    • Функция помечена как устаревшая (@deprecated), и рекомендуется использовать вместо нее более современный метод ExtensionsService::isInstalled().

/**
 * Returns installed extension type: 'module' if extension is a module,
 * 'plug' if extension is a plugin or FALSE if extension is not installed.
 *
 * @param string $name Module code
 * @return mixed
 * @global CotDB $db
 * @deprecated
 * @see ExtensionsService::getType()
 */
function cot_extension_type($name)
{
	global $db, $db_core;

	$res = $db->query("SELECT ct_plug FROM $db_core WHERE ct_code = ?", $name);
	if ($res->rowCount() == 0)
	{
		return false;
	}
	$is_plug = (int) $res->fetchColumn();
	return $is_plug ? 'plug' : 'module';
}

Разбор:

  1. Функция cot_extension_type — проверяет тип расширения по его коду (модуль или плагин).

  2. Параметр:

    • $name — код расширения (модуля или плагина), для которого нужно определить тип.
  3. Логика работы:

    • Выполняется SQL-запрос к базе данных для получения значения поля ct_plug из таблицы расширений ($db_core). Это поле указывает, является ли расширение плагином или модулем.
    • Если расширение не найдено в базе (нет строки с таким кодом), функция возвращает false.
    • Если расширение найдено, то проверяется значение поля ct_plug:
      • Если это плагин (значение поля равно 1), возвращается строка 'plug'.
      • Если это модуль (значение поля равно 0), возвращается строка 'module'.
  4. Депрецированный статус:

    • Функция помечена как устаревшая (@deprecated), и рекомендуется использовать вместо нее более современный метод ExtensionsService::getType().

/**
 * Возвращает массив, содержащий метаинформацию для всех расширений в каталоге
 *
 * @param string $dir Каталог для поиска расширений
 * @return array Массив с кодами расширений и информацией о них
 */
function cot_extension_list_info($dir)
{
    $ext_list = array(); // Массив для хранения информации о расширениях
    clearstatcache(); // Очищаем кэш статистики файлов, чтобы обновленные данные были корректными
    $dp = opendir($dir); // Открываем каталог для чтения

    // Перебираем все файлы и каталоги в указанном каталоге
    while ($f = readdir($dp))
    {
        $path = $dir . '/' . $f; // Формируем полный путь к текущему элементу

        // Проверяем, что это не скрытая папка (начинающаяся с точки),
        // это действительно каталог и в нем есть файл $f.setup.php
        if ($f[0] != '.' && is_dir($path) && file_exists("$path/$f.setup.php"))
        {
            // Загружаем метаинформацию о расширении из файла $f.setup.php
            $info = cot_infoget("$path/$f.setup.php", 'COT_EXT');

            // Если метаинформация не загружена, пробуем загрузить в старом формате для плагинов
            if (!$info && cot_plugin_active('genoa')) {
                $info = cot_infoget("$path/$f.setup.php", 'SED_EXTPLUGIN');
            }

            // Если информация не найдена, используем значения по умолчанию
            if ($info == false) {
                // Не удалось загрузить блок информации
                // Используем данные по умолчанию
                $info = array(
                    'Code' => $f, // Код расширения
                    'Name' => $f, // Название расширения (по умолчанию совпадает с кодом)
                    'Description' => '', // Описание
                    'Category' => '', // Категория
                    'Version' => '', // Версия
                    'Date' => '', // Дата
                    'Author' => '', // Автор
                    'Copyright' => '', // Авторские права
                    'Notes' => '', // Примечания
                    'Auth_members' => '', // Права для участников
                    'Lock_members' => '', // Блокировка для участников
                    'Auth_guests' => '', // Права для гостей
                    'Lock_guests' => '', // Блокировка для гостей
                    'Requires_modules' => '', // Требуемые модули
                    'Requires_plugins' => '', // Требуемые плагины
                    'Recommends_modules' => '', // Рекомендуемые модули
                    'Recommends_plugins' => '' // Рекомендуемые плагины
                );
            }

            // Если категория не указана, задаем значение по умолчанию
            if (empty($info['Category'])) {
                $info['Category'] = 'misc-ext'; // Устанавливаем категорию как 'misc-ext'
            }

            // Добавляем информацию о расширении в массив
            $ext_list[$f] = $info;
        }
    }

    closedir($dp); // Закрываем каталог

    return $ext_list; // Возвращаем массив с информацией о расширениях
}

Разбор:

  1. Функция cot_extension_list_info — собирает информацию о всех расширениях в указанном каталоге и возвращает эту информацию в виде ассоциативного массива.

  2. Параметр:

    • $dir — путь к каталогу, в котором нужно искать расширения.
  3. Алгоритм работы:

    • Очищается кэш статистики файлов с помощью clearstatcache(), чтобы убедиться, что файлы и каталоги будут проверены заново.
    • Открывается указанный каталог с помощью opendir().
    • Перебираются все элементы в каталоге с помощью readdir(). Для каждого элемента проверяется:
      • Это не скрытая папка (не начинается с точки).
      • Это действительно каталог.
      • В каталоге существует файл с именем $f.setup.php.
    • Если файл с метаинформацией найден, вызывается функция cot_infoget() для извлечения информации из него.
      • Если информация не была загружена, пробуется старый формат для плагинов, если активирован плагин "genoa".
    • Если не удается получить информацию, используется массив с данными по умолчанию.
    • Если категория расширения не указана, она устанавливается как 'misc-ext'.
    • В массив $ext_list добавляется информация о расширении.
    • Закрывается каталог с помощью closedir().
  4. Возвращаемое значение:

    • Массив $ext_list, где ключами являются коды расширений, а значениями — ассоциативные массивы с информацией о расширениях.

 


/**
 * Удаляет модуль из ядра
 *
 * @param string $name Название модуля
 * @return bool Возвращает true, если модуль был удален успешно, иначе false
 * @global CotDB $db Объект базы данных
 */
function cot_extension_remove($name)
{
    global $db, $db_core; // Получаем доступ к объекту базы данных и таблице ядра

    // Выполняем запрос на удаление записи в таблице ядра с кодом модуля, равным $name
    return $db->delete($db_core, "ct_code = '$name'"); // Удаляем запись с кодом расширения $name
}

Разбор:

  1. Функция cot_extension_remove — удаляет модуль из базы данных ядра системы.

  2. Параметр:

    • $name — название (код) модуля, который нужно удалить.
  3. Алгоритм работы:

    • С помощью глобальной переменной $db выполняется запрос на удаление записи из таблицы ядра, которая хранит информацию о модулях.
    • Удаляется запись, где поле ct_code соответствует значению $name.
  4. Возвращаемое значение:

    • Возвращает результат выполнения операции удаления:
      • true, если запись успешно удалена.
      • false, если произошла ошибка при удалении.

/**
 * Обновляет номер версии модуля в реестре
 *
 * @param string $name Название модуля
 * @param string $version Новая строка версии
 * @return bool Возвращает true, если версия была обновлена успешно, иначе false
 * @global CotDB $db Объект базы данных
 */
function cot_extension_update($name, $version)
{
    global $db, $db_core; // Получаем доступ к объекту базы данных и таблице ядра

    // Выполняем запрос на обновление версии модуля в таблице ядра для модуля с кодом $name
    return $db->update($db_core, array('ct_version' => $version), "ct_code = '$name'"); // Обновляем поле версии для модуля $name
}

Разбор:

  1. Функция cot_extension_update — обновляет номер версии модуля в базе данных ядра системы.

  2. Параметры:

    • $name — название (код) модуля, версию которого нужно обновить.
    • $version — новая версия модуля в виде строки.
  3. Алгоритм работы:

    • С помощью глобальной переменной $db выполняется запрос на обновление поля ct_version в таблице ядра, где код модуля равен $name.
    • Обновляется только версия модуля, другие поля остаются без изменений.
  4. Возвращаемое значение:

    • Возвращает результат выполнения операции обновления:
      • true, если версия была обновлена успешно.
      • false, если произошла ошибка при обновлении.

 


/**
 * Регистрирует плагин или модуль в реестре хуков
 *
 * Пример:
 * <code>
 * $hook_bindings = array(
 *     array(
 *         'part' => 'rss',  // Часть плагина
 *         'hook' => 'rss.main',  // Хук, с которым связан плагин
 *         'order' => 20  // Порядок выполнения
 *     ),
 *     array(
 *         'part' => 'header',  // Часть плагина
 *         'hook' => 'header.tags',  // Хук для заголовков
 *     )
 * );
 *
 * cot_plugin_add($hook_bindings, 'test', 'Test plugin', false);
 * </code>
 *
 * @param array $hook_bindings Маппинг привязки хуков
 * @param string $code Название модуля или плагина (код)
 * @param string $title Название модуля или плагина
 * @param bool $isModule TRUE для модулей, FALSE для плагинов
 * @return int Количество добавленных записей
 * @global CotDB $db Объект базы данных
 */
function cot_plugin_add($hook_bindings, $code, $title, $isModule = false)
{
    // Если название плагина пустое, устанавливаем его равным коду
    if (empty($title)) {
        $title = $code;
    }

    // Массив для добавляемых строк в таблицу
    $insertRows = [];
    
    // Проходим по каждой привязке хука
    foreach ($hook_bindings as $binding) {
        // Устанавливаем активность хука по умолчанию в 1 (активен)
        $active = 1;
        
        // Если в привязке указано 'active' и оно равно 0, то деактивируем хук
        if (isset($binding['active']) && !$binding['active']) {
            $active = 0;
        }
        
        // Добавляем строку для вставки в массив
        $insertRows[] = [
            'pl_hook' => $binding['hook'],  // Хук
            'pl_code' => $code,  // Код плагина или модуля
            'pl_part' => $binding['part'],  // Часть плагина
            'pl_title' => $title,  // Название плагина или модуля
            'pl_file' => empty($binding['file']) ? "$code/$code.{$binding['part']}.php" : $code . '/' . $binding['file'],  // Путь к файлу
            'pl_order' => $binding['order'],  // Порядок выполнения
            'pl_active' => $active,  // Активность хука
            'pl_module' => (int) $isModule  // Тип: 1 — модуль, 0 — плагин
        ];
    }

    // Начинаем транзакцию базы данных
    Cot::$db->beginTransaction();
    try {
        // Вставляем записи в таблицу хуков плагинов
        $result = Cot::$db->insert(Cot::$db->plugins, $insertRows);
        // Если все прошло успешно, подтверждаем транзакцию
        Cot::$db->commit();
    } catch (Exception $e) {
        // Если произошла ошибка, откатываем транзакцию
        Cot::$db->rollBack();
        return 0;  // Возвращаем 0 в случае ошибки
    }

    // Возвращаем количество добавленных записей
    return $result;
}

 

Разбор:

  1. Функция cot_plugin_add — используется для регистрации плагинов или модулей в системе, а точнее — в реестре хуков. Хуки — это механизмы для расширения функционала системы, которые позволяют подключать и управлять плагинами и модулями.

  2. Параметры:

    • $hook_bindings — массив, который содержит информацию о привязке плагина или модуля к хука. В нем указываются:
      • part — часть плагина (например, 'rss', 'header').
      • hook — сам хук (например, 'rss.main', 'header.tags').
      • order — порядок выполнения.
      • active (необязательно) — активен ли плагин (1 — активен, 0 — не активен).
      • file (необязательно) — путь к файлу плагина для данной части.
    • $code — уникальный код плагина или модуля.
    • $title — название плагина или модуля.
    • $isModule — если значение равно true, то это модуль, если false — плагин.
  3. Алгоритм работы:

    • Если название плагина не указано, оно будет равно коду.
    • Для каждой привязки хука создается запись в массиве $insertRows с необходимыми полями (название, путь к файлу, активность и т. д.).
    • Затем начинается транзакция в базе данных для вставки данных.
    • В случае ошибки транзакция откатывается.
    • Если вставка прошла успешно, то транзакция коммитится, и возвращается количество добавленных записей.
  4. Возвращаемое значение:

    • Количество записей, успешно добавленных в базу данных.
    • Если возникла ошибка при добавлении записей, возвращается 0.

/**
 * Удаляет плагин или одну из его частей из реестра хуков
 *
 * @param string $name Название модуля или плагина
 * @param int $binding_id ID привязки, которую нужно удалить, или 0 для удаления всех привязок
 * @return int Количество удалённых привязок
 * @global CotDB $db Объект базы данных
 */
function cot_plugin_remove($name, $binding_id = 0)
{
    global $db, $db_plugins;

    // Строим условие для поиска привязки по коду плагина
    $condition = "pl_code = '$name'";
    
    // Если передан идентификатор привязки, добавляем его в условие
    if ($binding_id > 0) {
        $condition .= " AND pl_id = $binding_id";
    }

    // Выполняем удаление из таблицы хуков плагинов и возвращаем количество удалённых записей
    return $db->delete($db_plugins, $condition);
}

Разбор:

  1. Функция cot_plugin_remove — используется для удаления плагина или его части (привязки) из реестра хуков. Хуки позволяют плагинам и модулям взаимодействовать с основным функционалом системы.

  2. Параметры:

    • $name — код плагина или модуля, который нужно удалить.
    • $binding_id — идентификатор привязки, которую нужно удалить. Если передать 0, то удалятся все привязки для данного плагина.
  3. Алгоритм работы:

    • Строится условие для поиска привязок с данным кодом плагина (pl_code = '$name').
    • Если передан ID привязки ($binding_id > 0), то условие расширяется для удаления конкретной привязки с этим ID.
    • Выполняется удаление записи из таблицы db_plugins с использованием условия $condition.
    • Возвращается количество удалённых привязок.
  4. Возвращаемое значение:

    • Функция возвращает количество удалённых привязок из базы данных.

Таким образом, данная функция помогает удалять как все привязки плагина, так и конкретную привязку по её ID из реестра хуков.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

40 minutes read Administrator

Comments (0)

No comments yet
Only registered users can post new comments

Content author

Administrator

Offline

Administrator

Last logged: 2026-07-05 18:32

  • Page published: 2025-01-26 12:19
  • Last update: 2025-01-26 13:06

Similar pages

Файл admin.extensions.php в Cotonti CMF
1 Файл "admin.extensions.php" в движке Cotonti CMF является важным компонентом административной части системы,
Файл extensions.php в Cotonti
2 Файл "extensions.php" в системе Cotonti CMF выполняет роль конфигурационного файла, содержащего список
HTML-шаблон admin.extensions.tpl в Cotonti
3 Описание файла шаблона admin.extensions.tpl в Cotonti CMF Файл шаблона admin.extensions.tpl, расположенный в