Merge pull request #1344 from acelaya-forks/feature/strinct-env-vars

Feature/strinct env vars
This commit is contained in:
Alejandro Celaya 2022-01-20 21:08:33 +01:00 committed by GitHub
commit dd5bce9694
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 367 additions and 69 deletions

View File

@ -4,10 +4,10 @@ declare(strict_types=1);
namespace Shlinkio\Shlink;
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
return (static function (): array {
$threshold = env('DELETE_SHORT_URL_THRESHOLD');
$threshold = EnvVars::DELETE_SHORT_URL_THRESHOLD()->loadFromEnv();
return [

View File

@ -3,12 +3,12 @@
declare(strict_types=1);
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
use Shlinkio\Shlink\Core\Config\EnvVars;
use function Functional\contains;
use function Shlinkio\Shlink\Config\env;
return (static function (): array {
$driver = env('DB_DRIVER');
$driver = EnvVars::DB_DRIVER()->loadFromEnv();
$isMysqlCompatible = contains(['maria', 'mysql'], $driver);
$resolveDriver = static fn () => match ($driver) {
@ -35,12 +35,12 @@ return (static function (): array {
],
default => [
'driver' => $resolveDriver(),
'dbname' => env('DB_NAME', 'shlink'),
'user' => env('DB_USER'),
'password' => env('DB_PASSWORD'),
'host' => env('DB_HOST', env('DB_UNIX_SOCKET')),
'port' => env('DB_PORT', $resolveDefaultPort()),
'unix_socket' => $isMysqlCompatible ? env('DB_UNIX_SOCKET') : null,
'dbname' => EnvVars::DB_NAME()->loadFromEnv('shlink'),
'user' => EnvVars::DB_USER()->loadFromEnv(),
'password' => EnvVars::DB_PASSWORD()->loadFromEnv(),
'host' => EnvVars::DB_HOST()->loadFromEnv(EnvVars::DB_UNIX_SOCKET()->loadFromEnv()),
'port' => EnvVars::DB_PORT()->loadFromEnv($resolveDefaultPort()),
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET()->loadFromEnv() : null,
'charset' => $resolveCharset(),
],
};

View File

@ -2,14 +2,14 @@
declare(strict_types=1);
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
return [
'geolite2' => [
'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
'temp_dir' => __DIR__ . '/../../data',
'license_key' => env('GEOLITE_LICENSE_KEY'),
'license_key' => EnvVars::GEOLITE_LICENSE_KEY()->loadFromEnv(),
],
];

View File

@ -5,10 +5,9 @@ declare(strict_types=1);
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Predis\ClientInterface as PredisClient;
use Shlinkio\Shlink\Common\Logger\LoggerAwareDelegatorFactory;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Symfony\Component\Lock;
use function Shlinkio\Shlink\Config\env;
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
return [
@ -25,7 +24,7 @@ return [
LOCAL_LOCK_FACTORY => ConfigAbstractFactory::class,
],
'aliases' => [
'lock_store' => env('REDIS_SERVERS') === null ? 'local_lock_store' : 'redis_lock_store',
'lock_store' => EnvVars::REDIS_SERVERS()->existsInEnv() ? 'redis_lock_store' : 'local_lock_store',
'redis_lock_store' => Lock\Store\RedisStore::class,
'local_lock_store' => Lock\Store\FlockStore::class,

View File

@ -4,20 +4,19 @@ declare(strict_types=1);
use Laminas\ServiceManager\Proxy\LazyServiceFactory;
use Shlinkio\Shlink\Common\Mercure\LcobucciJwtProvider;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Symfony\Component\Mercure\Hub;
use Symfony\Component\Mercure\HubInterface;
use function Shlinkio\Shlink\Config\env;
return (static function (): array {
$publicUrl = env('MERCURE_PUBLIC_HUB_URL');
$publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL()->loadFromEnv();
return [
'mercure' => [
'public_hub_url' => $publicUrl,
'internal_hub_url' => env('MERCURE_INTERNAL_HUB_URL', $publicUrl),
'jwt_secret' => env('MERCURE_JWT_SECRET'),
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL()->loadFromEnv($publicUrl),
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET()->loadFromEnv(),
'jwt_issuer' => 'Shlink',
],

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ERROR_CORRECTION;
use const Shlinkio\Shlink\DEFAULT_QR_CODE_FORMAT;
@ -13,11 +13,15 @@ use const Shlinkio\Shlink\DEFAULT_QR_CODE_SIZE;
return [
'qr_codes' => [
'size' => (int) env('DEFAULT_QR_CODE_SIZE', DEFAULT_QR_CODE_SIZE),
'margin' => (int) env('DEFAULT_QR_CODE_MARGIN', DEFAULT_QR_CODE_MARGIN),
'format' => env('DEFAULT_QR_CODE_FORMAT', DEFAULT_QR_CODE_FORMAT),
'error_correction' => env('DEFAULT_QR_CODE_ERROR_CORRECTION', DEFAULT_QR_CODE_ERROR_CORRECTION),
'round_block_size' => (bool) env('DEFAULT_QR_CODE_ROUND_BLOCK_SIZE', DEFAULT_QR_CODE_ROUND_BLOCK_SIZE),
'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,
),
],
];

View File

@ -5,18 +5,17 @@ declare(strict_types=1);
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Laminas\ServiceManager\Proxy\LazyServiceFactory;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
return [
'rabbitmq' => [
'enabled' => (bool) env('RABBITMQ_ENABLED', false),
'host' => env('RABBITMQ_HOST'),
'port' => (int) env('RABBITMQ_PORT', '5672'),
'user' => env('RABBITMQ_USER'),
'password' => env('RABBITMQ_PASSWORD'),
'vhost' => env('RABBITMQ_VHOST', '/'),
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED()->loadFromEnv(false),
'host' => EnvVars::RABBITMQ_HOST()->loadFromEnv(),
'port' => (int) EnvVars::RABBITMQ_PORT()->loadFromEnv('5672'),
'user' => EnvVars::RABBITMQ_USER()->loadFromEnv(),
'password' => EnvVars::RABBITMQ_PASSWORD()->loadFromEnv(),
'vhost' => EnvVars::RABBITMQ_VHOST()->loadFromEnv('/'),
],
'dependencies' => [

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
use const Shlinkio\Shlink\DEFAULT_REDIRECT_CACHE_LIFETIME;
use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE;
@ -10,14 +10,16 @@ use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE;
return [
'not_found_redirects' => [
'invalid_short_url' => env('DEFAULT_INVALID_SHORT_URL_REDIRECT'),
'regular_404' => env('DEFAULT_REGULAR_404_REDIRECT'),
'base_url' => env('DEFAULT_BASE_URL_REDIRECT'),
'invalid_short_url' => EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT()->loadFromEnv(),
'regular_404' => EnvVars::DEFAULT_REGULAR_404_REDIRECT()->loadFromEnv(),
'base_url' => EnvVars::DEFAULT_BASE_URL_REDIRECT()->loadFromEnv(),
],
'redirects' => [
'redirect_status_code' => (int) env('REDIRECT_STATUS_CODE', DEFAULT_REDIRECT_STATUS_CODE),
'redirect_cache_lifetime' => (int) env('REDIRECT_CACHE_LIFETIME', DEFAULT_REDIRECT_CACHE_LIFETIME),
'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE()->loadFromEnv(DEFAULT_REDIRECT_STATUS_CODE),
'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME()->loadFromEnv(
DEFAULT_REDIRECT_CACHE_LIFETIME,
),
],
];

View File

@ -2,10 +2,10 @@
declare(strict_types=1);
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
return (static function (): array {
$redisServers = env('REDIS_SERVERS');
$redisServers = EnvVars::REDIS_SERVERS()->loadFromEnv();
return match ($redisServers) {
null => [],
@ -14,7 +14,7 @@ return (static function (): array {
'default_lifetime' => 86400, // 24h
'redis' => [
'servers' => $redisServers,
'sentinel_service' => env('REDIS_SENTINEL_SERVICE'),
'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE()->loadFromEnv(),
],
],
],

View File

@ -3,13 +3,12 @@
declare(strict_types=1);
use Mezzio\Router\FastRouteRouter;
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
return [
'router' => [
'base_path' => env('BASE_PATH', ''),
'base_path' => EnvVars::BASE_PATH()->loadFromEnv(''),
'fastroute' => [
FastRouteRouter::CONFIG_CACHE_ENABLED => true,

View File

@ -2,12 +2,12 @@
declare(strict_types=1);
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
use const Shlinkio\Shlink\MIN_TASK_WORKERS;
return (static function () {
$taskWorkers = (int) env('TASK_WORKER_NUM', 16);
$taskWorkers = (int) EnvVars::TASK_WORKER_NUM()->loadFromEnv(16);
return [
@ -17,11 +17,11 @@ return (static function () {
'swoole-http-server' => [
'host' => '0.0.0.0',
'port' => (int) env('PORT', 8080),
'port' => (int) EnvVars::PORT()->loadFromEnv(8080),
'process-name' => 'shlink',
'options' => [
'worker_num' => (int) env('WEB_WORKER_NUM', 16),
'worker_num' => (int) EnvVars::WEB_WORKER_NUM()->loadFromEnv(16),
'task_worker_num' => max($taskWorkers, MIN_TASK_WORKERS),
],
],

View File

@ -2,35 +2,35 @@
declare(strict_types=1);
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
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) env('ANONYMIZE_REMOTE_ADDR', true),
'anonymize_remote_addr' => (bool) EnvVars::ANONYMIZE_REMOTE_ADDR()->loadFromEnv(true),
// Tells if visits to not-found URLs should be tracked. The disable_tracking option takes precedence
'track_orphan_visits' => (bool) env('TRACK_ORPHAN_VISITS', true),
'track_orphan_visits' => (bool) EnvVars::TRACK_ORPHAN_VISITS()->loadFromEnv(true),
// A query param that, if provided, will disable tracking of one particular visit. Always takes precedence
'disable_track_param' => env('DISABLE_TRACK_PARAM'),
'disable_track_param' => EnvVars::DISABLE_TRACK_PARAM()->loadFromEnv(),
// If true, visits will not be tracked at all
'disable_tracking' => (bool) env('DISABLE_TRACKING', false),
'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING()->loadFromEnv(false),
// If true, visits will be tracked, but neither the IP address, nor the location will be resolved
'disable_ip_tracking' => (bool) env('DISABLE_IP_TRACKING', false),
'disable_ip_tracking' => (bool) EnvVars::DISABLE_IP_TRACKING()->loadFromEnv(false),
// If true, the referrer will not be tracked
'disable_referrer_tracking' => (bool) env('DISABLE_REFERRER_TRACKING', false),
'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING()->loadFromEnv(false),
// If true, the user agent will not be tracked
'disable_ua_tracking' => (bool) env('DISABLE_UA_TRACKING', false),
'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING()->loadFromEnv(false),
// A list of IP addresses, patterns or CIDR blocks from which tracking is disabled by default
'disable_tracking_from' => env('DISABLE_TRACKING_FROM'),
'disable_tracking_from' => EnvVars::DISABLE_TRACKING_FROM()->loadFromEnv(),
],
];

View File

@ -2,14 +2,14 @@
declare(strict_types=1);
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
use const Shlinkio\Shlink\DEFAULT_SHORT_CODES_LENGTH;
use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH;
return (static function (): array {
$shortCodesLength = max(
(int) env('DEFAULT_SHORT_CODES_LENGTH', DEFAULT_SHORT_CODES_LENGTH),
(int) EnvVars::DEFAULT_SHORT_CODES_LENGTH()->loadFromEnv(DEFAULT_SHORT_CODES_LENGTH),
MIN_SHORT_CODES_LENGTH,
);
@ -17,12 +17,12 @@ return (static function (): array {
'url_shortener' => [
'domain' => [
'schema' => ((bool) env('IS_HTTPS_ENABLED', true)) ? 'https' : 'http',
'hostname' => env('DEFAULT_DOMAIN', ''),
'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED()->loadFromEnv(true)) ? 'https' : 'http',
'hostname' => EnvVars::DEFAULT_DOMAIN()->loadFromEnv(''),
],
'default_short_codes_length' => $shortCodesLength,
'auto_resolve_titles' => (bool) env('AUTO_RESOLVE_TITLES', false),
'append_extra_path' => (bool) env('REDIRECT_APPEND_EXTRA_PATH', false),
'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES()->loadFromEnv(false),
'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH()->loadFromEnv(false),
],
];

View File

@ -2,16 +2,17 @@
declare(strict_types=1);
use function Shlinkio\Shlink\Config\env;
use Shlinkio\Shlink\Core\Config\EnvVars;
return (static function (): array {
$webhooks = env('VISITS_WEBHOOKS');
$webhooks = EnvVars::VISITS_WEBHOOKS()->loadFromEnv();
return [
'visits_webhooks' => [
'webhooks' => $webhooks === null ? [] : explode(',', $webhooks),
'notify_orphan_visits_to_webhooks' => (bool) env('NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS', false),
'notify_orphan_visits_to_webhooks' =>
(bool) EnvVars::NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS()->loadFromEnv(false),
],
];

View File

@ -21,7 +21,7 @@ $isTestEnv = env('APP_ENV') === 'test';
return (new ConfigAggregator\ConfigAggregator([
! $isTestEnv
? new EnvVarLoaderProvider('config/params/generated_config.php')
? new EnvVarLoaderProvider('config/params/generated_config.php', Core\Config\EnvVars::cases())
: new ConfigAggregator\ArrayProvider([]),
Mezzio\ConfigProvider::class,
Mezzio\Router\ConfigProvider::class,

View File

@ -0,0 +1,156 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Config;
use ReflectionClass;
use ReflectionClassConstant;
use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
use function array_values;
use function Functional\contains;
use function Shlinkio\Shlink\Config\env;
// TODO Convert to enum
/**
* @method static EnvVars DELETE_SHORT_URL_THRESHOLD()
* @method static EnvVars DB_DRIVER()
* @method static EnvVars DB_NAME()
* @method static EnvVars DB_USER()
* @method static EnvVars DB_PASSWORD()
* @method static EnvVars DB_HOST()
* @method static EnvVars DB_UNIX_SOCKET()
* @method static EnvVars DB_PORT()
* @method static EnvVars GEOLITE_LICENSE_KEY()
* @method static EnvVars REDIS_SERVERS()
* @method static EnvVars REDIS_SENTINEL_SERVICE()
* @method static EnvVars MERCURE_PUBLIC_HUB_URL()
* @method static EnvVars MERCURE_INTERNAL_HUB_URL()
* @method static EnvVars MERCURE_JWT_SECRET()
* @method static EnvVars DEFAULT_QR_CODE_SIZE()
* @method static EnvVars DEFAULT_QR_CODE_MARGIN()
* @method static EnvVars DEFAULT_QR_CODE_FORMAT()
* @method static EnvVars DEFAULT_QR_CODE_ERROR_CORRECTION()
* @method static EnvVars DEFAULT_QR_CODE_ROUND_BLOCK_SIZE()
* @method static EnvVars RABBITMQ_ENABLED()
* @method static EnvVars RABBITMQ_HOST()
* @method static EnvVars RABBITMQ_PORT()
* @method static EnvVars RABBITMQ_USER()
* @method static EnvVars RABBITMQ_PASSWORD()
* @method static EnvVars RABBITMQ_VHOST()
* @method static EnvVars DEFAULT_INVALID_SHORT_URL_REDIRECT()
* @method static EnvVars DEFAULT_REGULAR_404_REDIRECT()
* @method static EnvVars DEFAULT_BASE_URL_REDIRECT()
* @method static EnvVars REDIRECT_STATUS_CODE()
* @method static EnvVars REDIRECT_CACHE_LIFETIME()
* @method static EnvVars BASE_PATH()
* @method static EnvVars PORT()
* @method static EnvVars TASK_WORKER_NUM()
* @method static EnvVars WEB_WORKER_NUM()
* @method static EnvVars ANONYMIZE_REMOTE_ADDR()
* @method static EnvVars TRACK_ORPHAN_VISITS()
* @method static EnvVars DISABLE_TRACK_PARAM()
* @method static EnvVars DISABLE_TRACKING()
* @method static EnvVars DISABLE_IP_TRACKING()
* @method static EnvVars DISABLE_REFERRER_TRACKING()
* @method static EnvVars DISABLE_UA_TRACKING()
* @method static EnvVars DISABLE_TRACKING_FROM()
* @method static EnvVars DEFAULT_SHORT_CODES_LENGTH()
* @method static EnvVars IS_HTTPS_ENABLED()
* @method static EnvVars DEFAULT_DOMAIN()
* @method static EnvVars AUTO_RESOLVE_TITLES()
* @method static EnvVars REDIRECT_APPEND_EXTRA_PATH()
* @method static EnvVars VISITS_WEBHOOKS()
* @method static EnvVars NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS()
*/
final class EnvVars
{
public const DELETE_SHORT_URL_THRESHOLD = 'DELETE_SHORT_URL_THRESHOLD';
public const DB_DRIVER = 'DB_DRIVER';
public const DB_NAME = 'DB_NAME';
public const DB_USER = 'DB_USER';
public const DB_PASSWORD = 'DB_PASSWORD';
public const DB_HOST = 'DB_HOST';
public const DB_UNIX_SOCKET = 'DB_UNIX_SOCKET';
public const DB_PORT = 'DB_PORT';
public const GEOLITE_LICENSE_KEY = 'GEOLITE_LICENSE_KEY';
public const REDIS_SERVERS = 'REDIS_SERVERS';
public const REDIS_SENTINEL_SERVICE = 'REDIS_SENTINEL_SERVICE';
public const MERCURE_PUBLIC_HUB_URL = 'MERCURE_PUBLIC_HUB_URL';
public const MERCURE_INTERNAL_HUB_URL = 'MERCURE_INTERNAL_HUB_URL';
public const MERCURE_JWT_SECRET = 'MERCURE_JWT_SECRET';
public const DEFAULT_QR_CODE_SIZE = 'DEFAULT_QR_CODE_SIZE';
public const DEFAULT_QR_CODE_MARGIN = 'DEFAULT_QR_CODE_MARGIN';
public const DEFAULT_QR_CODE_FORMAT = 'DEFAULT_QR_CODE_FORMAT';
public const DEFAULT_QR_CODE_ERROR_CORRECTION = 'DEFAULT_QR_CODE_ERROR_CORRECTION';
public const DEFAULT_QR_CODE_ROUND_BLOCK_SIZE = 'DEFAULT_QR_CODE_ROUND_BLOCK_SIZE';
public const RABBITMQ_ENABLED = 'RABBITMQ_ENABLED';
public const RABBITMQ_HOST = 'RABBITMQ_HOST';
public const RABBITMQ_PORT = 'RABBITMQ_PORT';
public const RABBITMQ_USER = 'RABBITMQ_USER';
public const RABBITMQ_PASSWORD = 'RABBITMQ_PASSWORD';
public const RABBITMQ_VHOST = 'RABBITMQ_VHOST';
public const DEFAULT_INVALID_SHORT_URL_REDIRECT = 'DEFAULT_INVALID_SHORT_URL_REDIRECT';
public const DEFAULT_REGULAR_404_REDIRECT = 'DEFAULT_REGULAR_404_REDIRECT';
public const DEFAULT_BASE_URL_REDIRECT = 'DEFAULT_BASE_URL_REDIRECT';
public const REDIRECT_STATUS_CODE = 'REDIRECT_STATUS_CODE';
public const REDIRECT_CACHE_LIFETIME = 'REDIRECT_CACHE_LIFETIME';
public const BASE_PATH = 'BASE_PATH';
public const PORT = 'PORT';
public const TASK_WORKER_NUM = 'TASK_WORKER_NUM';
public const WEB_WORKER_NUM = 'WEB_WORKER_NUM';
public const ANONYMIZE_REMOTE_ADDR = 'ANONYMIZE_REMOTE_ADDR';
public const TRACK_ORPHAN_VISITS = 'TRACK_ORPHAN_VISITS';
public const DISABLE_TRACK_PARAM = 'DISABLE_TRACK_PARAM';
public const DISABLE_TRACKING = 'DISABLE_TRACKING';
public const DISABLE_IP_TRACKING = 'DISABLE_IP_TRACKING';
public const DISABLE_REFERRER_TRACKING = 'DISABLE_REFERRER_TRACKING';
public const DISABLE_UA_TRACKING = 'DISABLE_UA_TRACKING';
public const DISABLE_TRACKING_FROM = 'DISABLE_TRACKING_FROM';
public const DEFAULT_SHORT_CODES_LENGTH = 'DEFAULT_SHORT_CODES_LENGTH';
public const IS_HTTPS_ENABLED = 'IS_HTTPS_ENABLED';
public const DEFAULT_DOMAIN = 'DEFAULT_DOMAIN';
public const AUTO_RESOLVE_TITLES = 'AUTO_RESOLVE_TITLES';
public const REDIRECT_APPEND_EXTRA_PATH = 'REDIRECT_APPEND_EXTRA_PATH';
public const VISITS_WEBHOOKS = 'VISITS_WEBHOOKS';
public const NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS = 'NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS';
/**
* @return string[]
*/
public static function cases(): array
{
static $constants;
if ($constants !== null) {
return $constants;
}
$ref = new ReflectionClass(self::class);
return $constants = array_values($ref->getConstants(ReflectionClassConstant::IS_PUBLIC));
}
private function __construct(private string $envVar)
{
}
public static function __callStatic(string $name, array $arguments): self
{
if (! contains(self::cases(), $name)) {
throw new InvalidArgumentException('Invalid env var: "' . $name . '"');
}
return new self($name);
}
public function loadFromEnv(mixed $default = null): mixed
{
return env($this->envVar, $default);
}
public function existsInEnv(): bool
{
return $this->loadFromEnv() !== null;
}
}

View File

@ -0,0 +1,139 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Config;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
use function putenv;
class EnvVarsTest extends TestCase
{
protected function setUp(): void
{
putenv(EnvVars::BASE_PATH . '=the_base_path');
putenv(EnvVars::DB_NAME . '=shlink');
}
protected function tearDown(): void
{
putenv(EnvVars::BASE_PATH . '=');
putenv(EnvVars::DB_NAME . '=');
}
/** @test */
public function casesReturnsTheSameListEveryTime(): void
{
$list = EnvVars::cases();
self::assertSame($list, EnvVars::cases());
self::assertSame([
EnvVars::DELETE_SHORT_URL_THRESHOLD,
EnvVars::DB_DRIVER,
EnvVars::DB_NAME,
EnvVars::DB_USER,
EnvVars::DB_PASSWORD,
EnvVars::DB_HOST,
EnvVars::DB_UNIX_SOCKET,
EnvVars::DB_PORT,
EnvVars::GEOLITE_LICENSE_KEY,
EnvVars::REDIS_SERVERS,
EnvVars::REDIS_SENTINEL_SERVICE,
EnvVars::MERCURE_PUBLIC_HUB_URL,
EnvVars::MERCURE_INTERNAL_HUB_URL,
EnvVars::MERCURE_JWT_SECRET,
EnvVars::DEFAULT_QR_CODE_SIZE,
EnvVars::DEFAULT_QR_CODE_MARGIN,
EnvVars::DEFAULT_QR_CODE_FORMAT,
EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION,
EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE,
EnvVars::RABBITMQ_ENABLED,
EnvVars::RABBITMQ_HOST,
EnvVars::RABBITMQ_PORT,
EnvVars::RABBITMQ_USER,
EnvVars::RABBITMQ_PASSWORD,
EnvVars::RABBITMQ_VHOST,
EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT,
EnvVars::DEFAULT_REGULAR_404_REDIRECT,
EnvVars::DEFAULT_BASE_URL_REDIRECT,
EnvVars::REDIRECT_STATUS_CODE,
EnvVars::REDIRECT_CACHE_LIFETIME,
EnvVars::BASE_PATH,
EnvVars::PORT,
EnvVars::TASK_WORKER_NUM,
EnvVars::WEB_WORKER_NUM,
EnvVars::ANONYMIZE_REMOTE_ADDR,
EnvVars::TRACK_ORPHAN_VISITS,
EnvVars::DISABLE_TRACK_PARAM,
EnvVars::DISABLE_TRACKING,
EnvVars::DISABLE_IP_TRACKING,
EnvVars::DISABLE_REFERRER_TRACKING,
EnvVars::DISABLE_UA_TRACKING,
EnvVars::DISABLE_TRACKING_FROM,
EnvVars::DEFAULT_SHORT_CODES_LENGTH,
EnvVars::IS_HTTPS_ENABLED,
EnvVars::DEFAULT_DOMAIN,
EnvVars::AUTO_RESOLVE_TITLES,
EnvVars::REDIRECT_APPEND_EXTRA_PATH,
EnvVars::VISITS_WEBHOOKS,
EnvVars::NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS,
], $list);
}
/**
* @test
* @dataProvider provideInvalidEnvVars
*/
public function exceptionIsThrownWhenTryingToLoadInvalidEnvVar(string $envVar): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid env var: "' . $envVar . '"');
EnvVars::{$envVar}();
}
public function provideInvalidEnvVars(): iterable
{
yield 'foo' => ['foo'];
yield 'bar' => ['bar'];
yield 'invalid' => ['invalid'];
}
/**
* @test
* @dataProvider provideExistingEnvVars
*/
public function existsInEnvReturnsExpectedValue(EnvVars $envVar, bool $exists): void
{
self::assertEquals($exists, $envVar->existsInEnv());
}
public 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 'DEFAULT_REGULAR_404_REDIRECT' => [EnvVars::DEFAULT_REGULAR_404_REDIRECT(), false];
}
/**
* @test
* @dataProvider provideEnvVarsValues
*/
public function expectedValueIsLoadedFromEnv(EnvVars $envVar, mixed $expected, mixed $default): void
{
self::assertEquals($expected, $envVar->loadFromEnv($default));
}
public 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'];
}
}