You are here

function token_tokens in Token 8

Same name and namespace in other branches
  1. 7 token.tokens.inc \token_tokens()

Implements hook_tokens().

File

./token.tokens.inc, line 475
Token callbacks for the token module.

Code

function token_tokens($type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $replacements = [];
  $language_manager = \Drupal::languageManager();
  $url_options = [
    'absolute' => TRUE,
  ];
  if (isset($options['langcode'])) {
    $url_options['language'] = $language_manager
      ->getLanguage($options['langcode']);
    $langcode = $options['langcode'];
  }
  else {
    $langcode = $language_manager
      ->getCurrentLanguage()
      ->getId();
  }

  // Date tokens.
  if ($type == 'date') {
    $date = !empty($data['date']) ? $data['date'] : \Drupal::time()
      ->getRequestTime();

    // @todo Remove when http://drupal.org/node/1173706 is fixed.
    $date_format_types = \Drupal::entityTypeManager()
      ->getStorage('date_format')
      ->loadMultiple();
    foreach ($tokens as $name => $original) {
      if (isset($date_format_types[$name]) && _token_module('date', $name) == 'token') {
        $replacements[$original] = \Drupal::service('date.formatter')
          ->format($date, $name, '', NULL, $langcode);
      }
    }
  }

  // Current date tokens.
  // @todo Remove when http://drupal.org/node/943028 is fixed.
  if ($type == 'current-date') {
    $replacements += \Drupal::token()
      ->generate('date', $tokens, [
      'date' => \Drupal::time()
        ->getRequestTime(),
    ], $options, $bubbleable_metadata);
  }

  // Comment tokens.
  if ($type == 'comment' && !empty($data['comment'])) {

    /* @var \Drupal\comment\CommentInterface $comment */
    $comment = $data['comment'];

    // Chained token relationships.
    if ($url_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'url')) {

      // Add fragment to url options.
      $replacements += \Drupal::token()
        ->generate('url', $url_tokens, [
        'url' => $comment
          ->toUrl('canonical', [
          'fragment' => "comment-{$comment->id()}",
        ]),
      ], $options, $bubbleable_metadata);
    }
  }

  // Node tokens.
  if ($type == 'node' && !empty($data['node'])) {

    /* @var \Drupal\node\NodeInterface $node */
    $node = $data['node'];
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'log':
          $replacements[$original] = (string) $node->revision_log->value;
          break;
        case 'content-type':
          $type_name = \Drupal::entityTypeManager()
            ->getStorage('node_type')
            ->load($node
            ->getType())
            ->label();
          $replacements[$original] = $type_name;
          break;
      }
    }

    // Chained token relationships.
    if (($parent_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'source')) && ($source_node = $node
      ->getUntranslated())) {
      $replacements += \Drupal::token()
        ->generate('node', $parent_tokens, [
        'node' => $source_node,
      ], $options, $bubbleable_metadata);
    }
    if (($node_type_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'content-type')) && ($node_type = NodeType::load($node
      ->bundle()))) {
      $replacements += \Drupal::token()
        ->generate('content-type', $node_type_tokens, [
        'node_type' => $node_type,
      ], $options, $bubbleable_metadata);
    }
    if ($url_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'url')) {
      $replacements += \Drupal::token()
        ->generate('url', $url_tokens, [
        'url' => $node
          ->toUrl(),
      ], $options, $bubbleable_metadata);
    }
  }

  // Content type tokens.
  if ($type == 'content-type' && !empty($data['node_type'])) {

    /* @var \Drupal\node\NodeTypeInterface $node_type */
    $node_type = $data['node_type'];
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'name':
          $replacements[$original] = $node_type
            ->label();
          break;
        case 'machine-name':
          $replacements[$original] = $node_type
            ->id();
          break;
        case 'description':
          $replacements[$original] = $node_type
            ->getDescription();
          break;
        case 'node-count':
          $count = \Drupal::entityQueryAggregate('node')
            ->aggregate('nid', 'COUNT')
            ->condition('type', $node_type
            ->id())
            ->execute();
          $replacements[$original] = (int) $count;
          break;
        case 'edit-url':
          $result = $node_type
            ->toUrl('edit-form', $url_options)
            ->toString(TRUE);
          $bubbleable_metadata
            ->addCacheableDependency($result);
          $replacements[$original] = $result
            ->getGeneratedUrl();
          break;
      }
    }
  }

  // Taxonomy term tokens.
  if ($type == 'term' && !empty($data['term'])) {

    /* @var \Drupal\taxonomy\TermInterface $term */
    $term = $data['term'];

    /** @var \Drupal\taxonomy\TermStorageInterface $term_storage */
    $term_storage = \Drupal::entityTypeManager()
      ->getStorage('taxonomy_term');
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'edit-url':
          $result = Url::fromRoute('entity.taxonomy_term.edit_form', [
            'taxonomy_term' => $term
              ->id(),
          ], $url_options)
            ->toString(TRUE);
          $bubbleable_metadata
            ->addCacheableDependency($result);
          $replacements[$original] = $result
            ->getGeneratedUrl();
          break;
        case 'parents':
          if ($parents = token_taxonomy_term_load_all_parents($term
            ->id(), $langcode)) {
            $replacements[$original] = token_render_array($parents, $options);
          }
          break;
        case 'root':
          $parents = $term_storage
            ->loadAllParents($term
            ->id());
          $root_term = end($parents);
          if ($root_term
            ->id() != $term
            ->id()) {
            $root_term = \Drupal::service('entity.repository')
              ->getTranslationFromContext($root_term, $langcode);
            $replacements[$original] = $root_term
              ->label();
          }
          break;
      }
    }

    // Chained token relationships.
    if ($url_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'url')) {
      $replacements += \Drupal::token()
        ->generate('url', $url_tokens, [
        'url' => $term
          ->toUrl(),
      ], $options, $bubbleable_metadata);
    }

    // [term:parents:*] chained tokens.
    if ($parents_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'parents')) {
      if ($parents = token_taxonomy_term_load_all_parents($term
        ->id(), $langcode)) {
        $replacements += \Drupal::token()
          ->generate('array', $parents_tokens, [
          'array' => $parents,
        ], $options, $bubbleable_metadata);
      }
    }
    if ($root_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'root')) {
      $parents = $term_storage
        ->loadAllParents($term
        ->id());
      $root_term = end($parents);
      if ($root_term->tid != $term
        ->id()) {
        $replacements += \Drupal::token()
          ->generate('term', $root_tokens, [
          'term' => $root_term,
        ], $options, $bubbleable_metadata);
      }
    }
  }

  // Vocabulary tokens.
  if ($type == 'vocabulary' && !empty($data['vocabulary'])) {
    $vocabulary = $data['vocabulary'];
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'machine-name':
          $replacements[$original] = $vocabulary
            ->id();
          break;
        case 'edit-url':
          $result = Url::fromRoute('entity.taxonomy_vocabulary.edit_form', [
            'taxonomy_vocabulary' => $vocabulary
              ->id(),
          ], $url_options)
            ->toString(TRUE);
          $bubbleable_metadata
            ->addCacheableDependency($result);
          $replacements[$original] = $result
            ->getGeneratedUrl();
          break;
      }
    }
  }

  // File tokens.
  if ($type == 'file' && !empty($data['file'])) {
    $file = $data['file'];
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'basename':
          $basename = pathinfo($file->uri->value, PATHINFO_BASENAME);
          $replacements[$original] = $basename;
          break;
        case 'extension':
          $extension = pathinfo($file->uri->value, PATHINFO_EXTENSION);
          $replacements[$original] = $extension;
          break;
        case 'size-raw':
          $replacements[$original] = (int) $file->filesize->value;
          break;
      }
    }
  }

  // User tokens.
  if ($type == 'user' && !empty($data['user'])) {

    /* @var \Drupal\user\UserInterface $account */
    $account = $data['user'];
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'picture':
          if ($account instanceof UserInterface && $account
            ->hasField('user_picture')) {

            /** @var \Drupal\Core\Render\RendererInterface $renderer */
            $renderer = \Drupal::service('renderer');
            $output = [
              '#theme' => 'user_picture',
              '#account' => $account,
            ];
            $replacements[$original] = $renderer
              ->renderPlain($output);
          }
          break;
        case 'roles':
          $roles = $account
            ->getRoles();
          $roles_names = array_combine($roles, $roles);
          $replacements[$original] = token_render_array($roles_names, $options);
          break;
      }
    }

    // Chained token relationships.
    if ($account instanceof UserInterface && $account
      ->hasField('user_picture') && ($picture_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'picture'))) {
      $replacements += \Drupal::token()
        ->generate('file', $picture_tokens, [
        'file' => $account->user_picture->entity,
      ], $options, $bubbleable_metadata);
    }
    if ($url_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'url')) {
      $replacements += \Drupal::token()
        ->generate('url', $url_tokens, [
        'url' => $account
          ->toUrl(),
      ], $options, $bubbleable_metadata);
    }
    if ($role_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'roles')) {
      $roles = $account
        ->getRoles();
      $roles_names = array_combine($roles, $roles);
      $replacements += \Drupal::token()
        ->generate('array', $role_tokens, [
        'array' => $roles_names,
      ], $options, $bubbleable_metadata);
    }
  }

  // Current user tokens.
  if ($type == 'current-user') {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'ip-address':
          $ip = \Drupal::request()
            ->getClientIp();
          $replacements[$original] = $ip;
          break;
      }
    }
  }

  // Menu link tokens.
  if ($type == 'menu-link' && !empty($data['menu-link'])) {

    /** @var \Drupal\Core\Menu\MenuLinkInterface $link */
    $link = $data['menu-link'];

    /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
    $menu_link_manager = \Drupal::service('plugin.manager.menu.link');
    if ($link instanceof MenuLinkContentInterface) {
      $link = $menu_link_manager
        ->createInstance($link
        ->getPluginId());
    }
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'id':
          $replacements[$original] = $link
            ->getPluginId();
          break;
        case 'title':
          $replacements[$original] = token_menu_link_translated_title($link, $langcode);
          break;
        case 'url':
          $result = $link
            ->getUrlObject()
            ->setAbsolute()
            ->toString(TRUE);
          $bubbleable_metadata
            ->addCacheableDependency($result);
          $replacements[$original] = $result
            ->getGeneratedUrl();
          break;
        case 'parent':

          /** @var \Drupal\Core\Menu\MenuLinkInterface $parent */
          if ($link
            ->getParent() && ($parent = $menu_link_manager
            ->createInstance($link
            ->getParent()))) {
            $replacements[$original] = token_menu_link_translated_title($parent, $langcode);
          }
          break;
        case 'parents':
          if ($parents = token_menu_link_load_all_parents($link
            ->getPluginId(), $langcode)) {
            $replacements[$original] = token_render_array($parents, $options);
          }
          break;
        case 'root':
          if ($link
            ->getParent() && ($parent_ids = array_keys(token_menu_link_load_all_parents($link
            ->getPluginId(), $langcode)))) {
            $root = $menu_link_manager
              ->createInstance(array_shift($parent_ids));
            $replacements[$original] = token_menu_link_translated_title($root, $langcode);
          }
          break;
      }
    }

    // Chained token relationships.

    /** @var \Drupal\Core\Menu\MenuLinkInterface $parent */
    if ($link
      ->getParent() && ($parent_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'parent')) && ($parent = $menu_link_manager
      ->createInstance($link
      ->getParent()))) {
      $replacements += \Drupal::token()
        ->generate('menu-link', $parent_tokens, [
        'menu-link' => $parent,
      ], $options, $bubbleable_metadata);
    }

    // [menu-link:parents:*] chained tokens.
    if ($parents_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'parents')) {
      if ($parents = token_menu_link_load_all_parents($link
        ->getPluginId(), $langcode)) {
        $replacements += \Drupal::token()
          ->generate('array', $parents_tokens, [
          'array' => $parents,
        ], $options, $bubbleable_metadata);
      }
    }
    if (($root_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'root')) && $link
      ->getParent() && ($parent_ids = array_keys(token_menu_link_load_all_parents($link
      ->getPluginId(), $langcode)))) {
      $root = $menu_link_manager
        ->createInstance(array_shift($parent_ids));
      $replacements += \Drupal::token()
        ->generate('menu-link', $root_tokens, [
        'menu-link' => $root,
      ], $options, $bubbleable_metadata);
    }
    if ($url_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'url')) {
      $replacements += \Drupal::token()
        ->generate('url', $url_tokens, [
        'url' => $link
          ->getUrlObject(),
      ], $options, $bubbleable_metadata);
    }
  }

  // Language tokens.
  if ($type == 'language' && !empty($langcode)) {
    $language = $language_manager
      ->getLanguage($langcode);
    if ($language) {
      foreach ($tokens as $name => $original) {
        switch ($name) {
          case 'name':
            $replacements[$original] = $language
              ->getName();
            break;
          case 'langcode':
            $replacements[$original] = $langcode;
            break;
          case 'direction':
            $replacements[$original] = $language
              ->getDirection();
            break;
          case 'domain':
            if (!isset($language_url_domains)) {
              $language_url_domains = \Drupal::config('language.negotiation')
                ->get('url.domains');
            }
            if (isset($language_url_domains[$langcode])) {
              $replacements[$original] = $language_url_domains[$langcode];
            }
            break;
          case 'prefix':
            if (!isset($language_url_prefixes)) {
              $language_url_prefixes = \Drupal::config('language.negotiation')
                ->get('url.prefixes');
            }
            if (isset($language_url_prefixes[$langcode])) {
              $replacements[$original] = $language_url_prefixes[$langcode];
            }
            break;
        }
      }
    }
  }

  // Current page tokens.
  if ($type == 'current-page') {
    $request = \Drupal::request();
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'title':
          $route = $request->attributes
            ->get(RouteObjectInterface::ROUTE_OBJECT);
          if ($route) {
            $title = \Drupal::service('title_resolver')
              ->getTitle($request, $route);
            $replacements[$original] = token_render_array_value($title);
          }
          break;
        case 'url':
          $bubbleable_metadata
            ->addCacheContexts([
            'url.path',
          ]);
          try {
            $url = Url::createFromRequest($request)
              ->setOptions($url_options);
          } catch (\Exception $e) {

            // Url::createFromRequest() can fail, e.g. on 404 pages.
            // Fall back and try again with Url::fromUserInput().
            try {
              $url = Url::fromUserInput($request
                ->getPathInfo(), $url_options);
            } catch (\Exception $e) {

              // Instantiation would fail again on malformed urls.
            }
          }
          if (isset($url)) {
            $result = $url
              ->toString(TRUE);
            $bubbleable_metadata
              ->addCacheableDependency($result);
            $replacements[$original] = $result
              ->getGeneratedUrl();
          }
          break;
        case 'page-number':
          if ($page = $request->query
            ->get('page')) {

            // @see PagerDefault::execute()
            $pager_page_array = explode(',', $page);
            $page = $pager_page_array[0];
          }
          $replacements[$original] = (int) $page + 1;
          break;
      }

      // [current-page:interface-language:*] chained tokens.
      if ($language_interface_tokens = \Drupal::token()
        ->findWithPrefix($tokens, 'interface-language')) {
        $language_interface = $language_manager
          ->getCurrentLanguage(LanguageInterface::TYPE_INTERFACE);
        $langcode = $language_interface
          ->getId();
        $replacements += \Drupal::token()
          ->generate('language', $language_interface_tokens, $data, [
          'langcode' => $langcode,
        ] + $options, $bubbleable_metadata);
      }

      // [current-page:content-language:*] chained tokens.
      if ($language_content_tokens = \Drupal::token()
        ->findWithPrefix($tokens, 'content-language')) {
        $language_content = $language_manager
          ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT);
        $langcode = $language_content
          ->getId();
        $replacements += \Drupal::token()
          ->generate('language', $language_content_tokens, $data, [
          'langcode' => $langcode,
        ] + $options, $bubbleable_metadata);
      }
    }

    // @deprecated
    // [current-page:arg] dynamic tokens.
    if ($arg_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'arg')) {
      $path = ltrim(\Drupal::service('path.current')
        ->getPath(), '/');

      // Make sure its a system path.
      $path = \Drupal::service('path_alias.manager')
        ->getPathByAlias($path);
      foreach ($arg_tokens as $name => $original) {
        $parts = explode('/', $path);
        if (is_numeric($name) && isset($parts[$name])) {
          $replacements[$original] = $parts[$name];
        }
      }
    }

    // [current-page:query] dynamic tokens.
    if ($query_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'query')) {
      $bubbleable_metadata
        ->addCacheContexts([
        'url.query_args',
      ]);
      foreach ($query_tokens as $name => $original) {
        if (\Drupal::request()->query
          ->has($name)) {
          $value = \Drupal::request()->query
            ->get($name);
          $replacements[$original] = $value;
        }
      }
    }

    // Chained token relationships.
    if ($url_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'url')) {
      $url = NULL;
      try {
        $url = Url::createFromRequest($request)
          ->setOptions($url_options);
      } catch (\Exception $e) {

        // Url::createFromRequest() can fail, e.g. on 404 pages.
        // Fall back and try again with Url::fromUserInput().
        try {
          $url = Url::fromUserInput($request
            ->getPathInfo(), $url_options);
        } catch (\Exception $e) {

          // Instantiation would fail again on malformed urls.
        }
      }

      // Add cache contexts to ensure this token functions on a per-path basis
      $bubbleable_metadata
        ->addCacheContexts([
        'url.path',
      ]);
      $replacements += \Drupal::token()
        ->generate('url', $url_tokens, [
        'url' => $url,
      ], $options, $bubbleable_metadata);
    }
  }

  // URL tokens.
  if ($type == 'url' && !empty($data['url'])) {

    /** @var \Drupal\Core\Url $url */
    $url = $data['url'];

    // To retrieve the correct path, modify a copy of the Url object.
    $path_url = clone $url;
    $path = '/';

    // Ensure the URL is routed to avoid throwing an exception.
    if ($url
      ->isRouted()) {
      $path .= $path_url
        ->setAbsolute(FALSE)
        ->setOption('fragment', NULL)
        ->getInternalPath();
    }
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'path':
          $value = !$url
            ->getOption('alias') ? \Drupal::service('path_alias.manager')
            ->getAliasByPath($path, $langcode) : $path;
          $replacements[$original] = $value;
          break;
        case 'alias':

          // @deprecated
          $alias = \Drupal::service('path_alias.manager')
            ->getAliasByPath($path, $langcode);
          $replacements[$original] = $alias;
          break;
        case 'absolute':
          $result = $url
            ->setAbsolute()
            ->toString(TRUE);
          $bubbleable_metadata
            ->addCacheableDependency($result);
          $replacements[$original] = $result
            ->getGeneratedUrl();
          break;
        case 'relative':
          $result = $url
            ->setAbsolute(FALSE)
            ->toString(TRUE);
          $bubbleable_metadata
            ->addCacheableDependency($result);
          $replacements[$original] = $result
            ->getGeneratedUrl();
          break;
        case 'brief':
          $result = $url
            ->setAbsolute()
            ->toString(TRUE);
          $bubbleable_metadata
            ->addCacheableDependency($result);
          $replacements[$original] = preg_replace([
            '!^https?://!',
            '!/$!',
          ], '', $result
            ->getGeneratedUrl());
          break;
        case 'unaliased':
          $unaliased = clone $url;
          $result = $unaliased
            ->setAbsolute()
            ->setOption('alias', TRUE)
            ->toString(TRUE);
          $bubbleable_metadata
            ->addCacheableDependency($result);
          $replacements[$original] = $result
            ->getGeneratedUrl();
          break;
        case 'args':
          $value = !$url
            ->getOption('alias') ? \Drupal::service('path_alias.manager')
            ->getAliasByPath($path, $langcode) : $path;
          $replacements[$original] = token_render_array(explode('/', $value), $options);
          break;
      }
    }

    // [url:args:*] chained tokens.
    if ($arg_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'args')) {
      $value = !$url
        ->getOption('alias') ? \Drupal::service('path_alias.manager')
        ->getAliasByPath($path, $langcode) : $path;
      $replacements += \Drupal::token()
        ->generate('array', $arg_tokens, [
        'array' => explode('/', ltrim($value, '/')),
      ], $options, $bubbleable_metadata);
    }

    // [url:unaliased:*] chained tokens.
    if ($unaliased_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'unaliased')) {
      $url
        ->setOption('alias', TRUE);
      $replacements += \Drupal::token()
        ->generate('url', $unaliased_tokens, [
        'url' => $url,
      ], $options, $bubbleable_metadata);
    }
  }

  // Entity tokens.
  if (!empty($data[$type]) && ($entity_type = \Drupal::service('token.entity_mapper')
    ->getEntityTypeForTokenType($type))) {

    /* @var \Drupal\Core\Entity\EntityInterface $entity */
    $entity = $data[$type];
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'url':
          if (_token_module($type, 'url') === 'token' && !$entity
            ->isNew() && $entity
            ->hasLinkTemplate('canonical')) {
            $result = $entity
              ->toUrl('canonical')
              ->toString(TRUE);
            $bubbleable_metadata
              ->addCacheableDependency($result);
            $replacements[$original] = $result
              ->getGeneratedUrl();
          }
          break;
        case 'original':
          if (_token_module($type, 'original') == 'token' && !empty($entity->original)) {
            $label = $entity->original
              ->label();
            $replacements[$original] = $label;
          }
          break;
      }
    }

    // [entity:url:*] chained tokens.
    if (($url_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'url')) && _token_module($type, 'url') == 'token') {
      $replacements += \Drupal::token()
        ->generate('url', $url_tokens, [
        'url' => $entity
          ->toUrl(),
      ], $options, $bubbleable_metadata);
    }

    // [entity:original:*] chained tokens.
    if (($original_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'original')) && _token_module($type, 'original') == 'token' && !empty($entity->original)) {
      $replacements += \Drupal::token()
        ->generate($type, $original_tokens, [
        $type => $entity->original,
      ], $options, $bubbleable_metadata);
    }

    // [entity:language:*] chained tokens.
    if (($language_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'language')) && _token_module($type, 'language') == 'token') {
      $language_options = array_merge($options, [
        'langcode' => $entity
          ->get('langcode')->value,
      ]);
      $replacements += \Drupal::token()
        ->generate('language', $language_tokens, [], $language_options, $bubbleable_metadata);
    }

    // Pass through to an generic 'entity' token type generation.
    $entity_data = [
      'entity_type' => $entity_type,
      'entity' => $entity,
      'token_type' => $type,
    ];

    // @todo Investigate passing through more data like everything from entity_extract_ids().
    $replacements += \Drupal::token()
      ->generate('entity', $tokens, $entity_data, $options, $bubbleable_metadata);
  }

  // Array tokens.
  if ($type == 'array' && !empty($data['array']) && is_array($data['array'])) {
    $array = $data['array'];
    $sort = isset($options['array sort']) ? $options['array sort'] : TRUE;
    $keys = token_element_children($array, $sort);

    /** @var \Drupal\Core\Render\RendererInterface $renderer */
    $renderer = \Drupal::service('renderer');
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'first':
          $value = $array[$keys[0]];
          $value = is_array($value) ? $renderer
            ->renderPlain($value) : (string) $value;
          $replacements[$original] = $value;
          break;
        case 'last':
          $value = $array[$keys[count($keys) - 1]];
          $value = is_array($value) ? $renderer
            ->renderPlain($value) : (string) $value;
          $replacements[$original] = $value;
          break;
        case 'count':
          $replacements[$original] = count($keys);
          break;
        case 'keys':
          $replacements[$original] = token_render_array($keys, $options);
          break;
        case 'reversed':
          $reversed = array_reverse($array, TRUE);
          $replacements[$original] = token_render_array($reversed, $options);
          break;
        case 'join':
          $replacements[$original] = token_render_array($array, [
            'join' => '',
          ] + $options);
          break;
      }
    }

    // [array:value:*] dynamic tokens.
    if ($value_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'value')) {
      foreach ($value_tokens as $key => $original) {
        if ((is_int($key) || $key[0] !== '#') && isset($array[$key])) {
          $replacements[$original] = token_render_array_value($array[$key], $options);
        }
      }
    }

    // [array:join:*] dynamic tokens.
    if ($join_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'join')) {
      foreach ($join_tokens as $join => $original) {
        $replacements[$original] = token_render_array($array, [
          'join' => $join,
        ] + $options);
      }
    }

    // [array:keys:*] chained tokens.
    if ($key_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'keys')) {
      $replacements += \Drupal::token()
        ->generate('array', $key_tokens, [
        'array' => $keys,
      ], $options, $bubbleable_metadata);
    }

    // [array:reversed:*] chained tokens.
    if ($reversed_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'reversed')) {
      $replacements += \Drupal::token()
        ->generate('array', $reversed_tokens, [
        'array' => array_reverse($array, TRUE),
      ], [
        'array sort' => FALSE,
      ] + $options, $bubbleable_metadata);
    }

    // @todo Handle if the array values are not strings and could be chained.
  }

  // Random tokens.
  if ($type == 'random') {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'number':
          $replacements[$original] = mt_rand();
          break;
      }
    }

    // [custom:hash:*] dynamic token.
    if ($hash_tokens = \Drupal::token()
      ->findWithPrefix($tokens, 'hash')) {
      $algos = hash_algos();
      foreach ($hash_tokens as $name => $original) {
        if (in_array($name, $algos)) {
          $replacements[$original] = hash($name, random_bytes(55));
        }
      }
    }
  }

  // If $type is a token type, $data[$type] is empty but $data[$entity_type] is
  // not, re-run token replacements.
  if (empty($data[$type]) && ($entity_type = \Drupal::service('token.entity_mapper')
    ->getEntityTypeForTokenType($type)) && $entity_type != $type && !empty($data[$entity_type]) && empty($options['recursive'])) {
    $data[$type] = $data[$entity_type];
    $options['recursive'] = TRUE;
    $replacements += \Drupal::moduleHandler()
      ->invokeAll('tokens', [
      $type,
      $tokens,
      $data,
      $options,
      $bubbleable_metadata,
    ]);
  }

  // If the token type specifics a 'needs-data' value, and the value is not
  // present in $data, then throw an error.
  if (!empty($GLOBALS['drupal_test_info']['test_run_id'])) {

    // Only check when tests are running.
    $type_info = \Drupal::token()
      ->getTypeInfo($type);
    if (!empty($type_info['needs-data']) && !isset($data[$type_info['needs-data']])) {
      trigger_error(t('Attempting to perform token replacement for token type %type without required data', [
        '%type' => $type,
      ]), E_USER_WARNING);
    }
  }
  return $replacements;
}