Skip to content

Commit df092db

Browse files
author
Kent Richards
committed
Add DrupalKernel bridge.
1 parent 42d28b3 commit df092db

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed

Bridges/DrupalKernel.php

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
3+
namespace PHPPM\Bridges;
4+
5+
use PHPPM\AppBootstrapInterface;
6+
use PHPPM\Bootstraps\BootstrapInterface;
7+
use PHPPM\Bridges\BridgeInterface;
8+
use PHPPM\Bridges\HttpKernel as SymfonyBridge;
9+
use React\Http\Request as ReactRequest;
10+
use React\Http\Response as ReactResponse;
11+
use Stack\Builder;
12+
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
13+
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
14+
use Symfony\Component\HttpFoundation\StreamedResponse as SymfonyStreamedResponse;
15+
use Symfony\Component\HttpKernel\TerminableInterface;
16+
17+
18+
class DrupalKernel extends SymfonyBridge implements BridgeInterface
19+
{
20+
21+
/**
22+
* Handle a request using a HttpKernelInterface implementing application.
23+
*
24+
* @param \React\Http\Request $request
25+
* @param \React\Http\Response $response
26+
*/
27+
public function onRequest(ReactRequest $request, ReactResponse $response)
28+
{
29+
if (null === $this->application) {
30+
return;
31+
}
32+
33+
$content = '';
34+
$headers = $request->getHeaders();
35+
$contentLength = isset($headers['Content-Length']) ? (int) $headers['Content-Length'] : 0;
36+
37+
$request->on('data', function($data)
38+
use ($request, $response, &$content, $contentLength)
39+
{
40+
// read data (may be empty for GET request)
41+
$content .= $data;
42+
43+
// handle request after receive
44+
if (strlen($content) >= $contentLength) {
45+
$syRequest = self::mapRequest($request, $content);
46+
47+
try {
48+
$syResponse = $this->application->handle($syRequest);
49+
} catch (\Exception $exception) {
50+
$response->writeHead(500); // internal server error
51+
$response->end();
52+
return;
53+
}
54+
55+
self::mapResponse($response, $syResponse);
56+
57+
if ($this->application instanceof TerminableInterface) {
58+
$this->application->terminate($syRequest, $syResponse);
59+
}
60+
}
61+
});
62+
}
63+
64+
/**
65+
* Convert React\Http\Request to Symfony\Component\HttpFoundation\Request
66+
*
67+
* @param ReactRequest $reactRequest
68+
* @return SymfonyRequest $syRequest
69+
*/
70+
protected static function mapRequest(ReactRequest $reactRequest, $content)
71+
{
72+
$method = $reactRequest->getMethod();
73+
$headers = $reactRequest->getHeaders();
74+
$query = $reactRequest->getQuery();
75+
$post = array();
76+
77+
// parse body?
78+
if (isset($headers['Content-Type']) && (0 === strpos($headers['Content-Type'], 'application/x-www-form-urlencoded'))
79+
&& in_array(strtoupper($method), array('POST', 'PUT', 'DELETE', 'PATCH'))
80+
) {
81+
parse_str($content, $post);
82+
}
83+
84+
$cookies = array();
85+
if (isset($headers['Cookie'])) {
86+
$headersCookie = explode(';', $headers['Cookie']);
87+
foreach ($headersCookie as $cookie) {
88+
list($name, $value) = explode('=', trim($cookie));
89+
$cookies[$name] = $value;
90+
}
91+
}
92+
93+
$parameters =
94+
in_array(strtoupper($method), array('POST', 'PUT', 'DELETE', 'PATCH'))
95+
? $post
96+
: $query;
97+
$syRequest = SymfonyRequest::create(
98+
// $uri, $method, $parameters, $cookies, $files, $server, $content
99+
$reactRequest->getPath(),
100+
$method,
101+
$parameters,
102+
$cookies,
103+
array(),
104+
array(),
105+
$content
106+
);
107+
$syRequest->headers->replace($headers);
108+
109+
// Set CGI/1.1 (RFC 3875) server vars.
110+
// @see http://php.net/manual/en/reserved.variables.server.php
111+
// @see http://www.faqs.org/rfcs/rfc3875.html
112+
$serverVars = array_merge(
113+
$syRequest->server->all(),
114+
array(
115+
'DOCUMENT_ROOT' => $_SERVER['DOCUMENT_ROOT'],
116+
'GATEWAY_INTERFACE' => 'CGI/1.1',
117+
'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'],
118+
// SCRIPT_FILENAME contains the name of the php-pm startup script.
119+
// Must override here.
120+
'SCRIPT_FILENAME' => $_SERVER['DOCUMENT_ROOT'] . $_SERVER['SCRIPT_NAME'],
121+
)
122+
);
123+
$syRequest->server->replace($serverVars);
124+
125+
return $syRequest;
126+
}
127+
128+
129+
/**
130+
* Convert Symfony\Component\HttpFoundation\Response to React\Http\Response
131+
*
132+
* @param ReactResponse $reactResponse
133+
* @param SymfonyResponse $syResponse
134+
*/
135+
protected static function mapResponse(ReactResponse $reactResponse,
136+
SymfonyResponse $syResponse)
137+
{
138+
$headers = $syResponse->headers->all();
139+
$reactResponse->writeHead($syResponse->getStatusCode(), $headers);
140+
141+
// @TODO convert StreamedResponse in an async manner
142+
if ($syResponse instanceof SymfonyStreamedResponse) {
143+
ob_start();
144+
$syResponse->sendContent();
145+
$content = ob_get_contents();
146+
ob_end_clean();
147+
}
148+
else {
149+
$content = $syResponse->getContent();
150+
}
151+
152+
$reactResponse->end($content);
153+
}
154+
155+
}

0 commit comments

Comments
 (0)