I need to run a command asynchronously. To do this, I'm trying to use Process Component.
The command I'm trying to start is calling a function that needs somes parameters. These parameters are given by Controller that launches the Process.
Problem is I don't know how to pass parameters to my command with Process Component.
Command :
protected function configure()
{
$this
->setName('generationpdf:classement')
->setDescription('Génère le PDF d\'un classement.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
ini_set('memory_limit','4000M');
$pdf = $this->imprimerAction();
$this->sendClassement($pdf);
$output->writeln('PDF envoyé.');
}
Controller :
$command = new PDFClassementCommand();
$input = new ArrayInput(array('id' => $id, 'errata' => $errata, 'precision' => $precision, 'group' => $group, 'logoAnnee' => $logoAnnee));
$output = new NullOutput();
$process = new Process($command);
$process->disableOutput();
$process->start();
Parameters I need to use are in ArrayInput but Process doesn't take array argument.
The process is expecting first argument to be string.
Example:
$process = new Process('ls');
To run command you will need to execute symfony console command
$process = new Process('/var/www/my-project/bin/console generationpdf:classement')
You don't have to hardcode the path, see how-to-get-the-root-dir.
You can pass parameters normally, like running command from cli
$process = new Process('/var/www/my-project/bin/console generationpdf:classement --id=5 --errata=someValue')
Related
If I execute a task using the console I can add --no-debug:
php app/console app:task-name web-user --no-debug
This is the function inside Controller that call the task 'task-name ' and it works properly
public function generateSomethingAction() {
$kernel = $this->get('kernel');
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput(array(
'command' => 'app:task-name'
));
$output = new BufferedOutput();
$application->run($input, $output);
......
I would like to know if is possible to add --no-debug if I call the command from a controller?
To pass additional parameters that don't need values, you can just add them to the ArrayInput array with a value of "true".
E.g.
$input = new ArrayInput([
'command' => 'app:task-name',
'--yell' => true,
'--no-debug' => true,
]);
I am migrating my Laravel 4.2 app to 5.1 (starting with 5.0) and am a lot of trouble with my console command unit tests. I have artisan commands for which I need to test the produced console output, proper question/response handling and interactions with other services (using mocks). For all its merits, the Laravel doc is unfortunately silent with regards to testing console commands.
I finally found a way to create those tests, but it feels like a hack with those setLaravel and setApplication calls.
Is there a better way to do this? I wish I could add my mock instances to the Laravel IoC container and let it create the commands to test with everything properly set. I'm afraid my unit tests will break easily with newer Laravel versions.
Here's my unit test:
Use statements:
use Mockery as m;
use App\Console\Commands\AddClientCommand;
use Symfony\Component\Console\Tester\CommandTester;
Setup
public function setUp() {
parent::setUp();
$this->store = m::mock('App\Services\Store');
$this->command = new AddClientCommand($this->store);
// Taken from laravel/framework artisan command unit tests
// (e.g. tests/Database/DatabaseMigrationRollbackCommandTest.php)
$this->command->setLaravel($this->app->make('Illuminate\Contracts\Foundation\Application'));
// Required to provide input to command questions (provides command->getHelper())
// Taken from ??? when I first built my command tests in Laravel 4.2
$this->command->setApplication($this->app->make('Symfony\Component\Console\Application'));
}
Input provided as command arguments. Checks console output
public function testReadCommandOutput() {
$commandTester = new CommandTester($this->command);
$result = $commandTester->execute([
'--client-name' => 'New Client',
]);
$this->assertSame(0, $result);
$templatePath = $this->testTemplate;
// Check console output
$this->assertEquals(1, preg_match('/^Client \'New Client\' was added./m', $commandTester->getDisplay()));
}
Input provided by simulated keyboard keys
public function testAnswerQuestions() {
$commandTester = new CommandTester($this->command);
// Simulate keyboard input in console for new client
$inputs = $this->command->getHelper('question');
$inputs->setInputStream($this->getInputStream("New Client\n"));
$result = $commandTester->execute([]);
$this->assertSame(0, $result);
$templatePath = $this->testTemplate;
// Check console output
$this->assertEquals(1, preg_match('/^Client \'New Client\' was added./m', $commandTester->getDisplay()));
}
protected function getInputStream($input) {
$stream = fopen('php://memory', 'r+', false);
fputs($stream, $input);
rewind($stream);
return $stream;
}
updates
This doesn't work in Laravel 5.1 #11946
I have done this before as follows - my console command returns a json response:
public function getConsoleResponse()
{
$kernel = $this->app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArrayInput([
'command' => 'test:command', // put your command name here
]),
$output = new Symfony\Component\Console\Output\BufferedOutput
);
return json_decode($output->fetch(), true);
}
So if you want to put this in it's own command tester class, or as a function within TestCase etc... up to you.
use Illuminate\Support\Facades\Artisan;
use Symfony\Component\Console\Output\BufferedOutput;
$output = new BufferedOutput();
Artisan::call('passport:client', [
'--password' => true,
'--name' => 'Temp Client',
'--no-interaction' => true,
], $output);
$stringOutput = $output->fetch();
I have created a console command in console/controllers with SuggestionController .
If i run command like php yii suggestions, its working.
I want to know how to execute console command from web without any extensions of yii2.
It can be done much simpler
$oldApp = \Yii::$app;
new \yii\console\Application([
'id' => 'Command runner',
'basePath' => '#app',
'components' => [
'db' => $oldApp->db,
],
);
\Yii::$app->runAction('migrate/up', ['migrationPath' => '#yii/rbac/migrations/', 'interactive' => false]);
\Yii:$app = $oldApp;
Github LINK
As of Yii2 - 2.0.11.2 advanced app -- this works
First let's make sure controller and namespace correct. In this case frontend app accessing console application import method()
In console\controllers\FhirController
Set the alias to be available in the console\config\main.php [OPTIONAL]
'aliases' => [
'#common' => dirname(__DIR__),
'#frontend' => dirname(dirname(__DIR__)) . '/frontend',
'#backend' => dirname(dirname(__DIR__)) . '/backend',
'#console' => dirname(dirname(__DIR__)) . '/console',
],
Finally from the frontend view, make the call like this:
In this case, calling the controller route fhir then method import()
$consoleController = new console\controllers\FhirController('fhir', Yii::$app);
$consoleController->runAction('import');
On a site I'm overhauling, I have a need for a background task, that can be toggled via an action, which requires that I can also find its pid using ps. After much googling and almost as much swearing, I pieced together the solution.
# First change to (already-calculated) correct dir:
chdir($strPath);
# Now execute with nohup, directed to dev/null, and crucially with & at end, to run async:
$output = shell_exec("nohup php yii <console controller>/<action> > /dev/null &");
Yes, I understand that shell_exec should be used with extreme caution, thanks.
This is way I found and used some time ago to run yii console controller/action (I used this for run migrations from web).
In your web controller action:
// default console commands outputs to STDOUT
// so this needs to be declared for wep app
if (! defined('STDOUT')) {
define('STDOUT', fopen('/tmp/stdout', 'w'));
}
$consoleController = new \yii\console\controllers\SuggestionController;
$consoleController->runAction('your action eg. index');
/**
* open the STDOUT output file for reading
*
* #var $message collects the resulting messages of the migrate command to be displayed in a view
*/
$handle = fopen('/tmp/stdout', 'r');
$message = '';
while (($buffer = fgets($handle, 4096)) !== false) {
$message .= $buffer . "\n";
}
fclose($handle);
return $message;
You can either exec() your command ´´´php yii suggestions´´´ but this may result in permission issues with the webserver user.
The better way is to use a ConsoleRunner extension, e.g. yii2-console-runner or yii2-console-runner-extension which do the job control job a little bit more sophisticated and more secure with popen().
Always be aware of code injections when executing exec() and the like!
I think this is the simplest solution:
$controller = new SuggestionController(Yii::$app->controller->id, Yii::$app);
$controller->actionSuggestions();
yii2 call console command from web or api .
my controller name :
UnitController
in folder
console/controllers
my action name :
actionUnitDesc
if(!defined('STDIN')) define('STDIN', fopen('php://stdin', 'rb'));
if(!defined('STDOUT')) define('STDOUT', fopen('php://stdout', 'wb'));
if(!defined('STDERR')) define('STDERR', fopen('php://stderr', 'wb'));
$consoleController = new \console\controllers\UnitController('unit', Yii::$app);
$consoleController->runAction('unit-des');
I'm having a nightmare trying to set up a cron job in my Symfony2 project.
I understand the principle of setting it up and where to put the code but I just cannot get it to do what I need.
Basically, I need the cron job to run every day and check a database of clients in order to find out if an invoice needs sending. The actual client referencing is yet to be done but I have written a test which I want to generate and email and invoice based on hardcoded values I pass to the function.
// AppBundle/Command/CronRunCommand.php
protected function execute(InputInterface $input, OutputInterface $output)
{
$request = new Request();
$request->attributes->set('client','14');
$request->attributes->set('invoice_id','3');
$request->attributes->set('dl','0');
$output->writeln('<comment>Running Invoice Cron Task...</comment>');
return $this->getContainer()->get('invoices')->generateInvoiceAction($request);
}
I have set invoices up as a service in my config.yml:
services:
invoices:
class: AppBundle\Controller\InvoiceController
And in InvoiceController there is a function that will generate an invoice by using Invoice Painter Bundle and then send it to the specified email address (currently hard coded for development purposes).
When I run the cron command on my console, it throws the following error:
[Symfony\Component\Debug\Exception\FatalErrorException]
Error: Call to a member function has() on null
I have searched for this and I believe it is to do with the fact that it's referencing a controller method and my command file does not extend controller, but I'm so confused about how I can do this - surely there is a way of running a method in a controller as a cron job?
Any help appreciated.
Michael
I fear you may still not be understanding the big picture. Console apps don't have a request object and thus the whole request_stack is not going to work. I know you tried creating a request object but that is not going to impact the request stack.
Your console app should look something like:
protected function execute(InputInterface $input, OutputInterface $output)
{
$data = [
'client' => 14,
'invoice' => 3,
'dl' => 0,
];
$invoiceManager = $this->getContainer()->get('invoices');
$results = $invoiceManager->generateInvoice($data);
}
Your controller action would be something like:
public function generateInvoiceAction(Request $request)
{
$data = [
'client' => $request->attribute->get('client'),
'invoice' => $request->attribute->get('invoice'),
'dl' => $request->attribute->get('dl'),
];
$invoiceManager = $this->getContainer()->get('invoices');
$results = $invoiceManager->generateInvoice($data);
The invoice manager might look like:
class InvoiceManager {
public function __construct($em) {
$em = $this->em;
}
public function generateInvoice($data) {
$client = $this->em->find('Client',$data['client']);
I'm writing a console component using Symfony2 libraries that consumes another application that's also written with symfony2 console components.
I want to mock the other applications console component, how do I go about achieving this? The application I'm building is simply consuming an existing command from another application:
Basically, how do you write a unit test for the code below:
protected function execute(InputInterface $input, OutputInterface $output)
{
$command = $this->getApplication()->find('demo:greet');
$arguments = array(
'command' => 'demo:greet',
'name' => 'Fabien',
'--yell' => true,
);
$input = new ArrayInput($arguments);
$returnCode = $command->run($input, $output);
// ...
}
Another application or the same application?
$commandMock = $this->getMock('Symfony\Component\Console\Command\Command');
$commandMock->expects($this->once())->method('run')->with(...)->will($this->returnValue(1));
$applicationMock = $this->getMockBuilder('Symfony\Component\Console\Application')
->disableConstructor()->getMock();
$applicationMock->method('find')
->with($this->equalTo('demo:greet'))
->will($this->returnValue($commandMock));
You just mock the command and mock the application to return the mocked command.