SimpleFastCgi.php 6.0 KB

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