Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
40.48% |
17 / 42 |
|
37.50% |
3 / 8 |
CRAP | |
0.00% |
0 / 1 |
YamlFormat | |
40.48% |
17 / 42 |
|
37.50% |
3 / 8 |
95.13 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
decode | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
3.14 | |||
isParserAvailable | |
60.00% |
3 / 5 |
|
0.00% |
0 / 1 |
5.02 | |||
parseWith | |
60.00% |
3 / 5 |
|
0.00% |
0 / 1 |
5.02 | |||
parseWithPhp | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
6 | |||
parseWithSymfony | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
supportsFileExtension | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
__toString | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Settings\Source\Format; |
4 | |
5 | use InvalidArgumentException; |
6 | use LogicException; |
7 | use Symfony\Component\Yaml\Exception\ParseException; |
8 | use Symfony\Component\Yaml\Yaml; |
9 | use UnexpectedValueException; |
10 | use Wikimedia\AtEase\AtEase; |
11 | |
12 | class YamlFormat implements SettingsFormat { |
13 | |
14 | public const PARSER_PHP_YAML = 'php-yaml'; |
15 | |
16 | public const PARSER_SYMFONY = 'symfony'; |
17 | |
18 | /** @var string[] */ |
19 | private $useParsers; |
20 | |
21 | /** |
22 | * @param string[] $useParsers which parsers to try in order. |
23 | */ |
24 | public function __construct( array $useParsers = [ self::PARSER_PHP_YAML, self::PARSER_SYMFONY ] ) { |
25 | $this->useParsers = $useParsers; |
26 | } |
27 | |
28 | public function decode( string $data ): array { |
29 | foreach ( $this->useParsers as $parser ) { |
30 | if ( self::isParserAvailable( $parser ) ) { |
31 | return $this->parseWith( $parser, $data ); |
32 | } |
33 | } |
34 | throw new LogicException( 'No parser available' ); |
35 | } |
36 | |
37 | /** |
38 | * Check whether a specific YAML parser is available. |
39 | * |
40 | * @param string $parser one of the PARSER_* constants. |
41 | * @return bool |
42 | */ |
43 | public static function isParserAvailable( string $parser ): bool { |
44 | switch ( $parser ) { |
45 | case self::PARSER_PHP_YAML: |
46 | return function_exists( 'yaml_parse' ); |
47 | case self::PARSER_SYMFONY: |
48 | return true; |
49 | default: |
50 | throw new InvalidArgumentException( 'Unknown parser: ' . $parser ); |
51 | } |
52 | } |
53 | |
54 | /** |
55 | * @param string $parser |
56 | * @param string $data |
57 | * @return array |
58 | */ |
59 | private function parseWith( string $parser, string $data ): array { |
60 | switch ( $parser ) { |
61 | case self::PARSER_PHP_YAML: |
62 | return $this->parseWithPhp( $data ); |
63 | case self::PARSER_SYMFONY: |
64 | return $this->parseWithSymfony( $data ); |
65 | default: |
66 | throw new InvalidArgumentException( 'Unknown parser: ' . $parser ); |
67 | } |
68 | } |
69 | |
70 | private function parseWithPhp( string $data ): array { |
71 | $previousValue = ini_set( 'yaml.decode_php', false ); |
72 | try { |
73 | $ndocs = 0; |
74 | $result = AtEase::quietCall( |
75 | 'yaml_parse', |
76 | $data, |
77 | 0, |
78 | $ndocs, |
79 | [ |
80 | /** |
81 | * Crash if provided YAML has PHP constants in it. |
82 | * We do not want to support that. |
83 | * |
84 | * @return never |
85 | */ |
86 | '!php/const' => static function () { |
87 | throw new UnexpectedValueException( |
88 | 'PHP constants are not supported' |
89 | ); |
90 | }, |
91 | ] |
92 | ); |
93 | if ( $result === false ) { |
94 | throw new UnexpectedValueException( 'Failed to parse YAML' ); |
95 | } |
96 | return $result; |
97 | } finally { |
98 | ini_set( 'yaml.decode_php', $previousValue ); |
99 | } |
100 | } |
101 | |
102 | private function parseWithSymfony( string $data ): array { |
103 | try { |
104 | return Yaml::parse( $data, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE ); |
105 | } catch ( ParseException $e ) { |
106 | throw new UnexpectedValueException( |
107 | 'Failed to parse YAML ' . $e->getMessage() |
108 | ); |
109 | } |
110 | } |
111 | |
112 | public static function supportsFileExtension( string $ext ): bool { |
113 | $ext = strtolower( $ext ); |
114 | return $ext === 'yml' || $ext === 'yaml'; |
115 | } |
116 | |
117 | public function __toString() { |
118 | return 'YAML'; |
119 | } |
120 | } |