Skip to content

Commit a6050f1

Browse files
authored
Merge pull request #63 from leocavalcante/swoole-psr
Adding PSR RequestHandlerInterface support for Swoole
2 parents 6c47cd0 + 0d36ae6 commit a6050f1

20 files changed

+629
-2
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
"Runtime\\React\\": "src/react/src",
4545
"Runtime\\RoadRunnerNyholm\\": "src/roadrunner-nyholm/src",
4646
"Runtime\\RoadRunnerSymfonyNyholm\\": "src/roadrunner-symfony-nyholm/src",
47-
"Runtime\\Swoole\\": "src/swoole/src"
47+
"Runtime\\Swoole\\": "src/swoole/src",
48+
"Runtime\\SwooleNyholm\\": "src/swoole-nyholm/src"
4849
}
4950
},
5051
"autoload-dev": {

src/swoole-nyholm/.github/FUNDING.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# These are supported funding model platforms
2+
3+
github: [nyholm]

src/swoole-nyholm/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/composer.lock
2+
/phpunit.xml
3+
/vendor/

src/swoole-nyholm/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Change Log
2+
3+
## 0.1.0
4+
5+
First version

src/swoole-nyholm/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2021 Tobias Nyholm
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

src/swoole-nyholm/README.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Swoole Runtime with nyholm/psr7
2+
3+
A runtime for [Swoole](https://www.swoole.co.uk/).
4+
5+
If you are new to the Symfony Runtime component, read more in the [main readme](https://github.com/php-runtime/runtime).
6+
7+
## Installation
8+
9+
```
10+
composer require runtime/swoole-nyholm
11+
```
12+
13+
## Usage
14+
15+
Define the environment variable `APP_RUNTIME` for your application.
16+
17+
```
18+
APP_RUNTIME=Runtime\SwooleNyholm\Runtime
19+
```
20+
21+
### Pure PHP
22+
23+
```php
24+
// public/index.php
25+
26+
use Swoole\Http\Request;
27+
use Swoole\Http\Response;
28+
29+
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
30+
31+
return function () {
32+
return function (Request $request, Response $response) {
33+
$response->header("Content-Type", "text/plain");
34+
$response->end("Hello World\n");
35+
};
36+
};
37+
```
38+
39+
### PSR
40+
41+
```php
42+
// public/index.php
43+
44+
use Nyholm\Psr7\Response;
45+
use Psr\Http\Message\ResponseInterface;
46+
use Psr\Http\Message\ServerRequestInterface;
47+
use Psr\Http\Server\RequestHandlerInterface;
48+
49+
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
50+
51+
class App implements RequestHandlerInterface {
52+
public function handle(ServerRequestInterface $request): ResponseInterface {
53+
$name = $request->getQueryParams()['name'] ?? 'World';
54+
return new Response(200, ['Server' => 'swoole-runtime'], "Hello, $name!");
55+
}
56+
}
57+
58+
return function(): RequestHandlerInterface {
59+
return new App();
60+
};
61+
```
62+
63+
## Using Options
64+
65+
You can define some configurations using Symfony's Runtime `APP_RUNTIME_OPTIONS` API.
66+
67+
| Option | Description | Default |
68+
| --- | --- | --- |
69+
| `host` | The host where the server should binds to (precedes `SWOOLE_HOST` environment variable) | `127.0.0.1` |
70+
| `port` | The port where the server should be listing (precedes `SWOOLE_PORT` environment variable) | `8000` |
71+
| `mode` | Swoole's server mode (precedes `SWOOLE_MODE` environment variable) | `SWOOLE_PROCESS` |
72+
| `settings` | All Swoole's server settings ([swoole.co.uk/docs/modules/swoole-server/configuration](https://www.swoole.co.uk/docs/modules/swoole-server/configuration)) | `[]` |
73+
74+
```php
75+
// public/index.php
76+
77+
use App\Kernel;
78+
79+
$_SERVER['APP_RUNTIME_OPTIONS'] = [
80+
'host' => '0.0.0.0',
81+
'port' => 9501,
82+
'mode' => SWOOLE_BASE,
83+
'settings' => [
84+
\Swoole\Constant::OPTION_WORKER_NUM => swoole_cpu_num() * 2,
85+
\Swoole\Constant::OPTION_ENABLE_STATIC_HANDLER => true,
86+
\Swoole\Constant::OPTION_DOCUMENT_ROOT => dirname(__DIR__).'/public'
87+
],
88+
];
89+
90+
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
91+
92+
return function (array $context) {
93+
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
94+
};
95+
```

src/swoole-nyholm/composer.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "runtime/swoole-nyholm",
3+
"type": "library",
4+
"description": "Swoole runtime with nyholm/psr7",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "Tobias Nyholm",
9+
"email": "[email protected]"
10+
}
11+
],
12+
"require": {
13+
"nyholm/psr7": "^1.4",
14+
"psr/http-server-handler": "^1.0",
15+
"symfony/runtime": "^5.3 || ^6.0"
16+
},
17+
"require-dev": {
18+
"symfony/phpunit-bridge": "^5.3"
19+
},
20+
"autoload": {
21+
"psr-4": {
22+
"Runtime\\SwooleNyholm\\": "src/"
23+
}
24+
},
25+
"autoload-dev": {
26+
"psr-4": {
27+
"Runtime\\SwooleNyholm\\Tests\\": "tests/"
28+
}
29+
},
30+
"minimum-stability": "dev",
31+
"prefer-stable": true
32+
}

src/swoole-nyholm/phpunit.xml.dist

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
4+
backupGlobals="false"
5+
colors="true"
6+
bootstrap="vendor/autoload.php"
7+
failOnRisky="true"
8+
failOnWarning="true"
9+
>
10+
<php>
11+
<ini name="error_reporting" value="-1"/>
12+
</php>
13+
<testsuites>
14+
<testsuite name="Unit">
15+
<directory>./tests/Unit</directory>
16+
</testsuite>
17+
</testsuites>
18+
<filter>
19+
<whitelist processUncoveredFilesFromWhitelist="true">
20+
<directory suffix=".php">src</directory>
21+
</whitelist>
22+
</filter>
23+
</phpunit>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Runtime\SwooleNyholm;
4+
5+
use Symfony\Component\Runtime\RunnerInterface;
6+
7+
/**
8+
* A simple runner that will run a callable.
9+
*
10+
* @author Tobias Nyholm <[email protected]>
11+
*/
12+
class CallableRunner implements RunnerInterface
13+
{
14+
/** @var ServerFactory */
15+
private $serverFactory;
16+
/** @var callable */
17+
private $application;
18+
19+
public function __construct(ServerFactory $serverFactory, callable $application)
20+
{
21+
$this->serverFactory = $serverFactory;
22+
$this->application = $application;
23+
}
24+
25+
public function run(): int
26+
{
27+
$this->serverFactory->createServer($this->application)->start();
28+
29+
return 0;
30+
}
31+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace Runtime\SwooleNyholm;
4+
5+
use Psr\Http\Server\RequestHandlerInterface;
6+
use Swoole\Http\Request;
7+
use Swoole\Http\Response;
8+
use Symfony\Component\Runtime\RunnerInterface;
9+
10+
class RequestHandlerRunner implements RunnerInterface
11+
{
12+
private const CHUNK_SIZE = 2097152; // 2MB
13+
14+
/**
15+
* @var ServerFactory
16+
*/
17+
private $serverFactory;
18+
19+
/**
20+
* @var RequestHandlerInterface
21+
*/
22+
private $application;
23+
24+
public function __construct(ServerFactory $serverFactory, RequestHandlerInterface $application)
25+
{
26+
$this->serverFactory = $serverFactory;
27+
$this->application = $application;
28+
}
29+
30+
public function run(): int
31+
{
32+
$this->serverFactory->createServer([$this, 'handle'])->start();
33+
34+
return 0;
35+
}
36+
37+
public function handle(Request $request, Response $response): void
38+
{
39+
$psrRequest = (new \Nyholm\Psr7\ServerRequest(
40+
$request->getMethod(),
41+
$request->server['request_uri'] ?? '/',
42+
array_change_key_case($request->server ?? [], CASE_UPPER),
43+
$request->rawContent(),
44+
'1.1',
45+
$request->server ?? []
46+
))
47+
->withQueryParams($request->get ?? []);
48+
49+
$psrResponse = $this->application->handle($psrRequest);
50+
51+
$response->setStatusCode($psrResponse->getStatusCode(), $psrResponse->getReasonPhrase());
52+
53+
foreach ($psrResponse->getHeaders() as $name => $values) {
54+
foreach ($values as $value) {
55+
$response->setHeader($name, $value);
56+
}
57+
}
58+
59+
$body = $psrResponse->getBody();
60+
$body->rewind();
61+
62+
if ($body->isReadable()) {
63+
if ($body->getSize() <= self::CHUNK_SIZE) {
64+
if ($contents = $body->getContents()) {
65+
$response->write($contents);
66+
}
67+
} else {
68+
while (!$body->eof() && ($contents = $body->read(self::CHUNK_SIZE))) {
69+
$response->write($contents);
70+
}
71+
}
72+
73+
$response->end();
74+
} else {
75+
$response->end((string) $body);
76+
}
77+
78+
$body->close();
79+
}
80+
}

0 commit comments

Comments
 (0)