docs/console.md
:::{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.