Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

EscaperExtension.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 14.44 KiB


001  <?php
002   
003  /*
004   * This file is part of Twig.
005   *
006   * (c) Fabien Potencier
007   *
008   * For the full copyright and license information, please view the LICENSE
009   * file that was distributed with this source code.
010   */
011   
012  namespace Twig\Extension {
013  use Twig\FileExtensionEscapingStrategy;
014  use Twig\NodeVisitor\EscaperNodeVisitor;
015  use Twig\TokenParser\AutoEscapeTokenParser;
016  use Twig\TwigFilter;
017   
018  final class EscaperExtension extends AbstractExtension
019  {
020      private $defaultStrategy;
021      private $escapers = [];
022   
023      /** @internal */
024      public $safeClasses = [];
025   
026      /** @internal */
027      public $safeLookup = [];
028   
029      /**
030       * @param string|false|callable $defaultStrategy An escaping strategy
031       *
032       * @see setDefaultStrategy()
033       */
034      public function __construct($defaultStrategy = 'html')
035      {
036          $this->setDefaultStrategy($defaultStrategy);
037      }
038   
039      public function getTokenParsers()
040      {
041          return [new AutoEscapeTokenParser()];
042      }
043   
044      public function getNodeVisitors()
045      {
046          return [new EscaperNodeVisitor()];
047      }
048   
049      public function getFilters()
050      {
051          return [
052              new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
053              new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
054              new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]),
055          ];
056      }
057   
058      /**
059       * Sets the default strategy to use when not defined by the user.
060       *
061       * The strategy can be a valid PHP callback that takes the template
062       * name as an argument and returns the strategy to use.
063       *
064       * @param string|false|callable $defaultStrategy An escaping strategy
065       */
066      public function setDefaultStrategy($defaultStrategy)
067      {
068          if ('name' === $defaultStrategy) {
069              $defaultStrategy = [FileExtensionEscapingStrategy::class, 'guess'];
070          }
071   
072          $this->defaultStrategy = $defaultStrategy;
073      }
074   
075      /**
076       * Gets the default strategy to use when not defined by the user.
077       *
078       * @param string $name The template name
079       *
080       * @return string|false The default strategy to use for the template
081       */
082      public function getDefaultStrategy($name)
083      {
084          // disable string callables to avoid calling a function named html or js,
085          // or any other upcoming escaping strategy
086          if (!\is_string($this->defaultStrategy) && false !== $this->defaultStrategy) {
087              return \call_user_func($this->defaultStrategy, $name);
088          }
089   
090          return $this->defaultStrategy;
091      }
092   
093      /**
094       * Defines a new escaper to be used via the escape filter.
095       *
096       * @param string   $strategy The strategy name that should be used as a strategy in the escape call
097       * @param callable $callable A valid PHP callable
098       */
099      public function setEscaper($strategy, callable $callable)
100      {
101          $this->escapers[$strategy] = $callable;
102      }
103   
104      /**
105       * Gets all defined escapers.
106       *
107       * @return callable[] An array of escapers
108       */
109      public function getEscapers()
110      {
111          return $this->escapers;
112      }
113   
114      public function setSafeClasses(array $safeClasses = [])
115      {
116          $this->safeClasses = [];
117          $this->safeLookup = [];
118          foreach ($safeClasses as $class => $strategies) {
119              $this->addSafeClass($class, $strategies);
120          }
121      }
122   
123      public function addSafeClass(string $class, array $strategies)
124      {
125          $class = ltrim($class, '\\');
126          if (!isset($this->safeClasses[$class])) {
127              $this->safeClasses[$class] = [];
128          }
129          $this->safeClasses[$class] = array_merge($this->safeClasses[$class], $strategies);
130   
131          foreach ($strategies as $strategy) {
132              $this->safeLookup[$strategy][$class] = true;
133          }
134      }
135  }
136   
137  class_alias('Twig\Extension\EscaperExtension', 'Twig_Extension_Escaper');
138  }
139   
140  namespace {
141  use Twig\Environment;
142  use Twig\Error\RuntimeError;
143  use Twig\Extension\CoreExtension;
144  use Twig\Extension\EscaperExtension;
145  use Twig\Markup;
146  use Twig\Node\Expression\ConstantExpression;
147  use Twig\Node\Node;
148   
149  /**
150   * Marks a variable as being safe.
151   *
152   * @param string $string A PHP variable
153   *
154   * @return string
155   */
156  function twig_raw_filter($string)
157  {
158      return $string;
159  }
160   
161  /**
162   * Escapes a string.
163   *
164   * @param mixed  $string     The value to be escaped
165   * @param string $strategy   The escaping strategy
166   * @param string $charset    The charset
167   * @param bool   $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
168   *
169   * @return string
170   */
171  function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
172  {
173      if ($autoescape && $string instanceof Markup) {
174          return $string;
175      }
176   
177      if (!\is_string($string)) {
178          if (\is_object($string) && method_exists($string, '__toString')) {
179              if ($autoescape) {
180                  $c = \get_class($string);
181                  $ext = $env->getExtension(EscaperExtension::class);
182                  if (!isset($ext->safeClasses[$c])) {
183                      $ext->safeClasses[$c] = [];
184                      foreach (class_parents($string) + class_implements($string) as $class) {
185                          if (isset($ext->safeClasses[$class])) {
186                              $ext->safeClasses[$c] = array_unique(array_merge($ext->safeClasses[$c], $ext->safeClasses[$class]));
187                              foreach ($ext->safeClasses[$class] as $s) {
188                                  $ext->safeLookup[$s][$c] = true;
189                              }
190                          }
191                      }
192                  }
193                  if (isset($ext->safeLookup[$strategy][$c]) || isset($ext->safeLookup['all'][$c])) {
194                      return (string) $string;
195                  }
196              }
197   
198              $string = (string) $string;
199          } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) {
200              return $string;
201          }
202      }
203   
204      if ('' === $string) {
205          return '';
206      }
207   
208      if (null === $charset) {
209          $charset = $env->getCharset();
210      }
211   
212      switch ($strategy) {
213          case 'html':
214              // see https://www.php.net/htmlspecialchars
215   
216              // Using a static variable to avoid initializing the array
217              // each time the function is called. Moving the declaration on the
218              // top of the function slow downs other escaping strategies.
219              static $htmlspecialcharsCharsets = [
220                  'ISO-8859-1' => true, 'ISO8859-1' => true,
221                  'ISO-8859-15' => true, 'ISO8859-15' => true,
222                  'utf-8' => true, 'UTF-8' => true,
223                  'CP866' => true, 'IBM866' => true, '866' => true,
224                  'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
225                  '1251' => true,
226                  'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
227                  'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
228                  'BIG5' => true, '950' => true,
229                  'GB2312' => true, '936' => true,
230                  'BIG5-HKSCS' => true,
231                  'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
232                  'EUC-JP' => true, 'EUCJP' => true,
233                  'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
234              ];
235   
236              if (isset($htmlspecialcharsCharsets[$charset])) {
237                  return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset);
238              }
239   
240              if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
241                  // cache the lowercase variant for future iterations
242                  $htmlspecialcharsCharsets[$charset] = true;
243   
244                  return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset);
245              }
246   
247              $string = twig_convert_encoding($string, 'UTF-8', $charset);
248              $string = htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8');
249   
250              return iconv('UTF-8', $charset, $string);
251   
252          case 'js':
253              // escape all non-alphanumeric characters
254              // into their \x or \uHHHH representations
255              if ('UTF-8' !== $charset) {
256                  $string = twig_convert_encoding($string, 'UTF-8', $charset);
257              }
258   
259              if (!preg_match('//u', $string)) {
260                  throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
261              }
262   
263              $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) {
264                  $char = $matches[0];
265   
266                  /*
267                   * A few characters have short escape sequences in JSON and JavaScript.
268                   * Escape sequences supported only by JavaScript, not JSON, are omitted.
269                   * \" is also supported but omitted, because the resulting string is not HTML safe.
270                   */
271                  static $shortMap = [
272                      '\\' => '\\\\',
273                      '/' => '\\/',
274                      "\x08" => '\b',
275                      "\x0C" => '\f',
276                      "\x0A" => '\n',
277                      "\x0D" => '\r',
278                      "\x09" => '\t',
279                  ];
280   
281                  if (isset($shortMap[$char])) {
282                      return $shortMap[$char];
283                  }
284   
285                  $codepoint = mb_ord($char, 'UTF-8');
286                  if (0x10000 > $codepoint) {
287                      return sprintf('\u%04X', $codepoint);
288                  }
289   
290                  // Split characters outside the BMP into surrogate pairs
291                  // https://tools.ietf.org/html/rfc2781.html#section-2.1
292                  $u = $codepoint - 0x10000;
293                  $high = 0xD800 | ($u >> 10);
294                  $low = 0xDC00 | ($u & 0x3FF);
295   
296                  return sprintf('\u%04X\u%04X', $high, $low);
297              }, $string);
298   
299              if ('UTF-8' !== $charset) {
300                  $string = iconv('UTF-8', $charset, $string);
301              }
302   
303              return $string;
304   
305          case 'css':
306              if ('UTF-8' !== $charset) {
307                  $string = twig_convert_encoding($string, 'UTF-8', $charset);
308              }
309   
310              if (!preg_match('//u', $string)) {
311                  throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
312              }
313   
314              $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) {
315                  $char = $matches[0];
316   
317                  return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8'));
318              }, $string);
319   
320              if ('UTF-8' !== $charset) {
321                  $string = iconv('UTF-8', $charset, $string);
322              }
323   
324              return $string;
325   
326          case 'html_attr':
327              if ('UTF-8' !== $charset) {
328                  $string = twig_convert_encoding($string, 'UTF-8', $charset);
329              }
330   
331              if (!preg_match('//u', $string)) {
332                  throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
333              }
334   
335              $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) {
336                  /**
337                   * This function is adapted from code coming from Zend Framework.
338                   *
339                   * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
340                   * @license   https://framework.zend.com/license/new-bsd New BSD License
341                   */
342                  $chr = $matches[0];
343                  $ord = \ord($chr);
344   
345                  /*
346                   * The following replaces characters undefined in HTML with the
347                   * hex entity for the Unicode replacement character.
348                   */
349                  if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) {
350                      return '&#xFFFD;';
351                  }
352   
353                  /*
354                   * Check if the current character to escape has a name entity we should
355                   * replace it with while grabbing the hex value of the character.
356                   */
357                  if (1 === \strlen($chr)) {
358                      /*
359                       * While HTML supports far more named entities, the lowest common denominator
360                       * has become HTML5's XML Serialisation which is restricted to the those named
361                       * entities that XML supports. Using HTML entities would result in this error:
362                       *     XML Parsing Error: undefined entity
363                       */
364                      static $entityMap = [
365                          34 => '&quot;', /* quotation mark */
366                          38 => '&amp;',  /* ampersand */
367                          60 => '&lt;',   /* less-than sign */
368                          62 => '&gt;',   /* greater-than sign */
369                      ];
370   
371                      if (isset($entityMap[$ord])) {
372                          return $entityMap[$ord];
373                      }
374   
375                      return sprintf('&#x%02X;', $ord);
376                  }
377   
378                  /*
379                   * Per OWASP recommendations, we'll use hex entities for any other
380                   * characters where a named entity does not exist.
381                   */
382                  return sprintf('&#x%04X;', mb_ord($chr, 'UTF-8'));
383              }, $string);
384   
385              if ('UTF-8' !== $charset) {
386                  $string = iconv('UTF-8', $charset, $string);
387              }
388   
389              return $string;
390   
391          case 'url':
392              return rawurlencode($string);
393   
394          default:
395              // check the ones set on CoreExtension for BC (to be removed in 3.0)
396              $legacyEscapers = $env->getExtension(CoreExtension::class)->getEscapers(false);
397              if (array_key_exists($strategy, $legacyEscapers)) {
398                  return $legacyEscapers[$strategy]($env, $string, $charset);
399              }
400   
401              $escapers = $env->getExtension(EscaperExtension::class)->getEscapers();
402              if (array_key_exists($strategy, $escapers)) {
403                  return $escapers[$strategy]($env, $string, $charset);
404              }
405   
406              $escapers = array_merge($legacyEscapers, $escapers);
407              $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers)));
408   
409              throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
410      }
411  }
412   
413  /**
414   * @internal
415   */
416  function twig_escape_filter_is_safe(Node $filterArgs)
417  {
418      foreach ($filterArgs as $arg) {
419          if ($arg instanceof ConstantExpression) {
420              return [$arg->getAttribute('value')];
421          }
422   
423          return [];
424      }
425   
426      return ['html'];
427  }
428  }
429