Remove last references to functional-php

This commit is contained in:
Alejandro Celaya 2023-11-30 18:09:15 +01:00
parent bff4bd12ae
commit 1854cc2f19
22 changed files with 147 additions and 114 deletions

View File

@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org). The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
## [Unreleased]
### Added
* *Nothing*
### Changed
* Remove dependency on functional-php library
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* *Nothing*
## [3.7.0] - 2023-11-25 ## [3.7.0] - 2023-11-25
### Added ### Added
* [#1798](https://github.com/shlinkio/shlink/issues/1798) Experimental support to send visits to an external Matomo instance. * [#1798](https://github.com/shlinkio/shlink/issues/1798) Experimental support to send visits to an external Matomo instance.

View File

@ -34,7 +34,6 @@
"laminas/laminas-servicemanager": "^3.21", "laminas/laminas-servicemanager": "^3.21",
"laminas/laminas-stdlib": "^3.17", "laminas/laminas-stdlib": "^3.17",
"league/uri": "^6.8", "league/uri": "^6.8",
"lstrojny/functional-php": "^1.17",
"matomo/matomo-php-tracker": "^3.2", "matomo/matomo-php-tracker": "^3.2",
"mezzio/mezzio": "^3.17", "mezzio/mezzio": "^3.17",
"mezzio/mezzio-fastroute": "^3.10", "mezzio/mezzio-fastroute": "^3.10",
@ -91,6 +90,7 @@
}, },
"files": [ "files": [
"config/constants.php", "config/constants.php",
"module/Core/functions/array-utils.php",
"module/Core/functions/functions.php" "module/Core/functions/functions.php"
] ]
}, },

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository; use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
use Shlinkio\Shlink\Core\Config\EnvVars; use Shlinkio\Shlink\Core\Config\EnvVars;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
return (static function (): array { return (static function (): array {
$driver = EnvVars::DB_DRIVER->loadFromEnv(); $driver = EnvVars::DB_DRIVER->loadFromEnv();

View File

@ -30,7 +30,7 @@ use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use function file_exists; use function file_exists;
use function Laminas\Stratigility\middleware; use function Laminas\Stratigility\middleware;
use function Shlinkio\Shlink\Config\env; use function Shlinkio\Shlink\Config\env;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
use function sprintf; use function sprintf;
use function sys_get_temp_dir; use function sys_get_temp_dir;

View File

@ -11,7 +11,7 @@ use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
use function Shlinkio\Shlink\Core\some; use function Shlinkio\Shlink\Core\ArrayUtils\some;
final class Version20200105165647 extends AbstractMigration final class Version20200105165647 extends AbstractMigration
{ {
@ -25,10 +25,9 @@ final class Version20200105165647 extends AbstractMigration
$visitLocations = $schema->getTable('visit_locations'); $visitLocations = $schema->getTable('visit_locations');
$this->skipIf(some( $this->skipIf(some(
self::COLUMNS, self::COLUMNS,
fn (string $v, string $newColName) => $visitLocations->hasColumn($newColName), fn (string $v, string|int $newColName) => $visitLocations->hasColumn((string) $newColName),
), 'New columns already exist'); ), 'New columns already exist');
foreach (self::COLUMNS as $columnName) { foreach (self::COLUMNS as $columnName) {
$qb = $this->connection->createQueryBuilder(); $qb = $this->connection->createQueryBuilder();
$qb->update('visit_locations') $qb->update('visit_locations')

View File

@ -17,7 +17,8 @@ use Symfony\Component\Process\PhpExecutableFinder;
use Throwable; use Throwable;
use function array_map; use function array_map;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
use function Shlinkio\Shlink\Core\ArrayUtils\some;
class CreateDatabaseCommand extends AbstractDatabaseCommand class CreateDatabaseCommand extends AbstractDatabaseCommand
{ {
@ -71,15 +72,9 @@ class CreateDatabaseCommand extends AbstractDatabaseCommand
$allMetadata = $this->em->getMetadataFactory()->getAllMetadata(); $allMetadata = $this->em->getMetadataFactory()->getAllMetadata();
$shlinkTables = array_map(static fn (ClassMetadata $metadata) => $metadata->getTableName(), $allMetadata); $shlinkTables = array_map(static fn (ClassMetadata $metadata) => $metadata->getTableName(), $allMetadata);
foreach ($shlinkTables as $shlinkTable) {
// If at least one of the shlink tables exist, we will consider the database exists somehow. // 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. // Any other inconsistency will be taken care of by the migrations.
if (contains($shlinkTable, $existingTables)) { return some($shlinkTables, static fn (string $shlinkTable) => contains($shlinkTable, $existingTables));
return true;
}
}
return false;
} }
private function ensureDatabaseExistsAndGetTables(): array private function ensureDatabaseExistsAndGetTables(): array

View File

@ -22,7 +22,7 @@ use Symfony\Component\Console\Style\SymfonyStyle;
use function array_map; use function array_map;
use function array_unique; use function array_unique;
use function explode; use function explode;
use function Shlinkio\SHlink\Core\flatten; use function Shlinkio\Shlink\Core\ArrayUtils\flatten;
use function sprintf; use function sprintf;
class CreateShortUrlCommand extends Command class CreateShortUrlCommand extends Command

View File

@ -16,14 +16,11 @@ use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use function array_filter;
use function array_keys; use function array_keys;
use function array_map; use function array_map;
use function Shlinkio\Shlink\Common\buildDateRange; use function Shlinkio\Shlink\Common\buildDateRange;
use function Shlinkio\Shlink\Core\ArrayUtils\select_keys;
use function Shlinkio\Shlink\Core\camelCaseToHumanFriendly; use function Shlinkio\Shlink\Core\camelCaseToHumanFriendly;
use function Shlinkio\Shlink\Core\contains;
use const ARRAY_FILTER_USE_KEY;
abstract class AbstractVisitsListCommand extends Command abstract class AbstractVisitsListCommand extends Command
{ {
@ -64,14 +61,7 @@ abstract class AbstractVisitsListCommand extends Command
]; ];
// Filter out unknown keys // Filter out unknown keys
return array_filter( return select_keys($rowData, ['referer', 'date', 'userAgent', 'country', 'city', ...$extraKeys]);
$rowData,
static fn (string $key) => contains(
$key,
['referer', 'date', 'userAgent', 'country', 'city', ...$extraKeys],
),
ARRAY_FILTER_USE_KEY,
);
}, [...$paginator->getCurrentPageResults()]); }, [...$paginator->getCurrentPageResults()]);
$extra = array_map(camelCaseToHumanFriendly(...), $extraKeys); $extra = array_map(camelCaseToHumanFriendly(...), $extraKeys);

View File

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\ArrayUtils;
use function array_filter;
use function array_reduce;
use function in_array;
use const ARRAY_FILTER_USE_KEY;
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: [],
);
}
/**
* Checks if a callback returns true for at least one item in a collection.
* @param callable(mixed $value, mixed $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;
}
/**
* Returns an array containing only those entries in the array whose key is in the supplied keys.
*/
function select_keys(array $array, array $keys): array
{
return array_filter(
$array,
static fn (string $key) => contains(
$key,
$keys,
),
ARRAY_FILTER_USE_KEY,
);
}

View File

@ -20,7 +20,6 @@ use function array_keys;
use function array_map; use function array_map;
use function array_reduce; use function array_reduce;
use function date_default_timezone_get; use function date_default_timezone_get;
use function in_array;
use function is_array; use function is_array;
use function print_r; use function print_r;
use function Shlinkio\Shlink\Common\buildDateRange; use function Shlinkio\Shlink\Common\buildDateRange;
@ -183,51 +182,3 @@ function enumValues(string $enum): array
$cache[$enum] = array_map(static fn (BackedEnum $type) => (string) $type->value, $enum::cases()) $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: [],
);
}
/**
* 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;
}

View File

@ -18,7 +18,7 @@ use Endroid\QrCode\Writer\WriterInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Core\Options\QrCodeOptions; use Shlinkio\Shlink\Core\Options\QrCodeOptions;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
use function strtolower; use function strtolower;
use function trim; use function trim;

View File

@ -12,7 +12,6 @@ use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType;
use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface; use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface;
use function Functional\compose;
use function str_replace; use function str_replace;
use function urlencode; use function urlencode;
@ -51,9 +50,6 @@ class NotFoundRedirectResolver implements NotFoundRedirectResolverInterface
private function resolvePlaceholders(UriInterface $currentUri, string $redirectUrl): string private function resolvePlaceholders(UriInterface $currentUri, string $redirectUrl): string
{ {
$domain = $currentUri->getAuthority();
$path = $currentUri->getPath();
try { try {
$redirectUri = Uri::createFromString($redirectUrl); $redirectUri = Uri::createFromString($redirectUrl);
} catch (SyntaxError $e) { } catch (SyntaxError $e) {
@ -64,18 +60,32 @@ class NotFoundRedirectResolver implements NotFoundRedirectResolverInterface
return $redirectUrl; return $redirectUrl;
} }
$replacePlaceholderForPattern = static fn (string $pattern, string $replace, callable $modifier) => $path = $currentUri->getPath();
static fn (?string $value) => $domain = $currentUri->getAuthority();
$value === null ? null : str_replace($modifier($pattern), $modifier($replace), $value);
$replacePlaceholders = static fn (callable $modifier) => compose( $replacePlaceholderForPattern = static fn (string $pattern, string $replace, ?string $value): string|null =>
$replacePlaceholderForPattern(self::DOMAIN_PLACEHOLDER, $domain, $modifier), $value === null ? null : str_replace($pattern, $replace, $value);
$replacePlaceholderForPattern(self::ORIGINAL_PATH_PLACEHOLDER, $path, $modifier),
$replacePlaceholders = static function (
callable $modifier,
?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);
};
$replacePlaceholdersInPath = static function (string $path) use ($replacePlaceholders): string {
$result = $replacePlaceholders(static fn (mixed $v) => $v, $path);
return str_replace('//', '/', $result ?? '');
};
$replacePlaceholdersInQuery = static fn (?string $query): string|null => $replacePlaceholders(
urlencode(...),
$query,
); );
$replacePlaceholdersInPath = compose(
$replacePlaceholders(static fn (mixed $v) => $v),
static fn (?string $path) => $path === null ? null : str_replace('//', '/', $path),
);
$replacePlaceholdersInQuery = $replacePlaceholders(urlencode(...));
return $redirectUri return $redirectUri
->withPath($replacePlaceholdersInPath($redirectUri->getPath())) ->withPath($replacePlaceholdersInPath($redirectUri->getPath()))

View File

@ -2,7 +2,7 @@
namespace Shlinkio\Shlink\Core\ShortUrl\Model; namespace Shlinkio\Shlink\Core\ShortUrl\Model;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
enum OrderableField: string enum OrderableField: string
{ {

View File

@ -11,9 +11,9 @@ use Shlinkio\Shlink\Core\Model\DeviceType;
use function array_keys; use function array_keys;
use function array_values; use function array_values;
use function is_array; use function is_array;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
use function Shlinkio\Shlink\Core\ArrayUtils\every;
use function Shlinkio\Shlink\Core\enumValues; use function Shlinkio\Shlink\Core\enumValues;
use function Shlinkio\Shlink\Core\every;
class DeviceLongUrlsValidator extends AbstractValidator class DeviceLongUrlsValidator extends AbstractValidator
{ {

View File

@ -2,7 +2,7 @@
namespace Shlinkio\Shlink\Core\Util; namespace Shlinkio\Shlink\Core\Util;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
enum RedirectStatus: int enum RedirectStatus: int
{ {

View File

@ -20,6 +20,7 @@ use function array_keys;
use function array_map; use function array_map;
use function explode; use function explode;
use function implode; use function implode;
use function Shlinkio\Shlink\Core\ArrayUtils\some;
use function str_contains; use function str_contains;
class RequestTracker implements RequestTrackerInterface, RequestMethodInterface class RequestTracker implements RequestTrackerInterface, RequestMethodInterface
@ -85,17 +86,13 @@ class RequestTracker implements RequestTrackerInterface, RequestMethodInterface
$remoteAddrParts = explode('.', $remoteAddr); $remoteAddrParts = explode('.', $remoteAddr);
$disableTrackingFrom = $this->trackingOptions->disableTrackingFrom; $disableTrackingFrom = $this->trackingOptions->disableTrackingFrom;
foreach ($disableTrackingFrom as $value) { return some($disableTrackingFrom, function (string $value) use ($ip, $remoteAddrParts): bool {
$range = str_contains($value, '*') $range = str_contains($value, '*')
? $this->parseValueWithWildcards($value, $remoteAddrParts) ? $this->parseValueWithWildcards($value, $remoteAddrParts)
: Factory::parseRangeString($value); : Factory::parseRangeString($value);
if ($range !== null && $ip->matches($range)) { return $range !== null && $ip->matches($range);
return true; });
}
}
return false;
} }
private function parseValueWithWildcards(string $value, array $remoteAddrParts): ?RangeInterface private function parseValueWithWildcards(string $value, array $remoteAddrParts): ?RangeInterface

View File

@ -28,7 +28,7 @@ use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\Visitor; use Shlinkio\Shlink\Core\Visit\Model\Visitor;
use function count; use function count;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
class NotifyVisitToWebHooksTest extends TestCase class NotifyVisitToWebHooksTest extends TestCase
{ {

View File

@ -32,8 +32,8 @@ use stdClass;
use Symfony\Component\Console\Style\StyleInterface; use Symfony\Component\Console\Style\StyleInterface;
use function count; use function count;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
use function Shlinkio\Shlink\Core\some; use function Shlinkio\Shlink\Core\ArrayUtils\some;
use function sprintf; use function sprintf;
use function str_contains; use function str_contains;

View File

@ -21,7 +21,7 @@ use Shlinkio\Shlink\Importer\Sources\ImportSource;
use function array_map; use function array_map;
use function range; use function range;
use function Shlinkio\Shlink\Core\every; use function Shlinkio\Shlink\Core\ArrayUtils\every;
use function strlen; use function strlen;
use function strtolower; use function strtolower;

View File

@ -17,7 +17,7 @@ use Shlinkio\Shlink\Rest\Exception\MissingAuthenticationException;
use Shlinkio\Shlink\Rest\Exception\VerifyAuthenticationException; use Shlinkio\Shlink\Rest\Exception\VerifyAuthenticationException;
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface; use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
class AuthenticationMiddleware implements MiddlewareInterface, StatusCodeInterface, RequestMethodInterface class AuthenticationMiddleware implements MiddlewareInterface, StatusCodeInterface, RequestMethodInterface
{ {

View File

@ -12,7 +12,7 @@ use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Core\Exception\MalformedBodyException; use Shlinkio\Shlink\Core\Exception\MalformedBodyException;
use function Shlinkio\Shlink\Core\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
use function Shlinkio\Shlink\Json\json_decode; use function Shlinkio\Shlink\Json\json_decode;
class BodyParserMiddleware implements MiddlewareInterface, RequestMethodInterface class BodyParserMiddleware implements MiddlewareInterface, RequestMethodInterface