You are here

public function TextimageFactory::processTokens in Textimage 8.3

Same name and namespace in other branches
  1. 8.4 src/TextimageFactory.php \Drupal\textimage\TextimageFactory::processTokens()

Textimage tokens replacement.

Parameters

string $key: The Textimage token key within the main token [textimage:key:...]. Key can take 'uri' or 'url' values.

array $tokens: The tokens to resolve.

array $data: Token data array.

\Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata: The bubbleable metadata.

Return value

array An array of token replacements.

Overrides TextimageFactoryInterface::processTokens

File

src/TextimageFactory.php, line 269

Class

TextimageFactory
Provides a factory for Textimage.

Namespace

Drupal\textimage

Code

public function processTokens($key, array $tokens, array $data, BubbleableMetadata $bubbleable_metadata) {

  // @todo Not only node?
  $node = isset($data['node']) ? $data['node'] : NULL;

  // Need to avoid endless loops, that would occur if there are
  // circular references in the tokens. Set static variables for
  // the nesting level and the stack of fields accessed so far.
  static $nesting_level;
  static $field_stack;
  if (!isset($nesting_level)) {
    $nesting_level = 0;
    $field_stack = [];
  }
  else {
    $nesting_level++;
  }

  // Get tokens specific for the required key.
  $sub_tokens = $this->token
    ->findWithPrefix($tokens, $key);

  // Return immediately if none, or no node.
  if (empty($sub_tokens) || !$node) {
    $this
      ->rollbackStack($nesting_level, $field_stack);
    return [];
  }

  // Loops through the tokens to resolve.
  $replacements = [];
  foreach ($sub_tokens as $sub_token => $original) {

    // Clear current nesting level field stack.
    unset($field_stack[$nesting_level]);

    // Get token elements.
    $sub_token_array = explode(':', $sub_token);

    // Get requested field name, continue if missing.
    $field_name = isset($sub_token_array[0]) ? $sub_token_array[0] : NULL;
    if (!$field_name) {
      continue;
    }

    // Check for recursion, i.e. the field is already engaged in a
    // token resolution. Throw a TextimageTokenException in case.
    if (in_array($field_name, $field_stack)) {
      $this
        ->rollbackStack($nesting_level, $field_stack);
      throw new TextimageTokenException($original);
    }

    // Set current requested field in the field stack.
    $field_stack[$nesting_level] = $field_name;

    // Get requested display mode, default to 'default'.
    $display_mode = isset($sub_token_array[1]) ? $sub_token_array[1] ?: 'default' : 'default';

    // Get requested sequence, default to NULL.
    $index = isset($sub_token_array[2]) ? $sub_token_array[2] : NULL;

    // Get field info, continue if missing.
    if (!($field_info = $node
      ->getFieldDefinition($field_name))) {
      continue;
    }

    // Get info on component providing formatting, continue if missing.
    $entity_display = entity_get_display('node', $node
      ->getType(), $display_mode);
    if (!$entity_display) {
      continue;
    }
    $entity_display_component = $entity_display
      ->getComponent($field_name);
    if (empty($entity_display_component['type'])) {
      continue;
    }

    // At this point, if Textimage is providing field formatting for the
    // current field, we can proceed accessing the data needed to resolve
    // the token.
    if (in_array($entity_display_component['type'], [
      'textimage_text_field_formatter',
      'textimage_image_field_formatter',
    ])) {

      // Get the image style used for the field formatting.
      $image_style_name = isset($entity_display_component['settings']['image_style']) ? $entity_display_component['settings']['image_style'] : NULL;
      if (!$image_style_name) {
        continue;
      }
      $image_style = ImageStyle::load($image_style_name);

      // Get the field items.
      $items = $node
        ->get($field_name);

      // Invoke Textimage API functions to return the token value requested.
      if (in_array($field_info
        ->getFieldStorageDefinition()
        ->getTypeProvider(), [
        'text',
        'core',
      ])) {
        $text = $this
          ->getTextFieldText($items);
        if ($field_info
          ->getFieldStorageDefinition()
          ->getCardinality() != 1 && $entity_display_component['settings']['image_text_values'] == 'itemize') {

          // Build separate image for each text value.
          try {
            $ret = [];
            foreach ($text as $text_value) {
              $textimage = $this
                ->get($bubbleable_metadata)
                ->setStyle($image_style)
                ->setTokenData($data)
                ->process($text_value);
              $ret[] = $this
                ->getTokenReplacement($textimage, $key);
            }

            // Return a single URI/URL if requested, or a comma separated
            // list of all the URIs/URLs generated.
            if (!is_null($index) && isset($ret[$index])) {
              $replacements[$original] = $ret[$index];
            }
            else {
              $replacements[$original] = implode(',', $ret);
            }
          } catch (TextimageTokenException $e) {

            // Callback ended up in circular loop, mark the failing token.
            $replacements[$original] = str_replace('textimage', 'void-textimage', $original);
            if ($nesting_level > 0) {

              // Returns up in the nesting of iteration with the failing
              // token.
              $this
                ->rollbackStack($nesting_level, $field_stack);
              throw new TextimageTokenException($e
                ->getToken());
            }
            else {

              // Inform about the token failure.
              $this->logger
                ->warning('Textimage token @token in node \'@node_title\' can not be resolved (circular reference). Remove the token to avoid this message.', [
                '@token' => $original,
                '@node_title' => $node
                  ->getTitle(),
              ]);
            }
          }
        }
        else {

          // Build single image with all text values.
          try {
            $textimage = $this
              ->get($bubbleable_metadata)
              ->setStyle($image_style)
              ->setTokenData($data)
              ->process($text);
            $replacements[$original] = $this
              ->getTokenReplacement($textimage, $key);
          } catch (TextimageTokenException $e) {

            // Callback ended up in circular loop, mark the failing token.
            $replacements[$original] = str_replace('textimage', 'void-textimage', $original);
            if ($nesting_level > 0) {

              // Returns up in the nesting of iteration with the failing
              // token.
              $this
                ->rollbackStack($nesting_level, $field_stack);
              throw new TextimageTokenException($e
                ->getToken());
            }
            else {

              // Inform about the token failure.
              $this->logger
                ->warning('Textimage token @token in node \'@node_title\' can not be resolved (circular reference). Remove the token to avoid this message.', [
                '@token' => $original,
                '@node_title' => $node
                  ->getTitle(),
              ]);
            }
          }
        }
      }
      elseif ($field_info
        ->getFieldStorageDefinition()
        ->getTypeProvider() == 'image') {

        // Image field. Get a separate Textimage from each of the images
        // in the field.
        try {
          $ret = [];
          foreach ($items as $item) {

            // Get source image from the image field item.
            $item_value = $item
              ->getValue();
            $textimage = $this
              ->get($bubbleable_metadata)
              ->setStyle($image_style)
              ->setTokenData($data)
              ->setSourceImageFile($item->entity, $item_value['width'], $item_value['height'])
              ->process(NULL);
            $ret[] = $this
              ->getTokenReplacement($textimage, $key);
          }

          // Return a single URI/URL if requested, or a comma separated
          // list of all the URIs/URLs generated.
          if (!is_null($index) && isset($ret[$index])) {
            $replacements[$original] = $ret[$index];
          }
          else {
            $replacements[$original] = implode(',', $ret);
          }
        } catch (TextimageTokenException $e) {

          // Callback ended up in circular loop, mark the failing token.
          $replacements[$original] = str_replace('textimage', 'void-textimage', $original);
          if ($nesting_level > 0) {

            // Returns up in the nesting of iteration with the failing token.
            $this
              ->rollbackStack($nesting_level, $field_stack);
            throw new TextimageTokenException($e
              ->getToken());
          }
          else {

            // Inform about the token failure.
            $this->logger
              ->warning('Textimage token @token in node \'@node_title\' can not be resolved (circular reference). Remove the token to avoid this message.', [
              '@token' => $original,
              '@node_title' => $node
                ->getTitle(),
            ]);
          }
        }
      }
    }
  }

  // Return to previous iteration.
  $this
    ->rollbackStack($nesting_level, $field_stack);
  return $replacements;
}