mirror of
https://github.com/UnickSoft/graphonline.git
synced 2026-04-16 21:30:08 +00:00
first commit
This commit is contained in:
157
lib/ExtraPacker/Config.php
Executable file
157
lib/ExtraPacker/Config.php
Executable 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
692
lib/ExtraPacker/ExtraPacker.php
Executable 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;
|
||||
}
|
||||
};
|
||||
?>
|
||||
25
lib/ExtraPacker/Lib/CssPacker.php
Executable file
25
lib/ExtraPacker/Lib/CssPacker.php
Executable 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);
|
||||
}
|
||||
};
|
||||
?>
|
||||
258
lib/ExtraPacker/Lib/HtmlPacker.php
Executable file
258
lib/ExtraPacker/Lib/HtmlPacker.php
Executable 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
438
lib/ExtraPacker/Lib/JSMin.php
Executable 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 {}
|
||||
?>
|
||||
206
lib/ExtraPacker/Lib/cssmin-v1.0.1.b3.php
Executable file
206
lib/ExtraPacker/Lib/cssmin-v1.0.1.b3.php
Executable 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
3675
lib/ExtraPacker/Lib/lessc.inc.php
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user