Skip to content

Commit 0eb0bc9

Browse files
authored
Merge pull request #1 from spiral/feature/code-improvements
Code improvements
2 parents aec4675 + 178ebd8 commit 0eb0bc9

File tree

8 files changed

+364
-150
lines changed

8 files changed

+364
-150
lines changed

README.md

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,57 +26,87 @@ with much greater performance and flexibility.
2626

2727
Repository:
2828
--------
29+
2930
This repository contains the codebase PSR-7 PHP workers.
3031
Check [spiral/roadrunner](https://github.com/spiral/roadrunner) to get application server.
3132

3233
Installation:
3334
--------
35+
3436
To install application server and HTTP codebase:
3537

3638
```bash
3739
$ composer require spiral/roadrunner-http nyholm/psr7
38-
$ vendor/bin/rr get-binary
40+
```
41+
42+
You can use the convenient installer to download the latest available compatible version of RoadRunner assembly:
43+
44+
```bash
45+
$ composer require spiral/roadrunner-cli --dev
46+
```
47+
48+
To download latest version of application server:
49+
50+
```bash
51+
$ vendor/bin/rr get
3952
```
4053

4154
> You can use any [PSR-17 compatible implementation](https://github.com/search?q=psr-17).
4255
4356
Example:
4457
-------
58+
4559
To init abstract RoadRunner worker:
4660

4761
```php
4862
<?php
4963

50-
use Spiral\RoadRunner;
51-
use Nyholm\Psr7\Factory;
64+
require __DIR__ . '/vendor/autoload.php';
5265

53-
ini_set('display_errors', 'stderr');
54-
include "vendor/autoload.php";
66+
use Nyholm\Psr7\Response;
67+
use Nyholm\Psr7\Factory\Psr17Factory;
5568

56-
$worker = new RoadRunner\Http\PSR7Worker(
57-
RoadRunner\Worker::create(),
58-
new Factory\Psr17Factory(),
59-
new Factory\Psr17Factory(),
60-
new Factory\Psr17Factory()
61-
);
69+
use Spiral\RoadRunner\Worker;
70+
use Spiral\RoadRunner\Http\PSR7Worker;
6271

63-
while ($req = $worker->waitRequest()) {
64-
try {
65-
$rsp = new \Nyholm\Psr7\Response();
66-
$rsp->getBody()->write("hello world");
6772

68-
$worker->respond($rsp);
73+
// Create new RoadRunner worker from global environment
74+
$worker = Worker::create();
75+
76+
// Create common PSR-17 HTTP factory
77+
$factory = new Psr17Factory();
78+
79+
//
80+
// Create PSR-7 worker and pass:
81+
// - RoadRunner worker
82+
// - PSR-17 ServerRequestFactory
83+
// - PSR-17 StreamFactory
84+
// - PSR-17 UploadFilesFactory
85+
//
86+
$psr7 = new PSR7Worker($worker, $factory, $factory, $factory);
87+
88+
while ($request = $psr7->waitRequest()) {
89+
try {
90+
// Reply by the 200 OK response
91+
$psr7->respond(new Response(200, [], 'Hello RoadRunner!'));
6992
} catch (\Throwable $e) {
70-
$worker->getWorker()->error((string)$e);
93+
94+
// Reply by the 500 Internal Server Error response
95+
$psr7->respond(new Response(500, [], 'Something Went Wrong!'));
96+
97+
// Report error to RoadRunner (optional)
98+
$worker->error((string)$e);
7199
}
72100
}
73101
```
74102

75103
Testing:
76104
--------
105+
77106
This codebase is automatically tested via host repository - [spiral/roadrunner](https://github.com/spiral/roadrunner).
78107

79108
License:
80109
--------
110+
81111
The MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information. Maintained
82112
by [Spiral Scout](https://spiralscout.com).

composer.json

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,52 @@
11
{
2-
"name": "spiral/roadrunner-http",
3-
"type": "server",
4-
"description": "RoadRunner: HTTP and PSR-7 worker",
5-
"license": "MIT",
6-
"authors": [
7-
{
8-
"name": "Anton Titov / Wolfy-J",
9-
"email": "[email protected]"
2+
"name": "spiral/roadrunner-http",
3+
"type": "library",
4+
"description": "RoadRunner: HTTP and PSR-7 worker",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "Anton Titov / Wolfy-J",
9+
"email": "[email protected]"
10+
},
11+
{
12+
"name": "RoadRunner Community",
13+
"homepage": "https://github.com/spiral/roadrunner/graphs/contributors"
14+
}
15+
],
16+
"require": {
17+
"php": ">=7.4",
18+
"ext-json": "*",
19+
"spiral/roadrunner-worker": "^2.0",
20+
"psr/http-factory": "^1.0.1",
21+
"psr/http-message": "^1.0.1"
1022
},
11-
{
12-
"name": "RoadRunner Community",
13-
"homepage": "https://github.com/spiral/roadrunner/graphs/contributors"
14-
}
15-
],
16-
"minimum-stability": "beta",
17-
"require": {
18-
"php": ">=7.4",
19-
"spiral/roadrunner": ">=2.0",
20-
"psr/http-factory": "^1.0.1",
21-
"psr/http-message": "^1.0.1"
22-
},
23-
"require-dev": {
24-
"nyholm/psr7": "^1.3",
25-
"phpstan/phpstan": "~0.12",
26-
"phpunit/phpunit": "~8.0"
27-
},
28-
"scripts": {
29-
"analyze": "phpstan analyze -c ./phpstan.neon.dist --no-progress --ansi"
30-
},
31-
"autoload": {
32-
"psr-4": {
33-
"Spiral\\RoadRunner\\Http\\": "src/"
34-
}
35-
}
23+
"autoload": {
24+
"psr-4": {
25+
"Spiral\\RoadRunner\\Http\\": "src"
26+
}
27+
},
28+
"require-dev": {
29+
"nyholm/psr7": "^1.3",
30+
"phpstan/phpstan": "~0.12",
31+
"phpunit/phpunit": "~8.0",
32+
"jetbrains/phpstorm-attributes": "^1.0",
33+
"vimeo/psalm": "^4.4",
34+
"symfony/var-dumper": "^5.1"
35+
},
36+
"scripts": {
37+
"analyze": "psalm"
38+
},
39+
"extra": {
40+
"branch-alias": {
41+
"dev-master": "2.1.x-dev"
42+
}
43+
},
44+
"suggest": {
45+
"spiral/roadrunner-cli": "Provides RoadRunner installation and management CLI tools"
46+
},
47+
"config": {
48+
"sort-packages": true
49+
},
50+
"minimum-stability": "dev",
51+
"prefer-stable": true
3652
}

psalm.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0"?>
2+
<psalm
3+
errorLevel="1"
4+
resolveFromConfigFile="true"
5+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6+
xmlns="https://getpsalm.org/schema/config"
7+
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
8+
>
9+
<issueHandlers>
10+
<RedundantConditionGivenDocblockType errorLevel="suppress" />
11+
<RedundantCastGivenDocblockType errorLevel="suppress" />
12+
</issueHandlers>
13+
<projectFiles>
14+
<directory name="src" />
15+
<ignoreFiles>
16+
<directory name="vendor" />
17+
</ignoreFiles>
18+
</projectFiles>
19+
</psalm>

src/HttpWorker.php

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,45 @@
11
<?php
22

33
/**
4-
* High-performance PHP process supervisor and load balancer written in Go. Http core.
4+
* This file is part of RoadRunner package.
55
*
6-
* @author Alex Bond
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
78
*/
89

910
declare(strict_types=1);
1011

1112
namespace Spiral\RoadRunner\Http;
1213

14+
use Spiral\RoadRunner\Payload;
1315
use Spiral\RoadRunner\WorkerInterface;
1416

17+
/**
18+
* @psalm-import-type HeadersList from Request
19+
* @psalm-import-type AttributesList from Request
20+
* @psalm-import-type UploadedFilesList from Request
21+
* @psalm-import-type CookiesList from Request
22+
*
23+
* @psalm-type RequestContext = array {
24+
* remoteAddr: string,
25+
* protocol: string,
26+
* method: string,
27+
* uri: string,
28+
* attributes: AttributesList,
29+
* headers: HeadersList,
30+
* cookies: CookiesList,
31+
* uploads: UploadedFilesList|null,
32+
* rawQuery: string,
33+
* parsed: bool
34+
* }
35+
*
36+
* @see Request
37+
*/
1538
class HttpWorker implements HttpWorkerInterface
1639
{
17-
/** @var WorkerInterface */
40+
/**
41+
* @var WorkerInterface
42+
*/
1843
private WorkerInterface $worker;
1944

2045
/**
@@ -34,73 +59,74 @@ public function getWorker(): WorkerInterface
3459
}
3560

3661
/**
37-
* Wait for incoming http request.
38-
*
39-
* @return Request|null
62+
* {@inheritDoc}
63+
* @throws \JsonException
4064
*/
4165
public function waitRequest(): ?Request
4266
{
43-
$payload = $this->getWorker()->waitPayload();
44-
if (empty($payload->body) && empty($payload->header)) {
45-
// termination request
67+
$payload = $this->worker->waitPayload();
68+
69+
// Termination request
70+
if ($payload === null || (!$payload->body && !$payload->header)) {
4671
return null;
4772
}
4873

49-
$request = new Request();
50-
$request->body = $payload->body;
74+
/** @var RequestContext $context */
75+
$context = \json_decode($payload->header, true, 512, \JSON_THROW_ON_ERROR);
5176

52-
$context = json_decode($payload->header, true);
53-
if ($context === null) {
54-
// invalid context
55-
return null;
56-
}
77+
return $this->createRequest($payload->body, $context);
78+
}
5779

58-
$this->hydrateRequest($request, $context);
80+
/**
81+
* {@inheritDoc}
82+
* @throws \JsonException
83+
*/
84+
public function respond(int $status, string $body, array $headers = []): void
85+
{
86+
$headers = (string)\json_encode([
87+
'status' => $status,
88+
'headers' => $headers ?: (object)[],
89+
], \JSON_THROW_ON_ERROR);
5990

60-
return $request;
91+
$this->worker->respond(new Payload($body, $headers));
6192
}
6293

6394
/**
64-
* Send response to the application server.
95+
* @param string $body
96+
* @param RequestContext $context
97+
* @return Request
6598
*
66-
* @param int $status Http status code
67-
* @param string $body Body of response
68-
* @param string[][] $headers An associative array of the message's headers. Each
69-
* key MUST be a header name, and each value MUST be an array of strings
70-
* for that header.
99+
* @psalm-suppress InaccessibleProperty
71100
*/
72-
public function respond(int $status, string $body, array $headers = []): void
101+
private function createRequest(string $body, array $context): Request
73102
{
74-
if ($headers === []) {
75-
// this is required to represent empty header set as map and not as array
76-
$headers = new \stdClass();
77-
}
103+
$request = new Request();
104+
$request->body = $body;
105+
106+
$this->hydrateRequest($request, $context);
78107

79-
$this->getWorker()->send(
80-
$body,
81-
(string) json_encode(['status' => $status, 'headers' => $headers])
82-
);
108+
return $request;
83109
}
84110

85111
/**
86112
* @param Request $request
87-
* @param array $context
113+
* @param RequestContext $context
114+
*
115+
* @psalm-suppress InaccessibleProperty
116+
* @psalm-suppress MixedPropertyTypeCoercion
88117
*/
89118
private function hydrateRequest(Request $request, array $context): void
90119
{
91120
$request->remoteAddr = $context['remoteAddr'];
92121
$request->protocol = $context['protocol'];
93122
$request->method = $context['method'];
94123
$request->uri = $context['uri'];
95-
$request->attributes = $context['attributes'] ?? [];
96-
$request->headers = $context['headers'];
97-
$request->cookies = $context['cookies'] ?? [];
98-
$request->uploads = $context['uploads'] ?? [];
99-
100-
$request->query = [];
101-
parse_str($context['rawQuery'], $request->query);
124+
\parse_str($context['rawQuery'], $request->query);
102125

103-
// indicates that body was parsed
104-
$request->parsed = $context['parsed'];
126+
$request->attributes = (array)($context['attributes'] ?? []);
127+
$request->headers = (array)($context['headers'] ?? []);
128+
$request->cookies = (array)($context['cookies'] ?? []);
129+
$request->uploads = (array)($context['uploads'] ?? []);
130+
$request->parsed = (bool)$context['parsed'];
105131
}
106132
}

0 commit comments

Comments
 (0)