Консольные команды на PHP

от автора

У многих, равно как и у меня, периодически возникает потребность в реализации каких-то не больших задач. Например распарсить сайт/API и сохранить данные в xml/json/csv, произвести какие-либо расчеты/пересчеты, перегнать данные из одного формата в другой, собрать статистику и т.д. и т.п. Замечу, что речь о задачах не связанных с текущими проектами.

Собирать тяжелый фреймворк ради удобных фич, лень, а реализовывать в рамках кода текущих проектов как-то не эстетично. Поэтому для экономии своего времени приходится создавать скрипт, копипастить в него куски кода из предыдущих наработок, подключать разнообразные библиотеки и запускать скрипт из консоли. При этом часто требуется некоторая интерактивность работы скрипта: обработка опций/аргументов, а то и диалоговое взаимодействие. Здесь главное чтобы не было настроения, которое хорошо описывается выражением «Аппетит приходит во время еды», тогда вообще не понятно к чему приведет работа над простой задачкой =)

В такие моменты я вспоминал удобную симфоническую консоль, к которой успел привыкнуть работая с проектами на
Symfony 2. Не в обиду другим консолям (zend, yii, django, ror etc), все хороши, просто так сложилось.

Когда в очередной раз потребовалось что-то распарсить, я опять вспомнил про консоль Symfony (Console Component) и тот факт, что это независимый компонент все больше подтолкнул меня к мысли использовать ее возможности.

За пару часов получилась простая тулза, в основе которой:

и менеджер зависимостей Composer, который нам поможет все это быстро собрать, добавлять новые либы, а также возьмет на себя автозагрузку классов.

Предположим, что нам очень понадобилось собрать список последних новостей, «Интернет» тематики. И в качестве источника нас вполне устраивает RSS сервиса Яндекс.Новости.

С помощью Сomposer-а создаем новый проект:

$ composer create-project suncat/console-commands ./cmd 

У меня Composer установлен глобально, поэтому он всегда доступен. Если вы им еще не пользуетесь, то для проверки примера его необходимо установить.

После скачивания приложения и всех зависимостей переходим в созданную директорию:

$ cd cmd     # для примера, при создании проекта задайте имя директории на свое усмотрение 

Структура следующая:

app/    console   # консоль src/         # автозагрузка psr-0    Command/  # классы ваших команд vendor/      # сторонние библиотеки 

Проверяем состояние:

$ app/console list 

Если видим справочную информацию и список доступных команд значит все ок. Выглядит это так:

Теперь создадим шаблон класса команды, которую мы планируем использовать для реализации задачи:

$ app/console generate 

В появившемся диалоге указываем название будущего класса:

Please enter the name of the command class: NewsInternetCommand 

В ответ получим уведомление:

Generated new command class to "./cmd/src/Command/NewsInternetCommand.php" 

Собственно все, команда готова, она появилась в списке доступных команд:

Но пока она не делает того что нужно (здесь можно открыть созданный класс в IDE или любимом редакторе и написать код команды).

Так как для нашего примера необходимо получать внешний контент и нам нравится ООП, поставим еще одну библиотеку:

$ composer require kriswallsmith/buzz 0.9 

Buzz — легкий HTTP клиент на PHP5.3. Будем использовать его для выполнения запросов к сервису новостей.

Создадим отдельный класс — YandexRSSNewsParser, который будет предоставлять классу команде подготовленный контент:

// ./src/Parser/YandexRSSNewsParser.php  namespace Parser;  use Buzz\Client\FileGetContents; use Buzz\Message\Request; use Buzz\Message\Response;  use DOMDocument; use DOMXPath;  class YandexRSSNewsParser {     private $method;     private $host;      /**      * Construct      */     public function __construct()     {         $this->method = 'GET';         $this->host = 'http://news.yandex.ru';     }      /**      * Get news      *      * @param $resource      *      * @return mixed      */     public function getNews($resource)     {         // content         $xml = $this->getData($resource);          if (false === $xml) {             return array();         }          $doc = new DOMDocument();         @$doc->loadXML($xml);         $xpath = new DOMXpath($doc);          // items         $items = $xpath->query('.//item');          $news = array();         foreach ($items as $item) {             $news[] = array(                 'datetime' => $xpath->evaluate("./pubDate", $item)->item(0)->nodeValue,                 'title' => $xpath->evaluate("./title", $item)->item(0)->nodeValue             );         }          return $news;     }      /**      * Get data      *      * @return mixed      */     protected function getData($resource)     {         $request = new Request($this->method, $resource, $this->host);         $response = new Response();          $client = new FileGetContents();          // processing get data         $attempt = 0;         do {             if ($attempt) {                 sleep($attempt);             }              try {                 $client->send($request, $response);             } catch (\Exception $e) {                 continue;             }         } while (false === ($response instanceof Response) && ++$attempt < 5);          if (false === ($response instanceof Response) || false === $response->isOk()) {             return false;         }          $data = $response->getContent();          return $data;     } } 

И отредактируем класс команды, для вывода в консоль заголовков последних новостей, рубрики «Интернет»:

// ./src/Command/NewsInternetCommand.php  namespace Command;  use Parser\YandexRSSNewsParser; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface;  /** * NewsInternetCommand */ class NewsInternetCommand extends Command {     /**      * Configuration of command      */     protected function configure()     {         $this             ->setName("news:internet")             ->setDescription("Command for parsing internet news")         ;     }      /**      * Execute command      *      * @param \Symfony\Component\Console\Input\InputInterface $input      * @param \Symfony\Component\Console\Output\OutputInterface $output      */     protected function execute(InputInterface $input, OutputInterface $output)     {         $parser = new YandexRSSNewsParser();          $output->writeln(array(             "",             "<info>Start parsing</info>",             ""         ));          // News         $news = $parser->getNews('/internet.rss');          foreach ($news as $item) {             $output->writeln(sprintf(                "<info>[%s]</info> <comment>%s</comment>",                $item['datetime'],                $item['title']             ));         }          $output->writeln(array(             "",             "<info>Done!</info>",             ""         ));     } } 

Теперь выполним подготовленную команду:

$ app/console news:internet 

Результат:

Получился очень простой, а за счет symfony/console и composer-а гибкий и удобный инструмент для организации консольных команд на PHP.

ссылка на оригинал статьи http://habrahabr.ru/post/173553/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *