Skip to main content
Version: 4.x

Multiple requests

Sending multiple requests and reading their responses (order preserved)​

<?php declare(strict_types=1);

namespace YourVendor\YourProject;

use hollodotme\FastCGI\Client;
use hollodotme\FastCGI\RequestContents\UrlEncodedFormData;
use hollodotme\FastCGI\Requests\PostRequest;
use hollodotme\FastCGI\SocketConnections\NetworkSocket;

$client = new Client();
$connection = new NetworkSocket('127.0.0.1', 9000);

$request1 = new PostRequest('/path/to/target/script.php', new UrlEncodedFormData(['key' => '1']));
$request2 = new PostRequest('/path/to/target/script.php', new UrlEncodedFormData(['key' => '2']));
$request3 = new PostRequest('/path/to/target/script.php', new UrlEncodedFormData(['key' => '3']));

$socketIds = [];

$socketIds[] = $client->sendAsyncRequest($connection, $request1);
$socketIds[] = $client->sendAsyncRequest($connection, $request2);
$socketIds[] = $client->sendAsyncRequest($connection, $request3);

echo 'Sent requests with IDs: ' . implode( ', ', $socketIds ) . "\n";

# Do something else here in the meanwhile

# Blocking call until all responses are received or read timed out
# Responses are read in same order the requests were sent
foreach ($client->readResponses(3000, ...$socketIds) as $response)
{
echo $response->getBody() . "\n";
}
# prints
1
2
3

See it in action​

The three requests run in parallel, but readResponses() returns their responses in the order the requests were sent. Switch to readReadyResponses() to compare it with the reactive way below:

1/8Create the client, a connection to PHP-FPM and three requests for script.php.

Your PHP scriptusing the FastCGI Clientpreparing the requestPHP-FPMworker #1idlescript.php · sleep(3)worker #2idlescript.php · sleep(1)worker #3idlescript.php · sleep(2)
0.0 s
  • your script works
  • your script waits (blocked)
  • on the way over the socket
  • the script runs in PHP-FPM
  • response ready, not yet read
Try it: change the example
Read the responses
multiple.php
$client = new Client();
$connection = new NetworkSocket('127.0.0.1', 9000);
$script = '/path/to/target/script.php';

$request1 = new PostRequest($script, new UrlEncodedFormData(['key' => '1', 'sleep' => 3]));
$request2 = new PostRequest($script, new UrlEncodedFormData(['key' => '2', 'sleep' => 1]));
$request3 = new PostRequest($script, new UrlEncodedFormData(['key' => '3', 'sleep' => 2]));

$socketIds = [];
$socketIds[] = $client->sendAsyncRequest($connection, $request1);
$socketIds[] = $client->sendAsyncRequest($connection, $request2);
$socketIds[] = $client->sendAsyncRequest($connection, $request3);

echo 'Sent requests with IDs: ' . implode(', ', $socketIds) . "\n";

foreach ($client->readResponses(3000, ...$socketIds) as $response)
{
echo $response->getBody() . "\n";
}
$ php multiple.php

Sending multiple requests and reading their responses (reactive)​

<?php declare(strict_types=1);

namespace YourVendor\YourProject;

use hollodotme\FastCGI\Client;
use hollodotme\FastCGI\RequestContents\UrlEncodedFormData;
use hollodotme\FastCGI\Requests\PostRequest;
use hollodotme\FastCGI\SocketConnections\NetworkSocket;

$client = new Client();
$connection = new NetworkSocket('127.0.0.1', 9000);

$request1 = new PostRequest('/path/to/target/script.php', new UrlEncodedFormData(['key' => '1', 'sleep' => 3]));
$request2 = new PostRequest('/path/to/target/script.php', new UrlEncodedFormData(['key' => '2', 'sleep' => 2]));
$request3 = new PostRequest('/path/to/target/script.php', new UrlEncodedFormData(['key' => '3', 'sleep' => 1]));

$socketIds = [];

$socketIds[] = $client->sendAsyncRequest($connection, $request1);
$socketIds[] = $client->sendAsyncRequest($connection, $request2);
$socketIds[] = $client->sendAsyncRequest($connection, $request3);

echo 'Sent requests with IDs: ' . implode( ', ', $socketIds ) . "\n";

# Do something else here in the meanwhile

# Loop until all responses were received
while ( $client->hasUnhandledResponses() )
{
# read all ready responses
foreach ( $client->readReadyResponses( 3000 ) as $response )
{
echo $response->getBody() . "\n";
}

echo '.';
}

# ... is the same as

while ( $client->hasUnhandledResponses() )
{
$readySocketIds = $client->getSocketIdsHavingResponse();

# read all ready responses
foreach ( $client->readResponses( 3000, ...$readySocketIds ) as $response )
{
echo $response->getBody() . "\n";
}

echo '.';
}

# ... is the same as

while ( $client->hasUnhandledResponses() )
{
$readySocketIds = $client->getSocketIdsHavingResponse();

# read all ready responses
foreach ($readySocketIds as $socketId)
{
$response = $client->readResponse($socketId, 3000);
echo $response->getBody() . "\n";
}

echo '.';
}
# prints
...............................................3
...............................................2
...............................................1

See it in action​

readReadyResponses() returns each response as soon as it is ready, so the fastest request is printed first:

1/9Create the client, a connection to PHP-FPM and three requests for script.php.

Your PHP scriptusing the FastCGI Clientpreparing the requestPHP-FPMworker #1idlescript.php · sleep(3)worker #2idlescript.php · sleep(1)worker #3idlescript.php · sleep(2)
0.0 s
  • your script works
  • your script polls
  • on the way over the socket
  • the script runs in PHP-FPM
Try it: change the example
Read the responses
multiple.php
$client = new Client();
$connection = new NetworkSocket('127.0.0.1', 9000);
$script = '/path/to/target/script.php';

$request1 = new PostRequest($script, new UrlEncodedFormData(['key' => '1', 'sleep' => 3]));
$request2 = new PostRequest($script, new UrlEncodedFormData(['key' => '2', 'sleep' => 1]));
$request3 = new PostRequest($script, new UrlEncodedFormData(['key' => '3', 'sleep' => 2]));

$socketIds = [];
$socketIds[] = $client->sendAsyncRequest($connection, $request1);
$socketIds[] = $client->sendAsyncRequest($connection, $request2);
$socketIds[] = $client->sendAsyncRequest($connection, $request3);

echo 'Sent requests with IDs: ' . implode(', ', $socketIds) . "\n";

while ($client->hasUnhandledResponses())
{
foreach ($client->readReadyResponses(3000) as $response)
{
echo $response->getBody() . "\n";
}

echo '.';
}
$ php multiple.php

Sending multiple requests and notifying callbacks (reactive)​

<?php declare(strict_types=1);

namespace YourVendor\YourProject;

use hollodotme\FastCGI\Client;
use hollodotme\FastCGI\RequestContents\UrlEncodedFormData;
use hollodotme\FastCGI\Requests\PostRequest;
use hollodotme\FastCGI\Interfaces\ProvidesResponseData;
use hollodotme\FastCGI\SocketConnections\NetworkSocket;
use Throwable;

$client = new Client();
$connection = new NetworkSocket('127.0.0.1', 9000);

$responseCallback = static function( ProvidesResponseData $response )
{
echo $response->getBody();
};

$failureCallback = static function ( Throwable $throwable )
{
echo $throwable->getMessage();
};

$request1 = new PostRequest('/path/to/target/script.php', new UrlEncodedFormData(['key' => '1', 'sleep' => 3]));
$request2 = new PostRequest('/path/to/target/script.php', new UrlEncodedFormData(['key' => '2', 'sleep' => 2]));
$request3 = new PostRequest('/path/to/target/script.php', new UrlEncodedFormData(['key' => '3', 'sleep' => 1]));

$request1->addResponseCallbacks($responseCallback);
$request1->addFailureCallbacks($failureCallback);

$request2->addResponseCallbacks($responseCallback);
$request2->addFailureCallbacks($failureCallback);

$request3->addResponseCallbacks($responseCallback);
$request3->addFailureCallbacks($failureCallback);

$socketIds = [];

$socketIds[] = $client->sendAsyncRequest($connection, $request1);
$socketIds[] = $client->sendAsyncRequest($connection, $request2);
$socketIds[] = $client->sendAsyncRequest($connection, $request3);

echo 'Sent requests with IDs: ' . implode( ', ', $socketIds ) . "\n";

# Do something else here in the meanwhile

# Blocking call until all responses were received and all callbacks notified
$client->waitForResponses(3000);

# ... is the same as

while ( $client->hasUnhandledResponses() )
{
$client->handleReadyResponses(3000);
}

# ... is the same as

while ( $client->hasUnhandledResponses() )
{
$readySocketIds = $client->getSocketIdsHavingResponse();

# read all ready responses
foreach ($readySocketIds as $socketId)
{
$client->handleResponse($socketId, 3000);
}
}

# ... is the same as

while ( $client->hasUnhandledResponses() )
{
$readySocketIds = $client->getSocketIdsHavingResponse();

$client->handleResponses(3000, ...$readySocketIds);
}
# prints
3
2
1

See it in action​

waitForResponses() calls the response callback of each request as soon as its response is there. Lower the timeout to see the failure callback of a request that takes too long:

1/6For each request, your script registers a response callback and a failure callback, then sends it. The requests run in parallel.

Your PHP scriptusing the FastCGI Clientpreparing the requestPHP-FPMworker #1idlescript.php · sleep(3)worker #2idlescript.php · sleep(1)worker #3idlescript.php · sleep(2)
0.0 s
  • your script works
  • your script waits (blocked)
  • a callback runs
  • on the way over the socket
  • the script runs in PHP-FPM
Try it: change the example
callbacks.php
$client = new Client();
$connection = new NetworkSocket('127.0.0.1', 9000);

foreach ([1 => 3, 2 => 1, 3 => 2] as $key => $sleep)
{
$request = new PostRequest(
'/path/to/target/script.php',
new UrlEncodedFormData(['key' => $key, 'sleep' => $sleep])
);
$request->addResponseCallbacks(
static fn(ProvidesResponseData $response) => print $response->getBody() . "\n"
);
$request->addFailureCallbacks(
static fn(Throwable $e) => print "Request #{$key} failed: {$e->getMessage()}\n"
);

$client->sendAsyncRequest($connection, $request);
}

$client->waitForResponses(2500);
$ php callbacks.php

Reading output buffer from worker script using pass through callbacks​

It may be useful to see the progression of a requested script by having access to the flushed output of that script. The php.ini default output buffering for php-fpm is 4096 bytes and is (hard-coded) disabled for CLI mode. (See documentation) Calling ob_implicit_flush() causes every call to echo or print to immediately be flushed.

The callee script could look like this:

<?php declare(strict_types=1);

ob_implicit_flush();

function show( string $string )
{
echo $string . str_repeat( "\r", 4096 - strlen( $string ) ) . "\n";
sleep( 1 );
}

show( 'One' );
show( 'Two' );
show( 'Three' );

error_log("Oh oh!\n");

echo 'End';

The caller than could look like this:

<?php declare(strict_types=1);

namespace YourVendor\YourProject;

use hollodotme\FastCGI\Client;
use hollodotme\FastCGI\Requests\GetRequest;
use hollodotme\FastCGI\SocketConnections\NetworkSocket;

$client = new Client();
$connection = new NetworkSocket('127.0.0.1', 9000);

# Each call passes either output (STDOUT) or error output (STDERR) of the script
$passThroughCallback = static function( string $outputBuffer, string $errorBuffer )
{
if ( '' !== $outputBuffer )
{
echo 'Output: ' . $outputBuffer;
}

if ( '' !== $errorBuffer )
{
echo 'Error: ' . $errorBuffer;
}
};

$request = new GetRequest('/path/to/target/script.php');
$request->addPassThroughCallbacks( $passThroughCallback );

$client->sendAsyncRequest($connection, $request);
$client->waitForResponses();
# prints immediately
Output: Content-type: text/html; charset=UTF-8

One
# sleeps 1 sec
Output: Two
# sleeps 1 sec
Output: Three
# sleeps 1 sec
Error: Oh oh!
Output: End

See it in action​

PHP-FPM sends flushed output right away. Compare a pass-through callback with reading the complete response:

1/8Create a request for a script that flushes its output while it runs, and register a pass-through callback.

Your PHP scriptusing the FastCGI Clientpreparing the requestPHP-FPMworker #1idleflushes output
0.0 s
  • your script works
  • your script waits (blocked)
  • a callback runs
  • on the way over the socket
  • the script runs in PHP-FPM
Try it: change the example
Receive the output
pass-through.php
$client = new Client();
$connection = new NetworkSocket('127.0.0.1', 9000);
$request = new GetRequest('/path/to/target/script.php');

$request->addPassThroughCallbacks(
static function (string $outputBuffer, string $errorBuffer): void
{
if ($outputBuffer !== '') { echo 'Output: ' . $outputBuffer; }
if ($errorBuffer !== '') { echo 'Error: ' . $errorBuffer; }
}
);

$client->sendAsyncRequest($connection, $request);
$client->waitForResponses();
$ php pass-through.php