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 | } |