{"id":13494005,"url":"https://github.com/hollodotme/fast-cgi-client","last_synced_at":"2025-05-15T02:05:41.452Z","repository":{"id":41322269,"uuid":"77831465","full_name":"hollodotme/fast-cgi-client","owner":"hollodotme","description":"A PHP fast CGI client for sending requests (a)synchronously to PHP-FPM","archived":false,"fork":false,"pushed_at":"2024-05-02T13:42:29.000Z","size":760,"stargazers_count":556,"open_issues_count":14,"forks_count":34,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-04-25T20:02:22.157Z","etag":null,"topics":["fastcgi","loop","network-socket","php","php-fpm","reactive","request","response","unix-domain-socket"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hollodotme.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-01-02T12:19:29.000Z","updated_at":"2025-02-26T15:21:33.000Z","dependencies_parsed_at":"2024-01-16T09:53:07.458Z","dependency_job_id":"a6f468f7-e697-42ad-b1c1-c5544a4c3312","html_url":"https://github.com/hollodotme/fast-cgi-client","commit_stats":{"total_commits":352,"total_committers":10,"mean_commits":35.2,"dds":"0.025568181818181768","last_synced_commit":"2116cd896dc131df48d3964906c034f388d0b672"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollodotme%2Ffast-cgi-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollodotme%2Ffast-cgi-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollodotme%2Ffast-cgi-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollodotme%2Ffast-cgi-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hollodotme","download_url":"https://codeload.github.com/hollodotme/fast-cgi-client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254259369,"owners_count":22040819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastcgi","loop","network-socket","php","php-fpm","reactive","request","response","unix-domain-socket"],"created_at":"2024-07-31T19:01:20.825Z","updated_at":"2025-05-15T02:05:41.432Z","avatar_url":"https://github.com/hollodotme.png","language":"PHP","funding_links":[],"categories":["PHP","Table of Contents"],"sub_categories":["Event","Library"],"readme":"![FastCGI Client CI PHP 7.1 - 8.1](https://github.com/hollodotme/fast-cgi-client/workflows/FastCGI%20Client%20CI%20PHP%207.1%20-%208.1/badge.svg)\n[![Latest Stable Version](https://poser.pugx.org/hollodotme/fast-cgi-client/v/stable)](https://packagist.org/packages/hollodotme/fast-cgi-client)\n[![Total Downloads](https://poser.pugx.org/hollodotme/fast-cgi-client/downloads)](https://packagist.org/packages/hollodotme/fast-cgi-client)\n\n# Fast CGI Client\n\nA PHP fast CGI client to send requests (a)synchronously to PHP-FPM using\nthe [FastCGI Protocol](http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html).\n\nThis library is based on the work\nof [Pierrick Charron](https://github.com/adoy)'s [PHP-FastCGI-Client](https://github.com/adoy/PHP-FastCGI-Client/)\nand was ported and modernized to latest PHP versions, extended with some features for handling multiple requests (in\nloops) and unit and integration tests as well.\n\n---\n\nThis is the documentation of the latest release.\n\nPlease have a look at the [backwards incompatible changes (BC breaks) in the changelog](./CHANGELOG.md).\n\nPlease see the following links for earlier releases:\n\n* PHP \u003e= 7.0 (EOL) [v1.0.0], [v1.0.1], [v1.1.0], [v1.2.0], [v1.3.0], [v1.4.0], [v1.4.1], [v1.4.2]\n* PHP \u003e= 7.1 [v2.0.0], [v2.0.1], [v2.1.0], [v2.2.0], [v2.3.0], [v2.4.0], [v2.4.1], [v2.4.2], [v2.4.3], [v2.5.0],\n  [v2.6.0], [v2.7.0], [v2.7.1], [v2.7.2], [v3.0.0-alpha], [v3.0.0-beta], [v3.0.0], [v3.0.1], [v3.1.0], [v3.1.1],\n  [v3.1.2], [v3.1.3], [v3.1.4], [v3.1.5]\n\nRead more about the journey to and changes in `v2.6.0`\nin [this blog post](https://github.com/hollodotme/fast-cgi-client/wiki/Background-Info-FastCgiClient-Version-2.6.0).\n\n---\n\nYou can find an experimental use-case in my related blog posts:\n\n* [Experimental async PHP vol. 1](https://github.com/hollodotme/fast-cgi-client/wiki/Experimental-Async-Php-Volume-1)\n* [Experimental async PHP vol. 2](https://github.com/hollodotme/fast-cgi-client/wiki/Experimental-Async-Php-Volume-2)\n\nYou can also find slides of my talks about this project on [speakerdeck.com](https://speakerdeck.com/hollodotme).\n\n---\n\n## Installation\n\n```bash\ncomposer require hollodotme/fast-cgi-client\n```\n\n---\n\n## Usage - connections\n\nThis library supports two types of connecting to a FastCGI server:\n\n1. Via network socket\n2. Via unix domain socket\n\n### Create a network socket connection\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\n\n$connection = new NetworkSocket(\n\t'127.0.0.1',    # Hostname\n\t9000,           # Port\n\t5000,           # Connect timeout in milliseconds (default: 5000)\n\t5000            # Read/write timeout in milliseconds (default: 5000)\n);\n```\n\n### Create a unix domain socket connection\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\SocketConnections\\UnixDomainSocket;\n\n$connection = new UnixDomainSocket(\n\t'/var/run/php/php7.3-fpm.sock',     # Socket path\n\t5000,                               # Connect timeout in milliseconds (default: 5000)\n\t5000                                # Read/write timeout in milliseconds (default: 5000)\n);\n```\n\n## Usage - single request\n\nThe following examples assume that the content of `/path/to/target/script.php` looks like this:\n\n```php\n\u003c?php declare(strict_types=1);\n\nsleep((int)($_REQUEST['sleep'] ?? 0));\necho $_REQUEST['key'] ?? '';\n```\n\n### Send request synchronously\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\Client;\nuse hollodotme\\FastCGI\\Requests\\PostRequest;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\n\n$client     = new Client();\n$connection = new NetworkSocket('127.0.0.1', 9000);\n$content    = http_build_query(['key' =\u003e 'value']);\n$request    = new PostRequest('/path/to/target/script.php', $content);\n\n$response = $client-\u003esendRequest($connection, $request);\n\necho $response-\u003egetBody();\n```\n\n```\n# prints\nvalue\n```\n\n### Send request asynchronously (Fire and forget)\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\Client;\nuse hollodotme\\FastCGI\\Requests\\PostRequest;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\n\n$client     = new Client();\n$connection = new NetworkSocket('127.0.0.1', 9000);\n$content    = http_build_query(['key' =\u003e 'value']);\n$request    = new PostRequest('/path/to/target/script.php', $content);\n\n$socketId = $client-\u003esendAsyncRequest($connection, $request);\n\necho \"Request sent, got ID: {$socketId}\";\n```\n\n### Read the response, after sending the async request\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\Client;\nuse hollodotme\\FastCGI\\Requests\\PostRequest;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\n\n$client     = new Client();\n$connection = new NetworkSocket('127.0.0.1', 9000);\n$content    = http_build_query(['key' =\u003e 'value']);\n$request    = new PostRequest('/path/to/target/script.php', $content);\n\n$socketId = $client-\u003esendAsyncRequest($connection, $request);\n\necho \"Request sent, got ID: {$socketId}\";\n\n# Do something else here in the meanwhile\n\n# Blocking call until response is received or read timed out\n$response = $client-\u003ereadResponse( \n\t$socketId,     # The socket ID \n\t3000            # Optional timeout to wait for response,\n\t\t\t\t\t# defaults to read/write timeout in milliseconds set in connection\n);\n\necho $response-\u003egetBody();\n```\n\n```\n# prints\nvalue\n```\n\n### Notify a callback when async request responded\n\nYou can register response and failure callbacks for each request. In order to notify the callbacks when a response was\nreceived instead of returning it, you need to use the `waitForResponse(int $socketId, ?int $timeoutMs = null)` method.\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\Client;\nuse hollodotme\\FastCGI\\Requests\\PostRequest;\nuse hollodotme\\FastCGI\\Interfaces\\ProvidesResponseData;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\nuse Throwable;\n\n$client     = new Client();\n$connection = new NetworkSocket('127.0.0.1', 9000);\n$content    = http_build_query(['key' =\u003e 'value']);\n$request    = new PostRequest('/path/to/target/script.php', $content);\n\n# Register a response callback, expects a `ProvidesResponseData` instance as the only parameter\n$request-\u003eaddResponseCallbacks(\n\tstatic function( ProvidesResponseData $response )\n\t{\n\t\techo $response-\u003egetBody();\t\n\t}\n);\n\n# Register a failure callback, expects a `\\Throwable` instance as the only parameter\n$request-\u003eaddFailureCallbacks(\n\tstatic function ( Throwable $throwable )\n\t{\n\t\techo $throwable-\u003egetMessage();\t\n\t}\n);\n\n$socketId = $client-\u003esendAsyncRequest($connection, $request);\n\necho \"Request sent, got ID: {$socketId}\";\n\n# Do something else here in the meanwhile\n\n# Blocking call until response is received or read timed out\n# If response was received all registered response callbacks will be notified\n$client-\u003ewaitForResponse( \n\t$socketId,     # The socket ID \n\t3000            # Optional timeout to wait for response,\n\t\t\t\t\t# defaults to read/write timeout in milliseconds set in connection\n);\n\n# ... is the same as\n\nwhile(true)\n{\n\tif ($client-\u003ehasResponse($socketId))\n\t{\n\t\t$client-\u003ehandleResponse($socketId, 3000);\n\t\tbreak;\n\t}\n}\n```\n\n```\n# prints\nvalue\n```\n\n---\n\n## Usage - multiple requests\n\n### Sending multiple requests and reading their responses (order preserved)\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\Client;\nuse hollodotme\\FastCGI\\Requests\\PostRequest;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\n\n$client     = new Client();\n$connection = new NetworkSocket('127.0.0.1', 9000);\n\n$request1 = new PostRequest('/path/to/target/script.php', http_build_query(['key' =\u003e '1']));\n$request2 = new PostRequest('/path/to/target/script.php', http_build_query(['key' =\u003e '2']));\n$request3 = new PostRequest('/path/to/target/script.php', http_build_query(['key' =\u003e '3']));\n\n$socketIds = [];\n\n$socketIds[] = $client-\u003esendAsyncRequest($connection, $request1);\n$socketIds[] = $client-\u003esendAsyncRequest($connection, $request2);\n$socketIds[] = $client-\u003esendAsyncRequest($connection, $request3);\n\necho 'Sent requests with IDs: ' . implode( ', ', $socketIds ) . \"\\n\";\n\n# Do something else here in the meanwhile\n\n# Blocking call until all responses are received or read timed out\n# Responses are read in same order the requests were sent\nforeach ($client-\u003ereadResponses(3000, ...$socketIds) as $response)\n{\n\techo $response-\u003egetBody() . \"\\n\";\t\n}\n```\n\n```\n# prints\n1\n2\n3\n```\n\n### Sending multiple requests and reading their responses (reactive)\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\Client;\nuse hollodotme\\FastCGI\\Requests\\PostRequest;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\n\n$client     = new Client();\n$connection = new NetworkSocket('127.0.0.1', 9000);\n\n$request1 = new PostRequest('/path/to/target/script.php', http_build_query(['key' =\u003e '1', 'sleep' =\u003e 3]));\n$request2 = new PostRequest('/path/to/target/script.php', http_build_query(['key' =\u003e '2', 'sleep' =\u003e 2]));\n$request3 = new PostRequest('/path/to/target/script.php', http_build_query(['key' =\u003e '3', 'sleep' =\u003e 1]));\n\n$socketIds = [];\n\n$socketIds[] = $client-\u003esendAsyncRequest($connection, $request1);\n$socketIds[] = $client-\u003esendAsyncRequest($connection, $request2);\n$socketIds[] = $client-\u003esendAsyncRequest($connection, $request3);\n\necho 'Sent requests with IDs: ' . implode( ', ', $socketIds ) . \"\\n\";\n\n# Do something else here in the meanwhile\n\n# Loop until all responses were received\nwhile ( $client-\u003ehasUnhandledResponses() )\n{\n\t# read all ready responses\n\tforeach ( $client-\u003ereadReadyResponses( 3000 ) as $response )\n\t{\n\t\techo $response-\u003egetBody() . \"\\n\";\n\t}\n\t\n\techo '.';\n}\n\n# ... is the same as\n\nwhile ( $client-\u003ehasUnhandledResponses() )\n{\n\t$readySocketIds = $client-\u003egetSocketIdsHavingResponse();\n\t\n\t# read all ready responses\n\tforeach ( $client-\u003ereadResponses( 3000, ...$readySocketIds ) as $response )\n\t{\n\t\techo $response-\u003egetBody() . \"\\n\";\n\t}\n\t\n\techo '.';\n}\n\n# ... is the same as\n\nwhile ( $client-\u003ehasUnhandledResponses() )\n{\n\t$readySocketIds = $client-\u003egetSocketIdsHavingResponse();\n\t\n\t# read all ready responses\n\tforeach ($readySocketIds as $socketId)\n\t{\n\t\t$response = $client-\u003ereadResponse($socketId, 3000);\n\t\techo $response-\u003egetBody() . \"\\n\";\n\t}\n\t\n\techo '.';\n}\n```\n\n```\n# prints\n...............................................3\n...............................................2\n...............................................1\n```\n\n### Sending multiple requests and notifying callbacks (reactive)\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\Client;\nuse hollodotme\\FastCGI\\Requests\\PostRequest;\nuse hollodotme\\FastCGI\\Interfaces\\ProvidesResponseData;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\nuse Throwable;\n\n$client     = new Client();\n$connection = new NetworkSocket('127.0.0.1', 9000);\n\n$responseCallback = static function( ProvidesResponseData $response )\n{\n\techo $response-\u003egetBody();\t\n};\n\n$failureCallback = static function ( Throwable $throwable )\n{\n\techo $throwable-\u003egetMessage();\t\n};\n\n$request1 = new PostRequest('/path/to/target/script.php', http_build_query(['key' =\u003e '1', 'sleep' =\u003e 3]));\n$request2 = new PostRequest('/path/to/target/script.php', http_build_query(['key' =\u003e '2', 'sleep' =\u003e 2]));\n$request3 = new PostRequest('/path/to/target/script.php', http_build_query(['key' =\u003e '3', 'sleep' =\u003e 1]));\n\n$request1-\u003eaddResponseCallbacks($responseCallback);\n$request1-\u003eaddFailureCallbacks($failureCallback);\n\n$request2-\u003eaddResponseCallbacks($responseCallback);\n$request2-\u003eaddFailureCallbacks($failureCallback);\n\n$request3-\u003eaddResponseCallbacks($responseCallback);\n$request3-\u003eaddFailureCallbacks($failureCallback);\n\n$socketIds = [];\n\n$socketIds[] = $client-\u003esendAsyncRequest($connection, $request1);\n$socketIds[] = $client-\u003esendAsyncRequest($connection, $request2);\n$socketIds[] = $client-\u003esendAsyncRequest($connection, $request3);\n\necho 'Sent requests with IDs: ' . implode( ', ', $socketIds ) . \"\\n\";\n\n# Do something else here in the meanwhile\n\n# Blocking call until all responses were received and all callbacks notified\n$client-\u003ewaitForResponses(3000);\n\n# ... is the same as\n\nwhile ( $client-\u003ehasUnhandledResponses() )\n{\n\t$client-\u003ehandleReadyResponses(3000);\n}\n\n# ... is the same as\n\nwhile ( $client-\u003ehasUnhandledResponses() )\n{\n\t$readySocketIds = $client-\u003egetSocketIdsHavingResponse();\n\t\n\t# read all ready responses\n\tforeach ($readySocketIds as $socketId)\n\t{\n\t\t$client-\u003ehandleResponse($socketId, 3000);\n\t}\n}\n```\n\n```\n# prints\n3\n2\n1\n```\n\n### Reading output buffer from worker script using pass through callbacks\n\nIt may be useful to see the progression of a requested script by having access to the flushed output of that script. The\nphp.ini default output buffering for php-fpm is 4096 bytes and is (hard-coded) disabled for CLI\nmode. ([See documentation](http://php.net/manual/en/outcontrol.configuration.php#ini.output-buffering))\nCalling `ob_implicit_flush()` causes every call to `echo` or `print` to immediately be flushed.\n\nThe callee script could look like this:\n\n```php\n\u003c?php declare(strict_types=1);\n\nob_implicit_flush();\n\nfunction show( string $string )\n{\n\techo $string . str_repeat( \"\\r\", 4096 - strlen( $string ) ) . \"\\n\";\n\tsleep( 1 );\n}\n\nshow( 'One' );\nshow( 'Two' );\nshow( 'Three' );\n\nerror_log(\"Oh oh!\\n\");\n\necho 'End';\n```\n\nThe caller than could look like this:\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\Client;\nuse hollodotme\\FastCGI\\Requests\\GetRequest;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\n\n$client     = new Client();\n$connection = new NetworkSocket('127.0.0.1', 9000);\n\n$passThroughCallback = static function( string $outputBuffer, string $errorBuffer )\n{\n\techo 'Output: ' . $outputBuffer;\n\techo 'Error: ' . $errorBuffer;\n};\n\n$request = new GetRequest('/path/to/target/script.php', '');\n$request-\u003eaddPassThroughCallbacks( $passThroughCallback );\n\n$client-\u003esendAsyncRequest($connection, $request);\n$client-\u003ewaitForResponses();\n```\n\n```\n# prints immediately\nBuffer: Content-type: text/html; charset=UTF-8\n\nOutput: One\n# sleeps 1 sec\nOutput: Two\n# sleeps 1 sec\nOutput: Three\n# sleeps 1 sec\nError: Oh oh!\nOutput: End\n```\n\n----\n\n### Requests\n\nRequests are defined by the following interface:\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace hollodotme\\FastCGI\\Interfaces;\n\ninterface ProvidesRequestData\n{\n\tpublic function getGatewayInterface() : string;\n\n\tpublic function getRequestMethod() : string;\n\n\tpublic function getScriptFilename() : string;\n\n\tpublic function getServerSoftware() : string;\n\n\tpublic function getRemoteAddress() : string;\n\n\tpublic function getRemotePort() : int;\n\n\tpublic function getServerAddress() : string;\n\n\tpublic function getServerPort() : int;\n\n\tpublic function getServerName() : string;\n\n\tpublic function getServerProtocol() : string;\n\n\tpublic function getContentType() : string;\n\n\tpublic function getContentLength() : int;\n\n\tpublic function getContent() : string;\n\n\tpublic function getCustomVars() : array;\n\n\tpublic function getParams() : array;\n\t\n\tpublic function getRequestUri() : string;\n}\n```\n\nAlongside with this interface, this package provides an abstract request class, containing default values to make the\nAPI more handy for you and 5 request method implementations of this abstract class:\n\n* `hollodotme\\FastCGI\\Requests\\GetRequest`\n* `hollodotme\\FastCGI\\Requests\\PostRequest`\n* `hollodotme\\FastCGI\\Requests\\PutRequest`\n* `hollodotme\\FastCGI\\Requests\\PatchRequest`\n* `hollodotme\\FastCGI\\Requests\\DeleteRequest`\n\nSo you can either implement the interface, inherit from the abstract class or simply use one of the 5 implementations.\n\n#### Default values\n\nThe abstract request class defines several default values which you can optionally overwrite:\n\n| Key               | Default value                     | Comment                                                                                 |\n|-------------------|-----------------------------------|-----------------------------------------------------------------------------------------|\n| GATEWAY_INTERFACE | FastCGI/1.0                       | Cannot be overwritten, because this is the only supported version of the client.        |\n| SERVER_SOFTWARE   | hollodotme/fast-cgi-client        |                                                                                         |\n| REMOTE_ADDR       | 192.168.0.1                       |                                                                                         |\n| REMOTE_PORT       | 9985                              |                                                                                         |\n| SERVER_ADDR       | 127.0.0.1                         |                                                                                         |\n| SERVER_PORT       | 80                                |                                                                                         |\n| SERVER_NAME       | localhost                         |                                                                                         |\n| SERVER_PROTOCOL   | HTTP/1.1                          | You can use the public class constants in `hollodotme\\FastCGI\\Constants\\ServerProtocol` |\n| CONTENT_TYPE      | application/x-www-form-urlencoded |                                                                                         |\n| REQUEST_URI       | \u003cempty string\u003e                    |                                                                                         |\n| CUSTOM_VARS       | empty array                       | You can use the methods `setCustomVar`, `addCustomVars` to add own key-value pairs      |\n\n#### Request contents\n\nIn order to make the composition of different request content types easier there are classes covering the typical\ncontent types:\n\n* [UrlEncodedFormData](./src/RequestContents/UrlEncodedFormData.php)\n* [MultipartFormData](./src/RequestContents/MultipartFormData.php)\n* [JsonData](./src/RequestContents/JsonData.php)\n\nYou can create your own request content type composer by implementing the following interface:\n\n[**ComposesRequestContent**](./src/Interfaces/ComposesRequestContent.php)\n\n```php\ninterface ComposesRequestContent\n{\n\tpublic function getContentType() : string;\n\n\tpublic function getContent() : string;\n}\n```\n\n##### Request content example: URL encoded form data (application/x-www-form-urlencoded)\n\n```php\n\u003c?php declare(strict_types=1);\n\nuse hollodotme\\FastCGI\\RequestContents\\UrlEncodedFormData;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\nuse hollodotme\\FastCGI\\Requests\\PostRequest;\nuse hollodotme\\FastCGI\\Client;\n\n$client = new Client();\n$connection = new NetworkSocket( '127.0.0.1', 9000 );\n\n$urlEncodedContent = new UrlEncodedFormData(\n\t[\n\t\t'nested' =\u003e [\n\t\t\t'one',\n\t\t\t'two'   =\u003e 'value2',\n\t\t\t'three' =\u003e [\n\t\t\t\t'value3',\n\t\t\t],\n\t\t],\n\t]\n);\n\n$postRequest = PostRequest::newWithRequestContent( '/path/to/target/script.php', $urlEncodedContent );\n\n$response = $client-\u003esendRequest( $connection, $postRequest );\n```\n\nThis example produces the following `$_POST` array at the target script:\n\n```\nArray\n(\n    [nested] =\u003e Array\n        (\n            [0] =\u003e one\n            [two] =\u003e value2\n            [three] =\u003e Array\n                (\n                    [0] =\u003e value3\n                )\n\n        )\n)\n```\n\n##### Request content example: multipart form data (multipart/form-data)\n\nMultipart form-data can be used to transfer any binary data as files to the target script just like a file upload in a\nbrowser does.\n\n**PLEASE NOTE:** Multipart form-data content type works with POST requests only.\n\n```php\n\u003c?php declare(strict_types=1);\n\nuse hollodotme\\FastCGI\\RequestContents\\MultipartFormData;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\nuse hollodotme\\FastCGI\\Requests\\PostRequest;\nuse hollodotme\\FastCGI\\Client;\n\n$client = new Client();\n$connection = new NetworkSocket( '127.0.0.1', 9000 );\n\n$multipartContent = new MultipartFormData(\n    # POST data\n\t[\n        'simple'          =\u003e 'value',\n\t\t'nested[]'        =\u003e 'one',\n\t\t'nested[two]'     =\u003e 'value2',\n\t\t'nested[three][]' =\u003e 'value3',\n\t],\n\t# FILES\n\t[\n\t\t'file1'        =\u003e __FILE__,\n\t\t'files[1]'     =\u003e __FILE__,\n\t\t'files[three]' =\u003e __FILE__,\n\t]\n);\n\n$postRequest = PostRequest::newWithRequestContent( '/path/to/target/script.php', $multipartContent );\n\n$response = $client-\u003esendRequest( $connection, $postRequest );\n```\n\nThis example produces the following `$_POST` and `$_FILES` array at the target script:\n\n```\n# $_POST\nArray\n(\n    [simple] =\u003e value\n    [nested] =\u003e Array\n        (\n            [0] =\u003e one\n            [two] =\u003e value2\n            [three] =\u003e Array\n                (\n                    [0] =\u003e value3\n                )\n\n        )\n\n)\n\n# $_FILES\nArray\n(\n    [file1] =\u003e Array\n        (\n            [name] =\u003e multipart.php\n            [type] =\u003e application/octet-stream\n            [tmp_name] =\u003e /tmp/phpiIdCNM\n            [error] =\u003e 0\n            [size] =\u003e 1086\n        )\n\n    [files] =\u003e Array\n        (\n            [name] =\u003e Array\n                (\n                    [1] =\u003e multipart.php\n                    [three] =\u003e multipart.php\n                )\n\n            [type] =\u003e Array\n                (\n                    [1] =\u003e application/octet-stream\n                    [three] =\u003e application/octet-stream\n                )\n\n            [tmp_name] =\u003e Array\n                (\n                    [1] =\u003e /tmp/phpAjHINL\n                    [three] =\u003e /tmp/phpicAmjN\n                )\n\n            [error] =\u003e Array\n                (\n                    [1] =\u003e 0\n                    [three] =\u003e 0\n                )\n\n            [size] =\u003e Array\n                (\n                    [1] =\u003e 1086\n                    [three] =\u003e 1086\n                )\n\n        )\n\n)\n```\n\n##### Request content example: JSON encoded data (application/json)\n\n```php\n\u003c?php declare(strict_types=1);\n\nuse hollodotme\\FastCGI\\RequestContents\\JsonData;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\nuse hollodotme\\FastCGI\\Requests\\PostRequest;\nuse hollodotme\\FastCGI\\Client;\n\n$client = new Client();\n$connection = new NetworkSocket( '127.0.0.1', 9000 );\n\n$jsonContent = new JsonData(\n\t[\n\t\t'nested' =\u003e [\n\t\t\t'one',\n\t\t\t'two'   =\u003e 'value2',\n\t\t\t'three' =\u003e [\n\t\t\t\t'value3',\n\t\t\t],\n\t\t],\n\t]\n);\n\n$postRequest = PostRequest::newWithRequestContent( '/path/to/target/script.php', $jsonContent );\n\n$response = $client-\u003esendRequest( $connection, $postRequest );\n```\n\nThis example produces the following content for `php://input` at the target script:\n\n```json\n{\n  \"nested\": {\n    \"0\": \"one\",\n    \"two\": \"value2\",\n    \"three\": [\n      \"value3\"\n    ]\n  }\n}\n```\n\n### Responses\n\nResponses are defined by the following interface:\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace hollodotme\\FastCGI\\Interfaces;\n\ninterface ProvidesResponseData\n{\n\tpublic function getHeaders() : array;\n\n\tpublic function getHeader( string $headerKey ) : array;\n\t\n\tpublic function getHeaderLine( string $headerKey ) : string;\n\n\tpublic function getBody() : string;\n\n\tpublic function getOutput() : string;\n\t\n\tpublic function getError() : string;\n\n\tpublic function getDuration() : float;\n}\n```\n\nAssuming `/path/to/target/script.php` has the following content:\n\n```php\n\u003c?php declare(strict_types=1);\n\necho 'Hello World';\nerror_log('Some error');\n```\n\nThe raw response would look like this:\n\n```\nContent-type: text/html; charset=UTF-8\n\nHello World\n```\n\n**Please note:**\n\n* All headers sent by your script will precede the response body\n* There won't be any HTTP specific headers like `HTTP/1.1 200 OK`, because there is no webserver involved.\n\nCustom headers will also be part of the response:\n\n```php\n\u003c?php declare(strict_types=1);\n\nheader('X-Custom: Header');\nheader('Set-Cookie: yummy_cookie=choco');\nheader('Set-Cookie: tasty_cookie=strawberry');\n\necho 'Hello World';\nerror_log('Some error');\n```\n\nThe raw response would look like this:\n\n```\nX-Custom: Header\nSet-Cookie: yummy_cookie=choco\nSet-Cookie: tasty_cookie=strawberry\nContent-type: text/html; charset=UTF-8\n\nHello World\n```\n\nYou can retrieve all of the response data separately from the response object:\n\n```php\n# Get all values of a single response header\n$response-\u003egetHeader('Set-Cookie'); \n// ['yummy_cookie=choco', 'tasty_cookie=strawberry']\n\n# Get all values of a single response header as comma separated string\n$response-\u003egetHeaderLine('Set-Cookie');\n// 'yummy_cookie=choco, tasty_cookie=strawberry'\n\n# Get all headers as grouped array\n$response-\u003egetHeaders();\n// [\n//   'X-Custom' =\u003e [\n//      'Header',\n//   ],\n//   'Set-Cookie' =\u003e [\n//      'yummy_cookie=choco',\n//      'tasty_cookie=strawberry',\n//   ],\n//   'Content-type' =\u003e [\n//      'text/html; charset=UTF-8',\n//   ],\n// ]\n\n# Get the body\n$response-\u003egetBody(); \n// 'Hello World'\n\n# Get the raw response output from STDOUT stream\n$response-\u003egetOutput();\n// 'X-Custom: Header\n// Set-Cookie: yummy_cookie=choco\n// Set-Cookie: tasty_cookie=strawberry\n// Content-type: text/html; charset=UTF-8\n// \n// Hello World'\n\n# Get the raw response from SFTERR stream\n$response-\u003egetError();\n// Some error\n\n# Get the duration\n$response-\u003egetDuration(); \n// e.g. 0.0016319751739502\n```\n\n---\n\n## Trouble shooting\n\n### \"File not found.\" response (php-fpm)\n\nThis response is generated by php-fpm for the preceding error `Primary script unknown` in case the requested script does\nnot exists or there are path traversals in its path like `/var/www/../run/script.php`.\n\nAlthough the given path may exist and would resolve to an absolute path in the file system, php-fpm does not do any path\nresolution and accepts only **absolute paths** to the script you want to execute.\n\nProgramatically you can handle this error like this:\n\n```php\nif (preg_match(\"#^Primary script unknown\\n?$#\", $response-\u003egetError()))\n{\n    throw new Exception('Could not find or resolve path to script for execution.');\n}\n\n# OR\n\nif ('404 Not Found' === $response-\u003egetHeaderLine('Status'))\n{\n    throw new Exception('Could not find or resolve path to script for execution.');\n}\n\n# OR\n\nif ('File not found.' === trim($response-\u003egetBody()))\n{\n    throw new Exception('Could not find or resolve path to script for execution.');\n}\n```\n\n---\n\n## Prepare local development environment\n\nThis requires `docker` and `docker-compose` installed on your machine.\n\n    make update\n\n## Run examples\n\n\tmake examples\n\n## Run all tests\n\n    make tests\n\n## Command line tool (for local debugging only)\n\n**Please note:** `bin/fcgiget` is not included and linked to `vendor/bin` via composer anymore since version `v3.1.2`for\nsecurity reasons. [Read more.](https://github.com/hollodotme/fast-cgi-client/pull/58)\n\nRun a call through a network socket:\n\n    docker-compose exec php74 php bin/fcgiget localhost:9001/status\n\nRun a call through a Unix Domain Socket\n\n    docker-compose exec php74 php bin/fcgiget unix:///var/run/php-uds.sock/status\n\nThis shows the response of the php-fpm status page.\n\n\n[v3.1.5]: https://github.com/hollodotme/fast-cgi-client/blob/v3.1.5/README.md\n\n[v3.1.4]: https://github.com/hollodotme/fast-cgi-client/blob/v3.1.4/README.md\n\n[v3.1.3]: https://github.com/hollodotme/fast-cgi-client/blob/v3.1.3/README.md\n\n[v3.1.2]: https://github.com/hollodotme/fast-cgi-client/blob/v3.1.2/README.md\n\n[v3.1.1]: https://github.com/hollodotme/fast-cgi-client/blob/v3.1.1/README.md\n\n[v3.1.0]: https://github.com/hollodotme/fast-cgi-client/blob/v3.1.0/README.md\n\n[v3.0.1]: https://github.com/hollodotme/fast-cgi-client/blob/v3.0.1/README.md\n\n[v3.0.0]: https://github.com/hollodotme/fast-cgi-client/blob/v3.0.0/README.md\n\n[v3.0.0-beta]: https://github.com/hollodotme/fast-cgi-client/blob/v3.0.0-beta/README.md\n\n[v3.0.0-alpha]: https://github.com/hollodotme/fast-cgi-client/blob/v3.0.0-alpha/README.md\n\n[v2.7.2]: https://github.com/hollodotme/fast-cgi-client/blob/v2.7.2/README.md\n\n[v2.7.1]: https://github.com/hollodotme/fast-cgi-client/blob/v2.7.1/README.md\n\n[v2.7.0]: https://github.com/hollodotme/fast-cgi-client/blob/v2.7.0/README.md\n\n[v2.6.0]: https://github.com/hollodotme/fast-cgi-client/blob/v2.6.0/README.md\n\n[v2.5.0]: https://github.com/hollodotme/fast-cgi-client/blob/v2.5.0/README.md\n\n[v2.4.3]: https://github.com/hollodotme/fast-cgi-client/blob/v2.4.3/README.md\n\n[v2.4.2]: https://github.com/hollodotme/fast-cgi-client/blob/v2.4.2/README.md\n\n[v2.4.1]: https://github.com/hollodotme/fast-cgi-client/blob/v2.4.1/README.md\n\n[v2.4.0]: https://github.com/hollodotme/fast-cgi-client/blob/v2.4.0/README.md\n\n[v2.3.0]: https://github.com/hollodotme/fast-cgi-client/blob/v2.3.0/README.md\n\n[v2.2.0]: https://github.com/hollodotme/fast-cgi-client/blob/v2.2.0/README.md\n\n[v2.1.0]: https://github.com/hollodotme/fast-cgi-client/blob/v2.1.0/README.md\n\n[v2.0.1]: https://github.com/hollodotme/fast-cgi-client/blob/v2.0.1/README.md\n\n[v2.0.0]: https://github.com/hollodotme/fast-cgi-client/blob/v2.0.0/README.md\n\n[v1.4.2]: https://github.com/hollodotme/fast-cgi-client/blob/v1.4.2/README.md\n\n[v1.4.1]: https://github.com/hollodotme/fast-cgi-client/blob/v1.4.1/README.md\n\n[v1.4.0]: https://github.com/hollodotme/fast-cgi-client/blob/v1.4.0/README.md\n\n[v1.3.0]: https://github.com/hollodotme/fast-cgi-client/blob/v1.3.0/README.md\n\n[v1.2.0]: https://github.com/hollodotme/fast-cgi-client/blob/v1.2.0/README.md\n\n[v1.1.0]: https://github.com/hollodotme/fast-cgi-client/blob/v1.1.0/README.md\n\n[v1.0.1]: https://github.com/hollodotme/fast-cgi-client/blob/v1.0.1/README.md\n\n[v1.0.0]: https://github.com/hollodotme/fast-cgi-client/blob/v1.0.0/README.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhollodotme%2Ffast-cgi-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhollodotme%2Ffast-cgi-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhollodotme%2Ffast-cgi-client/lists"}