Skip to content

Commit

Permalink
Merge branch 'master' into psr-comp
Browse files Browse the repository at this point in the history
# Conflicts:
#	composer.json
  • Loading branch information
xepozz committed Dec 31, 2023
2 parents c4f7b5b + e1882a5 commit 0660ac6
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 19 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"require": {
"php": "^8.1",
"psr/http-message": "^1.0|^2.0",
"psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0",
"yiisoft/middleware-dispatcher": "^5.2"
},
Expand All @@ -46,7 +47,6 @@
"roave/infection-static-analysis-plugin": "^1.34",
"spatie/phpunit-watcher": "^1.23",
"vimeo/psalm": "^5.16",
"yiisoft/strings": "^2.4",
"yiisoft/test-support": "^3.0"
},
"autoload": {
Expand Down
45 changes: 27 additions & 18 deletions src/FileRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;
use Yiisoft\Strings\StringHelper;

final class FileRouter implements MiddlewareInterface
{
Expand Down Expand Up @@ -48,16 +47,33 @@ public function withNamespace(string $namespace): self

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
/**
* @psalm-var class-string|null $controllerClass
*/
$controllerClass = $this->parseController($request);
if ($controllerClass === null) {
return $handler->handle($request);
}
$action = $this->parseAction($request);
/** @psalm-suppress InvalidPropertyFetch */
$actions = $controllerClass::$actions ?? [
'HEAD' => 'head',
'OPTIONS' => 'options',
'GET' => 'index',
'POST' => 'create',
'PUT' => 'update',
'DELETE' => 'delete',
];
$action = $actions[$request->getMethod()] ?? null;

if ($action === null) {
return $handler->handle($request);
}

if (!method_exists($controllerClass, $action)) {
return $handler->handle($request);
}

/** @psalm-suppress InvalidPropertyFetch */
$middlewares = $controllerClass::$middlewares[$action] ?? [];
$middlewares[] = [$controllerClass, $action];

Expand All @@ -66,18 +82,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
return $middlewareDispatcher->dispatch($request, $handler);
}

private function parseAction(ServerRequestInterface $request): ?string
{
return match ($request->getMethod()) {
'HEAD', 'GET' => 'index',
'POST' => 'create',
'PUT' => 'update',
'DELETE' => 'delete',
default => throw new \Exception('Not implemented.'),
};
}

private function parseController(ServerRequestInterface $request): mixed
private function parseController(ServerRequestInterface $request): ?string
{
$path = $request->getUri()->getPath();
if ($path === '/') {
Expand All @@ -89,15 +94,19 @@ private function parseController(ServerRequestInterface $request): mixed
fn(array $matches) => strtoupper($matches[1]),
$path,
);
$directoryPath = StringHelper::directoryName($controllerName);

$controllerName = StringHelper::basename($controllerName);
if (!preg_match('#^(.*?)/([^/]+)/?$#', $controllerName, $matches)) {
return null;
}
$directoryPath = $matches[1];
$controllerName = $matches[2];
}

$controller = $controllerName . $this->classPostfix;

$className = str_replace(
['/', '\\\\'],
['\\', '\\'],
['\\/\\', '\\/', '\\\\'],
'\\',
$this->namespace . '\\' . $this->baseControllerDirectory . '\\' . $directoryPath . '\\' . $controller
);

Expand Down
72 changes: 72 additions & 0 deletions tests/FileRouterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ public function testMiddleware(): void
$this->assertEquals('x-header-value', $response->getHeaderLine('X-Header-Name'));
}

public function testTrailingSlash(): void
{
/**
* @var FileRouter $router
*/
$router = $this->createRouter();
$router = $router
->withNamespace('Yiisoft\FileRouter\Tests\Support\App1');

$handler = $this->createExceptionHandler();
$request = new ServerRequest(
method: 'GET',
uri: '/user/',
);

$response = $router->process($request, $handler);

$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('Hello, index!', (string) $response->getBody());
}

#[DataProvider('dataRouter')]
public function testRouter(string $method, string $uri, string $expectedResponse): void
{
Expand Down Expand Up @@ -89,6 +110,23 @@ public static function dataRouter(): iterable
}

public function testUnsupportedMethod(): void
{
$router = $this->createRouter();
$router = $router
->withNamespace('Yiisoft\FileRouter\Tests\Support\App1');

$handler = $this->createExceptionHandler();
$request = new ServerRequest(
method: 'HEAD',
uri: '/',
);

$this->expectException(\Exception::class);
$this->expectExceptionMessage('Not implemented from tests.');
$router->process($request, $handler);
}

public function testNotImplementedAction(): void
{
$router = $this->createRouter();
$router = $router
Expand All @@ -105,6 +143,40 @@ public function testUnsupportedMethod(): void
$router->process($request, $handler);
}

public function testUnknownController(): void
{
$router = $this->createRouter();
$router = $router
->withNamespace('Yiisoft\FileRouter\Tests\Support\App1');

$handler = $this->createExceptionHandler();
$request = new ServerRequest(
method: 'DELETE',
uri: '/test/123',
);

$this->expectException(\Exception::class);
$this->expectExceptionMessage('Not implemented from tests.');
$router->process($request, $handler);
}

public function testIncorrectUrl(): void
{
$router = $this->createRouter();
$router = $router
->withNamespace('Yiisoft\FileRouter\Tests\Support\App1');

$handler = $this->createExceptionHandler();
$request = new ServerRequest(
method: 'DELETE',
uri: '/test//123///',
);

$this->expectException(\Exception::class);
$this->expectExceptionMessage('Not implemented from tests.');
$router->process($request, $handler);
}

public function testBaseController(): void
{
$router = $this->createRouter();
Expand Down
4 changes: 4 additions & 0 deletions tests/Support/App1/Controller/IndexController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@

class IndexController
{
public static array $actions = [
'GET' => 'index',
'DELETE' => 'delete',
];
public function index(): ResponseInterface
{
return new TextResponse('Hello, index!', 200, ['X-Header-Name' => 'X-Header-Value']);
Expand Down

0 comments on commit 0660ac6

Please sign in to comment.