interitty/console

Extension of the standard Symfony/Console by some other features that are specific for use in the Interitty projects.

v1.0.9 2024-12-12 17:53 UTC

This package is auto-updated.

Last update: 2024-12-12 16:54:44 UTC


README

Extension of the standard Symfony/Console by some other features that are specific for use in the Interitty projects.

Requirements

Installation

The best way to install interitty/console is using Composer:

composer require interitty/console

Then register the extension in the Nette config file:

# app/config/config.neon
extensions:
    console: Interitty\Console\Nette\DI\ConsoleExtension(%consoleMode%)

Features

The Interitty/Console provides some additional features to the standard Symfony/Console library.

Console output

The BaseCommand class provides write and writeError methods that allow writing to standard output respectively to standard error output without the need to transmit the OutputInterface object to every method.

protected function foo(): void
{
    // outputs multiple lines to the console (adding PHP_EOL at the end of each line)
    $this->write([
        'User Creator',
        '============',
        '',
    ]);

    // the value returned by someMethod() can be an iterator (https://php.net/iterator)
    // that generates and returns the messages with the 'yield' PHP keyword
    $this->write($this->someMethod());

    // outputs a message followed by a PHP_EOL
    $this->write('Whoa!');

    // outputs a message without adding a PHP_EOL at the end of the line
    $this->write('You are about to ', false);
    $this->write('create a user.', false);

    // outputs a message to standard error output
    $this->writeError('<error>big bada bum</error>');
}

This is possible thanks to stored InputInterface and OutputInterface objects that are accessible via getInput, getOutput, and getErrorOutput methods.

Debug log timestamped

Especially when dealing with complicated situations, it can be helpful to know when an event occurred. For this case, standard methods like write and writeError are extended with the option to turn on the timestamp at the beginning of the message in "debug" mode.

Just turn on the static variable $isLogTimestamped in the command.

Exception processor

When the Exception or any Throwable object was thrown in the execute process, there is a standard processException method that converts the exception message to a standard error output message. It also dumps a trace log when the Debug verbosity mode is set.

Lazy-loading

The ConsoleExtension extension automatically binds all registered services of type Symfony\Component\Console\Command\Command using a simple implementation LazyCommandLoader.

The value defined by the AsCommand attribute is used as the command name.

use Interitty\Console\BaseCommand;
use Symfony\Component\Console\Attribute\AsCommand;

#[AsCommand(name: 'app:dummy')]
class DummyCommand extends BaseCommand
{
}

However, many existing classes still need to start using this attribute. It may also be helpful to set the name to a different value. In these cases, using so-called tags when defining a service is possible.

services:
    commands.dummy:
        class: App\DummyCommand
        tags: [console.command: app:dummy]
        # or
        tags: [console.command: {name: app:dummy}]

Long running prevention

Sometimes, the command may process a massive volume of data regularly. It is usually routinely run, for example, every 10 minutes, to react as quickly as possible to new data. Thanks to the "Exclusive command call lock" functionality, it is ensured that any further run is preemptively terminated with a log message. However, the first run may run for several days, and all possible related logs go into one file.

To avoid this, a processCheckLock function has been implemented which can optionally be called in the code to check if the command was run yesterday.

protected function processExecute(): int
{
    do {
        // ... do something important
        $this->processCheckLock();
    } while (...);
    return self::SUCCESS;
}

Exclusive command call lock

Usually, rerunning the same command leads to an unwanted and difficult-to-detect error. Therefore, a simple check is implemented by default using the Symfony/Lock package, which can be simply disabled if necessary using the static $isExclusive parameter.

use Interitty\Console\BaseCommand;

class NotExclusiveCommand extends BaseCommand
{
    /** @var bool */
    protected static bool $isExclusive = false;
}

Otherwise, when the same command is re-executed with the same parameters, a logical exception will occur with the text "The command is already running in another process".

ProgressBar factory

The factory method createProgressBar is available for easier access to the progress bar.

protected function processExecute(): int
{
    $data = $this->getData();
    $progressBar = $this->createProgressBar($data->count());
    $progressBar->start();
    foreach($data as $item) {
        // Do some logic here
        $progressBar->advance();
    }
    $progressBar->finish();
    return self::SUCCESS;
}