summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/Translate/MessageGroupConfigurationParser.php
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
committerYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
commitfc7369835258467bf97eb64f184b93691f9a9fd5 (patch)
treedaabd60089d2dd76d9f5fb416b005fbe159c799d /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.php158
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;
+ }
+}