diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f01eecc..64a22146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this When selecting 301 redirects, you can also configure the time redirects are cached, to mitigate deviations in stats. +* [#734](https://github.com/shlinkio/shlink/issues/734) Added support to redirect to deeplinks and other links with schemas different from `http` and `https`. * [#709](https://github.com/shlinkio/shlink/issues/709) Added multi-architecture builds for the docker image. #### Changed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index da4b26c6..5f00dd0d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ Then you will have to follow these steps: * Run `./indocker bin/cli db:migrate` to get database migrations up to date. * Run `./indocker bin/cli api-key:generate` to get your first API key generated. -Once you finish this, you will have the project exposed in ports `8080` through nginx+php-fpm and `8000` through swoole. +Once you finish this, you will have the project exposed in ports `8000` through nginx+php-fpm and `8080` through swoole. > Note: The `indocker` shell script is a helper used to run commands inside the main docker container. diff --git a/composer.json b/composer.json index 86e35f3f..d733ba89 100644 --- a/composer.json +++ b/composer.json @@ -34,6 +34,7 @@ "laminas/laminas-servicemanager": "^3.4", "laminas/laminas-stdlib": "^3.2", "lcobucci/jwt": "^4.0@alpha", + "league/uri": "^6.2", "lstrojny/functional-php": "^1.9", "mezzio/mezzio": "^3.2", "mezzio/mezzio-fastroute": "^3.0", diff --git a/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php index 7369f1f6..06cdd274 100644 --- a/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php +++ b/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Command\ShortUrl; -use Laminas\Diactoros\Uri; use Shlinkio\Shlink\CLI\Util\ExitCodes; use Shlinkio\Shlink\Core\Exception\InvalidUrlException; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; @@ -128,19 +127,15 @@ class GenerateShortUrlCommand extends Command $shortCodeLength = $input->getOption('shortCodeLength') ?? $this->defaultShortCodeLength; try { - $shortUrl = $this->urlShortener->urlToShortCode( - new Uri($longUrl), - $tags, - ShortUrlMeta::fromRawData([ - ShortUrlMetaInputFilter::VALID_SINCE => $input->getOption('validSince'), - ShortUrlMetaInputFilter::VALID_UNTIL => $input->getOption('validUntil'), - ShortUrlMetaInputFilter::CUSTOM_SLUG => $customSlug, - ShortUrlMetaInputFilter::MAX_VISITS => $maxVisits !== null ? (int) $maxVisits : null, - ShortUrlMetaInputFilter::FIND_IF_EXISTS => $input->getOption('findIfExists'), - ShortUrlMetaInputFilter::DOMAIN => $input->getOption('domain'), - ShortUrlMetaInputFilter::SHORT_CODE_LENGTH => $shortCodeLength, - ]), - ); + $shortUrl = $this->urlShortener->urlToShortCode($longUrl, $tags, ShortUrlMeta::fromRawData([ + ShortUrlMetaInputFilter::VALID_SINCE => $input->getOption('validSince'), + ShortUrlMetaInputFilter::VALID_UNTIL => $input->getOption('validUntil'), + ShortUrlMetaInputFilter::CUSTOM_SLUG => $customSlug, + ShortUrlMetaInputFilter::MAX_VISITS => $maxVisits !== null ? (int) $maxVisits : null, + ShortUrlMetaInputFilter::FIND_IF_EXISTS => $input->getOption('findIfExists'), + ShortUrlMetaInputFilter::DOMAIN => $input->getOption('domain'), + ShortUrlMetaInputFilter::SHORT_CODE_LENGTH => $shortCodeLength, + ])); $io->writeln([ sprintf('Processed long URL: %s', $longUrl), diff --git a/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php index bcf00acb..689a5e7c 100644 --- a/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php @@ -8,7 +8,6 @@ use PHPUnit\Framework\Assert; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; -use Psr\Http\Message\UriInterface; use Shlinkio\Shlink\CLI\Command\ShortUrl\GenerateShortUrlCommand; use Shlinkio\Shlink\CLI\Util\ExitCodes; use Shlinkio\Shlink\Core\Entity\ShortUrl; @@ -88,7 +87,7 @@ class GenerateShortUrlCommandTest extends TestCase { $shortUrl = new ShortUrl(''); $urlToShortCode = $this->urlShortener->urlToShortCode( - Argument::type(UriInterface::class), + Argument::type('string'), Argument::that(function (array $tags) { Assert::assertEquals(['foo', 'bar', 'baz', 'boo', 'zar'], $tags); return $tags; diff --git a/module/Core/src/Action/AbstractTrackingAction.php b/module/Core/src/Action/AbstractTrackingAction.php index 4d883794..4bf391c7 100644 --- a/module/Core/src/Action/AbstractTrackingAction.php +++ b/module/Core/src/Action/AbstractTrackingAction.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Action; use Fig\Http\Message\RequestMethodInterface; -use Laminas\Diactoros\Uri; +use League\Uri\Uri; use Mezzio\Router\Middleware\ImplicitHeadMiddleware; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -67,7 +67,7 @@ abstract class AbstractTrackingAction implements MiddlewareInterface, RequestMet private function buildUrlToRedirectTo(ShortUrl $shortUrl, array $currentQuery, ?string $disableTrackParam): string { - $uri = new Uri($shortUrl->getLongUrl()); + $uri = Uri::createFromString($shortUrl->getLongUrl()); $hardcodedQuery = parse_query($uri->getQuery()); if ($disableTrackParam !== null) { unset($currentQuery[$disableTrackParam]); diff --git a/module/Core/src/Model/CreateShortUrlData.php b/module/Core/src/Model/CreateShortUrlData.php index 24ed90a6..9b64302d 100644 --- a/module/Core/src/Model/CreateShortUrlData.php +++ b/module/Core/src/Model/CreateShortUrlData.php @@ -4,41 +4,32 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Model; -use Psr\Http\Message\UriInterface; - final class CreateShortUrlData { - private UriInterface $longUrl; + private string $longUrl; private array $tags; private ShortUrlMeta $meta; - public function __construct( - UriInterface $longUrl, - array $tags = [], - ?ShortUrlMeta $meta = null - ) { + public function __construct(string $longUrl, array $tags = [], ?ShortUrlMeta $meta = null) + { $this->longUrl = $longUrl; $this->tags = $tags; $this->meta = $meta ?? ShortUrlMeta::createEmpty(); } - /** - */ - public function getLongUrl(): UriInterface + public function getLongUrl(): string { return $this->longUrl; } /** - * @return array + * @return string[] */ public function getTags(): array { return $this->tags; } - /** - */ public function getMeta(): ShortUrlMeta { return $this->meta; diff --git a/module/Core/src/Service/UrlShortener.php b/module/Core/src/Service/UrlShortener.php index 4544bfc0..7892f959 100644 --- a/module/Core/src/Service/UrlShortener.php +++ b/module/Core/src/Service/UrlShortener.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Service; use Doctrine\ORM\EntityManagerInterface; -use Psr\Http\Message\UriInterface; use Shlinkio\Shlink\Core\Domain\Resolver\DomainResolverInterface; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\InvalidUrlException; @@ -42,10 +41,8 @@ class UrlShortener implements UrlShortenerInterface * @throws InvalidUrlException * @throws Throwable */ - public function urlToShortCode(UriInterface $url, array $tags, ShortUrlMeta $meta): ShortUrl + public function urlToShortCode(string $url, array $tags, ShortUrlMeta $meta): ShortUrl { - $url = (string) $url; - // First, check if a short URL exists for all provided params $existingShortUrl = $this->findExistingShortUrlIfExists($url, $tags, $meta); if ($existingShortUrl !== null) { diff --git a/module/Core/src/Service/UrlShortenerInterface.php b/module/Core/src/Service/UrlShortenerInterface.php index 802eb048..e26530ca 100644 --- a/module/Core/src/Service/UrlShortenerInterface.php +++ b/module/Core/src/Service/UrlShortenerInterface.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Service; -use Psr\Http\Message\UriInterface; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\InvalidUrlException; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; @@ -17,5 +16,5 @@ interface UrlShortenerInterface * @throws NonUniqueSlugException * @throws InvalidUrlException */ - public function urlToShortCode(UriInterface $url, array $tags, ShortUrlMeta $meta): ShortUrl; + public function urlToShortCode(string $url, array $tags, ShortUrlMeta $meta): ShortUrl; } diff --git a/module/Core/test/Service/UrlShortenerTest.php b/module/Core/test/Service/UrlShortenerTest.php index 2c67bf27..f1ef88af 100644 --- a/module/Core/test/Service/UrlShortenerTest.php +++ b/module/Core/test/Service/UrlShortenerTest.php @@ -9,7 +9,6 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\DBAL\Connection; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\ORMException; -use Laminas\Diactoros\Uri; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; @@ -65,7 +64,7 @@ class UrlShortenerTest extends TestCase public function urlIsProperlyShortened(): void { $shortUrl = $this->urlShortener->urlToShortCode( - new Uri('http://foobar.com/12345/hello?foo=bar'), + 'http://foobar.com/12345/hello?foo=bar', [], ShortUrlMeta::createEmpty(), ); @@ -89,7 +88,7 @@ class UrlShortenerTest extends TestCase $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); $shortUrl = $this->urlShortener->urlToShortCode( - new Uri('http://foobar.com/12345/hello?foo=bar'), + 'http://foobar.com/12345/hello?foo=bar', [], ShortUrlMeta::createEmpty(), ); @@ -112,7 +111,7 @@ class UrlShortenerTest extends TestCase $this->expectException(ORMException::class); $this->urlShortener->urlToShortCode( - new Uri('http://foobar.com/12345/hello?foo=bar'), + 'http://foobar.com/12345/hello?foo=bar', [], ShortUrlMeta::createEmpty(), ); @@ -131,7 +130,7 @@ class UrlShortenerTest extends TestCase $this->expectException(NonUniqueSlugException::class); $this->urlShortener->urlToShortCode( - new Uri('http://foobar.com/12345/hello?foo=bar'), + 'http://foobar.com/12345/hello?foo=bar', [], ShortUrlMeta::fromRawData(['customSlug' => 'custom-slug']), ); @@ -151,7 +150,7 @@ class UrlShortenerTest extends TestCase $findExisting = $repo->findBy(Argument::any())->willReturn([$expected]); $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); - $result = $this->urlShortener->urlToShortCode(new Uri($url), $tags, $meta); + $result = $this->urlShortener->urlToShortCode($url, $tags, $meta); $findExisting->shouldHaveBeenCalledOnce(); $getRepo->shouldHaveBeenCalledOnce(); @@ -235,7 +234,7 @@ class UrlShortenerTest extends TestCase ]); $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); - $result = $this->urlShortener->urlToShortCode(new Uri($url), $tags, $meta); + $result = $this->urlShortener->urlToShortCode($url, $tags, $meta); $this->assertSame($expected, $result); $findExisting->shouldHaveBeenCalledOnce(); diff --git a/module/Rest/src/Action/ShortUrl/CreateShortUrlAction.php b/module/Rest/src/Action/ShortUrl/CreateShortUrlAction.php index 489d1277..97097808 100644 --- a/module/Rest/src/Action/ShortUrl/CreateShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/CreateShortUrlAction.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Rest\Action\ShortUrl; -use Laminas\Diactoros\Uri; use Psr\Http\Message\ServerRequestInterface as Request; use Shlinkio\Shlink\Core\Exception\ValidationException; use Shlinkio\Shlink\Core\Model\CreateShortUrlData; @@ -28,6 +27,6 @@ class CreateShortUrlAction extends AbstractCreateShortUrlAction } $meta = ShortUrlMeta::fromRawData($postData); - return new CreateShortUrlData(new Uri($postData['longUrl']), (array) ($postData['tags'] ?? []), $meta); + return new CreateShortUrlData($postData['longUrl'], (array) ($postData['tags'] ?? []), $meta); } } diff --git a/module/Rest/src/Action/ShortUrl/SingleStepCreateShortUrlAction.php b/module/Rest/src/Action/ShortUrl/SingleStepCreateShortUrlAction.php index daeb3d04..46385556 100644 --- a/module/Rest/src/Action/ShortUrl/SingleStepCreateShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/SingleStepCreateShortUrlAction.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Rest\Action\ShortUrl; -use Laminas\Diactoros\Uri; use Psr\Http\Message\ServerRequestInterface as Request; use Shlinkio\Shlink\Core\Exception\ValidationException; use Shlinkio\Shlink\Core\Model\CreateShortUrlData; @@ -46,6 +45,6 @@ class SingleStepCreateShortUrlAction extends AbstractCreateShortUrlAction ]); } - return new CreateShortUrlData(new Uri($query['longUrl'])); + return new CreateShortUrlData($query['longUrl']); } } diff --git a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php index 3a343d60..66f1eaaa 100644 --- a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php @@ -7,7 +7,6 @@ namespace ShlinkioTest\Shlink\Rest\Action\ShortUrl; use Cake\Chronos\Chronos; use Laminas\Diactoros\ServerRequest; use Laminas\Diactoros\ServerRequestFactory; -use Laminas\Diactoros\Uri; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; @@ -50,7 +49,7 @@ class CreateShortUrlActionTest extends TestCase { $shortUrl = new ShortUrl(''); $shorten = $this->urlShortener->urlToShortCode( - Argument::type(Uri::class), + Argument::type('string'), Argument::type('array'), $expectedMeta, )->willReturn($shortUrl); diff --git a/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php index 1af4aeba..d63a83b9 100644 --- a/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php @@ -9,7 +9,6 @@ use PHPUnit\Framework\Assert; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; -use Psr\Http\Message\UriInterface; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\ValidationException; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; @@ -71,8 +70,8 @@ class SingleStepCreateShortUrlActionTest extends TestCase ]); $findApiKey = $this->apiKeyService->check('abc123')->willReturn(true); $generateShortCode = $this->urlShortener->urlToShortCode( - Argument::that(function (UriInterface $argument) { - Assert::assertEquals('http://foobar.com', (string) $argument); + Argument::that(function (string $argument): string { + Assert::assertEquals('http://foobar.com', $argument); return $argument; }), [],