diff options
author | Yaco <franco@reevo.org> | 2020-06-04 11:01:00 -0300 |
---|---|---|
committer | Yaco <franco@reevo.org> | 2020-06-04 11:01:00 -0300 |
commit | fc7369835258467bf97eb64f184b93691f9a9fd5 (patch) | |
tree | daabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/extensions/Translate/MessageGroupConfigurationParser.php |
first commit
Diffstat (limited to 'www/wiki/extensions/Translate/MessageGroupConfigurationParser.php')
-rw-r--r-- | www/wiki/extensions/Translate/MessageGroupConfigurationParser.php | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/www/wiki/extensions/Translate/MessageGroupConfigurationParser.php b/www/wiki/extensions/Translate/MessageGroupConfigurationParser.php new file mode 100644 index 00000000..acb45237 --- /dev/null +++ b/www/wiki/extensions/Translate/MessageGroupConfigurationParser.php @@ -0,0 +1,158 @@ +<?php +/** + * + * @file + * @author Niklas Laxström + * @license GPL-2.0-or-later + */ + +/** + * Utility class to parse and validate message group configurations. + * @since 2014.01 + */ +class MessageGroupConfigurationParser { + protected $baseSchema; + + public function __construct() { + // Don't perform validations if library not available + if ( class_exists( RomaricDrigon\MetaYaml\MetaYaml::class ) ) { + $this->baseSchema = $this->getBaseSchema(); + } + } + + /** + * Easy to use function to get valid group configurations from YAML. Those not matching + * schema will be ignored, if schema validation is enabled. + * + * @param string $data Yaml + * @param callable|null $callback Optional callback which is called on errors. Parameters are + * document index, processed configuration and error message. + * @return array Group configurations indexed by message group id. + */ + public function getHopefullyValidConfigurations( $data, $callback = null ) { + if ( !is_callable( $callback ) ) { + $callback = function () { + /*noop*/ + }; + } + + $documents = self::getDocumentsFromYaml( $data ); + $configurations = self::parseDocuments( $documents ); + $groups = []; + + if ( is_array( $this->baseSchema ) ) { + foreach ( $configurations as $index => $config ) { + try { + $this->validate( $config ); + $groups[$config['BASIC']['id']] = $config; + } catch ( Exception $e ) { + $callback( $index, $config, $e->getMessage() ); + } + } + } else { + foreach ( $configurations as $index => $config ) { + if ( isset( $config['BASIC']['id'] ) ) { + $groups[$config['BASIC']['id']] = $config; + } else { + $callback( $index, $config, 'id is missing' ); + } + } + } + + return $groups; + } + + /** + * Given a Yaml string, returns the non-empty documents as an array. + * + * @param string $data + * @return string[] + */ + public function getDocumentsFromYaml( $data ) { + return preg_split( "/^---$/m", $data, -1, PREG_SPLIT_NO_EMPTY ); + } + + /** + * Returns group configurations from YAML documents. If there is document containing template, + * it will be merged with other configurations. + * + * @param array $documents + * @return array Unvalidated group configurations + */ + public function parseDocuments( array $documents ) { + $groups = []; + $template = []; + + foreach ( $documents as $document ) { + $document = TranslateYaml::loadString( $document ); + + if ( isset( $document['TEMPLATE'] ) ) { + $template = $document['TEMPLATE']; + } else { + $groups[] = $document; + } + } + + if ( $template ) { + foreach ( $groups as $i => $group ) { + $groups[$i] = self::mergeTemplate( $template, $group ); + // Little hack to allow aggregate groups to be defined in same file with other groups. + if ( $groups[$i]['BASIC']['class'] === 'AggregateMessageGroup' ) { + unset( $groups[$i]['FILES'] ); + } + } + } + + return $groups; + } + + public function getBaseSchema() { + return TranslateYaml::load( __DIR__ . '/data/group-yaml-schema.yaml' ); + } + + /** + * Validates group configuration against schema. + * + * @param array $config + * @throws Exception If configuration is not valid. + */ + public function validate( array $config ) { + $schema = $this->baseSchema; + + foreach ( $config as $sectionName => $section ) { + if ( !isset( $section['class'] ) ) { + continue; + } + + $class = $section['class']; + // There is no sane way to check whether *class* implements interface in PHP + if ( !method_exists( $class, 'getExtraSchema' ) ) { + continue; + } + + $extra = call_user_func( [ $class, 'getExtraSchema' ] ); + $schema = array_replace_recursive( $schema, $extra ); + } + + $schema = new RomaricDrigon\MetaYaml\MetaYaml( $schema ); + $schema->validate( $config ); + } + + /** + * Merges a document template (base) to actual definition (specific) + * @param array $base + * @param array $specific + * @return array + */ + public static function mergeTemplate( array $base, array $specific ) { + foreach ( $specific as $key => $value ) { + if ( is_array( $value ) && isset( $base[$key] ) && is_array( $base[$key] ) ) { + $base[$key] = self::mergeTemplate( $base[$key], $value ); + } else { + $base[$key] = $value; + } + } + + return $base; + } +} |