I have a doubt launching a command process from my controller. The command process work fine from command line, and from my controller works using
MyProcess->run();
but not works
MyProcess->start()
I don't understand why!
I need launch it in background (asynchronously) and i would need use start()
//Launch process
$process = new Process('php ../app/console preinscripciones:enviar');
$process->start();//not work using start but it does it if I use run()
// executes after the command finishes
if (!$process->isSuccessful()) {
throw new \RuntimeException($process->getErrorOutput());
}
echo $process->getOutput();
Thanks in advance!
Related
I'm trying to execute a Symfony Command using the Symfony Process component so it executes asynchronously when getting an API request.
When I do so I get the error message that Code: 127(Command not found), but when I run it manually from my console it works like a charm.
This is the call:
public function asyncTriggerExportWitnesses(): bool
{
$process = new Process(['php /var/www/bin/console app:excel:witness']);
$process->setTimeout(600);
$process->setOptions(['create_new_console' => true]);
$process->start();
$this->logInfo('pid witness export: ' . $process->getPid());
if (!$process->isSuccessful()) {
$this->logError('async witness export failed: ' . $process->getErrorOutput());
throw new ProcessFailedException($process);
}
return true;
}
And this is the error I get:
The command \"'php /var/www/bin/console app:excel:witness'\" failed.
Exit Code: 127(Command not found)
Working directory: /var/www/public
Output:================
Error Output:================
sh: exec: line 1: php /var/www/bin/console app:excel:witness: not found
What is wrong with my usage of the Process component?
Calling it like this doesn't work either:
$process = new Process(['/usr/local/bin/php', '/var/www/bin/console', 'app:excel:witness']);
this results in following error:
The command \"'/usr/local/bin/php' '/var/www/bin/console' 'app:excel:witness'\" failed.
Exit Code: ()
Working directory: /var/www/public
Output:
================
Error Output:
================
First, note that the Process component is not meant to run asynchronously after the parent process dies. So triggering async jobs to run during an API request is a not a good use case.
These two comments in the docs about running things asynchronously are very pertinent:
If a Response is sent before a child process had a chance to complete, the server process will be killed (depending on your OS). It means that your task will be stopped right away. Running an asynchronous process is not the same as running a process that survives its parent process.
If you want your process to survive the request/response cycle, you can take advantage of the kernel.terminate event, and run your command synchronously inside this event. Be aware that kernel.terminate is called only if you use PHP-FPM.
Beware also that if you do that, the said PHP-FPM process will not be available to serve any new request until the subprocess is finished. This means you can quickly block your FPM pool if you’re not careful enough. That is why it’s generally way better not to do any fancy things even after the request is sent, but to use a job queue instead.
If you want to run jobs asynchronously, just store the job "somewhere" (e.d a database, redis, a textfile, etc), and have a decoupled consumer go through the "pending jobs" and execute whatever you need without triggering the job within an API request.
This above is very easy to implement, but you could also just use Symfony Messenger that will do it for you. Dispatch messages on your API request, consume messages with your job queue consumer.
All this being said, your use of process is also failing because you are trying mixing sync and async methods.
Your second attempt at calling the command is at least successful in finding the executable, but since you call isSuccessful() before the job is done.
If you use start() (instead of run()), you cannot simply call isSuccessful() directly, because the job is not finished yet.
Here is how you would execute an async job (although again, this would very rarely be useful during an API request):
class ProcessCommand extends Command
{
protected static $defaultName = 'process_bg';
protected function execute(InputInterface $input, OutputInterface $output)
{
$phpBinaryFinder = new PhpExecutableFinder();
$pr = new Process([$phpBinaryFinder->find(), 'bin/console', 'bg']);
$pr->setWorkingDirectory(__DIR__ . '/../..');
$pr->start();
while ($pr->isRunning()) {
$output->write('.');
}
$output->writeln('');
if ( ! $pr->isSuccessful()) {
$output->writeln('Error!!!');
return self::FAILURE;
}
$output->writeln('Job finished');
return self::SUCCESS;
}
}
I like to use exec().
You'd need to add a couple of bits to the end of your command:
Use '2>&1' so the output has somewhere to go. From memory, this is important so that PHP isn't waiting for the output to be returned (or streamed or whatever).
Put '&' on the end to make the command run in the background.
Then it's a good idea to return a 202 (Accepted) rather than 200, because we don't yet know whether it was successful, as the command hasn't completed.
public function runMyCommandIntheBackground(string $projectDir): Response
exec("{ProjectDir}/bin/console your:command:name 2>&1 &");
return new Response('', Response::HTTP_ACCEPTED);
}
Now i'm doing some project use laravel framework. do i able to run Symfony Process function inside a queue jobs?
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
right now i want to run some commend using Symfony Process function for this
process = new Process("facebook-scraper --filename public/data/nintendo.csv --pages 5 nintendo");
if outside the queue. this code can run succesful. but when i want to make it run inside the queue jobs. it can't.
how do i able to run symfony Process function inside queue on jobs laravel.
I think the problem is the paths. Replace the --filename option value with the absolute path (from /):
$path = public_path('data/nintendo.csv');
$process = new Process("facebook-scraper --filename {$path} --pages 5 nintendo");
...
...
And try to use full path to executable (facebook-scraper).
You can use which to find it.
Example:
$ which facebook-scraper
/usr/bin/facebook-scraper
I am using Ubuntu 18.04 and I have implemented openFace library which works fine through terminal that takes two input parameter containing name of images
Terminal command
python /home/machine/openface/demos/compare_two_pic.py {/home/machine/openface/demos/images/5G5X0dnI5xkD.jpg,/home/machine/openface/demos/images/orange.jpg}
I just pasted this command in Symfony but i didn't get any result.
$process = new Process(['python2', '/home/machine/openface/demos/compare_two_pic.py {/home/machine/openface/demos/images/5G5X0dnI5xkD.jpg,/home/machine/openface/demos/images/orange.jpg}',$arg(optional)]);
$process->run();
echo "here".$process->getOutput();
// executes after the command finishes
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
echo $process->getOutput();
I make a web service to just call a function and check. Is this function working or not?
I have put this comment
$process = Process::fromShellCommandline('python2 /home/machine/openface/demos/compare_two_pic.py {/home/machine/openface/demos/images/5G5X0dnI5xkD.jpg,/home/machine/openface/demos/images/orange.jpg}');
Symfony: 4.1
PHP: 7.1
I have working websocket server using Ratchet. The websocket itself works fin. I can run it from the terminal using Symfony's commands
php bin/console app:websocket:execute
I'm having trouble getting around some of these issues:
You need to dedicate a terminal to running this command
Most webhosting services don't give you access to the terminal
I want admins to be able to toggle the websocket server on and off
Admins aren't required to know what a terminal is
For issue 1, I try to use this "detaching" cheat, but it doesn't solve issue 2:
php bin/console app:websocket:execute > /dev/null 2>&1 &
In order to tackle all four issues. I have tried using a process. but the issues with this approach is:
$process->run() - running a process with php bin/console always ends in a timeout
$process-start() - starting a process means it runs asynchronously, but it also means the process is terminated once the request ends, terminating my websocket server too.
Here is an example
$process = new Process("php bin/console");
$process->setWorkingDirectory(getcwd() . "/../");
$process->setTimeout(10);
$process->run(); // Stalls for 10 seconds, then throws timeout exception
$process-start(); // Doesn't stall, but terminates at end of request
// $process->run() ==== unreachable code
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
I have tried creating a console application, and run the command from there. but the same issues as a process apply here.
$application = new Application($this->kernel);
$application->setAutoExit(false);
$input = new ArrayInput(array(
'command' => 'app:websocket:execute'
));
try {
$ob = new BufferedOutput();
$application->run($input, $ob);
$output = $ob->fetch();
} catch (\Exception $e) {
return null;
}
As a final resort, I tried a bundle called DtcQueueBundle, because it mentions the following:
Ease of Use
Kickoff background tasks with a line of code or two
Easily add background worker services
Turn any code into background task with a few lines
So I did what they asked, created a worker and tried to run it as a "background task"
use App\Ratchet\ForumUpdater;
use Ratchet\Http\HttpServer;
use Ratchet\Server\IoServer;
use Ratchet\WebSocket\WsServer;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class SocketWorker extends \Dtc\QueueBundle\Model\Worker
{
public function execute()
{
$server = IoServer::factory(
new HttpServer(
new WsServer(
new ForumUpdater()
)
),
8080
);
$server->run();
return "Websocket started";
}
public function getName()
{
return "websocket-server";
}
}
Their docs are the absolute worst! I even tried to dig into their code to start jobs from inside my controller. But I couldn't get it to run in a detached manner.
Whatever happens, I believe my command isn't running because it highjacks my PHP thread. I would like to know, is it possible to detach this endless process? Is it even possible to run two PHP instances together? I would think so!
Thanks for any help, sorry for the long post
I am trying to run a .sh file that will import a excel file to my database. Both files are in same directory inside the public folder. For some reason the exec command isn't being executed or neither any error occurs.
.sh file colde:
IFS=,
while read column1
do
echo "SQL COMMAND GOES HERE"
done < file.csv | mysql --user='myusername' --password='mypassword' -D databasename;
echo "finish"
In my php file i have tried following:
$content = file_get_contents('folder_name/file_name.sh');
echo exec($content);
And:
shell_exec('sh /folder_name/file_name.sh');
Note: I can directly execute the sh file from gitbash however I want it to be done using function in Laravel controller. I'm using windows OS.
you can use Process Component of Symfony that is already in Laravel http://symfony.com/doc/current/components/process.html
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
$process = new Process('sh /folder_name/file_name.sh');
$process->run();
// executes after the command finishes
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
echo $process->getOutput();
All of these answers are outdated now, instead use (Symfony 4.2 or higher):
$process = Process::fromShellCommandline('/deploy.sh');
Or
$process = new Process(['/deploy.sh']);
https://symfony.com/doc/current/components/process.html
I know this is a little late but I can't add a comment (due to being a new member) but to fix the issue in Windows " 'sh' is not recognized as an internal or external command, operable program or batch file." I had to change the new process from:
$process = new Process('sh /folder_name/file_name.sh');
to use the following syntax:
$process = new Process('/folder_name/file_name.sh');
The only problem with is that when uploading to a Linux server it will need to be changed to call sh.
Hope this helps anyone who hit this issue when following the accepted answer in Windows.
In Symfony 5.2.0 that used by Laravel 8.x (same as current Symfony Version 6.0 used by Laravel 9.x), you need to specify the sh command and your argument, in your case:
use Symfony\Component\Process\Process;
$process = new Process(['/usr/bin/sh', 'folder_name/file_name.sh']);
$process->run();
The system will find folder_name/file_name.sh from your /public folder (if it executed from a url), if you want to use another working directory, specify that in the second Process parameter.
$process = new Process(['/usr/bin/sh', 'folder_name/file_name.sh'], '/var/www/app');
And the /usr/bin/sh sometimes have a different place for each user, but that is a default one. Type whereis sh to find it out.