Файл Resources.php в Cotonti
файл предоставляет методы для добавления внешних и встроенных стилей и скриптов в заголовок и подвал страницы
Основное назначение файла Resources.php в системной папке Cotonti CMF — это управление ресурсами, такими как CSS, JavaScript файлы, и их интеграция в страницы сайта. Этот файл предоставляет методы для добавления внешних и встроенных стилей и скриптов в заголовок и подвал страницы, а также для обработки псевдонимов ресурсов и минификации кода.
Суть содержания кода:
-
Ресурсы для страницы: Файл содержит методы для добавления ссылок на стили и скрипты (функции
linkFile,linkFileFooter), которые могут быть размещены в различных частях страницы (заголовок или подвал), а также встроенных стилей и скриптов с использованием методовembedиembedFooter. -
Минификация: Методы, такие как
minify, обеспечивают минификацию кода, сокращая его для уменьшения времени загрузки страницы. -
Псевдонимы ресурсов: Методы
setAliasиgetAliasпозволяют работать с псевдонимами ресурсов, заменяя длинные или повторяющиеся пути на короткие и удобные для использования в коде. -
Проверка добавленных файлов: Функция
isFileAddedпозволяет проверить, был ли уже добавлен файл в ресурсный список, предотвращая дублирование. -
Управление порядком загрузки: Ресурсы можно добавлять с определенным порядковым номером (параметр
order), что позволяет контролировать порядок их вставки в HTML.
Характеристика стиля PHP:
-
Объектно-ориентированный стиль: Код использует статические методы (например,
static::linkFile,static::$alias) для работы с ресурсами. Это указывает на использование объектно-ориентированного подхода, где ресурсы инкапсулируются в классе и доступны через статические вызовы. -
Гибкость и расширяемость: Использование псевдонимов (через
setAliasиgetAlias) позволяет гибко управлять путями к ресурсам, а механизмы минификации и добавления файлов с заданным порядком (сортировка черезksort) повышают расширяемость кода. -
Качество и безопасность: Есть проверки на существование файлов перед их добавлением (
file_exists), а также возможность управления перезаписью ресурсов (canReWrite). Это помогает избежать ошибок и нежелательных перезаписей. -
Чистота и читаемость: Код хорошо структурирован с комментариями, которые объясняют назначение каждой функции и детали их работы. Однако, можно было бы улучшить документацию, добавив больше подробностей по функционалу каждого метода.
В целом, код организован с акцентом на модульность, управляемость и гибкость. Это типичный пример объектно-ориентированного подхода в PHP, где ресурсы централизованно управляются через класс с возможностью расширения и контроля за загрузкой.
defined('COT_CODE') or die('Wrong URL'); // Проверка, что код был вызван из системы Cotonti, иначе завершение выполнения с сообщением об ошибке.
/**
* Cotonti Resource control class
*
* @package API - Resources
* @copyright (c) Cotonti Team
* @license https://github.com/Cotonti/Cotonti/blob/master/License.txt
*/
class Resources // Объявление класса Resources, который будет отвечать за управление ресурсами в системе Cotonti
{
/**
* @var array predefined aliases
*/
protected static $alias = array ( // Массив, который хранит предустановленные алиасы для различных ресурсов.
'@jQuery' => 'js/jquery.min.js', // Алиас для jQuery, который указывает на путь к файлу js/jquery.min.js
'@ckeditor' => 'plugins/ckeditor/lib/ckeditor.js', // Алиас для CKEditor, указывающий на путь к файлу ckeditor.js
'@ckeditorPreset.js' => 'plugins/ckeditor/presets/ckeditor.default.set.js', // Алиас для набора пресетов CKEditor
'@bootstrap.js' => 'lib/bootstrap/js/bootstrap.min.js', // Алиас для Bootstrap JS, указывающий на путь к файлу bootstrap.min.js
'@bootstrap.css' => 'lib/bootstrap/css/bootstrap.min.css', // Алиас для Bootstrap CSS, указывающий на путь к файлу bootstrap.min.css
'@bootstrapTheme.css' => null // Неопределённое значение для темы Bootstrap. Место для настройки по желанию.
);
// ==== predefined alias constants ====
const jQuery = '@jQuery'; // Константа для алиаса jQuery
const bootstrap = '@bootstrap.js'; // Константа для алиаса Bootstrap JS
const ckeditor = '@ckeditor'; // Константа для алиаса CKEditor
// ==== /predefined alias constants ====
/**
* @var array Resources Registry
*/
protected static $registry = []; // Массив для регистрации ресурсов, которые могут быть использованы в различных частях системы Cotonti.
/**
* @var array footer Resources Registry
*/
protected static $footerRc = []; // Массив для хранения ресурсов, которые будут загружены в подвал страницы (footer).
/**
* @var array header Resources Registry
*/
protected static $headerRc = []; // Массив для хранения ресурсов, которые будут загружены в заголовок страницы (header).
protected static $addedFiles = array(); // Массив для хранения файлов, которые уже были добавлены в систему.
protected static $skip_minification = false; // Переменная, которая указывает, нужно ли пропускать процесс минимизации файлов (например, для JS или CSS).
protected static $cacheOn = false; // Переменная, которая указывает, включен ли кэш в системе.
protected static $consolidate = false; // Переменная, которая указывает, нужно ли объединять ресурсы (например, несколько файлов JS или CSS в один).
protected static $isAdmin = false; // Переменная, которая проверяет, является ли текущий пользователь администратором.
protected static $minify = false; // Переменная, которая указывает, нужно ли минимизировать ресурсы (JS, CSS).
/**
* @var bool header.php executed?
*/
protected static $headerComplete = false; // Переменная, которая указывает, был ли выполнен файл header.php.
private static $htmlCleanupEnabled = false; // Переменная для включения или отключения очистки HTML-кода.
/**
* @var string $dir Resources cache dir
*/
protected static $dir = ''; // Переменная для хранения пути к директории кэша для ресурсов.
public static function __init()
{
// Проверка, если константа COT_HEADER_COMPLETE определена, то ставим значение $headerComplete в true.
if (defined('COT_HEADER_COMPLETE')) {
static::$headerComplete = true;
}
// Устанавливаем значение переменной $cacheOn из конфигурации Cotonti.
static::$cacheOn = Cot::$cfg['cache'];
// Преобразуем настройку конфигурации для объединения ресурсов в булев тип и сохраняем её.
static::$consolidate = (bool) Cot::$cfg['headrc_consolidate'];
// Устанавливаем путь к директории для кэшированных ресурсов.
static::$dir = Cot::$cfg['cache_dir'] . '/assets/';
// Проверка, является ли текущий пользователь администратором.
static::$isAdmin = defined('COT_ADMIN');
// Устанавливаем значение минимизации ресурсов из конфигурации.
static::$minify = (bool) Cot::$cfg['headrc_minify'];
// Включаем очистку HTML-кода, если это указано в конфигурации и если пользователь не является администратором.
static::$htmlCleanupEnabled = (bool) Cot::$cfg['html_cleanup'] && !static::$isAdmin;
// Если нужно объединять ресурсы, проверяем, включен ли кэш и разрешена ли эта опция, а также не является ли пользователь администратором.
static::$consolidate = static::$cacheOn && static::$consolidate && !static::$isAdmin;
// Если объединение ресурсов включено, создаём директорию для кэширования ресурсов, если она ещё не существует.
if (static::$consolidate) {
if (!file_exists(static::$dir)) {
mkdir(static::$dir, Cot::$cfg['dir_perms'], true); // Создаём директорию с нужными правами доступа.
}
}
}
/**
* Добавляет файл JS/CSS в реестр ресурсов для последующей консолидации с другими
* такими ресурсами и сохранения в кэш.
*
* Рекомендуется использовать файлы вместо встроенного кода и использовать этот метод
* вместо Resources::addEmbed(). Используйте этот способ для любых статических JavaScript или
* CSS файлов.
*
* Не размещайте приватные данные в ресурсных файлах — это небезопасно. Если вам действительно нужно это,
* используйте прямой вывод вместо этого.
*
* @param string $path Путь к файлу *.js или *.css
* @param string $type Тип ресурса
* @param int $order Номер приоритета
* @param string $scope Область использования ресурса. Область — это селектор домена, где используется ресурс. Допустимые области:
* 'global' - глобально для всего сайта, будет включен везде, это самая статичная и постоянная область;
* 'guest' - только для незарегистрированных посетителей;
* 'user' - только для зарегистрированных пользователей;
* 'group_123' - для участников конкретной группы (maingrp), в этом примере для группы с id=123.
* Рекомендуется использовать область 'global', когда это возможно, так как это дает наилучшие возможности кэширования.
* @return bool Возвращает TRUE, если успешно, или FALSE, если файл не найден
* @throws Exception
*/
public static function addFile($path, $type = '', $order = 50, $scope = 'global')
{
// Если header.php уже был выполнен, пытаемся добавить файл в футер
if (static::$headerComplete) {
return Resources::linkFileFooter($path, $type, $order); // Возвращаем результат добавления в футер
}
// Разделяем путь по символу "?", если он присутствует (для параметров в URL)
$tmp = explode('?', $path); // Разделяем строку пути
$fileName = $tmp[0]; // Извлекаем имя файла без параметров
// Проверяем, был ли файл уже добавлен
if (in_array($fileName, static::$addedFiles)) return false; // Если файл уже добавлен, возвращаем false
// Если файл начинается с символа '@', используем алиас для замены
if (mb_strpos($fileName, '@') === 0) {
$fileName = static::$alias[$fileName]; // Заменяем на соответствующий алиас
} elseif (
mb_strpos($fileName, 'http://') === false
&& mb_strpos($fileName, 'https://') === false
&& mb_strpos($fileName, '//') !== 0
) {
// Если это локальный файл, проверяем его существование
if (!file_exists($fileName)) {
throw new Exception('Resource file «' . $fileName . '» not exists'); // Выбрасываем исключение, если файл не найден
}
}
// Если тип ресурса не задан, определяем его по расширению файла
if (empty($type)) {
$type = preg_match('#\.(min\.)?(js|css)$#', mb_strtolower($fileName), $m) ? $m[2] : 'js'; // Определяем тип по расширению файла
}
// Добавляем файл в список уже добавленных
static::$addedFiles[] = $tmp[0]; // Добавляем имя файла в список добавленных файлов
// Если включена консолидация и минимизация ресурсов, и файл не минимизирован
if (
static::$consolidate
&& static::$minify
&& !static::$skip_minification
&& mb_strpos($fileName, '.min.') === false
&& mb_strpos($fileName, '.pack.') === false
) {
// Минимизируем файл и сохраняем его в кэш
if ($fileName != '') {
$bname = ($type == 'css') ? str_replace('/', '._.', $fileName) : basename($fileName) . '.min'; // Формируем имя файла
$content = file_get_contents($fileName); // Читаем содержимое файла
if($content == '' || $content === false) return false; // Если файл пустой или не удалось прочитать, возвращаем false
$code = static::minify($content, $type); // Минимизируем содержимое
$path = static::$dir . $bname; // Путь для сохранения минимизированного файла
file_put_contents($path, $code); // Сохраняем минимизированный файл
}
}
// Если консолидация включена и кэширование активно, добавляем файл в реестр ресурсов
if (static::$consolidate && static::$cacheOn && !static::$isAdmin) {
static::$registry[$type][$scope][$order][] = $path; // Добавляем файл в реестр
} else {
// В противном случае добавляем файл в список файлов
static::$registry[$type]['files'][$scope][$order][] = $path; // Добавляем файл в реестр файлов
}
// Добавляем дополнительные файлы, связанные с этим
foreach (static::additionalFiles($tmp[0]) as $file) {
static::addFile($file, '', $order, $scope); // Рекурсивно добавляем дополнительные файлы
}
// Возвращаем успешный результат
return true; // Возвращаем true, если файл успешно добавлен
}
protected static function additionalFiles($file)
{
$ret = array(); // Создаём пустой массив для хранения дополнительных файлов
switch ($file) // Проводим проверку на конкретный файл
{
case '@bootstrap.js':
$ret[] = '@bootstrap.css'; // Если это '@bootstrap.js', добавляем '@bootstrap.css'
$ret[] = '@bootstrapTheme.css'; // Добавляем '@bootstrapTheme.css'
break;
case '@ckeditor':
$ret[] = '@ckeditorPreset.js'; // Если это '@ckeditor', добавляем '@ckeditorPreset.js'
break;
}
return $ret; // Возвращаем массив дополнительных файлов
}
/**
* Вставляет фрагмент встроенного кода в реестр ресурсов CSS/JS для хедера.
*
* Настоятельно рекомендуется использовать файлы для CSS/JS, когда это возможно,
* и вызывать функцию Resources::AddFile() для них, а не встраивать код
* на страницу с использованием этой функции. Эта функция должна использоваться для
* динамически генерируемого кода, который не может быть сохранён в статических файлах.
*
* @param string $identifier Алфавитно-цифровой идентификатор для фрагмента, используемый для управления обновлениями и т.д.
* @param string $code Встроенный код стилей или скрипта
* @param string $scope Область ресурса. Смотрите описание этого параметра в документации Resources::AddFile().
* @param string $type Тип ресурса: 'js' или 'css'
* @param int $order Приоритет порядка
*
* @return bool Эта функция всегда возвращает TRUE
* @see Resources::AddFile()
*/
public static function addEmbed($code, $type = 'js', $order = 50, $scope = 'global', $identifier = '')
{
// header.php выполнен. Пытаемся добавить код в footer
if (static::$headerComplete) {
static::embedFooter($code, $type, $order); // Если header.php уже выполнен, добавляем код в footer
}
// Если используется консолидация и минификация, сохраняем в файл
if (static::$consolidate && static::$cacheOn && !static::$isAdmin) {
if (!$identifier) {
$identifier = md5($code . $type); // Если идентификатор не задан, генерируем его из хеша кода и типа
}
// Сохраняем как файл
$path = static::$dir . $identifier . '.' . $type; // Формируем путь для сохранения файла
if (!file_exists($path) || md5($code) != md5_file($path)) {
// Если файл не существует или его хеш не совпадает с хешем нового кода
if (static::$minify && !static::$skip_minification) {
$code = static::minify($code, $type); // Минифицируем код, если это требуется
}
file_put_contents($path, $code); // Записываем код в файл
}
static::$registry[$type][$scope][$order][] = $path; // Добавляем путь к файлу в реестр
} else {
$separator = !static::$htmlCleanupEnabled ? "\n" : ' '; // Определяем разделитель в зависимости от настроек очистки HTML
if ($type === 'js') {
$code = trim($code); // Убираем лишние пробелы с начала и конца кода
$last = (substr($code, -1));
if ($last !== ';') {
$separator = ";" . $separator; // Добавляем точку с запятой в конец, если её нет
}
}
if (!isset(static::$registry[$type]['embed'])) {
static::$registry[$type]['embed'] = []; // Если раздела для встраиваемых ресурсов ещё нет, создаём его
}
if (!isset(static::$registry[$type]['embed'][$scope][$order])) {
static::$registry[$type]['embed'][$scope][$order] = ''; // Если для указанного порядка нет значения, создаём его
}
static::$registry[$type]['embed'][$scope][$order] .= $code . $separator; // Добавляем код в реестр
}
return true; // Функция всегда возвращает true
}
public static function render()
{
global $theme, $cot_rc_html;
if (!isset($cot_rc_html[$theme]) || !static::$consolidate) {
$cot_rc_html = static::consolidate(); // Если нет консолидации или ресурсы ещё не собраны, вызываем метод для консолидации
}
$ret = '';
$pass = true;
if (is_array($cot_rc_html) && isset($cot_rc_html[$theme])) {
foreach ($cot_rc_html[$theme] as $scope => $html) {
switch ($scope) {
case 'global':
$pass = true; // Для глобальных ресурсов всегда разрешаем добавление
break;
case 'guest':
$pass = Cot::$usr['id'] == 0; // Для гостей проверяем, что пользователь не авторизован
break;
case 'user':
$pass = Cot::$usr['id'] > 0; // Для зарегистрированных пользователей проверяем, что пользователь авторизован
break;
default:
$parts = explode('_', $scope);
$pass = count($parts) == 2 && $parts[0] == 'group' && $parts[1] == Cot::$usr['maingrp']; // Для группы проверяем, что группа совпадает с основной группой пользователя
}
if ($pass) {
$ret = $html . $ret; // Если условие проходит, добавляем HTML в результат
}
}
}
// Теперь собираем ресурсы, которые не должны быть минифицированы
if (!is_array(static::$headerRc)) {
return $ret; // Если нет ресурсов для хедера, возвращаем результат
}
// CSS должен идти первым
ksort(static::$headerRc);
foreach (static::$headerRc as $type => $data) {
if (!empty(static::$headerRc[$type]) && is_array(static::$headerRc[$type])) {
ksort(static::$headerRc[$type]);
foreach (static::$headerRc[$type] as $order => $htmlArr) {
foreach ($htmlArr as $key => $path) {
if (mb_strpos($type, '_embed') !== false) {
$ret .= $path; // Для встроенных ресурсов добавляем путь
if (!static::$htmlCleanupEnabled) {
$ret .= "\n"; // Если очистка HTML отключена, добавляем новую строку
}
} else {
if (mb_strpos($path, '@') === 0) {
$path = static::$alias[$path]; // Если путь начинается с алиаса, заменяем на реальный путь
if (empty($path)) {
continue; // Если путь пустой, пропускаем
}
}
$ret .= cot_rc("code_rc_{$type}_file", ['url' => $path]); // Добавляем путь через функцию cot_rc
if (!static::$htmlCleanupEnabled) {
$ret .= "\n"; // Если очистка HTML отключена, добавляем новую строку
}
}
}
}
}
}
static::$headerComplete = true; // Отмечаем, что хедер завершён
return trim($ret); // Возвращаем итоговый результат
}
}
/**
* Consolidate all resources and make single file
*/
protected static function consolidate()
{
global $cot_rc_html, $theme;
// Если нужно собирать и ужимать, делаем это
if (!is_array(static::$registry)) {
return false; // Если нет реестра ресурсов, возвращаем false
}
// CSS должен идти первым
ksort(static::$registry);
// Строим вывод хедера
$cot_rc_html[$theme] = [];
// Консолидация ресурсов
if (static::$cacheOn && static::$consolidate && !static::$isAdmin) {
clearstatcache(); // Очищаем кеш файлов
foreach (static::$registry as $type => $scope_data) {
// Консолидация
foreach ($scope_data as $scope => $ordered_files) {
ksort($ordered_files); // Сортируем файлы по порядку
$target_path = static::$dir . $scope . '.' . $theme . '.' . $type; // Формируем путь к конечному файлу
$files = [];
foreach ($ordered_files as $order => $o_files) {
$files = array_merge($files, $o_files); // Объединяем файлы
}
$files = array_unique($files); // Убираем дубли
foreach ($files as $key => $file) {
if (mb_strpos($file, '@') === 0) {
$tmp = static::$alias[$file];
if (empty($tmp)) {
unset($files[$key]); // Если путь пустой, убираем файл из списка
} else {
$files[$key] = $tmp; // Заменяем алиас на реальный путь
}
}
}
$code = '';
$modified = false;
$fileTime = 0;
if (!file_exists($target_path)) {
// Просто создаем новый кеш-файл
$file_list = $files;
$modified = true; // Отмечаем, что файл изменён
} else {
$fileTime = filemtime($target_path); // Получаем время последнего изменения файла
// Загружаем список файлов, уже закешированных
$file_list = unserialize(file_get_contents("{$target_path}.idx"));
$file_list = !empty($file_list) ? $file_list : []; // Если список пустой, инициализируем как пустой массив
// Проверяем присутствие или время модификации каждого файла
foreach ($files as $path) {
if (!in_array($path, $file_list) || filemtime($path) >= $fileTime) {
$modified = true; // Если файл изменён или не найден в списке, помечаем его как изменённый
break;
}
}
}
if ($modified) {
// Пересоздаём кеш
$current_path = str_replace('\\', '/', realpath('.'));
foreach ($files as $path) {
// Получаем содержимое файла и удаляем BOM
$file_code = str_replace(pack('CCC', 0xef, 0xbb, 0xbf), '', file_get_contents($path));
if ($type == 'css') {
if (strpos($path, '._.') !== false) {
// Восстанавливаем оригинальный путь к файлу
$path = str_replace('._.', '/', basename($path));
}
if ($path[0] === '/') {
$path = mb_substr($path, 1); // Убираем ведущий слэш из пути
}
$file_path = str_replace('\\', '/', dirname(realpath($path)));
$relative_path = str_replace($current_path, '', $file_path);
if ($relative_path[0] === '/') {
$relative_path = mb_substr($relative_path, 1); // Убираем ведущий слэш из относительного пути
}
// Применяем CSS импорты
if (
preg_match_all(
'#@import\s+url\((\'|")?([^\'")]+)\1?\);#i',
$file_code,
$mt,
PREG_SET_ORDER
)
) {
foreach ($mt as $m) {
if (preg_match('#^https?://#i', $m[2])) {
$filename = $m[2];
} else {
$filename = empty($relative_path) ? $m[2] : $relative_path . '/' . $m[2];
}
$file_code = str_replace($m[0], file_get_contents($filename), $file_code); // Заменяем импорт на содержимое
}
}
// Исправляем URL
if (preg_match_all(
'#\burl\((\'|")?([^\)"\']+)\1?\)#i',
$file_code,
$mt,
PREG_SET_ORDER)
) {
foreach ($mt as $m) {
$fileFullName = trim(empty($relative_path) ? $m[2] : $relative_path . '/' . $m[2]);
$tmp = explode('?', $fileFullName);
$fileName = $tmp[0];
$fileName = str_replace($current_path, '', str_replace('\\', '/', realpath($fileName)));
if (!$fileName) {
continue;
}
if ($fileName[0] === '/') {
$fileName = mb_substr($fileName, 1);
}
if (!empty($tmp[1])) {
$fileName .= '?' . $tmp[1];
}
$file_code = str_replace($m[0], 'url("' . $fileName . '")', $file_code); // Заменяем URL на правильный путь
}
}
}
$separator = "\n";
if ($type == 'js') {
$file_code = trim($file_code);
$last = (substr($file_code, -1));
if ($last != ';') {
$separator = ";\n";
}
}
$code .= $file_code . $separator; // Добавляем содержимое файла в итоговый код
}
file_put_contents($target_path, $code); // Записываем результат в файл
if (Cot::$cfg['gzip']) {
file_put_contents("$target_path.gz", gzencode($code)); // Если включена сжатие, записываем сжатую версию
}
file_put_contents("$target_path.idx", serialize($files)); // Сохраняем список файлов в индекс
$fileTime = filemtime($target_path); // Обновляем время последней модификации файла
}
$rcUrl = "rc.php?rc=$scope.$theme.$type&nc=" . $fileTime; // Формируем URL для ресурса
if (empty($cot_rc_html[$theme][$scope])) {
$cot_rc_html[$theme][$scope] = '';
}
$cot_rc_html[$theme][$scope] .= cot_rc("code_rc_{$type}_file", ['url' => $rcUrl]); // Добавляем ссылку на ресурс
if (!static::$htmlCleanupEnabled) {
$cot_rc_html[$theme][$scope] .= "\n";
}
}
}
// Сохраняем вывод
static::$cacheOn && Cot::$cache && Cot::$cache->db->store('cot_rc_html', $cot_rc_html);
} else {
$log = []; // Лог путей, чтобы избежать дублирования
foreach (static::$registry as $type => $resData) {
if (!empty(static::$registry[$type]['files']) && is_array(static::$registry[$type]['files'])) {
foreach (static::$registry[$type]['files'] as $scope => $scope_data) {
ksort($scope_data);
foreach ($scope_data as $order => $files) {
foreach ($files as $file) {
if (!in_array($file, $log)) {
$fileName = $file;
if (mb_strpos($file, '@') === 0) {
$fileName = static::$alias[$file];
if (empty($fileName)) {
continue;
}
}
if (empty($cot_rc_html[$theme][$scope])) {
$cot_rc_html[$theme][$scope] = '';
}
$cot_rc_html[$theme][$scope] .= cot_rc(
"code_rc_{$type}_file",
['url' => $fileName]
);
if (!static::$htmlCleanupEnabled) {
$cot_rc_html[$theme][$scope] .= "\n";
}
$log[] = $file; // Добавляем файл в лог
}
}
}
}
}
if (!empty(static::$registry[$type]['embed']) && is_array(static::$registry[$type]['embed'])) {
foreach (static::$registry[$type]['embed'] as $scope => $scope_data) {
ksort($scope_data);
foreach ($scope_data as $order => $code) {
if (empty($cot_rc_html[$theme][$scope])) {
$cot_rc_html[$theme][$scope] = '';
}
$cot_rc_html[$theme][$scope] .= cot_rc("code_rc_{$type}_embed", ['code' => $code]); // Добавляем встроенный код
if (!static::$htmlCleanupEnabled) {
$cot_rc_html[$theme][$scope] .= "\n";
}
}
}
}
}
}
return $cot_rc_html; // Возвращаем финальный результат
}
/**
* Render footer resources
*/
public static function renderFooter()
{
// Проверяем, является ли $footerRc массивом
if (!is_array(static::$footerRc)) {
return false; // Если нет, возвращаем false
}
// CSS должен идти первым
ksort(static::$footerRc);
$ret = '';
// Проходим по типам ресурсов в $footerRc
foreach (static::$footerRc as $type => $data) {
// Проверяем, есть ли данные для текущего типа и является ли это массивом
if (!empty(static::$footerRc[$type]) && is_array(static::$footerRc[$type])) {
ksort(static::$footerRc[$type]); // Сортируем ресурсы внутри типа
// Проходим по каждому ресурсу в массиве
foreach (static::$footerRc[$type] as $order => $htmlArr) {
// Проходим по каждому элементу массива
foreach ($htmlArr as $key => $path) {
// Если это встроенный код (embed), добавляем его напрямую
if (mb_strpos($type, '_embed') !== false) {
$ret .= $path;
if (!static::$htmlCleanupEnabled) {
$ret .= "\n"; // Если очистка HTML не включена, добавляем перенос строки
}
} else {
// Если это не встроенный код, проверяем алиас пути
if (mb_strpos($path, '@') === 0) {
$path = static::$alias[$path]; // Заменяем алиас на реальный путь
if ($path == '') {
continue; // Если путь пустой, пропускаем его
}
}
// Добавляем ресурс в итоговый вывод
$ret .= cot_rc("code_rc_{$type}_file", ['url' => $path]);
if (!static::$htmlCleanupEnabled) {
$ret .= "\n"; // Если очистка HTML не включена, добавляем перенос строки
}
}
}
}
}
}
return trim($ret); // Убираем лишние пробелы в начале и в конце и возвращаем результат
}
/**
* Удобный способ вывода ссылки на CSS/JS файл в шапке страницы
*
* @param string $path Путь/URL к стилям *.css или скрипту *.js
* @param string $type
* @param int $order
* @return bool
*
* @throws Exception
*/
public static function linkFile($path, $type = '', $order = 50)
{
// Если header.php уже выполнен, пытаемся добавить файл в футер
if (static::$headerComplete) {
return Resources::linkFileFooter($path, $type, $order);
}
$tmp = explode('?', $path); // Разделяем путь на основную часть и параметры
$fileName = $tmp[0]; // Основное имя файла без параметров
// Если файл уже был добавлен, выходим
if (in_array($fileName, static::$addedFiles)) {
return false;
}
// Если путь начинается с '@', заменяем его на соответствующий алиас
if (mb_strpos($fileName, '@') === 0) {
$fileName = static::$alias[$fileName];
} elseif (
mb_strpos($fileName, 'http://') === false
&& mb_strpos($fileName, 'https://') === false
&& mb_strpos($fileName, '//') !== 0
) {
// Если файл не является абсолютным URL, проверяем его существование
if (!file_exists($fileName)) {
throw new Exception('Resource file «' . $fileName . '» not exists');
}
}
// Если тип не указан, определяем его по расширению файла (css или js)
if (empty($type)) {
$type = preg_match('#\.(js|css)$#i', $fileName, $m) ? strtolower($m[1]) : 'js';
}
// Добавляем файл в список добавленных
static::$addedFiles[] = $tmp[0];
static::$headerRc[$type][$order][] = $path; // Добавляем путь в список ресурсов для вывода в header
// Обрабатываем дополнительные файлы, если они есть
foreach (static::additionalFiles($tmp[0]) as $file) {
static::linkFile($file, '', $order); // Рекурсивно добавляем файлы
}
}
/**
* Удобный способ добавления JavaScript или CSS файла в футер
*
* @param string $path Путь к JavaScript или CSS файлу
* @param string $type
* @param int $order
* @return bool
* @throws Exception
*/
public static function linkFileFooter($path, $type = '', $order = 50)
{
$tmp = explode('?', $path); // Разделяем путь на основную часть и параметры
$fileName = $tmp[0]; // Основное имя файла без параметров
// Если файл уже был добавлен, выходим
if (in_array($fileName, static::$addedFiles)) {
return false;
}
// Если путь начинается с '@', заменяем его на соответствующий алиас
if (mb_strpos($fileName, '@') === 0) {
$fileName = static::$alias[$fileName];
} elseif (
mb_strpos($fileName, 'http://') === false
&& mb_strpos($fileName, 'https://') === false
&& mb_strpos($fileName, '//') !== 0
) {
// Если файл не является абсолютным URL, проверяем его существование
if (!file_exists($fileName)) {
throw new Exception('Resource file «' . $fileName . '» not exists');
}
}
// Если тип не указан, определяем его по расширению файла (css или js)
if (empty($type)) {
$type = preg_match('#\.(js|css)$#i', $fileName, $m) ? strtolower($m[1]) : 'js';
}
// Добавляем файл в список добавленных
static::$addedFiles[] = $tmp[0];
static::$footerRc[$type][$order][] = $path; // Добавляем путь в список ресурсов для вывода в footer
// Обрабатываем дополнительные файлы, если они есть
foreach (static::additionalFiles($tmp[0]) as $file) {
$type = preg_match('#\.(js|css)$#i', $file, $m) ? strtolower($m[1]) : 'js';
// Если это CSS и header еще не завершен, добавляем файл в header
if ($type == 'css' && !static::$headerComplete) {
static::linkFile($file, 'css', $order);
} else {
static::linkFileFooter($file, '', $order); // Иначе добавляем файл в footer
}
}
}
/**
* Удобный способ вывода встроенного стиля/скрипта в шапке страницы
*
* Пример: Resources::embed(" alert('ssssss') ");
* Пример: Resources::embed(" .blablabla {color: #000000} ", 'css');
*
* @param string $code Код стиля или скрипта
* @param int $order Порядок вывода
* @param string $type Тип ресурса: 'js' или 'css'
* @param string $attr Атрибут для тега вывода
*/
public static function embed($code, $type = 'js', $order = 50, $attr = '')
{
// Если header.php уже выполнен, пытаемся добавить код в футер
if (static::$headerComplete) {
Resources::embedFooter($code, $type, $order, $attr);
}
// Добавляем код стиля или скрипта в список ресурсов для вывода в header
static::$headerRc[$type . '_embed'][$order][] = cot_rc(
"code_rc_{$type}_embed",
['code' => $code, 'attr' => $attr]
);
}
/**
* Удобный способ вывода встроенного стиля/скрипта в футере страницы
*
* Пример: Resources::embedFooter(" alert('ssssss') ");
*
* @param string $code Код стиля или скрипта
* @param string $type Тип ресурса: 'js' или 'css'
* @param int $order Порядок вывода
* @param string $attr Атрибут для тега вывода
*/
public static function embedFooter($code, $type = 'js', $order = 50, $attr = '')
{
// Добавляем код стиля или скрипта в список ресурсов для вывода в footer
static::$footerRc[$type . '_embed'][$order][] = cot_rc("code_rc_{$type}_embed", array(
'code' => $code,
'attr' => $attr
));
}
/**
* Функция минимизации JS/CSS кода
*
* @param string $code Код для минимизации
* @param string $type Тип: 'js' или 'css'
*
* @return string Минифицированный код
*/
public static function minify($code, $type = 'js')
{
if ($type == 'js') {
require_once './lib/jsmin.php'; // Подключаем библиотеку для минимизации JS
$code = JSMin::minify($code); // Минифицируем JS код
} elseif ($type == 'css') {
require_once './lib/cssmin.php'; // Подключаем библиотеку для минимизации CSS
$code = minify_css($code); // Минифицируем CSS код
}
return $code; // Возвращаем минимизированный код
}
/**
* Устанавливает псевдоним ресурса
* @param string $newAlias Новый псевдоним
* @param string $value Значение для псевдонима
* @param bool $canReWrite Разрешение на перезапись существующего псевдонима
* @return bool Возвращает true, если псевдоним был установлен успешно, иначе false
*/
public static function setAlias($newAlias, $value = '', $canReWrite = false)
{
// Если псевдоним пустой, возвращаем false
if ($newAlias == '') {
return false;
}
// Если псевдоним не начинается с '@', добавляем его в начало
if (mb_strpos($newAlias, '@') === false) {
$newAlias = '@' . $newAlias;
}
// Если перезапись не разрешена, а псевдоним уже существует, возвращаем false
if (!$canReWrite && isset(static::$alias[$newAlias]) && static::$alias[$newAlias] !== null) {
return false;
}
// Устанавливаем значение для псевдонима
static::$alias[$newAlias] = $value;
return true;
}
/**
* Получить значение по псевдониму
* @param string $aliasName Имя псевдонима
* @return mixed|null Возвращает значение псевдонима или null, если он не найден
*/
public static function getAlias($aliasName)
{
// Если имя псевдонима пустое, возвращаем null
if ($aliasName == '') {
return null;
}
// Если псевдоним не начинается с '@', добавляем его в начало
if (mb_strpos($aliasName, '@') === false) {
$aliasName = '@' . $aliasName;
}
// Если псевдоним не существует, возвращаем null
if (!isset(static::$alias[$aliasName])) {
return null;
}
// Возвращаем значение псевдонима
return static::$alias[$aliasName];
}
/**
* Проверяет, был ли файл уже добавлен
*
* Псевдонимы файлов не разрешаются. Они могут быть переопределены. Используйте псевдонимы, если это возможно.
*
* @param string $fileName Имя файла или его псевдоним
* @return bool Возвращает true, если файл уже добавлен, иначе false
*/
public static function isFileAdded($fileName)
{
// Проверяем, был ли файл уже добавлен
if (in_array($fileName, static::$addedFiles)) {
return true;
}
return false;
}
// Инициализация ресурсов
Resources::__init();
Комментарии (0)
Автор контента
Оффлайн
Administrator
Последняя авторизация: 05.07.2026 18:32
- Страница размещена: 24.01.2025 13:14
- Последнее обновление: 24.01.2025 13:38