Собирать тяжелый фреймворк ради удобных фич, лень, а реализовывать в рамках кода текущих проектов как-то не эстетично. Поэтому для экономии своего времени приходится создавать скрипт, копипастить в него куски кода из предыдущих наработок, подключать разнообразные библиотеки и запускать скрипт из консоли. При этом часто требуется некоторая интерактивность работы скрипта: обработка опций/аргументов, а то и диалоговое взаимодействие. Здесь главное чтобы не было настроения, которое хорошо описывается выражением «Аппетит приходит во время еды», тогда вообще не понятно к чему приведет работа над простой задачкой =)
В такие моменты я вспоминал удобную симфоническую консоль, к которой успел привыкнуть работая с проектами на
Symfony 2. Не в обиду другим консолям (zend, yii, django, ror etc), все хороши, просто так сложилось.
Когда в очередной раз потребовалось что-то распарсить, я опять вспомнил про консоль Symfony (Console Component) и тот факт, что это независимый компонент все больше подтолкнул меня к мысли использовать ее возможности.
За пару часов получилась простая тулза, в основе которой:
- symfony/console — сама консоль
- symfony/finder — для поиска и подключения к приложению наших комманд
- suncat/symfony-console-extra — несколько плюшек для того чтобы это все работало
и менеджер зависимостей 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/
Добавить комментарий