fast cgi client

其他类别 2025-08-24

fast cgi client

PHP fast cgi client使用FASTCGI协议同步向PHP-FPM发送请求(a)。

该库基于Pierrick Charron的PHP-Fastcgi-Client的工作,并已将其移植和现代化为最新的PHP版本,并具有一些用于处理多个请求(以循环)和单位和集成测试的功能。


这是最新版本的文档。

请查看ChangElog中不兼容的更改(BC断裂)。

请参阅以下链接以获取早期版本:

  • php> = 7.0(eol)v1.0.0,v1.0.1,v1.1.0,v1.2.0,v1.3.0,v1.4.0,v1.4.0,v1.4.1,v1.4.4.2
  • PHP >= 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, 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,v3.1.2,v3.1.3,v3.1.4,v3.1.5

在此博客文章中阅读有关v2.6.0的旅程和变化的更多信息。


您可以在我相关的博客文章中找到一个实验用例:

  • 实验异步PHP卷。 1
  • 实验异步PHP卷。 2

您还可以在SpeakerDeck.com上找到有关此项目的谈话的幻灯片。


安装

composer require hollodotme/fast-cgi-client

用法 - 连接

该库支持两种连接到FastCGI服务器的类型:

  1. 通过网络插座
  2. 通过Unix域插座

创建网络插座连接

  declare (strict_types= 1 );

namespace YourVendor  YourProject ;

use hollodotme  FastCGI  SocketConnections  NetworkSocket ;

$ connection = new NetworkSocket (
	' 127.0.0.1 ' ,    # Hostname
	9000 ,           # Port
	5000 ,           # Connect timeout in milliseconds (default: 5000)
	5000            # Read/write timeout in milliseconds (default: 5000)
);

创建一个UNIX域插座连接

  declare (strict_types= 1 );

namespace YourVendor  YourProject ;

use hollodotme  FastCGI  SocketConnections  UnixDomainSocket ;

$ connection = new UnixDomainSocket (
	' /var/run/php/php7.3-fpm.sock ' ,     # Socket path
	5000 ,                               # Connect timeout in milliseconds (default: 5000)
	5000                                # Read/write timeout in milliseconds (default: 5000)
);

用法 - 单个请求

以下示例假设/path/to/target/script.php的内容看起来像这样:

  declare (strict_types= 1 );

sleep (( int )( $ _REQUEST [ ' sleep ' ] ?? 0 ));
echo $ _REQUEST [ ' key ' ] ?? '' ;

同步发送请求

  declare (strict_types= 1 );

namespace YourVendor  YourProject ;

use hollodotme  FastCGI  Client ;
use hollodotme  FastCGI  Requests  PostRequest ;
use hollodotme  FastCGI  SocketConnections  NetworkSocket ;

$ client     = new Client ();
$ connection = new NetworkSocket ( ' 127.0.0.1 ' , 9000 );
$ content    = http_build_query ([ ' key ' => ' value ' ]);
$ request    = new PostRequest ( ' /path/to/target/script.php ' , $ content );

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

echo $ response -> getBody ();
 # prints
value

异步发送请求(开火和忘记)

  declare (strict_types= 1 );

namespace YourVendor  YourProject ;

use hollodotme  FastCGI  Client ;
use hollodotme  FastCGI  Requests  PostRequest ;
use hollodotme  FastCGI  SocketConnections  NetworkSocket ;

$ client     = new Client ();
$ connection = new NetworkSocket ( ' 127.0.0.1 ' , 9000 );
$ content    = http_build_query ([ ' key ' => ' value ' ]);
$ request    = new PostRequest ( ' /path/to/target/script.php ' , $ content );

$ socketId = $ client -> sendAsyncRequest ( $ connection , $ request );

echo " Request sent, got ID: { $ socketId }" ;

在发送异步请求后阅读响应

readResponse( $socketId, # The socket ID 3000 # Optional timeout to wait for response, # defaults to read/write timeout in milliseconds set in connection ); echo $response->getBody();">
  declare (strict_types= 1 );

namespace YourVendor  YourProject ;

use hollodotme  FastCGI  Client ;
use hollodotme  FastCGI  Requests  PostRequest ;
use hollodotme  FastCGI  SocketConnections  NetworkSocket ;

$ client     = new Client ();
$ connection = new NetworkSocket ( ' 127.0.0.1 ' , 9000 );
$ content    = http_build_query ([ ' key ' => ' value ' ]);
$ request    = new PostRequest ( ' /path/to/target/script.php ' , $ content );

$ socketId = $ client -> sendAsyncRequest ( $ connection , $ request );

echo " Request sent, got ID: { $ socketId }" ;

# Do something else here in the meanwhile

# Blocking call until response is received or read timed out
$ response = $ client -> readResponse ( 
	$ socketId ,     # The socket ID 
	3000            # Optional timeout to wait for response,
					# defaults to read/write timeout in milliseconds set in connection
);

echo $ response -> getBody ();
 # prints
value

在异步请求回复时通知回调

您可以为每个请求注册响应和故障回调。为了在收到响应而不是返回响应时通知回调,您需要使用waitForResponse(int $socketId, ?int $timeoutMs = null)方法。

waitForResponse( $socketId, # The socket ID 3000 # Optional timeout to wait for response, # defaults to read/write timeout in milliseconds set in connection ); # ... is the same as while(true) { if ($client->hasResponse($socketId)) { $client->handleResponse($socketId, 3000); break; } }">
  declare (strict_types= 1 );

namespace YourVendor  YourProject ;

use hollodotme  FastCGI  Client ;
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 );
$ content    = http_build_query ([ ' key ' => ' value ' ]);
$ request    = new PostRequest ( ' /path/to/target/script.php ' , $ content );

# Register a response callback, expects a `ProvidesResponseData` instance as the only parameter
$ request -> addResponseCallbacks (
	static function ( ProvidesResponseData $ response )
	{
		echo $ response -> getBody ();	
	}
);

# Register a failure callback, expects a `Throwable` instance as the only parameter
$ request -> addFailureCallbacks (
	static function ( Throwable $ throwable )
	{
		echo $ throwable -> getMessage ();	
	}
);

$ socketId = $ client -> sendAsyncRequest ( $ connection , $ request );

echo " Request sent, got ID: { $ socketId }" ;

# Do something else here in the meanwhile

# Blocking call until response is received or read timed out
# If response was received all registered response callbacks will be notified
$ client -> waitForResponse ( 
	$ socketId ,     # The socket ID 
	3000            # Optional timeout to wait for response,
					# defaults to read/write timeout in milliseconds set in connection
);

# ... is the same as

while ( true )
{
	if ( $ client -> hasResponse ( $ socketId ))
	{
		$ client -> handleResponse ( $ socketId , 3000 );
		break ;
	}
}
 # prints
value

用法 - 多个请求

发送多个请求并阅读其答复(保留订单)

readResponses(3000, ...$socketIds) as $response) { echo $response->getBody() . "n"; }">
  declare (strict_types= 1 );

namespace YourVendor  YourProject ;

use hollodotme  FastCGI  Client ;
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 ' , http_build_query ([ ' key ' => ' 1 ' ]));
$ request2 = new PostRequest ( ' /path/to/target/script.php ' , http_build_query ([ ' key ' => ' 2 ' ]));
$ request3 = new PostRequest ( ' /path/to/target/script.php ' , http_build_query ([ ' 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

发送多个请求并阅读他们的答复(反应性)

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 '.'; }">
  declare (strict_types= 1 );

namespace YourVendor  YourProject ;

use hollodotme  FastCGI  Client ;
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 ' , http_build_query ([ ' key ' => ' 1 ' , ' sleep ' => 3 ]));
$ request2 = new PostRequest ( ' /path/to/target/script.php ' , http_build_query ([ ' key ' => ' 2 ' , ' sleep ' => 2 ]));
$ request3 = new PostRequest ( ' /path/to/target/script.php ' , http_build_query ([ ' 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

发送多个请求并通知回调(反应性)

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); } }">
  declare (strict_types= 1 );

namespace YourVendor  YourProject ;

use hollodotme  FastCGI  Client ;
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 ' , http_build_query ([ ' key ' => ' 1 ' , ' sleep ' => 3 ]));
$ request2 = new PostRequest ( ' /path/to/target/script.php ' , http_build_query ([ ' key ' => ' 2 ' , ' sleep ' => 2 ]));
$ request3 = new PostRequest ( ' /path/to/target/script.php ' , http_build_query ([ ' 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 );
	}
}
 # prints
3
2
1

使用通过回调从Worker脚本读取输出缓冲区

通过访问该脚本的冲洗输出,可以看到请求脚本的进度可能很有用。 PHP-FPM的php.ini默认输出缓冲为4096字节,并且(硬编码)用于CLI模式。 (请参阅文档)呼叫ob_implicit_flush()会导致每个呼叫echoprint立即被冲洗。

Callee脚本看起来像这样:

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

呼叫者看起来像这样:

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

$ passThroughCallback = static function ( string $ outputBuffer , string $ errorBuffer )
{
	echo ' Output: ' . $ outputBuffer ;
	echo ' Error: ' . $ errorBuffer ;
};

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

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

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

请求

请求由以下接口定义:

  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 () : string ;

	public function getCustomVars () : array ;

	public function getParams () : array ;
	
	public function getRequestUri () : string ;
}

除此界面外,此软件包还提供了一个抽象请求类,其中包含默认值,使您对您更方便,并且该摘要类的5请求方法实现:

  • hollodotmeFastCGIRequestsGetRequest
  • hollodotmeFastCGIRequestsPostRequest
  • hollodotmeFastCGIRequestsPutRequest
  • hollodotmeFastCGIRequestsPatchRequest
  • hollodotmeFastCGIRequestsDeleteRequest

因此,您可以实现从抽象类继承的接口,也可以简单地使用5个实现之一。

默认值

抽象请求类定义了几个默认值,您可以选择覆盖这些值:

钥匙 默认值 评论
Gateway_interface fastcgi/1.0 不能被覆盖,因为这是客户端的唯一支持版本。
server_software hollodotme/fast-cgi-client
远程_ADDR 192.168.0.1
远程_PORT 9985
server_addr 127.0.0.1
server_port 80
server_name Localhost
server_protocol http/1.1 您可以在hollodotmeFastCGIConstantsServerProtocol中使用公共类常数
content_type 应用/X-WWW-Form-urlenCoded
request_uri
custom_vars 空数组 您可以使用setCustomVaraddCustomVars添加自己的键值对

请求内容

为了使不同请求内容类型的组成更容易,有涵盖典型内容类型的类:

  • urlencodedefformdata
  • MultipartFormData
  • jsondata

您可以通过实现以下接口来创建自己的请求内容类型的作曲家:

构图requestContent

 interface ComposesRequestContent
{
	public function getContentType () : string ;

	public function getContent () : string ;
}
请求内容示例:URL编码的表单数据(Application/X-WWW-Form-urlCoded)
  declare (strict_types= 1 );

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

$ client = new Client ();

					
					
下载源码

通过命令行克隆项目:

git clone https://github.com/hollodotme/fast-cgi-client.git