Make phan stricter about scalar types by setting scalar_implicit_cast to false (the default in mediawiki-phan-config) Bug: T242536 Bug: T301991 Change-Id: Ia2fe30b17804186571722e728578121c8b75d455
120 lines
2.9 KiB
PHP
120 lines
2.9 KiB
PHP
<?php
|
|
|
|
namespace MediaWiki\Settings\Source\Format;
|
|
|
|
use LogicException;
|
|
use Symfony\Component\Yaml\Exception\ParseException;
|
|
use Symfony\Component\Yaml\Yaml;
|
|
use UnexpectedValueException;
|
|
use Wikimedia\AtEase\AtEase;
|
|
|
|
class YamlFormat implements SettingsFormat {
|
|
|
|
public const PARSER_PHP_YAML = 'php-yaml';
|
|
|
|
public const PARSER_SYMFONY = 'symfony';
|
|
|
|
/** @var string[] */
|
|
private $useParsers;
|
|
|
|
/**
|
|
* @param string[] $useParsers which parsers to try in order.
|
|
*/
|
|
public function __construct( array $useParsers = [ self::PARSER_PHP_YAML, self::PARSER_SYMFONY ] ) {
|
|
$this->useParsers = $useParsers;
|
|
}
|
|
|
|
public function decode( string $data ): array {
|
|
foreach ( $this->useParsers as $parser ) {
|
|
if ( self::isParserAvailable( $parser ) ) {
|
|
return $this->parseWith( $parser, $data );
|
|
}
|
|
}
|
|
throw new LogicException( 'No parser available' );
|
|
}
|
|
|
|
/**
|
|
* Check whether a specific YAML parser is available.
|
|
*
|
|
* @param string $parser one of the PARSER_* constants.
|
|
* @return bool
|
|
*/
|
|
public static function isParserAvailable( string $parser ): bool {
|
|
switch ( $parser ) {
|
|
case self::PARSER_PHP_YAML:
|
|
return function_exists( 'yaml_parse' );
|
|
case self::PARSER_SYMFONY:
|
|
return true;
|
|
default:
|
|
throw new LogicException( 'Unknown parser: ' . $parser );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $parser
|
|
* @param string $data
|
|
* @return array
|
|
*/
|
|
private function parseWith( string $parser, string $data ): array {
|
|
switch ( $parser ) {
|
|
case self::PARSER_PHP_YAML:
|
|
return $this->parseWithPhp( $data );
|
|
case self::PARSER_SYMFONY:
|
|
return $this->parseWithSymfony( $data );
|
|
default:
|
|
throw new LogicException( 'Unknown parser: ' . $parser );
|
|
}
|
|
}
|
|
|
|
private function parseWithPhp( string $data ): array {
|
|
// @phan-suppress-next-line PhanTypeMismatchArgumentInternal Scalar okay with php8.1
|
|
$previousValue = ini_set( 'yaml.decode_php', false );
|
|
try {
|
|
$ndocs = 0;
|
|
$result = AtEase::quietCall(
|
|
'yaml_parse',
|
|
$data,
|
|
0,
|
|
$ndocs,
|
|
[
|
|
/**
|
|
* Crash if provided YAML has PHP constants in it.
|
|
* We do not want to support that.
|
|
*
|
|
* @return never
|
|
*/
|
|
'!php/const' => static function () {
|
|
throw new UnexpectedValueException(
|
|
'PHP constants are not supported'
|
|
);
|
|
},
|
|
]
|
|
);
|
|
if ( $result === false ) {
|
|
throw new UnexpectedValueException( 'Failed to parse YAML' );
|
|
}
|
|
return $result;
|
|
} finally {
|
|
ini_set( 'yaml.decode_php', $previousValue );
|
|
}
|
|
}
|
|
|
|
private function parseWithSymfony( string $data ): array {
|
|
try {
|
|
return Yaml::parse( $data, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE );
|
|
} catch ( ParseException $e ) {
|
|
throw new UnexpectedValueException(
|
|
'Failed to parse YAML ' . $e->getMessage()
|
|
);
|
|
}
|
|
}
|
|
|
|
public static function supportsFileExtension( string $ext ): bool {
|
|
$ext = strtolower( $ext );
|
|
return $ext === 'yml' || $ext === 'yaml';
|
|
}
|
|
|
|
public function __toString() {
|
|
return 'YAML';
|
|
}
|
|
}
|