From 549c6605f0c2ebab0573f632df6d553a184ac342 Mon Sep 17 00:00:00 2001
From: Alejandro Celaya <alejandrocelaya@gmail.com>
Date: Thu, 30 Nov 2023 09:13:29 +0100
Subject: [PATCH] Replaced usage of Functional\contians

---
 composer.json                                 |  8 +++---
 config/autoload/entity-manager.global.php     |  4 +--
 config/test/test_config.global.php            |  4 +--
 .../src/Command/Db/CreateDatabaseCommand.php  | 15 +++++++----
 .../ShortUrl/CreateShortUrlCommand.php        |  9 +++----
 .../Visit/AbstractVisitsListCommand.php       |  5 ++--
 module/Core/functions/functions.php           | 25 ++++++++++++++++---
 module/Core/src/Action/Model/QrCodeParams.php |  4 +--
 .../src/ShortUrl/Model/OrderableField.php     |  4 +--
 .../Validation/DeviceLongUrlsValidator.php    |  4 +--
 module/Core/src/Util/RedirectStatus.php       |  6 ++---
 .../NotifyVisitToWebHooksTest.php             |  4 +--
 .../Importer/ImportedLinksProcessorTest.php   |  4 +--
 .../Middleware/AuthenticationMiddleware.php   | 12 ++++-----
 .../src/Middleware/BodyParserMiddleware.php   |  6 ++---
 15 files changed, 68 insertions(+), 46 deletions(-)

diff --git a/composer.json b/composer.json
index 0e1b996e..8071556e 100644
--- a/composer.json
+++ b/composer.json
@@ -46,12 +46,12 @@
         "php-middleware/request-id": "^4.1",
         "pugx/shortid-php": "^1.1",
         "ramsey/uuid": "^4.7",
-        "shlinkio/shlink-common": "^5.7",
+        "shlinkio/shlink-common": "dev-main#1f1b3b8 as 5.8",
         "shlinkio/shlink-config": "^2.5",
         "shlinkio/shlink-event-dispatcher": "^3.1",
-        "shlinkio/shlink-importer": "^5.2",
-        "shlinkio/shlink-installer": "^8.6",
-        "shlinkio/shlink-ip-geolocation": "^3.3",
+        "shlinkio/shlink-importer": "dev-main#4616c54 as 5.3",
+        "shlinkio/shlink-installer": "dev-develop#cb0eaea as 8.7",
+        "shlinkio/shlink-ip-geolocation": "dev-main#ea88ae8 as 3.4",
         "shlinkio/shlink-json": "^1.1",
         "spiral/roadrunner": "^2023.2",
         "spiral/roadrunner-cli": "^2.5",
diff --git a/config/autoload/entity-manager.global.php b/config/autoload/entity-manager.global.php
index 58899217..44095656 100644
--- a/config/autoload/entity-manager.global.php
+++ b/config/autoload/entity-manager.global.php
@@ -5,11 +5,11 @@ declare(strict_types=1);
 use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
 use Shlinkio\Shlink\Core\Config\EnvVars;
 
-use function Functional\contains;
+use function Shlinkio\Shlink\Core\contains;
 
 return (static function (): array {
     $driver = EnvVars::DB_DRIVER->loadFromEnv();
-    $isMysqlCompatible = contains(['maria', 'mysql'], $driver);
+    $isMysqlCompatible = contains($driver, ['maria', 'mysql']);
 
     $resolveDriver = static fn () => match ($driver) {
         'postgres' => 'pdo_pgsql',
diff --git a/config/test/test_config.global.php b/config/test/test_config.global.php
index 1beed0e3..75937bec 100644
--- a/config/test/test_config.global.php
+++ b/config/test/test_config.global.php
@@ -28,9 +28,9 @@ use Symfony\Component\Console\Event\ConsoleTerminateEvent;
 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
 
 use function file_exists;
-use function Functional\contains;
 use function Laminas\Stratigility\middleware;
 use function Shlinkio\Shlink\Config\env;
+use function Shlinkio\Shlink\Core\contains;
 use function sprintf;
 use function sys_get_temp_dir;
 
@@ -41,7 +41,7 @@ $isApiTest = env('TEST_ENV') === 'api';
 $isCliTest = env('TEST_ENV') === 'cli';
 $isE2eTest = $isApiTest || $isCliTest;
 $coverageType = env('GENERATE_COVERAGE');
-$generateCoverage = contains(['yes', 'pretty'], $coverageType);
+$generateCoverage = contains($coverageType, ['yes', 'pretty']);
 
 $coverage = null;
 if ($isE2eTest && $generateCoverage) {
diff --git a/module/CLI/src/Command/Db/CreateDatabaseCommand.php b/module/CLI/src/Command/Db/CreateDatabaseCommand.php
index c70e2f76..b9bb8f10 100644
--- a/module/CLI/src/Command/Db/CreateDatabaseCommand.php
+++ b/module/CLI/src/Command/Db/CreateDatabaseCommand.php
@@ -17,8 +17,7 @@ use Symfony\Component\Process\PhpExecutableFinder;
 use Throwable;
 
 use function array_map;
-use function Functional\contains;
-use function Functional\some;
+use function Shlinkio\Shlink\Core\contains;
 
 class CreateDatabaseCommand extends AbstractDatabaseCommand
 {
@@ -72,9 +71,15 @@ class CreateDatabaseCommand extends AbstractDatabaseCommand
         $allMetadata = $this->em->getMetadataFactory()->getAllMetadata();
         $shlinkTables = array_map(static fn (ClassMetadata $metadata) => $metadata->getTableName(), $allMetadata);
 
-        // If at least one of the shlink tables exist, we will consider the database exists somehow.
-        // Any other inconsistency will be taken care of by the migrations.
-        return some($shlinkTables, static fn (string $shlinkTable) => contains($existingTables, $shlinkTable));
+        foreach ($shlinkTables as $shlinkTable) {
+            // If at least one of the shlink tables exist, we will consider the database exists somehow.
+            // Any other inconsistency will be taken care of by the migrations.
+            if (contains($shlinkTable, $existingTables)) {
+                return true;
+            }
+        }
+
+        return false;
     }
 
     private function ensureDatabaseExistsAndGetTables(): array
diff --git a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php
index f55f247d..3277f763 100644
--- a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php
+++ b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php
@@ -20,10 +20,9 @@ use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Style\SymfonyStyle;
 
 use function array_map;
+use function array_unique;
 use function explode;
-use function Functional\curry;
-use function Functional\flatten;
-use function Functional\unique;
+use function Shlinkio\SHlink\Core\flatten;
 use function sprintf;
 
 class CreateShortUrlCommand extends Command
@@ -144,8 +143,8 @@ class CreateShortUrlCommand extends Command
             return ExitCode::EXIT_FAILURE;
         }
 
-        $explodeWithComma = curry(explode(...))(',');
-        $tags = unique(flatten(array_map($explodeWithComma, $input->getOption('tags'))));
+        $explodeWithComma = static fn (string $tag) => explode(',', $tag);
+        $tags = array_unique(flatten(array_map($explodeWithComma, $input->getOption('tags'))));
         $customSlug = $input->getOption('custom-slug');
         $maxVisits = $input->getOption('max-visits');
         $shortCodeLength = $input->getOption('short-code-length') ?? $this->options->defaultShortCodesLength;
diff --git a/module/CLI/src/Command/Visit/AbstractVisitsListCommand.php b/module/CLI/src/Command/Visit/AbstractVisitsListCommand.php
index a247380e..8766ecc5 100644
--- a/module/CLI/src/Command/Visit/AbstractVisitsListCommand.php
+++ b/module/CLI/src/Command/Visit/AbstractVisitsListCommand.php
@@ -19,9 +19,9 @@ use Symfony\Component\Console\Output\OutputInterface;
 use function array_filter;
 use function array_keys;
 use function array_map;
-use function in_array;
 use function Shlinkio\Shlink\Common\buildDateRange;
 use function Shlinkio\Shlink\Core\camelCaseToHumanFriendly;
+use function Shlinkio\Shlink\Core\contains;
 
 use const ARRAY_FILTER_USE_KEY;
 
@@ -66,10 +66,9 @@ abstract class AbstractVisitsListCommand extends Command
             // Filter out unknown keys
             return array_filter(
                 $rowData,
-                static fn (string $key) => in_array(
+                static fn (string $key) => contains(
                     $key,
                     ['referer', 'date', 'userAgent', 'country', 'city', ...$extraKeys],
-                    strict: true,
                 ),
                 ARRAY_FILTER_USE_KEY,
             );
diff --git a/module/Core/functions/functions.php b/module/Core/functions/functions.php
index 32d357e3..bcda4bb4 100644
--- a/module/Core/functions/functions.php
+++ b/module/Core/functions/functions.php
@@ -6,7 +6,6 @@ namespace Shlinkio\Shlink\Core;
 
 use BackedEnum;
 use Cake\Chronos\Chronos;
-use Cake\Chronos\ChronosInterface;
 use DateTimeInterface;
 use Doctrine\ORM\Mapping\Builder\FieldBuilder;
 use Jaybizzle\CrawlerDetect\CrawlerDetect;
@@ -18,8 +17,10 @@ use Shlinkio\Shlink\Common\Util\DateRange;
 use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode;
 
 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;
 use function Shlinkio\Shlink\Common\buildDateRange;
@@ -57,7 +58,7 @@ function parseDateRangeFromQuery(array $query, string $startDateName, string $en
 /**
  * @return ($date is null ? null : Chronos)
  */
-function normalizeOptionalDate(string|DateTimeInterface|ChronosInterface|null $date): ?Chronos
+function normalizeOptionalDate(string|DateTimeInterface|Chronos|null $date): ?Chronos
 {
     $parsedDate = match (true) {
         $date === null || $date instanceof Chronos => $date,
@@ -68,7 +69,7 @@ function normalizeOptionalDate(string|DateTimeInterface|ChronosInterface|null $d
     return $parsedDate?->setTimezone(date_default_timezone_get());
 }
 
-function normalizeDate(string|DateTimeInterface|ChronosInterface $date): Chronos
+function normalizeDate(string|DateTimeInterface|Chronos $date): Chronos
 {
     return normalizeOptionalDate($date);
 }
@@ -180,3 +181,21 @@ function enumValues(string $enum): array
         $cache[$enum] = array_map(static fn (BackedEnum $type) => (string) $type->value, $enum::cases())
     );
 }
+
+function contains(mixed $value, array $array): bool
+{
+    return in_array($value, $array, strict: true);
+}
+
+/**
+ * @param array[] $multiArray
+ * @return array
+ */
+function flatten(array $multiArray): array
+{
+    return array_reduce(
+        $multiArray,
+        static fn (array $carry, array $value) => [...$carry, ...$value],
+        initial: [],
+    );
+}
diff --git a/module/Core/src/Action/Model/QrCodeParams.php b/module/Core/src/Action/Model/QrCodeParams.php
index 306c2b44..51162d5f 100644
--- a/module/Core/src/Action/Model/QrCodeParams.php
+++ b/module/Core/src/Action/Model/QrCodeParams.php
@@ -18,7 +18,7 @@ use Endroid\QrCode\Writer\WriterInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Shlinkio\Shlink\Core\Options\QrCodeOptions;
 
-use function Functional\contains;
+use function Shlinkio\Shlink\Core\contains;
 use function strtolower;
 use function trim;
 
@@ -74,7 +74,7 @@ final class QrCodeParams
     private static function resolveWriter(array $query, QrCodeOptions $defaults): WriterInterface
     {
         $qFormat = self::normalizeParam($query['format'] ?? '');
-        $format = contains(self::SUPPORTED_FORMATS, $qFormat) ? $qFormat : self::normalizeParam($defaults->format);
+        $format = contains($qFormat, self::SUPPORTED_FORMATS) ? $qFormat : self::normalizeParam($defaults->format);
 
         return match ($format) {
             'svg' => new SvgWriter(),
diff --git a/module/Core/src/ShortUrl/Model/OrderableField.php b/module/Core/src/ShortUrl/Model/OrderableField.php
index ac1bc632..1b61a155 100644
--- a/module/Core/src/ShortUrl/Model/OrderableField.php
+++ b/module/Core/src/ShortUrl/Model/OrderableField.php
@@ -2,7 +2,7 @@
 
 namespace Shlinkio\Shlink\Core\ShortUrl\Model;
 
-use function Functional\contains;
+use function Shlinkio\Shlink\Core\contains;
 
 enum OrderableField: string
 {
@@ -16,8 +16,8 @@ enum OrderableField: string
     public static function isBasicField(string $value): bool
     {
         return contains(
-            [self::LONG_URL->value, self::SHORT_CODE->value, self::DATE_CREATED->value, self::TITLE->value],
             $value,
+            [self::LONG_URL->value, self::SHORT_CODE->value, self::DATE_CREATED->value, self::TITLE->value],
         );
     }
 
diff --git a/module/Core/src/ShortUrl/Model/Validation/DeviceLongUrlsValidator.php b/module/Core/src/ShortUrl/Model/Validation/DeviceLongUrlsValidator.php
index 9fda1809..5694f6e1 100644
--- a/module/Core/src/ShortUrl/Model/Validation/DeviceLongUrlsValidator.php
+++ b/module/Core/src/ShortUrl/Model/Validation/DeviceLongUrlsValidator.php
@@ -10,9 +10,9 @@ use Shlinkio\Shlink\Core\Model\DeviceType;
 
 use function array_keys;
 use function array_values;
-use function Functional\contains;
 use function Functional\every;
 use function is_array;
+use function Shlinkio\Shlink\Core\contains;
 use function Shlinkio\Shlink\Core\enumValues;
 
 class DeviceLongUrlsValidator extends AbstractValidator
@@ -41,7 +41,7 @@ class DeviceLongUrlsValidator extends AbstractValidator
 
         $validValues = enumValues(DeviceType::class);
         $keys = array_keys($value);
-        if (! every($keys, static fn ($key) => contains($validValues, $key))) {
+        if (! every($keys, static fn ($key) => contains($key, $validValues))) {
             $this->error(self::INVALID_DEVICE);
             return false;
         }
diff --git a/module/Core/src/Util/RedirectStatus.php b/module/Core/src/Util/RedirectStatus.php
index 76c047f4..313dc432 100644
--- a/module/Core/src/Util/RedirectStatus.php
+++ b/module/Core/src/Util/RedirectStatus.php
@@ -2,7 +2,7 @@
 
 namespace Shlinkio\Shlink\Core\Util;
 
-use function Functional\contains;
+use function Shlinkio\Shlink\Core\contains;
 
 enum RedirectStatus: int
 {
@@ -13,11 +13,11 @@ enum RedirectStatus: int
 
     public function allowsCache(): bool
     {
-        return contains([self::STATUS_301, self::STATUS_308], $this);
+        return contains($this, [self::STATUS_301, self::STATUS_308]);
     }
 
     public function isLegacyStatus(): bool
     {
-        return contains([self::STATUS_301, self::STATUS_302], $this);
+        return contains($this, [self::STATUS_301, self::STATUS_302]);
     }
 }
diff --git a/module/Core/test/EventDispatcher/NotifyVisitToWebHooksTest.php b/module/Core/test/EventDispatcher/NotifyVisitToWebHooksTest.php
index f85a9d44..c4ca402a 100644
--- a/module/Core/test/EventDispatcher/NotifyVisitToWebHooksTest.php
+++ b/module/Core/test/EventDispatcher/NotifyVisitToWebHooksTest.php
@@ -28,7 +28,7 @@ use Shlinkio\Shlink\Core\Visit\Entity\Visit;
 use Shlinkio\Shlink\Core\Visit\Model\Visitor;
 
 use function count;
-use function Functional\contains;
+use function Shlinkio\Shlink\Core\contains;
 
 class NotifyVisitToWebHooksTest extends TestCase
 {
@@ -102,7 +102,7 @@ class NotifyVisitToWebHooksTest extends TestCase
                 return true;
             }),
         )->willReturnCallback(function ($_, $webhook) use ($invalidWebhooks) {
-            $shouldReject = contains($invalidWebhooks, $webhook);
+            $shouldReject = contains($webhook, $invalidWebhooks);
             return $shouldReject ? new RejectedPromise(new Exception('')) : new FulfilledPromise('');
         });
         $this->logger->expects($this->exactly(count($invalidWebhooks)))->method('warning')->with(
diff --git a/module/Core/test/Importer/ImportedLinksProcessorTest.php b/module/Core/test/Importer/ImportedLinksProcessorTest.php
index bf2896e2..5b174053 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\contains;
 use function Functional\some;
+use function Shlinkio\Shlink\Core\contains;
 use function sprintf;
 use function str_contains;
 
@@ -128,8 +128,8 @@ class ImportedLinksProcessorTest extends TestCase
         $this->em->method('getRepository')->with(ShortUrl::class)->willReturn($this->repo);
         $this->repo->expects($this->exactly(count($urls)))->method('findOneByImportedUrl')->willReturnCallback(
             fn (ImportedShlinkUrl $url): ?ShortUrl => contains(
-                ['https://foo', 'https://baz2', 'https://baz3'],
                 $url->longUrl,
+                ['https://foo', 'https://baz2', 'https://baz3'],
             ) ? ShortUrl::fromImport($url, true) : null,
         );
         $this->shortCodeHelper->expects($this->exactly(2))->method('ensureShortCodeUniqueness')->willReturn(true);
diff --git a/module/Rest/src/Middleware/AuthenticationMiddleware.php b/module/Rest/src/Middleware/AuthenticationMiddleware.php
index 7b911817..85ec61b7 100644
--- a/module/Rest/src/Middleware/AuthenticationMiddleware.php
+++ b/module/Rest/src/Middleware/AuthenticationMiddleware.php
@@ -17,16 +17,16 @@ use Shlinkio\Shlink\Rest\Exception\MissingAuthenticationException;
 use Shlinkio\Shlink\Rest\Exception\VerifyAuthenticationException;
 use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
 
-use function Functional\contains;
+use function Shlinkio\Shlink\Core\contains;
 
 class AuthenticationMiddleware implements MiddlewareInterface, StatusCodeInterface, RequestMethodInterface
 {
     public const API_KEY_HEADER = 'X-Api-Key';
 
     public function __construct(
-        private ApiKeyServiceInterface $apiKeyService,
-        private array $routesWithoutApiKey,
-        private array $routesWithQueryApiKey,
+        private readonly ApiKeyServiceInterface $apiKeyService,
+        private readonly array $routesWithoutApiKey,
+        private readonly array $routesWithQueryApiKey,
     ) {
     }
 
@@ -38,7 +38,7 @@ class AuthenticationMiddleware implements MiddlewareInterface, StatusCodeInterfa
             $routeResult === null
             || $routeResult->isFailure()
             || $request->getMethod() === self::METHOD_OPTIONS
-            || contains($this->routesWithoutApiKey, $routeResult->getMatchedRouteName())
+            || contains($routeResult->getMatchedRouteName(), $this->routesWithoutApiKey)
         ) {
             return $handler->handle($request);
         }
@@ -61,7 +61,7 @@ class AuthenticationMiddleware implements MiddlewareInterface, StatusCodeInterfa
     {
         $routeName = $routeResult->getMatchedRouteName();
         $query = $request->getQueryParams();
-        $isRouteWithApiKeyInQuery = contains($this->routesWithQueryApiKey, $routeName);
+        $isRouteWithApiKeyInQuery = contains($routeName, $this->routesWithQueryApiKey);
         $apiKey = $isRouteWithApiKeyInQuery ? ($query['apiKey'] ?? '') : $request->getHeaderLine(self::API_KEY_HEADER);
 
         if (empty($apiKey)) {
diff --git a/module/Rest/src/Middleware/BodyParserMiddleware.php b/module/Rest/src/Middleware/BodyParserMiddleware.php
index c31bc268..b0548f97 100644
--- a/module/Rest/src/Middleware/BodyParserMiddleware.php
+++ b/module/Rest/src/Middleware/BodyParserMiddleware.php
@@ -12,7 +12,7 @@ use Psr\Http\Server\MiddlewareInterface;
 use Psr\Http\Server\RequestHandlerInterface;
 use Shlinkio\Shlink\Core\Exception\MalformedBodyException;
 
-use function Functional\contains;
+use function Shlinkio\Shlink\Core\contains;
 use function Shlinkio\Shlink\Json\json_decode;
 
 class BodyParserMiddleware implements MiddlewareInterface, RequestMethodInterface
@@ -25,11 +25,11 @@ class BodyParserMiddleware implements MiddlewareInterface, RequestMethodInterfac
         // In requests that do not allow body or if the body has already been parsed, continue to next middleware
         if (
             ! empty($currentParams)
-            || contains([
+            || contains($method, [
                 self::METHOD_GET,
                 self::METHOD_HEAD,
                 self::METHOD_OPTIONS,
-            ], $method)
+            ])
         ) {
             return $handler->handle($request);
         }