From bff4bd12ae08038bb6223bde0f8514d5bcb2650f Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 30 Nov 2023 14:34:21 +0100 Subject: [PATCH] Removed more functional-php usages --- composer.json | 3 ++ data/migrations/Version20200105165647.php | 9 ++--- data/migrations/Version20200106215144.php | 19 ++++++---- data/migrations/Version20200110182849.php | 13 +++---- .../Command/Domain/DomainRedirectsCommand.php | 12 +++---- module/CLI/src/Util/ShlinkTable.php | 28 +++++++++++---- module/Core/functions/functions.php | 36 +++++++++++++++++-- .../src/Config/NotFoundRedirectResolver.php | 7 ++-- .../ShortUrlMethodsProcessor.php | 29 +++++++++------ module/Core/src/Domain/DomainService.php | 19 ++++++---- .../Async/AbstractNotifyVisitListener.php | 4 +-- .../src/ShortUrl/Model/DeviceLongUrlPair.php | 26 +++++--------- .../Validation/DeviceLongUrlsValidator.php | 2 +- .../Core/src/Tag/Repository/TagRepository.php | 5 +-- .../RabbitMq/NotifyVisitToRabbitMqTest.php | 9 +++-- .../Importer/ImportedLinksProcessorTest.php | 2 +- .../test/ShortUrl/Entity/ShortUrlTest.php | 2 +- .../TrimTrailingSlashMiddlewareTest.php | 8 ++--- module/Rest/src/ConfigProvider.php | 8 +++-- ...wardsCompatibleProblemDetailsException.php | 6 ++-- 20 files changed, 156 insertions(+), 91 deletions(-) diff --git a/composer.json b/composer.json index 8071556e..e295539e 100644 --- a/composer.json +++ b/composer.json @@ -80,6 +80,9 @@ "symfony/var-dumper": "^6.3", "veewee/composer-run-parallel": "^1.3" }, + "conflict": { + "symfony/var-exporter": ">=6.3.9,<=6.4.0" + }, "autoload": { "psr-4": { "Shlinkio\\Shlink\\CLI\\": "module/CLI/src", diff --git a/data/migrations/Version20200105165647.php b/data/migrations/Version20200105165647.php index fb3b7961..bb497021 100644 --- a/data/migrations/Version20200105165647.php +++ b/data/migrations/Version20200105165647.php @@ -11,7 +11,7 @@ use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Types\Types; use Doctrine\Migrations\AbstractMigration; -use function Functional\some; +use function Shlinkio\Shlink\Core\some; final class Version20200105165647 extends AbstractMigration { @@ -23,11 +23,12 @@ final class Version20200105165647 extends AbstractMigration public function preUp(Schema $schema): void { $visitLocations = $schema->getTable('visit_locations'); - $this->skipIf(some( - self::COLUMNS, - fn (string $v, string $newColName) => $visitLocations->hasColumn($newColName), + $this->skipIf(some( + self::COLUMNS, + fn (string $v, string $newColName) => $visitLocations->hasColumn($newColName), ), 'New columns already exist'); + foreach (self::COLUMNS as $columnName) { $qb = $this->connection->createQueryBuilder(); $qb->update('visit_locations') diff --git a/data/migrations/Version20200106215144.php b/data/migrations/Version20200106215144.php index 830daf64..f5faba4e 100644 --- a/data/migrations/Version20200106215144.php +++ b/data/migrations/Version20200106215144.php @@ -7,11 +7,10 @@ namespace ShlinkMigrations; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Types; use Doctrine\Migrations\AbstractMigration; -use function Functional\none; - final class Version20200106215144 extends AbstractMigration { private const COLUMNS = ['latitude', 'longitude']; @@ -22,16 +21,24 @@ final class Version20200106215144 extends AbstractMigration public function up(Schema $schema): void { $visitLocations = $schema->getTable('visit_locations'); - $this->skipIf(none( - self::COLUMNS, - fn (string $oldColName) => $visitLocations->hasColumn($oldColName), - ), 'Old columns do not exist'); + $this->skipIf($this->oldColumnsDoNotExist($visitLocations), 'Old columns do not exist'); foreach (self::COLUMNS as $colName) { $visitLocations->dropColumn($colName); } } + public function oldColumnsDoNotExist(Table $visitLocations): bool + { + foreach (self::COLUMNS as $oldColName) { + if ($visitLocations->hasColumn($oldColName)) { + return false; + } + } + + return true; + } + /** * @throws Exception */ diff --git a/data/migrations/Version20200110182849.php b/data/migrations/Version20200110182849.php index b267bfbc..4b608bb2 100644 --- a/data/migrations/Version20200110182849.php +++ b/data/migrations/Version20200110182849.php @@ -9,9 +9,6 @@ use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; -use function Functional\each; -use function Functional\partial_left; - final class Version20200110182849 extends AbstractMigration { private const DEFAULT_EMPTY_VALUE = ''; @@ -31,11 +28,11 @@ final class Version20200110182849 extends AbstractMigration public function up(Schema $schema): void { - each( - self::COLUMN_DEFAULTS_MAP, - fn (array $columns, string $tableName) => - each($columns, partial_left([$this, 'setDefaultValueForColumnInTable'], $tableName)), - ); + foreach (self::COLUMN_DEFAULTS_MAP as $tableName => $columns) { + foreach ($columns as $columnName) { + $this->setDefaultValueForColumnInTable($tableName, $columnName); + } + } } /** diff --git a/module/CLI/src/Command/Domain/DomainRedirectsCommand.php b/module/CLI/src/Command/Domain/DomainRedirectsCommand.php index 4a3f8062..bf08e7f3 100644 --- a/module/CLI/src/Command/Domain/DomainRedirectsCommand.php +++ b/module/CLI/src/Command/Domain/DomainRedirectsCommand.php @@ -14,8 +14,8 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use function Functional\filter; -use function Functional\invoke; +use function array_filter; +use function array_map; use function sprintf; use function str_contains; @@ -23,7 +23,7 @@ class DomainRedirectsCommand extends Command { public const NAME = 'domain:redirects'; - public function __construct(private DomainServiceInterface $domainService) + public function __construct(private readonly DomainServiceInterface $domainService) { parent::__construct(); } @@ -52,9 +52,9 @@ class DomainRedirectsCommand extends Command $askNewDomain = static fn () => $io->ask('Domain authority for which you want to set specific redirects'); /** @var string[] $availableDomains */ - $availableDomains = invoke( - filter($this->domainService->listDomains(), static fn (DomainItem $item) => ! $item->isDefault), - 'toString', + $availableDomains = array_map( + static fn (DomainItem $item) => $item->toString(), + array_filter($this->domainService->listDomains(), static fn (DomainItem $item) => ! $item->isDefault), ); if (empty($availableDomains)) { $input->setArgument('domain', $askNewDomain()); diff --git a/module/CLI/src/Util/ShlinkTable.php b/module/CLI/src/Util/ShlinkTable.php index cd38e5cd..c421c613 100644 --- a/module/CLI/src/Util/ShlinkTable.php +++ b/module/CLI/src/Util/ShlinkTable.php @@ -8,30 +8,30 @@ use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Output\OutputInterface; -use function Functional\intersperse; +use function array_pop; final class ShlinkTable { private const DEFAULT_STYLE_NAME = 'default'; private const TABLE_TITLE_STYLE = ' %s '; - private function __construct(private readonly Table $baseTable, private readonly bool $withRowSeparators) + private function __construct(private readonly Table $baseTable, private readonly bool $withRowSeparators = false) { } public static function default(OutputInterface $output): self { - return new self(new Table($output), false); + return new self(new Table($output)); } public static function withRowSeparators(OutputInterface $output): self { - return new self(new Table($output), true); + return new self(new Table($output), withRowSeparators: true); } public static function fromBaseTable(Table $baseTable): self { - return new self($baseTable, false); + return new self($baseTable); } public function render(array $headers, array $rows, ?string $footerTitle = null, ?string $headerTitle = null): void @@ -39,7 +39,7 @@ final class ShlinkTable $style = Table::getStyleDefinition(self::DEFAULT_STYLE_NAME); $style->setFooterTitleFormat(self::TABLE_TITLE_STYLE) ->setHeaderTitleFormat(self::TABLE_TITLE_STYLE); - $tableRows = $this->withRowSeparators ? intersperse($rows, new TableSeparator()) : $rows; + $tableRows = $this->withRowSeparators ? $this->addRowSeparators($rows) : $rows; $table = clone $this->baseTable; $table->setStyle($style) @@ -49,4 +49,20 @@ final class ShlinkTable ->setHeaderTitle($headerTitle) ->render(); } + + private function addRowSeparators(array $rows): array + { + $aggregation = []; + $separator = new TableSeparator(); + + foreach ($rows as $row) { + $aggregation[] = $row; + $aggregation[] = $separator; + } + + // Remove last separator + array_pop($aggregation); + + return $aggregation; + } } diff --git a/module/Core/functions/functions.php b/module/Core/functions/functions.php index bcda4bb4..f13fc670 100644 --- a/module/Core/functions/functions.php +++ b/module/Core/functions/functions.php @@ -16,10 +16,10 @@ use PUGX\Shortid\Factory as ShortIdFactory; use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode; +use function array_keys; use function array_map; use function array_reduce; use function date_default_timezone_get; -use function Functional\reduce_left; use function in_array; use function is_array; use function print_r; @@ -95,10 +95,12 @@ function getNonEmptyOptionalValueFromInputFilter(InputFilter $inputFilter, strin function arrayToString(array $array, int $indentSize = 4): string { $indent = str_repeat(' ', $indentSize); + $names = array_keys($array); $index = 0; - return reduce_left($array, static function ($messages, string $name, $_, string $acc) use (&$index, $indent) { + return array_reduce($names, static function (string $acc, string $name) use (&$index, $indent, $array) { $index++; + $messages = $array[$name]; return $acc . sprintf( "%s%s'%s' => %s", @@ -199,3 +201,33 @@ function flatten(array $multiArray): array initial: [], ); } + +/** + * Checks if a callback returns true for at least one item in a collection. + * @param callable(mixed $value, string|number $key): bool $callback + */ +function some(iterable $collection, callable $callback): bool +{ + foreach ($collection as $key => $value) { + if ($callback($value, $key)) { + return true; + } + } + + return false; +} + +/** + * Checks if a callback returns true for all item in a collection. + * @param callable(mixed $value, string|number $key): bool $callback + */ +function every(iterable $collection, callable $callback): bool +{ + foreach ($collection as $key => $value) { + if (! $callback($value, $key)) { + return false; + } + } + + return true; +} diff --git a/module/Core/src/Config/NotFoundRedirectResolver.php b/module/Core/src/Config/NotFoundRedirectResolver.php index 3ab2e740..540956ee 100644 --- a/module/Core/src/Config/NotFoundRedirectResolver.php +++ b/module/Core/src/Config/NotFoundRedirectResolver.php @@ -13,7 +13,6 @@ use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface; use function Functional\compose; -use function Functional\id; use function str_replace; use function urlencode; @@ -23,8 +22,8 @@ class NotFoundRedirectResolver implements NotFoundRedirectResolverInterface private const ORIGINAL_PATH_PLACEHOLDER = '{ORIGINAL_PATH}'; public function __construct( - private RedirectResponseHelperInterface $redirectResponseHelper, - private LoggerInterface $logger, + private readonly RedirectResponseHelperInterface $redirectResponseHelper, + private readonly LoggerInterface $logger, ) { } @@ -73,7 +72,7 @@ class NotFoundRedirectResolver implements NotFoundRedirectResolverInterface $replacePlaceholderForPattern(self::ORIGINAL_PATH_PLACEHOLDER, $path, $modifier), ); $replacePlaceholdersInPath = compose( - $replacePlaceholders(id(...)), + $replacePlaceholders(static fn (mixed $v) => $v), static fn (?string $path) => $path === null ? null : str_replace('//', '/', $path), ); $replacePlaceholdersInQuery = $replacePlaceholders(urlencode(...)); diff --git a/module/Core/src/Config/PostProcessor/ShortUrlMethodsProcessor.php b/module/Core/src/Config/PostProcessor/ShortUrlMethodsProcessor.php index 05ecdb6c..42f00889 100644 --- a/module/Core/src/Config/PostProcessor/ShortUrlMethodsProcessor.php +++ b/module/Core/src/Config/PostProcessor/ShortUrlMethodsProcessor.php @@ -9,25 +9,34 @@ use Mezzio\Router\Route; use Shlinkio\Shlink\Core\Action\RedirectAction; use Shlinkio\Shlink\Core\Util\RedirectStatus; -use function array_values; -use function count; -use function Functional\partition; - use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE; +/** + * Sets the appropriate allowed methods on the redirect route, based on the redirect status code that was configured. + * * For "legacy" status codes (301 and 302) the redirect URL will work only on GET method. + * * For other status codes (307 and 308) the redirect URL will work on any method. + */ class ShortUrlMethodsProcessor { public function __invoke(array $config): array { - [$redirectRoutes, $rest] = partition( - $config['routes'] ?? [], - static fn (array $route) => $route['name'] === RedirectAction::class, - ); - if (count($redirectRoutes) === 0) { + $allRoutes = $config['routes'] ?? []; + $redirectRoute = null; + $rest = []; + + // Get default route from routes array + foreach ($allRoutes as $route) { + if ($route['name'] === RedirectAction::class) { + $redirectRoute ??= $route; + } else { + $rest[] = $route; + } + } + + if ($redirectRoute === null) { return $config; } - [$redirectRoute] = array_values($redirectRoutes); $redirectStatus = RedirectStatus::tryFrom( $config['redirects']['redirect_status_code'] ?? 0, ) ?? DEFAULT_REDIRECT_STATUS_CODE; diff --git a/module/Core/src/Domain/DomainService.php b/module/Core/src/Domain/DomainService.php index 9aa4e3d0..93adbf5f 100644 --- a/module/Core/src/Domain/DomainService.php +++ b/module/Core/src/Domain/DomainService.php @@ -15,8 +15,6 @@ use Shlinkio\Shlink\Rest\ApiKey\Role; use Shlinkio\Shlink\Rest\Entity\ApiKey; use function array_map; -use function Functional\first; -use function Functional\group; class DomainService implements DomainServiceInterface { @@ -49,12 +47,19 @@ class DomainService implements DomainServiceInterface { /** @var DomainRepositoryInterface $repo */ $repo = $this->em->getRepository(Domain::class); - $groups = group( - $repo->findDomains($apiKey), - fn (Domain $domain) => $domain->authority === $this->defaultDomain ? 'default' : 'domains', - ); + $allDomains = $repo->findDomains($apiKey); + $defaultDomain = null; + $restOfDomains = []; - return [first($groups['default'] ?? []), $groups['domains'] ?? []]; + foreach ($allDomains as $domain) { + if ($domain->authority === $this->defaultDomain) { + $defaultDomain = $domain; + } else { + $restOfDomains[] = $domain; + } + } + + return [$defaultDomain, $restOfDomains]; } /** diff --git a/module/Core/src/EventDispatcher/Async/AbstractNotifyVisitListener.php b/module/Core/src/EventDispatcher/Async/AbstractNotifyVisitListener.php index dae9130f..3ec9417c 100644 --- a/module/Core/src/EventDispatcher/Async/AbstractNotifyVisitListener.php +++ b/module/Core/src/EventDispatcher/Async/AbstractNotifyVisitListener.php @@ -13,7 +13,7 @@ use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface; use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Throwable; -use function Functional\each; +use function array_walk; abstract class AbstractNotifyVisitListener extends AbstractAsyncListener { @@ -46,7 +46,7 @@ abstract class AbstractNotifyVisitListener extends AbstractAsyncListener $updates = $this->determineUpdatesForVisit($visit); try { - each($updates, fn (Update $update) => $this->publishingHelper->publishUpdate($update)); + array_walk($updates, fn (Update $update) => $this->publishingHelper->publishUpdate($update)); } catch (Throwable $e) { $this->logger->debug( 'Error while trying to notify {name} with new visit. {e}', diff --git a/module/Core/src/ShortUrl/Model/DeviceLongUrlPair.php b/module/Core/src/ShortUrl/Model/DeviceLongUrlPair.php index c7b1efc0..a83ec01d 100644 --- a/module/Core/src/ShortUrl/Model/DeviceLongUrlPair.php +++ b/module/Core/src/ShortUrl/Model/DeviceLongUrlPair.php @@ -6,7 +6,6 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Model; use Shlinkio\Shlink\Core\Model\DeviceType; -use function Functional\group; use function trim; final class DeviceLongUrlPair @@ -25,27 +24,20 @@ final class DeviceLongUrlPair * * The first one is a list of mapped instances for those entries in the map with non-null value * * The second is a list of DeviceTypes which have been provided with value null * - * @param array $map + * @param array $map * @return array{array, DeviceType[]} */ public static function fromMapToChangeSet(array $map): array { - $toRemove = []; // TODO Use when group is removed - $toKeep = []; // TODO Use when group is removed - $typesWithNullUrl = group($map, static fn (?string $longUrl) => $longUrl === null ? 'remove' : 'keep'); - - $deviceTypesToRemove = []; - foreach ($typesWithNullUrl['remove'] ?? [] as $deviceType => $_) { - $deviceTypesToRemove[] = DeviceType::from($deviceType); - } - $pairsToKeep = []; - /** - * @var string $deviceType - * @var string $longUrl - */ - foreach ($typesWithNullUrl['keep'] ?? [] as $deviceType => $longUrl) { - $pairsToKeep[$deviceType] = self::fromRawTypeAndLongUrl($deviceType, $longUrl); + $deviceTypesToRemove = []; + + foreach ($map as $deviceType => $longUrl) { + if ($longUrl === null) { + $deviceTypesToRemove[] = DeviceType::from($deviceType); + } else { + $pairsToKeep[$deviceType] = self::fromRawTypeAndLongUrl($deviceType, $longUrl); + } } return [$pairsToKeep, $deviceTypesToRemove]; diff --git a/module/Core/src/ShortUrl/Model/Validation/DeviceLongUrlsValidator.php b/module/Core/src/ShortUrl/Model/Validation/DeviceLongUrlsValidator.php index 5694f6e1..0c3b19c2 100644 --- a/module/Core/src/ShortUrl/Model/Validation/DeviceLongUrlsValidator.php +++ b/module/Core/src/ShortUrl/Model/Validation/DeviceLongUrlsValidator.php @@ -10,10 +10,10 @@ use Shlinkio\Shlink\Core\Model\DeviceType; use function array_keys; use function array_values; -use function Functional\every; use function is_array; use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\enumValues; +use function Shlinkio\Shlink\Core\every; class DeviceLongUrlsValidator extends AbstractValidator { diff --git a/module/Core/src/Tag/Repository/TagRepository.php b/module/Core/src/Tag/Repository/TagRepository.php index d74da44a..ce8b1f76 100644 --- a/module/Core/src/Tag/Repository/TagRepository.php +++ b/module/Core/src/Tag/Repository/TagRepository.php @@ -18,7 +18,7 @@ use Shlinkio\Shlink\Rest\ApiKey\Spec\WithApiKeySpecsEnsuringJoin; use Shlinkio\Shlink\Rest\Entity\ApiKey; use function array_map; -use function Functional\each; +use function array_walk; use function Shlinkio\Shlink\Core\camelCaseToSnakeCase; use const PHP_INT_MAX; @@ -95,7 +95,8 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito $nonBotVisitsSubQb = $buildVisitsSubQb(true, 'non_bot_visits'); // Apply API key specification to all sub-queries - each([$tagsSubQb, $allVisitsSubQb, $nonBotVisitsSubQb], $applyApiKeyToNativeQb); + $queryBuilders = [$tagsSubQb, $allVisitsSubQb, $nonBotVisitsSubQb]; + array_walk($queryBuilders, $applyApiKeyToNativeQb); // A native query builder needs to be used here, because DQL and ORM query builders do not support // sub-queries at "from" and "join" level. diff --git a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php index 0002d3b1..e722bf25 100644 --- a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php +++ b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php @@ -27,9 +27,8 @@ use Shlinkio\Shlink\Core\Visit\Model\Visitor; use Shlinkio\Shlink\Core\Visit\Transformer\OrphanVisitDataTransformer; use Throwable; +use function array_walk; use function count; -use function Functional\each; -use function Functional\noop; class NotifyVisitToRabbitMqTest extends TestCase { @@ -77,7 +76,7 @@ class NotifyVisitToRabbitMqTest extends TestCase { $visitId = '123'; $this->em->expects($this->once())->method('find')->with(Visit::class, $visitId)->willReturn($visit); - each($expectedChannels, function (string $method): void { + array_walk($expectedChannels, function (string $method): void { $this->updatesGenerator->expects($this->once())->method($method)->with( $this->isInstanceOf(Visit::class), )->willReturn(Update::forTopicAndPayload('', [])); @@ -153,7 +152,7 @@ class NotifyVisitToRabbitMqTest extends TestCase yield 'legacy non-orphan visit' => [ true, $visit = Visit::forValidShortUrl(ShortUrl::withLongUrl('https://longUrl'), Visitor::emptyInstance()), - noop(...), + static fn () => null, function (MockObject & PublishingHelperInterface $helper) use ($visit): void { $helper->method('publishUpdate')->with(self::callback(function (Update $update) use ($visit): bool { $payload = $update->payload; @@ -170,7 +169,7 @@ class NotifyVisitToRabbitMqTest extends TestCase yield 'legacy orphan visit' => [ true, Visit::forBasePath(Visitor::emptyInstance()), - noop(...), + static fn () => null, function (MockObject & PublishingHelperInterface $helper): void { $helper->method('publishUpdate')->with(self::callback(function (Update $update): bool { $payload = $update->payload; diff --git a/module/Core/test/Importer/ImportedLinksProcessorTest.php b/module/Core/test/Importer/ImportedLinksProcessorTest.php index 5b174053..2cdbf654 100644 --- a/module/Core/test/Importer/ImportedLinksProcessorTest.php +++ b/module/Core/test/Importer/ImportedLinksProcessorTest.php @@ -32,8 +32,8 @@ use stdClass; use Symfony\Component\Console\Style\StyleInterface; use function count; -use function Functional\some; use function Shlinkio\Shlink\Core\contains; +use function Shlinkio\Shlink\Core\some; use function sprintf; use function str_contains; diff --git a/module/Core/test/ShortUrl/Entity/ShortUrlTest.php b/module/Core/test/ShortUrl/Entity/ShortUrlTest.php index c1d66e61..ba6fab58 100644 --- a/module/Core/test/ShortUrl/Entity/ShortUrlTest.php +++ b/module/Core/test/ShortUrl/Entity/ShortUrlTest.php @@ -20,8 +20,8 @@ use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl; use Shlinkio\Shlink\Importer\Sources\ImportSource; use function array_map; -use function Functional\every; use function range; +use function Shlinkio\Shlink\Core\every; use function strlen; use function strtolower; diff --git a/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php b/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php index b43eed91..b05ab7d9 100644 --- a/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php +++ b/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php @@ -16,9 +16,6 @@ use Psr\Http\Server\RequestHandlerInterface; use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Middleware\TrimTrailingSlashMiddleware; -use function Functional\compose; -use function Functional\const_function; - class TrimTrailingSlashMiddlewareTest extends TestCase { private MockObject & RequestHandlerInterface $requestHandler; @@ -34,7 +31,10 @@ class TrimTrailingSlashMiddlewareTest extends TestCase ServerRequestInterface $inputRequest, callable $assertions, ): void { - $arg = compose($assertions, const_function(true)); + $arg = static function (...$args) use ($assertions): bool { + $assertions(...$args); + return true; + }; $this->requestHandler->expects($this->once())->method('handle')->with($this->callback($arg))->willReturn( new Response(), ); diff --git a/module/Rest/src/ConfigProvider.php b/module/Rest/src/ConfigProvider.php index 7c57d8b1..067c6952 100644 --- a/module/Rest/src/ConfigProvider.php +++ b/module/Rest/src/ConfigProvider.php @@ -4,8 +4,9 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Rest; +use function array_filter; use function array_map; -use function Functional\first; +use function reset; use function Shlinkio\Shlink\Config\loadConfigFromGlob; use function sprintf; @@ -34,8 +35,9 @@ class ConfigProvider private static function buildUnversionedHealthRouteFromExistingRoutes(array $routes): ?array { - $healthRoute = first($routes, fn (array $route) => $route['path'] === '/health'); - if ($healthRoute === null) { + $healthRoutes = array_filter($routes, fn (array $route) => $route['path'] === '/health'); + $healthRoute = reset($healthRoutes); + if ($healthRoute === false) { return null; } diff --git a/module/Rest/src/Exception/BackwardsCompatibleProblemDetailsException.php b/module/Rest/src/Exception/BackwardsCompatibleProblemDetailsException.php index 685d3795..8cfb918c 100644 --- a/module/Rest/src/Exception/BackwardsCompatibleProblemDetailsException.php +++ b/module/Rest/src/Exception/BackwardsCompatibleProblemDetailsException.php @@ -15,8 +15,8 @@ use Shlinkio\Shlink\Core\Exception\TagConflictException; use Shlinkio\Shlink\Core\Exception\TagNotFoundException; use Shlinkio\Shlink\Core\Exception\ValidationException; +use function end; use function explode; -use function Functional\last; /** @deprecated */ class BackwardsCompatibleProblemDetailsException extends RuntimeException implements ProblemDetailsExceptionInterface @@ -77,7 +77,9 @@ class BackwardsCompatibleProblemDetailsException extends RuntimeException implem private function remapType(string $wrappedType): string { - $lastSegment = last(explode('/', $wrappedType)); + $segments = explode('/', $wrappedType); + $lastSegment = end($segments); + return match ($lastSegment) { ValidationException::ERROR_CODE => 'INVALID_ARGUMENT', DeleteShortUrlException::ERROR_CODE => 'INVALID_SHORT_URL_DELETION',