Token.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php
  2. namespace Drupal\token;
  3. use Drupal\Core\Cache\CacheBackendInterface;
  4. use Drupal\Core\Language\LanguageInterface;
  5. use Drupal\Core\Utility\Token as TokenBase;
  6. /**
  7. * Service to retrieve token information.
  8. *
  9. * This service replaces the core's token service and provides the same
  10. * functionality by extending it. It also provides additional functionality
  11. * commonly required by the additional support provided by token module and
  12. * other modules.
  13. */
  14. class Token extends TokenBase implements TokenInterface {
  15. /**
  16. * Token definitions.
  17. *
  18. * @var array[]|null
  19. * An array of token definitions, or NULL when the definitions are not set.
  20. *
  21. * @see self::resetInfo()
  22. */
  23. protected $globalTokenTypes;
  24. /**
  25. * {@inheritdoc}
  26. */
  27. public function getInfo() {
  28. if (empty($this->tokenInfo)) {
  29. $cache_id = 'token_info_sorted:' . $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
  30. $cache = $this->cache->get($cache_id);
  31. if ($cache) {
  32. $this->tokenInfo = $cache->data;
  33. }
  34. else {
  35. $token_info = $this->moduleHandler->invokeAll('token_info');
  36. $this->moduleHandler->alter('token_info', $token_info);
  37. foreach (array_keys($token_info['types']) as $type_key) {
  38. if (isset($token_info['types'][$type_key]['type'])) {
  39. $base_type = $token_info['types'][$type_key]['type'];
  40. // If this token type extends another token type, then merge in
  41. // the base token type's tokens.
  42. if (isset($token_info['tokens'][$base_type])) {
  43. $token_info['tokens'] += [$type_key => []];
  44. $token_info['tokens'][$type_key] += $token_info['tokens'][$base_type];
  45. }
  46. }
  47. else {
  48. // Add a 'type' value to each token type information.
  49. $token_info['types'][$type_key]['type'] = $type_key;
  50. }
  51. }
  52. // Pre-sort tokens.
  53. $by_name = $this->prepareMultisort($token_info['types']);
  54. array_multisort($by_name, SORT_ASC, SORT_NATURAL | SORT_FLAG_CASE, $token_info['types']);
  55. foreach (array_keys($token_info['tokens']) as $type) {
  56. $by_name = $this->prepareMultisort($token_info['tokens'][$type]);
  57. array_multisort($by_name, SORT_ASC, SORT_NATURAL | SORT_FLAG_CASE, $token_info['tokens'][$type]);
  58. }
  59. $this->tokenInfo = $token_info;
  60. $this->cache->set($cache_id, $this->tokenInfo, CacheBackendInterface::CACHE_PERMANENT, array(
  61. static::TOKEN_INFO_CACHE_TAG,
  62. ));
  63. }
  64. }
  65. return $this->tokenInfo;
  66. }
  67. /**
  68. * Extracts data from the token data for use in array_multisort().
  69. *
  70. * @param array $token_info
  71. * List of tokens or token types, each element must have a name key.
  72. *
  73. * @return string[]
  74. * List of the names keyed by the token key.
  75. */
  76. protected function prepareMultisort($token_info) {
  77. $by_name = [];
  78. foreach ($token_info as $key => $token_info_element) {
  79. $by_name[$key] = $token_info_element['name'];
  80. }
  81. return $by_name;
  82. }
  83. /**
  84. * {@inheritdoc}
  85. */
  86. public function getTokenInfo($token_type, $token) {
  87. if (empty($this->tokenInfo)) {
  88. $this->getInfo();
  89. }
  90. return isset($this->tokenInfo['tokens'][$token_type][$token]) ? $this->tokenInfo['tokens'][$token_type][$token] : NULL;
  91. }
  92. /**
  93. * {@inheritdoc}
  94. */
  95. public function getTypeInfo($token_type) {
  96. if (empty($this->tokenInfo)) {
  97. $this->getInfo();
  98. }
  99. return isset($this->tokenInfo['types'][$token_type]) ? $this->tokenInfo['types'][$token_type] : NULL;
  100. }
  101. /**
  102. * {@inheritdoc}
  103. */
  104. public function getGlobalTokenTypes() {
  105. if (empty($this->globalTokenTypes)) {
  106. $token_info = $this->getInfo();
  107. foreach ($token_info['types'] as $type => $type_info) {
  108. // If the token types has not specified that 'needs-data' => TRUE, then
  109. // it is a global token type that will always be replaced in any context.
  110. if (empty($type_info['needs-data'])) {
  111. $this->globalTokenTypes[] = $type;
  112. }
  113. }
  114. }
  115. return $this->globalTokenTypes;
  116. }
  117. /**
  118. * {@inheritdoc}
  119. */
  120. function getInvalidTokens($type, $tokens) {
  121. $token_info = $this->getInfo();
  122. $invalid_tokens = array();
  123. foreach ($tokens as $token => $full_token) {
  124. if (isset($token_info['tokens'][$type][$token])) {
  125. continue;
  126. }
  127. // Split token up if it has chains.
  128. $parts = explode(':', $token, 2);
  129. if (!isset($token_info['tokens'][$type][$parts[0]])) {
  130. // This is an invalid token (not defined).
  131. $invalid_tokens[] = $full_token;
  132. }
  133. elseif (count($parts) == 2) {
  134. $sub_token_info = $token_info['tokens'][$type][$parts[0]];
  135. if (!empty($sub_token_info['dynamic'])) {
  136. // If this token has been flagged as a dynamic token, skip it.
  137. continue;
  138. }
  139. elseif (empty($sub_token_info['type'])) {
  140. // If the token has chains, but does not support it, it is invalid.
  141. $invalid_tokens[] = $full_token;
  142. }
  143. else {
  144. // Recursively check the chained tokens.
  145. $sub_tokens = $this->findWithPrefix(array($token => $full_token), $parts[0]);
  146. $invalid_tokens = array_merge($invalid_tokens, $this->getInvalidTokens($sub_token_info['type'], $sub_tokens));
  147. }
  148. }
  149. }
  150. return $invalid_tokens;
  151. }
  152. /**
  153. * {@inheritdoc}
  154. */
  155. public function getInvalidTokensByContext($value, array $valid_types = []) {
  156. if (in_array('all', $valid_types)) {
  157. $info = $this->getInfo();
  158. $valid_types = array_keys($info['types']);
  159. }
  160. else {
  161. // Add the token types that are always valid in global context.
  162. $valid_types = array_merge($valid_types, $this->getGlobalTokenTypes());
  163. }
  164. $invalid_tokens = array();
  165. $value_tokens = is_string($value) ? $this->scan($value) : $value;
  166. foreach ($value_tokens as $type => $tokens) {
  167. if (!in_array($type, $valid_types)) {
  168. // If the token type is not a valid context, its tokens are invalid.
  169. $invalid_tokens = array_merge($invalid_tokens, array_values($tokens));
  170. }
  171. else {
  172. // Check each individual token for validity.
  173. $invalid_tokens = array_merge($invalid_tokens, $this->getInvalidTokens($type, $tokens));
  174. }
  175. }
  176. array_unique($invalid_tokens);
  177. return $invalid_tokens;
  178. }
  179. /**
  180. * {@inheritdoc}
  181. */
  182. public function resetInfo() {
  183. parent::resetInfo();
  184. $this->globalTokenTypes = NULL;
  185. }
  186. }