first commit

This commit is contained in:
/usr/bin/nano
2017-04-15 01:34:36 +03:00
commit c715e2a604
5325 changed files with 329700 additions and 0 deletions

157
lib/ExtraPacker/Config.php Executable file
View File

@@ -0,0 +1,157 @@
<?php
/**
* Класс настроечных функций ExtraPacker-а
*
* @author Zmi
*/
class ExtraPacker_Config
{
private static function GetDocRoot()
{
$path = BASEPATH;
if (SITE_IN_DIR)
{
$path = substr($path, 0, strrpos($path, SITE_IN_DIR));
}
return $path;
}
private static function GetPathFromUrl($url)
{
$url = substr($url, strpos($url, SITE_ROOT) + strlen(SITE_ROOT));
return self::GetDocRoot() . $url;
}
public static function GetPathJsFileFromUrl($url)
{
return self::GetPathFromUrl($url);
}
public static function GetPathCssFileFromUrl($url)
{
return self::GetPathFromUrl($url);
}
private static function GetAddrPackFile($path)
{
return str_replace(self::GetDocRoot(), '/', $path);
}
public static function GetAddrJsPackFile($path)
{
return self::GetAddrPackFile($path);
}
public static function GetAddrCssPackFile($path)
{
return self::GetAddrPackFile($path);
}
public static function ChangeCssUrl($m)
{
// Если сайт в папке то надо добавить эту папку в пути поиска
$docRoot = str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT']);
$docRoot = substr($docRoot, -1, 1) == '/' ? substr($docRoot, 0, -1) : $docRoot;
$dir = str_replace($docRoot, '', BASEPATH);
$dir = trim($dir, '/');
$dir = empty($dir) ? '' : "/{$dir}";
$curUrl = $dir . $GLOBALS['__engineExtraPackerChangeCssUrl__curUrl__'];
$url = isset($m[1]) ? trim($m[1], '\'"') : '';
if ($url)
{
// Это не Less-переменная, не путь вида http://google.com/ и не url(data:image/png;base64,....
if (strpos($url, "@") === false && strpos($url, "://") === false && strpos($url, "data:") !== 0)
{
// Это не абсолютный адрес
if ($url[0] !== '/')
{
$url = "$curUrl/$url";
}
// Если это абсолтный путь но к корню это сайта, но сайт запущен из каталога то переделываем пути так что бы они были как-будто из каталога
elseif (strlen($dir) && strpos($url, $dir) === false)
{
$url = $dir . $url;
}
}
}
return $url;
}
public static function ChangeCssUrl_Url($url)
{
$url = self::ChangeCssUrl($url);
return "url('$url')";
}
public static function ChangeCssUrl_Src($url)
{
$url = self::ChangeCssUrl($url);
return "src='$url'";
}
/**
* Предобработка каждого файла перед помещением в общий архив
*
* @param string $content
* @param string $file
* @param string $type либо js либо css
*/
public static function PrepareEachFile($content, $path, $type)
{
global $g_config;
$url = str_replace(BASEPATH, '/', $path);
if ($type == 'css')
{
// Меняем пути к файлам
$GLOBALS['__engineExtraPackerChangeCssUrl__curUrl__'] = dirname($url);
$content = preg_replace_callback("~url\((.*?)\)~is", array(__CLASS__, "ChangeCssUrl_Url"), $content);
unset($GLOBALS['__engineExtraPackerChangeCssUrl__curUrl__']);
}
return $content;
}
/**
* Подготовка спакованного js-контента перед записью в файл
*
* Данная функция по умолчанию дописывает пустой обработчик ошибок в начало js файла если это production
*/
public static function PrepareAllJs($content)
{
global $g_config;
$noErrorsCode = '';
// Если не debug режим то выключаем javascript ошибки
if (!$g_config['phpIni']['display_errors'])
{
ob_start();
?>
function __MyErrHandler(msg)
{
return true;
}
window.onerror = __MyErrHandler;
<?php
$noErrorsCode = ob_get_clean();
}
return $noErrorsCode . $content;
}
/**
* Подготовка всего спакованного контента css перед записью в файл
*
* Пример использования:
* Пусть у нас галит ф-я переписывания путей к картинкам в css, и она ставит BASEPATH тогда можно сделать что-то вроде такого:
* return str_replace("/home/Sites/test.com/i/", "/i/")
*/
public static function PrepareAllCss($content)
{
return $content;
}
};
?>

692
lib/ExtraPacker/ExtraPacker.php Executable file
View File

@@ -0,0 +1,692 @@
<?php
require_once dirname(__FILE__) . "/Lib/CssPacker.php";
require_once dirname(__FILE__) . "/Lib/HtmlPacker.php";
require_once dirname(__FILE__) . "/Lib/JSMin.php";
require_once dirname(__FILE__) . "/Lib/lessc.inc.php";
/**
* ExtraPacker - для склеивания и сжатия css и js файлов, а так же сжатия html-контента
*
* Выбирает все подключаемые js-ки (тоже с css-ками) собирает в 1 файл и пакует записывая файл info с информацией по запакованным файлам.
* Поддерживает механизм транзакций.
*
* @author Zmi
*/
class ExtraPacker
{
/**
* Коэфициент сжатия для js/css при GZIP сжатии
*/
const COEF_GZIP_COMPRESS = 9;
/**
* Тег после которого должны встать ссылки на запакованные js и css файлы должен рассполагаться в head разделе документа
*/
const TAG_EXTRAPACKER = '<!-- extraPacker -->';
const MEMORY_FOR_PACKING = 67108864; // 64Mb
private $addrJsCacheFileInfo;
private $addrJsCacheFile;
private $addrCssCacheFileInfo;
private $addrCssCacheFile;
private $addrNumJsTrans;
private $addrNumCssTrans;
private $flgHtmlPack;
private $flgCssPack;
private $flgJsPack;
private $flgUseTransSystem;
private $gzipPostfix = '.gz';
private $pCallBackFunctionJS = NULL;
private $pCallBackFunctionCSS = NULL;
private $pFuncGetAddrJsPackFile = NULL;
private $pFuncGetAddrCssPackFile = NULL;
private $pGetPathJsFileFromUrl = NULL;
private $pGetPathCssFileFromUrl = NULL;
private $arrExeptions_js;
private $arrExeptionsNotAdd_js;
private $arrExeptions_css;
private $arrExeptionsNotAdd_css;
private $pFuncPreWriteCache;
private $pFuncPostWriteCache;
private $flgUseGZIP;
private $flgBuffering;
private $pPrepareEachFile = NULL;
// Только склеивать файлы
const ONLY_MERGE_CSS = false;
const ONLY_MERGE_JS = false;
// Перепаковывать каждый раз (удобно если = DEBUG_MODE)
const ALWAYS_INDEPENDENT_PACK = false;
const ALWAYS_REPACK = false;
private static function MakeDir($path)
{
if (is_dir($path))
{
return;
}
$path = explode("/", $path);
if (count($path) == 1)
{
$path = explode("\\", $path[0]);
}
$d = "";
foreach ($path as $dir)
{
$d .= $dir . "/";
if ($d != "/" && $d != "/home/" && !is_dir($d))
{
@mkdir($d, 0777);
@chmod($d, 0777);
}
}
}
private static function WriteFile($file, $b)
{
self::MakeDir(dirname($file));
fclose(fopen($file, "a+"));
$f = fopen($file,"r+");
fwrite($f, $b);
fclose($f);
@chmod($file, 0777);
}
private function HtmlPack($html)
{
return Minify_HTML::minify($html);
}
private function PrepareJsContent($jsContent)
{
if ($this->pCallBackFunctionJS)
{
$jsContent = call_user_func($this->pCallBackFunctionJS, $jsContent);
}
return $jsContent;
}
private function PrepareCssContent($cssContent)
{
if ($this->pCallBackFunctionCSS)
{
$cssContent = call_user_func($this->pCallBackFunctionCSS, $cssContent);
}
return $cssContent;
}
private static function ReadFile($file, $flag = false)
{
$file = trim($file);
if (!strlen(trim($file)))
{
return false;
}
if ($flag && $file[0] == "/")
{
$file = $_SERVER["DOCUMENT_ROOT"] . $file;
}
$b = false;
if (strstr($file, "http://") == $file)
{
$f = fopen($file, "r");
while (!feof($f))
{
$b .= fread($f, 1024);
}
}
elseif (file_exists($file) && is_readable($file))
{
$f = fopen($file, "r");
$b = fread($f, filesize($file));
}
if (isset($f) && $f)
{
fclose($f);
}
return $b;
}
private function GetTrans($type)
{
if (file_exists($type == 'js' ? $this->addrNumJsTrans : $this->addrNumCssTrans))
{
$number = self::ReadFile($type == 'js' ? $this->addrNumJsTrans : $this->addrNumCssTrans);
return (int)$number;
}
else
{
return 0;
}
}
private function IncTrans($type)
{
self::WriteFile($type == 'js' ? $this->addrNumJsTrans : $this->addrNumCssTrans, time());
}
private function UnickList($arrFiles)
{
$arrRes = array();
foreach ($arrFiles as $file)
{
$arrRes[$file['addr']] = $file;
}
return array_values($arrRes);
}
public static function MemInBytes($mem)
{
$lastSymbol = strtoupper(substr($mem, -1));
$mem = in_array($lastSymbol, array('K', 'M', 'G')) ? substr($mem, 0, -1) : $mem;
switch ($lastSymbol)
{
case 'K':
$mem *= 1024;
break;
case 'M':
$mem *= (1024 * 1024);
break;
case 'G':
$mem *= (1024 * 1024 * 1024);
break;
};
return $mem;
}
private function WriteDataInCache($type, $arrFiles, $arrExeptions = array())
{
// Устанавливаем памяти на выполнение побольше т.к. будет идти создание pack-версии js/css
global $g_config;
$memInBytes = self::MemInBytes($g_config['phpIni']['memory_limit']);
if (self::MEMORY_FOR_PACKING > $memInBytes)
{
ini_set('memory_limit', self::MEMORY_FOR_PACKING);
}
// Устанавлиаем время выполнения скрипта больше той что установлено сейчас на 30 секунд
ini_set('max_execution_time', intval(intval($g_config['phpIni']['max_execution_time']) + 30));
if ($this->pFuncPreWriteCache)
{
call_user_func($this->pFuncPreWriteCache);
}
if ($this->flgUseTransSystem)
{
$this->IncTrans($type);
}
if ($this->flgUseTransSystem)
{
$ext = '.' . $type;
$t = str_replace($ext, self::GetTrans($type) . $ext, $type == 'js' ? $this->addrJsCacheFile : $this->addrCssCacheFile);
$type == 'js' ? $this->addrJsCacheFile = $t : $this->addrCssCacheFile = $t;
}
// Удаляем файлы из списка для запаковки которых уже нет на сервере
foreach ($arrFiles as $k => $v)
{
if (!file_exists($v['addr']))
{
unset($arrFiles[$k]);
}
}
$packInfoAboutFiles = serialize($arrFiles);
self::WriteFile($type == 'js' ? $this->addrJsCacheFileInfo : $this->addrCssCacheFileInfo, $packInfoAboutFiles);
foreach ($arrExeptions as $k => $v)
{
$arrExeptions[$k] = call_user_func($type == 'js' ? $this->pGetPathJsFileFromUrl : $this->pGetPathCssFileFromUrl , $v);
}
$endl = $type == 'js' ? ";" : '';
$fullExeptionContent = "";
$fullContent = "";
foreach ($arrFiles as $file)
{
$file = $file['addr'];
$curContent = file_get_contents($file);
// Если стоит обработка каждого файла перед добавлением в сжатый архив
if ($this->pPrepareEachFile)
{
$curContent = call_user_func($this->pPrepareEachFile, $curContent, $file, $type);
}
if (in_array($file, $arrExeptions))
{
$fullExeptionContent .= $curContent . $endl;
}
else
{
$fullContent .= $curContent . $endl;
}
}
if ($type == 'js')
{
if (!self::ONLY_MERGE_JS)
{
$packed = JSMin::minify($fullContent);
}
else
{
$packed = $fullContent;
}
}
else
{
$less = new lessc();
$less->importDir = BASEPATH . 'i/css/';
$fullContent = $less->parse($fullContent);
if (!self::ONLY_MERGE_CSS)
{
$cssPacker = new CssPacker($fullContent);
$packed = $cssPacker->Pack();
}
else
{
$packed = $fullContent;
}
}
$packed = $type == 'js' ? ($packed . $fullExeptionContent) : ($fullExeptionContent . $packed);
$packed = $type == 'js' ? $this->PrepareJsContent($packed) : $this->PrepareCssContent($packed); // Дополнительно обработать контент пользовательской функцией (если есть)
self::WriteFile($type == 'js' ? $this->addrJsCacheFile : $this->addrCssCacheFile, $packed);
self::WriteFile(($type == 'js' ? $this->addrJsCacheFile : $this->addrCssCacheFile) . $this->gzipPostfix, gzencode($packed, self::COEF_GZIP_COMPRESS));
if ($this->pFuncPostWriteCache)
{
call_user_func($this->pFuncPostWriteCache);
}
}
/**
* Если массив now содержиться в storage то перепаковывать не надо т.к. все запакованные данные есть
*/
private function NeedRePack($storage, $now)
{
if (self::ALWAYS_REPACK) return true;
$ret = false;
foreach ($now as $v)
{
if (!in_array($v, $storage))
{
$ret = true;
break;
}
}
return $ret;
}
private function JsPack($html)
{
$arrExeptions = $this->arrExeptions_js;
$arrExeptionsNotAdd = $this->arrExeptionsNotAdd_js;
$s = preg_replace('/<!--.*-->/Uis', '', $html);
$m = array();
preg_match("~<head.*?>(.*?)</head>~is", $s, $m);
if (empty($m))
{
return $html;
}
$s = $m[1];
$m = array();
preg_match_all("~<script.*?src=['\"](.*?).js['\"].*?></script>~", $s, $m);
if (empty($m))
{
return $html;
}
$arrJsFiles = array();
foreach ($m[1] as $k => $jsAddr)
{
$jsAddr = $jsAddr . ".js";
if (in_array($jsAddr, $arrExeptionsNotAdd))
{
unset($m[1][$k]);
continue;
}
$html = str_ireplace($m[0][$k], "", $html);
$jsAddr = call_user_func($this->pGetPathJsFileFromUrl, $jsAddr);
$whenModify = filemtime($jsAddr);
$arrJsFiles[] = array('time' => $whenModify, 'addr' => $jsAddr);
}
$arrJsFiles = $this->UnickList($arrJsFiles);
if (file_exists($this->addrJsCacheFileInfo))
{
$strInfoJsFiles = file_get_contents($this->addrJsCacheFileInfo);
$arrInfoJsFiles = unserialize($strInfoJsFiles);
if ($this->NeedRePack($arrInfoJsFiles, $arrJsFiles))
{
if (self::ALWAYS_INDEPENDENT_PACK)
{
$arrInfoJsFiles = array();
}
$arrAllJsFiles = self::AUnique(self::AMerge($arrInfoJsFiles, $arrJsFiles));
self::WriteDataInCache('js', $arrAllJsFiles, $arrExeptions);
}
else
{
if ($this->flgUseTransSystem)
{
$this->addrJsCacheFile = str_replace(".js", self::GetTrans('js') . ".js", $this->addrJsCacheFile);
}
}
}
else
{
self::WriteDataInCache('js', $arrJsFiles , $arrExeptions);
}
$includeFileAddr = ($this->flgBuffering && $this->flgUseGZIP) ? $this->addrJsCacheFile.$this->gzipPostfix : $this->addrJsCacheFile;
$inc = call_user_func($this->pFuncGetAddrJsPackFile, $includeFileAddr);
$html = str_ireplace(self::TAG_EXTRAPACKER, self::TAG_EXTRAPACKER . '<script type="text/javascript" charset="UTF-8" src="' . $inc . '"></script>', $html);
return $html;
}
private static function AUnique($arr)
{
$a = array();
foreach ($arr as $v)
{
$a[$v['addr']] = $v;
}
return array_values($a);
}
// По сути тот же самый array_merge
private static function AMerge()
{
$arrs = func_get_args(); // Массивы которые нужно слить в один
$eaxA = array(); // Результирующий массив
$eaxAddrsOnly = array(); // Массив только адресов к файлам (нужен просто для более локаничного вычисления)
foreach ($arrs as $arr)
{
foreach ($arr as $canBeElem)
{
// Если текущий файл уже был добавлен
if (in_array($canBeElem['addr'], $eaxAddrsOnly))
{
// То находим его и проверяем, если время его изменения в новом массиве больше старого то заменяем, иначе ничего не делаем
foreach ($eaxA as $k => $v)
{
if ($v['addr'] == $canBeElem['addr'])
{
if ($v['time'] < $canBeElem['time'])
{
$eaxA[$k] = $canBeElem;
}
}
}
}
// Если же этого файла нет в массиве то добавляем его следущим
else
{
$eaxA[] = $canBeElem;
$eaxAddrsOnly[] = $canBeElem['addr'];
}
}
}
return $eaxA;
}
private function CssPack($html)
{
$arrExeptions = $this->arrExeptions_css;
$arrExeptionsNotAdd = $this->arrExeptionsNotAdd_css;
$s = preg_replace('/<!--.*-->/Uis', '', $html);
$m = array();
preg_match("~<head.*?>(.*?)</head>~is", $s, $m);
if (empty($m))
{
return $html;
}
$s = $m[1];
$m = array();
preg_match_all("~<link.*?href=['\"](.*?)\.(css|less)['\"].*?>~", $s, $m);
if (empty($m))
{
return $html;
}
$arrCssFiles = array();
foreach ($m[1] as $k => $cssAddr)
{
$ext = $m[2][$k];
$cssAddr .= ".{$ext}";
if (in_array($cssAddr, $arrExeptionsNotAdd))
{
unset($m[1][$k]);
continue;
}
$html = str_ireplace($m[0][$k], "", $html);
$cssAddr = call_user_func($this->pGetPathCssFileFromUrl, $cssAddr);
$whenModify = filemtime($cssAddr);
$arrCssFiles[] = array('time' => $whenModify, 'addr' => $cssAddr);
}
$arrCssFiles = $this->UnickList($arrCssFiles);
if (file_exists($this->addrCssCacheFileInfo))
{
$strInfoCssFiles = file_get_contents($this->addrCssCacheFileInfo);
$arrInfoCssFiles = unserialize($strInfoCssFiles);
if ($this->NeedRePack($arrInfoCssFiles, $arrCssFiles))
{
if (self::ALWAYS_INDEPENDENT_PACK)
{
$arrInfoCssFiles = array();
}
$arrAllCssFiles = self::AUnique(self::AMerge($arrInfoCssFiles, $arrCssFiles));
self::WriteDataInCache('css', $arrAllCssFiles, $arrExeptions);
}
else
{
if ($this->flgUseTransSystem)
{
$this->addrCssCacheFile = str_replace(".css", self::GetTrans('css') . ".css", $this->addrCssCacheFile);
}
}
}
else
{
self::WriteDataInCache('css', $arrCssFiles , $arrExeptions);
}
$includeFileAddr = ($this->flgBuffering && $this->flgUseGZIP) ? $this->addrCssCacheFile.$this->gzipPostfix : $this->addrCssCacheFile;
$inc = call_user_func($this->pFuncGetAddrCssPackFile, $includeFileAddr);
$html = str_ireplace(self::TAG_EXTRAPACKER, self::TAG_EXTRAPACKER . '<link rel="stylesheet" charset="UTF-8" type="text/css" href="' . $inc . '" />', $html);
return $html;
}
private function CanUseGZIP()
{
$httpAcceptEncoding = isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : '';
$hasGzip = strstr($httpAcceptEncoding, 'gzip') !== false && extension_loaded('zlib');
$canEncoding = true;
if (!isset($_SERVER['HTTP_USER_AGENT']))
{
return false;
}
$matches = array();
if (stripos($_SERVER['HTTP_USER_AGENT'], 'safari') === false)
{
if (!strstr($_SERVER['HTTP_USER_AGENT'], 'Opera')
&&
preg_match('/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i', $_SERVER['HTTP_USER_AGENT'], $matches))
{
if (isset($matches[1]))
{
$version = floatval($matches[1]);
if ($version < 6)
{
$canEncoding = false;
}
if ($version == 6 && !strstr($_SERVER['HTTP_USER_AGENT'], 'EV1'))
{
$canEncoding = false;
}
}
}
}
else
{
$canEncoding = false;
}
return $canEncoding && $hasGzip;
}
/**
* Функция запаковки контента в зависимости от параметров пакера
*
* @param string html-контент документа в котором содержится head с подключенными js-ками и css-ами
*/
public function Pack($html)
{
// Если нету тега для замены ExtraPacker-ом то просто вернуть контент
if (strpos($html, self::TAG_EXTRAPACKER) === false)
{
return $html;
}
if ($this->flgJsPack)
{
$html = $this->JsPack($html);
}
if ($this->flgCssPack)
{
$html = $this->CssPack($html);
}
if ($this->flgHtmlPack)
{
$html = $this->HtmlPack($html);
}
return $html;
}
/**
* Конструктор объекта
*
* @param pointer $pGetPathJsFileFromUrl - ссылка на функцию которая получит из url в src js-ки физический путь на диске к этому файлу
* @param pointer $pGetPathCssFileFromUrl - ссылка на функцию которая получит из url в href css-ки физический путь на диске к этому файлу
* @param pointer $pFuncGetAddrJsPackFile - ссылка на фунцию которая получит из path на диске спакованного js-файла url который подставится в новый src спакованной js-ки
* @param pointer $pFuncGetAddrCssPackFile - ссылка на фунцию которая получит из path на диске спакованного css-файла url который подставится в новый href спакованной css-ки
* @param pointer $pFuncPreWriteCache - указатель на функцию выполняемую до начала работы по созданию нового кеш-файла (обычно блокировка сайта)
* @param pointer $pFuncPostWriteCache - указатель на функцию выполняемую по завершению работы и записи кеш-файла (обычно разблокировка сайта)
* @param string $addrJsCacheFileInfo - путь к временному файлу с информацией о спакованных js файлах в текущей транзакции
* @param string $addrJsCacheFile - путь к временному кеш файлу спакованных js-ок (GZIP будут с таким же именем только префикс из настроек добавится)
* @param string $addrCssCacheFileInfo - путь к временному файлу с информацией о спакованных css файлах в текущей транзакции
* @param string $addrCssCacheFile - путь к временному кеш файлу спакованных css-ок (GZIP будут с таким же именем только префикс из настроек добавится)
* @param bool $flgHtmlPack - флаг включения html-запаковки (могут быть проблемы если используете js-подстветки кода сайте т.к. в начале удалятся пробелы и табуляция)
* @param bool $flgCssPack - флаг включения css-запаковки
* @param bool $flgJsPack - флаг включения js-запаковки
* @param array $arrExeptions_js - массив исключений 1-го порядка (url-ы как и src), данные файлы прибавятся склеенному в конец, но не будут поковаться
* @param array $arrExeptionsNotAdd_js - массив исключений 2-го порядка - вообще не добавятся в запокованный файл
* @param array $arrExeptions_css - массив исключений 1-го порядка (url-ы как и href), данные файлы прибавятся склеенному в конец, но не будут поковаться
* @param array $arrExeptionsNotAdd_css - массив исключений 2-го порядка - вообще не добавятся в запокованный файл
* @param bool $flgUseTransSystem - использоватьс систему транзакций (в url запакованного файла ставится № говорящий о текущей транзакции)
* @param string $addrNumJsTrans - путь к файлу содержащий № js транзакций
* @param string $addrNumCssTrans - путь к файлу содержащий № css транзакций
* @param bool $flgBuffering - включить авто-определение GZIP буферизации для браузеров
*/
public function __construct(
$pGetPathJsFileFromUrl,
$pGetPathCssFileFromUrl,
$pFuncGetAddrJsPackFile,
$pFuncGetAddrCssPackFile,
$pFuncPreWriteCache = NULL,
$pFuncPostWriteCache = NULL,
$addrJsCacheFileInfo = '',
$addrJsCacheFile = '',
$addrCssCacheFileInfo = '',
$addrCssCacheFile = '',
$flgHtmlPack = true,
$flgCssPack = true,
$flgJsPack = true,
$arrExeptions_js = array(),
$arrExeptionsNotAdd_js = array(),
$arrExeptions_css = array(),
$arrExeptionsNotAdd_css = array(),
$flgUseTransSystem = true,
$addrNumJsTrans = '',
$addrNumCssTrans = '',
$flgBuffering = true,
$pPrepareEachFile = NULL,
$pCallBackFunctionCSS = NULL,
$pCallBackFunctionJS = NULL
)
{
$this->pGetPathJsFileFromUrl = $pGetPathJsFileFromUrl;
$this->pGetPathCssFileFromUrl = $pGetPathCssFileFromUrl;
$this->pFuncGetAddrJsPackFile = $pFuncGetAddrJsPackFile;
$this->pFuncGetAddrCssPackFile = $pFuncGetAddrCssPackFile;
$this->pFuncPreWriteCache = $pFuncPreWriteCache;
$this->pFuncPostWriteCache = $pFuncPostWriteCache;
$defaultDir = str_replace("\\", "/", dirname(__FILE__)) . '/tmp/';
$this->addrJsCacheFileInfo = $addrJsCacheFileInfo ? $addrJsCacheFileInfo : ($defaultDir . "js/cache/info.txt");
$this->addrJsCacheFile = $addrJsCacheFile ? $addrJsCacheFile : ($defaultDir . "js/cache/js.js");
$this->addrCssCacheFileInfo = $addrCssCacheFileInfo ? $addrCssCacheFileInfo : ($defaultDir . "css/cache/info.txt");
$this->addrCssCacheFile = $addrCssCacheFile ? $addrCssCacheFile : ($defaultDir . "css/cache/css.css");
$this->flgHtmlPack = $flgHtmlPack;
$this->flgCssPack = $flgCssPack;
$this->flgJsPack = $flgJsPack;
$this->arrExeptions_js = $arrExeptions_js;
$this->arrExeptionsNotAdd_js = $arrExeptionsNotAdd_js;
$this->arrExeptions_css = $arrExeptions_css;
$this->arrExeptionsNotAdd_css = $arrExeptionsNotAdd_css;
$this->flgUseTransSystem = $flgUseTransSystem;
$this->addrNumJsTrans = $addrNumJsTrans ? $addrNumJsTrans : ($defaultDir . 'js/cache/trans.txt');
$this->addrNumCssTrans = $addrNumCssTrans ? $addrNumCssTrans : ($defaultDir . 'css/cache/trans.txt');
$this->flgBuffering = $flgBuffering;
$this->flgUseGZIP = self::CanUseGZIP();
$this->pPrepareEachFile = $pPrepareEachFile;
$this->pCallBackFunctionCSS = $pCallBackFunctionCSS;
$this->pCallBackFunctionJS = $pCallBackFunctionJS;
}
};
?>

View File

@@ -0,0 +1,25 @@
<?php
require_once dirname(__FILE__) . "/cssmin-v1.0.1.b3.php";
/**
* Пакеровщик css файлов
*
* @author Zmi
*/
class CssPacker
{
private $content;
public function __construct($c)
{
$this->content = $c;
}
public function Pack()
{
return cssmin::minify($this->content);
}
};
?>

View File

@@ -0,0 +1,258 @@
<?php
/**
* Class Minify_HTML
* @package Minify
*/
/**
* Compress HTML
*
* This is a heavy regex-based removal of whitespace, unnecessary comments and
* tokens. IE conditional comments are preserved. There are also options to have
* STYLE and SCRIPT blocks compressed by callback functions.
*
* A test suite is available.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_HTML {
/**
* @var boolean
*/
protected $_jsCleanComments = true;
/**
* "Minify" an HTML page
*
* @param string $html
*
* @param array $options
*
* 'cssMinifier' : (optional) callback function to process content of STYLE
* elements.
*
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored.
*
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype.
*
* @return string
*/
public static function minify($html, $options = array()) {
$min = new self($html, $options);
return $min->process();
}
/**
* Create a minifier object
*
* @param string $html
*
* @param array $options
*
* 'cssMinifier' : (optional) callback function to process content of STYLE
* elements.
*
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored.
*
* 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
*
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype.
*
* @return null
*/
public function __construct($html, $options = array())
{
$this->_html = str_replace("\r\n", "\n", trim($html));
if (isset($options['xhtml'])) {
$this->_isXhtml = (bool)$options['xhtml'];
}
if (isset($options['cssMinifier'])) {
$this->_cssMinifier = $options['cssMinifier'];
}
if (isset($options['jsMinifier'])) {
$this->_jsMinifier = $options['jsMinifier'];
}
if (isset($options['jsCleanComments'])) {
$this->_jsCleanComments = (bool)$options['jsCleanComments'];
}
}
/**
* Minify the markeup given in the constructor
*
* @return string
*/
public function process()
{
if ($this->_isXhtml === null) {
$this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
}
$this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
$this->_placeholders = array();
// replace SCRIPTs (and minify) with placeholders
$this->_html = preg_replace_callback(
'/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
,array($this, '_removeScriptCB')
,$this->_html);
// replace STYLEs (and minify) with placeholders
$this->_html = preg_replace_callback(
'/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
,array($this, '_removeStyleCB')
,$this->_html);
// remove HTML comments (not containing IE conditional comments).
$this->_html = preg_replace_callback(
'/<!--([\\s\\S]*?)-->/'
,array($this, '_commentCB')
,$this->_html);
// replace PREs with placeholders
$this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
,array($this, '_removePreCB')
,$this->_html);
// replace TEXTAREAs with placeholders
$this->_html = preg_replace_callback(
'/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
,array($this, '_removeTextareaCB')
,$this->_html);
// trim each line.
// @todo take into account attribute values that span multiple lines.
$this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
// remove ws around block/undisplayed elements
$this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
.'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
.'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
.'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
.'|ul)\\b[^>]*>)/i', '$1', $this->_html);
// remove ws outside of all elements
$this->_html = preg_replace(
'/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</'
,'>$1$2$3<'
,$this->_html);
// use newlines before 1st attribute in open tags (to limit line lengths)
$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
// fill placeholders
$this->_html = str_replace(
array_keys($this->_placeholders)
,array_values($this->_placeholders)
,$this->_html
);
// issue 229: multi-pass to catch scripts that didn't get replaced in textareas
$this->_html = str_replace(
array_keys($this->_placeholders)
,array_values($this->_placeholders)
,$this->_html
);
return $this->_html;
}
protected function _commentCB($m)
{
return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
? $m[0]
: '';
}
protected function _reservePlace($content)
{
$placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
$this->_placeholders[$placeholder] = $content;
return $placeholder;
}
protected $_isXhtml = null;
protected $_replacementHash = null;
protected $_placeholders = array();
protected $_cssMinifier = null;
protected $_jsMinifier = null;
protected function _removePreCB($m)
{
return $this->_reservePlace("<pre{$m[1]}");
}
protected function _removeTextareaCB($m)
{
return $this->_reservePlace("<textarea{$m[1]}");
}
protected function _removeStyleCB($m)
{
$openStyle = "<style{$m[1]}";
$css = $m[2];
// remove HTML comments
$css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
// remove CDATA section markers
$css = $this->_removeCdata($css);
// minify
$minifier = $this->_cssMinifier
? $this->_cssMinifier
: 'trim';
$css = call_user_func($minifier, $css);
return $this->_reservePlace($this->_needsCdata($css)
? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
: "{$openStyle}{$css}</style>"
);
}
protected function _removeScriptCB($m)
{
$openScript = "<script{$m[2]}";
$js = $m[3];
// whitespace surrounding? preserve at least one space
$ws1 = ($m[1] === '') ? '' : ' ';
$ws2 = ($m[4] === '') ? '' : ' ';
// remove HTML comments (and ending "//" if present)
if ($this->_jsCleanComments) {
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
}
// remove CDATA section markers
$js = $this->_removeCdata($js);
// minify
$minifier = $this->_jsMinifier
? $this->_jsMinifier
: 'trim';
$js = call_user_func($minifier, $js);
return $this->_reservePlace($this->_needsCdata($js)
? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
: "{$ws1}{$openScript}{$js}</script>{$ws2}"
);
}
protected function _removeCdata($str)
{
return (false !== strpos($str, '<![CDATA['))
? str_replace(array('<![CDATA[', ']]>'), '', $str)
: $str;
}
protected function _needsCdata($str)
{
return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
}
};
?>

438
lib/ExtraPacker/Lib/JSMin.php Executable file
View File

@@ -0,0 +1,438 @@
<?php
/**
* JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
*
* <code>
* $minifiedJs = JSMin::minify($js);
* </code>
*
* This is a modified port of jsmin.c. Improvements:
*
* Does not choke on some regexp literals containing quote characters. E.g. /'/
*
* Spaces are preserved after some add/sub operators, so they are not mistakenly
* converted to post-inc/dec. E.g. a + ++b -> a+ ++b
*
* Preserves multi-line comments that begin with /*!
*
* PHP 5 or higher is required.
*
* Permission is hereby granted to use this version of the library under the
* same terms as jsmin.c, which has the following license:
*
* --
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* The Software shall be used for Good, not Evil.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* --
*
* @package JSMin
* @author Ryan Grove <ryan@wonko.com> (PHP port)
* @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
* @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://code.google.com/p/jsmin-php/
*/
class JSMin {
const ORD_LF = 10;
const ORD_SPACE = 32;
const ACTION_KEEP_A = 1;
const ACTION_DELETE_A = 2;
const ACTION_DELETE_A_B = 3;
protected $a = "\n";
protected $b = '';
protected $input = '';
protected $inputIndex = 0;
protected $inputLength = 0;
protected $lookAhead = null;
protected $output = '';
protected $lastByteOut = '';
protected $keptComment = '';
/**
* Minify Javascript.
*
* @param string $js Javascript to be minified
*
* @return string
*/
public static function minify($js)
{
$jsmin = new JSMin($js);
return $jsmin->min();
}
/**
* @param string $input
*/
public function __construct($input)
{
$this->input = $input;
}
/**
* Perform minification, return result
*
* @return string
*/
public function min()
{
if ($this->output !== '') { // min already run
return $this->output;
}
$mbIntEnc = null;
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
$mbIntEnc = mb_internal_encoding();
mb_internal_encoding('8bit');
}
$this->input = str_replace("\r\n", "\n", $this->input);
$this->inputLength = strlen($this->input);
$this->action(self::ACTION_DELETE_A_B);
while ($this->a !== null) {
// determine next command
$command = self::ACTION_KEEP_A; // default
if ($this->a === ' ') {
if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
&& ($this->b === $this->lastByteOut)) {
// Don't delete this space. If we do, the addition/subtraction
// could be parsed as a post-increment
} elseif (! $this->isAlphaNum($this->b)) {
$command = self::ACTION_DELETE_A;
}
} elseif ($this->a === "\n") {
if ($this->b === ' ') {
$command = self::ACTION_DELETE_A_B;
// in case of mbstring.func_overload & 2, must check for null b,
// otherwise mb_strpos will give WARNING
} elseif ($this->b === null
|| (false === strpos('{[(+-!~', $this->b)
&& ! $this->isAlphaNum($this->b))) {
$command = self::ACTION_DELETE_A;
}
} elseif (! $this->isAlphaNum($this->a)) {
if ($this->b === ' '
|| ($this->b === "\n"
&& (false === strpos('}])+-"\'', $this->a)))) {
$command = self::ACTION_DELETE_A_B;
}
}
$this->action($command);
}
$this->output = trim($this->output);
if ($mbIntEnc !== null) {
mb_internal_encoding($mbIntEnc);
}
return $this->output;
}
/**
* ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
* ACTION_DELETE_A = Copy B to A. Get the next B.
* ACTION_DELETE_A_B = Get the next B.
*
* @param int $command
* @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
*/
protected function action($command)
{
// make sure we don't compress "a + ++b" to "a+++b", etc.
if ($command === self::ACTION_DELETE_A_B
&& $this->b === ' '
&& ($this->a === '+' || $this->a === '-')) {
// Note: we're at an addition/substraction operator; the inputIndex
// will certainly be a valid index
if ($this->input[$this->inputIndex] === $this->a) {
// This is "+ +" or "- -". Don't delete the space.
$command = self::ACTION_KEEP_A;
}
}
switch ($command) {
case self::ACTION_KEEP_A: // 1
$this->output .= $this->a;
if ($this->keptComment) {
$this->output = rtrim($this->output, "\n");
$this->output .= $this->keptComment;
$this->keptComment = '';
}
$this->lastByteOut = $this->a;
// fallthrough intentional
case self::ACTION_DELETE_A: // 2
$this->a = $this->b;
if ($this->a === "'" || $this->a === '"') { // string literal
$str = $this->a; // in case needed for exception
for(;;) {
$this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get();
if ($this->a === $this->b) { // end quote
break;
}
if ($this->isEOF($this->a)) {
throw new JSMin_UnterminatedStringException(
"JSMin: Unterminated String at byte {$this->inputIndex}: {$str}");
}
$str .= $this->a;
if ($this->a === '\\') {
$this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get();
$str .= $this->a;
}
}
}
// fallthrough intentional
case self::ACTION_DELETE_A_B: // 3
$this->b = $this->next();
if ($this->b === '/' && $this->isRegexpLiteral()) {
$this->output .= $this->a . $this->b;
$pattern = '/'; // keep entire pattern in case we need to report it in the exception
for(;;) {
$this->a = $this->get();
$pattern .= $this->a;
if ($this->a === '[') {
for(;;) {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
if ($this->a === ']') {
break;
}
if ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
}
if ($this->isEOF($this->a)) {
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated set in RegExp at byte "
. $this->inputIndex .": {$pattern}");
}
}
}
if ($this->a === '/') { // end pattern
break; // while (true)
} elseif ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
} elseif ($this->isEOF($this->a)) {
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated RegExp at byte {$this->inputIndex}: {$pattern}");
}
$this->output .= $this->a;
$this->lastByteOut = $this->a;
}
$this->b = $this->next();
}
// end case ACTION_DELETE_A_B
}
}
/**
* @return bool
*/
protected function isRegexpLiteral()
{
if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
// we obviously aren't dividing
return true;
}
if ($this->a === ' ' || $this->a === "\n") {
$length = strlen($this->output);
if ($length < 2) { // weird edge case
return true;
}
// you can't divide a keyword
if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
if ($this->output === $m[0]) { // odd but could happen
return true;
}
// make sure it's a keyword, not end of an identifier
$charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
if (! $this->isAlphaNum($charBeforeKeyword)) {
return true;
}
}
}
return false;
}
/**
* Return the next character from stdin. Watch out for lookahead. If the character is a control character,
* translate it to a space or linefeed.
*
* @return string
*/
protected function get()
{
$c = $this->lookAhead;
$this->lookAhead = null;
if ($c === null) {
// getc(stdin)
if ($this->inputIndex < $this->inputLength) {
$c = $this->input[$this->inputIndex];
$this->inputIndex += 1;
} else {
$c = null;
}
}
if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
return $c;
}
if ($c === "\r") {
return "\n";
}
return ' ';
}
/**
* Does $a indicate end of input?
*
* @param string $a
* @return bool
*/
protected function isEOF($a)
{
return ord($a) <= self::ORD_LF;
}
/**
* Get next char (without getting it). If is ctrl character, translate to a space or newline.
*
* @return string
*/
protected function peek()
{
$this->lookAhead = $this->get();
return $this->lookAhead;
}
/**
* Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
*
* @param string $c
*
* @return bool
*/
protected function isAlphaNum($c)
{
return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
}
/**
* Consume a single line comment from input (possibly retaining it)
*/
protected function consumeSingleLineComment()
{
$comment = '';
while (true) {
$get = $this->get();
$comment .= $get;
if (ord($get) <= self::ORD_LF) { // end of line reached
// if IE conditional comment
if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
$this->keptComment .= "/{$comment}";
}
return;
}
}
}
/**
* Consume a multiple line comment from input (possibly retaining it)
*
* @throws JSMin_UnterminatedCommentException
*/
protected function consumeMultipleLineComment()
{
$this->get();
$comment = '';
for(;;) {
$get = $this->get();
if ($get === '*') {
if ($this->peek() === '/') { // end of comment reached
$this->get();
if (0 === strpos($comment, '!')) {
// preserved by YUI Compressor
if (!$this->keptComment) {
// don't prepend a newline if two comments right after one another
$this->keptComment = "\n";
}
$this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
} else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
// IE conditional
$this->keptComment .= "/*{$comment}*/";
}
return;
}
} elseif ($get === null) {
throw new JSMin_UnterminatedCommentException(
"JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
}
$comment .= $get;
}
}
/**
* Get the next character, skipping over comments. Some comments may be preserved.
*
* @return string
*/
protected function next()
{
$get = $this->get();
if ($get === '/') {
switch ($this->peek()) {
case '/':
$this->consumeSingleLineComment();
$get = "\n";
break;
case '*':
$this->consumeMultipleLineComment();
$get = ' ';
break;
}
}
return $get;
}
}
class JSMin_UnterminatedStringException extends Exception {}
class JSMin_UnterminatedCommentException extends Exception {}
class JSMin_UnterminatedRegExpException extends Exception {}
?>

View File

@@ -0,0 +1,206 @@
<?php
/**
* cssmin.php - A simple CSS minifier.
* --
*
* <code>
* include("cssmin.php");
* file_put_contents("path/to/target.css", cssmin::minify(file_get_contents("path/to/source.css")));
* </code>
* --
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* --
*
* @package cssmin
* @author Joe Scylla <joe.scylla@gmail.com>
* @copyright 2008 Joe Scylla <joe.scylla@gmail.com>
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 1.0.1.b3 (2008-10-02)
*/
class cssmin
{
/**
* Minifies stylesheet definitions
*
* <code>
* $css_minified = cssmin::minify(file_get_contents("path/to/target/file.css"));
* </code>
*
* @param string $css Stylesheet definitions as string
* @param array|string $options Array or comma speperated list of options:
*
* - remove-last-semicolon: Removes the last semicolon in
* the style definition of an element (activated by default).
*
* - preserve-urls: Preserves every url defined in an url()-
* expression. This option is only required if you have
* defined really uncommon urls with multiple spaces or
* combination of colon, semi-colon, braces with leading or
* following spaces.
* @return string Minified stylesheet definitions
*/
public static function minify($css, $options = "remove-last-semicolon")
{
$options = ($options == "") ? array() : (is_array($options) ? $options : explode(",", $options));
if (in_array("preserve-urls", $options))
{
// Encode url() to base64
$css = preg_replace_callback("/url\s*\((.*)\)/siU", "cssmin_encode_url", $css);
}
// Remove comments
$css = preg_replace("/\/\*[\d\D]*?\*\/|\t+/", " ", $css);
// Replace CR, LF and TAB to spaces
$css = str_replace(array("\n", "\r", "\t"), " ", $css);
// Replace multiple to single space
$css = preg_replace("/\s\s+/", " ", $css);
// Remove unneeded spaces
$css = preg_replace("/\s*({|}|\[|\]|=|~|\+|>|\||;|:|,)\s*/", "$1", $css);
if (in_array("remove-last-semicolon", $options))
{
// Removes the last semicolon of every style definition
$css = str_replace(";}", "}", $css);
}
$css = trim($css);
if (in_array("preserve-urls", $options))
{
// Decode url()
$css = preg_replace_callback("/url\s*\((.*)\)/siU", "cssmin_encode_url", $css);
}
return $css;
}
/**
* Return a array structure of a stylesheet definitions.
*
* <code>
* $css_structure = cssmin::toArray(file_get_contents("path/to/target/file.css"));
* </code>
*
* @param string $css Stylesheet definitions as string
* @param string $options Options for {@link cssmin::minify()}
* @return array Structure of the stylesheet definitions as array
*/
public static function toArray($css, $options = "")
{
$r = array();
$css = cssmin::minify($css, $options);
preg_match_all("/(.+){(.+:.+);}/U", $css, $items);
if (count($items[0]) > 0)
{
for ($i = 0; $i < $c = count($items[0]); $i++)
{
$keys = explode(",", $items[1][$i]);
$styles_tmp = explode(";", $items[2][$i]);
$styles = array();
foreach ($styles_tmp as $style)
{
$style_tmp = explode(":", $style);
$styles[$style_tmp[0]] = $style_tmp[1];
}
$r[] = array
(
"keys" => cssmin_array_clean($keys),
"styles" => cssmin_array_clean($styles)
);
}
}
return $r;
}
/**
* Return a array structure created by {@link cssmin::toArray()} to a string.
*
* <code>
* $css_string = cssmin::toString($css_structure);
* </code>
*
* @param array $css
* @return array
*/
public static function toString(array $array)
{
$r = "";
foreach ($array as $item)
{
$r .= implode(",", $item["keys"]) . "{";
foreach ($item["styles"] as $key => $value)
{
$r .= $key . ":" . $value . ";";
}
$r .= "}";
}
return $r;
}
}
/**
* Trims all elements of the array and removes empty elements.
*
* @param array $array
* @return array
*/
function cssmin_array_clean(array $array)
{
$r = array();
$c = count($v);
if (cssmin_array_is_assoc($array))
{
foreach ($array as $key => $value)
{
$r[$key] = trim($value);
}
}
else
{
foreach ($array as $value)
{
if (trim($value) != "")
{
$r[] = trim($value);
}
}
}
return $r;
}
/**
* Return if a value is a associative array.
*
* @param array $array
* @return bool
*/
function cssmin_array_is_assoc($array)
{
if (!is_array($array))
{
return false;
}
else
{
krsort($array, SORT_STRING);
return !is_numeric(key($array));
}
}
/**
* Encodes a url() expression.
*
* @param array $match
* @return string
*/
function cssmin_encode_url($match)
{
return "url(" . base64_encode(trim($match[1])) . ")";
}
/**
* Decodes a url() expression.
*
* @param array $match
* @return string
*/
function cssmin_decode_url($match)
{
return "url(" . base64_decode($match[1]) . ")";
}
?>

3675
lib/ExtraPacker/Lib/lessc.inc.php Executable file

File diff suppressed because it is too large Load Diff