FastCGI.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <?php
  2. /**
  3. * fastcgi 协议解析 相关
  4. * 简单实现,测试时使用,可能会有bug,不要用到生产环境
  5. * @author walkor <worker-man@qq.com>
  6. * */
  7. class FastCGI{
  8. const VERSION_1 = 1;
  9. const BEGIN_REQUEST = 1;
  10. const ABORT_REQUEST = 2;
  11. const END_REQUEST = 3;
  12. const PARAMS = 4;
  13. const STDIN = 5;
  14. const STDOUT = 6;
  15. const STDERR = 7;
  16. const DATA = 8;
  17. const GET_VALUES = 9;
  18. const GET_VALUES_RESULT = 10;
  19. const UNKNOWN_TYPE = 11;
  20. const MAXTYPE = self::UNKNOWN_TYPE;
  21. const HEAD_LENGTH = 8;
  22. private function __construct(){}
  23. /**
  24. * 判断数据包是否全部接收完成
  25. *
  26. * @param string $data
  27. * @return int 0:完成 >0:还要接收int字节
  28. */
  29. public static function input($data)
  30. {
  31. while(1)
  32. {
  33. $data_length = strlen($data);
  34. // 长度小于包头长度,继续读
  35. if($data_length < self::HEAD_LENGTH)
  36. {
  37. return self::HEAD_LENGTH - $data_length;
  38. }
  39. $headers = unpack(
  40. "Cversion/".
  41. "Ctype/".
  42. "nrequestId/".
  43. "ncontentLength/".
  44. "CpaddingLength/".
  45. "Creserved/"
  46. , $data);
  47. $total_length = self::HEAD_LENGTH + $headers['contentLength'] + $headers['paddingLength'];
  48. // 全部接收完毕
  49. if($data_length == $total_length)
  50. {
  51. return 0;
  52. }
  53. // 数据长度不够一个包长
  54. else if($data_length < $total_length)
  55. {
  56. return $total_length - $data_length;
  57. }
  58. // 数据长度大于一个包长,还有后续包
  59. else
  60. {
  61. $data = substr($data, $total_length);
  62. }
  63. }
  64. return 0;
  65. }
  66. /**
  67. * 解析全部fastcgi协议包,并设置相应环境变量
  68. *
  69. * @param string $data
  70. * @return array
  71. */
  72. public static function decode($data)
  73. {
  74. $params = array();
  75. $_GET = $_POST = $GLOBALS['HTTP_RAW_POST_DATA'] = array();
  76. while(1)
  77. {
  78. if(!$data)
  79. {
  80. break;
  81. }
  82. $headers = unpack(
  83. "Cversion/".
  84. "Ctype/".
  85. "nrequestId/".
  86. "ncontentLength/".
  87. "CpaddingLength/".
  88. "Creserved/"
  89. , $data);
  90. // 获得环境变量等
  91. if($headers['type'] == self::PARAMS)
  92. {
  93. // 解析名-值
  94. $offset = self::HEAD_LENGTH;
  95. while($offset + $headers['paddingLength'] < $headers['contentLength'])
  96. {
  97. $namelen = ord($data[$offset++]);
  98. // 127字节或更少的长度能在一字节中编码,而更长的长度总是在四字节中编码
  99. if($namelen > 127)
  100. {
  101. $namelen = (($namelen & 0x7f) << 24) +
  102. (ord($data[$offset++]) << 16) +
  103. (ord($data[$offset++]) << 8) +
  104. ord($data[$offset++]);
  105. }
  106. // 值的长度
  107. $valuelen = ord($data[$offset++]);
  108. if($valuelen > 127)
  109. {
  110. $valuelen = (($valuelen & 0x7f) << 24) +
  111. (ord($data[$offset++]) << 16) +
  112. (ord($data[$offset++]) << 8) +
  113. ord($data[$offset++]);
  114. }
  115. // 名
  116. $name = substr($data, $offset, $namelen);
  117. $offset += $namelen;
  118. $value = substr($data, $offset, $valuelen);
  119. $offset += $valuelen;
  120. $params[$name] = $value;
  121. }
  122. // 解析$_SERVER
  123. foreach($params as $key=>$value)
  124. {
  125. $_SERVER[$key]=$value;
  126. }
  127. if(array_key_exists('HTTP_COOKIE', $params))
  128. {
  129. foreach(explode(';', $params['HTTP_COOKIE']) as $coo)
  130. {
  131. $nameval = explode('=', trim($coo));
  132. $_COOKIE[$nameval[0]] = urldecode($nameval[1]);
  133. }
  134. }
  135. }
  136. elseif($headers['type'] == self::STDIN)
  137. {
  138. // 为啥是8,还要研究下
  139. $data = substr($data, 8, $headers['contentLength']);
  140. // 解析$GLOBALS['HTTP_RAW_POST_DATA']
  141. $GLOBALS['HTTP_RAW_POST_DATA'] = $data;
  142. // 解析POST
  143. parse_str($data, $_POST);
  144. }
  145. $total_length = self::HEAD_LENGTH + $headers['contentLength'] + $headers['paddingLength'];
  146. $data = substr($data, $total_length);
  147. }
  148. // 解析GET
  149. parse_str(preg_replace('/^\/.*?\?/', '', $_SERVER['REQUEST_URI']), $_GET);
  150. return array('header' => $headers, 'data' => '');
  151. }
  152. /**
  153. * 打包fastcgi协议,用于返回数据给nginx
  154. *
  155. * @param array $header
  156. * @param string $data
  157. * @return string
  158. */
  159. public static function encode($header, $data)
  160. {
  161. $data = "Content-type: text/html\r\n\r\n" . $data;
  162. $contentLength = strlen($data);
  163. $head_data = pack("CCnnxx",
  164. self::VERSION_1,
  165. self::STDOUT,
  166. $header['requestId'],
  167. $contentLength
  168. );
  169. return $head_data.$data;
  170. }
  171. }