Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions Slim/Interfaces/RouteInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,27 +55,6 @@ public function getMethods(): array;
*/
public function getRouteGroup(): ?RouteGroup;

/**
* Retrieve a specific route argument.
*/
public function getArgument(string $name, ?string $default = null): ?string;

/**
* Get route arguments.
*
* @return array<string, string>
*/
public function getArguments(): array;

/**
* Set route arguments.
*
* @param array<string,string> $arguments The arguments.
*
* @return RouteInterface
*/
public function setArguments(array $arguments): RouteInterface;

/**
* @return array<MiddlewareInterface|callable|string>
*/
Expand Down
10 changes: 3 additions & 7 deletions Slim/Middleware/ErrorExceptionMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ final class ErrorExceptionMiddleware implements MiddlewareInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$errorHandler = set_error_handler(function ($code, $message, $file, $line) {
set_error_handler(function ($code, $message, $file, $line) {
$level = error_reporting();
if (($level & $code) === 0) {
// silent error
Expand All @@ -35,13 +35,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
});

try {
$response = $handler->handle($request);
return $handler->handle($request);
} finally {
if ($errorHandler) {
restore_error_handler();
}
restore_error_handler();
}

return $response;
}
}
5 changes: 4 additions & 1 deletion Slim/Middleware/JsonExceptionMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ final class JsonExceptionMiddleware implements MiddlewareInterface

private const DEFAULT_TYPE = 'application/json';

private int $jsonOptions = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR;
private int $jsonOptions = JSON_PRETTY_PRINT
| JSON_UNESCAPED_SLASHES
| JSON_PARTIAL_OUTPUT_ON_ERROR
| JSON_INVALID_UTF8_SUBSTITUTE;

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
Expand Down
14 changes: 11 additions & 3 deletions Slim/Middleware/OutputBufferingMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use function in_array;
use function ob_end_clean;
use function ob_get_clean;
use function ob_get_level;
use function ob_start;

final class OutputBufferingMiddleware implements MiddlewareInterface
Expand Down Expand Up @@ -54,12 +55,19 @@ public function __construct(StreamFactoryInterface $streamFactory, string $style
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$level = ob_get_level();
ob_start();

try {
ob_start();
$response = $handler->handle($request);
$output = ob_get_clean();
$output = '';
while (ob_get_level() > $level) {
$output = (string)ob_get_clean() . $output;
}
} catch (Throwable $e) {
ob_end_clean();
while (ob_get_level() > $level) {
ob_end_clean();
}
throw $e;
}

Expand Down
38 changes: 0 additions & 38 deletions Slim/Routing/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
use Slim\Interfaces\MiddlewareCollectionInterface;
use Slim\Interfaces\RouteInterface;

use function array_key_exists;

final class Route implements RouteInterface, MiddlewareCollectionInterface
{
use MiddlewareCollectionTrait;
Expand Down Expand Up @@ -42,13 +40,6 @@ final class Route implements RouteInterface, MiddlewareCollectionInterface
*/
private ?RouteGroup $group;

/**
* Route parameters
*
* @var array<string, string>
*/
private array $arguments = [];

/**
* @param array<string> $methods
* @param string $pattern
Expand Down Expand Up @@ -112,33 +103,4 @@ public function getRouteGroup(): ?RouteGroup
{
return $this->group;
}

/**
* {@inheritdoc}
*/
public function getArgument(string $name, ?string $default = null): ?string
{
if (array_key_exists($name, $this->arguments)) {
return $this->arguments[$name];
}
return $default;
}

/**
* {@inheritdoc}
*/
public function getArguments(): array
{
return $this->arguments;
}

/**
* {@inheritdoc}
*/
public function setArguments(array $arguments): RouteInterface
{
$this->arguments = $arguments;

return $this;
}
}
52 changes: 52 additions & 0 deletions tests/Middleware/ErrorExceptionMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,56 @@ public function testProcessReturnsResponse(): void

$this->assertSame($response, $result);
}

public function testErrorHandlerIsRestoredWhenNoPreviousHandlerExists(): void
{
$previous = set_error_handler(static fn() => false);
restore_error_handler();

$request = $this->createMock(ServerRequestInterface::class);
$response = $this->createMock(ResponseInterface::class);

$handler = $this->createMock(RequestHandlerInterface::class);
$handler->expects($this->once())
->method('handle')
->willReturn($response);

$app = AppFactory::create();
$middleware = $app->getContainer()->get(ErrorExceptionMiddleware::class);

$middleware->process($request, $handler);

$current = set_error_handler(static fn() => false);
restore_error_handler();

$this->assertSame($previous, $current);
}

public function testErrorHandlerIsRestoredWhenAPreviousHandlerExists(): void
{
$custom = static fn() => false;
set_error_handler($custom);

try {
$request = $this->createMock(ServerRequestInterface::class);
$response = $this->createMock(ResponseInterface::class);

$handler = $this->createMock(RequestHandlerInterface::class);
$handler->expects($this->once())
->method('handle')
->willReturn($response);

$app = AppFactory::create();
$middleware = $app->getContainer()->get(ErrorExceptionMiddleware::class);

$middleware->process($request, $handler);

$current = set_error_handler(static fn() => false);
restore_error_handler();

$this->assertSame($custom, $current);
} finally {
restore_error_handler();
}
}
}
28 changes: 28 additions & 0 deletions tests/Middleware/JsonExceptionMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,34 @@ public function handle(ServerRequestInterface $request): ResponseInterface
$this->assertStringContainsString('Test message', (string)$response->getBody());
}

public function testSubstitutesInvalidUtf8(): void
{
$middleware = (new JsonExceptionMiddleware(new ResponseFactory()))
->withMimeType('application/json')
->withErrorDetails(true);

$request = (new ServerRequestFactory())->createServerRequest('GET', '/')
->withHeader('Accept', 'application/json');

$handler = new class implements RequestHandlerInterface {
public function handle(ServerRequestInterface $request): ResponseInterface
{
throw new RuntimeException("Invalid \xB1\x31 UTF-8 sequence");
}
};

$response = $middleware->process($request, $handler);
$decoded = json_decode((string)$response->getBody(), true);

$this->assertSame(500, $response->getStatusCode());
$this->assertIsArray($decoded);
$this->assertSame('Application Error', $decoded['message']);
$this->assertSame(
"Invalid \u{FFFD}1 UTF-8 sequence",
$decoded['exception'][0]['message'],
);
}

public function testWithJsonOptionsChangesEncoding(): void
{
$middleware = (new JsonExceptionMiddleware(new ResponseFactory()))
Expand Down
72 changes: 72 additions & 0 deletions tests/Middleware/OutputBufferingMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
use Slim\Tests\Traits\AppTestTrait;

use function ob_get_contents;
use function ob_get_level;
use function ob_start;

final class OutputBufferingMiddlewareTest extends TestCase
{
Expand Down Expand Up @@ -146,4 +148,74 @@ public function testOutputBufferIsCleanedWhenThrowableIsCaught()
$this->assertSame('', ob_get_contents());
}
}

public function testNestedOutputBuffersAreCapturedAndLevelRestored(): void
{
$level = ob_get_level();
$app = AppFactory::create();

$responseFactory = $app->getContainer()->get(ResponseFactoryInterface::class);
$streamFactory = $app->getContainer()->get(StreamFactoryInterface::class);

$outputBufferingMiddleware = new OutputBufferingMiddleware($streamFactory, OutputBufferingMiddleware::APPEND);
$app->add($outputBufferingMiddleware);

$middleware = function () use ($responseFactory) {
$response = $responseFactory->createResponse();
$response->getBody()->write('Body');
echo 'Outer';
ob_start();
echo 'Inner';

return $response;
};
$app->add($middleware);
$app->addRoutingMiddleware();

$request = $app->getContainer()
->get(ServerRequestFactoryInterface::class)
->createServerRequest('GET', '/');

$response = $app->handle($request);

$this->assertSame('BodyOuterInner', (string)$response->getBody());
$this->assertSame($level, ob_get_level());
}

public function testNestedOutputBuffersAreRestoredWhenThrowableIsCaught(): void
{
$level = ob_get_level();
$app = AppFactory::create();
$streamFactory = $app->getContainer()->get(StreamFactoryInterface::class);

$middleware = function () {
echo 'Outer';
ob_start();
echo 'Inner';
throw new Exception('Oops...');
};

$outputBufferingMiddleware = new OutputBufferingMiddleware($streamFactory, OutputBufferingMiddleware::PREPEND);

$app->add($outputBufferingMiddleware);
$app->add($middleware);
$app->addRoutingMiddleware();

$app->get('/', function (ServerRequestInterface $request, ResponseInterface $response) {
return $response;
});

$request = $app->getContainer()
->get(ServerRequestFactoryInterface::class)
->createServerRequest('GET', '/');

try {
$app->handle($request);
$this->fail('Expected exception was not thrown.');
} catch (Exception $e) {
$this->assertSame('Oops...', $e->getMessage());
$this->assertSame($level, ob_get_level());
$this->assertSame('', (string)ob_get_contents());
}
}
}
17 changes: 0 additions & 17 deletions tests/Routing/RouteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,23 +137,6 @@ public function testGetMethodsReturnsCorrectMethods(): void
$this->assertSame($methods, $route->getMethods());
}

public function testSetArgumentsStoresStringArguments(): void
{
$methods = ['GET'];
$pattern = '/users/{id}';
$handler = function () {
return 'handler';
};

$route = new Route($methods, $pattern, $handler);

$arguments = ['id' => '123', 'slug' => 'john-doe'];
$route->setArguments($arguments);

$this->assertSame($arguments, $route->getArguments());
$this->assertSame('123', $route->getArgument('id'));
}

private function createMiddleware(): MiddlewareInterface
{
return new class implements MiddlewareInterface {
Expand Down