docs/console.md

Summary

Maintainability
Test Coverage
:::{php:namespace} Atk4\Ui
:::

:::{php:class} Console
:::

# Console

:::{figure} images/console.png
:::

With console you can output real-time information to the user directly from PHP. It can
be used do direct output from slow method or even execute commands on the server (such as `ping`).

Demo: https://ui.atk4.org/demos/interactive/console.php

## Basic Usage

:::{php:method} set($callback)
:::

:::{php:method} send($callback)
:::

After adding a console to your {ref}`render_tree`, you just need to set a callback:

```
$console = Console::addTo($app);
$console->set(function (Console $console) {
    // this will be executed through SSE request
    $console->output('hello');
    echo 'world'; // also will be redirected to console
    sleep(2);
    $console->send(new \Atk4\Ui\Js\JsExpression('alert([])', ['The wait is over']));
});
```

Console uses {ref}`sse` which works pretty much out-of-the-box with the modern browsers and unlike websockets
do not require you to set up additional ports on the server. JavaScript in a browser captures real-time
events and displays it on a black background.

Console integrates nicely with DebugTrait (https://atk4-core.readthedocs.io/en/develop/debug.html?highlight=debug),
and also allows you to execute shell process on the server while redirecting output in real-time.

## Using With Object

:::{php:method} runMethod($callback)
:::

We recommend that you pack up your busineess logic into your Model methods. When it's time to call your method,
you could either do this:

```
$user->generateReport(30);
```

Which would execute your own routine for some report generation, but doing it though a normal request will look like
your site is slow and is unable to load page quick. Alternative is to run it through a console:

```
$console->runMethod($user, 'generateReport', [30]);
```

This will display console to the user and will even output information from inside the model:

```
use \Atk4\Core\DebugTrait();

public function generateReport($pages)
{
    $this->info('converting report to PDF');

    // slow stuff
    $this->info('almost done, be patient..');

    // more slow stuff
    return true;
}
```

You can also execute static methods:

```
$console->runMethod('StaticLib', 'myStaticMethod');
```

## Executing Commands

:::{php:method} exec($cmd, $args)
:::

:::{php:attr} lastExitCode
:::

To execute a command, use:

```
$console->exec('/sbin/ping', ['-c', '5', '-i', '1', '192.168.0.1']);
```

This will run a command, and will stream command output to you. Console is implemented to capture both STDOUT and STDERR in
real-time then display it on the console using color. Console does not support ANSI output.

Method exec can be executed directly on the $console or inside the callback:

```
$console->set(function (Console $console) {
    $console->eval();
});
```

Without callback, eval will wrap itself into a callback but you can only execute a single command. When using callback
form, you can execute multiple commands:

```
Console::addTo($app)->set(function (Console $c) {
    $c
        ->exec('/sbin/ping', ['-c', '5', '-i', '1', '192.168.0.1'])
        ->exec('/sbin/ping', ['-c', '5', '-i', '2', '8.8.8.8'])
        ->exec('/bin/no-such-command');
});
```

Method exec() will return `$this` if command was run inside callback and was successful. It will return `false` on error
and will return `null` if called outside of callback. You may also refer to {php:attr}`Console::$lastExitCode` which
contains exit code of the last command.

Normally it's safe to chain `exec` which ensures that execution will stack. Should any command fail, the subsequent
`exec` won't be performed.

NOTE that for each invocation `exec` will spawn a new process, but if you want to execute multiple processes, you
can wrap them into `bash -c`:

```
Console::addTo($app)->exec('bash', [
    '-c',
    'cd ..; echo \'Running "composer update" in `pwd`\'; composer --no-ansi update; echo \'Self-updated. OK to refresh now!\'',
]);
```

This also demonstrates argument escaping.