Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.55% covered (success)
98.55%
68 / 69
91.67% covered (success)
91.67%
11 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
Application
98.55% covered (success)
98.55%
68 / 69
91.67% covered (success)
91.67%
11 / 12
43
0.00% covered (danger)
0.00%
0 / 1
 initialize
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 initializeMaintenanceMode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 initializeSource
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
 initializeLogger
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
11
 initializeCaptcha
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
7
 initializeMiddleware
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
12
 reinitializeMiddlewareConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLoggerConfig
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 getRateLimit
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 getRequestLimits
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getIpBlacklist
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getAccessUnpublishedOnDomain
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace BO\Zmscitizenapi;
6
7use BO\Slim\Traits\CacheInitializationTrait;
8use Psr\SimpleCache\CacheInterface;
9
10/**
11 * @SuppressWarnings(PHPMD.TooManyFields)
12 * @SuppressWarnings(PHPMD.NPathComplexity)
13 * @TODO: Refactor this class into smaller focused classes (LoggerInitializer, MiddlewareInitializer) to reduce complexity
14 */
15class Application extends \BO\Slim\Application
16{
17    use CacheInitializationTrait;
18
19    public const IDENTIFIER = 'zms';
20    public const MODULE_NAME = 'zmscitizenapi';
21    public static string $source_name = "dldb,zms";
22    public static $http = null;
23    public static array $http_curl_config = [];
24    public static ?CacheInterface $cache = null;
25    // Cache config
26    public static string $CACHE_DIR;
27    public static int $SOURCE_CACHE_TTL;
28    public static bool $MAINTENANCE_MODE_ENABLED;
29    // Logger config
30
31    public static int $LOGGER_MAX_REQUESTS;
32    public static int $LOGGER_MAX_ERROR_REQUESTS;
33    public static int $LOGGER_RESPONSE_LENGTH;
34    public static int $LOGGER_STACK_LINES;
35    public static int $LOGGER_MESSAGE_SIZE;
36    public static int $LOGGER_CACHE_TTL;
37    public static int $LOGGER_MAX_RETRIES;
38    public static int $LOGGER_BACKOFF_MIN;
39    public static int $LOGGER_BACKOFF_MAX;
40    public static int $LOGGER_LOCK_TIMEOUT;
41    // Captcha config
42    public static bool $CAPTCHA_ENABLED;
43    public static string $CAPTCHA_TOKEN_SECRET;
44    public static int $CAPTCHA_TOKEN_TTL;
45    public static string $ALTCHA_CAPTCHA_SITE_KEY;
46    public static string $ALTCHA_CAPTCHA_SITE_SECRET;
47    public static string $ALTCHA_CAPTCHA_ENDPOINT_CHALLENGE;
48    public static string $ALTCHA_CAPTCHA_ENDPOINT_VERIFY;
49    // Rate limiting config
50    public static int $RATE_LIMIT_MAX_REQUESTS;
51    public static int $RATE_LIMIT_CACHE_TTL;
52    public static int $RATE_LIMIT_MAX_RETRIES;
53    public static int $RATE_LIMIT_BACKOFF_MIN;
54    public static int $RATE_LIMIT_BACKOFF_MAX;
55    public static int $RATE_LIMIT_LOCK_TIMEOUT;
56    // Request limits config
57    public static int $MAX_REQUEST_SIZE;
58    public static int $MAX_STRING_LENGTH;
59    public static int $MAX_RECURSION_DEPTH;
60    // IP Filter config
61    public static string $IP_BLACKLIST;
62
63    public static string $ACCESS_UNPUBLISHED_ON_DOMAIN;
64
65    public static function initialize(): void
66    {
67        self::initializeMaintenanceMode();
68        self::initializeSource();
69        self::initializeLogger();
70        self::initializeCaptcha();
71        self::initializeCache(__DIR__ . '/cache');
72        self::initializeMiddleware();
73    }
74
75    private static function initializeMaintenanceMode(): void
76    {
77        self::$MAINTENANCE_MODE_ENABLED = filter_var(getenv('MAINTENANCE_ENABLED'), FILTER_VALIDATE_BOOLEAN);
78    }
79
80    private static function initializeSource(): void
81    {
82        $fromEnv = getenv('SOURCE_NAME');
83        if (is_string($fromEnv) && trim($fromEnv) !== '') {
84            self::$source_name = trim($fromEnv);
85        }
86    }
87
88    /**
89     * @SuppressWarnings(PHPMD.NPathComplexity)
90     * @TODO: Extract logger initialization logic into a dedicated LoggerInitializer class
91     */
92    private static function initializeLogger(): void
93    {
94        self::$LOGGER_MAX_REQUESTS = (int) (getenv('ZMS_CITIZENAPI_LOGGER_MAX_REQUESTS') ?: 1000);
95        self::$LOGGER_MAX_ERROR_REQUESTS = (int) (getenv('ZMS_CITIZENAPI_LOGGER_MAX_ERROR_REQUESTS') ?: 0);
96        self::$LOGGER_RESPONSE_LENGTH = (int) (getenv('ZMS_CITIZENAPI_LOGGER_RESPONSE_LENGTH') ?: 1048576);
97        // 1MB
98        self::$LOGGER_STACK_LINES = (int) (getenv('ZMS_CITIZENAPI_LOGGER_STACK_LINES') ?: 20);
99        self::$LOGGER_MESSAGE_SIZE = (int) (getenv('ZMS_CITIZENAPI_LOGGER_MESSAGE_SIZE') ?: 8192);
100        // 8KB
101        self::$LOGGER_CACHE_TTL = (int) (getenv('ZMS_CITIZENAPI_LOGGER_CACHE_TTL') ?: 60);
102        self::$LOGGER_MAX_RETRIES = (int) (getenv('ZMS_CITIZENAPI_LOGGER_MAX_RETRIES') ?: 3);
103        self::$LOGGER_BACKOFF_MIN = (int) (getenv('ZMS_CITIZENAPI_LOGGER_BACKOFF_MIN') ?: 100);
104        self::$LOGGER_BACKOFF_MAX = (int) (getenv('ZMS_CITIZENAPI_LOGGER_BACKOFF_MAX') ?: 1000);
105        self::$LOGGER_LOCK_TIMEOUT = (int) (getenv('ZMS_CITIZENAPI_LOGGER_LOCK_TIMEOUT') ?: 5);
106        \BO\Slim\LoggerService::configure(self::getLoggerConfig());
107    }
108
109    private static function initializeCaptcha(): void
110    {
111        self::$CAPTCHA_ENABLED = filter_var(getenv('CAPTCHA_ENABLED'), FILTER_VALIDATE_BOOLEAN);
112        self::$CAPTCHA_TOKEN_SECRET = getenv('CAPTCHA_TOKEN_SECRET') ?: '';
113        self::$CAPTCHA_TOKEN_TTL = (int) getenv('CAPTCHA_TOKEN_TTL') ?: 300;
114        self::$ALTCHA_CAPTCHA_SITE_KEY = getenv('ALTCHA_CAPTCHA_SITE_KEY') ?: '';
115        self::$ALTCHA_CAPTCHA_SITE_SECRET = getenv('ALTCHA_CAPTCHA_SITE_SECRET') ?: '';
116        self::$ALTCHA_CAPTCHA_ENDPOINT_CHALLENGE = getenv('ALTCHA_CAPTCHA_ENDPOINT_CHALLENGE')
117            ?: 'https://captcha.muenchen.de/api/v1/captcha/challenge';
118        self::$ALTCHA_CAPTCHA_ENDPOINT_VERIFY = getenv('ALTCHA_CAPTCHA_ENDPOINT_VERIFY')
119            ?: 'https://captcha.muenchen.de/api/v1/captcha/verify';
120    }
121
122    /**
123     * @SuppressWarnings(PHPMD.NPathComplexity)
124     * @TODO: Extract middleware initialization logic into a dedicated MiddlewareInitializer class
125     */
126    private static function initializeMiddleware(): void
127    {
128        // Rate limiting
129        self::$RATE_LIMIT_MAX_REQUESTS = (int) (getenv('RATE_LIMIT_MAX_REQUESTS') ?: 60);
130        self::$RATE_LIMIT_CACHE_TTL = (int) (getenv('RATE_LIMIT_CACHE_TTL') ?: 60);
131        self::$RATE_LIMIT_MAX_RETRIES = (int) (getenv('RATE_LIMIT_MAX_RETRIES') ?: 3);
132        self::$RATE_LIMIT_BACKOFF_MIN = (int) (getenv('RATE_LIMIT_BACKOFF_MIN') ?: 10);
133        self::$RATE_LIMIT_BACKOFF_MAX = (int) (getenv('RATE_LIMIT_BACKOFF_MAX') ?: 50);
134        self::$RATE_LIMIT_LOCK_TIMEOUT = (int) (getenv('RATE_LIMIT_LOCK_TIMEOUT') ?: 1);
135        // Request limits
136        self::$MAX_REQUEST_SIZE = (int) (getenv('MAX_REQUEST_SIZE') ?: 10485760);
137        // 10MB
138        self::$MAX_STRING_LENGTH = (int) (getenv('MAX_STRING_LENGTH') ?: 32768);
139        // 32KB
140        self::$MAX_RECURSION_DEPTH = (int) (getenv('MAX_RECURSION_DEPTH') ?: 10);
141        // IP Filter
142        self::$IP_BLACKLIST = getenv('IP_BLACKLIST') ?: '';
143
144        self::$ACCESS_UNPUBLISHED_ON_DOMAIN = getenv('ACCESS_UNPUBLISHED_ON_DOMAIN') ?: '';
145    }
146
147    public static function reinitializeMiddlewareConfig(): void
148    {
149        self::initializeMiddleware();
150    }
151
152    public static function getLoggerConfig(): array
153    {
154        return [
155            'maxRequests' => self::$LOGGER_MAX_REQUESTS,
156            'maxErrorRequests' => self::$LOGGER_MAX_ERROR_REQUESTS,
157            'responseLength' => self::$LOGGER_RESPONSE_LENGTH,
158            'stackLines' => self::$LOGGER_STACK_LINES,
159            'messageSize' => self::$LOGGER_MESSAGE_SIZE,
160            'cacheTtl' => self::$LOGGER_CACHE_TTL,
161            'maxRetries' => self::$LOGGER_MAX_RETRIES,
162            'backoffMin' => self::$LOGGER_BACKOFF_MIN,
163            'backoffMax' => self::$LOGGER_BACKOFF_MAX,
164            'lockTimeout' => self::$LOGGER_LOCK_TIMEOUT
165        ];
166    }
167
168    public static function getRateLimit(): array
169    {
170        return [
171            'maxRequests' => self::$RATE_LIMIT_MAX_REQUESTS,
172            'cacheExpiry' => self::$RATE_LIMIT_CACHE_TTL,
173            'maxRetries' => self::$RATE_LIMIT_MAX_RETRIES,
174            'backoffMin' => self::$RATE_LIMIT_BACKOFF_MIN,
175            'backoffMax' => self::$RATE_LIMIT_BACKOFF_MAX,
176            'lockTimeout' => self::$RATE_LIMIT_LOCK_TIMEOUT
177        ];
178    }
179
180    public static function getRequestLimits(): array
181    {
182        return [
183            'maxSize' => self::$MAX_REQUEST_SIZE,
184            'maxStringLength' => self::$MAX_STRING_LENGTH,
185            'maxRecursionDepth' => self::$MAX_RECURSION_DEPTH
186        ];
187    }
188
189    public static function getIpBlacklist(): string
190    {
191        return self::$IP_BLACKLIST ?: '';
192    }
193
194    public static function getAccessUnpublishedOnDomain(): ?string
195    {
196        return self::$ACCESS_UNPUBLISHED_ON_DOMAIN ?: null;
197    }
198}
199
200Application::initialize();