I fork long running processes to the background. You don't need PCNTL functions for this.
In an import module which takes some minutes to run, I have a file "importworker.php"
<?php namespace ProcessWire;
include(__DIR__ . "/../../../index.php"); // bootstrapping PW
error_reporting(2); // setting error reporting
// ini_set('max_execution_time', 300); // 300 seconds = 5 minutes
wire('log')->save('productimport', "starting import: " . date('Y-m-d H:i:s'));
$importModule = wire('modules')->get("ProcessImportProducts");
$importModule->importController('start');
wire('log')->save('productimport', "Import finished: " . date('Y-m-d H:i:s'));
Then there is a method for forking the heavy work into the background
public function startImportWorker() {
$path = $this->config->paths->siteModules . "{$this->className}/";
$command = "php {$path}importworker.php";
$outputFile = "{$path}output.txt";
$pid = shell_exec(sprintf("%s > $outputFile 2>&1 & echo $!", $command));
return;
}
All output of the importworker script is piped to output.txt. So I can see what happens when the process is running in the background. Some methods in my module echo stuff so I can see it in output.txt.
Also for longer running loops in my module, I use the ini_set('max_execution_time', 300) method to prolong execution time.
And I unset variables along the way to take care of memory issues.
With some ajaxy JS, I get the contents of output.txt and show them inside a div#status in my module, so the user knows that there is sth going on.
var ProcessImportProducts = {
init: function() {
$('#startimport').on('click', function(e){
e.preventDefault();
$.get($(this).data('href'), function( data ) {
// console.log(data);
ProcessImportProducts.pollResults(0);
});
});
},
pollResults: function(timestamp) {
var statusUrl = '?getstatus=1';
var statusText = $('#status');
// var loader = $('.loader').clone();
if(!timestamp) statusText.html('');
$.ajax(
{
type: 'GET',
dataType: 'json',
url: statusUrl,
success: function(data){
// console.log(data);
// if file has changed append data to statusText
if(timestamp != data.timestamp ) statusText.html(data.message).append('<div class="loader"></div>');
// call the function again, this time with the timestamp we just got from server
var timeout = setTimeout(function() {
ProcessImportProducts.pollResults(data.timestamp);
}, 1000);
if(data.timestamp == 0) {
clearTimeout(timeout);
$('.loader').addClass('hide');
}
// scroll to bottom of status div
statusText.scrollTop(statusText.prop("scrollHeight"));
}
}
);
}
};
$(document).ready(function() {
ProcessImportProducts.init();
});
EDIT: heres the part of my ___execute() function, that returns the status stuff for the JS
if($this->config->ajax) {
if($this->input->start == 1){
$this->startImportWorker();
echo 1;
return;
}
if($this->input->getstatus == 1) $this->returnStatus();
} else {
// module output to screen
}
Here's a good read about running processes in the background: https://medium.com/async-php/multi-process-php-94a4e5a4be05
Hope that helps.