За последние 24 часа нас посетили 18040 программистов и 1652 робота. Сейчас ищут 1506 программистов ...

Автоматическое сжатие JS файлов

Тема в разделе "Решения, алгоритмы", создана пользователем Чужой, 25 май 2012.

  1. Чужой

    Чужой Активный пользователь

    С нами с:
    25 май 2012
    Сообщения:
    5
    Симпатии:
    0
    Предпосылки
    Один из способов оптимизации скорости загрузки 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):
    1. new CJasmin();
    на
    Код (Text):
    1. 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):
    1.  
    2. <?php
    3. /*
    4.  jasmin.php
    5.  
    6.  Скрипт для автосжатия js файлов, предоставляет класс CJasmin.
    7.  При создании экземпляра просматривает указанную директорию
    8.  в поисках JavaScript файлов (по расширению) и отправляет их
    9.  на сжатие Google Closure Compiler. Исходный файл переименовывает.
    10.  
    11.  Foreigner Design Studios, 25.05.2012
    12. */
    13.  
    14.  
    15.  
    16. // Класс CJasmin, запуск сжатия - созданием экземпляра объекта
    17. // Параметры конструктора ($dir = '', $use_url = TRUE, $verbose = TRUE), где
    18. // $dir - рабочая директория, по-умолчанию getcwd()
    19. // $use_url - гуглу для сжатия отправлять URL *.js файлов, иначе - содержимое файлов
    20. // $verbose - выводить подробный отчет о работе
    21. // ------
    22. // * Требуются права на запись в директорию
    23. // ** Простите за мой индийский, давно не кодил )
    24.  
    25. class CJasmin {
    26.  
    27.     // Выводится в verbose режиме для контроля версии
    28.     const version = 1;
    29.    
    30.     // Лимит времени выполнения в секундах (прерывает цикл обработки нескольких файлов)
    31.     const time_limit = 1;
    32.    
    33.     // Имя файла-маяка для блокировки запуска нескольких обработок в одной директории
    34.     const lock_filename = "jasmin.lock";
    35.    
    36.     // Постфикс имени файла для переименования оригинала
    37.     const original_postfix = ".last";
    38.    
    39.     // Параметр verbose
    40.     private $v;
    41.    
    42.     // Параметр use_url
    43.     private $u;
    44.    
    45.     // Старая рабочая директория
    46.     private $last_wd;
    47.    
    48.     // microtime начала работы
    49.     private $t;
    50.    
    51.     // handle lock-файла
    52.     private $lock_file;
    53.    
    54.     // Старый error_reporting
    55.     private $last_e;
    56.    
    57.     // Выполняет начальные проверки, создаёт файл блокировки повторного запуска
    58.     // Запускает процесс обработки
    59.     function __construct($dir = '', $use_url = TRUE, $verbose = TRUE){
    60.         $this->t = microtime(TRUE);
    61.         $this->u = $use_url;
    62.         $this->v = $verbose;
    63.         $this->b('JASMIN ' . self::version . "\nЗапуск автоматического сжатия *.js\n");
    64.        
    65.         // Для подавления предупреждений fopen и fclose
    66.         // когда файл существует
    67.         $this->last_e = error_reporting(E_ERROR);
    68.        
    69.         $this->last_wd = getcwd();
    70.         if(!chdir($dir?$dir:'.')){
    71.             $this->b("Перейти в выбранную директорию не удалось\n");
    72.             return;
    73.         }
    74.         $this->b('Рабочая директория: ' . getcwd() . "\n");
    75.        
    76.         // Открываем файл, блокирующий повторный запуск
    77.         // Возможно наткнёмся на уже закрытый файл, но это не страшно, его удалит деструктор
    78.         // и скрипт сработает при повторном запуске
    79.         if( ($this->lock_file=fopen(self::lock_filename, 'x')) === FALSE )
    80.              $this->b("Ошибка создания jasmin.lock\nВозможно скрипт уже запущен или\nзапрещена запись в указанную директорию\n");
    81.         else $this->a();
    82.     }
    83.    
    84.     // Закрывает и удаляет файл блокировки, возвращает все на места
    85.     function __destruct(){
    86.         fclose($this->lock_file);
    87.         unlink(self::lock_filename);
    88.         chdir($this->last_wd);
    89.         error_reporting($this->last_e);
    90.         $this->b("Задержали вас на ".(microtime(true)-$this->t)." сек.\nBYE\n");
    91.     }
    92.    
    93.     // Выполняет поиск, чтение, сжатие и запись файлов
    94.     private function a(){
    95.         if($use_url)$this->b("Включен параметр отправки URL\n");
    96.         $this->b('Получение списка файлов... ');
    97.         if( ($fs=glob('*.js')) === FALSE ){
    98.             $this->b("Ошибка\n");
    99.             return;
    100.         }
    101.         $this->b("Готово\n");
    102.         foreach($fs as $a){
    103.  
    104.             // Предел времени выполнения проверяем здесь, ибо далее применяются continue
    105.             if( microtime(true)-$this->t > self::time_limit ){
    106.                 $this->b("Достигнут лимит времени выполнения\n");
    107.                 break;
    108.             }
    109.  
    110.             $this->b("[ -> Обрабатываем \"$a\"]\nЧтение хвоста файла... ");
    111.             if( ($f=fopen($a, "r")) === FALSE ){
    112.                 $this->b("Ошибка fopen\n");
    113.                 continue;
    114.             }
    115.             fseek($f,-4,SEEK_END);
    116.             $sz=ftell($f)+4;
    117.             $buf=fgets($f);
    118.             // Если используем ссылку на js, уже можно закрывать
    119.             if ( $this->u ) fclose($f);
    120.  
    121.             $this->b("Прочитано: '".str_replace("\n",'\n',addslashes($buf))."'\n");
    122.             if ( $buf == "//c4" ){
    123.                 if ( !($this->u) ) fclose($f);
    124.                 $this->b("Хвост соотв. сжатому файлу, пропускаем\n");
    125.                 continue;
    126.             }
    127.             $this->b("Хвост не соотв. сжатому файлу, сжимаем... ");
    128.  
    129.             // Сжатие (API Google Closure Compiler)
    130.             $o['http']['method'] = 'POST';
    131.             $o['http']['header'] = 'Content-type: application/x-www-form-urlencoded';
    132.             $o['http']['content']= "output_format=json&output_info=compiled_code&output_info=warnings&output_info=errors&compilation_level=SIMPLE_OPTIMIZATIONS&warning_level=default";
    133.             if( $this->u ) {
    134.                 // Вроде не должно в $a быть полного пути, поэтому смелее...
    135.                 $o['http']['content'].="&code_url=" . str_replace(strtolower($_SERVER['DOCUMENT_ROOT']),"http://".$_SERVER['HTTP_HOST'],str_replace('\\','/',strtolower(getcwd())) . "/$a");
    136.                 // P.S. Всё равно страшно... + могут быть проблемы с русскими буквами
    137.             } else {
    138.                 rewind($f);
    139.                 $o['http']['content'].="&js_code=".urlencode(fread($f,$sz));
    140.                 fclose($f);
    141.             }
    142.  
    143.             if ( ($o=file_get_contents('http://closure-compiler.appspot.com/compile', false, stream_context_create($o))) === FALSE){
    144.                 $this->b("Ошибка file_get_contents\n");
    145.                 continue;
    146.             }
    147.  
    148.             $o=json_decode(str_replace("\\\\u","\\u",$o));
    149.             if($o==NULL){
    150.                 $this->b("Ошибка - NULL ответ\n");
    151.                 continue;
    152.             }
    153.             $this->b("Готово\n");
    154.  
    155.             if ( $o->compiledCode == "" ){
    156.                 unset($o->compiledCode);
    157.                 print_r($o);
    158.                 $this->b("\nРезультат пуст, пропускаем этот файл\n");
    159.                 continue;
    160.             }
    161.  
    162.             $this->b("Записываем временный файл jasmin.tmp... ");
    163.             if( file_put_contents("jasmin.tmp",$o->compiledCode."//c4") === FALSE ){
    164.                 $this->b("Ошибка\n");
    165.                 continue;
    166.             }
    167.             $this->b("Готово\nПереименовываем исходный файл (+'.last')... ");
    168.             if(!rename($a,$a.".last")){
    169.                 $this->b("Ошибка\n");
    170.                 continue;
    171.             }
    172.             $this->b("Готово\nПереименовываем временный файл... ");
    173.             if(!rename("jasmin.tmp",$a)){
    174.                 $this->b("Ошибка\n");
    175.                 continue;
    176.             }
    177.             $this->b("Готово\nОбработка файла успешно завершена.\n");
    178.         }
    179.     }
    180.  
    181.     // Вывод сообщений в соотв. с парам verbose
    182.     private function b($a){if($this->v)echo $a;}
    183.    
    184. } // End of class CJasmin
    185.  
    186. // Раскоментируйте эту строку если хотите запустить сжатие из этого файла.
    187. // Например, когда скрипт выполняется кроном или подключается
    188. // new CJasmin();
    189.  
    190. // Для быстрой реакции на обновление файлов надо запускать скрипт почащще
    191. // НО
    192. // Большую часть времени скрипт будет работать вхолостую читая хвосты js файлов
    193. // решение обдумывается...
    194.  
    195. // Пример псевдооптимизации Радхкараджа Сатри Мунди
    196. // Пропускаем некоторые вызовы
    197. // if(mt_rand(0,1)==0) new CJasmin();
    198.  
    199. // Примечание: не рекомендуется хранить экземпляр класса, т.к. изначально
    200. // планировалось, что деструктор сработает сразу по завершении сжатия
    201.  
    202. ?>


    Заключение
    Целью запиливания данного кода в интернеты было получение грамотной критики, улучшения качества кода и повышения его применябельности.
     
  2. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    в чем отличие от йахушной сжималки?

    Добавлено спустя 31 секунду:
    а то что мои коды куда-то отравляются, меня вобще безумно смущает... зачем?

    Добавлено спустя 54 секунды:
    это не есть гуд
     
  3. Чужой

    Чужой Активный пользователь

    С нами с:
    25 май 2012
    Сообщения:
    5
    Симпатии:
    0
    Прежде всего в том, что автор не в курсе о её существовании, есть ссылка? или самому гуглить? а не! Надо "йа%уйнуть", наверное, она же от yahoo...

    Когда "это" проектировалось меня тоже смутило такое решение, но я напряг силу воли и решился =)
    Во-первых, парсить JS прям средствами PHP в теле скрипта может статься долго, да и стыдно сказать, я не осилю сейчас такой парсер писать, особенно с учетом того, что это уже решено средствами приложения от гугла. Ставить и налаживать это приложение - тоже вариант, но это совсем другая песня с другими решениями и скриптави автоматизации, вопрос был именно в минимуме настроечных шагов, дабы скрипт начинал работу почти сразу после загрузки на произвольный хостинг, особенно не на свой VPS. А тут, понятно, только внешняя WEB служба. Если отказываться от неё, то реально только вытрехать пробелы и комменты, без других оптимизаций.

    Что не так? Это есть фича, и ничего беды не предвещает.
    Ну, во-первых, бэкап - первый закон сисадмина ;)
    Копия то создается, а вот сама подмена позволит просто ссылаться на этот файл. Один и тот же код для подключения на локальном сервере разработки и на продакшн сервере - это, по моему мнению, удобно. только на локальном будет работать толстый js, а на продакшене может максимум пару раз (десятков если не успеет ;) ) загрузится не сжатый. Хотя это конечно идея, создавать js.min.js... дальше придумать не могу, надо помощь вызвать. Баагбииир!..
     
  4. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    имеет смысл класть рядом min-ифайнутый файл, проверяя дату основного. со всеми вытекающими.

    http://developer.yahoo.com/yui/compressor/
     
  5. d1gi

    d1gi Активный пользователь

    С нами с:
    24 май 2009
    Сообщения:
    326
    Симпатии:
    0
  6. Чужой

    Чужой Активный пользователь

    С нами с:
    25 май 2012
    Сообщения:
    5
    Симпатии:
    0
    ого, целый фрэймворк, толсто ) спасибо за ссылки, интересно было покачать )