Reputation: 5849
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?
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
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