素のPHPからfcgi(php-fpm)経由でfuelPHPのコントローラーを実行
時間のかかる処理をバックグラウンドで処理するため、素のPHPからfcgi呼び出しを試してみました。
素のPHPからfast cgiを呼び出す(PHP Fast CGI Client)
呼び出すプログラムは、何かフレームワークに乗っかっているパターンが大半だと思います。
今回はfuelPHPのコントローラーを呼び出してみます。
fuelPHPのサンプル
簡単なコントローラーを作成しました。
・call.php
- <?php
- class Controller_Call extends Controller {
- public function action_index() {
- echo 'Controller_Call!'.PHP_EOL;
- echo 'post1:'.Input::post('post1').PHP_EOL;
- echo 'post2:'.Input::post('post2').PHP_EOL;
- }
- }
fcgi経由での呼び出し
ミソはFCGI_PARAMSの指定です。
・呼び出すスクリプトはfuel/public/index.php
・PATH_INFOで呼び出すコントローラー名を指定
こんな感じになりました。
- <?php
- // http://saburi380.blogspot.com/2014/11/javafast-cgi-client.html
- class FCGIConnection {
- //
- // 8. Types and Constants
- //
- const FCGI_VERSION_1 = 1;
- const FCGI_BEGIN_REQUEST = 1;
- const FCGI_ABORT_REQUEST = 2;
- const FCGI_END_REQUEST = 3;
- const FCGI_PARAMS = 4;
- const FCGI_STDIN = 5;
- const FCGI_STDOUT = 6;
- const FCGI_STDERR = 7;
- const FCGI_DATA = 8;
- const FCGI_GET_VALUES = 9;
- const FCGI_GET_VALUES_RESULT = 10;
- const FCGI_UNKNOWN_TYPE = 11;
- const FCGI_MAXTYPE = self::FCGI_UNKNOWN_TYPE;
- const FCGI_KEEP_CONN = 1;
- const FCGI_RESPONDER = 1;
- const FCGI_AUTHORIZER = 2;
- const FCGI_FILTER = 3;
- private $_socket;
- private $_stream;
- public function __construct() {
- $this->_socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
- $ret = socket_connect($this->_socket, '/var/run/php/php7.2-fpm.sock');
- if ($ret === false) {
- $this->_log('open error.');
- } else {
- $this->_log('open success.');
- }
- /*
- $this->_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
- $ret = socket_connect($this->_socket, '127.0.0.1', 9000);
- if ($ret === false) {
- $this->_log('connect error.');
- } else {
- $this->_log('connect success.');
- }
- */
- }
- //
- // 5. Application Record Types
- //
- //
- // 5.1 FCGI_BEGIN_REQUEST
- //
- public function sendBeginRequest($requestID, $keepalive) {
- $this->_stream = '';
- $this->sendRecordHeader($requestID, self::FCGI_BEGIN_REQUEST, 8);
- $role = self::FCGI_RESPONDER;
- $stream = '';
- $stream .= chr($role >> 8);
- $stream .= chr($role);
- $stream .= $keepalive ? chr(self::FCGI_KEEP_CONN) : chr(0);
- for($i = 0; $i < 5; $i++) {
- $stream .= chr(0); // padding = 5
- }
- $this->_send($stream);
- }
- //
- // 5.2 Name-Value Pair Stream: FCGI_PARAMS
- //
- public function sendParams($requestID, $params) {
- $intParamsLength = 0;
- foreach($params as $key => $value) {
- $intParamsLength += $this->_calcParamLength($key, $value);
- }
- $this->_log("FCGI_PARAMS: length=" . $intParamsLength);
- $pad = $this->sendRecordHeader($requestID, self::FCGI_PARAMS, $intParamsLength);
- foreach($params as $key => $value) {
- $this->_sendParam($requestID, $key, $value);
- }
- for($i = 0; $i < $pad; $i++) {
- $this->_send(chr(0));
- }
- $this->flush();
- }
- //
- // 3. Protocol Basics
- //
- //
- // 3.3 Records (All data is carried in "records")
- //
- public function sendRecordHeader($requestID, $recordType, $contentLength) {
- $intPaddingLength = $contentLength % 8;
- if($intPaddingLength != 0) {
- $intPaddingLength = (8 - $intPaddingLength);
- }
- $this->_log(" Record Header: " . $recordType . ", pad=" . $intPaddingLength);
- $stream = '';
- $stream .= chr(self::FCGI_VERSION_1);
- $stream .= chr($recordType);
- $stream .= chr($requestID >> 8);
- $stream .= chr($requestID);
- $stream .= chr($contentLength >> 8);
- $stream .= chr($contentLength);
- $stream .= chr($intPaddingLength); // paddingLength
- $stream .= chr(0); // reserved
- $this->_send($stream);
- return $intPaddingLength;
- }
- //
- // 3.4 Name-Value Pairs
- //
- private function _calcParamLength($name, $value){
- if (empty($name) || empty($value)) {
- return 0;
- }
- $nameLength = strlen($name);
- $valueLength = strlen($value);
- if($nameLength < 0x80){
- if($valueLength < 0x80){
- // FCGI_NameValuePair11
- return $nameLength + $valueLength + 2;
- }else{
- // FCGI_NameValuePair14
- return $nameLength + $valueLength + 5;
- }
- }else{
- if($valueLength < 0x80){
- // FCGI_NameValuePair41
- return $nameLength + $valueLength + 5;
- }else{
- // FCGI_NameValuePair44
- return $nameLength + $valueLength + 8;
- }
- }
- }
- public function _sendParam($requestID, $name, $value) {
- if (empty($name) || empty($value)) {
- return;
- }
- $nameLength = strlen($name);
- $valueLength = strlen($value);
- $stream = '';
- if($nameLength < 0x80){
- if($valueLength < 0x80){
- // FCGI_NameValuePair11
- $stream .= chr($nameLength);
- $stream .= chr($valueLength);
- }else{
- // FCGI_NameValuePair14
- $stream .= chr($nameLength);
- $stream .= chr(0x80 | $valueLength >> 24);
- $stream .= chr($valueLength >> 16);
- $stream .= chr($valueLength >> 8);
- $stream .= chr($valueLength);
- }
- }else{
- if($valueLength < 0x80){
- // FCGI_NameValuePair41
- $stream .= chr(0x80 | $nameLength >> 24);
- $stream .= chr($nameLength >> 16);
- $stream .= chr($nameLength >> 8);
- $stream .= chr($nameLength);
- $stream .= chr($valueLength);
- }else{
- // FCGI_NameValuePair44
- $stream .= chr(0x80 | $nameLength >> 24);
- $stream .= chr($nameLength >> 16);
- $stream .= chr($nameLength >> 8);
- $stream .= chr($nameLength);
- $stream .= chr(0x80 | $valueLength >> 24);
- $stream .= chr($valueLength >> 16);
- $stream .= chr($valueLength >> 8);
- $stream .= chr($valueLength);
- }
- }
- $stream .= $name;
- $stream .= $value;
- $this->_send($stream);
- }
- //
- // 5.3 Byte Streams: FCGI_STDIN
- //
- public function sendStdin($requestID, $body) {
- $this->_log("FCGI_STDIN: length=" . strlen($body));
- $pad = $this->sendRecordHeader($requestID, self::FCGI_STDIN, strlen($body));
- $this->_send($body);
- for($i = 0; $i < $pad; $i++) {
- $this->_send(chr(0));
- }
- $this->flush();
- }
- //
- // 5.3 Byte Streams: FCGI_STDOUT, FCGI_STDERR, 5.5 FCGI_END_REQUEST
- //
- public function recvStdoutStderrAndWaitEndRequest() {
- $version;
- $recordType = -1;
- $requestID = -1;
- $contentLength = 0;
- $paddingLength = 0;
- while(true){
- if($recordType < 0){
- $raw = $this->_read();
- if ($raw === '') {
- break;
- }
- $version = ord($raw);
- if($version < 0) {
- break;
- }
- if($version != self::FCGI_VERSION_1){
- $this->_log('recv record version error: ' . $version);
- break;
- }
- $recordType = ord($this->_read());
- $requestID = (ord($this->_read()) << 8) + ord($this->_read());
- $contentLength = (ord($this->_read()) << 8) + ord($this->_read());
- $paddingLength = ord($this->_read());
- $this->_read(); // reserved
- }
- switch ($recordType) {
- case self::FCGI_STDOUT:
- $this->_log('FCGI_STDOUT: requestID=' . $requestID . ', contentLength=' . $contentLength . ', paddingLength=' . $paddingLength);
- $readed = $this->_read($contentLength);
- $this->_read($paddingLength);
- echo $readed . PHP_EOL;
- $this->_log('FCGI_STDOUT: done');
- $recordType = -1;
- break;
- case self::FCGI_STDERR:
- $this->_log('FCGI_STDERR: requestID=' . $requestID . ', contentLength=' . $contentLength . ', paddingLength=' + $paddingLength);
- $readed = $this->_read($contentLength);
- $this->_read($paddingLength);
- echo $readed;
- $this->_log('FCGI_STDERR: done');
- $recordType = -1;
- break;
- case self::FCGI_END_REQUEST:
- $startStat = (ord($this->_read()) << 24) + (ord($this->_read()) << 16) + (ord($this->_read()) << 8) + ord($this->_read());
- $endStat = ord($this->_read());
- $recordType = -1;
- $this->_log('FCGI_END_REQUEST: requestID=' . $requestID . ', appStatus=' . $startStat . ', protocolStatus=' . $endStat);
- $this->_read(3);
- break;
- default:
- $this->_log('recv record type error: ' . $recordType);
- break;
- }
- }
- //return appStatAndProtStat;
- }
- private function _send($msg) {
- $this->_stream .= $msg;
- }
- public function flush() {
- $ret = socket_write($this->_socket, $this->_stream);
- if ($ret === false) {
- $this->_log('flush error!');
- $this->_log($this->_stream);
- } else {
- $this->_log('--> flush ok');
- }
- $this->_stream = '';
- }
- private function _read($lentgh = 1) {
- return socket_read($this->_socket, $lentgh);
- }
- public function close() {
- socket_close($this->_socket);
- }
- private function _log($msg) {
- echo $msg.PHP_EOL;
- }
- }
- $fcgic = new FCGIConnection();
- $method = "POST";
- $scriptFileName = '/home/baranche/php/fuelphp/public/index.php';
- $queryString = "get1=get_hello&get2=get_world";
- $requestBodyString = "post1=post_hello&post2=post_world";
- $requestID = 1;
- // B. Typical Protocol Message Flow
- // {FCGI_BEGIN_REQUEST, 1, {FCGI_RESPONDER, 0}}
- $fcgic->sendBeginRequest($requestID, false);
- // {FCGI_PARAMS, 1, "..."}
- $params = [
- 'QUERY_STRING' => $queryString,
- 'REQUEST_METHOD' => $method,
- 'SCRIPT_FILENAME' => $scriptFileName,
- 'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
- 'CONTENT_LENGTH' => strlen($requestBodyString),
- 'PATH_INFO' => 'call'
- ];
- $fcgic->sendParams($requestID, $params);
- if(count($params) > 0){
- // {FCGI_PARAMS, 1, ""}
- $params = [];
- $fcgic->sendParams($requestID, $params);
- }
- // {FCGI_STDIN, 1, "..."}
- $fcgic->sendStdin($requestID, $requestBodyString);
- // {FCGI_STDIN, 1, ""}
- $fcgic->sendStdin($requestID, "");
- // {FCGI_STDOUT, 1, "Content-type: text/html\r\n\r\n\n ... "}
- // {FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}
- $fcgic->recvStdoutStderrAndWaitEndRequest();
- $fcgic->close();
実行してみます。
$ sudo -u www-data php sock.php
open success.
Record Header: 1, pad=0
FCGI_PARAMS: length=203
Record Header: 4, pad=5
--> flush ok
FCGI_PARAMS: length=0
Record Header: 4, pad=0
--> flush ok
FCGI_STDIN: length=33
Record Header: 5, pad=7
--> flush ok
FCGI_STDIN: length=0
Record Header: 5, pad=0
--> flush ok
FCGI_STDOUT: requestID=1, contentLength=93, paddingLength=3
Content-type: text/html; charset=UTF-8
Controller_Call!
post1:post_hello
post2:post_world
FCGI_STDOUT: done
FCGI_END_REQUEST: requestID=1, appStatus=0, protocolStatus=0
ちゃんとcallコントローラーが呼び出せました。
fuelPHPに関連するライブラリのロードもうまく動いてくれているようです。