Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
97.62% |
123 / 126 |
|
92.59% |
25 / 27 |
CRAP | |
0.00% |
0 / 1 |
PhpHandler | |
97.62% |
123 / 126 |
|
92.59% |
25 / 27 |
55 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
getIniSetting | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPhpVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getPhpMajorVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPhpMinorVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPhpReleaseVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPhpExtraVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDateTime | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getErrorSettings | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getErrorSetting | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getErrorReportingList | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasErrorLevel | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getLimits | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getLimit | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getExtensions | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasExtension | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDisabledFunctions | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasDisabledFunction | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDisabledClasses | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasDisabledClass | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
prepare | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
1 | |||
prepareHeaderAsString | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
prepareAsString | |
100.00% |
27 / 27 |
|
100.00% |
1 / 1 |
7 | |||
log | |
100.00% |
33 / 33 |
|
100.00% |
1 / 1 |
15 | |||
parseErrorSettings | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
5.03 | |||
parseLimits | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
parseDisabled | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
3.33 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Debug\Handler; |
15 | |
16 | use Pop\Log\Logger; |
17 | |
18 | /** |
19 | * Debug time handler class |
20 | * |
21 | * @category Pop |
22 | * @package Pop\Debug |
23 | * @author Nick Sagona, III <dev@nolainteractive.com> |
24 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
25 | * @license http://www.popphp.org/license New BSD License |
26 | * @version 2.2.0 |
27 | */ |
28 | class PhpHandler extends AbstractHandler |
29 | { |
30 | |
31 | /** |
32 | * PHP version |
33 | * @var string |
34 | */ |
35 | protected string $version = PHP_VERSION; |
36 | /** |
37 | * PHP major version |
38 | * @var int |
39 | */ |
40 | protected int $majorVersion = PHP_MAJOR_VERSION; |
41 | |
42 | /** |
43 | * PHP minor version |
44 | * @var int |
45 | */ |
46 | protected int $minorVersion = PHP_MINOR_VERSION; |
47 | |
48 | /** |
49 | * PHP release version |
50 | * @var int |
51 | */ |
52 | protected int $releaseVersion = PHP_RELEASE_VERSION; |
53 | |
54 | /** |
55 | * PHP version extra |
56 | * @var string |
57 | */ |
58 | protected string $extraVersion = PHP_EXTRA_VERSION; |
59 | |
60 | /** |
61 | * PHP date/time |
62 | * @var ?string |
63 | */ |
64 | protected ?string $dateTime = null; |
65 | |
66 | /** |
67 | * PHP error settings |
68 | * @var array |
69 | */ |
70 | protected array $errorSettings = [ |
71 | 'error_reporting_list' => [], |
72 | 'error_reporting' => null, |
73 | 'display_errors' => null, |
74 | 'display_startup_errors' => null, |
75 | 'log_errors' => null, |
76 | ]; |
77 | |
78 | /** |
79 | * PHP limits |
80 | * @var array |
81 | */ |
82 | protected array $limits = [ |
83 | 'max_execution_time' => null, |
84 | 'max_input_time' => null, |
85 | 'max_input_nesting_level' => null, |
86 | 'max_input_vars' => null, |
87 | 'post_max_size' => null, |
88 | 'file_uploads' => null, |
89 | 'upload_max_filesize' => null, |
90 | 'max_file_uploads' => null, |
91 | ]; |
92 | |
93 | /** |
94 | * PHP extensions |
95 | * @var array |
96 | */ |
97 | protected array $extensions = []; |
98 | |
99 | /** |
100 | * PHP disabled classes |
101 | * @var array |
102 | */ |
103 | protected array $disabledFunctions = []; |
104 | |
105 | /** |
106 | * PHP disabled classes |
107 | * @var array |
108 | */ |
109 | protected array $disabledClasses = []; |
110 | |
111 | /** |
112 | * PHP error reporting constants |
113 | * @var array |
114 | */ |
115 | protected array $errorReporting = [ |
116 | E_ALL => 'E_ALL', |
117 | E_USER_DEPRECATED => 'E_USER_DEPRECATED', |
118 | E_DEPRECATED => 'E_DEPRECATED', |
119 | E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', |
120 | E_STRICT => 'E_STRICT', |
121 | E_USER_NOTICE => 'E_USER_NOTICE', |
122 | E_USER_WARNING => 'E_USER_WARNING', |
123 | E_USER_ERROR => 'E_USER_ERROR', |
124 | E_COMPILE_WARNING => 'E_COMPILE_WARNING', |
125 | E_COMPILE_ERROR => 'E_COMPILE_ERROR', |
126 | E_CORE_WARNING => 'E_CORE_WARNING', |
127 | E_CORE_ERROR => 'E_CORE_ERROR', |
128 | E_NOTICE => 'E_NOTICE', |
129 | E_PARSE => 'E_PARSE', |
130 | E_WARNING => 'E_WARNING', |
131 | E_ERROR => 'E_ERROR', |
132 | ]; |
133 | |
134 | /** |
135 | * Constructor |
136 | * |
137 | * Instantiate a PHP handler object |
138 | * |
139 | * @param ?string $name |
140 | * @param ?Logger $logger |
141 | * @param array $loggingParams |
142 | */ |
143 | public function __construct(?string $name = null, ?Logger $logger = null, array $loggingParams = []) |
144 | { |
145 | parent::__construct($name, $logger, $loggingParams); |
146 | |
147 | $this->parseErrorSettings(); |
148 | $this->parseLimits(); |
149 | $this->parseDisabled(); |
150 | |
151 | $this->dateTime = ini_get('date.timezone'); |
152 | $this->extensions = array_map('strtolower', get_loaded_extensions()); |
153 | |
154 | sort($this->extensions); |
155 | } |
156 | |
157 | /** |
158 | * Get PHP INI setting |
159 | * |
160 | * @param string $setting |
161 | * @return mixed |
162 | */ |
163 | public function getIniSetting(string $setting): mixed |
164 | { |
165 | return ini_get($setting); |
166 | } |
167 | |
168 | /** |
169 | * Get full PHP version |
170 | * |
171 | * @param bool $withExtra |
172 | * @return string |
173 | */ |
174 | public function getPhpVersion(bool $withExtra = true): string |
175 | { |
176 | return (!$withExtra) ? str_replace($this->extraVersion, '', $this->version) : $this->version; |
177 | } |
178 | |
179 | /** |
180 | * Get PHP major version |
181 | * |
182 | * @return int |
183 | */ |
184 | public function getPhpMajorVersion(): int |
185 | { |
186 | return $this->majorVersion; |
187 | } |
188 | |
189 | /** |
190 | * Get PHP minor version |
191 | * |
192 | * @return int |
193 | */ |
194 | public function getPhpMinorVersion(): int |
195 | { |
196 | return $this->minorVersion; |
197 | } |
198 | |
199 | /** |
200 | * Get PHP release version |
201 | * |
202 | * @return int |
203 | */ |
204 | public function getPhpReleaseVersion(): int |
205 | { |
206 | return $this->releaseVersion; |
207 | } |
208 | |
209 | /** |
210 | * Get PHP extra version |
211 | * |
212 | * @return string |
213 | */ |
214 | public function getPhpExtraVersion(): string |
215 | { |
216 | return $this->extraVersion; |
217 | } |
218 | |
219 | /** |
220 | * Get date/time |
221 | * |
222 | * @return ?string |
223 | */ |
224 | public function getDateTime(): ?string |
225 | { |
226 | return $this->dateTime; |
227 | } |
228 | |
229 | /** |
230 | * Get error settings |
231 | * |
232 | * @return array |
233 | */ |
234 | public function getErrorSettings(): array |
235 | { |
236 | return $this->errorSettings; |
237 | } |
238 | |
239 | /** |
240 | * Get error setting |
241 | * |
242 | * @param string $setting |
243 | * @return mixed |
244 | */ |
245 | public function getErrorSetting(string $setting): mixed |
246 | { |
247 | return $this->errorSettings[$setting] ?? null; |
248 | } |
249 | |
250 | /** |
251 | * Get human-readable error reporting list |
252 | * |
253 | * @return array |
254 | */ |
255 | public function getErrorReportingList(): array |
256 | { |
257 | return $this->errorSettings['error_reporting_list']; |
258 | } |
259 | |
260 | /** |
261 | * Has error level |
262 | * |
263 | * @param int $level |
264 | * @return bool |
265 | */ |
266 | public function hasErrorLevel(int $level): bool |
267 | { |
268 | return in_array($this->errorReporting[$level], $this->errorSettings['error_reporting_list']); |
269 | } |
270 | |
271 | /** |
272 | * Get limits |
273 | * |
274 | * @return array |
275 | */ |
276 | public function getLimits(): array |
277 | { |
278 | return $this->limits; |
279 | } |
280 | |
281 | /** |
282 | * Get limit |
283 | * |
284 | * @param string $limit |
285 | * @return mixed |
286 | */ |
287 | public function getLimit(string $limit): mixed |
288 | { |
289 | return $this->limits[$limit] ?? null; |
290 | } |
291 | |
292 | /** |
293 | * Get extensions |
294 | * |
295 | * @return array |
296 | */ |
297 | public function getExtensions(): array |
298 | { |
299 | return $this->extensions; |
300 | } |
301 | |
302 | /** |
303 | * Has extensions |
304 | * |
305 | * @param string $extension |
306 | * @return bool |
307 | */ |
308 | public function hasExtension(string $extension): bool |
309 | { |
310 | return in_array(strtolower($extension), $this->extensions); |
311 | } |
312 | |
313 | /** |
314 | * Get disabled functions |
315 | * |
316 | * @return array |
317 | */ |
318 | public function getDisabledFunctions(): array |
319 | { |
320 | return $this->disabledFunctions; |
321 | } |
322 | |
323 | /** |
324 | * Has disabled function |
325 | * |
326 | * @param string $function |
327 | * @return bool |
328 | */ |
329 | public function hasDisabledFunction(string $function): bool |
330 | { |
331 | return in_array($function, $this->disabledFunctions); |
332 | } |
333 | |
334 | /** |
335 | * Get disabled classes |
336 | * |
337 | * @return array |
338 | */ |
339 | public function getDisabledClasses(): array |
340 | { |
341 | return $this->disabledClasses; |
342 | } |
343 | |
344 | /** |
345 | * Has disabled class |
346 | * |
347 | * @param string $class |
348 | * @return bool |
349 | */ |
350 | public function hasDisabledClass(string $class): bool |
351 | { |
352 | return in_array($class, $this->disabledClasses); |
353 | } |
354 | |
355 | /** |
356 | * Prepare handler data for storage |
357 | * |
358 | * @return array |
359 | */ |
360 | public function prepare(): array |
361 | { |
362 | return [ |
363 | 'php_version' => $this->version, |
364 | 'php_major_version' => $this->majorVersion, |
365 | 'php_minor_version' => $this->minorVersion, |
366 | 'php_release_version' => $this->releaseVersion, |
367 | 'php_extra_version' => $this->extraVersion, |
368 | 'data_time' => $this->dateTime, |
369 | 'error_settings' => $this->errorSettings, |
370 | 'limits' => $this->limits, |
371 | 'extensions' => $this->extensions, |
372 | 'disabled_functions' => $this->disabledFunctions, |
373 | 'disabled_classes' => $this->disabledClasses, |
374 | ]; |
375 | } |
376 | |
377 | /** |
378 | * Prepare header string |
379 | * |
380 | * @return string |
381 | */ |
382 | public function prepareHeaderAsString(): string |
383 | { |
384 | $string = ((!empty($this->name)) ? $this->name . ' ' : '') . 'PHP Handler'; |
385 | $string .= PHP_EOL . str_repeat('=', strlen($string)) . PHP_EOL; |
386 | |
387 | return $string; |
388 | } |
389 | |
390 | /** |
391 | * Prepare handler data as string |
392 | * |
393 | * @return string |
394 | */ |
395 | public function prepareAsString(): string |
396 | { |
397 | $string = 'PHP Version: ' . $this->version . PHP_EOL . str_repeat('-', strlen($this->version) + 13) . PHP_EOL; |
398 | $string .= ' - Major Version: ' . $this->majorVersion . PHP_EOL; |
399 | $string .= ' - Minor Version: ' . $this->minorVersion . PHP_EOL; |
400 | $string .= ' - Release Version: ' . $this->releaseVersion . PHP_EOL; |
401 | $string .= ' - Extra Version: ' . $this->extraVersion . PHP_EOL . PHP_EOL; |
402 | |
403 | $string .= 'Date/Time:' . PHP_EOL . str_repeat('-', 10) . PHP_EOL; |
404 | $string .= ' - ' . $this->dateTime . PHP_EOL . PHP_EOL; |
405 | |
406 | $string .= 'Error Settings:' . PHP_EOL . str_repeat('-', 15) . PHP_EOL; |
407 | $string .= ' - Display Errors: ' . (($this->errorSettings['display_errors']) ? 'On' : 'Off') . PHP_EOL; |
408 | $string .= ' - Display Startup Errors: ' . (($this->errorSettings['display_startup_errors']) ? 'On' : 'Off') . PHP_EOL; |
409 | $string .= ' - Log Errors: ' . (($this->errorSettings['log_errors']) ? 'On' : 'Off') . PHP_EOL; |
410 | $string .= ' - Error Reporting: ' . $this->errorSettings['error_reporting'] . PHP_EOL; |
411 | $string .= ' - Error Reporting List: ' . implode(', ', $this->errorSettings['error_reporting_list']) . PHP_EOL . PHP_EOL; |
412 | |
413 | $string .= 'Limits:' . PHP_EOL . str_repeat('-', 7) . PHP_EOL; |
414 | $string .= ' - Max Execution Time: ' . $this->limits['max_execution_time'] . PHP_EOL; |
415 | $string .= ' - Max Input Time: ' . $this->limits['max_input_time'] . PHP_EOL; |
416 | $string .= ' - Max Input Nesting Level: ' . $this->limits['max_input_nesting_level'] . PHP_EOL; |
417 | $string .= ' - Max Input Vars: ' . $this->limits['max_input_vars'] . PHP_EOL; |
418 | $string .= ' - Post Max Size: ' . $this->limits['post_max_size'] . PHP_EOL; |
419 | $string .= ' - File Uploads: ' . (($this->limits['file_uploads']) ? 'On' : 'Off') . PHP_EOL; |
420 | $string .= ' - Upload Max Filesize: ' . $this->limits['upload_max_filesize'] . PHP_EOL; |
421 | $string .= ' - Max File Uploads: ' . $this->limits['max_file_uploads'] . PHP_EOL . PHP_EOL; |
422 | |
423 | $string .= 'Disabled Functions:' . PHP_EOL . str_repeat('-', 19) . PHP_EOL; |
424 | $string .= ' - ' . (!empty($this->disabledFunctions) ? implode(', ', $this->disabledFunctions) : '(None)') . PHP_EOL . PHP_EOL; |
425 | |
426 | $string .= 'Disabled Classes:' . PHP_EOL . str_repeat('-', 17) . PHP_EOL; |
427 | $string .= ' - ' . (!empty($this->disabledClasses) ? implode(', ', $this->disabledClasses) : '(None)') . PHP_EOL . PHP_EOL; |
428 | |
429 | return $string; |
430 | } |
431 | |
432 | /** |
433 | * Trigger handler logging |
434 | * |
435 | * @throws Exception |
436 | * @return void |
437 | */ |
438 | public function log(): void |
439 | { |
440 | if (($this->hasLogger()) && ($this->hasLoggingParams())) { |
441 | $logLevel = $this->loggingParams['level'] ?? null; |
442 | $versionLimit = $this->loggingParams['version'] ?? null; |
443 | $extensions = $this->loggingParams['extensions'] ?? null; |
444 | $useContext = $this->loggingParams['context'] ?? null; |
445 | |
446 | if ($logLevel !== null) { |
447 | $context = []; |
448 | |
449 | if (!empty($versionLimit) || !empty($extensions)) { |
450 | if (!empty($versionLimit)) { |
451 | if (version_compare($this->getPhpVersion(false), $versionLimit) == -1) { |
452 | $this->logger->log($logLevel, 'The current version of PHP ' . $this->getPhpVersion(false) . |
453 | ' is less than the required version ' . $versionLimit . '.', |
454 | $context |
455 | ); |
456 | } |
457 | } |
458 | if (!empty($extensions)) { |
459 | if (is_string($extensions)) { |
460 | if (str_contains($extensions, ',')) { |
461 | $extensions = array_map(function ($value) { |
462 | return strtolower(trim($value)); |
463 | }, explode(',', $extensions)); |
464 | } else { |
465 | $extensions = [strtolower($extensions)]; |
466 | } |
467 | } |
468 | $extensionDiff = array_diff($extensions, $this->extensions); |
469 | if (!empty($extensionDiff)) { |
470 | $this->logger->log($logLevel, 'The current of PHP extensions are required but not active: ' . |
471 | implode(', ', $extensionDiff), |
472 | $context |
473 | ); |
474 | } |
475 | } |
476 | } else { |
477 | $context['php_info'] = (($useContext !== null) && (strtolower($useContext) == 'text')) ? |
478 | $this->prepareAsString() : $this->prepare(); |
479 | |
480 | if (is_string($useContext)) { |
481 | $context['format'] = $useContext; |
482 | } |
483 | |
484 | $this->logger->log($logLevel, "PHP info has been logged", $context); |
485 | } |
486 | } else { |
487 | throw new Exception('Error: The log level parameter was not set.'); |
488 | } |
489 | } |
490 | } |
491 | |
492 | /** |
493 | * Get PHP error settings |
494 | * |
495 | * @return void |
496 | */ |
497 | public function parseErrorSettings(): void |
498 | { |
499 | $this->errorSettings['error_reporting'] = ini_get('error_reporting'); |
500 | $this->errorSettings['display_errors'] = !empty(ini_get('display_errors')); |
501 | $this->errorSettings['display_startup_errors'] = !empty(ini_get('display_startup_errors')); |
502 | $this->errorSettings['log_errors'] = !empty(ini_get('log_errors')); |
503 | |
504 | if (!empty($this->errorSettings['error_reporting'])) { |
505 | if ($this->errorSettings['error_reporting'] == E_ALL) { |
506 | $this->errorSettings['error_reporting_list'][] = $this->errorReporting[E_ALL]; |
507 | } else { |
508 | foreach($this->errorReporting as $errorNumber => $errorName) { |
509 | if (($this->errorSettings['error_reporting'] & $errorNumber) == $errorNumber) { |
510 | $this->errorSettings['error_reporting_list'][] = $errorName; |
511 | } |
512 | } |
513 | } |
514 | } |
515 | } |
516 | |
517 | /** |
518 | * Get PHP limits |
519 | * |
520 | * @return void |
521 | */ |
522 | public function parseLimits(): void |
523 | { |
524 | $this->limits['max_execution_time'] = ini_get('max_execution_time'); |
525 | $this->limits['max_input_time'] = ini_get('max_input_time'); |
526 | $this->limits['max_input_nesting_level'] = ini_get('max_input_nesting_level'); |
527 | $this->limits['max_input_vars'] = ini_get('max_input_vars'); |
528 | $this->limits['post_max_size'] = ini_get('post_max_size'); |
529 | $this->limits['file_uploads'] = ini_get('file_uploads'); |
530 | $this->limits['upload_max_filesize'] = ini_get('upload_max_filesize'); |
531 | $this->limits['max_file_uploads'] = ini_get('max_file_uploads'); |
532 | } |
533 | |
534 | /** |
535 | * Get PHP disabled functions and classes |
536 | * |
537 | * @return void |
538 | */ |
539 | public function parseDisabled(): void |
540 | { |
541 | $disabledFunctions = ini_get('disable_functions'); |
542 | $disabledClasses = ini_get('disable_classes'); |
543 | |
544 | if (!empty($disabledFunctions)) { |
545 | $this->disabledFunctions = array_map('trim', explode(',', $disabledFunctions)); |
546 | } |
547 | if (!empty($disabledClasses)) { |
548 | $this->disabledClasses = array_map('trim', explode(',', $disabledClasses)); |
549 | } |
550 | } |
551 | |
552 | } |