Предпосылки Один из способов оптимизации скорости загрузки Web-страницы это сжатие (минимизация и оптимизация) кода JavaScript файлов. Если gzip сжатие файла реализуется средствами Web-сервера, то подготовить код, например удалить комментарии, должен сам разработчик. Предлагаемое решение На вашем сервере разработки вы пишете JS код, загружаете его на рабочий сервер, а сжатый файл создастся сам, и подменит собой загруженный. Для внедрения решения вам необходимо выбрать способ запуска процедуры авто-сжатия (скорее всего ещё добавить проверку сервера, на котором он запущен, чтобы предотвратить сжатие файлов на сервере разработки). Запуск может выполняться планировщиком cron или при каждом посещении любой страницы вашего проекта (данный способ требует определенных оптимизаций). Область применения Предполагается, что при активном использовании JavaScript в проекте, разрабатываемом на отдельном сервере разработки, одним из способов автоматического сжатия ваших JS файлов может стать php-скрипт, работающий на основном сайте и после получения обновленных JS файлов, сжимающий их. Описание jasmin.php содержит класс, при создании экземпляра которого происходит следующее: - в указанной директории по порядку открываются все js файлы - с конца файла считываются 4 символа, если они == "//c4" файл пропускается - остальные файлы отправляются для сжатия сюда http://closure-compiler.appspot.com/ - исходный файл подменяется сжатым - если файл обрабатывался слишком долго, цикл прерывается досрочно * Примечание Запуск вложенного теста на денвере закончится сообщением "Cannot retrieve content from URL: http : // ....... / myScript.js" т.к. по-умолчанию используется отправка URL сжимаемого файла, для отправки содержимого вместо URL в index.php замените Код (Text): new CJasmin(); на Код (Text): new CJasmin('',FALSE); Код Архив Содержимое архива: index.php Тест-файл с примером старта авто сжатия Подключает jasmin.php, создает экземпляр класса CJasmin myScript.js Пример JS файла для сжатия Не несет полезного кода, этот файл будет сжат jasmin.php Код класса, комментарии jasmin-silent.php Тот же код, но вырезаны все процедуры, отвечающие за вывод сообщений Содержимое файла jasmin.php С раскраской http://pastebin.com/embed_iframe.php?i=Z0rcuVkz Код (Text): <?php /* jasmin.php Скрипт для автосжатия js файлов, предоставляет класс CJasmin. При создании экземпляра просматривает указанную директорию в поисках JavaScript файлов (по расширению) и отправляет их на сжатие Google Closure Compiler. Исходный файл переименовывает. Foreigner Design Studios, 25.05.2012 */ // Класс CJasmin, запуск сжатия - созданием экземпляра объекта // Параметры конструктора ($dir = '', $use_url = TRUE, $verbose = TRUE), где // $dir - рабочая директория, по-умолчанию getcwd() // $use_url - гуглу для сжатия отправлять URL *.js файлов, иначе - содержимое файлов // $verbose - выводить подробный отчет о работе // ------ // * Требуются права на запись в директорию // ** Простите за мой индийский, давно не кодил ) class CJasmin { // Выводится в verbose режиме для контроля версии const version = 1; // Лимит времени выполнения в секундах (прерывает цикл обработки нескольких файлов) const time_limit = 1; // Имя файла-маяка для блокировки запуска нескольких обработок в одной директории const lock_filename = "jasmin.lock"; // Постфикс имени файла для переименования оригинала const original_postfix = ".last"; // Параметр verbose private $v; // Параметр use_url private $u; // Старая рабочая директория private $last_wd; // microtime начала работы private $t; // handle lock-файла private $lock_file; // Старый error_reporting private $last_e; // Выполняет начальные проверки, создаёт файл блокировки повторного запуска // Запускает процесс обработки function __construct($dir = '', $use_url = TRUE, $verbose = TRUE){ $this->t = microtime(TRUE); $this->u = $use_url; $this->v = $verbose; $this->b('JASMIN ' . self::version . "\nЗапуск автоматического сжатия *.js\n"); // Для подавления предупреждений fopen и fclose // когда файл существует $this->last_e = error_reporting(E_ERROR); $this->last_wd = getcwd(); if(!chdir($dir?$dir:'.')){ $this->b("Перейти в выбранную директорию не удалось\n"); return; } $this->b('Рабочая директория: ' . getcwd() . "\n"); // Открываем файл, блокирующий повторный запуск // Возможно наткнёмся на уже закрытый файл, но это не страшно, его удалит деструктор // и скрипт сработает при повторном запуске if( ($this->lock_file=fopen(self::lock_filename, 'x')) === FALSE ) $this->b("Ошибка создания jasmin.lock\nВозможно скрипт уже запущен или\nзапрещена запись в указанную директорию\n"); else $this->a(); } // Закрывает и удаляет файл блокировки, возвращает все на места function __destruct(){ fclose($this->lock_file); unlink(self::lock_filename); chdir($this->last_wd); error_reporting($this->last_e); $this->b("Задержали вас на ".(microtime(true)-$this->t)." сек.\nBYE\n"); } // Выполняет поиск, чтение, сжатие и запись файлов private function a(){ if($use_url)$this->b("Включен параметр отправки URL\n"); $this->b('Получение списка файлов... '); if( ($fs=glob('*.js')) === FALSE ){ $this->b("Ошибка\n"); return; } $this->b("Готово\n"); foreach($fs as $a){ // Предел времени выполнения проверяем здесь, ибо далее применяются continue if( microtime(true)-$this->t > self::time_limit ){ $this->b("Достигнут лимит времени выполнения\n"); break; } $this->b("[ -> Обрабатываем \"$a\"]\nЧтение хвоста файла... "); if( ($f=fopen($a, "r")) === FALSE ){ $this->b("Ошибка fopen\n"); continue; } fseek($f,-4,SEEK_END); $sz=ftell($f)+4; $buf=fgets($f); // Если используем ссылку на js, уже можно закрывать if ( $this->u ) fclose($f); $this->b("Прочитано: '".str_replace("\n",'\n',addslashes($buf))."'\n"); if ( $buf == "//c4" ){ if ( !($this->u) ) fclose($f); $this->b("Хвост соотв. сжатому файлу, пропускаем\n"); continue; } $this->b("Хвост не соотв. сжатому файлу, сжимаем... "); // Сжатие (API Google Closure Compiler) $o['http']['method'] = 'POST'; $o['http']['header'] = 'Content-type: application/x-www-form-urlencoded'; $o['http']['content']= "output_format=json&output_info=compiled_code&output_info=warnings&output_info=errors&compilation_level=SIMPLE_OPTIMIZATIONS&warning_level=default"; if( $this->u ) { // Вроде не должно в $a быть полного пути, поэтому смелее... $o['http']['content'].="&code_url=" . str_replace(strtolower($_SERVER['DOCUMENT_ROOT']),"http://".$_SERVER['HTTP_HOST'],str_replace('\\','/',strtolower(getcwd())) . "/$a"); // P.S. Всё равно страшно... + могут быть проблемы с русскими буквами } else { rewind($f); $o['http']['content'].="&js_code=".urlencode(fread($f,$sz)); fclose($f); } if ( ($o=file_get_contents('http://closure-compiler.appspot.com/compile', false, stream_context_create($o))) === FALSE){ $this->b("Ошибка file_get_contents\n"); continue; } $o=json_decode(str_replace("\\\\u","\\u",$o)); if($o==NULL){ $this->b("Ошибка - NULL ответ\n"); continue; } $this->b("Готово\n"); if ( $o->compiledCode == "" ){ unset($o->compiledCode); print_r($o); $this->b("\nРезультат пуст, пропускаем этот файл\n"); continue; } $this->b("Записываем временный файл jasmin.tmp... "); if( file_put_contents("jasmin.tmp",$o->compiledCode."//c4") === FALSE ){ $this->b("Ошибка\n"); continue; } $this->b("Готово\nПереименовываем исходный файл (+'.last')... "); if(!rename($a,$a.".last")){ $this->b("Ошибка\n"); continue; } $this->b("Готово\nПереименовываем временный файл... "); if(!rename("jasmin.tmp",$a)){ $this->b("Ошибка\n"); continue; } $this->b("Готово\nОбработка файла успешно завершена.\n"); } } // Вывод сообщений в соотв. с парам verbose private function b($a){if($this->v)echo $a;} } // End of class CJasmin // Раскоментируйте эту строку если хотите запустить сжатие из этого файла. // Например, когда скрипт выполняется кроном или подключается // new CJasmin(); // Для быстрой реакции на обновление файлов надо запускать скрипт почащще // НО // Большую часть времени скрипт будет работать вхолостую читая хвосты js файлов // решение обдумывается... // Пример псевдооптимизации Радхкараджа Сатри Мунди // Пропускаем некоторые вызовы // if(mt_rand(0,1)==0) new CJasmin(); // Примечание: не рекомендуется хранить экземпляр класса, т.к. изначально // планировалось, что деструктор сработает сразу по завершении сжатия ?> Заключение Целью запиливания данного кода в интернеты было получение грамотной критики, улучшения качества кода и повышения его применябельности.
в чем отличие от йахушной сжималки? Добавлено спустя 31 секунду: а то что мои коды куда-то отравляются, меня вобще безумно смущает... зачем? Добавлено спустя 54 секунды: это не есть гуд
Прежде всего в том, что автор не в курсе о её существовании, есть ссылка? или самому гуглить? а не! Надо "йа%уйнуть", наверное, она же от yahoo... Когда "это" проектировалось меня тоже смутило такое решение, но я напряг силу воли и решился =) Во-первых, парсить JS прям средствами PHP в теле скрипта может статься долго, да и стыдно сказать, я не осилю сейчас такой парсер писать, особенно с учетом того, что это уже решено средствами приложения от гугла. Ставить и налаживать это приложение - тоже вариант, но это совсем другая песня с другими решениями и скриптави автоматизации, вопрос был именно в минимуме настроечных шагов, дабы скрипт начинал работу почти сразу после загрузки на произвольный хостинг, особенно не на свой VPS. А тут, понятно, только внешняя WEB служба. Если отказываться от неё, то реально только вытрехать пробелы и комменты, без других оптимизаций. Что не так? Это есть фича, и ничего беды не предвещает. Ну, во-первых, бэкап - первый закон сисадмина Копия то создается, а вот сама подмена позволит просто ссылаться на этот файл. Один и тот же код для подключения на локальном сервере разработки и на продакшн сервере - это, по моему мнению, удобно. только на локальном будет работать толстый js, а на продакшене может максимум пару раз (десятков если не успеет ) загрузится не сжатый. Хотя это конечно идея, создавать js.min.js... дальше придумать не могу, надо помощь вызвать. Баагбииир!..
имеет смысл класть рядом min-ифайнутый файл, проверяя дату основного. со всеми вытекающими. http://developer.yahoo.com/yui/compressor/