From a59d44d598b8f6a0e826db9f9bcc7aa43fa6a368 Mon Sep 17 00:00:00 2001 From: Mark Carver Date: Wed, 17 Jul 2019 14:46:28 -0500 Subject: [PATCH] Issue #3068483 by markcarver: Automatically map !translate YAML tag to TranslatableMarkup Signed-off-by: Mark Carver --- .../TaggedSerializationInterface.php | 28 ++++++++++ .../TaggedSerializationTrait.php | 39 ++++++++++++++ .../Drupal/Component/Serialization/Yaml.php | 11 +++- .../Component/Serialization/YamlPecl.php | 17 +++++-- .../Component/Serialization/YamlSymfony.php | 24 ++++++++- core/lib/Drupal/Core/Serialization/Yaml.php | 51 +++++++++++++++++++ 6 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 core/lib/Drupal/Component/Serialization/TaggedSerializationInterface.php create mode 100644 core/lib/Drupal/Component/Serialization/TaggedSerializationTrait.php diff --git a/core/lib/Drupal/Component/Serialization/TaggedSerializationInterface.php b/core/lib/Drupal/Component/Serialization/TaggedSerializationInterface.php new file mode 100644 index 0000000000..361ea65bf5 --- /dev/null +++ b/core/lib/Drupal/Component/Serialization/TaggedSerializationInterface.php @@ -0,0 +1,28 @@ + '\Drupal\Component\Serialization\YamlPecl::applyBooleanCallbacks', - ]); + $data = yaml_parse($raw, 0, $ndocs, static::getTagCallbacks()); restore_error_handler(); return $data; } @@ -79,6 +79,15 @@ public static function getFileExtension() { return 'yml'; } + /** + * {@inheritdoc} + */ + public static function getTagCallbacks() { + return [ + YAML_BOOL_TAG => static::class . '::applyBooleanCallbacks', + ]; + } + /** * Applies callbacks after parsing to ignore 1.1 style booleans. * diff --git a/core/lib/Drupal/Component/Serialization/YamlSymfony.php b/core/lib/Drupal/Component/Serialization/YamlSymfony.php index d35e09cd48..2ea01d4ba2 100644 --- a/core/lib/Drupal/Component/Serialization/YamlSymfony.php +++ b/core/lib/Drupal/Component/Serialization/YamlSymfony.php @@ -5,12 +5,15 @@ use Drupal\Component\Serialization\Exception\InvalidDataTypeException; use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Dumper; +use Symfony\Component\Yaml\Tag\TaggedValue; use Symfony\Component\Yaml\Yaml as SymfonyYaml; /** * Default serialization for YAML using the Symfony component. */ -class YamlSymfony implements SerializationInterface { +class YamlSymfony implements TaggedSerializationInterface { + + use TaggedSerializationTrait; /** * {@inheritdoc} @@ -34,7 +37,24 @@ public static function decode($raw) { $yaml = new Parser(); // Make sure we have a single trailing newline. A very simple config like // 'foo: bar' with no newline will fail to parse otherwise. - return $yaml->parse($raw, SymfonyYaml::PARSE_EXCEPTION_ON_INVALID_TYPE); + $data = $yaml->parse($raw, SymfonyYaml::PARSE_EXCEPTION_ON_INVALID_TYPE | SymfonyYaml::PARSE_CUSTOM_TAGS); + + $is_array = is_array($data); + if (!$is_array) { + $data = [$data]; + } + + // Support Symfony 3.3 TaggedValue objects. + array_walk_recursive($data, function (&$value) { + if ($value instanceof TaggedValue) { + $callbacks = static::getTagCallbacks(); + $args = (array) $value->getValue(); + $tag = $value->getTag(); + return isset($callbacks[$tag]) ? $callbacks[$tag](...$args) : $args; + } + }); + + return $is_array ? $data : reset($data); } catch (\Exception $e) { throw new InvalidDataTypeException($e->getMessage(), $e->getCode(), $e); diff --git a/core/lib/Drupal/Core/Serialization/Yaml.php b/core/lib/Drupal/Core/Serialization/Yaml.php index 566f8cad4c..1b089249cf 100644 --- a/core/lib/Drupal/Core/Serialization/Yaml.php +++ b/core/lib/Drupal/Core/Serialization/Yaml.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Serialization; +use Drupal\Component\Serialization\TaggedSerializationInterface; use Drupal\Core\Site\Settings; use Drupal\Component\Serialization\Yaml as ComponentYaml; @@ -12,6 +13,13 @@ */ class Yaml extends ComponentYaml { + /** + * Translation Manager. + * + * @var \Drupal\Core\StringTranslation\TranslationInterface + */ + protected static $translation; + /** * {@inheritdoc} */ @@ -21,8 +29,51 @@ protected static function getSerializer() { $class = Settings::get('yaml_parser_class')) { static::$serializer = $class; + + // Merge the tag callbacks from this proxy to the chosen serializer. + if (static::$serializer instanceof TaggedSerializationInterface) { + static::$serializer::setTagCallbacks(array_merge( + static::$serializer::getTagCallbacks(), + static::getTagCallbacks() + )); + } } return parent::getSerializer(); } + /** + * Retrieves the Translation Manager. + * + * @return \Drupal\Core\StringTranslation\TranslationInterface + * The Translation Manager. + */ + protected static function getTranslation() { + if (!isset(static::$translation)) { + static::$translation = \Drupal::translation(); + } + return static::$translation; + } + + /** + * {@inheritdoc} + */ + public static function getTagCallbacks() { + return [ + '!translate' => static::class . '::applyTranslateCallback', + ]; + } + + /** + * Callback for applying the !translate tag. + * + * @param mixed ... + * The arguments to pass to the Translation manager. + * + * @return \Drupal\Core\StringTranslation\TranslatableMarkup + * A new TranslatableMarkup object. + */ + public static function applyTranslateCallback(...$args) { + return static::getTranslation()->translate(...$args); + } + } -- 2.22.0