eliashaeussler/task-runner

Progress helper for long-running CLI tasks, based on Symfony Console

Installs: 165

Dependents: 1

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 1

pkg:composer/eliashaeussler/task-runner

1.0.1 2025-12-26 20:38 UTC

This package is auto-updated.

Last update: 2025-12-26 20:41:04 UTC


README

Task Runner

Coverage CGL Tests Supported PHP Versions

Task Runner is a simple PHP library targeted for CLI-oriented applications and projects. It provides a progress helper for long-running CLI tasks, based on Symfony Console.

🔥 Installation

Packagist Packagist Downloads

composer require eliashaeussler/task-runner

⚡️ Usage

Basic usage

A common use case is to wrap a long-running process in a TaskRunner instance, which handles progress messages and the overall task execution:

use EliasHaeussler\TaskRunner;
use Symfony\Component\Console;

// 1. Create a new TaskRunner instance
$output = new Console\Output\ConsoleOutput();
$taskRunner = new TaskRunner\TaskRunner($output);

// 2. Run the task
$taskRunner->run(
    'Downloading some large files from the internet',
    static function () {
        // Do some long-running work here...
    },
);

This will output the following progress message:

Downloading some large files from the internet... Done

Progress message decorators

User-oriented progress messages are decorated by a ProgressDecorator instance. By default, a simple progress decorator is used. It displays the various steps like follows:

  • Progress start: Downloading some large files from the internet...
  • Task successful: Downloading some large files from the internet... Done
  • Task failed: Downloading some large files from the internet... Failed

It is possible to pass a customized progress decorator to the TaskRunner instance:

use EliasHaeussler\TaskRunner;
use Symfony\Component\Console;

// 1. Create a custom progress decorator
$decorator = new class implements TaskRunner\Decorator\ProgressDecorator
{
    public function progress(string $message, bool &$newLine = false): string
    {
        return ''.$message.'... ';
    }

    public function done(mixed $result = null): string
    {
        return '🥳'
    }

    public function failed(?Throwable $exception = null): string
    {
        return '💥';
    }
}

// 2. Create a new TaskRunner instance
$output = new Console\Output\ConsoleOutput();
$taskRunner = new TaskRunner\TaskRunner($output, $decorator);

// 3. Run the task
$taskRunner->run(
    'Downloading some large files from the internet',
    static function () {
        // Do some long-running work here...
    },
);

The above example will output the following progress messages:

  • Progress start: ⏳ Downloading some large files from the internet...
  • Task successful: ⏳ Downloading some large files from the internet... 🥳
  • Task failed: ⏳ Downloading some large files from the internet... 💥

Custom output

Some long-running tasks may also produce output that should be displayed to the user. For this usecase, the executing task receives a RunnerContext instance, which holds a buffered output stream. This can be used to write output to the console:

use EliasHaeussler\TaskRunner;

$taskRunner->run(
    'Downloading some large files from the internet',
    static function (TaskRunner\RunnerContext $context) {
        // Do some long-running work here...

        $context->output->writeln('This content is displayed to the user.');
    },
);

The additional output will be displayed after the progress message:

Downloading some large files from the internet... Done
This content is displayed to the user.

Error Handling

If an error occurs during the execution of the task, the TaskRunner will automatically catch it, mark the test execution as failed, and by default throw the catched exception afterwards:

Downloading some large files from the internet... Failed
PHP Fatal error:  Uncaught RuntimeException: Downloading failed.

This behavior can be customized in various ways.

Avoid throwing an exception

In some cases, a thrown exception may not be relevant for the user and hence should not avoid further execution of the application. This behavior can be controlled with the passed RunnerContext instance:

use EliasHaeussler\TaskRunner;

$taskRunner->run(
    'Downloading some large files from the internet',
    static function (TaskRunner\RunnerContext $context) {
        $context->throwExceptions = false;

        // Do some long-running work here...

        if ($downloadFailed) {
            throw new RuntimeException('Downloading failed.');
        }
    },
);

In the above example, the task execution is still marked as failed, but the exception is not thrown afterwards:

Downloading some large files from the internet... Failed

Control task result

In addition, it is also possible to control the task result manually, even if no exception was thrown:

use EliasHaeussler\TaskRunner;

$taskRunner->run(
    'Downloading some large files from the internet',
    static function (TaskRunner\RunnerContext $context) {
        // Do some long-running work here...

        if ($downloadFailed) {
            $context->markAsFailed();
        }
    },
);

In the above example, the task execution is now manually marked as failed:

Downloading some large files from the internet... Failed

Receive task result

If the provided task is successful, the TaskRunner returns the result of the executed task:

use EliasHaeussler\TaskRunner;

$files = $taskRunner->run(
    'Downloading some large files from the internet',
    static function (TaskRunner\RunnerContext $context) {
        // Do some long-running work here...

        return $files;
    },
);

// $files is now the result of the task

If a task does not provide a result, null will be returned instead:

use EliasHaeussler\TaskRunner;

$files = $taskRunner->run(
    'Downloading some large files from the internet',
    static function (TaskRunner\RunnerContext $context) {
        // Do some long-running work here...
    },
);

// $files is always NULL

This, however, can be slightly adapted, if the task is declared as returning void. In this case, the TaskRunner will return a dedicated TaskResult object instead:

use EliasHaeussler\TaskRunner;

$result = $taskRunner->run(
    'Downloading some large files from the internet',
    static function (TaskRunner\RunnerContext $context): void {
        // Do some long-running work here...
    },
);

// $result is now a TaskResult instance, e.g. TaskResult::Success

In case a task throws an exception and the passed RunnerContext was modified to avoid throwing exceptions, the TaskResult will return TaskResult::Failed instead:

use EliasHaeussler\TaskRunner;

$result = $taskRunner->run(
    'Downloading some large files from the internet',
    static function (TaskRunner\RunnerContext $context) {
        $context->throwExceptions = false;

        // Do some long-running work here...

        if ($downloadFailed) {
            $context->markAsFailed();
        }
    },
);

// $result is now a TaskResult::Failed instance

🧑‍💻 Contributing

Please have a look at CONTRIBUTING.md.

⭐ License

This project is licensed under GNU General Public License 3.0 (or later).