diff --git a/CHANGELOG.md b/CHANGELOG.md index 566ae9dc..1a0b0c9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this ### Changed * [#1935](https://github.com/shlinkio/shlink/issues/1935) Replace dependency on abandoned `php-middleware/request-id` with userland simple middleware. +* [#1988](https://github.com/shlinkio/shlink/issues/1988) Remove dependency on `league\uri` package. ### Deprecated * *Nothing* diff --git a/composer.json b/composer.json index c309aeef..0ea76443 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,6 @@ "laminas/laminas-inputfilter": "^2.27", "laminas/laminas-servicemanager": "^3.21", "laminas/laminas-stdlib": "^3.17", - "league/uri": "^6.8", "matomo/matomo-php-tracker": "^3.2", "mezzio/mezzio": "^3.17", "mezzio/mezzio-fastroute": "^3.11", diff --git a/module/Core/src/Config/NotFoundRedirectResolver.php b/module/Core/src/Config/NotFoundRedirectResolver.php index ce5401d2..cfb09c8e 100644 --- a/module/Core/src/Config/NotFoundRedirectResolver.php +++ b/module/Core/src/Config/NotFoundRedirectResolver.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Config; -use League\Uri\Exceptions\SyntaxError; -use League\Uri\Uri; +use Laminas\Diactoros\Exception\InvalidArgumentException; +use Laminas\Diactoros\Uri; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\UriInterface; use Psr\Log\LoggerInterface; @@ -51,8 +51,8 @@ class NotFoundRedirectResolver implements NotFoundRedirectResolverInterface private function resolvePlaceholders(UriInterface $currentUri, string $redirectUrl): string { try { - $redirectUri = Uri::createFromString($redirectUrl); - } catch (SyntaxError $e) { + $redirectUri = new Uri($redirectUrl); + } catch (InvalidArgumentException $e) { $this->logger->warning('It was not possible to parse "{url}" as a valid URL: {e}', [ 'e' => $e, 'url' => $redirectUrl, @@ -63,26 +63,22 @@ class NotFoundRedirectResolver implements NotFoundRedirectResolverInterface $path = $currentUri->getPath(); $domain = $currentUri->getAuthority(); - $replacePlaceholderForPattern = static fn (string $pattern, string $replace, ?string $value): string|null => - $value === null ? null : str_replace($pattern, $replace, $value); - $replacePlaceholders = static function ( callable $modifier, - ?string $value, + string $value, ) use ( - $replacePlaceholderForPattern, $path, $domain, - ): string|null { - $value = $replacePlaceholderForPattern($modifier(self::DOMAIN_PLACEHOLDER), $modifier($domain), $value); - return $replacePlaceholderForPattern($modifier(self::ORIGINAL_PATH_PLACEHOLDER), $modifier($path), $value); + ): string { + $value = str_replace(urlencode(self::DOMAIN_PLACEHOLDER), $modifier($domain), $value); + return str_replace(urlencode(self::ORIGINAL_PATH_PLACEHOLDER), $modifier($path), $value); }; $replacePlaceholdersInPath = static function (string $path) use ($replacePlaceholders): string { $result = $replacePlaceholders(static fn (mixed $v) => $v, $path); - return str_replace('//', '/', $result ?? ''); + return str_replace('//', '/', $result); }; - $replacePlaceholdersInQuery = static fn (?string $query): string|null => $replacePlaceholders( + $replacePlaceholdersInQuery = static fn (string $query): string => $replacePlaceholders( urlencode(...), $query, ); diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php b/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php index c322f195..bbf616f7 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\ShortUrl\Helper; use GuzzleHttp\Psr7\Query; +use Laminas\Diactoros\Uri; use Laminas\Stdlib\ArrayUtils; -use League\Uri\Uri; use Psr\Http\Message\ServerRequestInterface; use Shlinkio\Shlink\Core\Model\DeviceType; use Shlinkio\Shlink\Core\Options\TrackingOptions; @@ -27,7 +27,7 @@ class ShortUrlRedirectionBuilder implements ShortUrlRedirectionBuilderInterface ): string { $currentQuery = $request->getQueryParams(); $device = DeviceType::matchFromUserAgent($request->getHeaderLine('User-Agent')); - $uri = Uri::createFromString($shortUrl->longUrlForDevice($device)); + $uri = new Uri($shortUrl->longUrlForDevice($device)); $shouldForwardQuery = $shortUrl->forwardQuery(); return $uri @@ -36,9 +36,9 @@ class ShortUrlRedirectionBuilder implements ShortUrlRedirectionBuilderInterface ->__toString(); } - private function resolveQuery(Uri $uri, array $currentQuery): ?string + private function resolveQuery(Uri $uri, array $currentQuery): string { - $hardcodedQuery = Query::parse($uri->getQuery() ?? ''); + $hardcodedQuery = Query::parse($uri->getQuery()); $disableTrackParam = $this->trackingOptions->disableTrackParam; if ($disableTrackParam !== null) { @@ -48,7 +48,7 @@ class ShortUrlRedirectionBuilder implements ShortUrlRedirectionBuilderInterface // We want to merge preserving numeric keys, as some params might be numbers $mergedQuery = ArrayUtils::merge($hardcodedQuery, $currentQuery, true); - return empty($mergedQuery) ? null : Query::build($mergedQuery); + return Query::build($mergedQuery); } private function resolvePath(Uri $uri, ?string $extraPath): string diff --git a/module/Core/test-api/Action/RedirectTest.php b/module/Core/test-api/Action/RedirectTest.php index f3edcbe4..bbcc6fec 100644 --- a/module/Core/test-api/Action/RedirectTest.php +++ b/module/Core/test-api/Action/RedirectTest.php @@ -18,6 +18,8 @@ class RedirectTest extends ApiTestCase public function properRedirectHappensBasedOnUserAgent(?string $userAgent, string $expectedRedirect): void { $response = $this->callShortUrl('def456', $userAgent); + + self::assertEquals(302, $response->getStatusCode()); self::assertEquals($expectedRedirect, $response->getHeaderLine('Location')); } diff --git a/module/Core/test/Config/NotFoundRedirectResolverTest.php b/module/Core/test/Config/NotFoundRedirectResolverTest.php index 0b943099..5ef5db2b 100644 --- a/module/Core/test/Config/NotFoundRedirectResolverTest.php +++ b/module/Core/test/Config/NotFoundRedirectResolverTest.php @@ -57,8 +57,14 @@ class NotFoundRedirectResolverTest extends TestCase yield 'base URL with trailing slash' => [ $uri = new Uri('/'), self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), - new NotFoundRedirectOptions(baseUrl: 'baseUrl'), - 'baseUrl', + new NotFoundRedirectOptions(baseUrl: 'https://example.com/baseUrl'), + 'https://example.com/baseUrl', + ]; + yield 'base URL without trailing slash' => [ + $uri = new Uri(''), + self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), + new NotFoundRedirectOptions(baseUrl: 'https://example.com/baseUrl'), + 'https://example.com/baseUrl', ]; yield 'base URL with domain placeholder' => [ $uri = new Uri('https://s.test'), @@ -72,17 +78,11 @@ class NotFoundRedirectResolverTest extends TestCase new NotFoundRedirectOptions(baseUrl: 'https://redirect-here.com/?domain={DOMAIN}'), 'https://redirect-here.com/?domain=s.test', ]; - yield 'base URL without trailing slash' => [ - $uri = new Uri(''), - self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), - new NotFoundRedirectOptions(baseUrl: 'baseUrl'), - 'baseUrl', - ]; yield 'regular 404' => [ $uri = new Uri('/foo/bar'), self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), - new NotFoundRedirectOptions(regular404: 'regular404'), - 'regular404', + new NotFoundRedirectOptions(regular404: 'https://example.com/regular404'), + 'https://example.com/regular404', ]; yield 'regular 404 with path placeholder in query' => [ $uri = new Uri('/foo/bar'), @@ -101,8 +101,8 @@ class NotFoundRedirectResolverTest extends TestCase yield 'invalid short URL' => [ new Uri('/foo'), self::notFoundType(self::requestForRoute(RedirectAction::class)), - new NotFoundRedirectOptions(invalidShortUrl: 'invalidShortUrl'), - 'invalidShortUrl', + new NotFoundRedirectOptions(invalidShortUrl: 'https://example.com/invalidShortUrl'), + 'https://example.com/invalidShortUrl', ]; yield 'invalid short URL with path placeholder' => [ new Uri('/foo'),