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

в 8:34, , рубрики: command line, console, php, symfony, symfony2, Веб-разработка, метки: , ,

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

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

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

В такие моменты я вспоминал удобную симфоническую консоль, к которой успел привыкнуть работая с проектами на
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

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

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

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

$ app/console generate

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

Please enter the name of the command class: NewsInternetCommand

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

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

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

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

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

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

$ composer require kriswallsmith/buzz 0.9

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

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

// ./src/Parser/YandexRSSNewsParser.php

namespace Parser;

use BuzzClientFileGetContents;
use BuzzMessageRequest;
use BuzzMessageResponse;

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 ParserYandexRSSNewsParser;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputArgument;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleInputInputOption;
use SymfonyComponentConsoleOutputOutputInterface;

/**
* NewsInternetCommand
*/
class NewsInternetCommand extends Command
{
    /**
     * Configuration of command
     */
    protected function configure()
    {
        $this
            ->setName("news:internet")
            ->setDescription("Command for parsing internet news")
        ;
    }

    /**
     * Execute command
     *
     * @param SymfonyComponentConsoleInputInputInterface $input
     * @param SymfonyComponentConsoleOutputOutputInterface $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

Результат:
Консольные команды на PHP

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

Автор: k0t0vsky

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js