/**
  * {@inheritdoc}
  */
 public function resolvePlaceholders($text, array $data = [], BubbleableMetadata $bubbleable_metadata = NULL, array $options = [])
 {
     $options += ['langcode' => NULL, 'clear' => FALSE];
     $placeholder_by_data = $this->scan($text);
     if (empty($placeholder_by_data)) {
         return [];
     }
     $replacements = [];
     foreach ($placeholder_by_data as $data_name => $placeholders) {
         foreach ($placeholders as $placeholder_main_part => $placeholder) {
             try {
                 if (!isset($data[$data_name])) {
                     throw new MissingDataException("There is no data with the name '{$data_name}' available.");
                 }
                 list($property_sub_paths, $filters) = $this->parseMainPlaceholderPart($placeholder_main_part, $placeholder);
                 $fetched_data = $this->dataFetcher->fetchDataBySubPaths($data[$data_name], $property_sub_paths, $bubbleable_metadata, $options['langcode']);
                 // Apply filters.
                 if ($filters) {
                     $value = $fetched_data->getValue();
                     $definition = $fetched_data->getDataDefinition();
                     foreach ($filters as $data) {
                         list($filter_id, $arguments) = $data;
                         $filter = $this->dataFilterManager->createInstance($filter_id);
                         if (!$filter->allowsNullValues() && !isset($value)) {
                             throw new MissingDataException("There is no data value for filter '{$filter_id}' to work on.");
                         }
                         $value = $filter->filter($definition, $value, $arguments, $bubbleable_metadata);
                         $definition = $filter->filtersTo($definition, $arguments);
                     }
                 } else {
                     $value = $fetched_data->getString();
                 }
                 // Escape the tokens, unless they are explicitly markup.
                 $replacements[$placeholder] = $value instanceof MarkupInterface ? $value : new HtmlEscapedText($value);
             } catch (\InvalidArgumentException $e) {
                 // Should we log warnings if there are problems other than missing
                 // data, like syntactically invalid placeholders?
                 if (!empty($options['clear'])) {
                     $replacements[$placeholder] = '';
                 }
             } catch (MissingDataException $e) {
                 if (!empty($options['clear'])) {
                     $replacements[$placeholder] = '';
                 }
             }
         }
     }
     return $replacements;
 }
 /**
  * @cover fetchDataByPropertyPath
  */
 public function testBubbleableMetadata()
 {
     $this->node->field_integer->setValue([]);
     // Save the node, so that it gets an ID and it has a cache tag.
     $this->node->save();
     // Also add a user for testing cache tags of references.
     $user = $this->entityTypeManager->getStorage('user')->create(['name' => 'test', 'type' => 'user']);
     $user->save();
     $this->node->uid->entity = $user;
     $bubbleable_metadata = new BubbleableMetadata();
     $this->dataFetcher->fetchDataByPropertyPath($this->node->getTypedData(), 'title.value', $bubbleable_metadata)->getValue();
     $expected = ['node:' . $this->node->id()];
     $this->assertEquals($expected, $bubbleable_metadata->getCacheTags());
     // Test cache tags of references are added correctly.
     $this->dataFetcher->fetchDataByPropertyPath($this->node->getTypedData(), 'uid.entity.name', $bubbleable_metadata)->getValue();
     $expected = ['node:' . $this->node->id(), 'user:' . $user->id()];
     $this->assertEquals($expected, $bubbleable_metadata->getCacheTags());
 }
 /**
  * @covers ::fetchDefinitionByPropertyPath
  * @expectedException \InvalidArgumentException
  * @expectedExceptionMessage The data selector 'unknown_property' cannot be applied because the definition of type 'integer' is not a list or a complex structure
  */
 public function testFetchingAtInvalidPosition()
 {
     $list_definition = $this->typedDataManager->createListDataDefinition('integer');
     // This should trigger an exception.
     $this->dataFetcher->fetchDefinitionByPropertyPath($list_definition, 'unknown_property');
 }