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 script works
- your script waits (blocked)
- on the way over the socket
- the script runs in PHP-FPM
- response ready, not yet read
$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";
}
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 script works
- your script polls
- on the way over the socket
- the script runs in PHP-FPM
$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 '.';
}
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 script works
- your script waits (blocked)
- a callback runs
- on the way over the socket
- the script runs in PHP-FPM
$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);
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 script works
- your script waits (blocked)
- a callback runs
- on the way over the socket
- the script runs in PHP-FPM
$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();