Run an asynchronous PHP task using Symfony Process
Asked Answered
F

3

12

For time-consuming tasks (email sending, image manipulation… you get the point), I want to run asynchronous PHP tasks.

It is quite easy on Linux, but I'm looking for a method that works on Windows too.

I want it to be simple, as it should be. No artillery, no SQL queueing, no again and again installing stuff… I just want to run a goddamn asynchronous task.

So I tried the Symfony Process Component. Problem is, running the task synchronously works fine, but when running it asynchronously it exits along the main script.

Is there a way to fix this?


composer require symfony/process

index.php

<?php
require './bootstrap.php';
$logFile = './log.txt';

file_put_contents($logFile, '');

append($logFile, 'script (A) : '.timestamp());

$process = new Process('php subscript.php');
$process->start(); // async, subscript exits prematurely…
//$process->run(); // sync, works fine

append($logFile, 'script (B) : '.timestamp());

subscript.php

<?php
require './bootstrap.php';
$logFile = './log.txt';

//ignore_user_abort(true); // doesn't solve issue…

append($logFile, 'subscript (A) : '.timestamp());
sleep(2);
append($logFile, 'subscript (B) : '.timestamp());

bootstrap.php

<?php
require './vendor/autoload.php';
class_alias('Symfony\Component\Process\Process', 'Process');

function append($file, $content) {
    file_put_contents($file, $content."\n", FILE_APPEND);
}

function timestamp() {
    list($usec, $sec) = explode(' ', microtime());
    return date('H:i:s', $sec) . ' ' . sprintf('%03d', floor($usec * 1000));
}

result

script (A) : 02:36:10 491
script (B) : 02:36:10 511
subscript (A) : 02:36:10 581
// subscript (B) is missing
Featureless answered 24/3, 2015 at 2:9 Comment(1)
In short: run commands in background using exec().Stearoptene
C
1

Main script must be waiting when async process will be completed. Try this code:

$process = new Process('php subscript.php');
$process->start();
do {
    $process->checkTimeout();
} while ($process->isRunning() && (sleep(1) !== false));
if (!$process->isSuccessful()) {
   throw new \Exception($process->getErrorOutput());
}
Christianchristiana answered 24/3, 2015 at 11:26 Comment(2)
this makes it basically similar to run how does this get up votesJeremiah
If main script must wait, then it's synchronous process. Your code is senseless, it will result in "max execution time" error. Dont get why people voting upStearoptene
C
1

If php supports fpm for Windows, you can listen to kernel.terminate event to provide all expensive tasks after response has been sent.

Service:

app.some_listener:
    class: SomeBundle\EventListener\SomeListener
    tags:
        - { name: kernel.event_listener, event: kernel.terminate, method: onKernelTerminate }

Listener:

<?php

namespace SomeBundle\EventListener;

use Symfony\Component\HttpKernel\Event\PostResponseEvent;

class SomeListener
{
    public function onKernelTerminate(PostResponseEvent $event)
    {
        // provide time consuming tasks here
    }
}
Corvus answered 21/1, 2016 at 11:3 Comment(1)
Thanks for your interest :) Though, I'm not using FastCGI and I was looking for a simple, portable solution. Apparently it's just not possible... I'll live with it.Featureless
C
0

Not the best solution, but:

$process = new Process('nohup php subscript.php &');
$process->start();
Corvus answered 20/1, 2016 at 16:1 Comment(1)
How does it differ form simple exec()?Stearoptene

© 2022 - 2024 — McMap. All rights reserved.