Contributte\Console - snadná integrace Symfony\Console do Nette Frameworku 08/08/2017

Jak jednoduše použít Symfony\Console ve vašich Nette projektech. Jak vytvořit vlastní command, jak ho zaregistrovat a nakonec spustit. S tímhle vším vám pomůže Contributte\Console.

Symfony\Console je v PHP světě poměrně rozšířený a známý nástroj. Napsala se celá řada příkazů pro práci s databází, generování obsahu, nespočet cronu a dalšího. A protože nechcem znovu vynálezat kolo, ukážeme si jak ji jednoduše vložíme do Nette projektu.

Spustit command z příkazové řádky se hodí např. při vývoji, kdy chceme vytvořit nové migrace, vymazat databázi, smazat cache, přidat uživatele apod. Taktéž se hodí při spouštění dlouhotrvajících scriptů, např. při počítání různých statistik, generování PDF. Svoje využití najdou commandy i při cron jobech. Obecně se dá řici, že commandy nám pomáháji ovládat aplikaci přes malé jednoduché příkazy, kterým lze předat nějaké argumenty (arguments), případně doladit přes atributy (options).

console foo:bar <argument> --option <baz> --option2

Rád bych vám ukázal tyto 3 způsoby integrace:

  • stará dobrá kdyby/console
  • čistě přes symfony/console
  • integrace přes contributte/console

Stará dobrá Kdyby\Console

Kdyby\Console je tu s námi už delší dobu, určitě spadá mezi nejpoužívanější balíčky do Nette vůbec. Je to parádní práce z dílny Filipa Procházky

V čem vidím problém, je konkurence a také, že se z Kdyby\Console stal poněkud větší baliček než by mohl být. Když chtěl někdo do teď integrovat symfony/console do Nette, šáhl v 99% po Kdyby balíčku. To znamená, že kdyby to Filip netáhnul dál, postupně by balíček stárnul. To že jsem balíček označil jako větší moloch, není myšleno hanlivě. Díky tomu můžeme dnes pohodlně zavolat php www/index.php a uvidíme nám známou consoli. Kvůli této pohodlnosti, je tam však celkem dost kódu, který považujicí spíše za nadbytečný. Kdyby přetěžuje váš router a vkládá tam routu, která se volá v případě že aplikace beží v CLI modu.

Do nedávna nebyla žádná jiná implementace Symfony\Console do Nette, což mi škoda. Člověk by měl mít na výběr z více možností, než si napíše vlastní (;-D).

Dnes tu proto máme contributte/console.

Čistá symfony/console integrace

Není nic jednoduššího než bez dalších závislostí přidat symfony console do Nette napřímo.

Nainstalujeme symfony console do našeho projektu.

composer require symfony/console

Dále musíme zaregistrovat Symfony\Component\Console\Application jako službu v našem configu. Můžeme definovat i jméno a verzi, nicméně, není to uplně důležité. V dalším kroku přidáme ukázkový command App\Commands\TestCommand.

services:
    console.application:
        class: Symfony\Component\Console\Application
        setup:
            # Configuration =================
            - setName('My CLI')
            - setVersion('1.0')

            # Commands ======================
            - add(App\Commands\TestCommand())

Poslední částí je spouštěcí script, který vytvoříme do bin/console.

#!/usr/bin/env php
<?php

/** @var Nette\DI\Container $container */
$container = require __DIR__ . '/../app/bootstrap.php';

// Run symfony application.
$app = $container->getByType(Symfony\Component\Console\Application::class);

// Ensure exit codes
exit($app->run());

Snadné, rychlé, téměr bez práce. Má to pár nevýhod a to, že commandy nejsou registrované a volané tzn. lazy a také, že každý command musíme ručně přidat do Symfony\Component\Console\Application.

Chtěl jsem skloubit Kdyby\Console spolu s jednoduchým a přímočarým řešením. A tak vznikl balíček contributte\console.

Integrace pomocí contributte/console

Tento balíček vznikl jako alternativa ke Kdyby\Console, jde ale jiným minimalistickým směrem. Stejně jako Filip, který se věnoval/věnuje údržbe Kdyby několik let, taktěž moje balíčky mají long-term-support.

Pomocí composeru nainstalujeme balíček.

composer require contributte/console

Registrace do Nette aplikace je přes CompilerExtension.

extensions:
    console: Contributte\Console\DI\ConsoleExtension

Konfigurovat lze všemožné parametry a to např.

console:
    name: Acme Project
    version: 1.0
    catchExceptions: true / false
    autoExit: true / false
    url: https://contributte.com
    helperSet: @customHelperSet
    helpers:
      - App\Console\MyHelper

Pro přidání vlastní commandu stačí vytvořit novou třídu s předkem Contributte\Console\Command\AbstractCommand.


namespace App\Console;

use Contributte\Console\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class FooCommand extends AbstractCommand
{

    /**
     * Configure command
     *
     * @return void
     */
    protected function configure()
    {
        $this->setName('foo');
        $this->setDescription('Example command');
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // Some magic..
    }

}

A poté zaregistrovat jako službu.

services:
    - App\Console\FooCommand

Hlavní rozdíl oproti Kdyby\Console je, že tento balíček nemodifikuje router, takže vám zavolání php www/index.php nespustí console.

Je nutné proto vytvořit entrypoint do bin/console.

#!/usr/bin/env php
<?php

/** @var Nette\DI\Container $container */
$container = require __DIR__ . '/../app/bootstrap.php';

// Get application from DI container.
$application = $container->getByType(Contributte\Console\Application::class);

// Run application.
exit($application->run());

A také mu nastavit práva na spouštění, chmod +x bin/console. Vyvolat consoli je už snadné.

bin/console
Acme Project

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -e, --env=ENV         Forced environment name (production, development, test)
      --no-sandbox      Disable commands which depends on "Sandbox".
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  help                           Displays help for a command
  list                           Lists commands
  foo                            Example command.

Hooray! :+1:


Budu rád pokud vyzkoušíte contributte/console nebo nějaký jiný balíček z rodiny Contributte, třeba contributte/event-dispatcher pro snadnou integraci Symfony/Events.