Skip to content

Commit 61f3f0c

Browse files
author
=
committed
Everything:
- Kernel Wrapper (and test) - Response implementation - Run command - README - Composer - PHPUnit - Git ignore
1 parent 6a3eb2c commit 61f3f0c

File tree

10 files changed

+1667
-44
lines changed

10 files changed

+1667
-44
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/vendor/
2+
/build/
3+
/coverage/

Bridge/KernelWrapper.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace PHPFastCGI\SpeedfonyBundle\Bridge;
4+
5+
use PHPFastCGI\FastCGIDaemon\KernelInterface;
6+
use PHPFastCGI\FastCGIDaemon\Http\RequestEnvironmentInterface;
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\HttpKernel\Kernel;
9+
10+
class KernelWrapper implements KernelInterface
11+
{
12+
/**
13+
* @var Kernel
14+
*/
15+
protected $kernel;
16+
17+
/**
18+
* Constructor.
19+
*
20+
* @param Kernel $kernel
21+
*/
22+
public function __construct(Kernel $kernel)
23+
{
24+
$this->kernel = $kernel;
25+
}
26+
27+
/**
28+
* {@inheritdoc}
29+
*/
30+
public function handleRequest(RequestEnvironmentInterface $requestEnvironment)
31+
{
32+
$server = $requestEnvironment->getServer();
33+
$query = $requestEnvironment->getQuery();
34+
$post = $requestEnvironment->getPost();
35+
$files = $requestEnvironment->getFiles();
36+
$cookies = $requestEnvironment->getCookies();
37+
$content = null;
38+
39+
$body = $requestEnvironment->getBody();
40+
41+
if (null !== $body) {
42+
$content = stream_get_contents($body);
43+
}
44+
45+
$request = new Request($query, $post, [], $cookies, $files, $server, $content);
46+
47+
$response = $this->kernel->handle($request);
48+
49+
$statusCode = $response->getStatusCode();
50+
51+
if (isset($response::$statusTexts[$statusCode])) {
52+
$reasonPhrase = $response::$statusTexts[$statusCode];
53+
} else {
54+
$reasonPhrase = '';
55+
}
56+
57+
$headers = explode("\r\n", trim((string) $response->headers));
58+
59+
return new Response($statusCode, $reasonPhrase, $headers, $response->getContent());
60+
}
61+
}

Bridge/Response.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace PHPFastCGI\SpeedfonyBundle\Bridge;
4+
5+
use PHPFastCGI\FastCGIDaemon\Http\ResponseInterface;
6+
7+
class Response implements ResponseInterface
8+
{
9+
/**
10+
* @var int
11+
*/
12+
protected $statusCode;
13+
14+
/**
15+
* @var string
16+
*/
17+
protected $reasonPhrase;
18+
19+
/**
20+
* @var string[]
21+
*/
22+
protected $headerLines;
23+
24+
/**
25+
* @var resource|string|null
26+
*/
27+
protected $body;
28+
29+
public function __construct($statusCode, $reasonPhrase, array $headerLines, $body = null)
30+
{
31+
$this->statusCode = $statusCode;
32+
$this->reasonPhrase = $reasonPhrase;
33+
$this->headerLines = $headerLines;
34+
$this->body = $body;
35+
}
36+
37+
/**
38+
* {@inheritdoc}
39+
*/
40+
public function getStatusCode()
41+
{
42+
return $this->statusCode;
43+
}
44+
45+
/**
46+
* {@inheritdoc}
47+
*/
48+
public function getReasonPhrase()
49+
{
50+
return $this->reasonPhrase;
51+
}
52+
53+
/**
54+
* {@inheritdoc}
55+
*/
56+
public function getHeaderLines()
57+
{
58+
return $this->headerLines;
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
public function getBody()
65+
{
66+
return $this->body;
67+
}
68+
}

Command/DaemonRunCommand.php

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@
22

33
namespace PHPFastCGI\SpeedfonyBundle\Command;
44

5-
use PHPFastCGI\FastCGIDaemon\SocketDaemon;
6-
use PHPFastCGI\FastCGIDaemon\StreamSocketDaemon;
5+
use PHPFastCGI\FastCGIDaemon\Daemon;
6+
use PHPFastCGI\SpeedfonyBundle\Bridge\KernelWrapper;
77
use Symfony\Component\Console\Command\Command;
8-
use Symfony\Component\Console\Input\InputArgument;
98
use Symfony\Component\Console\Input\InputInterface;
109
use Symfony\Component\Console\Input\InputOption;
1110
use Symfony\Component\Console\Output\OutputInterface;
12-
use Symfony\Component\HttpFoundation\Request;
1311
use Symfony\Component\HttpKernel\Kernel;
1412

1513
class DaemonRunCommand extends Command
@@ -28,53 +26,22 @@ protected function configure()
2826
$this
2927
->setName('speedfony:daemon:run')
3028
->setDescription('Execute the FCGI daemon')
31-
->addArgument('cycles', InputArgument::OPTIONAL, 'Request cycles to live for, 0 means infinite (default is 20)', 20)
32-
->addOption('port', null, InputOption::VALUE_REQUIRED, 'Port to listen on');
29+
->addOption('target', null, InputOption::VALUE_REQUIRED, 'Socket path');
3330
}
3431

3532
protected function execute(InputInterface $input, OutputInterface $output)
3633
{
37-
$getIntegerInput = function ($type, $name, $minimumValue) use ($input) {
38-
if ('argument' === $type) {
39-
$value = (string) $input->getArgument($name);
40-
} elseif ('option' === $type) {
41-
$value = (string) $input->getOption($name);
42-
} else {
43-
throw new \LogicException('Unknown input type: ' . $type);
44-
}
34+
$target = $input->getOption('target');
4535

46-
$intValue = (int) $value;
47-
48-
if ((string) $intValue !== $value) {
49-
throw new \Exception('The ' . $argument . ' argument must be an integer');
50-
} elseif ($value < $minimumValue) {
51-
throw new \Exception('The ' . $argument . ' argument must be at least ' . $minimumValue);
52-
}
53-
54-
return $value;
55-
};
56-
57-
if (null !== $input->getOption('port')) {
58-
$daemon = new SocketDaemon($getIntegerInput('option', 'port', 0));
36+
if (null !== $target) {
37+
$stream = stream_socket_server($target);
5938
} else {
60-
$daemon = new StreamSocketDaemon();
39+
$stream = fopen('php://fd/' . Daemon::FCGI_LISTENSOCK_FILENO, 'r');
6140
}
6241

63-
$maximumCycles = $getIntegerInput('argument', 'cycles', 0);
64-
65-
for($cycles = 0; ($maximumCycles == 0) || $cycles < $maximumCycles; $cycles++) {
66-
$request = $daemon->getRequest();
42+
$kernelWrapper = new KernelWrapper($this->kernel);
6743

68-
$httpRequest = new Request(array(), array(), array(), array(),
69-
array(), $request->getServer(), $request->getContent());
70-
71-
$response = str_replace(
72-
array('HTTP/1.1 ', 'HTTP/1.0 '),
73-
array('Status: ', 'Status: '),
74-
(string) $this->kernel->handle($httpRequest)
75-
);
76-
77-
$request->respond($response);
78-
}
44+
$daemon = new Daemon($stream);
45+
$daemon->run($kernelWrapper);
7946
}
8047
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
# SpeedfonyBundle
1+
# Speedfony Bundle
22

33
A symfony2 bundle which allows applications to reduce overheads by exposing symfony's Request-Response structure to a Fast CGI daemon.

Tests/Bridge/KernelWrapperTest.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
namespace PHPFastCGI\SpeedfonyBundle\Tests\Bridge;
4+
5+
use PHPFastCGI\FastCGIDaemon\Http\RequestEnvironmentBuilder;
6+
use PHPFastCGI\SpeedfonyBundle\Bridge\KernelWrapper;
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\HttpFoundation\Response;
9+
10+
class KernelWrapperTest extends \PHPUnit_Framework_TestCase
11+
{
12+
/**
13+
* @var string[]
14+
*/
15+
protected $params;
16+
17+
/**
18+
* @var string
19+
*/
20+
protected $content;
21+
22+
public function testKernelWrapper()
23+
{
24+
// Build response
25+
$response = new Response('Hello World', 200);
26+
27+
// Mock kernel and get wrapper
28+
$kernelMock = new MockKernel(function (Request $request) use ($response) {
29+
foreach ($this->params as $name => $value) {
30+
$this->assertEquals($request->server->get(strtoupper($name)), $value);
31+
}
32+
33+
$this->assertEquals($request->query->all(), ['bar' => 'foo', 'world' => 'hello' ]);
34+
$this->assertEquals($request->request->all(), ['foo' => 'bar', 'hello' => 'world' ]);
35+
$this->assertEquals($request->cookies->all(), ['one' => 'two', 'three' => 'four', 'five' => 'six' ]);
36+
37+
return $response;
38+
});
39+
40+
$kernelWrapper = new KernelWrapper($kernelMock);
41+
42+
// Build request
43+
$builder = new RequestEnvironmentBuilder();
44+
45+
$this->params = [
46+
'SERVER_PROTOCOL' => 'HTTP/1.1',
47+
'REQUEST_METHOD' => 'POST',
48+
'content_type' => 'application/x-www-form-urlencoded',
49+
'REQUEST_URI' => '/my-page',
50+
'QUERY_STRING' => 'bar=foo&world=hello',
51+
'HTTP_cookie' => 'one=two; three=four; five=six',
52+
];
53+
54+
$this->content = 'foo=bar&hello=world';
55+
56+
foreach ($this->params as $name => $value) {
57+
$builder->addParam($name, $value);
58+
}
59+
60+
$builder->addStdin($this->content);
61+
62+
$requestEnvironment = $builder->getRequestEnvironment();
63+
64+
// Hand to callback
65+
$returnedResponse = $kernelWrapper->handleRequest($requestEnvironment);
66+
67+
$returnedOutput = (
68+
'HTTP/' . $response->getProtocolVersion() . ' ' . $returnedResponse->getStatusCode() . ' ' . $returnedResponse->getReasonPhrase() . "\r\n" .
69+
implode("\r\n", $returnedResponse->getHeaderLines()) . "\r\n\r\n" .
70+
$returnedResponse->getBody()
71+
);
72+
73+
$this->assertEquals($returnedOutput, (string) $response);
74+
}
75+
}

Tests/Bridge/MockKernel.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace PHPFastCGI\SpeedfonyBundle\Tests\Bridge;
4+
5+
use Symfony\Component\Config\Loader\LoaderInterface;
6+
use Symfony\Component\HttpFoundation\Request;
7+
use Symfony\Component\HttpKernel\Kernel;
8+
9+
class MockKernel extends Kernel
10+
{
11+
/**
12+
* @var callable
13+
*/
14+
protected $callback;
15+
16+
/**
17+
* Constructor.
18+
*
19+
* @param callable $callback
20+
*/
21+
public function __construct($callback)
22+
{
23+
$this->callback = $callback;
24+
25+
parent::__construct('dev', false);
26+
}
27+
28+
/**
29+
* {@inheritdoc}
30+
*/
31+
public function registerBundles()
32+
{
33+
return [];
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
public function registerContainerConfiguration(LoaderInterface $loader)
40+
{
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function handle(Request $request, $type = 1, $catch = true)
47+
{
48+
return call_user_func($this->callback, $request);
49+
}
50+
}

composer.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "phpfastcgi/speedfony-bundle",
3+
"description": "A bundle to integrate a FastCGI daemon with the symfony2 framework",
4+
"keywords": ["speedfony", "bundle", "fastcgi", "server", "fast cgi", "daemon"],
5+
"type": "symfony-bundle",
6+
"license": "GPL v2",
7+
"authors": [
8+
{
9+
"name": "Andrew Carter",
10+
"email": "andrewcarter1992@gmail.com"
11+
}
12+
],
13+
"require": {
14+
"php": ">=5.4.0",
15+
"symfony/dependency-injection": "~2.0",
16+
"symfony/framework-bundle": "~2.0",
17+
"symfony/http-kernel": "~2.0",
18+
"symfony/console": "~2.0",
19+
"symfony/config": "~2.0",
20+
"phpfastcgi/fastcgi-daemon": "0.1.0"
21+
},
22+
"require-dev": {
23+
"satooshi/php-coveralls": "dev-master"
24+
},
25+
"autoload": {
26+
"psr-4": {
27+
"PHPFastCGI\\SpeedfonyBundle\\": ""
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)