annotate core/lib/Drupal/Core/StringTranslation/TranslatableMarkup.php @ 13:5fb285c0d0e3

Update Drupal core to 8.4.7 via Composer. Security update; I *think* we've been lucky to get away with this so far, as we don't support self-registration which seems to be used by the so-called "drupalgeddon 2" attack that 8.4.5 was vulnerable to.
author Chris Cannam
date Mon, 23 Apr 2018 09:33:26 +0100
parents 4c8ae668cc8c
children 1fec387a4317
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\StringTranslation;
Chris@0 4
Chris@0 5 use Drupal\Component\Render\FormattableMarkup;
Chris@0 6 use Drupal\Component\Utility\ToStringTrait;
Chris@0 7 use Drupal\Component\Utility\Unicode;
Chris@0 8
Chris@0 9 /**
Chris@0 10 * Provides translatable markup class.
Chris@0 11 *
Chris@0 12 * This object, when cast to a string, will return the formatted, translated
Chris@0 13 * string. Avoid casting it to a string yourself, because it is preferable to
Chris@0 14 * let the rendering system do the cast as late as possible in the rendering
Chris@0 15 * process, so that this object itself can be put, untranslated, into render
Chris@0 16 * caches and thus the cache can be shared between different language contexts.
Chris@0 17 *
Chris@0 18 * @see \Drupal\Component\Render\FormattableMarkup
Chris@0 19 * @see \Drupal\Core\StringTranslation\TranslationManager::translateString()
Chris@0 20 * @see \Drupal\Core\Annotation\Translation
Chris@0 21 */
Chris@0 22 class TranslatableMarkup extends FormattableMarkup {
Chris@0 23
Chris@0 24 use ToStringTrait;
Chris@0 25
Chris@0 26 /**
Chris@0 27 * The string to be translated.
Chris@0 28 *
Chris@0 29 * @var string
Chris@0 30 */
Chris@0 31 protected $string;
Chris@0 32
Chris@0 33 /**
Chris@0 34 * The translated markup without placeholder replacements.
Chris@0 35 *
Chris@0 36 * @var string
Chris@0 37 */
Chris@0 38 protected $translatedMarkup;
Chris@0 39
Chris@0 40 /**
Chris@0 41 * The translation options.
Chris@0 42 *
Chris@0 43 * @var array
Chris@0 44 */
Chris@0 45 protected $options;
Chris@0 46
Chris@0 47 /**
Chris@0 48 * The string translation service.
Chris@0 49 *
Chris@0 50 * @var \Drupal\Core\StringTranslation\TranslationInterface
Chris@0 51 */
Chris@0 52 protected $stringTranslation;
Chris@0 53
Chris@0 54 /**
Chris@0 55 * Constructs a new class instance.
Chris@0 56 *
Chris@0 57 * When possible, use the
Chris@0 58 * \Drupal\Core\StringTranslation\StringTranslationTrait $this->t(). Otherwise
Chris@0 59 * create a new \Drupal\Core\StringTranslation\TranslatableMarkup object
Chris@0 60 * directly.
Chris@0 61 *
Chris@0 62 * Calling the trait's t() method or instantiating a new TranslatableMarkup
Chris@0 63 * object serves two purposes:
Chris@0 64 * - At run-time it translates user-visible text into the appropriate
Chris@0 65 * language.
Chris@0 66 * - Static analyzers detect calls to t() and new TranslatableMarkup, and add
Chris@0 67 * the first argument (the string to be translated) to the database of
Chris@0 68 * strings that need translation. These strings are expected to be in
Chris@0 69 * English, so the first argument should always be in English.
Chris@0 70 * To allow the site to be localized, it is important that all human-readable
Chris@0 71 * text that will be displayed on the site or sent to a user is made available
Chris@0 72 * in one of the ways supported by the
Chris@0 73 * @link https://www.drupal.org/node/322729 Localization API @endlink.
Chris@0 74 * See the @link https://www.drupal.org/node/322729 Localization API @endlink
Chris@0 75 * pages for more information, including recommendations on how to break up or
Chris@0 76 * not break up strings for translation.
Chris@0 77 *
Chris@0 78 * @section sec_translating_vars Translating Variables
Chris@0 79 * $string should always be an English literal string.
Chris@0 80 *
Chris@0 81 * $string should never contain a variable, such as:
Chris@0 82 * @code
Chris@0 83 * new TranslatableMarkup($text)
Chris@0 84 * @endcode
Chris@0 85 * There are several reasons for this:
Chris@0 86 * - Using a variable for $string that is user input is a security risk.
Chris@0 87 * - Using a variable for $string that has even guaranteed safe text (for
Chris@0 88 * example, user interface text provided literally in code), will not be
Chris@0 89 * picked up by the localization static text processor. (The parameter could
Chris@0 90 * be a variable if the entire string in $text has been passed into t() or
Chris@0 91 * new TranslatableMarkup() elsewhere as the first argument, but that
Chris@0 92 * strategy is not recommended.)
Chris@0 93 *
Chris@0 94 * It is especially important never to call new TranslatableMarkup($user_text)
Chris@0 95 * or t($user_text) where $user_text is some text that a user entered -- doing
Chris@0 96 * that can lead to cross-site scripting and other security problems. However,
Chris@0 97 * you can use variable substitution in your string, to put variable text such
Chris@0 98 * as user names or link URLs into translated text. Variable substitution
Chris@0 99 * looks like this:
Chris@0 100 * @code
Chris@0 101 * new TranslatableMarkup("@name's blog", array('@name' => $account->getDisplayName()));
Chris@0 102 * @endcode
Chris@0 103 * Basically, you can put placeholders like @name into your string, and the
Chris@0 104 * method will substitute the sanitized values at translation time. (See the
Chris@0 105 * Localization API pages referenced above and the documentation of
Chris@0 106 * \Drupal\Component\Render\FormattableMarkup::placeholderFormat()
Chris@0 107 * for details about how to safely and correctly define variables in your
Chris@0 108 * string.) Translators can then rearrange the string as necessary for the
Chris@0 109 * language (e.g., in Spanish, it might be "blog de @name").
Chris@0 110 *
Chris@0 111 * @param string $string
Chris@0 112 * A string containing the English text to translate.
Chris@0 113 * @param array $arguments
Chris@0 114 * (optional) An associative array of replacements to make after
Chris@0 115 * translation. Based on the first character of the key, the value is
Chris@0 116 * escaped and/or themed. See
Chris@0 117 * \Drupal\Component\Render\FormattableMarkup::placeholderFormat() for
Chris@0 118 * details.
Chris@0 119 * @param array $options
Chris@0 120 * (optional) An associative array of additional options, with the following
Chris@0 121 * elements:
Chris@0 122 * - 'langcode' (defaults to the current language): A language code, to
Chris@0 123 * translate to a language other than what is used to display the page.
Chris@0 124 * - 'context' (defaults to the empty context): The context the source
Chris@0 125 * string belongs to.
Chris@0 126 * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
Chris@0 127 * (optional) The string translation service.
Chris@0 128 *
Chris@0 129 * @throws \InvalidArgumentException
Chris@0 130 * Exception thrown when $string is not a string.
Chris@0 131 *
Chris@0 132 * @see \Drupal\Component\Render\FormattableMarkup::placeholderFormat()
Chris@0 133 * @see \Drupal\Core\StringTranslation\StringTranslationTrait::t()
Chris@0 134 *
Chris@0 135 * @ingroup sanitization
Chris@0 136 */
Chris@0 137 public function __construct($string, array $arguments = [], array $options = [], TranslationInterface $string_translation = NULL) {
Chris@0 138 if (!is_string($string)) {
Chris@0 139 $message = $string instanceof TranslatableMarkup ? '$string ("' . $string->getUntranslatedString() . '") must be a string.' : '$string ("' . (string) $string . '") must be a string.';
Chris@0 140 throw new \InvalidArgumentException($message);
Chris@0 141 }
Chris@0 142 $this->string = $string;
Chris@0 143 $this->arguments = $arguments;
Chris@0 144 $this->options = $options;
Chris@0 145 $this->stringTranslation = $string_translation;
Chris@0 146 }
Chris@0 147
Chris@0 148 /**
Chris@0 149 * Gets the untranslated string value stored in this translated string.
Chris@0 150 *
Chris@0 151 * @return string
Chris@0 152 * The string stored in this wrapper.
Chris@0 153 */
Chris@0 154 public function getUntranslatedString() {
Chris@0 155 return $this->string;
Chris@0 156 }
Chris@0 157
Chris@0 158 /**
Chris@0 159 * Gets a specific option from this translated string.
Chris@0 160 *
Chris@0 161 * @param string $name
Chris@0 162 * Option name.
Chris@0 163 *
Chris@0 164 * @return mixed
Chris@0 165 * The value of this option or empty string of option is not set.
Chris@0 166 */
Chris@0 167 public function getOption($name) {
Chris@0 168 return isset($this->options[$name]) ? $this->options[$name] : '';
Chris@0 169 }
Chris@0 170
Chris@0 171 /**
Chris@0 172 * Gets all options from this translated string.
Chris@0 173 *
Chris@0 174 * @return mixed[]
Chris@0 175 * The array of options.
Chris@0 176 */
Chris@0 177 public function getOptions() {
Chris@0 178 return $this->options;
Chris@0 179 }
Chris@0 180
Chris@0 181 /**
Chris@0 182 * Gets all arguments from this translated string.
Chris@0 183 *
Chris@0 184 * @return mixed[]
Chris@0 185 * The array of arguments.
Chris@0 186 */
Chris@0 187 public function getArguments() {
Chris@0 188 return $this->arguments;
Chris@0 189 }
Chris@0 190
Chris@0 191 /**
Chris@0 192 * Renders the object as a string.
Chris@0 193 *
Chris@0 194 * @return string
Chris@0 195 * The translated string.
Chris@0 196 */
Chris@0 197 public function render() {
Chris@0 198 if (!isset($this->translatedMarkup)) {
Chris@0 199 $this->translatedMarkup = $this->getStringTranslation()->translateString($this);
Chris@0 200 }
Chris@0 201
Chris@0 202 // Handle any replacements.
Chris@0 203 if ($args = $this->getArguments()) {
Chris@0 204 return $this->placeholderFormat($this->translatedMarkup, $args);
Chris@0 205 }
Chris@0 206 return $this->translatedMarkup;
Chris@0 207 }
Chris@0 208
Chris@0 209 /**
Chris@0 210 * Magic __sleep() method to avoid serializing the string translator.
Chris@0 211 */
Chris@0 212 public function __sleep() {
Chris@0 213 return ['string', 'arguments', 'options'];
Chris@0 214 }
Chris@0 215
Chris@0 216 /**
Chris@0 217 * Gets the string translation service.
Chris@0 218 *
Chris@0 219 * @return \Drupal\Core\StringTranslation\TranslationInterface
Chris@0 220 * The string translation service.
Chris@0 221 */
Chris@0 222 protected function getStringTranslation() {
Chris@0 223 if (!$this->stringTranslation) {
Chris@0 224 $this->stringTranslation = \Drupal::service('string_translation');
Chris@0 225 }
Chris@0 226
Chris@0 227 return $this->stringTranslation;
Chris@0 228 }
Chris@0 229
Chris@0 230 /**
Chris@0 231 * Returns the string length.
Chris@0 232 *
Chris@0 233 * @return int
Chris@0 234 * The length of the string.
Chris@0 235 */
Chris@0 236 public function count() {
Chris@0 237 return Unicode::strlen($this->render());
Chris@0 238 }
Chris@0 239
Chris@0 240 }