Maybe your question isn’t about threads themselves, but this question will be accessed by people looking for solutions about threads in php.
First of all, it is important to understand the definition of what a threads is:
In computer science, Thread is a small program that works
as a subsystem, being a way of a process self-divide into
two or more tasks.
This is a very complex procedure to perform with PHP, since parallelism is not usually the focus of PHP programmers, but it is not impossible to do. And there are still several approaches, some simpler and safer others not so much. See below for some methods to perform or simulate threads in php.
Method 1: Create a call to your own server
The first, simpler approach is defined in this answer: /a/217559/7130
However this method is not an asynchronous execution and would be less performative than calling a method directly within the same process.
Method 2: Cronjobs
Another approach, also very simple is cron or cronjob, in windows task scheduler.
The technique consists of creating a routine that is read from time to time behind some "task" that is scheduled, you can store this list of tasks to be performed by the script in a database or file with the parameters.
It’s not very performative either, but it’s simpler and easier to implement, as long as you have access to a task scheduling feature.
Method 3: PHP Process Control (PCNTL )
Well, all of the above methods cannot be executed asynchronously, separating the php process into 2. Maybe this is the only way to thread a class method.
Needing to create a solution that did just that, where it was possible to initialize a routine/method that ran in the background while I already gave the return to the user and then just checked the progress and log of this task.
The result was the elaboration of an abstract class to be extended by sub-classes with specific tasks, in the example below in the gist link I do the deletion of files in asynchronous form and record logs during the process
See part of the method below:
/**
* Executa a tarefa com os parâmetros pré-definidos.
* @param mixed $parameters
*/
final public function run($parameters) {
try {
//Não tenho certeza se há necessidade,
//porém essas funções adicionam um controle para que o
//processo continue à ser execudado mesmo que o usuário abandone a tela.
ignore_user_abort(true);
set_time_limit(0);
// Inicializo o signal Handler
pcntl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork(); //Cria um novo processo, se criado com sucesso, o código executado daqui para baixo será executado em outra thread.
if ($pid == -1) {//Se o retorno for -1 significa que não foi possível.
throw new \RuntimeException("Could not start a fork process.");
} else if ($pid) {
//Se o valor for diferente de zero Significa que ainda está rodando no pai
// pcntl_waitpid($pid, $exitcode, WNOHANG); // prevents zombie processes
// $this->log("Thread Still the same");
return;
} else {
ignore_user_abort();
$sid = posix_setsid();
self::$shmId = shm_attach((int) (ftok(self::$file, 'A') . self::$line), self::SHARED_MEMORY_SIZE);
$this->var_key_fork_pid = $this->alocatesharedMemory($sid, 112105100); //112105100
shm_put_var(self::$shmId, $this->var_key_status, 'RUNNING');
// $this->log("SubTask" . $sid);
// $this->log("SubPid" . $this->var_key_fork_pid);
// $this->log("Substatus" . $this->var_key_status);
$this->onPreExecute($parameters);
// $this->log("SessionDelay: " . session_id());
try {
$result = $this->doInBackground($parameters);
} catch (\Exception $e) {
$this->log("[ErrExec:" . $e->getCode() . "] " . $e->getMessage());
$this->cancel($e->getMessage());
}
$this->onPostExecute($result);
//Ao finalizar, emitir o sinal de finalizado e apagar a memória compartilhada alocada.
if (@shm_has_var(self::$shmId, $this->var_key_fork_pid)) {
shm_put_var(self::$shmId, $this->var_key_fork_pid, null);
shm_put_var(self::$shmId, $this->var_key_status, 'FINISHED');
shm_remove(self::$shmId);
gc_collect_cycles();
posix_kill(getmypid(), SIGKILL);
}
}
} catch (\Exception $e) {
$msg = "[Err:" . $e->getCode() . "] " . $e->getMessage();
$this->log($msg);
self::onError($msg,$e);
}
}
Explanation:
Thread separation happens on the line where I use the following function:
$pid = pcntl_fork();
this function can return -1 if it fails to create the process branch (Fork). The value other than 0 is the block that will continue running in the main process the other value is the process id "child" or Fork, this block will run in a completely separate process.
To make the communication between the 2 I use the shared memory, since they are 2 separate processes and both do not have access to the same variables (not even for $_SESSION).
So inside your example could be something like this:
if($pid == 0){ //Se estiver na thread principal
SolicitarOpcoesPagamento();
}else if($pid != -1){ //Se tiver no fork
ConsultarOpcoesPagamento();
}
Complete Example: https://gist.github.com/LeonanCarvalho/62c6fe0b62db8a478f502f84c5734c83
In my example you would need to model a class for each task, these classes should inherit from the abstract class and implement some methods.
It consists of using the pcntl functions, which is the official php extension for process control, mainly the function pcntl_fork to create a separation from the main process.
It was also necessary to use some auxiliary extensions such as the POSIX and "Semaphore, Shared Memory and IPC".
Result of the above example:
One problem known to me is that it is not possible to use PDO instances in Singleton mode between 2 processes, you need in the branched process ("Fork") destroy the Singleton instances of PDO and then create a new PDO connection.
This method is in use for 1 year, it is responsible for importing files into the system, instead of occupying the connection with the server I just receive the data and then process it in the background on the server, freeing the user connection so that it continues to work on the system.
Method 4: Pthreads
Although it already exists in php 5.6, I strongly recommend using it only in PHP7, it has been completely refactored in PHP7 and is safer to use. This will require a ZTS (Zend Thread Safety) version of PHP 7.x installed, along with pthreads v3 installed. So far I have not found link to download this version ready, but you can still compile it https://www.sitepoint.com/install-php-extensions-source/#Installing-a-third-party-Extension ( Link in English).
The official documentation is: http://php.net/manual/en/book.pthreads.php
But the good examples are here: https://www.sitepoint.com/parallel-programming-pthreads-php-fundamentals/ (link in English)
In the specific case of the question, I do not know if these last 2 methods would meet, there is no way you can be sure that the ConsultarOpcoesPagamento
will end in parallel with the ConsultarOpcoesPagamento
, but it would look something like this:
class OpcoesPagamento extends Threaded
{
public $codigo;
public $opcoes;
public function Solicitar($cpfOuCnpj, $telefone, $email, $nrParcelas, $dataPagamento)
{
/** **/
$this->codigo = md5($cpfOuCnpj);
}
public function Consultar()
{
$this->opcoes = array("status" => "pendente","opcoes"=> array());
}
}
$OpcoesPagamento = new OpcoesPagamento;
$thread = new class($OpcoesPagamento) extends Thread {
private $task;
public function __construct(Threaded $task)
{
$this->task = $task;
}
public function run()
{
$this->task->Solicitar();
$this->task->Consultar();
}
};
$thread->start() && $thread->join();
var_dump($task->codigo);
var_dump($task->opcoes);
Another relevant project that may be of interest is this one with plenty of examples: https://github.com/krakjoe/pthreads, also requires PHP7. I couldn’t find a linux compatible version.
In short, there are several ways to perform a Thread in a class method, some simulating and others with specific components, either asynchronous or synchronous. Which you use should be based on your requirements and your infrastructure, since they need a little advanced knowledge to implement any of these techniques.
You need to return immediately or consult first?
– Rafael Nery
I need to return the tracking code and run the Consultingparallel payment function
– Gabriel Rodrigues
In this case you need to create two threads and synchronize the two correct?
– Celso Marigo Jr
I’m not able to visualize the use of threads for this. The only way I’m able to see is if you call
SolicitarOpcoesPagamento()
andConsultarOpcoesPagamento()
separately and independently of each other. But as you said, this does not occur.– Don't Panic
Related: https://answall.com/a/207984/3635, perhaps the part that interests you is where I spoke: Where Thread would be interesting, still yes recommend you read everything to understand some details.
– Guilherme Nascimento
@Guilhermenascimento thanks for the recommendation!
– Gabriel Rodrigues