diff --git a/.editorconfig b/.editorconfig
index a998937..20d4cdf 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -8,8 +8,14 @@ indent_style = space
indent_size = 4
trim_trailing_whitespace = true
-[*.{yml, yaml, sh, conf, neon*}]
+[*.yaml]
+indent_size = 2
+
+[*.yml]
indent_size = 2
[*.md]
trim_trailing_whitespace = false
+
+[Makefile]
+indent_style = tab
diff --git a/.gitattributes b/.gitattributes
index bc4b7e4..a0650eb 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,10 +1,9 @@
* text=auto
-/.github export-ignore
-/tests export-ignore
-/.* export-ignore
-/phpunit.xml* export-ignore
-/phpstan.* export-ignore
-/psalm.* export-ignore
-/infection.* export-ignore
-/codecov.* export-ignore
+/.* export-ignore
+/tests export-ignore
+/phpunit.xml* export-ignore
+/psalm.* export-ignore
+/psalm-baseline.xml export-ignore
+/infection.* export-ignore
+/rector.php export-ignore
diff --git a/.github/workflows/cs-fix.yml b/.github/workflows/cs-fix.yml
new file mode 100644
index 0000000..0395b27
--- /dev/null
+++ b/.github/workflows/cs-fix.yml
@@ -0,0 +1,12 @@
+on:
+ push:
+ branches:
+ - '*'
+
+name: Fix Code Style
+
+jobs:
+ cs-fix:
+ permissions:
+ contents: write
+ uses: spiral/gh-actions/.github/workflows/cs-fix.yml@master
diff --git a/.gitignore b/.gitignore
index 4059830..c1e1d81 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,20 +1,9 @@
-# Composer lock file
-composer.lock
-
-# IDEs
-/.idea
-/.vscode
-
-# Vendors
-/vendor
-**/vendor
-
-# Temp dirs & trash
-/tests/server*
-clover*
-cover*
-.DS_Store
-*.cache
-
-.phpunit.cache/
-.phpunit.result.cache
+/.*
+!/.github/
+!/.php-cs-fixer.dist.php
+!/.editorconfig
+!/.gitattributes
+/runtime/
+/vendor/
+/composer.lock
+*.log
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..7b5866c
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,12 @@
+include(__DIR__ . '/src')
+ ->include(__DIR__ . '/tests')
+ ->include(__FILE__)
+ ->allowRisky(false)
+ ->build();
diff --git a/composer.json b/composer.json
index 2459f2d..2c5cbee 100644
--- a/composer.json
+++ b/composer.json
@@ -49,10 +49,11 @@
"require-dev": {
"jetbrains/phpstorm-attributes": "^1.0",
"nyholm/psr7": "^1.3",
- "phpunit/phpunit": "^10.0",
+ "phpunit/phpunit": "^10.5",
+ "spiral/code-style": "^2.3",
"spiral/dumper": "^3.3",
"symfony/process": "^6.2 || ^7.0",
- "vimeo/psalm": "^5.9"
+ "vimeo/psalm": "^6.13"
},
"autoload": {
"psr-4": {
@@ -70,9 +71,6 @@
"url": "https://github.com/sponsors/roadrunner-server"
}
],
- "scripts": {
- "analyze": "psalm"
- },
"suggest": {
"spiral/roadrunner-cli": "Provides RoadRunner installation and management CLI tools",
"ext-protobuf": "Provides Protocol Buffers support. Without it, performance will be lower."
@@ -81,5 +79,16 @@
"sort-packages": true
},
"minimum-stability": "dev",
- "prefer-stable": true
+ "prefer-stable": true,
+ "scripts": {
+ "cs:fix": "php-cs-fixer fix -v",
+ "psalm": "psalm",
+ "psalm:baseline": "psalm --set-baseline=psalm-baseline.xml",
+ "psalm:ci": "psalm --output-format=github --shepherd --show-info=false --stats --threads=4",
+ "test": "phpunit --color=always --testdox",
+ "test:cc": [
+ "@putenv XDEBUG_MODE=coverage",
+ "phpunit --coverage-clover=runtime/phpunit/logs/clover.xml --color=always"
+ ]
+ }
}
diff --git a/phpunit.xml b/phpunit.xml
index c12c7b2..43def94 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,13 +1,14 @@
@@ -15,8 +16,21 @@
+
+
+
+
+
+
+
+
+
+
src
-
+
+ tests
+
+
diff --git a/psalm.xml b/psalm.xml
index 817a397..d509fb5 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -7,6 +7,7 @@
resolveFromConfigFile="true"
findUnusedBaselineEntry="false"
findUnusedCode="false"
+ ensureOverrideAttribute="false"
>
diff --git a/src/GlobalState.php b/src/GlobalState.php
index 04f87ed..9303149 100644
--- a/src/GlobalState.php
+++ b/src/GlobalState.php
@@ -4,12 +4,6 @@
namespace Spiral\RoadRunner\Http;
-use function time;
-use function microtime;
-use function strtoupper;
-use function str_replace;
-use function implode;
-
final class GlobalState
{
/**
@@ -35,22 +29,22 @@ public static function enrichServerVars(Request $request): array
$server = self::$cachedServer;
$server['REQUEST_URI'] = $request->uri;
- $server['REQUEST_TIME'] = time();
- $server['REQUEST_TIME_FLOAT'] = microtime(true);
+ $server['REQUEST_TIME'] = \time();
+ $server['REQUEST_TIME_FLOAT'] = \microtime(true);
$server['REMOTE_ADDR'] = $request->getRemoteAddr();
$server['REQUEST_METHOD'] = $request->method;
$server['HTTP_USER_AGENT'] = '';
foreach ($request->headers as $key => $value) {
- $key = strtoupper(str_replace('-', '_', $key));
+ $key = \strtoupper(\str_replace('-', '_', $key));
if ($key == 'CONTENT_TYPE' || $key == 'CONTENT_LENGTH') {
- $server[$key] = implode(', ', $value);
+ $server[$key] = \implode(', ', $value);
continue;
}
- $server['HTTP_' . $key] = implode(', ', $value);
+ $server['HTTP_' . $key] = \implode(', ', $value);
}
return $server;
diff --git a/src/HttpWorker.php b/src/HttpWorker.php
index e82c1bb..dd9528f 100644
--- a/src/HttpWorker.php
+++ b/src/HttpWorker.php
@@ -35,6 +35,8 @@
* }
*
* @see Request
+ *
+ * @api
*/
class HttpWorker implements HttpWorkerInterface
{
@@ -42,8 +44,7 @@ class HttpWorker implements HttpWorkerInterface
public function __construct(
private readonly WorkerInterface $worker,
- ) {
- }
+ ) {}
public function getWorker(): WorkerInterface
{
@@ -83,13 +84,13 @@ public function waitRequest(): ?Request
* @param array> $headers
* @throws \JsonException
*/
- public function respond(int $status, string|Generator $body = '', array $headers = [], bool $endOfStream = true): void
+ public function respond(int $status, string|\Generator $body = '', array $headers = [], bool $endOfStream = true): void
{
if ($status < 200 && $status >= 100 && $body !== '') {
throw new \InvalidArgumentException('Unable to send a body with informational status code.');
}
- if ($body instanceof Generator) {
+ if ($body instanceof \Generator) {
$this->respondStream($status, $body, $headers, $endOfStream);
return;
}
@@ -101,7 +102,7 @@ public function respond(int $status, string|Generator $body = '', array $headers
/**
* @param array> $headers
*/
- private function respondStream(int $status, Generator $body, array $headers = [], bool $endOfStream = true): void
+ private function respondStream(int $status, \Generator $body, array $headers = [], bool $endOfStream = true): void
{
$worker = $this->worker instanceof StreamWorkerInterface
? $this->worker->withStreamMode()
@@ -110,7 +111,7 @@ private function respondStream(int $status, Generator $body, array $headers = []
do {
if (!$body->valid()) {
// End of generator
- $content = (string)$body->getReturn();
+ $content = (string) $body->getReturn();
if ($endOfStream === false && $content === '') {
// We don't need to send an empty frame if the stream is not ended
return;
@@ -118,12 +119,12 @@ private function respondStream(int $status, Generator $body, array $headers = []
/** @psalm-suppress TooManyArguments */
$worker->respond(
$this->createRespondPayload($status, $content, $headers, $endOfStream),
- static::$codec
+ static::$codec,
);
break;
}
- $content = (string)$body->current();
+ $content = (string) $body->current();
if ($worker->getPayload(StreamStop::class) !== null) {
$body->throw(new StreamStoppedException());
@@ -160,12 +161,12 @@ private function arrayToRequest(string $body, array $context): Request
protocol: $context['protocol'],
method: $context['method'],
uri: $context['uri'],
- headers: $this->filterHeaders((array)($context['headers'] ?? [])),
- cookies: (array)($context['cookies'] ?? []),
- uploads: (array)($context['uploads'] ?? []),
+ headers: $this->filterHeaders((array) ($context['headers'] ?? [])),
+ cookies: (array) ($context['cookies'] ?? []),
+ uploads: (array) ($context['uploads'] ?? []),
attributes: [
Request::PARSED_BODY_ATTRIBUTE_NAME => $context['parsed'],
- ] + (array)($context['attributes'] ?? []),
+ ] + (array) ($context['attributes'] ?? []),
query: $query,
body: $body,
parsed: $context['parsed'],
@@ -253,7 +254,7 @@ private function arrayToHeaderValue(array $headers = []): array
*/
foreach ($headers as $key => $value) {
/** @psalm-suppress DocblockTypeContradiction */
- $value = \array_filter(\is_array($value) ? $value : [$value], static fn (mixed $v): bool => \is_string($v));
+ $value = \array_filter(\is_array($value) ? $value : [$value], static fn(mixed $v): bool => \is_string($v));
if ($value !== []) {
$result[$key] = new HeaderValue(['value' => $value]);
}
@@ -270,7 +271,7 @@ private function createRespondPayload(int $status, string $body, array $headers
$head = static::$codec === Frame::CODEC_PROTO
? (new Response(['status' => $status, 'headers' => $this->arrayToHeaderValue($headers)]))
->serializeToString()
- : \json_encode(['status' => $status, 'headers' => $headers ?: (object)[]], \JSON_THROW_ON_ERROR);
+ : \json_encode(['status' => $status, 'headers' => $headers ?: (object) []], \JSON_THROW_ON_ERROR);
return new Payload(body: $body, header: $head, eos: $eos);
}
diff --git a/src/HttpWorkerInterface.php b/src/HttpWorkerInterface.php
index 8643e77..3531765 100644
--- a/src/HttpWorkerInterface.php
+++ b/src/HttpWorkerInterface.php
@@ -6,7 +6,6 @@
use Generator;
use Spiral\RoadRunner\WorkerAwareInterface;
-use Stringable;
/**
* @psalm-import-type HeadersList from Request
@@ -22,7 +21,7 @@ public function waitRequest(): ?Request;
* Send response to the application server.
*
* @param int $status Http status code
- * @param Generator|string $body Body of response.
+ * @param \Generator|string $body Body of response.
* If the body is a generator, then each yielded value will be sent as a separated stream chunk.
* Returned value will be sent as a last stream package.
* Note: Stream response is supported by RoadRunner since version 2023.3
@@ -30,5 +29,5 @@ public function waitRequest(): ?Request;
* message's headers. Each key MUST be a header name, and each value MUST be an array of strings for
* that header.
*/
- public function respond(int $status, string|Generator $body, array $headers = []): void;
+ public function respond(int $status, string|\Generator $body, array $headers = []): void;
}
diff --git a/src/PSR7Worker.php b/src/PSR7Worker.php
index 43569b6..0f3e230 100644
--- a/src/PSR7Worker.php
+++ b/src/PSR7Worker.php
@@ -4,7 +4,6 @@
namespace Spiral\RoadRunner\Http;
-use Generator;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\ServerRequestInterface;
@@ -13,13 +12,14 @@
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UploadedFileInterface;
use Spiral\RoadRunner\WorkerInterface;
-use Stringable;
/**
* Manages PSR-7 request and response.
*
* @psalm-import-type UploadedFile from Request
* @psalm-import-type UploadedFilesList from Request
+ *
+ * @api
*/
class PSR7Worker implements PSR7WorkerInterface
{
@@ -32,7 +32,6 @@ class PSR7Worker implements PSR7WorkerInterface
private readonly HttpWorker $httpWorker;
-
/**
* @var string[] Valid values for HTTP protocol version
*/
@@ -91,38 +90,11 @@ public function respond(ResponseInterface $response): void
$response->getStatusCode(),
$this->chunkSize > 0
? $this->streamToGenerator($response->getBody())
- : (string)$response->getBody(),
- $response->getHeaders()
+ : (string) $response->getBody(),
+ $response->getHeaders(),
);
}
- /**
- * @return Generator Compatible
- * with {@see HttpWorker::respondStream}.
- */
- private function streamToGenerator(StreamInterface $stream): Generator
- {
- $stream->rewind();
- $size = $stream->getSize();
- if ($size !== null && $size < $this->chunkSize) {
- return (string)$stream;
- }
- $sum = 0;
- while (!$stream->eof()) {
- if ($size === null) {
- $chunk = $stream->read($this->chunkSize);
- } else {
- $left = $size - $sum;
- $chunk = $stream->read(\min($this->chunkSize, $left));
- if ($left <= $this->chunkSize && \strlen($chunk) === $left) {
- return $chunk;
- }
- }
- $sum += \strlen($chunk);
- yield $chunk;
- }
- }
-
/**
* Returns altered copy of _SERVER variable. Sets ip-address,
* request-time and other values.
@@ -158,7 +130,7 @@ protected function mapRequest(Request $httpRequest, array $server): ServerReques
$request = $this->requestFactory->createServerRequest(
$httpRequest->method,
$httpRequest->uri,
- $server
+ $server,
);
$request = $request
@@ -205,7 +177,7 @@ protected function wrapUploads(array $files): array
continue;
}
- if (\UPLOAD_ERR_OK === $file['error']) {
+ if ($file['error'] === \UPLOAD_ERR_OK) {
$stream = $this->streamFactory->createStreamFromFile($file['tmpName']);
} else {
$stream = $this->streamFactory->createStream();
@@ -216,7 +188,7 @@ protected function wrapUploads(array $files): array
$file['size'],
$file['error'],
$file['name'],
- $file['mime']
+ $file['mime'],
);
}
@@ -241,4 +213,31 @@ private static function fetchProtocolVersion(string $version): string
return $v;
}
+
+ /**
+ * @return \Generator Compatible
+ * with {@see HttpWorker::respondStream}.
+ */
+ private function streamToGenerator(StreamInterface $stream): \Generator
+ {
+ $stream->rewind();
+ $size = $stream->getSize();
+ if ($size !== null && $size < $this->chunkSize) {
+ return (string) $stream;
+ }
+ $sum = 0;
+ while (!$stream->eof()) {
+ if ($size === null) {
+ $chunk = $stream->read($this->chunkSize);
+ } else {
+ $left = $size - $sum;
+ $chunk = $stream->read(\min($this->chunkSize, $left));
+ if ($left <= $this->chunkSize && \strlen($chunk) === $left) {
+ return $chunk;
+ }
+ }
+ $sum += \strlen($chunk);
+ yield $chunk;
+ }
+ }
}
diff --git a/src/Request.php b/src/Request.php
index dbe17ce..b40c4c8 100644
--- a/src/Request.php
+++ b/src/Request.php
@@ -49,12 +49,11 @@ public function __construct(
public readonly array $query = [],
public readonly string $body = '',
public readonly bool $parsed = false,
- ) {
- }
+ ) {}
public function getRemoteAddr(): string
{
- return (string)($this->attributes['ipAddress'] ?? $this->remoteAddr);
+ return (string) ($this->attributes['ipAddress'] ?? $this->remoteAddr);
}
/**
@@ -63,7 +62,7 @@ public function getRemoteAddr(): string
public function getParsedBody(): ?array
{
if ($this->parsed) {
- return (array)\json_decode($this->body, true, 512, \JSON_THROW_ON_ERROR);
+ return (array) \json_decode($this->body, true, 512, \JSON_THROW_ON_ERROR);
}
return null;
diff --git a/tests/Feature/StreamResponseTest.php b/tests/Feature/StreamResponseTest.php
index 9da68b8..5c5b9e8 100644
--- a/tests/Feature/StreamResponseTest.php
+++ b/tests/Feature/StreamResponseTest.php
@@ -4,7 +4,6 @@
namespace Spiral\RoadRunner\Tests\Http\Feature;
-use Exception;
use PHPUnit\Framework\TestCase;
use Spiral\Goridge\SocketRelay;
use Spiral\RoadRunner\Http\Exception\StreamStoppedException;
@@ -22,20 +21,6 @@ class StreamResponseTest extends TestCase
private Worker $worker;
private $serverAddress = 'tcp://127.0.0.1:6002';
- protected function setUp(): void
- {
- parent::setUp();
- ServerRunner::start();
- ServerRunner::getBuffer();
- }
-
- protected function tearDown(): void
- {
- unset($this->relay, $this->worker);
- ServerRunner::stop();
- parent::tearDown();
- }
-
/**
* Regular case
*/
@@ -123,7 +108,7 @@ public function testExceptionInGenerator(): void
(function () {
yield 'Hel';
yield 'lo,';
- throw new Exception('test');
+ throw new \Exception('test');
})(),
);
@@ -163,6 +148,20 @@ public function testStopStreamAfterStreamEnd(): void
$this->assertFalse($this->getWorker()->hasPayload());
}
+ protected function setUp(): void
+ {
+ parent::setUp();
+ ServerRunner::start();
+ ServerRunner::getBuffer();
+ }
+
+ protected function tearDown(): void
+ {
+ unset($this->relay, $this->worker);
+ ServerRunner::stop();
+ parent::tearDown();
+ }
+
private function getRelay(): SocketRelay
{
return $this->relay ??= SocketRelay::create($this->serverAddress);
diff --git a/tests/Server/Client.php b/tests/Server/Client.php
index e5dc0c7..1afd62f 100644
--- a/tests/Server/Client.php
+++ b/tests/Server/Client.php
@@ -4,7 +4,6 @@
namespace Spiral\RoadRunner\Tests\Http\Server;
-use Fiber;
use Spiral\Goridge\Frame;
use Spiral\RoadRunner\Tests\Http\Server\Command\BaseCommand;
use Spiral\RoadRunner\Tests\Http\Server\Command\StreamStop;
@@ -19,7 +18,6 @@ class Client
/** @var string[] */
private array $writeQueue = [];
- /** @var string */
private string $readBuffer = '';
public function __construct(
@@ -29,11 +27,6 @@ public function __construct(
\socket_set_nonblock($this->socket);
}
- public function __destruct()
- {
- \socket_close($this->socket);
- }
-
public static function init(\Socket $socket): self
{
return new self($socket);
@@ -59,10 +52,15 @@ public function process(): void
$this->writeQueue();
}
- Fiber::suspend();
+ \Fiber::suspend();
} while (true);
}
+ public function __destruct()
+ {
+ \socket_close($this->socket);
+ }
+
private function onInit()
{
$this->writeQueue[] = Frame::packFrame(new Frame('{"pid":true}', [], Frame::CONTROL));
@@ -130,7 +128,7 @@ private function readNBytes(int $bytes, bool $canBeLess = false): string
}
if ($data === '') {
- Fiber::suspend();
+ \Fiber::suspend();
continue;
}
@@ -170,4 +168,4 @@ private function onCommand(BaseCommand $command): void
break;
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/Server/Command/BaseCommand.php b/tests/Server/Command/BaseCommand.php
index f36e7fd..8a2a843 100644
--- a/tests/Server/Command/BaseCommand.php
+++ b/tests/Server/Command/BaseCommand.php
@@ -9,9 +9,11 @@
abstract class BaseCommand
{
public const COMMAND_KEY = 'test-command';
+
protected Frame $frame;
- public function __construct() {
+ public function __construct()
+ {
$this->frame = new Frame(\json_encode([self::COMMAND_KEY => static::class]));
}
@@ -25,5 +27,5 @@ public function getResponse(): string
return Frame::packFrame($this->getResponseFrame());
}
- public abstract function getResponseFrame(): Frame;
-}
\ No newline at end of file
+ abstract public function getResponseFrame(): Frame;
+}
diff --git a/tests/Server/Server.php b/tests/Server/Server.php
index 2e1f865..4b7c85b 100644
--- a/tests/Server/Server.php
+++ b/tests/Server/Server.php
@@ -4,19 +4,15 @@
namespace Spiral\RoadRunner\Tests\Http\Server;
-use Fiber;
-use RuntimeException;
-use Socket;
-
class Server
{
- /** @var false|resource|Socket */
+ /** @var false|resource|\Socket */
private $socket;
/** @var Client[] */
private array $clients = [];
- /** @var Fiber[] */
+ /** @var \Fiber[] */
private array $fibers = [];
public function __construct(
@@ -31,11 +27,6 @@ public function __construct(
echo "Server started\n";
}
- public function __destruct()
- {
- \socket_close($this->socket);
- }
-
public static function init(int $port = 6002): self
{
return new self($port);
@@ -48,7 +39,7 @@ public function process(): void
$key = \array_key_last($this->clients) + 1;
try {
$this->clients[$key] = Client::init($client);
- $this->fibers[$key] = new Fiber($this->clients[$key]->process(...));
+ $this->fibers[$key] = new \Fiber($this->clients[$key]->process(...));
} catch (\Throwable) {
unset($this->clients[$key], $this->fibers[$key]);
}
@@ -59,11 +50,16 @@ public function process(): void
$fiber->isStarted() ? $fiber->resume() : $fiber->start();
if ($fiber->isTerminated()) {
- throw new RuntimeException('Client terminated.');
+ throw new \RuntimeException('Client terminated.');
}
} catch (\Throwable) {
unset($this->clients[$key], $this->fibers[$key]);
}
}
}
+
+ public function __destruct()
+ {
+ \socket_close($this->socket);
+ }
}
diff --git a/tests/Server/ServerRunner.php b/tests/Server/ServerRunner.php
index 0ba49f8..9bb8b8a 100644
--- a/tests/Server/ServerRunner.php
+++ b/tests/Server/ServerRunner.php
@@ -4,9 +4,6 @@
namespace Spiral\RoadRunner\Tests\Http\Server;
-
-use RuntimeException;
-use Symfony\Component\Process\PhpProcess;
use Symfony\Component\Process\Process;
class ServerRunner
@@ -19,7 +16,7 @@ public static function start(int $timeout = 5): void
self::$process = new Process(['php', 'run_server.php'], __DIR__);
$run = false;
self::$process->setTimeout($timeout);
- self::$process->start(static function (string $type, string $output) use (&$run) {
+ self::$process->start(static function (string $type, string $output) use (&$run) {
if (!$run && $type === Process::OUT && \str_contains($output, 'Server started')) {
$run = true;
}
@@ -30,7 +27,7 @@ public static function start(int $timeout = 5): void
});
if (!self::$process->isRunning()) {
- throw new RuntimeException('Error starting Server: ' . self::$process->getErrorOutput());
+ throw new \RuntimeException('Error starting Server: ' . self::$process->getErrorOutput());
}
// wait for roadrunner to start
@@ -42,7 +39,7 @@ public static function start(int $timeout = 5): void
}
if (!$run) {
- throw new RuntimeException('Error starting Server: timeout');
+ throw new \RuntimeException('Error starting Server: timeout');
}
}
diff --git a/tests/Unit/HttpWorkerTest.php b/tests/Unit/HttpWorkerTest.php
index 6a094c2..ec1b44c 100644
--- a/tests/Unit/HttpWorkerTest.php
+++ b/tests/Unit/HttpWorkerTest.php
@@ -24,7 +24,6 @@ final class HttpWorkerTest extends TestCase
'uri' => 'http://localhost',
'parsed' => false,
];
-
private const REQUIRED_REQUEST_DATA = [
'remoteAddr' => '127.0.0.1',
'protocol' => 'HTTP/1.1',
@@ -33,124 +32,9 @@ final class HttpWorkerTest extends TestCase
'attributes' => [Request::PARSED_BODY_ATTRIBUTE_NAME => false],
'query' => ['first' => 'value', 'arr' => ['foo bar', 'baz']],
'parsed' => false,
- 'body' => 'foo'
+ 'body' => 'foo',
];
- #[DataProvider('requestDataProvider')]
- public function testWaitRequestFromArray(array $header, array $expected): void
- {
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('waitPayload')
- ->willReturn(new Payload('foo', \json_encode($header)));
-
- $worker = new HttpWorker($worker);
-
- $this->assertEquals(new Request(...$expected), $worker->waitRequest());
- }
-
- #[DataProvider('requestDataProvider')]
- public function testWaitRequestFromProto(array $header, array $expected): void
- {
- $request = self::createProtoRequest($header);
-
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('waitPayload')
- ->willReturn(new Payload('foo', $request->serializeToString()));
-
- $worker = new HttpWorker($worker);
-
- $this->assertEquals(new Request(...$expected), $worker->waitRequest());
- }
-
- #[DataProvider('emptyRequestDataProvider')]
- public function testWaitRequestWithEmptyData(?Payload $payload): void
- {
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('waitPayload')
- ->willReturn($payload);
-
- $worker = new HttpWorker($worker);
-
- $this->assertEquals(null, $worker->waitRequest());
- }
-
- public function testEmptyBodyShouldBeConvertedIntoEmptyArrayWithParsedTrue(): void
- {
- $request = self::createProtoRequest(\array_merge(self::REQUIRED_REQUEST_DATA, ['parsed' => true]));
-
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('waitPayload')
- ->willReturn(new Payload('', $request->serializeToString()));
-
- $worker = new HttpWorker($worker);
-
- $request = $worker->waitRequest();
- $this->assertSame([], $request->getParsedBody());
- }
-
- public function testRespondUnableToSendBodyWithInfoStatusException(): void
- {
- $worker = new HttpWorker($this->createMock(WorkerInterface::class));
-
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Unable to send a body with informational status code.');
- $worker->respond(100, 'foo');
- }
-
- public function testRespondWithProtoCodec(): void
- {
- $expectedHeader = new Response([
- 'status' => 200,
- 'headers' => ['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])],
- ]);
-
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('respond')
- ->with(new Payload('foo', $expectedHeader->serializeToString()), Frame::CODEC_PROTO);
-
- (new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_PROTO);
- $worker = new HttpWorker($worker);
-
- $worker->respond(200, 'foo', ['Content-Type' => ['application/x-www-form-urlencoded']]);
- }
-
- #[DataProvider('headersDataProvider')]
- public function testRespondWithProtoCodecWithHeaders(array $headers, array $expected): void
- {
- $expectedHeader = new Response(['status' => 200, 'headers' => $expected]);
-
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('respond')
- ->with(new Payload('foo', $expectedHeader->serializeToString()), Frame::CODEC_PROTO);
-
- (new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_PROTO);
- $worker = new HttpWorker($worker);
-
- $worker->respond(200, 'foo', $headers);
- }
-
- public function testRespondWithJsonCodec(): void
- {
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('respond')
- ->with(new Payload('foo', \json_encode([
- 'status' => 200,
- 'headers' => ['Content-Type' => ['application/x-www-form-urlencoded']]
- ])), Frame::CODEC_JSON);
-
- (new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_JSON);
- $worker = new HttpWorker($worker);
-
- $worker->respond(200, 'foo', ['Content-Type' => ['application/x-www-form-urlencoded']]);
- }
-
public static function requestDataProvider(): \Traversable
{
yield [self::REQUIRED_PAYLOAD_DATA, self::REQUIRED_REQUEST_DATA];
@@ -158,8 +42,8 @@ public static function requestDataProvider(): \Traversable
\array_merge(self::REQUIRED_PAYLOAD_DATA, ['parsed' => true]),
\array_merge(
self::REQUIRED_REQUEST_DATA,
- ['parsed' => true, 'attributes' => [Request::PARSED_BODY_ATTRIBUTE_NAME => true]]
- )
+ ['parsed' => true, 'attributes' => [Request::PARSED_BODY_ATTRIBUTE_NAME => true]],
+ ),
];
yield [
\array_merge(self::REQUIRED_PAYLOAD_DATA, [
@@ -171,7 +55,7 @@ public static function requestDataProvider(): \Traversable
]),
\array_merge(self::REQUIRED_REQUEST_DATA, [
'headers' => ['Content-Type' => ['application/x-www-form-urlencoded']],
- ])
+ ]),
];
yield [
\array_merge(self::REQUIRED_PAYLOAD_DATA, [
@@ -181,7 +65,7 @@ public static function requestDataProvider(): \Traversable
]),
\array_merge(self::REQUIRED_REQUEST_DATA, [
'cookies' => ['theme' => 'light'],
- ])
+ ]),
];
yield [
\array_merge(self::REQUIRED_PAYLOAD_DATA, [
@@ -207,7 +91,7 @@ public static function requestDataProvider(): \Traversable
'size' => 1235,
'error' => 0,
'tmpName' => '/tmp/php/php2h4j1o',
- ]
+ ],
],
'nested' => [
'some-key' => [
@@ -217,7 +101,7 @@ public static function requestDataProvider(): \Traversable
'error' => 0,
'tmpName' => '/tmp/php/php1h4j1o',
],
- ]
+ ],
],
]),
\array_merge(self::REQUIRED_REQUEST_DATA, [
@@ -243,7 +127,7 @@ public static function requestDataProvider(): \Traversable
'size' => 1235,
'error' => 0,
'tmpName' => '/tmp/php/php2h4j1o',
- ]
+ ],
],
'nested' => [
'some-key' => [
@@ -253,9 +137,9 @@ public static function requestDataProvider(): \Traversable
'error' => 0,
'tmpName' => '/tmp/php/php1h4j1o',
],
- ]
+ ],
],
- ])
+ ]),
];
yield [
\array_merge(self::REQUIRED_PAYLOAD_DATA, [
@@ -266,9 +150,9 @@ public static function requestDataProvider(): \Traversable
\array_merge(self::REQUIRED_REQUEST_DATA, [
'attributes' => [
Request::PARSED_BODY_ATTRIBUTE_NAME => false,
- 'foo' => 'bar'
+ 'foo' => 'bar',
],
- ])
+ ]),
];
}
@@ -282,14 +166,14 @@ public static function headersDataProvider(): \Traversable
{
yield [
['Content-Type' => ['application/x-www-form-urlencoded']],
- ['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])]
+ ['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])],
];
yield [
['Content-Type' => ['application/x-www-form-urlencoded'], 'X-Test' => ['foo', 'bar']],
[
'Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']]),
'X-Test' => new HeaderValue(['value' => ['foo', 'bar']]),
- ]
+ ],
];
yield [['Content-Type' => [null]], []];
yield [['Content-Type' => [1]], []];
@@ -302,15 +186,135 @@ public static function headersDataProvider(): \Traversable
[
'X-Test' => new HeaderValue(['value' => ['foo', 'bar']]),
'X-Test2' => new HeaderValue(['value' => ['foo']]),
- ]
+ ],
];
yield [
['Content-Type' => 'application/x-www-form-urlencoded'],
- ['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])]
+ ['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])],
];
yield [['Content-Type' => new \stdClass()], []];
}
+ #[DataProvider('requestDataProvider')]
+ public function testWaitRequestFromArray(array $header, array $expected): void
+ {
+ $worker = $this->createMock(WorkerInterface::class);
+ $worker->expects($this->once())
+ ->method('waitPayload')
+ ->willReturn(new Payload('foo', \json_encode($header)));
+
+ $worker = new HttpWorker($worker);
+
+ $this->assertEquals(new Request(...$expected), $worker->waitRequest());
+ }
+
+ #[DataProvider('requestDataProvider')]
+ public function testWaitRequestFromProto(array $header, array $expected): void
+ {
+ $request = self::createProtoRequest($header);
+
+ $worker = $this->createMock(WorkerInterface::class);
+ $worker->expects($this->once())
+ ->method('waitPayload')
+ ->willReturn(new Payload('foo', $request->serializeToString()));
+
+ $worker = new HttpWorker($worker);
+
+ $this->assertEquals(new Request(...$expected), $worker->waitRequest());
+ }
+
+ #[DataProvider('emptyRequestDataProvider')]
+ public function testWaitRequestWithEmptyData(?Payload $payload): void
+ {
+ $worker = $this->createMock(WorkerInterface::class);
+ $worker->expects($this->once())
+ ->method('waitPayload')
+ ->willReturn($payload);
+
+ $worker = new HttpWorker($worker);
+
+ $this->assertEquals(null, $worker->waitRequest());
+ }
+
+ public function testEmptyBodyShouldBeConvertedIntoEmptyArrayWithParsedTrue(): void
+ {
+ $request = self::createProtoRequest(\array_merge(self::REQUIRED_REQUEST_DATA, ['parsed' => true]));
+
+ $worker = $this->createMock(WorkerInterface::class);
+ $worker->expects($this->once())
+ ->method('waitPayload')
+ ->willReturn(new Payload('', $request->serializeToString()));
+
+ $worker = new HttpWorker($worker);
+
+ $request = $worker->waitRequest();
+ $this->assertSame([], $request->getParsedBody());
+ }
+
+ public function testRespondUnableToSendBodyWithInfoStatusException(): void
+ {
+ $worker = new HttpWorker($this->createMock(WorkerInterface::class));
+
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Unable to send a body with informational status code.');
+ $worker->respond(100, 'foo');
+ }
+
+ public function testRespondWithProtoCodec(): void
+ {
+ $expectedHeader = new Response([
+ 'status' => 200,
+ 'headers' => ['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])],
+ ]);
+
+ $worker = $this->createMock(WorkerInterface::class);
+ $worker->expects($this->once())
+ ->method('respond')
+ ->with(new Payload('foo', $expectedHeader->serializeToString()), Frame::CODEC_PROTO);
+
+ (new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_PROTO);
+ $worker = new HttpWorker($worker);
+
+ $worker->respond(200, 'foo', ['Content-Type' => ['application/x-www-form-urlencoded']]);
+ }
+
+ #[DataProvider('headersDataProvider')]
+ public function testRespondWithProtoCodecWithHeaders(array $headers, array $expected): void
+ {
+ $expectedHeader = new Response(['status' => 200, 'headers' => $expected]);
+
+ $worker = $this->createMock(WorkerInterface::class);
+ $worker->expects($this->once())
+ ->method('respond')
+ ->with(new Payload('foo', $expectedHeader->serializeToString()), Frame::CODEC_PROTO);
+
+ (new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_PROTO);
+ $worker = new HttpWorker($worker);
+
+ $worker->respond(200, 'foo', $headers);
+ }
+
+ public function testRespondWithJsonCodec(): void
+ {
+ $worker = $this->createMock(WorkerInterface::class);
+ $worker->expects($this->once())
+ ->method('respond')
+ ->with(new Payload('foo', \json_encode([
+ 'status' => 200,
+ 'headers' => ['Content-Type' => ['application/x-www-form-urlencoded']],
+ ])), Frame::CODEC_JSON);
+
+ (new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_JSON);
+ $worker = new HttpWorker($worker);
+
+ $worker->respond(200, 'foo', ['Content-Type' => ['application/x-www-form-urlencoded']]);
+ }
+
+ protected function tearDown(): void
+ {
+ (new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(null);
+ }
+
private static function createProtoRequest(array $values): \RoadRunner\HTTP\DTO\V1\Request
{
$toHeaderValue = static function (string $key, bool $wrap = true) use (&$values): void {
@@ -325,7 +329,7 @@ private static function createProtoRequest(array $values): \RoadRunner\HTTP\DTO\
$toHeaderValue('attributes');
$toHeaderValue('cookies');
- return new \RoadRunner\HTTP\DTO\V1\Request([
+ return new \RoadRunner\HTTP\DTO\V1\Request([
'remote_addr' => $values['remoteAddr'],
'protocol' => $values['protocol'],
'method' => $values['method'],
@@ -338,9 +342,4 @@ private static function createProtoRequest(array $values): \RoadRunner\HTTP\DTO\
'attributes' => $values['attributes'] ?? [],
]);
}
-
- protected function tearDown(): void
- {
- (new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(null);
- }
}
diff --git a/tests/Unit/PSR7WorkerTest.php b/tests/Unit/PSR7WorkerTest.php
index 2cbf17e..70d002e 100644
--- a/tests/Unit/PSR7WorkerTest.php
+++ b/tests/Unit/PSR7WorkerTest.php
@@ -14,7 +14,6 @@
use Spiral\RoadRunner\Tests\Http\Unit\Stub\TestRelay;
use Spiral\RoadRunner\Worker;
-
#[CoversClass(PSR7Worker::class)]
#[CoversClass(GlobalState::class)]
#[RunClassInSeparateProcess]
@@ -36,7 +35,7 @@ public function testStateServerLeak(): void
[
[
'Content-Type' => ['application/html'],
- 'Connection' => ['keep-alive']
+ 'Connection' => ['keep-alive'],
],
[
'REQUEST_URI' => 'http://localhost',
@@ -49,14 +48,14 @@ public function testStateServerLeak(): void
],
[
[
- 'Content-Type' => ['application/json']
+ 'Content-Type' => ['application/json'],
],
[
'REQUEST_URI' => 'http://localhost',
'REMOTE_ADDR' => '127.0.0.1',
'REQUEST_METHOD' => 'GET',
'HTTP_USER_AGENT' => '',
- 'CONTENT_TYPE' => 'application/json'
+ 'CONTENT_TYPE' => 'application/json',
],
],
];
@@ -73,8 +72,8 @@ public function testStateServerLeak(): void
'parsed' => false,
];
- $head = (string)\json_encode($body, \JSON_THROW_ON_ERROR);
- $frame = new Frame($head .'test', [\strlen($head)]);
+ $head = (string) \json_encode($body, \JSON_THROW_ON_ERROR);
+ $frame = new Frame($head . 'test', [\strlen($head)]);
$relay->addFrames($frame);
@@ -87,7 +86,6 @@ public function testStateServerLeak(): void
}
}
-
protected function tearDown(): void
{
// Clean all extra output buffers
diff --git a/tests/Unit/StreamResponseTest.php b/tests/Unit/StreamResponseTest.php
index f785742..fd73172 100644
--- a/tests/Unit/StreamResponseTest.php
+++ b/tests/Unit/StreamResponseTest.php
@@ -15,11 +15,6 @@ final class StreamResponseTest extends TestCase
private TestRelay $relay;
private Worker $worker;
- protected function tearDown(): void
- {
- unset($this->relay, $this->worker);
- }
-
/**
* Regular case
*/
@@ -73,6 +68,11 @@ public function testStopStreamResponse(): void
self::assertSame('Hello,', $this->getRelay()->getReceivedBody());
}
+ protected function tearDown(): void
+ {
+ unset($this->relay, $this->worker);
+ }
+
private function getRelay(): TestRelay
{
return $this->relay ??= new TestRelay();
diff --git a/tests/Unit/Stub/TestRelay.php b/tests/Unit/Stub/TestRelay.php
index 4b2cd34..8c2013c 100644
--- a/tests/Unit/Stub/TestRelay.php
+++ b/tests/Unit/Stub/TestRelay.php
@@ -28,11 +28,11 @@ public function addFrame(
bool $stream = false,
bool $stopStream = false,
): self {
- $head = (string)\json_encode([
+ $head = (string) \json_encode([
'status' => $status,
'headers' => $headers,
], \JSON_THROW_ON_ERROR);
- $frame = new Frame($head .$body, [\strlen($head)]);
+ $frame = new Frame($head . $body, [\strlen($head)]);
$frame->byte10 |= $stream ? Frame::BYTE10_STREAM : 0;
$frame->byte10 |= $stopStream ? Frame::BYTE10_STOP : 0;
return $this->addFrames($frame);
@@ -50,7 +50,7 @@ public function getReceived(): array
public function getReceivedBody(): string
{
- return \implode('', \array_map(static fn (Frame $frame)
+ return \implode('', \array_map(static fn(Frame $frame)
=> \substr($frame->payload, $frame->options[0]), $this->received));
}