D4V1D
D4V1D

Reputation: 5849

How to call Symfony2 console command within controller and have streamed output?

So I have a basic requirement: I need to call a Symfony2's custom console command from a controller (the script is also called by a CRON job but I want it to be callable from a web-browser).

I followed this tutorial to make it work and here it is:

<?php

namespace AppBundle\Controller\Admin;

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class TacheController extends Controller
{
    /**
     * @Route("/admin/taches/facebook", name="admin_tache_facebook")
     *
     * @return Response
     */
    public function facebookAction(Request $request)
    {
        ini_set('max_execution_time', -1);
        $kernel = $this->get('kernel');
        $application = new Application($kernel);
        $application->setAutoExit(false);

        $input = new ArrayInput(array(
            'command' => 'nrv:fetch:facebook',
        ));
        // You can use NullOutput() if you don't need the output
        $output = new BufferedOutput();
        $application->run($input, $output);

        // return the output, don't use if you used NullOutput()
        $content = $output->fetch();

        // return new Response(""), if you used NullOutput()
        return new Response($content);
    }
}

However, the console command is quite long to run (~2mn) and therefore, the page hangs for that time until it displays all the output from the command.

My goal is to have the output as it is displayed in the console like when using the web console with ConsoleBundle. I thought of the use of ob_start() and ob_end_flush() but I don't know how to use them in this context.

Is what I'm trying to achieve even possible? How can I make it so?


Solution

As per the answer provided by @MichałSznurawa, I had to extend \Symfony\Component\Console\Output\BufferedOutput and implements doWrite() method. Here it is:

<?php

namespace AppBundle\Console;

use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpFoundation\StreamedResponse;

class StreamedOutput extends BufferedOutput
{
    public function doWrite($message, $newline)
    {
        $response = new StreamedResponse();
        $response->setCallback(function() use($message) {
            echo $message;
            flush();
        });

        $response->send();
    }
}

And amend the controller as per the following:

$output = new StreamedOutput();

The result is the page streaming the output of the command as soon as it get executed (instead of waiting for it to finish).

Upvotes: 2

Views: 1438

Answers (1)

michaJlS
michaJlS

Reputation: 2500

You will have to implement own output class and inject it instead of BufferedOutput. You can extend abstract class \Symfony\Component\Console\Output\Output or implement interface vendor/symfony/symfony/src/Symfony/Component/Console/Output/OutputInterface.php:20.

Your implementation should be streaming responses like described here: http://symfony.com/doc/current/components/http_foundation/introduction.html#streaming-a-response instead of waiting for end of execution of command and sending back all output together. That will work of course only if your command prints something during an execution.

Upvotes: 1

Related Questions