Skip to content

Commit db3ed0e

Browse files
committed
Initial commit
0 parents  commit db3ed0e

File tree

8 files changed

+651
-0
lines changed

8 files changed

+651
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/vendor/

Bootstraps/Laravel.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace PHPPM\Bootstraps;
4+
5+
use Stack\Builder;
6+
7+
/**
8+
* A default bootstrap for the Laravel framework
9+
*/
10+
class Laravel implements StackableBootstrapInterface
11+
{
12+
/**
13+
* @var string|null The application environment
14+
*/
15+
protected $appenv;
16+
17+
/**
18+
* Store the application
19+
*
20+
* @var Symfony\Component\HttpKernel\HttpKernelInterface
21+
*/
22+
protected $app;
23+
24+
/**
25+
* Instantiate the bootstrap, storing the $appenv
26+
*/
27+
public function __construct($appenv)
28+
{
29+
$this->appenv = $appenv;
30+
}
31+
32+
/**
33+
* Create a Symfony application
34+
*/
35+
public function getApplication()
36+
{
37+
if (file_exists(__DIR__ . '/autoload.php') && file_exists(__DIR__ . '/start.php')) {
38+
require_once __DIR__ . '/autoload.php';
39+
$this->app = require_once __DIR__ . '/start.php';
40+
}
41+
42+
return $this->app;
43+
}
44+
45+
/**
46+
* Return the StackPHP stack.
47+
*/
48+
public function getStack(Builder $stack)
49+
{
50+
$sessionReject = $this->app->bound('session.reject') ? $this->app['session.reject'] : null;
51+
52+
$stack
53+
->push('Illuminate\Cookie\Guard', $this->app['encrypter'])
54+
->push('Illuminate\Cookie\Queue', $this->app['cookie'])
55+
->push('Illuminate\Session\Middleware', $this->app['session'], $sessionReject);
56+
57+
return $stack;
58+
}
59+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace PHPPM\Bootstraps;
4+
5+
use Stack\Builder;
6+
7+
/**
8+
* Stack\Builder extension for use with HttpKernel middlewares
9+
*/
10+
interface StackableBootstrapInterface extends BootstrapInterface
11+
{
12+
public function getStack(Builder $stack);
13+
}

Bootstraps/Symfony.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace PHPPM\Bootstraps;
4+
5+
use Stack\Builder;
6+
use Symfony\Component\HttpKernel\HttpCache\Store;
7+
8+
/**
9+
* A default bootstrap for the Symfony framework
10+
*/
11+
class Symfony implements StackableBootstrapInterface
12+
{
13+
/**
14+
* @var string|null The application environment
15+
*/
16+
protected $appenv;
17+
18+
/**
19+
* Instantiate the bootstrap, storing the $appenv
20+
*/
21+
public function __construct($appenv)
22+
{
23+
$this->appenv = $appenv;
24+
}
25+
26+
/**
27+
* Create a Symfony application
28+
*/
29+
public function getApplication()
30+
{
31+
if (file_exists('./app/AppKernel.php')) {
32+
require_once './app/AppKernel.php';
33+
}
34+
35+
$app = new \AppKernel($this->appenv, false);
36+
$app->loadClassCache();
37+
38+
return $app;
39+
}
40+
41+
/**
42+
* Return the StackPHP stack.
43+
*/
44+
public function getStack(Builder $stack)
45+
{
46+
return $stack;
47+
}
48+
}

Bridges/HttpKernel.php

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
3+
namespace PHPPM\Bridges;
4+
5+
use PHPPM\AppBootstrapInterface;
6+
use PHPPM\Bootstraps\BootstrapInterface;
7+
use PHPPM\Bridges\BridgeInterface;
8+
use React\Http\Request as ReactRequest;
9+
use React\Http\Response as ReactResponse;
10+
use Stack\Builder;
11+
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
12+
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
13+
14+
class HttpKernel implements BridgeInterface
15+
{
16+
/**
17+
* An application implementing the HttpKernelInterface
18+
*
19+
* @var \Symfony\Component\HttpFoundation\HttpKernelInterface
20+
*/
21+
protected $application;
22+
23+
/**
24+
* Bootstrap an application implementing the HttpKernelInterface.
25+
*
26+
* In the process of bootstrapping we decorate our application with any number of
27+
* *middlewares* using StackPHP's Stack\Builder.
28+
*
29+
* The app bootstraping itself is actually proxied off to an object implementing the
30+
* PHPPM\Bridges\BridgeInterface interface which should live within your app itself and
31+
* be able to be autoloaded.
32+
*
33+
* @param string $appBootstrap The name of the class used to bootstrap the application
34+
* @param string|null $appBootstrap The environment your application will use to bootstrap (if any)
35+
* @see http://stackphp.com
36+
*/
37+
public function bootstrap($appBootstrap, $appenv)
38+
{
39+
require_once './vendor/autoload.php';
40+
41+
if (false === class_exists($appBootstrap)) {
42+
$appBootstrap = '\\' . $appBootstrap;
43+
if (false === class_exists($appBootstrap)) {
44+
return false;
45+
}
46+
}
47+
48+
$bootstrap = new $appBootstrap($appenv);
49+
50+
if ($bootstrap instanceof BootstrapInterface) {
51+
$this->application = $bootstrap->getApplication();
52+
53+
if ($bootstrap instanceof StackableBootstrapInterface) {
54+
$stack = new Builder();
55+
$stack = $bootstrap->getStack($stack);
56+
$this->application = $stack->resolve($this->application);
57+
}
58+
}
59+
}
60+
61+
/**
62+
* Handle a request using a HttpKernelInterface implementing application.
63+
*
64+
* @param \React\Http\Request $request
65+
* @param \React\Http\Response $response
66+
*/
67+
public function onRequest(ReactRequest $request, ReactResponse $response)
68+
{
69+
if (null === $this->application) {
70+
return;
71+
}
72+
73+
$content = '';
74+
$headers = $request->getHeaders();
75+
$contentLength = isset($headers['Content-Length']) ? (int) $headers['Content-Length'] : 0;
76+
77+
$request->on('data', function($data)
78+
use ($request, $response, &$content, $contentLength)
79+
{
80+
// read data (may be empty for GET request)
81+
$content .= $data;
82+
83+
// handle request after receive
84+
if (strlen($content) >= $contentLength) {
85+
$syRequest = self::mapRequest($request, $content);
86+
87+
try {
88+
$syResponse = $this->application->handle($syRequest);
89+
} catch (\Exception $exception) {
90+
$response->writeHead(500); // internal server error
91+
$response->end();
92+
return;
93+
}
94+
95+
self::mapResponse($response, $syResponse);
96+
}
97+
});
98+
}
99+
100+
/**
101+
* Convert React\Http\Request to Symfony\Component\HttpFoundation\Request
102+
*
103+
* @param ReactRequest $reactRequest
104+
* @return SymfonyRequest $syRequest
105+
*/
106+
protected static function mapRequest(ReactRequest $reactRequest, $content)
107+
{
108+
$method = $reactRequest->getMethod();
109+
$headers = $reactRequest->getHeaders();
110+
$query = $reactRequest->getQuery();
111+
$post = array();
112+
113+
// parse body?
114+
if (isset($headers['Content-Type']) && (0 === strpos($headers['Content-Type'], 'application/x-www-form-urlencoded')
115+
&& in_array(strtoupper($method), array('PUT', 'DELETE', 'PATCH'))
116+
) {
117+
parse_str($content, $post);
118+
}
119+
120+
$syRequest = new SymfonyRequest(
121+
// $query, $request, $attributes, $cookies, $files, $server, $content
122+
$query, $post, array(), array(), array(), array(), $content
123+
);
124+
125+
$syRequest->setMethod($method);
126+
$syRequest->headers->replace($headers);
127+
$syRequest->server->set('REQUEST_URI', $reactRequest->getPath());
128+
$syRequest->server->set('SERVER_NAME', explode(':', $headers['Host'])[0]);
129+
130+
return $syRequest;
131+
}
132+
133+
/**
134+
* Convert Symfony\Component\HttpFoundation\Response to React\Http\Response
135+
*
136+
* @param ReactResponse $reactResponse
137+
* @param SymfonyResponse $syResponse
138+
*/
139+
protected static function mapResponse(ReactResponse $reactResponse,
140+
SymfonyResponse $syResponse)
141+
{
142+
$headers = $syResponse->headers->all();
143+
$reactResponse->writeHead($syResponse->getStatusCode(), $headers);
144+
$reactResponse->end($syResponse->getContent());
145+
}
146+
}

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# PHP-PM HttpKernel Adapter
2+
3+
HttpKernel adapter for use of Symfony and Laravel frameworks with PHP-PM. See https://github.com/php-pm/php-pm.
4+
5+
### Setup
6+
7+
1. Install PHP-PM
8+
9+
composer require php-pm/php-pm:dev-master
10+
11+
2. Install HttpKernel Adapter
12+
13+
composer require php-pm/httpkernel-adapter:dev-master
14+

composer.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "php-pm/httpkernel-adapter",
3+
"require": {
4+
"stack/builder": "1.0.*",
5+
"symfony/http-foundation": "2.6.*",
6+
"symfony/http-kernel": "2.6.*"
7+
},
8+
"autoload": {
9+
"psr-4": {
10+
"PHPPM\\": ""
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)