Skip to content
Open
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
16 changes: 10 additions & 6 deletions src/UrlMatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Yiisoft\Http\Method;
use Yiisoft\Router\MatchingResult;
use Yiisoft\Router\RouteCollectionInterface;
use Yiisoft\Router\UrlGeneratorInterface;
use Yiisoft\Router\UrlMatcherInterface;

use function array_merge;
Expand Down Expand Up @@ -75,6 +76,7 @@ final class UrlMatcher implements UrlMatcherInterface
*/
public function __construct(
private RouteCollectionInterface $routeCollection,
private UrlGeneratorInterface $urlGenerator,
private ?CacheInterface $cache = null,
?array $config = null,
?RouteCollector $fastRouteCollector = null,
Expand All @@ -94,19 +96,21 @@ public function match(ServerRequestInterface $request): MatchingResult
}

$dispatchData = $this->getDispatchData();
$path = urldecode($request
->getUri()
->getPath());
$path = urldecode($request->getUri()->getPath());
$uriPrefix = $this->urlGenerator->getUriPrefix();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we pass prefix and not the whole URL generator?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without dependency? Nope.

$prefixLength = strlen($uriPrefix);
$method = $request->getMethod();

if ($uriPrefix !== '' && str_starts_with($path, $uriPrefix) && $path[$prefixLength] === '/') {
$path = substr($path, $prefixLength);
}

/**
* @psalm-var ResultNotFound|ResultMethodNotAllowed|ResultFound $result
*/
$result = $this
->getDispatcher($dispatchData)
->dispatch($method, $request
->getUri()
->getHost() . $path);
->dispatch($method, $request->getUri()->getHost() . $path);

/** @psalm-suppress ArgumentTypeCoercion Psalm can't determine correct type here */
return $result[0] !== Dispatcher::FOUND
Expand Down
8 changes: 7 additions & 1 deletion tests/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ private function createContainer(?string $postfix = null): Container
private function getDiConfig(?string $postfix = null): array
{
$params = $this->getParams();
return require dirname(__DIR__) . '/config/di' . ($postfix !== null ? '-' . $postfix : '') . '.php';
if ($postfix === null) {
return require dirname(__DIR__) . '/config/di.php';
}
$config = require dirname(__DIR__) . '/config/di.php';
$postfixConfig = require dirname(__DIR__) . '/config/di' . ($postfix !== null ? '-' . $postfix : '') . '.php';

return array_merge($config, $postfixConfig);
}

private function getParams(): array
Expand Down
150 changes: 95 additions & 55 deletions tests/UrlMatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
use PHPUnit\Framework\TestCase;
use Psr\SimpleCache\CacheInterface;
use RuntimeException;
use Yiisoft\Router\FastRoute\UrlGenerator;
use Yiisoft\Router\FastRoute\UrlMatcher;
use Yiisoft\Router\Group;
use Yiisoft\Router\Route;
use Yiisoft\Router\RouteCollection;
use Yiisoft\Router\RouteCollector;
use Yiisoft\Router\UrlGeneratorInterface;
use Yiisoft\Router\UrlMatcherInterface;

final class UrlMatcherTest extends TestCase
Expand All @@ -21,8 +23,8 @@ public function testDefaultsAreInResult(): void
{
$routes = [
Route::get('/[{name}]')
->action(fn () => 1)
->defaults(['name' => 'test']),
->action(fn () => 1)
->defaults(['name' => 'test']),
];

$urlMatcher = $this->createUrlMatcher($routes);
Expand Down Expand Up @@ -86,6 +88,21 @@ public function testSimpleRouteWithParam(): void
$this->assertSame('23', $arguments['id']);
}

public function testSimpleRouteWithUriPrefix(): void
{
$routes = [
Route::get('/site/posts')->action(fn () => 1),
];

$urlMatcher = $this->createUrlMatcher($routes, uriPrefix: $prefix = '/blog');

$request = new ServerRequest('GET', $prefix . '/site/posts');

$result = $urlMatcher->match($request);

$this->assertTrue($result->isSuccess());
}

public function testSimpleRouteWithUrlencodedParam(): void
{
$routes = [
Expand All @@ -110,22 +127,26 @@ public function testSimpleRouteWithHostSuccess(): void
{
$routes = [
Route::get('/site/index')
->action(fn () => 1)
->hosts('yii.test', 'yii.dev'),
->action(fn () => 1)
->hosts('yii.test', 'yii.dev'),
Route::get('/site/index')
->action(fn () => 1)
->host('{user}.yiiframework.com'),
->action(fn () => 1)
->host('{user}.yiiframework.com'),
];

$urlMatcher = $this->createUrlMatcher($routes);

$request = new ServerRequest('GET', '/site/index');
$request1 = $request->withUri($request
->getUri()
->withHost('yii.test'));
$request2 = $request->withUri($request
->getUri()
->withHost('rustamwin.yiiframework.com'));
$request1 = $request->withUri(
$request
->getUri()
->withHost('yii.test')
);
$request2 = $request->withUri(
$request
->getUri()
->withHost('rustamwin.yiiframework.com')
);

$result1 = $urlMatcher->match($request1);
$result2 = $urlMatcher->match($request2);
Expand All @@ -141,21 +162,27 @@ public function testSimpleRouteWithMultipleHostSuccess(): void
{
$routes = [
Route::get('/site/index')
->action(fn () => 1)
->hosts('yii.test', 'yii.com', 'yii.ru'),
->action(fn () => 1)
->hosts('yii.test', 'yii.com', 'yii.ru'),
];

$urlMatcher = $this->createUrlMatcher($routes);
$request = new ServerRequest('GET', '/site/index');
$request1 = $request->withUri($request
->getUri()
->withHost('yii.test'));
$request2 = $request->withUri($request
->getUri()
->withHost('yii.com'));
$errorRequest = $request->withUri($request
->getUri()
->withHost('example.com'));
$request1 = $request->withUri(
$request
->getUri()
->withHost('yii.test')
);
$request2 = $request->withUri(
$request
->getUri()
->withHost('yii.com')
);
$errorRequest = $request->withUri(
$request
->getUri()
->withHost('example.com')
);

$result1 = $urlMatcher->match($request1);
$result2 = $urlMatcher->match($request2);
Expand All @@ -169,12 +196,12 @@ public function testSimpleRouteWithMultipleHostSuccess(): void
public function testMultipleHostException(): void
{
$route = Route::get('/')
->action(fn () => 1)
->hosts(
'https://yiiframework.com/',
'yf.com',
'{user}.yii.com'
);
->action(fn () => 1)
->hosts(
'https://yiiframework.com/',
'yf.com',
'{user}.yii.com'
);

$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Placeholders are not allowed with multiple host names.');
Expand All @@ -187,22 +214,26 @@ public function testSimpleRouteWithHostFailed(): void
{
$routes = [
Route::get('/site/index')
->action(fn () => 1)
->host('yii.test'),
->action(fn () => 1)
->host('yii.test'),
Route::get('/site/index')
->action(fn () => 1)
->host('yiiframework.{zone:ru|com}'),
->action(fn () => 1)
->host('yiiframework.{zone:ru|com}'),
];

$urlMatcher = $this->createUrlMatcher($routes);

$request = new ServerRequest('GET', '/site/index');
$request1 = $request->withUri($request
->getUri()
->withHost('yee.test'));
$request2 = $request->withUri($request
->getUri()
->withHost('yiiframework.uz'));
$request1 = $request->withUri(
$request
->getUri()
->withHost('yee.test')
);
$request2 = $request->withUri(
$request
->getUri()
->withHost('yiiframework.uz')
);

$result1 = $urlMatcher->match($request1);
$result2 = $urlMatcher->match($request2);
Expand Down Expand Up @@ -372,11 +403,11 @@ public function testNoCache(): void
{
$routes = [
Route::get('/')
->action(fn () => 1)
->name('site/index'),
->action(fn () => 1)
->name('site/index'),
Route::methods(['GET', 'POST'], '/contact')
->action(fn () => 1)
->name('site/contact'),
->action(fn () => 1)
->name('site/contact'),
];

$request = new ServerRequest('GET', '/contact');
Expand All @@ -394,9 +425,9 @@ public function testHasCache(): void
{
$routes = [
Route::get('/')
->name('site/index'),
->name('site/index'),
Route::methods(['GET', 'POST'], '/contact')
->name('site/contact'),
->name('site/contact'),
];

$cacheArray = [
Expand Down Expand Up @@ -430,8 +461,8 @@ public function testStaticRouteExcludeFromMatching(): void
{
$routes = [
Route::get('/test')
->action(fn () => 1)
->name('test'),
->action(fn () => 1)
->name('test'),
];

$urlMatcher = $this->createUrlMatcher($routes);
Expand All @@ -445,11 +476,11 @@ public function testCacheError(): void
{
$routes = [
Route::get('/')
->action(fn () => 1)
->name('site/index'),
->action(fn () => 1)
->name('site/index'),
Route::methods(['GET', 'POST'], '/contact')
->action(fn () => 1)
->name('site/contact'),
->action(fn () => 1)
->name('site/contact'),
];

$request = new ServerRequest('GET', '/contact');
Expand All @@ -468,7 +499,8 @@ public function testPure(): void
$matcher = new UrlMatcher(
new RouteCollection(
new RouteCollector()
)
),
$this->createMock(UrlGeneratorInterface::class)
);

$result = $matcher->match(new ServerRequest('GET', '/contact'));
Expand All @@ -487,11 +519,19 @@ public function testStaticRoutes(): void
$this->assertFalse($result->isSuccess());
}

private function createUrlMatcher(array $routes, CacheInterface $cache = null): UrlMatcherInterface
{
private function createUrlMatcher(
array $routes,
CacheInterface $cache = null,
?string $uriPrefix = null
): UrlMatcherInterface {
$rootGroup = Group::create()->routes(...$routes);
$collector = new RouteCollector();
$collector->addGroup($rootGroup);
return new UrlMatcher(new RouteCollection($collector), $cache, ['cache_key' => 'route-cache']);
$routeCollection = new RouteCollection($collector);
$urlGenerator ??= new UrlGenerator($routeCollection);
if ($uriPrefix !== null) {
$urlGenerator->setUriPrefix($uriPrefix);
}
return new UrlMatcher($routeCollection, $urlGenerator, $cache, ['cache_key' => 'route-cache']);
}
}