Статьи

Как создать команду Laravel CSS-Minify

В этой статье вы узнаете, как использовать инструмент командной строки Artisan от Laravel и как создать настраиваемую команду. Обратите внимание, что вы должны быть знакомы с фреймворком Laravel, чтобы получить большую часть этой статьи.

Что мы строим

В этом уроке мы собираемся создать команду для минимизации наших ресурсов CSS, которая будет использоваться следующим образом:

cssmin 'output_path'   'file1' ... 'fileN'   -- comments -- concat 
  • output_path : (обязательный) путь для сохранения минимизированных файлов ( style.css -> style.min.css ).
  • file1 ... fileN : (обязательный) список файлов для минимизации.
  • --comments : (необязательно) добавьте эту опцию, чтобы оставлять комментарии.
  • --concat : (необязательно) объединить минимизированные файлы в один файл с именем all.min.css .

Что такое команда Laravel

Artisan — это название утилиты командной строки в Laravel. Он поставляется с набором предопределенных команд, которые вы можете перечислить с помощью списка php artisan list . Если вы хотите показать справку для определенной команды, вы можете использовать php artisan help command .

Создание команды Css Minifier

Чтобы создать команду ремесленника, вы можете использовать command:make command. Эта команда принимает один аргумент:

  • name : name класса для команды.

и три варианта:

  • --command : имя, которое нужно ввести для запуска команды.
  • --path : по умолчанию команды хранятся в папке app/commands , однако вы можете изменить это с помощью этой опции.
  • --namespace : вы можете использовать эту опцию для пространства имен вашего набора команд, например, в команде command:make , команда make находится в пространстве имен command .

Теперь, чтобы создать нашу команду, мы будем использовать php artisan command:make CssMinCommand --command=cssmin которая создаст файл CssMinCommand.php в нашем каталоге app/commands .

 use Illuminate\Console\Command; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; class CssminCommand extends Command{ protected $name = 'cssmin'; protected $description = 'Command description.'; public function __construct(){ parent::__construct(); } public function fire(){ // } protected function getArguments(){ return array( array('example', InputArgument::REQUIRED, 'An example argument.'), ); } protected function getOptions(){ return array( array('example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null), ); } } 

Наш класс CssMinCommand расширяет Illuminate\Console\Command и переопределяет два метода ( getArguments , getOptions ).

  • getArguments : эта функция возвращает массив аргументов, которые должны быть переданы команде (например: список файлов, которые мы передаем команде cssmin ).
  • getOptions : возвращает список параметров или переключателей, которые вы можете передать команде. (например, --comments ).

Примечание: параметры могут иметь или не иметь значения, --comments — это только флаг, который возвращает true если он передан команде, тогда как --ouptput='public/assets' вернет значение.

Когда ваша команда выполняется, вызывается метод fire , так что именно здесь мы должны поместить нашу командную логику.

Регистрация команды

если вы попытаетесь запустить нашу команду php artisan cssmin 'args' вы получите Command "cssmin" is not defined .

Чтобы зарегистрировать команду, вам нужно добавить ее в файл artisan.php :

 Artisan::add( new CssMinCommand ); //or through the container Artisan::add( App::make("CssMinCommand") ); 

Если вы не хотите помещать свои команды в файл artisan.php , вы можете создать отдельный файл и включить его, или, если вы создаете пакет, вы можете зарегистрировать их в своем поставщике услуг .

аргументы

В нашем методе getArguments мы определим наш output и files .
Чтобы определить аргумент, нам нужно передать массив значений:

 array (   'name' ,   'mode' ,   'description' ,   'defaultValue'   ) 
  • name : name ключа, которое будет использоваться при получении аргументов.
  • mode : может иметь один из трех вариантов:

    • InputArgument::REQUIRED : аргумент обязателен.
    • InputArgument::OPTIONAL : аргумент является необязательным.
    • InputArgument::IS_ARRAY : аргумент принимает несколько значений (например, file1...fileN ).

    Однако вы можете комбинировать их как InputArgument::IS_ARRAY | InputArgument::REQUIRED InputArgument::IS_ARRAY | InputArgument::REQUIRED (аргумент обязателен и должен быть массивом).

  • description : полезно при выводе команды help.
  • defaultValue : если аргумент не был предоставлен.

Итак, наш метод getArguments будет:

 protected function getArguments(){ return array( array( 'output', InputArgument::REQUIRED, 'Path to output directory' ), array( 'files', InputArgument::IS_ARRAY | InputArgument::OPTIONAL , "List of css files to minify" ), ); } 

Примечание: при использовании аргумента IS_ARRAY он должен быть последним в массиве возвращаемых аргументов. (Очевидно).

Параметры

Наша команда cssmin будет иметь только две опции. Чтобы определить опцию, мы передаем массив:

 array ( 'name' ,   'shortcut' ,   'mode' ,   'description' ,   'defaultValue' ) 
  • name : название вашего варианта (например, comments ).
  • shortcut : более короткая версия вашего варианта (например: --verbose и -v ).
  • mode : может быть одним из четырех параметров ( InputOption::VALUE_IS_ARRAY , InputOption::VALUE_OPTIONAL , InputOption::VALUE_REQUIRED , InputOption::VALUE_NONE ), первые три значения аналогичны аргументам.

    • VALUE_NONE : указывает, что опция является логическим флагом (например: --verbose ).
  • description : полезно при выводе команды help.

  • defaultValue : если значение параметра не было предоставлено.

Итак, наш метод getOptions будет:

 protected function getOptions(){ return array( array('comments', 'c', InputOption::VALUE_NONE, 'Don\'t strip comments' , null), array('concat', null, InputOption::VALUE_NONE, 'Concat the minified result to one file' , null), ); } 

Запуск команды

Когда вызывается наш метод fire нам нужно собрать наши аргументы и параметры. Мы можем сделать отдельную функцию, чтобы сделать это для нас:

 private function init(){ // retrun an array $this->files = $this->argument('files'); // return a string $this->output_path = $this->argument('output'); // return true if passed, otherwise false $this->comments = $this->option('comments'); // return true if passed, otherwise false $this->concat = $this->option('concat'); } 

Методы argument и option принимают ключ в качестве аргумента и возвращают соответствующее значение.

Чтобы наш пример был чистым и простым, мы будем использовать эту простую функцию с небольшой модификацией для процесса минимизации.

 private function minify( $css, $comments ){ // Normalize whitespace $css = preg_replace( '/\s+/', ' ', $css ); // Remove comment blocks, everything between /* and */, unless preserved with /*! ... */ if( !$comments ){ $css = preg_replace( '/\/\*[^\!](.*?)\*\//', '', $css ); }//if // Remove ; before } $css = preg_replace( '/;(?=\s*})/', '', $css ); // Remove space after , : ; { } */ > $css = preg_replace( '/(,|:|;|\{|}|\*\/|>) /', '$1', $css ); // Remove space before , ; { } ( ) > $css = preg_replace( '/ (,|;|\{|}|\(|\)|>)/', '$1', $css ); // Strips leading 0 on decimal values (converts 0.5px into .5px) $css = preg_replace( '/(:| )0\.([0-9]+)(%|em|ex|px|in|cm|mm|pt|pc)/i', '${1}.${2}${3}', $css ); // Strips units if value is 0 (converts 0px to 0) $css = preg_replace( '/(:| )(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)/i', '${1}0', $css ); // Converts all zeros value into short-hand $css = preg_replace( '/0 0 0 0/', '0', $css ); // Shortern 6-character hex color codes to 3-character where possible $css = preg_replace( '/#([a-f0-9])\\1([a-f0-9])\\2([a-f0-9])\\3/i', '#\1\2\3', $css ); return trim( $css ); }//minify 

Теперь для обработки наших аргументов (файлов) мы собираемся создать отдельный метод для выполнения этой работы.

 private function processFiles(){ // array of minified css $css_result = []; foreach ( $this->files as $file ) { //read file content $file_content = file_get_contents( $file ); //minify CSS and add it to the result array $css_result[] = $this->minify( $file_content, $this->comments ); }//foreach // if the concat flag is true if( $this->concat ){ // join the array of minified css $css_concat = implode( PHP_EOL, $css_result ); // save to file file_put_contents($this->output_path . '/all.min.css', $css_concat); }//if else{ foreach ($css_result as $key => $css) { //remove '.css' to add '.min.css' $filename = basename( $this->files[$key], '.css' ) . '.min.css'; // save to file file_put_contents($this->output_path . '/' . $filename, $css); }//for }//else }//processFiles 

Наконец, наш метод fire будет вызывать только два метода:

 public function fire(){ $this->init(); $this->processFiles(); } 

Совет: Вы также можете запустить внешнюю команду, используя метод call .

 $this->call('command:name', array('argument' => 'foo', '--option' => 'bar')); 

Чтобы проверить нашу команду, мы собираемся скопировать некоторые CSS-файлы в наш каталог public/css , а затем запустить команду.

 php artisan cssmin 'public/css' 'public/css/style.css' 'public/css/responsive.css' php artisan cssmin 'public/css' 'public/css/style.css' 'public/css/responsive.css' --comments --concat 

Первая команда создаст два файла ( style.min.css , style.min.css ) в каталоге public/css .

Поскольку мы использовали флаги --comments и --concat , мы получим файл all.min.css содержащий два файла с оставленными комментариями.

Наша команда не очень наглядна и не дает никаких сообщений или уведомлений!

Улучшение команды

Прежде чем мы продолжим, в финальном репозитории GitHub я создам новый тег для нашей команды, чтобы вы могли переключаться и тестировать каждый из них.

Чтобы сделать команду немного многословной, Laravel предоставляет нам несколько функций вывода:

 $this->line("This is a simple line message"); $this->info("This is an info"); $this->comment("This is a comment"); $this->question("This is a question"); $this->error("This is an error"); 

Это выведет:

Output

Помимо отображения сообщений вы можете запросить информацию у пользователя, например:

 $confirm = $this->confirm("'style.min.css' already exists, overwrite?", false); $filename = $this->ask("Save the file as?", 'all.min.css'); $choice = $this->choice( 'Please select a level of minication:', [ 'minify all', 'leave comments' ], 'default value' ); $password = $this->secret('Type your password to confirm:'); 
  • Метод confirm принимает два аргумента: сообщение с вопросом и значение по умолчанию, если пользователь вводит что-то отличное от y/n .

  • Метод ask будет запрашивать у пользователя ввод, а не только y/n , и, если он оставлен пустым, возвращается значение по умолчанию.

  • Метод choice предоставит пользователю нумерованный список для выбора, и если он останется пустым, будет возвращено значение по умолчанию.

  • secret метод подскажет пользователю вопрос и скроет ввод, но пользовательский ввод будет возвращен.

На самом деле, Laravel просто делает API-интерфейс консоли Symfony более простым и многословным, и есть еще много всего, что вам нужно.

Давайте сделаем нашу команду более многословной и будем держать пользователя в курсе выполненных задач.

 private function processFiles(){ $css_result = []; foreach ( $this->files as $file ) { $this->comment("'{$file}'"); $this->info("Loading file"); //read file content $file_content = file_get_contents( $file ); $this->info("minifying"); //minify CSS and add it to the result array $css_result[] = $this->minify( $file_content, $this->comments ); }//foreach if( $this->concat ){ $this->comment("Concatenating into one file"); $css_concat = implode( PHP_EOL, $css_result ); $this->info("Saving to '{$this->output_path}/all.min.css'"); file_put_contents($this->output_path . '/all.min.css', $css_concat); }//if else{ foreach ($css_result as $key => $css) { //remove '.css' to add '.min.css' $filename = basename( $this->files[$key], '.css' ) . '.min.css'; $this->comment("Saving '{$filename}'"); file_put_contents($this->output_path . '/' . $filename, $css); }//for }//else }//processFiles 

Наша функция теперь печатает несколько полезных сообщений, чтобы отслеживать, что происходит.

command log

Примечание. Это будет помечено как v2 нашей команды в репозитории GitHub.

При создании приложения мы привыкли выводить список доступных маршрутов (маршруты php artisan routes ).

routes

Symfony предоставляет функцию, которая позволяет легко распечатать такую ​​таблицу. Проверьте документацию для примера. Далее мы увидим, как мы можем использовать некоторые помощники Symfony Console.

Использование помощников Symfony Console

Чтобы проиллюстрировать использование некоторых помощников Symfony, мы будем использовать помощник прогресса, чтобы постоянно информировать пользователя о ходе выполнения задания.

В конце нашего метода init нам потребуется прогресс из HelperSet , затем запустите наш индикатор выполнения.

 $this->progressbar = $this->getHelperSet()->get('progress'); $this->progressbar->start($this->output, count($this->files) ); 

Метод start принимает два аргумента, $this->output является экземпляром ConsoleOuput из консоли Symfony. Второй аргумент — это максимальное количество шагов.

Каждый раз, когда мы обрабатываем файл в нашем методе processFiles мы продвигаем индикатор выполнения на один шаг, а когда работа завершается, мы заканчиваем индикатор выполнения и печатаем уведомление.

 private function processFiles(){ $css_result = []; foreach ( $this->files as $file ) { //read file content $file_content = file_get_contents( $file ); //minify CSS and add it to the result array $css_result[] = $this->minify( $file_content, $this->comments ); // sleep for one second to see the effect //sleep(1); $this->progressbar->advance(); }//foreach if( $this->concat ){ $css_concat = implode( PHP_EOL, $css_result ); file_put_contents($this->output_path . '/all.min.css', $css_concat); }//if else{ foreach ($css_result as $key => $css) { //remove '.css' to add '.min.css' $filename = basename( $this->files[$key], '.css' ) . '.min.css'; file_put_contents($this->output_path . '/' . $filename, $css); }//for }//else $this->progressbar->finish(); $this->info('Done'); }//processFiles 

Вы можете попробовать команду с несколькими файлами или раскомментировать строку функции sleep чтобы увидеть живой эффект.

progressbar

Примечание. Эта версия будет помечена как v3 в окончательном хранилище.

Вывод

В этой статье мы узнали, как создавать и расширять команды Laravel. В Laravel есть много встроенных команд, которые вы можете изучить, и вы также можете проверить наш окончательный репозиторий на GitHub, чтобы проверить конечный результат. Вопросов? Комментарии? Хотели бы вы увидеть больше учебных пособий по Artisan Command? Дайте нам знать!