Skip to main content
Version: 4.x

Requests

Requests are defined by the following interface:

<?php declare(strict_types=1);

namespace hollodotme\FastCGI\Interfaces;

interface ProvidesRequestData
{
public function getGatewayInterface() : string;

public function getRequestMethod() : string;

public function getScriptFilename() : string;

public function getServerSoftware() : string;

public function getRemoteAddress() : string;

public function getRemotePort() : int;

public function getServerAddress() : string;

public function getServerPort() : int;

public function getServerName() : string;

public function getServerProtocol() : string;

public function getContentType() : string;

public function getContentLength() : int;

public function getContent() : ?ComposesRequestContent;

public function getCustomVars() : array;

public function getParams() : array;

public function getRequestUri() : string;

public function getResponseCallbacks() : array;

public function getFailureCallbacks() : array;

public function getPassThroughCallbacks() : array;
}

Alongside with this interface, this package provides an abstract request class, containing default values to make the API more handy for you and 5 request method implementations of this abstract class:

  • hollodotme\FastCGI\Requests\GetRequest
  • hollodotme\FastCGI\Requests\PostRequest
  • hollodotme\FastCGI\Requests\PutRequest
  • hollodotme\FastCGI\Requests\PatchRequest
  • hollodotme\FastCGI\Requests\DeleteRequest

So you can either implement the interface, inherit from the abstract class or simply use one of the 5 implementations.

Default values​

The abstract request class defines several default values which you can optionally overwrite:

KeyDefault valueComment
GATEWAY_INTERFACEFastCGI/1.0Cannot be overwritten, because this is the only supported version of the client.
SERVER_SOFTWAREhollodotme/fast-cgi-client
REMOTE_ADDR192.168.0.1
REMOTE_PORT9985
SERVER_ADDR127.0.0.1
SERVER_PORT80
SERVER_NAMElocalhost
SERVER_PROTOCOLHTTP/1.1You can use the public class constants in hollodotme\FastCGI\Constants\ServerProtocol
CONTENT_TYPEapplication/x-www-form-urlencodedIs set to the content type of the request content, if the request has one
REQUEST_URI(empty string)
CUSTOM_VARSempty arrayYou can use the methods setCustomVar, addCustomVars to add own key-value pairs

Each of these values has a setter in the abstract request class: setServerSoftware(), setRemoteAddress(), setRemotePort(), setServerAddress(), setServerPort(), setServerName(), setServerProtocol(), setContentType() and setRequestUri(). Custom variables can be removed again with resetCustomVars().

All parameters of a request are sent in FastCGI records of at most 65535 bytes. If they are longer in total, they are split into multiple records between two parameters, so php-fpm can decode them. A single parameter (name and value) that is longer than 65535 bytes is split as well, which the FastCGI specification allows, but php-fpm does not accept such a parameter and closes the connection. The client then throws a ReadFailedException.

Please note: setContent() overwrites the content type of the request. If you need a content type that differs from the one of the request content, call setContentType() after the content was set.

Under the hood: FastCGI records​

You never deal with the FastCGI protocol yourself, but it helps to know how a request travels to PHP-FPM: as a sequence of records with at most 65,535 bytes of content each. Change the size of the body to see how it is split:

1/10On the socket, a request is a sequence of records. Each record has an 8-byte header with its type, the request ID and the length of its content — at most 65,535 bytes.

Your PHP scriptusing the FastCGI Clientpreparing the requestPHP-FPMworker #1idleruns the script
0.0 s
  • your script works
  • your script waits (blocked)
  • on the way over the socket
  • the script runs in PHP-FPM
Try it: change the example
records.php
$client = new Client();
$connection = new NetworkSocket('127.0.0.1', 9000);
$request = new PostRequest(
'/path/to/target/script.php',
new PlainText(str_repeat('x', 160 * 1024))
);

$response = $client->sendRequest($connection, $request);

echo $response->getBody();
FastCGI records on the socket

Query parameters​

If the target script expects query parameters ($_GET), you can pass them as an array to any request. There is no need to compose a query string or to set the QUERY_STRING variable on your own.

<?php declare(strict_types=1);

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 );

$request = new GetRequest( '/path/to/target/script.php' );
$request->setRequestUri( '/some/path' );
$request->setQueryParams(
[
'key' => 'value',
'list' => ['one', 'two'],
]
);

$response = $client->sendRequest( $connection, $request );

This example produces the following values at the target script:

# $_GET
Array
(
[key] => value
[list] => Array
(
[0] => one
[1] => two
)

)

# $_SERVER['REQUEST_URI']
/some/path?key=value&list%5B0%5D=one&list%5B1%5D=two

# $_SERVER['QUERY_STRING']
key=value&list%5B0%5D=one&list%5B1%5D=two

Please note:

  • The query parameters are encoded according to RFC 3986.
  • If the request URI already contains a query string, the query parameters are appended to it.
  • Query parameters take precedence over a QUERY_STRING that was set as a custom variable.
  • If you don't set query parameters, no QUERY_STRING is added and the request URI is sent as it is.

Request contents​

In order to make the composition of different request content types easier there are classes covering the typical content types:

The content of a request is optional. You can pass it as the second argument to the constructor of a request or set it later with setContent(). Both ways also set the content type and content length of the request:

$request = new PostRequest( '/path/to/target/script.php', new JsonData( ['key' => 'value'] ) );

# ... is the same as

$request = new PostRequest( '/path/to/target/script.php' );
$request->setContent( new JsonData( ['key' => 'value'] ) );

The content length and the content type are determined from the content object when the request is sent. So you can still change the content object after it was passed to the request, e.g. add files to a MultipartFormData object. A content type that was set with setContentType() takes precedence over the content type of the content object.

You can create your own request content type composer by implementing the following interface:

ComposesRequestContent

interface ComposesRequestContent
{
public function getContentType() : string;

public function getContent() : string;
}

Request content example: URL encoded form data (application/x-www-form-urlencoded)​

<?php declare(strict_types=1);

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

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

$urlEncodedContent = new UrlEncodedFormData(
[
'nested' => [
'one',
'two' => 'value2',
'three' => [
'value3',
],
],
]
);

$postRequest = new PostRequest( '/path/to/target/script.php', $urlEncodedContent );

$response = $client->sendRequest( $connection, $postRequest );

This example produces the following $_POST array at the target script:

Array
(
[nested] => Array
(
[0] => one
[two] => value2
[three] => Array
(
[0] => value3
)

)
)

Request content example: multipart form data (multipart/form-data)​

Multipart form-data can be used to transfer any binary data as files to the target script just like a file upload in a browser does.

PLEASE NOTE: Multipart form-data content type works with POST requests only.

The MIME type of each file is detected using the fileinfo extension, which is required by this library. Files whose type cannot be determined are sent as application/octet-stream.

<?php declare(strict_types=1);

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

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

$multipartContent = new MultipartFormData(
# POST data
[
'simple' => 'value',
'nested[]' => 'one',
'nested[two]' => 'value2',
'nested[three][]' => 'value3',
],
# FILES
[
'file1' => __FILE__,
'files[1]' => __FILE__,
'files[three]' => __FILE__,
]
);

$postRequest = new PostRequest( '/path/to/target/script.php', $multipartContent );

$response = $client->sendRequest( $connection, $postRequest );

This example produces the following $_POST and $_FILES array at the target script:

# $_POST
Array
(
[simple] => value
[nested] => Array
(
[0] => one
[two] => value2
[three] => Array
(
[0] => value3
)

)

)

# $_FILES
Array
(
[file1] => Array
(
[name] => multipart.php
[type] => application/octet-stream
[tmp_name] => /tmp/phpiIdCNM
[error] => 0
[size] => 1086
)

[files] => Array
(
[name] => Array
(
[1] => multipart.php
[three] => multipart.php
)

[type] => Array
(
[1] => application/octet-stream
[three] => application/octet-stream
)

[tmp_name] => Array
(
[1] => /tmp/phpAjHINL
[three] => /tmp/phpicAmjN
)

[error] => Array
(
[1] => 0
[three] => 0
)

[size] => Array
(
[1] => 1086
[three] => 1086
)

)

)

Request content example: JSON encoded data (application/json)​

<?php declare(strict_types=1);

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

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

$jsonContent = new JsonData(
[
'nested' => [
'one',
'two' => 'value2',
'three' => [
'value3',
],
],
]
);

$postRequest = new PostRequest( '/path/to/target/script.php', $jsonContent );

$response = $client->sendRequest( $connection, $postRequest );

This example produces the following content for php://input at the target script:

{
"nested": {
"0": "one",
"two": "value2",
"three": [
"value3"
]
}
}