Skip to content

Commit 071920b

Browse files
Merge pull request #11 from PHPFastCGI/fastcgi-request
FastCGI request object
2 parents f6a6494 + 18cb2cc commit 071920b

14 files changed

+388
-226
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ A FastCGI daemon written in PHP.
1010

1111
Using this daemon, applications can stay alive between HTTP requests whilst operating behind the protection of a FastCGI enabled web server.
1212

13-
The daemon requires a handler to be defined that accepts PSR-7 requests and returns PSR-7 responses.
13+
The daemon requires a handler to be defined that accepts request objects and returns PSR-7 or HttpFoundation responses.
1414

1515
The [Speedfony Bundle](https://github.com/PHPFastCGI/SpeedfonyBundle) integrates this daemon with the symfony2 framework.
1616
The [Slimmer package](https://github.com/PHPFastCGI/Slimmer) integrates this daemon with the Slim v3 framework.
@@ -27,11 +27,13 @@ Below is an example of a simple 'Hello, World!' FastCGI application in PHP.
2727
require_once dirname(__FILE__) . '/../vendor/autoload.php';
2828

2929
use PHPFastCGI\FastCGIDaemon\ApplicationFactory;
30-
use Psr\Http\Message\ServerRequestInterface;
30+
use PHPFastCGI\FastCGIDaemon\Http\RequestInterface;
3131
use Zend\Diactoros\Response\HtmlResponse;
3232

3333
// A simple kernel. This is the core of your application
34-
$kernel = function (ServerRequestInterface $request) {
34+
$kernel = function (RequestInterface $request) {
35+
// $request->getServerRequest() returns PSR-7 server request object
36+
// $request->getHttpFoundationRequest() returns HTTP foundation request object
3537
return new HtmlResponse('<h1>Hello, World!</h1>');
3638
};
3739

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"psr/http-message": "~1.0",
1515
"psr/log": "~1.0",
1616
"zendframework/zend-diactoros": "~1.0",
17+
"symfony/http-foundation": "~2.7",
1718
"symfony/console": "~2.5"
1819
},
1920
"require-dev": {

src/CallbackWrapper.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace PHPFastCGI\FastCGIDaemon;
44

5-
use Psr\Http\Message\ServerRequestInterface;
5+
use PHPFastCGI\FastCGIDaemon\Http\RequestInterface;
66

77
/**
88
* Wraps a callback (such as a closure, function or class and method pair) as an
@@ -34,7 +34,7 @@ public function __construct($handler)
3434
/**
3535
* {@inheritdoc}
3636
*/
37-
public function handleRequest(ServerRequestInterface $request)
37+
public function handleRequest(RequestInterface $request)
3838
{
3939
return call_user_func($this->callback, $request);
4040
}

src/Connection/StreamSocketConnectionPool.php

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,20 +114,18 @@ protected function acceptConnection(ConnectionHandlerFactoryInterface $connectio
114114
{
115115
$clientSocket = @stream_socket_accept($this->serverSocket);
116116

117-
if (false === $clientSocket) {
118-
return;
119-
}
120-
121-
stream_set_blocking($clientSocket, 0);
117+
if (false !== $clientSocket) {
118+
stream_set_blocking($clientSocket, 0);
122119

123-
$connection = new StreamSocketConnection($clientSocket);
124-
$handler = $connectionHandlerFactory->createConnectionHandler($connection);
120+
$connection = new StreamSocketConnection($clientSocket);
121+
$handler = $connectionHandlerFactory->createConnectionHandler($connection);
125122

126-
$id = spl_object_hash($connection);
123+
$id = spl_object_hash($connection);
127124

128-
$this->clientSockets[$id] = $clientSocket;
129-
$this->connections[$id] = $connection;
130-
$this->connectionHandlers[$id] = $handler;
125+
$this->clientSockets[$id] = $clientSocket;
126+
$this->connections[$id] = $connection;
127+
$this->connectionHandlers[$id] = $handler;
128+
}
131129
}
132130

133131
/**

src/ConnectionHandler/ConnectionHandler.php

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66
use PHPFastCGI\FastCGIDaemon\DaemonInterface;
77
use PHPFastCGI\FastCGIDaemon\Exception\DaemonException;
88
use PHPFastCGI\FastCGIDaemon\Exception\ProtocolException;
9-
use PHPFastCGI\FastCGIDaemon\Http\RequestBuilder;
9+
use PHPFastCGI\FastCGIDaemon\Http\Request;
1010
use PHPFastCGI\FastCGIDaemon\KernelInterface;
1111
use Psr\Http\Message\ResponseInterface;
1212
use Psr\Http\Message\StreamInterface;
1313
use Psr\Log\LoggerAwareInterface;
1414
use Psr\Log\LoggerAwareTrait;
1515
use Psr\Log\LoggerInterface;
1616
use Psr\Log\NullLogger;
17+
use Symfony\Component\HttpFoundation\Response as HttpFoundationResponse;
18+
use Zend\Diactoros\Stream;
1719

1820
/**
1921
* The default implementation of the ConnectionHandlerInterface.
@@ -112,7 +114,7 @@ public function close()
112114
$this->bufferLength = 0;
113115

114116
foreach ($this->requests as $request) {
115-
$request['builder']->clean();
117+
fclose($request['stdin']);
116118
}
117119

118120
$this->requests = [];
@@ -203,6 +205,8 @@ protected function writeResponse($requestId, $headerData, StreamInterface $strea
203205

204206
$this->writeRecord($requestId, DaemonInterface::FCGI_STDOUT, $writeData);
205207
} while ($writeSize === 65535);
208+
209+
$this->writeRecord($requestId, DaemonInterface::FCGI_STDOUT);
206210
}
207211

208212
/**
@@ -220,9 +224,7 @@ protected function endRequest($requestId, $appStatus = 0, $protocolStatus = Daem
220224

221225
$keepAlive = $this->requests[$requestId]['keepAlive'];
222226

223-
if (isset($this->requests[$requestId]['builder'])) {
224-
$this->requests[$requestId]['builder']->clean();
225-
}
227+
fclose($this->requests[$requestId]['stdin']);
226228

227229
unset($this->requests[$requestId]);
228230

@@ -286,7 +288,11 @@ protected function processBeginRequestRecord($requestId, $contentData)
286288

287289
$keepAlive = DaemonInterface::FCGI_KEEP_CONNECTION & $content['flags'];
288290

289-
$this->requests[$requestId] = ['keepAlive' => $keepAlive];
291+
$this->requests[$requestId] = [
292+
'keepAlive' => $keepAlive,
293+
'stdin' => fopen('php://temp', 'r+'),
294+
'params' => [],
295+
];
290296

291297
if ($this->shutdown) {
292298
$this->endRequest($requestId, 0, DaemonInterface::FCGI_OVERLOADED);
@@ -297,8 +303,6 @@ protected function processBeginRequestRecord($requestId, $contentData)
297303
$this->endRequest($requestId, 0, DaemonInterface::FCGI_UNKNOWN_ROLE);
298304
return;
299305
}
300-
301-
$this->requests[$requestId]['builder'] = new RequestBuilder();
302306
}
303307

304308
/**
@@ -349,7 +353,7 @@ protected function processParamsRecord($requestId, $contentData)
349353

350354
$content = unpack($contentFormat, $contentData);
351355

352-
$this->requests[$requestId]['builder']->addParam($content['name'], $content['value']);
356+
$this->requests[$requestId]['params'][$content['name']] = $content['value'];
353357
}
354358

355359
/**
@@ -372,7 +376,7 @@ protected function processStdinRecord($requestId, $contentData)
372376
return;
373377
}
374378

375-
$this->requests[$requestId]['builder']->addStdin($contentData);
379+
fwrite($this->requests[$requestId]['stdin'], $contentData);
376380
}
377381

378382
/**
@@ -400,16 +404,23 @@ protected function processAbortRequestRecord($requestId)
400404
*/
401405
protected function dispatchRequest($requestId)
402406
{
403-
$request = $this->requests[$requestId]['builder']->getRequest();
407+
$request = new Request(
408+
$this->requests[$requestId]['params'],
409+
$this->requests[$requestId]['stdin']
410+
);
404411

405412
try {
406413
$response = $this->kernel->handleRequest($request);
407414

408-
if (!$response instanceof ResponseInterface) {
409-
throw new \LogicException('Kernel must return a PSR-7 HTTP response message');
415+
if ($response instanceof ResponseInterface) {
416+
$this->sendResponse($requestId, $response);
417+
} elseif ($response instanceof HttpFoundationResponse) {
418+
$this->sendHttpFoundationResponse($requestId, $response);
419+
} else {
420+
throw new \LogicException('Kernel must return a PSR-7 or HttpFoundation response message');
410421
}
411422

412-
$this->sendResponse($requestId, $response);
423+
$this->endRequest($requestId);
413424
} catch (\Exception $exception) {
414425
$this->logger->error($exception->getMessage());
415426

@@ -425,7 +436,10 @@ protected function dispatchRequest($requestId)
425436
*/
426437
protected function sendResponse($requestId, ResponseInterface $response)
427438
{
428-
$headerData = "Status: {$response->getStatusCode()} {$response->getReasonPhrase()}\r\n";
439+
$statusCode = $response->getStatusCode();
440+
$reasonPhrase = $response->getReasonPhrase();
441+
442+
$headerData = "Status: {$statusCode} {$reasonPhrase}\r\n";
429443

430444
foreach ($response->getHeaders() as $name => $values) {
431445
$headerData .= $name.': '.implode(', ', $values)."\r\n";
@@ -434,8 +448,25 @@ protected function sendResponse($requestId, ResponseInterface $response)
434448
$headerData .= "\r\n";
435449

436450
$this->writeResponse($requestId, $headerData, $response->getBody());
451+
}
437452

438-
$this->writeRecord($requestId, DaemonInterface::FCGI_STDOUT);
439-
$this->endRequest($requestId);
453+
/**
454+
* Send a HttpFoundation response to the client.
455+
*
456+
* @param int $requestId The request id to respond to
457+
* @param HttpFoundationResponse $response The HTTP foundation response message
458+
*/
459+
protected function sendHttpFoundationResponse($requestId, HttpFoundationResponse $response)
460+
{
461+
$statusCode = $response->getStatusCode();
462+
463+
$headerData = "Status: {$statusCode}\r\n";
464+
$headerData .= $response->headers . "\r\n";
465+
466+
$stream = new Stream('php://memory', 'r+');
467+
$stream->write($response->getContent());
468+
$stream->rewind();
469+
470+
$this->writeResponse($requestId, $headerData, $stream);
440471
}
441472
}
Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22

33
namespace PHPFastCGI\FastCGIDaemon\Http;
44

5+
use Symfony\Component\HttpFoundation\Request as HttpFoundationRequest;
56
use Zend\Diactoros\ServerRequest;
67
use Zend\Diactoros\ServerRequestFactory;
78

89
/**
9-
* The default implementation of the RequestBuilderInterface using the Zend
10-
* Diactoros PSR-7 implementation.
10+
* The default implementation of the RequestInterface
1111
*/
12-
class RequestBuilder implements RequestBuilderInterface
12+
class Request implements RequestInterface
1313
{
1414
/**
15-
* @var string[]
15+
* @var array
1616
*/
1717
protected $params;
1818

@@ -23,41 +23,51 @@ class RequestBuilder implements RequestBuilderInterface
2323

2424
/**
2525
* Constructor.
26+
*
27+
* @param array $params The FastCGI server params as an associative array
28+
* @param resource $stdin The FastCGI stdin data as a stream resource
2629
*/
27-
public function __construct()
30+
public function __construct(array $params, $stdin)
2831
{
2932
$this->params = [];
30-
$this->stdin = fopen('php://temp', 'r+');
33+
34+
foreach ($params as $name => $value) {
35+
$this->params[strtoupper($name)] = $value;
36+
}
37+
38+
$this->stdin = $stdin;
39+
40+
rewind($this->stdin);
3141
}
3242

3343
/**
3444
* {@inheritdoc}
3545
*/
36-
public function addParam($name, $value)
46+
public function getParams()
3747
{
38-
$this->params[strtoupper($name)] = $value;
48+
return $this->params;
3949
}
4050

4151
/**
4252
* {@inheritdoc}
4353
*/
44-
public function addStdin($data)
54+
public function getQuery()
4555
{
46-
fwrite($this->stdin, $data);
56+
$query = [];
57+
58+
if (isset($this->params['QUERY_STRING'])) {
59+
parse_str($this->params['QUERY_STRING'], $query);
60+
}
61+
62+
return $query;
4763
}
4864

4965
/**
5066
* {@inheritdoc}
5167
*/
52-
public function getRequest()
68+
public function getPost()
5369
{
54-
rewind($this->stdin);
55-
56-
$query = $post = $cookies = [];
57-
58-
if (isset($this->params['QUERY_STRING'])) {
59-
parse_str($this->params['QUERY_STRING'], $query);
60-
}
70+
$post = [];
6171

6272
if (isset($this->params['REQUEST_METHOD']) && isset($this->params['CONTENT_TYPE'])) {
6373
$requestMethod = $this->params['REQUEST_METHOD'];
@@ -71,6 +81,16 @@ public function getRequest()
7181
}
7282
}
7383

84+
return $post;
85+
}
86+
87+
/**
88+
* {@inheritdoc}
89+
*/
90+
public function getCookies()
91+
{
92+
$cookies = [];
93+
7494
if (isset($this->params['HTTP_COOKIE'])) {
7595
$cookiePairs = explode(';', $this->params['HTTP_COOKIE']);
7696

@@ -80,6 +100,26 @@ public function getRequest()
80100
}
81101
}
82102

103+
return $cookies;
104+
}
105+
106+
/**
107+
* {@inheritdoc}
108+
*/
109+
public function getStdin()
110+
{
111+
return $this->stdin;
112+
}
113+
114+
/**
115+
* {@inheritdoc}
116+
*/
117+
public function getServerRequest()
118+
{
119+
$query = $this->getQuery();
120+
$post = $this->getPost();
121+
$cookies = $this->getCookies();
122+
83123
$server = ServerRequestFactory::normalizeServer($this->params);
84124
$headers = ServerRequestFactory::marshalHeaders($server);
85125
$uri = ServerRequestFactory::marshalUriFromServer($server, $headers);
@@ -96,11 +136,12 @@ public function getRequest()
96136
/**
97137
* {@inheritdoc}
98138
*/
99-
public function clean()
139+
public function getHttpFoundationRequest()
100140
{
101-
fclose($this->stdin);
141+
$query = $this->getQuery();
142+
$post = $this->getPost();
143+
$cookies = $this->getCookies();
102144

103-
$this->params = [];
104-
$this->stdin = null;
145+
return new HttpFoundationRequest($query, $post, [], $cookies, [], $this->params, $this->stdin);
105146
}
106147
}

0 commit comments

Comments
 (0)