diff --git a/config/autoload/delete_short_urls.global.php b/config/autoload/delete_short_urls.global.php index a3964e71..3d562f78 100644 --- a/config/autoload/delete_short_urls.global.php +++ b/config/autoload/delete_short_urls.global.php @@ -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 [ diff --git a/config/autoload/entity-manager.global.php b/config/autoload/entity-manager.global.php index c83db2a8..d98d37dc 100644 --- a/config/autoload/entity-manager.global.php +++ b/config/autoload/entity-manager.global.php @@ -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(), ], }; diff --git a/config/autoload/geolite2.global.php b/config/autoload/geolite2.global.php index fd11e52a..cf1f57fc 100644 --- a/config/autoload/geolite2.global.php +++ b/config/autoload/geolite2.global.php @@ -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(), ], ]; diff --git a/config/autoload/locks.global.php b/config/autoload/locks.global.php index 16fdbbca..8b018d1e 100644 --- a/config/autoload/locks.global.php +++ b/config/autoload/locks.global.php @@ -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() ? 'local_lock_store' : 'redis_lock_store', 'redis_lock_store' => Lock\Store\RedisStore::class, 'local_lock_store' => Lock\Store\FlockStore::class, diff --git a/config/autoload/mercure.global.php b/config/autoload/mercure.global.php index 7eb356ab..ba261369 100644 --- a/config/autoload/mercure.global.php +++ b/config/autoload/mercure.global.php @@ -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', ], diff --git a/config/autoload/qr-codes.global.php b/config/autoload/qr-codes.global.php index 7940ad18..d72198af 100644 --- a/config/autoload/qr-codes.global.php +++ b/config/autoload/qr-codes.global.php @@ -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, + ), ], ]; diff --git a/config/autoload/rabbit.global.php b/config/autoload/rabbit.global.php index adf304c8..faa5f569 100644 --- a/config/autoload/rabbit.global.php +++ b/config/autoload/rabbit.global.php @@ -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' => [ diff --git a/config/autoload/redirects.global.php b/config/autoload/redirects.global.php index 20cde8eb..08439b2a 100644 --- a/config/autoload/redirects.global.php +++ b/config/autoload/redirects.global.php @@ -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, + ), ], ]; diff --git a/config/autoload/redis.global.php b/config/autoload/redis.global.php index 7af209d6..6bb1961e 100644 --- a/config/autoload/redis.global.php +++ b/config/autoload/redis.global.php @@ -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(), ], ], ], diff --git a/config/autoload/router.global.php b/config/autoload/router.global.php index 55397e27..fd1f9525 100644 --- a/config/autoload/router.global.php +++ b/config/autoload/router.global.php @@ -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, diff --git a/config/autoload/swoole.global.php b/config/autoload/swoole.global.php index d5a6fd55..9d2c423f 100644 --- a/config/autoload/swoole.global.php +++ b/config/autoload/swoole.global.php @@ -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), ], ], diff --git a/config/autoload/tracking.global.php b/config/autoload/tracking.global.php index 2dc23890..b2596830 100644 --- a/config/autoload/tracking.global.php +++ b/config/autoload/tracking.global.php @@ -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(), ], ]; diff --git a/config/autoload/url-shortener.global.php b/config/autoload/url-shortener.global.php index d5b4bfe5..25de914a 100644 --- a/config/autoload/url-shortener.global.php +++ b/config/autoload/url-shortener.global.php @@ -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), ], ]; diff --git a/config/autoload/webhooks.global.php b/config/autoload/webhooks.global.php index 6bbbfbda..8e768e39 100644 --- a/config/autoload/webhooks.global.php +++ b/config/autoload/webhooks.global.php @@ -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), ], ]; diff --git a/config/config.php b/config/config.php index 0adb0208..3dad2105 100644 --- a/config/config.php +++ b/config/config.php @@ -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, diff --git a/module/Core/src/Config/EnvVars.php b/module/Core/src/Config/EnvVars.php new file mode 100644 index 00000000..e3771e4c --- /dev/null +++ b/module/Core/src/Config/EnvVars.php @@ -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; + } +}