Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.00% covered (success)
96.00%
24 / 25
87.50% covered (warning)
87.50%
7 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Env
96.00% covered (success)
96.00%
24 / 25
87.50% covered (warning)
87.50%
7 / 8
20
0.00% covered (danger)
0.00%
0 / 1
 load
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 require
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 validate
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
7
 string
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 bool
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 int
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isBoolean
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 isInteger
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Config;
6
7use Cosray\Exception\InvalidEnvironment;
8
9use function Cosray\env;
10
11final class Env
12{
13    /** @var list<string> */
14    private const BOOLEAN = [
15        'APP_DEBUG',
16        'SITE_SESSION_ENABLED',
17        'SESSION_COOKIE_SECURE',
18        'QUEUE_AFTER_RESPONSE',
19        'PASSWORD_BREACH_CHECK',
20    ];
21
22    /** @var list<string> */
23    private const INTEGER = [
24        'AUTH_REMEMBER_LIFETIME',
25        'SESSION_COOKIE_LIFETIME',
26        'SESSION_IDLE_TIMEOUT',
27    ];
28
29    public static function load(): self
30    {
31        return new self();
32    }
33
34    /** @param non-empty-string|list<non-empty-string> $variables */
35    public function require(string|array $variables): void
36    {
37        $missing = array_filter((array) $variables, static fn(string $key): bool => env($key) === null);
38
39        if ($missing !== []) {
40            throw new InvalidEnvironment(
41                'Missing required environment variable(s): ' . implode(', ', $missing),
42            );
43        }
44    }
45
46    public function validate(): void
47    {
48        foreach (self::BOOLEAN as $key) {
49            $value = env($key);
50
51            if ($value !== null && !self::isBoolean($value)) {
52                throw new InvalidEnvironment("Environment variable {$key} must be a boolean.");
53            }
54        }
55
56        foreach (self::INTEGER as $key) {
57            $value = env($key);
58
59            if ($value !== null && !self::isInteger($value)) {
60                throw new InvalidEnvironment("Environment variable {$key} must be an integer.");
61            }
62        }
63    }
64
65    public function string(string $key, ?string $default = null): ?string
66    {
67        $value = env($key, $default);
68
69        return $value === null ? null : (string) $value;
70    }
71
72    public function bool(string $key, bool $default): bool
73    {
74        return filter_var(env($key, $default), FILTER_VALIDATE_BOOL);
75    }
76
77    public function int(string $key, int $default): int
78    {
79        return (int) env($key, $default);
80    }
81
82    private static function isBoolean(mixed $value): bool
83    {
84        if (is_bool($value)) {
85            return true;
86        }
87
88        return (
89            is_string($value)
90                && in_array(strtolower($value), ['1', '0', 'true', 'false', 'on', 'off', 'yes', 'no'], true)
91        );
92    }
93
94    private static function isInteger(mixed $value): bool
95    {
96        return is_int($value) || is_string($value) && ctype_digit($value);
97    }
98}