Если сделать так:
то вместо чего-то типа такого:
Cекрет фокуса
Комментарии
Смысл работы хелпера заключается в обходе всех добавленных скриптов, вычислении хеша с учетом названий файлов, даты модифицирования скриптов (mtime), текстов встроенных скриптов, сборки всего этого в один файл и сжатии (опционально) напоследок с помощью JSMin
Если какой-либо встроенный скрипт кешировать не нужно, т.к., например, он часто меняется, то нужно добавить //@non-cache:
Пропущенные встроенные скрипты будут подключаться после собранного файла, поэтому нужно убедиться, чтобы от них не было зависимостей.
Не забудьте правильно разместить плагин и указать потом Zend_View addHelperPath
Если требуется сжатие скриптов, нужно положить в доступное по __autoload (либо добавить require) место класс JSMin
Перед непосредственным использованием хелпера нужно где-то указать, в какую папку скрипт должен записывать собранный файл:
После модификации JS-скриптов никаких дополнительных телодвижений производить не нужно — хеш изменится и хелпер сам сделает новую сборку файла (и он уже будет с другим названием, поэтому проблемы с клиентским кешированием не будет). Естественно сборка производится однократно при первом обращении.
Аналогичное решение MagicHeadLink для сборки CSS-файлов, но только использующее Minify можно скачать ниже (лень отдельной статьей оформлять — утомился раскрашивать исходники :) ).
MagicHeadScript + JSMin:
mhs.zip (4 Кб)
MagicHeadLink + Minify (урезанный):
mhl.zip (8 Кб)
<?php $this->headScript()->appendFile('/js/my1.js');?>
<?php $this->headScript()->appendFile('/js/my2.js');?>
<?php $this->headScript()->captureStart() ?>
var action = '<?php echo $this->baseUrl ?>';
<?php $this->headScript()->captureEnd() ?>
<?php echo $this->headScript(); ?>
<?php echo $this->magicHeadScript(); ?>
то вместо чего-то типа такого:
получим на выходе:<script type="text/javascript" src="/js/my1.js"></script>
<script type="text/javascript" src="/js/my2.js"></script>
<script type="text/javascript">
var action = '/123';
</script>
<script type="text/javascript" src="/cache/js/1b1004a203..._compressed.js"></script>
Cекрет фокуса
/**
* @license Public domain
*/
class My_View_Helper_MagicHeadScript extends Zend_View_Helper_HeadScript
{
private static $cacheDir;
private static $combine = 1;
private static $compress = 1;
private static $symlinks = array();
private $_cache = array();
static public function setConfig($cacheDir, $combine = 1, $compress = 1, $symlinks = array())
{
self::$cacheDir = rtrim($dir, '/') . '/';
self::$symlinks = $symlinks;
self::$combine = $combine;
self::$compress = $compress;
}
public function magicHeadScript()
{
if (self::$combine) {
return $this->toString();
} else {
return $this->view->headScript();
}
}
public function itemToString($item, $indent, $escapeStart, $escapeEnd)
{
$attrString = '';
if (!empty($item->attributes)) {
foreach ($item->attributes as $key => $value) {
if (!$this->arbitraryAttributesAllowed()
&& !in_array($key, $this->_optionalAttributes))
{
continue;
}
if ('defer' == $key) {
$value = 'defer';
}
$attrString .= sprintf(' %s="%s"', $key, ($this->_autoEscape) ? $this->_escape($value) : $value);
}
}
$type = ($this->_autoEscape) ? $this->_escape($item->type) : $item->type;
$html = $indent . '<script type="' . $type . '"' . $attrString . '>';
if (!empty($item->source)) {
$html .= PHP_EOL . $indent . ' ' . $escapeStart . PHP_EOL . $item->source . $indent . ' ' . $escapeEnd . PHP_EOL . $indent;
}
$html .= '</script>';
if (isset($item->attributes['conditional'])
&& !empty($item->attributes['conditional'])
&& is_string($item->attributes['conditional']))
{
$html = '<!--[if ' . $item->attributes['conditional'] . ']> ' . $html . '<![endif]-->';
}
return $html;
}
public function searchJsFile($src)
{
$path = $_SERVER['DOCUMENT_ROOT'] . $src;
if (is_readable($path)) {
return $path;
}
foreach (self::$symlinks as $virtualPath => $realPath) {
$path = str_replace($virtualPath, $realPath, "/$src");
if (is_readable($path)) {
return $path;
}
}
return false;
}
public function isCachable($item)
{
if (isset($item->attributes['conditional'])
&& !empty($item->attributes['conditional'])
&& is_string($item->attributes['conditional']))
{
return false;
}
if (!empty($item->source) && false===strpos($item->source, '//@non-cache')) {
return true;
}
if (!isset($item->attributes['src']) || !$this->searchJsFile($item->attributes['src'])) {
return false;
}
return true;
}
public function cache($item)
{
if (!empty($item->source)) {
$this->_cache[] = $item->source;
} else {
$filePath = $this->searchJsFile($item->attributes['src']);
$this->_cache[] = array(
'filepath' => $filePath,
'mtime' => filemtime($filePath)
);
}
}
public function toString($indent = null)
{
$headScript = $this->view->headScript();
$indent = (null !== $indent)
? $headScript->getWhitespace($indent)
: $headScript->getIndent();
if ($this->view) {
$useCdata = $this->view->doctype()->isXhtml() ? true : false;
} else {
$useCdata = $headScript->useCdata ? true : false;
}
$escapeStart = ($useCdata) ? '//<![CDATA[' : '//<!--';
$escapeEnd = ($useCdata) ? '//]]>' : '//-->';
$items = array();
$headScript->getContainer()->ksort();
foreach ($headScript as $item) {
if (!$headScript->_isValid($item)) {
continue;
}
if (!$this->isCachable($item)) {
$items[] = $this->itemToString($item, $indent, $escapeStart, $escapeEnd);
} else {
$this->cache($item);
}
}
array_unshift($items, $this->itemToString($this->getCompiledItem(), $indent, $escapeStart, $escapeEnd));
$return = implode($headScript->getSeparator(), $items);
return $return;
}
private function getCompiledItem()
{
$filename = md5(serialize($this->_cache));
$path = self::$cacheDir . $filename . (self::$compress? '_compressed' : '') . '.js';
if (!file_exists($path)) {
//...debug("Combine javascripts to $path...");
mkdir(dirname($path), 0777, true);
$jsContent = '';
foreach ($this->_cache as $js) {
if (is_array($js)) {
$jsContent .= file_get_contents($js['filepath']) . "\n\n";
//...debug($js['filepath'] . ' ... OK');
} else {
$jsContent .= $js . "\n\n";
//...debug('Inline JavaScript ... OK');
}
}
if ($compress) {
$jsContent = JSMin::minify($jsContent);
}
file_put_contents($path, $jsContent);
}
$url = str_replace($_SERVER['DOCUMENT_ROOT'], '', $path);
$item = $this->createData('text/javascript', array('src'=>$url));
return $item;
}
}
* This source code was highlighted with Source Code Highlighter.
Комментарии
Смысл работы хелпера заключается в обходе всех добавленных скриптов, вычислении хеша с учетом названий файлов, даты модифицирования скриптов (mtime), текстов встроенных скриптов, сборки всего этого в один файл и сжатии (опционально) напоследок с помощью JSMin
Если какой-либо встроенный скрипт кешировать не нужно, т.к., например, он часто меняется, то нужно добавить //@non-cache:
<?php $this->headScript()->captureStart() ?>
//@non-cache
var ip = <?php echo $ip ?>
<?php $this->headScript()->captureEnd() ?>
Пропущенные встроенные скрипты будут подключаться после собранного файла, поэтому нужно убедиться, чтобы от них не было зависимостей.
Не забудьте правильно разместить плагин и указать потом Zend_View addHelperPath
Если требуется сжатие скриптов, нужно положить в доступное по __autoload (либо добавить require) место класс JSMin
Перед непосредственным использованием хелпера нужно где-то указать, в какую папку скрипт должен записывать собранный файл:
My_View_Helper_MagicHeadScript::setConfig('/path/to/cache');
После модификации JS-скриптов никаких дополнительных телодвижений производить не нужно — хеш изменится и хелпер сам сделает новую сборку файла (и он уже будет с другим названием, поэтому проблемы с клиентским кешированием не будет). Естественно сборка производится однократно при первом обращении.
Аналогичное решение MagicHeadLink для сборки CSS-файлов, но только использующее Minify можно скачать ниже (лень отдельной статьей оформлять — утомился раскрашивать исходники :) ).
Скачать
MagicHeadScript + JSMin:
mhs.zip (4 Кб)
MagicHeadLink + Minify (урезанный):
mhl.zip (8 Кб)