4

I need to execute a long-lasting command within a controller of my Symfony2 application and to return to the user in real time the output of the terminal.

I have read this:

http://symfony.com/doc/current/components/process.html#getting-real-time-process-output

I can't figure out how to print in real time the terminal output in a Twig template.

EDIT: Thanks to the Matteo's code and users comments, the final implementation is:

/**
 * @Route("/genera-xxx-r", name="commission_generate_r_xxx")
 * @Method({"GET"})
 */
public function generateRXXXsAction()
{
    //remove time constraints if your script last very long
    set_time_limit(0);        

    $rFolderPath = $this->container->getParameter('xxx_settings')['r_setting_folder_path'];
    $script = 'R --slave -f ' . $rFolderPath . 'main.R';

    $response = new StreamedResponse();
    $process = new Process($script);
    $response->setCallback(function() use ($process) {
        $process->run(function ($type, $buffer) {
            //if you don't want to render a template, please refer to the @Matteo's reply
            echo $this->renderView('AppBundle:Commission:_process.html.twig',
                array(
                    'type' => $type,
                    'buffer' => $buffer
                ));
            //according to @Ilmari Karonen a flush call could fix some buffering issues
            flush();
        });
    });
    $response->setStatusCode(200);
    return $response;
}
luchaninov
  • 6,792
  • 6
  • 60
  • 75
Gianluca78
  • 794
  • 1
  • 9
  • 24

1 Answers1

3

If you need lo launch a simple shell script and capture the output, you can use a StreamedResponse in conjunction with the Process callback you posted.

As Example, suppose you have a very simple bash script like follow:

loop.sh

for i in {1..500}
do
   echo "Welcome $i times"
done

You can implement your action like:

/**
 * @Route("/process", name="_processaction")
 */
public function processAction()
{
    // If your script take a very long time:
    // set_time_limit(0);
    $script='/path-script/.../loop.sh';
    $process = new Process($script);

    $response->setCallback(function() use ($process) {
        $process->run(function ($type, $buffer) {
            if (Process::ERR === $type) {
                echo 'ERR > '.$buffer;
            } else {
                echo 'OUT > '.$buffer;
                echo '<br>';
            }
        });
    });
    $response->setStatusCode(200);
    return $response;
}

And depends on the buffer length you can have an output like:

.....
OUT > Welcome 40 times Welcome 41 times 
OUT > Welcome 42 times Welcome 43 times 
OUT > Welcome 44 times Welcome 45 times 
OUT > Welcome 46 times Welcome 47 times 
OUT > Welcome 48 times 
OUT > Welcome 49 times Welcome 50 times 
OUT > Welcome 51 times Welcome 52 times 
OUT > Welcome 53 times 
.....

You can wrap this in a portion of a page with a render controller as example:

<div id="process">
    {{ render(controller(
        'AcmeDemoBundle:Test:processAction'
    )) }}
</div>

More info here

Hope this help

Matt S
  • 14,976
  • 6
  • 57
  • 76
Matteo
  • 37,680
  • 11
  • 100
  • 115
  • Thank you very much. It works but I would like to pass the output to a Twig template. If I pass the entire response to the template I have not the same result described above. Can you help me please? – Gianluca78 Mar 23 '15 at 14:58
  • You want to render the process output with a twig files or you want to return a response that twig will render for you? – Matteo Mar 23 '15 at 15:00
  • I want to return a response that twig render for me. I'm sorry but I realized that your example print the output at the end of the bash script execution. I would like to print the output _during_ the script execution... Because the script can last more than 30 minutes! – Gianluca78 Mar 23 '15 at 15:06
  • Sorry @Gianluca78 but the outout is rendered *during* the execution: see the `OUT` . You can use the template engine into the renderer function and/or use this action as a render_controller twig block. – Matteo Mar 23 '15 at 15:24
  • Hi @Gianluca78 I update my answer hope this clarify my intent – Matteo Mar 23 '15 at 15:40
  • Thank you for your clarification. There is a little problem. Using the template as in your example produces an output like the following: HTTP/1.0 200 OK Cache-Control: no-cache Date: Mon, 23 Mar 2015 16:19:54 GMT > Welcome 1 times Welcome 2 times Welcome 3 times Welcome 4 times Welcome 5 times Welcome 6 times Welcome 7 times Welcome 8 times
    HTTP/1.0 200 OK Cache-Control: no-cache Date: Mon, 23 Mar 2015 16:19:54 GMT > Welcome 9 times
    – Gianluca78 Mar 23 '15 at 16:21
  • Ok, I have fixed it. Please replace in your answer the render method with the renderView one. By the way, it worked but I don't know why I have not real time output. Messages are displayed only at the end of the script execution. – Gianluca78 Mar 23 '15 at 16:55
  • Ok i revert to the previous version (without twig) I don't undestand why you have a syncronus response, i post a *working* example may depend of the buffer size? Try dumping a lot of data.... – Matteo Mar 23 '15 at 16:58
  • @Gianluca78 if you post your code is more easy for me to help you... let me know – Matteo Mar 23 '15 at 17:02
  • @Gianluca78: For the buffering issue, a simple [flush()](http://php.net/manual/en/function.flush.php) after the echo *might* fix it, assuming there are no other buffering layers involved. – Ilmari Karonen Mar 23 '15 at 20:57
  • @Matteo Thank you for your explanation ! I still have some issues with the template rendering, could you help me ? https://stackoverflow.com/questions/49328153/how-to-correctly-use-streamedresponse-and-realtime-output – D. Schreier Mar 20 '18 at 19:25