Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
97.78% |
44 / 45 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
| Parser | |
97.78% |
44 / 45 |
|
80.00% |
4 / 5 |
23 | |
0.00% |
0 / 1 |
| parse | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
| parseOrThrow | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
| checkConfig | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
5.07 | |||
| addChild | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
| getValue | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
12 | |||
| ksortRecursive | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
| 1 | <?php declare(strict_types=1); |
| 2 | /* |
| 3 | * This file is part of Aplus Framework Config Library. |
| 4 | * |
| 5 | * (c) Natan Felles <natanfelles@gmail.com> |
| 6 | * |
| 7 | * For the full copyright and license information, please view the LICENSE |
| 8 | * file that was distributed with this source code. |
| 9 | */ |
| 10 | namespace Framework\Config\Parsers; |
| 11 | |
| 12 | use Closure; |
| 13 | use JetBrains\PhpStorm\Pure; |
| 14 | use SensitiveParameter; |
| 15 | |
| 16 | /** |
| 17 | * Class Parser. |
| 18 | * |
| 19 | * @package config |
| 20 | */ |
| 21 | abstract class Parser |
| 22 | { |
| 23 | /** |
| 24 | * Parse the config output. |
| 25 | * |
| 26 | * @param mixed $config |
| 27 | * |
| 28 | * @throws ParserException If config is invalid or data can not be parsed |
| 29 | * |
| 30 | * @return array<int|string,mixed> The parsed configs |
| 31 | */ |
| 32 | abstract public static function parse(mixed $config) : array; |
| 33 | |
| 34 | /** |
| 35 | * @param Closure $function |
| 36 | * |
| 37 | * @throws ParserException If config data can not be parsed |
| 38 | * |
| 39 | * @return array<int|string,mixed> |
| 40 | */ |
| 41 | protected static function parseOrThrow(Closure $function) : array |
| 42 | { |
| 43 | \set_error_handler(static function ($severity, $message, $file, $line) : void { |
| 44 | $message = static::class . ': ' . $message; |
| 45 | throw new ParserException($message, $severity, $severity, $file, $line); |
| 46 | }); |
| 47 | $result = $function(); |
| 48 | \restore_error_handler(); |
| 49 | return $result; |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * Check for config issues. |
| 54 | * |
| 55 | * @param mixed $config The parser configuration |
| 56 | * |
| 57 | * @throws ParserException If config is invalid |
| 58 | */ |
| 59 | protected static function checkConfig(#[SensitiveParameter] mixed $config) : void |
| 60 | { |
| 61 | if ( ! \is_string($config)) { |
| 62 | throw new ParserException(static::class . ' config must be a string'); |
| 63 | } |
| 64 | $file = \realpath($config); |
| 65 | if ($file === false || ! \is_file($file)) { |
| 66 | throw new ParserException('File not found: ' . $config); |
| 67 | } |
| 68 | if ( ! \is_readable($file)) { |
| 69 | throw new ParserException('File is not readable: ' . $config); |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | /** |
| 74 | * Recursively adds childs to an array tree. |
| 75 | * |
| 76 | * @param array<int|string,mixed> $parent The main array, where the childs will be added |
| 77 | * @param array<int|string,mixed> $childs Childs to add |
| 78 | * @param mixed $value The last child value |
| 79 | */ |
| 80 | protected static function addChild(array &$parent, array $childs, mixed $value) : void |
| 81 | { |
| 82 | $key = \array_shift($childs); |
| 83 | $parent[$key] = []; |
| 84 | if ($childs === []) { |
| 85 | $parent[$key] = $value; |
| 86 | return; |
| 87 | } |
| 88 | static::addChild($parent[$key], $childs, $value); |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Interprets a string value and returns it with a PHP type. |
| 93 | * |
| 94 | * @param string $value The input value |
| 95 | * |
| 96 | * @return array<int|string,mixed>|bool|float|int|string|null The output value |
| 97 | */ |
| 98 | #[Pure] |
| 99 | protected static function getValue(string $value) : array|bool|int|float|string|null |
| 100 | { |
| 101 | $value = \trim($value); |
| 102 | $lowerValue = \strtolower($value); |
| 103 | if ($lowerValue === 'true') { |
| 104 | return true; |
| 105 | } |
| 106 | if ($lowerValue === 'false') { |
| 107 | return false; |
| 108 | } |
| 109 | if ($lowerValue === 'null') { |
| 110 | return null; |
| 111 | } |
| 112 | if (\is_numeric($value) && $value >= \PHP_INT_MIN && $value <= \PHP_INT_MAX) { |
| 113 | return \str_contains($value, '.') ? (float) $value : (int) $value; |
| 114 | } |
| 115 | if (\str_starts_with($value, '"') && \str_ends_with($value, '"')) { |
| 116 | $value = \substr($value, 1, -1); |
| 117 | return \strtr($value, [ |
| 118 | '\"' => '"', |
| 119 | '\\\\' => '\\', |
| 120 | ]); |
| 121 | } |
| 122 | if (\str_starts_with($value, "'") && \str_ends_with($value, "'")) { |
| 123 | return \substr($value, 1, -1); |
| 124 | } |
| 125 | return $value; |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * Sort arrays by keys recursively. |
| 130 | * |
| 131 | * @param mixed $value The input value |
| 132 | * |
| 133 | * @return mixed The output value (sorted by keys if the $value is an array) |
| 134 | */ |
| 135 | protected static function ksortRecursive(mixed $value) : mixed |
| 136 | { |
| 137 | if ( ! \is_array($value)) { |
| 138 | return $value; |
| 139 | } |
| 140 | \ksort($value); |
| 141 | foreach ($value as &$val) { |
| 142 | $val = static::ksortRecursive($val); |
| 143 | } |
| 144 | return $value; |
| 145 | } |
| 146 | } |