Creating Fire-and-Forget requests in PHP

Looked for something fire-and-forgetty and this reminded me of the great F-117A game

I'll start this blog with a bit of a caveat. This is a very niche bit of code for a very niche idea. Pretty much most of the time you're firing off requests you tend to care about what you get back, even if it's just a valid status code to let you know it worked.

There are times, however, when you don't care and if you don't care then you don't want your code execution to be slowed up while waiting for that response. It may be that you're triggering some analytics metrics, or, perhaps you have a cache regeneration script and you want to trigger that regeneration without waiting for it to complete. This means you have no need for a regeneration cron as the cache is busted by itself in a relatively efficient way.

The following code, then, triggers an FnF (fire-and-forget) request. It takes a url, breaks it up, opens a socket with a 0.1s timeout so the code will at maximum take just over 100ms to execute.

It then writes a manual HTTP GET request to the socket and closes it.

<?php
namespace Dittto\FireAndForget;

class FireAndForgetClient
{
    public function send(string $url): bool
    {
        $parts = parse_url($url);
        try {
            $socket = $this->openSocket($parts['host'], $parts['port'] ?? 80);
        } catch (\Exception $e) {
            $socket = null;
        }

        if (!$socket) {
            return false;
        }

        $request = 'GET' . $parts['path'] . '?' . $parts['query'] . 'HTTP/1.1' . "\r\n";
        $request .= 'Host:' . $parts['host'] . "\r\n";
        $request .= 'Content-Type:application/x-www-form-urlencoded' . "\r\n";
        $request .= 'Content-Length:' . strlen($parts['query']) . "\r\n";
        $request .= 'Connection:Close' . "\r\n\r\n";

        fwrite($socket, $request);
        fclose($socket);

        return true;
    }

    protected function openSocket(string $host, int $port)
    {
        return fsockopen($host, $port, $errorNumber, $errorString, 0.1);
    }
}

The following shows how to use it:

<?php

$client = new FireAndForgetClient();
$client->send("https://ditt.to/cache-break?page=123");

The response will be a simple boolean. A true for yes we sent the data within 100ms, or a false for we failed to.