Обзор

CLI означает интерфейс командной строки. Приложений командной строки выполняются из командной строки или в командной строке. Одно из преимуществ приложений CLI заключается в том, что они не имеют слоя представления (только потенциально отражающего выходные данные на экране) и могут выполняться более чем по одному за раз. Некоторые из распространенных способов использования-это задания cron, сценарии манипуляций, сценарии импорта данных, командные утилиты и многое другое.

Приложения CLI выполняются из командной строки. Они полезны для создания cron заданий, скриптов, командных утилит и многого другого.

Структура

Вы можете создать приложение CLI в Phalcon, используя класс Phalcon\Cli\Console. Этот класс расширяется от основного абстрактного класса приложений и использует каталог, в котором расположены сценарии задач. Сценарии задач - это классы, которые расширяют Phalcon\Cli\Task и содержат код, который нам нужно выполнить.

Структура каталогов приложения CLI может выглядеть следующим образом:

  • app/tasks/MainTask.php
  • app/cli.php основной загрузочный файл

 

В приведенном выше примере cli.php является точкой входа нашего приложения, в то время как каталог src/tasks содержит все классы задач, которые обрабатывают каждую команду.

ПРИМЕЧАНИЕ: Каждый файл задачи и класс должны быть снабжены суффиксом Task. Задача по умолчанию (если параметры не были переданы) - MainTask, а метод по умолчанию, который будет выполняться внутри задачи- main

Создание загрузочного файла

Как и в обычных MVC приложениях, для начальной загрузки приложения используется загрузочный файл. Вместо загрузчика index.php в веб-приложениях мы используем файл cli.php для начальной загрузки приложения.

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

<?php

declare(strict_types=1);

use Exception;
use Phalcon\Cli\Console;
use Phalcon\Cli\Dispatcher;
use Phalcon\Di\FactoryDefault\Cli as CliDI;
use Phalcon\Exception as PhalconException;
use Phalcon\Loader;
use Throwable;

$loader = new Loader();
$loader->registerNamespaces(
    [
       'MyApp' => 'src/',
    ]
);
$loader->register();

$container  = new CliDI();
$dispatcher = new Dispatcher();

$dispatcher->setDefaultNamespace('MyApp\Tasks');
$container->setShared('dispatcher', $dispatcher);

$console = new Console($container);

$arguments = [];
foreach ($argv as $k => $arg) {
    if ($k === 1) {
        $arguments['task'] = $arg;
    } elseif ($k === 2) {
        $arguments['action'] = $arg;
    } elseif ($k >= 3) {
        $arguments['params'][] = $arg;
    }
}

try {
    $console->handle($arguments);
} catch (PhalconException $e) {
    fwrite(STDERR, $e->getMessage() . PHP_EOL);
    exit(1);
} catch (Throwable $throwable) {
    fwrite(STDERR, $throwable->getMessage() . PHP_EOL);
    exit(1);
} catch (Exception $exception) {
    fwrite(STDERR, $exception->getMessage() . PHP_EOL);
    exit(1);
}

Давайте рассмотрим код выше более подробно.

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

Загрузчик

$loader = new Loader();
$loader->registerNamespaces(
    [
       'MyApp' => 'src/',
    ]
);
$loader->register();

Создать  автозагрузчик Phalcon и зарегистрироваться пространства имен в директории src/.

ПРИМЕЧАНИЕ: Если вы решили использовать автозагрузчик Composer в вашем composer.json, вам не нужно регистрировать загрузчик в этом приложении

DI

$container  = new CliDI();

Нам нужен контейнер для инъекции зависимостей. Вы можете использовать контейнер Phalcon\Di\FactoryDefault\Cli, в котором уже зарегистрированы сервисы. Кроме того, вы всегда можете использовать Phalcon\Di и регистрировать необходимые вам услуги, одну за другой.

Диспетчер

$dispatcher = new Dispatcher();

$dispatcher->setDefaultNamespace('MyApp\Tasks');
$container->setShared('dispatcher', $dispatcher);

Приложениям CLI требуется определенный диспетчер. Phalcon\Cli\Dispatcher предлагает ту же функциональность, что и основной диспетчер для приложений MVC, но он адаптирован к приложениям CLI. Как и ожидалось, мы создаем экземпляр объекта dispatcher, устанавливаем пространство имен по умолчанию и затем регистрируем его в контейнере DI.

Приложение

$console = new Console($container);

Как уже упоминалось выше, приложение командной строки осуществляется с помощью Phalcon\Cli\Console. Здесь мы создаем объект и передать его в контейнер.

Аргументы Наше приложение нуждается в аргументах. Они приходят в виде :

php ./cli.php argument1 argument2 argument3 ...

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

$arguments = [];
foreach ($argv as $k => $arg) {
    if ($k === 1) {
        $arguments['task'] = $arg;
    } elseif ($k === 2) {
        $arguments['action'] = $arg;
    } elseif ($k >= 3) {
        $arguments['params'][] = $arg;
    }
}

Как вы можете видеть выше, мы используем $argv для получения того, что было передано через командную строку, и мы разделяем эти аргументы соответственно, чтобы понять, какие задачи и действия должны быть вызваны и с какими параметрами.

Итак, для следующего примера:

try {
    $console->handle($arguments);
} catch (PhalconException $e) {
    fwrite(STDERR, $e->getMessage() . PHP_EOL);
    exit(1);
} catch (Throwable $throwable) {
    fwrite(STDERR, $throwable->getMessage() . PHP_EOL);
    exit(1);
} catch (Exception $exception) {
    fwrite(STDERR, $exception->getMessage() . PHP_EOL);
    exit(1);
}

В приведенном выше коде мы используем наш консольный объект и вызываем handle с вычисляемыми параметрами. Приложение CLI выполнит необходимую маршрутизацию и отправит запрошенную задачу и действие. Если возникает исключение, оно будет поймано операторами catch и ошибки будут отображаться на экране соответственно.

Исключения

Любое исключение, вызванное в компоненте Phalcon\Cli\Console, будет иметь тип Phalcon\Cli\Console\Exception, что позволяет вам ловить исключение специально.

Задачи

Задачи являются эквивалентом контроллеров в приложении MVC. Любое приложение CLI нуждается по крайней мере в одной задаче под названием MainTask и mainAction, и у каждой задачи должно быть mainAction. Любая определенная задача должна иметь mainAction, которая будет вызвана, если никакое действие не определено. Вы не ограничены количеством действий, которые может содержать каждая задача. 

Ниже приведен пример файла app/tasks/MainTask.php:

<?php

declare(strict_types=1);

namespace MyApp\Tasks;

use Phalcon\Cli\Task;

class MainTask extends Task
{
    public function mainAction()
    {
        echo 'This is the default task and the default action' . PHP_EOL;
    }
}

Вы можете реализовать свои собственные задачи, либо расширяя поставляемый Phalcon\Cli\Task, либо написав свой собственный класс, реализующий Phalcon\Cli\TaskInterface.

Действия

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

<?php

declare(strict_types=1);

namespace MyApp\Tasks;

use Phalcon\Cli\Task;

class UsersTask extends Task
{
    public function mainAction()
    {
        echo 'This is the default task and the default action' . PHP_EOL;
    }

    public function regenerateAction(int $count = 0)
    {
        echo 'This is the retenerate action' . PHP_EOL;
    }
}

Затем мы можем вызвать действие main (действие по умолчанию):

./cli.php users 

или действие regenerate:

./cli.php users regenerate

Обработка параметров

Вы также можете передавать параметры в действие. Пример того, как обрабатывать параметры, можно найти выше, в Примере файла bootstrap.

<?php

declare(strict_types=1);

namespace MyApp\Tasks;

use Phalcon\Cli\Task;

class UsersTask extends Task
{
    public function mainAction()
    {
        echo 'Это задача по умолчанию и действие по умолчанию' . PHP_EOL;
    }

    public function addAction(int $first, int $second)
    {
        echo $first + $second . PHP_EOL;
    }
}

Затем мы можем выполнить следующую команду:

php cli.php users add 4 5

9

Запуск цепочки команд

Вы также можете цеплять задачи. Чтобы запустить их один за другим, нам нужно внести небольшое изменение в наш bootstrap: нам нужно зарегистрировать наше приложение в контейнере DI:

// ...
$console = new Console($container);
$container->setShared('console', $console);

$arguments = [];
// ...

Теперь, когда консольное приложение находится внутри контейнера DI, Мы можем получить к нему доступ из любой задачи.

Предположим, что мы хотим вызвать printAction() из задачи Users, все, что нам нужно сделать, это вызвать его с помощью контейнера.

<?php

namespace MyApp\Tasks;

use Phalcon\Cli\Console;
use Phalcon\Cli\Task;

/**
 * @property Console $console
 */
class UsersTask extends Task
{
    public function mainAction()
    {
        echo 'Это задача по умолчанию и действие по умолчанию' . PHP_EOL;

        $this->console->handle(
            [
                'task'   => 'main',
                'action' => 'print',
            ]
        );
    }

    public function printAction()
    {
        echo 'Меня тоже напечатают!' . PHP_EOL;
    }
}

Эта техника позволяет запускать любую задачу и любое действие из любой другой задачи. Однако это не рекомендуется, потому что это может привести к кошмарам обслуживания. Лучше расширить Phalcon\Cli\Task и реализовать там свою логику.

Модули

Приложения CLI также могут обрабатывать различные модули, как и приложения MVC. Вы можете зарегистрироваться различные модули приложения командной строки, чтобы обрабатывать различные пути приложения командной строки. Это позволяет лучше организовать код и группировать задачи.

Приложение командной строки предлагает следующие методы:

  • getDefaultModule - string - Возвращает имя модуля по умолчанию
  • getModule(string $name) - array/object - Возвращает определение модуля, зарегистрированное в приложении через имя модуля
  • getModules - array - Возврат модулей, зарегистрированных в приложении
  • registerModules(array $modules, bool $merge = false) - AbstractApplication - Регистрация массива модулей, имеющихся в приложении
  • setDefaultModule(string $defaultModule) - AbstractApplication - Задает имя модуля, которое будет использоваться, если маршрутизатор не возвращает допустимый модуль

 

Вы можете зарегистрироватьfrontend иbackend модуль для вашего консольного приложения следующим образом:

<?php

declare(strict_types=1);

use Exception;
use MyApp\Modules\Backend\Module as BackendModule;
use MyApp\Modules\Frontend\Module as FrontendModule;
use Phalcon\Cli\Console;
use Phalcon\Cli\Dispatcher;
use Phalcon\Di\FactoryDefault\Cli as CliDI;
use Phalcon\Exception as PhalconException;
use Phalcon\Loader;
use Throwable;

$loader = new Loader();
$loader->registerNamespaces(
    [
       'MyApp' => 'src/',
    ]
);
$loader->register();

$container  = new CliDI();
$dispatcher = new Dispatcher();

$dispatcher->setDefaultNamespace('MyApp\Tasks');
$container->setShared('dispatcher', $dispatcher);

$console = new Console($container);

$console->registerModules(
    [
        'frontend' => [
            'className' => BackendModule::class,
            'path'      => './src/frontend/Module.php',
        ],
        'backend' => [
            'className' => FrontendModule::class,
            'path'      => './src/backend/Module.php',
        ],
    ]
);

$arguments = [];
foreach ($argv as $k => $arg) {
    if ($k === 1) {
        $arguments['task'] = $arg;
    } elseif ($k === 2) {
        $arguments['action'] = $arg;
    } elseif ($k >= 3) {
        $arguments['params'][] = $arg;
    }
}

try {
    $console->handle($arguments);
} catch (PhalconException $e) {
    fwrite(STDERR, $e->getMessage() . PHP_EOL);
    exit(1);
} catch (Throwable $throwable) {
    fwrite(STDERR, $throwable->getMessage() . PHP_EOL);
    exit(1);
} catch (Exception $exception) {
    fwrite(STDERR, $exception->getMessage() . PHP_EOL);
    exit(1);
}

Приведенный выше код предполагает, что вы структурировали свои каталоги так, чтобы они содержали модули в каталогах frontend и backend.

  • src/
  • src/backend/Module.php
  • src/frontend/Module.php
  • php cli.php

 

Маршруты

Приложение CLI имеет свой собственный маршрутизатор. По умолчанию приложение Phalcon CLI использует объект Phalcon\Cli\Router, но вы можете реализовать свой собственный с помощью объекта  Phalcon\Cli\RouterInterface.

Подобно приложению MVC, Phalcon\Cli\Router использует объекты Phalcon\Cli\Router\Route для хранения информации о маршруте. Вы всегда можете реализовать свои собственные объекты, реализовав Phalcon\Cli\Router\RouteInterface.

Маршруты принимают ожидаемые параметры регулярных выражений, такие как a-zA-Z0-9 и т. д. Есть также дополнительные заполнители, которыми вы можете воспользоваться:

ЗаполнительОписание
:module Модуль (нужно сначала установить модули)
:task Имя задачи
:namespace Имя пространства имен
:action Действие
:params Никакие параметры
:int Является ли это целочисленным параметром маршрута

 

Phalcon\Cli\Router поставляется с двумя предопределенными маршрутами, так что он работает прямо из коробки. Это:

  • /:task/:action
  • /:task/:action/:params

 

Если вы не хотите использовать маршруты по умолчанию, все, что вам нужно сделать, это передать false в объекте Phalcon\Cli\Router object upon construction.

<?php

declare(strict_types=1);

use Phalcon\Cli\Router;

$router = new Router(false);

Для получения дополнительной информации о маршрутах и классах маршрутов см. страницу Маршрутизация .

События

Приложения CLI также осведомлены о событиях. Вы можете использовать методы setEventsManager и getEventsManager получить доступ к менеджер событий.

Доступны следующие события:

СобытиеСтопОписание
afterHandleTask Да Вызывается после обработки задачи
afterStartModule Да Вызывается после обработки модуля (если используются модули)
beforeHandleTask Нет Вызывается до обработки задачи
beforeStartModule Да Вызывается перед обработкой модуля (если используются модули)
boot Да Вызывается при загрузке приложения

 

Если вы используете Phalcon\Cli\Dispatcher , вы также можете воспользоваться преимуществом события beforeException , которое может остановить операции и запускается из объекта dispatcher.