mirror of
https://github.com/shlinkio/shlink.git
synced 2024-12-22 23:23:42 -06:00
commit
fa7969c746
19
CHANGELOG.md
19
CHANGELOG.md
@ -4,6 +4,25 @@ 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).
|
||||
|
||||
## [4.2.2] - 2024-10-14
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* [#2208](https://github.com/shlinkio/shlink/issues/2208) Explicitly promote installer config options as env vars, instead of as a side effect of loading the app config.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2213](https://github.com/shlinkio/shlink/issues/2213) Fix spaces being replaced with underscores in query parameter names, when forwarded from short URL to long URL.
|
||||
* [#2217](https://github.com/shlinkio/shlink/issues/2217) Fix docker image tag suffix being leaked to the version set inside Shlink, producing invalid SemVer version patterns.
|
||||
* [#2212](https://github.com/shlinkio/shlink/issues/2212) Fix env vars read in docker entry point not properly falling back to their `_FILE` suffixed counterpart.
|
||||
|
||||
|
||||
## [4.2.1] - 2024-10-04
|
||||
### Added
|
||||
* [#2183](https://github.com/shlinkio/shlink/issues/2183) Redis database index to be used can now be specified in the connection URI path, and Shlink will honor it.
|
||||
|
@ -45,7 +45,7 @@
|
||||
"ramsey/uuid": "^4.7",
|
||||
"shlinkio/doctrine-specification": "^2.1.1",
|
||||
"shlinkio/shlink-common": "^6.3",
|
||||
"shlinkio/shlink-config": "^3.0",
|
||||
"shlinkio/shlink-config": "^3.2",
|
||||
"shlinkio/shlink-event-dispatcher": "^4.1",
|
||||
"shlinkio/shlink-importer": "^5.3.2",
|
||||
"shlinkio/shlink-installer": "^9.2",
|
||||
|
@ -6,7 +6,7 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return (static function (): array {
|
||||
$redisServers = EnvVars::REDIS_SERVERS->loadFromEnv();
|
||||
$redis = ['pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv(false)];
|
||||
$redis = ['pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv()];
|
||||
$cacheRedisBlock = $redisServers === null ? [] : [
|
||||
'redis' => [
|
||||
'servers' => $redisServers,
|
||||
@ -16,7 +16,7 @@ return (static function (): array {
|
||||
|
||||
return [
|
||||
'cache' => [
|
||||
'namespace' => EnvVars::CACHE_NAMESPACE->loadFromEnv('Shlink'),
|
||||
'namespace' => EnvVars::CACHE_NAMESPACE->loadFromEnv(),
|
||||
...$cacheRedisBlock,
|
||||
],
|
||||
'redis' => $redis,
|
||||
|
@ -23,11 +23,6 @@ return (static function (): array {
|
||||
$value = $envVar->loadFromEnv();
|
||||
return $value === null ? null : (string) $value;
|
||||
};
|
||||
$resolveDefaultPort = static fn () => match ($driver) {
|
||||
'postgres' => '5432',
|
||||
'mssql' => '1433',
|
||||
default => '3306',
|
||||
};
|
||||
$resolveCharset = static fn () => match ($driver) {
|
||||
// This does not determine charsets or collations in tables or columns, but the charset used in the data
|
||||
// flowing in the connection, so it has to match what has been set in the database.
|
||||
@ -43,11 +38,11 @@ return (static function (): array {
|
||||
],
|
||||
default => [
|
||||
'driver' => $resolveDriver(),
|
||||
'dbname' => EnvVars::DB_NAME->loadFromEnv('shlink'),
|
||||
'dbname' => EnvVars::DB_NAME->loadFromEnv(),
|
||||
'user' => $readCredentialAsString(EnvVars::DB_USER),
|
||||
'password' => $readCredentialAsString(EnvVars::DB_PASSWORD),
|
||||
'host' => EnvVars::DB_HOST->loadFromEnv(EnvVars::DB_UNIX_SOCKET->loadFromEnv()),
|
||||
'port' => EnvVars::DB_PORT->loadFromEnv($resolveDefaultPort()),
|
||||
'host' => EnvVars::DB_HOST->loadFromEnv(),
|
||||
'port' => EnvVars::DB_PORT->loadFromEnv(),
|
||||
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null,
|
||||
'charset' => $resolveCharset(),
|
||||
'driverOptions' => $driver !== 'mssql' ? [] : [
|
||||
|
@ -7,7 +7,7 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
return [
|
||||
|
||||
'matomo' => [
|
||||
'enabled' => (bool) EnvVars::MATOMO_ENABLED->loadFromEnv(false),
|
||||
'enabled' => (bool) EnvVars::MATOMO_ENABLED->loadFromEnv(),
|
||||
'base_url' => EnvVars::MATOMO_BASE_URL->loadFromEnv(),
|
||||
'site_id' => EnvVars::MATOMO_SITE_ID->loadFromEnv(),
|
||||
'api_token' => EnvVars::MATOMO_API_TOKEN->loadFromEnv(),
|
||||
|
@ -15,7 +15,7 @@ return (static function (): array {
|
||||
|
||||
'mercure' => [
|
||||
'public_hub_url' => $publicUrl,
|
||||
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv($publicUrl),
|
||||
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv(),
|
||||
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
|
||||
'jwt_issuer' => 'Shlink',
|
||||
],
|
||||
|
@ -4,32 +4,17 @@ declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_BG_COLOR;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_COLOR;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ERROR_CORRECTION;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_FORMAT;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_MARGIN;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ROUND_BLOCK_SIZE;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_SIZE;
|
||||
|
||||
return [
|
||||
|
||||
'qr_codes' => [
|
||||
'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(DEFAULT_QR_CODE_SIZE),
|
||||
'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(DEFAULT_QR_CODE_MARGIN),
|
||||
'format' => EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(DEFAULT_QR_CODE_FORMAT),
|
||||
'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ERROR_CORRECTION,
|
||||
),
|
||||
'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ROUND_BLOCK_SIZE,
|
||||
),
|
||||
'enabled_for_disabled_short_urls' => (bool) EnvVars::QR_CODE_FOR_DISABLED_SHORT_URLS->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS,
|
||||
),
|
||||
'color' => EnvVars::DEFAULT_QR_CODE_COLOR->loadFromEnv(DEFAULT_QR_CODE_COLOR),
|
||||
'bg_color' => EnvVars::DEFAULT_QR_CODE_BG_COLOR->loadFromEnv(DEFAULT_QR_CODE_BG_COLOR),
|
||||
'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(),
|
||||
'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(),
|
||||
'format' => EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(),
|
||||
'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(),
|
||||
'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(),
|
||||
'enabled_for_disabled_short_urls' => (bool) EnvVars::QR_CODE_FOR_DISABLED_SHORT_URLS->loadFromEnv(),
|
||||
'color' => EnvVars::DEFAULT_QR_CODE_COLOR->loadFromEnv(),
|
||||
'bg_color' => EnvVars::DEFAULT_QR_CODE_BG_COLOR->loadFromEnv(),
|
||||
'logo_url' => EnvVars::DEFAULT_QR_CODE_LOGO_URL->loadFromEnv(),
|
||||
],
|
||||
|
||||
|
@ -7,13 +7,13 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
return [
|
||||
|
||||
'rabbitmq' => [
|
||||
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(false),
|
||||
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(),
|
||||
'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(),
|
||||
'use_ssl' => (bool) EnvVars::RABBITMQ_USE_SSL->loadFromEnv(false),
|
||||
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv('5672'),
|
||||
'use_ssl' => (bool) EnvVars::RABBITMQ_USE_SSL->loadFromEnv(),
|
||||
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv(),
|
||||
'user' => EnvVars::RABBITMQ_USER->loadFromEnv(),
|
||||
'password' => EnvVars::RABBITMQ_PASSWORD->loadFromEnv(),
|
||||
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv('/'),
|
||||
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv(),
|
||||
],
|
||||
|
||||
];
|
||||
|
@ -7,7 +7,7 @@ return [
|
||||
'rabbitmq' => [
|
||||
'enabled' => true,
|
||||
'host' => 'shlink_rabbitmq',
|
||||
'port' => '5672',
|
||||
'port' => 5672,
|
||||
'user' => 'rabbit',
|
||||
'password' => 'rabbit',
|
||||
],
|
||||
|
@ -4,9 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_REDIRECT_CACHE_LIFETIME;
|
||||
use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE;
|
||||
|
||||
return [
|
||||
|
||||
'not_found_redirects' => [
|
||||
@ -16,10 +13,8 @@ return [
|
||||
],
|
||||
|
||||
'redirects' => [
|
||||
'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(DEFAULT_REDIRECT_STATUS_CODE->value),
|
||||
'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(
|
||||
DEFAULT_REDIRECT_CACHE_LIFETIME,
|
||||
),
|
||||
'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(),
|
||||
'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(),
|
||||
],
|
||||
|
||||
];
|
||||
|
@ -7,7 +7,7 @@ namespace Shlinkio\Shlink\Core;
|
||||
return [
|
||||
|
||||
'robots' => [
|
||||
'allow-all-short-urls' => (bool) Config\EnvVars::ROBOTS_ALLOW_ALL_SHORT_URLS->loadFromEnv(false),
|
||||
'allow-all-short-urls' => (bool) Config\EnvVars::ROBOTS_ALLOW_ALL_SHORT_URLS->loadFromEnv(),
|
||||
'user-agents' => splitByComma(Config\EnvVars::ROBOTS_USER_AGENTS->loadFromEnv()),
|
||||
],
|
||||
|
||||
|
@ -8,7 +8,7 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
return [
|
||||
|
||||
'router' => [
|
||||
'base_path' => EnvVars::BASE_PATH->loadFromEnv(''),
|
||||
'base_path' => EnvVars::BASE_PATH->loadFromEnv(),
|
||||
|
||||
'fastroute' => [
|
||||
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
|
||||
|
@ -21,7 +21,7 @@ return (static function (): array {
|
||||
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
|
||||
|
||||
// TODO This should be based on config, not the env var
|
||||
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false) ? '[/]' : '';
|
||||
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv() ? '[/]' : '';
|
||||
|
||||
return [
|
||||
|
||||
|
@ -11,25 +11,25 @@ return [
|
||||
'tracking' => [
|
||||
// Tells if IP addresses should be anonymized before persisting, to fulfil data protection regulations
|
||||
// This applies only if IP address tracking is enabled
|
||||
'anonymize_remote_addr' => (bool) EnvVars::ANONYMIZE_REMOTE_ADDR->loadFromEnv(true),
|
||||
'anonymize_remote_addr' => (bool) EnvVars::ANONYMIZE_REMOTE_ADDR->loadFromEnv(),
|
||||
|
||||
// Tells if visits to not-found URLs should be tracked. The disable_tracking option takes precedence
|
||||
'track_orphan_visits' => (bool) EnvVars::TRACK_ORPHAN_VISITS->loadFromEnv(true),
|
||||
'track_orphan_visits' => (bool) EnvVars::TRACK_ORPHAN_VISITS->loadFromEnv(),
|
||||
|
||||
// A query param that, if provided, will disable tracking of one particular visit. Always takes precedence
|
||||
'disable_track_param' => EnvVars::DISABLE_TRACK_PARAM->loadFromEnv(),
|
||||
|
||||
// If true, visits will not be tracked at all
|
||||
'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(false),
|
||||
'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(),
|
||||
|
||||
// If true, visits will be tracked, but neither the IP address, nor the location will be resolved
|
||||
'disable_ip_tracking' => (bool) EnvVars::DISABLE_IP_TRACKING->loadFromEnv(false),
|
||||
'disable_ip_tracking' => (bool) EnvVars::DISABLE_IP_TRACKING->loadFromEnv(),
|
||||
|
||||
// If true, the referrer will not be tracked
|
||||
'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING->loadFromEnv(false),
|
||||
'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING->loadFromEnv(),
|
||||
|
||||
// If true, the user agent will not be tracked
|
||||
'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING->loadFromEnv(false),
|
||||
'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING->loadFromEnv(),
|
||||
|
||||
// A list of IP addresses, patterns or CIDR blocks from which tracking is disabled by default
|
||||
'disable_tracking_from' => splitByComma(EnvVars::DISABLE_TRACKING_FROM->loadFromEnv()),
|
||||
|
@ -5,29 +5,28 @@ declare(strict_types=1);
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_SHORT_CODES_LENGTH;
|
||||
use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH;
|
||||
|
||||
return (static function (): array {
|
||||
$shortCodesLength = max(
|
||||
(int) EnvVars::DEFAULT_SHORT_CODES_LENGTH->loadFromEnv(DEFAULT_SHORT_CODES_LENGTH),
|
||||
(int) EnvVars::DEFAULT_SHORT_CODES_LENGTH->loadFromEnv(),
|
||||
MIN_SHORT_CODES_LENGTH,
|
||||
);
|
||||
$modeFromEnv = EnvVars::SHORT_URL_MODE->loadFromEnv(ShortUrlMode::STRICT->value);
|
||||
$modeFromEnv = EnvVars::SHORT_URL_MODE->loadFromEnv();
|
||||
$mode = ShortUrlMode::tryFrom($modeFromEnv) ?? ShortUrlMode::STRICT;
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [ // TODO Refactor this structure to url_shortener.schema and url_shortener.default_domain
|
||||
'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv(true)) ? 'https' : 'http',
|
||||
'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(''),
|
||||
'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv()) ? 'https' : 'http',
|
||||
'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(),
|
||||
],
|
||||
'default_short_codes_length' => $shortCodesLength,
|
||||
'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(true),
|
||||
'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(false),
|
||||
'multi_segment_slugs_enabled' => (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(false),
|
||||
'trailing_slash_enabled' => (bool) EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false),
|
||||
'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(),
|
||||
'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(),
|
||||
'multi_segment_slugs_enabled' => (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(),
|
||||
'trailing_slash_enabled' => (bool) EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(),
|
||||
'mode' => $mode,
|
||||
],
|
||||
|
||||
|
@ -8,18 +8,13 @@ use Laminas\ConfigAggregator;
|
||||
use Laminas\Diactoros;
|
||||
use Mezzio;
|
||||
use Mezzio\ProblemDetails;
|
||||
use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider;
|
||||
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
use function Shlinkio\Shlink\Core\enumValues;
|
||||
|
||||
$isTestEnv = env('APP_ENV') === 'test';
|
||||
|
||||
return (new ConfigAggregator\ConfigAggregator(
|
||||
providers: [
|
||||
! $isTestEnv
|
||||
? new EnvVarLoaderProvider('config/params/generated_config.php', enumValues(Core\Config\EnvVars::class))
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
Mezzio\ConfigProvider::class,
|
||||
Mezzio\Router\ConfigProvider::class,
|
||||
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
|
||||
|
@ -6,14 +6,21 @@ use Laminas\ServiceManager\ServiceManager;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Lock;
|
||||
|
||||
use function Shlinkio\Shlink\Config\loadEnvVarsFromConfig;
|
||||
use function Shlinkio\Shlink\Core\enumValues;
|
||||
|
||||
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
// This is one of the first files loaded. Configure the timezone here
|
||||
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv(date_default_timezone_get()));
|
||||
// Promote env vars from installer config
|
||||
loadEnvVarsFromConfig('config/params/generated_config.php', enumValues(EnvVars::class));
|
||||
|
||||
// This is one of the first files loaded. Configure the timezone and memory limit here
|
||||
ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv());
|
||||
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv());
|
||||
|
||||
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
|
||||
// It needs to be placed here as individual config files will not be loaded once config is cached
|
||||
@ -23,10 +30,6 @@ if (! class_exists(LOCAL_LOCK_FACTORY)) {
|
||||
|
||||
return (static function (): ServiceManager {
|
||||
$config = require __DIR__ . '/config.php';
|
||||
|
||||
// Set memory limit right after loading config, to ensure installer config has been promoted as env vars
|
||||
ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv('512M'));
|
||||
|
||||
$container = new ServiceManager($config['dependencies']);
|
||||
$container->setService('config', $config);
|
||||
|
||||
|
@ -8,14 +8,19 @@ mkdir -p data/cache data/locks data/log data/proxies
|
||||
|
||||
flags="--no-interaction --clear-db-cache"
|
||||
|
||||
# Read env vars through Shlink command, so that it applies the `_FILE` env var fallback logic
|
||||
geolite_license_key=$(bin/cli env-var:read GEOLITE_LICENSE_KEY)
|
||||
skip_initial_geolite_download=$(bin/cli env-var:read SKIP_INITIAL_GEOLITE_DOWNLOAD)
|
||||
initial_api_key=$(bin/cli env-var:read INITIAL_API_KEY)
|
||||
|
||||
# Skip downloading GeoLite2 db file if the license key env var was not defined or skipping was explicitly set
|
||||
if [ -z "${GEOLITE_LICENSE_KEY}" ] || [ "${SKIP_INITIAL_GEOLITE_DOWNLOAD}" = "true" ]; then
|
||||
if [ -z "${geolite_license_key}" ] || [ "${skip_initial_geolite_download}" = "true" ]; then
|
||||
flags="${flags} --skip-download-geolite"
|
||||
fi
|
||||
|
||||
# If INITIAL_API_KEY was provided, create an initial API key
|
||||
if [ -n "${INITIAL_API_KEY}" ]; then
|
||||
flags="${flags} --initial-api-key=${INITIAL_API_KEY}"
|
||||
if [ -n "${initial_api_key}" ]; then
|
||||
flags="${flags} --initial-api-key=${initial_api_key}"
|
||||
fi
|
||||
|
||||
php vendor/bin/shlink-installer init ${flags}
|
||||
|
@ -45,6 +45,8 @@ return [
|
||||
Command\RedirectRule\ManageRedirectRulesCommand::class,
|
||||
|
||||
Command\Integration\MatomoSendVisitsCommand::NAME => Command\Integration\MatomoSendVisitsCommand::class,
|
||||
|
||||
Command\Config\ReadEnvVarCommand::NAME => Command\Config\ReadEnvVarCommand::class,
|
||||
],
|
||||
],
|
||||
|
||||
|
@ -75,6 +75,8 @@ return [
|
||||
Command\RedirectRule\ManageRedirectRulesCommand::class => ConfigAbstractFactory::class,
|
||||
|
||||
Command\Integration\MatomoSendVisitsCommand::class => ConfigAbstractFactory::class,
|
||||
|
||||
Command\Config\ReadEnvVarCommand::class => InvokableFactory::class,
|
||||
],
|
||||
],
|
||||
|
||||
|
68
module/CLI/src/Command/Config/ReadEnvVarCommand.php
Normal file
68
module/CLI/src/Command/Config/ReadEnvVarCommand.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\CLI\Command\Config;
|
||||
|
||||
use Closure;
|
||||
use Shlinkio\Shlink\CLI\Util\ExitCode;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
use function Shlinkio\Shlink\Config\formatEnvVarValue;
|
||||
use function Shlinkio\Shlink\Core\ArrayUtils\contains;
|
||||
use function Shlinkio\Shlink\Core\enumValues;
|
||||
use function sprintf;
|
||||
|
||||
class ReadEnvVarCommand extends Command
|
||||
{
|
||||
public const NAME = 'env-var:read';
|
||||
|
||||
/** @var Closure(string $envVar): mixed */
|
||||
private readonly Closure $loadEnvVar;
|
||||
|
||||
public function __construct(?Closure $loadEnvVar = null)
|
||||
{
|
||||
$this->loadEnvVar = $loadEnvVar ?? static fn (string $envVar) => EnvVars::from($envVar)->loadFromEnv();
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName(self::NAME)
|
||||
->setHidden()
|
||||
->setDescription('Display current value for an env var')
|
||||
->addArgument('envVar', InputArgument::REQUIRED, 'The env var to read');
|
||||
}
|
||||
|
||||
protected function interact(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$envVar = $input->getArgument('envVar');
|
||||
$validEnvVars = enumValues(EnvVars::class);
|
||||
|
||||
if ($envVar === null) {
|
||||
$envVar = $io->choice('Select the env var to read', $validEnvVars);
|
||||
}
|
||||
|
||||
if (! contains($envVar, $validEnvVars)) {
|
||||
throw new InvalidArgumentException(sprintf('%s is not a valid Shlink environment variable', $envVar));
|
||||
}
|
||||
|
||||
$input->setArgument('envVar', $envVar);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$envVar = $input->getArgument('envVar');
|
||||
$output->writeln(formatEnvVarValue(($this->loadEnvVar)($envVar)));
|
||||
|
||||
return ExitCode::EXIT_SUCCESS;
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ abstract class AbstractDatabaseCommand extends AbstractLockedCommand
|
||||
|
||||
public function __construct(
|
||||
LockFactory $locker,
|
||||
private ProcessRunnerInterface $processRunner,
|
||||
private readonly ProcessRunnerInterface $processRunner,
|
||||
PhpExecutableFinder $phpFinder,
|
||||
) {
|
||||
parent::__construct($locker);
|
||||
|
54
module/CLI/test/Command/Config/ReadEnvVarCommandTest.php
Normal file
54
module/CLI/test/Command/Config/ReadEnvVarCommandTest.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\CLI\Command\Config;
|
||||
|
||||
use Monolog\Test\TestCase;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Shlinkio\Shlink\CLI\Command\Config\ReadEnvVarCommand;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use ShlinkioTest\Shlink\CLI\Util\CliTestUtils;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
class ReadEnvVarCommandTest extends TestCase
|
||||
{
|
||||
private CommandTester $commandTester;
|
||||
private string $envVarValue = 'the_env_var_value';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->commandTester = CliTestUtils::testerForCommand(new ReadEnvVarCommand(fn () => $this->envVarValue));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function errorIsThrownIfProvidedEnvVarIsInvalid(): void
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('foo is not a valid Shlink environment variable');
|
||||
|
||||
$this->commandTester->execute(['envVar' => 'foo']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function valueIsPrintedIfProvidedEnvVarIsValid(): void
|
||||
{
|
||||
$this->commandTester->execute(['envVar' => EnvVars::BASE_PATH->value]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
|
||||
self::assertStringNotContainsString('Select the env var to read', $output);
|
||||
self::assertStringContainsString($this->envVarValue, $output);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function envVarNameIsRequestedIfArgumentIsMissing(): void
|
||||
{
|
||||
$this->commandTester->setInputs([EnvVars::BASE_PATH->value]);
|
||||
$this->commandTester->execute([]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Select the env var to read', $output);
|
||||
self::assertStringContainsString($this->envVarValue, $output);
|
||||
}
|
||||
}
|
@ -4,12 +4,27 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Config;
|
||||
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode;
|
||||
|
||||
use function date_default_timezone_get;
|
||||
use function file_get_contents;
|
||||
use function is_file;
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
use function Shlinkio\Shlink\Config\parseEnvVar;
|
||||
use function sprintf;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_BG_COLOR;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_COLOR;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ERROR_CORRECTION;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_FORMAT;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_MARGIN;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ROUND_BLOCK_SIZE;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_SIZE;
|
||||
use const Shlinkio\Shlink\DEFAULT_REDIRECT_CACHE_LIFETIME;
|
||||
use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE;
|
||||
use const Shlinkio\Shlink\DEFAULT_SHORT_CODES_LENGTH;
|
||||
|
||||
enum EnvVars: string
|
||||
{
|
||||
case DELETE_SHORT_URL_THRESHOLD = 'DELETE_SHORT_URL_THRESHOLD';
|
||||
@ -74,10 +89,68 @@ enum EnvVars: string
|
||||
case ROBOTS_USER_AGENTS = 'ROBOTS_USER_AGENTS';
|
||||
case TIMEZONE = 'TIMEZONE';
|
||||
case MEMORY_LIMIT = 'MEMORY_LIMIT';
|
||||
case INITIAL_API_KEY = 'INITIAL_API_KEY';
|
||||
case SKIP_INITIAL_GEOLITE_DOWNLOAD = 'SKIP_INITIAL_GEOLITE_DOWNLOAD';
|
||||
|
||||
public function loadFromEnv(mixed $default = null): mixed
|
||||
public function loadFromEnv(): mixed
|
||||
{
|
||||
return env($this->value) ?? $this->loadFromFileEnv() ?? $default;
|
||||
return env($this->value) ?? $this->loadFromFileEnv() ?? $this->defaultValue();
|
||||
}
|
||||
|
||||
private function defaultValue(): string|int|bool|null
|
||||
{
|
||||
return match ($this) {
|
||||
self::MEMORY_LIMIT => '512M',
|
||||
self::TIMEZONE => date_default_timezone_get(),
|
||||
|
||||
self::DEFAULT_SHORT_CODES_LENGTH => DEFAULT_SHORT_CODES_LENGTH,
|
||||
self::SHORT_URL_MODE => ShortUrlMode::STRICT->value,
|
||||
self::IS_HTTPS_ENABLED, self::AUTO_RESOLVE_TITLES => true,
|
||||
self::REDIRECT_APPEND_EXTRA_PATH,
|
||||
self::MULTI_SEGMENT_SLUGS_ENABLED,
|
||||
self::SHORT_URL_TRAILING_SLASH => false,
|
||||
self::DEFAULT_DOMAIN, self::BASE_PATH => '',
|
||||
self::CACHE_NAMESPACE => 'Shlink',
|
||||
|
||||
self::REDIS_PUB_SUB_ENABLED,
|
||||
self::MATOMO_ENABLED,
|
||||
self::ROBOTS_ALLOW_ALL_SHORT_URLS => false,
|
||||
|
||||
self::DB_NAME => 'shlink',
|
||||
self::DB_HOST => self::DB_UNIX_SOCKET->loadFromEnv(),
|
||||
self::DB_DRIVER => 'sqlite',
|
||||
self::DB_PORT => match (self::DB_DRIVER->loadFromEnv()) {
|
||||
'postgres' => '5432',
|
||||
'mssql' => '1433',
|
||||
default => '3306',
|
||||
},
|
||||
|
||||
self::MERCURE_INTERNAL_HUB_URL => self::MERCURE_PUBLIC_HUB_URL->loadFromEnv(),
|
||||
|
||||
self::DEFAULT_QR_CODE_SIZE, => DEFAULT_QR_CODE_SIZE,
|
||||
self::DEFAULT_QR_CODE_MARGIN, => DEFAULT_QR_CODE_MARGIN,
|
||||
self::DEFAULT_QR_CODE_FORMAT, => DEFAULT_QR_CODE_FORMAT,
|
||||
self::DEFAULT_QR_CODE_ERROR_CORRECTION, => DEFAULT_QR_CODE_ERROR_CORRECTION,
|
||||
self::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE, => DEFAULT_QR_CODE_ROUND_BLOCK_SIZE,
|
||||
self::QR_CODE_FOR_DISABLED_SHORT_URLS, => DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS,
|
||||
self::DEFAULT_QR_CODE_COLOR, => DEFAULT_QR_CODE_COLOR,
|
||||
self::DEFAULT_QR_CODE_BG_COLOR, => DEFAULT_QR_CODE_BG_COLOR,
|
||||
|
||||
self::RABBITMQ_ENABLED, self::RABBITMQ_USE_SSL => false,
|
||||
self::RABBITMQ_PORT => 5672,
|
||||
self::RABBITMQ_VHOST => '/',
|
||||
|
||||
self::REDIRECT_STATUS_CODE => DEFAULT_REDIRECT_STATUS_CODE->value,
|
||||
self::REDIRECT_CACHE_LIFETIME => DEFAULT_REDIRECT_CACHE_LIFETIME,
|
||||
|
||||
self::ANONYMIZE_REMOTE_ADDR, self::TRACK_ORPHAN_VISITS => true,
|
||||
self::DISABLE_TRACKING,
|
||||
self::DISABLE_IP_TRACKING,
|
||||
self::DISABLE_REFERRER_TRACKING,
|
||||
self::DISABLE_UA_TRACKING => false,
|
||||
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,11 +28,15 @@ readonly class ShortUrlRedirectionBuilder implements ShortUrlRedirectionBuilderI
|
||||
?string $extraPath = null,
|
||||
): string {
|
||||
$uri = new Uri($this->redirectionResolver->resolveLongUrl($shortUrl, $request));
|
||||
$currentQuery = $request->getQueryParams();
|
||||
$shouldForwardQuery = $shortUrl->forwardQuery();
|
||||
$baseQueryString = $uri->getQuery();
|
||||
$basePath = $uri->getPath();
|
||||
|
||||
// Get current query by manually parsing query string, instead of using $request->getQueryParams().
|
||||
// That prevents some weird PHP logic in which some characters in param names are converted to ensure resulting
|
||||
// names are valid variable names.
|
||||
$currentQuery = Query::parse($request->getUri()->getQuery());
|
||||
|
||||
return $uri
|
||||
->withQuery($shouldForwardQuery ? $this->resolveQuery($baseQueryString, $currentQuery) : $baseQueryString)
|
||||
->withPath($this->resolvePath($basePath, $extraPath))
|
||||
|
@ -122,4 +122,22 @@ class RedirectTest extends ApiTestCase
|
||||
self::assertEquals(302, $response->getStatusCode());
|
||||
self::assertEquals($longUrl, $response->getHeaderLine('Location'));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function queryParametersAreProperlyForwarded(): void
|
||||
{
|
||||
$slug = 'forward-query-params';
|
||||
$this->callApiWithKey('POST', '/short-urls', [
|
||||
RequestOptions::JSON => [
|
||||
'longUrl' => 'https://example.com',
|
||||
'customSlug' => $slug,
|
||||
'forwardQuery' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$response = $this->callShortUrl($slug, [RequestOptions::QUERY => ['foo bar' => '123']]);
|
||||
|
||||
self::assertEquals(302, $response->getStatusCode());
|
||||
self::assertEquals('https://example.com?foo%20bar=123', $response->getHeaderLine('Location'));
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ namespace ShlinkioTest\Shlink\Core\Config;
|
||||
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\TestWith;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
@ -24,9 +25,9 @@ class EnvVarsTest extends TestCase
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
putenv(EnvVars::BASE_PATH->value . '=');
|
||||
putenv(EnvVars::DB_NAME->value . '=');
|
||||
putenv(EnvVars::DB_PASSWORD->value . '_FILE=');
|
||||
putenv(EnvVars::BASE_PATH->value);
|
||||
putenv(EnvVars::DB_NAME->value);
|
||||
putenv(EnvVars::DB_PASSWORD->value . '_FILE');
|
||||
}
|
||||
|
||||
#[Test, DataProvider('provideExistingEnvVars')]
|
||||
@ -37,26 +38,23 @@ class EnvVarsTest extends TestCase
|
||||
|
||||
public static function provideExistingEnvVars(): iterable
|
||||
{
|
||||
yield 'DB_NAME' => [EnvVars::DB_NAME, true];
|
||||
yield 'BASE_PATH' => [EnvVars::BASE_PATH, true];
|
||||
yield 'DB_DRIVER' => [EnvVars::DB_DRIVER, false];
|
||||
yield 'DB_NAME (is set)' => [EnvVars::DB_NAME, true];
|
||||
yield 'BASE_PATH (is set)' => [EnvVars::BASE_PATH, true];
|
||||
yield 'DB_DRIVER (has default)' => [EnvVars::DB_DRIVER, true];
|
||||
yield 'DEFAULT_REGULAR_404_REDIRECT' => [EnvVars::DEFAULT_REGULAR_404_REDIRECT, false];
|
||||
}
|
||||
|
||||
#[Test, DataProvider('provideEnvVarsValues')]
|
||||
public function expectedValueIsLoadedFromEnv(EnvVars $envVar, mixed $expected, mixed $default): void
|
||||
public function expectedValueIsLoadedFromEnv(EnvVars $envVar, mixed $expected): void
|
||||
{
|
||||
self::assertEquals($expected, $envVar->loadFromEnv($default));
|
||||
self::assertEquals($expected, $envVar->loadFromEnv());
|
||||
}
|
||||
|
||||
public static function provideEnvVarsValues(): iterable
|
||||
{
|
||||
yield 'DB_NAME without default' => [EnvVars::DB_NAME, 'shlink', null];
|
||||
yield 'DB_NAME with default' => [EnvVars::DB_NAME, 'shlink', 'foobar'];
|
||||
yield 'BASE_PATH without default' => [EnvVars::BASE_PATH, 'the_base_path', null];
|
||||
yield 'BASE_PATH with default' => [EnvVars::BASE_PATH, 'the_base_path', 'foobar'];
|
||||
yield 'DB_DRIVER without default' => [EnvVars::DB_DRIVER, null, null];
|
||||
yield 'DB_DRIVER with default' => [EnvVars::DB_DRIVER, 'foobar', 'foobar'];
|
||||
yield 'DB_NAME (is set)' => [EnvVars::DB_NAME, 'shlink'];
|
||||
yield 'BASE_PATH (is set)' => [EnvVars::BASE_PATH, 'the_base_path'];
|
||||
yield 'DB_DRIVER (has default)' => [EnvVars::DB_DRIVER, 'sqlite'];
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@ -64,4 +62,20 @@ class EnvVarsTest extends TestCase
|
||||
{
|
||||
self::assertEquals('this_is_the_password', EnvVars::DB_PASSWORD->loadFromEnv());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[TestWith(['mysql', '3306'])]
|
||||
#[TestWith(['maria', '3306'])]
|
||||
#[TestWith(['postgres', '5432'])]
|
||||
#[TestWith(['mssql', '1433'])]
|
||||
public function defaultPortIsResolvedBasedOnDbDriver(string $dbDriver, string $expectedPort): void
|
||||
{
|
||||
putenv(EnvVars::DB_DRIVER->value . '=' . $dbDriver);
|
||||
|
||||
try {
|
||||
self::assertEquals($expectedPort, EnvVars::DB_PORT->loadFromEnv());
|
||||
} finally {
|
||||
putenv(EnvVars::DB_DRIVER->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace ShlinkioTest\Shlink\Core\ShortUrl\Helper;
|
||||
|
||||
use Laminas\Diactoros\ServerRequestFactory;
|
||||
use Laminas\Diactoros\Uri;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\TestWith;
|
||||
@ -53,62 +54,70 @@ class ShortUrlRedirectionBuilderTest extends TestCase
|
||||
|
||||
public static function provideData(): iterable
|
||||
{
|
||||
$request = static fn (array $query = []) => ServerRequestFactory::fromGlobals()->withQueryParams($query);
|
||||
$request = static fn (string $query = '') => ServerRequestFactory::fromGlobals()->withUri(
|
||||
(new Uri())->withQuery($query),
|
||||
);
|
||||
|
||||
yield ['https://example.com/foo/bar?some=thing', $request(), null, true];
|
||||
yield ['https://example.com/foo/bar?some=thing', $request(), null, null];
|
||||
yield ['https://example.com/foo/bar?some=thing', $request(), null, false];
|
||||
yield ['https://example.com/foo/bar?some=thing&else', $request(['else' => null]), null, true];
|
||||
yield ['https://example.com/foo/bar?some=thing&foo=bar', $request(['foo' => 'bar']), null, true];
|
||||
yield ['https://example.com/foo/bar?some=thing&foo=bar', $request(['foo' => 'bar']), null, null];
|
||||
yield ['https://example.com/foo/bar?some=thing', $request(['foo' => 'bar']), null, false];
|
||||
yield ['https://example.com/foo/bar?some=thing&123=foo', $request(['123' => 'foo']), null, true];
|
||||
yield ['https://example.com/foo/bar?some=thing&456=foo', $request([456 => 'foo']), null, true];
|
||||
yield ['https://example.com/foo/bar?some=thing&456=foo', $request([456 => 'foo']), null, null];
|
||||
yield ['https://example.com/foo/bar?some=thing', $request([456 => 'foo']), null, false];
|
||||
yield ['https://example.com/foo/bar?some=thing&else', $request('else'), null, true];
|
||||
yield ['https://example.com/foo/bar?some=thing&foo=bar', $request('foo=bar'), null, true];
|
||||
yield ['https://example.com/foo/bar?some=thing&foo=bar', $request('foo=bar'), null, null];
|
||||
yield ['https://example.com/foo/bar?some=thing', $request('foo=bar'), null, false];
|
||||
yield ['https://example.com/foo/bar?some=thing&123=foo', $request('123=foo'), null, true];
|
||||
yield ['https://example.com/foo/bar?some=thing&456=foo', $request('456=foo'), null, true];
|
||||
yield ['https://example.com/foo/bar?some=thing&456=foo', $request('456=foo'), null, null];
|
||||
yield ['https://example.com/foo/bar?some=thing', $request('456=foo'), null, false];
|
||||
yield [
|
||||
'https://example.com/foo/bar?some=overwritten&foo=bar',
|
||||
$request(['foo' => 'bar', 'some' => 'overwritten']),
|
||||
$request('foo=bar&some=overwritten'),
|
||||
null,
|
||||
true,
|
||||
];
|
||||
yield [
|
||||
'https://example.com/foo/bar?some=overwritten',
|
||||
$request(['foobar' => 'notrack', 'some' => 'overwritten']),
|
||||
$request('foobar=notrack&some=overwritten'),
|
||||
null,
|
||||
true,
|
||||
];
|
||||
yield [
|
||||
'https://example.com/foo/bar?some=overwritten',
|
||||
$request(['foobar' => 'notrack', 'some' => 'overwritten']),
|
||||
$request('foobar=notrack&some=overwritten'),
|
||||
null,
|
||||
null,
|
||||
];
|
||||
yield [
|
||||
'https://example.com/foo/bar?some=thing',
|
||||
$request(['foobar' => 'notrack', 'some' => 'overwritten']),
|
||||
$request('foobar=notrack&some=overwritten'),
|
||||
null,
|
||||
false,
|
||||
];
|
||||
yield ['https://example.com/foo/bar/something/else-baz?some=thing', $request(), '/something/else-baz', true];
|
||||
yield [
|
||||
'https://example.com/foo/bar/something/else-baz?some=thing&hello=world',
|
||||
$request(['hello' => 'world']),
|
||||
$request('hello=world',),
|
||||
'/something/else-baz',
|
||||
true,
|
||||
];
|
||||
yield [
|
||||
'https://example.com/foo/bar/something/else-baz?some=thing&hello=world',
|
||||
$request(['hello' => 'world']),
|
||||
$request('hello=world',),
|
||||
'/something/else-baz',
|
||||
null,
|
||||
];
|
||||
yield [
|
||||
'https://example.com/foo/bar/something/else-baz?some=thing',
|
||||
$request(['hello' => 'world']),
|
||||
$request('hello=world',),
|
||||
'/something/else-baz',
|
||||
false,
|
||||
];
|
||||
yield [
|
||||
'https://example.com/foo/bar/something/else-baz?some=thing¶meter%20with%20spaces=world',
|
||||
$request('parameter with spaces=world',),
|
||||
'/something/else-baz',
|
||||
true,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user