Most react-PHP code I’ve seen in the open that is not extension-based (doesn’t extend the PHP core language) uses some kind Event-Loop, something similar to how many technologies or languages(I.e. JavaScript) implement concurrency that doesn’t support threads.

And historically on single-core CPU’s threads practically transformed into loops underneath. But the main idea is that in many languages, an async behavior is very easily achieved, maybe you need a keyword, maybe you need a special class, but in general, it is simple.

Now if you want the easiest way to do something asynchronous in PHP it will be a little costly, you can do that by spawning a new PHP process directly from PHP.

The problem with this approach is that implicitly the process that you spawn it’s tied to the PHP parent process so, that means even if you have asynchronicity (you can execute code in the parent and the child process without blocking each other) the parent must still wait for the child to finish.

In an *nix environment you can use the shell operator & to create the process in background (not being tied to any parent), and in Windows you can use a special option for the proc_open function that was added very recently(Version 7.4.4. 19 Mar 2020)

Now let’s see a practical example of how we can use this in Laravel(note that for a mid to larger project we should stick to Laravel Queues).

First, let’s create a new command ex: SendConactMailNotification which should reside in: {our_project}/app/Console/Commands:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Log;

class SendConactMailNotification extends Command {

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'send:contact-email-notification {idMsg}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send an email notification for the contact message that was sent.';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct() {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle() {

        Log::channel('single')->info("\{\{EMAIL:  Tring to send email notification  \}\}");
        $id = $this->argument('idMsg');

        if (!is_numeric($id)) {
            return;
        }
        try {
            $db_msg = \App\Models\ContactMsgModel::find($id);
        } catch (Exception $ex) {
            return;
        }
        if ($db_msg->mail_sent) {
            return;
        }
        try {
            Mail::html('Message from ' . $db_msg->name . ' [ ' . $db_msg->email . ' ] Msg:<br>' . $db_msg->message, function ($message) {
                $message->from('website@flashsoft.ro', 'Flashsoft.ro - Website Contact');
                $message->to('andrei@flashsoft.ro');
                $message->subject('Flashsoft.ro Contact Msg');
            });
            $db_msg->mail_sent = (bool) Mail::failures();
            $db_msg->save();
        } catch (\Exception $e) {
            Log::channel('single')->error("\{\{EMAIL EXCEPTION: {$e->getMessage()}  \}\}");
        }
    }

}

And now, here is how we would invoke this command in an asynchronous way in some Controller of our own:

// ...
// ... CODE ABOVE

                $execUnix = true;
                $command = implode( " ", ['php', '../artisan', 'send:contact-email-notification', $db_msg->id, '> /dev/null &']);
                if( PHP_VERSION_ID >= 70404){
                    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){
                        $nullstream = fopen('NUL', 'c');
                                    $desc =  [
                                                ['pipe', 'r'],
                                                $nullstream,
                                                $nullstream,
                                            ];
                     proc_open($command, $desc, $pipes, null, null, ["create_new_console" => true, "suppress_errors" => true]);
                     $execUnix = false;
                    } 
                }elseif($execUnix === true){
                    exec($command);
                }

// ...
// ... CODE Below

Now if you don’t use PHP on windows(and you also will not need above PHP v 7.4.4) you can do just this:

// ...
// ... CODE ABOVE

$command = implode( " ", ['php', '../artisan', 'send:contact-email-notification', $db_msg->id, '> /dev/null &']);
  
exec($command);

// ...
// ... CODE Below

Be aware that Process component from Symfony (that Laravel also uses) does not support "create_new_console" argument, so it can’t be used for windows instead of directly calling proc_open, I made a request for adding support on Github maybe they will add it.

Later Edit:

Symfony merged my PR per this issue: https://github.com/symfony/symfony/issues/36583