PluginProbe
Elementor Website Builder – more than just a page builder / 3.28.0-dev1
Elementor Website Builder – more than just a page builder v3.28.0-dev1
4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 All 452 releases
elementor / vendor_prefixed / twig / src / Runtime / EscaperRuntime.php

EscaperRuntime.php in Elementor Website Builder – more than just a page builder 3.28.0-dev1, at vendor_prefixed/twig/src/Runtime/EscaperRuntime.php

255 lines 12.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 namespace ElementorDeps\Twig\Runtime;
12
13 use ElementorDeps\Twig\Error\RuntimeError;
14 use ElementorDeps\Twig\Extension\RuntimeExtensionInterface;
15 use ElementorDeps\Twig\Markup;
16 final class EscaperRuntime implements RuntimeExtensionInterface
17 {
18 /** @var array<string, callable(string $string, string $charset): string> */
19 private $escapers = [];
20 /** @internal */
21 public $safeClasses = [];
22 /** @internal */
23 public $safeLookup = [];
24 private $charset;
25 public function __construct($charset = 'UTF-8')
26 {
27 $this->charset = $charset;
28 }
29 /**
30 * Defines a new escaper to be used via the escape filter.
31 *
32 * @param string $strategy The strategy name that should be used as a strategy in the escape call
33 * @param callable(string $string, string $charset): string $callable A valid PHP callable
34 */
35 public function setEscaper($strategy, callable $callable)
36 {
37 $this->escapers[$strategy] = $callable;
38 }
39 /**
40 * Gets all defined escapers.
41 *
42 * @return array<string, callable(string $string, string $charset): string> An array of escapers
43 */
44 public function getEscapers()
45 {
46 return $this->escapers;
47 }
48 public function setSafeClasses(array $safeClasses = [])
49 {
50 $this->safeClasses = [];
51 $this->safeLookup = [];
52 foreach ($safeClasses as $class => $strategies) {
53 $this->addSafeClass($class, $strategies);
54 }
55 }
56 public function addSafeClass(string $class, array $strategies)
57 {
58 $class = \ltrim($class, '\\');
59 if (!isset($this->safeClasses[$class])) {
60 $this->safeClasses[$class] = [];
61 }
62 $this->safeClasses[$class] = \array_merge($this->safeClasses[$class], $strategies);
63 foreach ($strategies as $strategy) {
64 $this->safeLookup[$strategy][$class] = \true;
65 }
66 }
67 /**
68 * Escapes a string.
69 *
70 * @param mixed $string The value to be escaped
71 * @param string $strategy The escaping strategy
72 * @param string|null $charset The charset
73 * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
74 *
75 * @throws RuntimeError
76 */
77 public function escape($string, string $strategy = 'html', ?string $charset = null, bool $autoescape = \false)
78 {
79 if ($autoescape && $string instanceof Markup) {
80 return $string;
81 }
82 if (!\is_string($string)) {
83 if (\is_object($string) && \method_exists($string, '__toString')) {
84 if ($autoescape) {
85 $c = \get_class($string);
86 if (!isset($this->safeClasses[$c])) {
87 $this->safeClasses[$c] = [];
88 foreach (\class_parents($string) + \class_implements($string) as $class) {
89 if (isset($this->safeClasses[$class])) {
90 $this->safeClasses[$c] = \array_unique(\array_merge($this->safeClasses[$c], $this->safeClasses[$class]));
91 foreach ($this->safeClasses[$class] as $s) {
92 $this->safeLookup[$s][$c] = \true;
93 }
94 }
95 }
96 }
97 if (isset($this->safeLookup[$strategy][$c]) || isset($this->safeLookup['all'][$c])) {
98 return (string) $string;
99 }
100 }
101 $string = (string) $string;
102 } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) {
103 // we return the input as is (which can be of any type)
104 return $string;
105 }
106 }
107 if ('' === $string) {
108 return '';
109 }
110 $charset = $charset ?: $this->charset;
111 switch ($strategy) {
112 case 'html':
113 // see https://www.php.net/htmlspecialchars
114 // Using a static variable to avoid initializing the array
115 // each time the function is called. Moving the declaration on the
116 // top of the function slow downs other escaping strategies.
117 static $htmlspecialcharsCharsets = ['ISO-8859-1' => \true, 'ISO8859-1' => \true, 'ISO-8859-15' => \true, 'ISO8859-15' => \true, 'utf-8' => \true, 'UTF-8' => \true, 'CP866' => \true, 'IBM866' => \true, '866' => \true, 'CP1251' => \true, 'WINDOWS-1251' => \true, 'WIN-1251' => \true, '1251' => \true, 'CP1252' => \true, 'WINDOWS-1252' => \true, '1252' => \true, 'KOI8-R' => \true, 'KOI8-RU' => \true, 'KOI8R' => \true, 'BIG5' => \true, '950' => \true, 'GB2312' => \true, '936' => \true, 'BIG5-HKSCS' => \true, 'SHIFT_JIS' => \true, 'SJIS' => \true, '932' => \true, 'EUC-JP' => \true, 'EUCJP' => \true, 'ISO8859-5' => \true, 'ISO-8859-5' => \true, 'MACROMAN' => \true];
118 if (isset($htmlspecialcharsCharsets[$charset])) {
119 return \htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset);
120 }
121 if (isset($htmlspecialcharsCharsets[\strtoupper($charset)])) {
122 // cache the lowercase variant for future iterations
123 $htmlspecialcharsCharsets[$charset] = \true;
124 return \htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset);
125 }
126 $string = $this->convertEncoding($string, 'UTF-8', $charset);
127 $string = \htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8');
128 return \iconv('UTF-8', $charset, $string);
129 case 'js':
130 // escape all non-alphanumeric characters
131 // into their \x or \uHHHH representations
132 if ('UTF-8' !== $charset) {
133 $string = $this->convertEncoding($string, 'UTF-8', $charset);
134 }
135 if (!\preg_match('//u', $string)) {
136 throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
137 }
138 $string = \preg_replace_callback('#[^a-zA-Z0-9,\\._]#Su', function ($matches) {
139 $char = $matches[0];
140 /*
141 * A few characters have short escape sequences in JSON and JavaScript.
142 * Escape sequences supported only by JavaScript, not JSON, are omitted.
143 * \" is also supported but omitted, because the resulting string is not HTML safe.
144 */
145 static $shortMap = ['\\' => '\\\\', '/' => '\\/', "\x08" => '\\b', "\f" => '\\f', "\n" => '\\n', "\r" => '\\r', "\t" => '\\t'];
146 if (isset($shortMap[$char])) {
147 return $shortMap[$char];
148 }
149 $codepoint = \mb_ord($char, 'UTF-8');
150 if (0x10000 > $codepoint) {
151 return \sprintf('\\u%04X', $codepoint);
152 }
153 // Split characters outside the BMP into surrogate pairs
154 // https://tools.ietf.org/html/rfc2781.html#section-2.1
155 $u = $codepoint - 0x10000;
156 $high = 0xd800 | $u >> 10;
157 $low = 0xdc00 | $u & 0x3ff;
158 return \sprintf('\\u%04X\\u%04X', $high, $low);
159 }, $string);
160 if ('UTF-8' !== $charset) {
161 $string = \iconv('UTF-8', $charset, $string);
162 }
163 return $string;
164 case 'css':
165 if ('UTF-8' !== $charset) {
166 $string = $this->convertEncoding($string, 'UTF-8', $charset);
167 }
168 if (!\preg_match('//u', $string)) {
169 throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
170 }
171 $string = \preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) {
172 $char = $matches[0];
173 return \sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : \mb_ord($char, 'UTF-8'));
174 }, $string);
175 if ('UTF-8' !== $charset) {
176 $string = \iconv('UTF-8', $charset, $string);
177 }
178 return $string;
179 case 'html_attr':
180 if ('UTF-8' !== $charset) {
181 $string = $this->convertEncoding($string, 'UTF-8', $charset);
182 }
183 if (!\preg_match('//u', $string)) {
184 throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
185 }
186 $string = \preg_replace_callback('#[^a-zA-Z0-9,\\.\\-_]#Su', function ($matches) {
187 /**
188 * This function is adapted from code coming from Zend Framework.
189 *
190 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
191 * @license https://framework.zend.com/license/new-bsd New BSD License
192 */
193 $chr = $matches[0];
194 $ord = \ord($chr);
195 /*
196 * The following replaces characters undefined in HTML with the
197 * hex entity for the Unicode replacement character.
198 */
199 if ($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr || $ord >= 0x7f && $ord <= 0x9f) {
200 return '&#xFFFD;';
201 }
202 /*
203 * Check if the current character to escape has a name entity we should
204 * replace it with while grabbing the hex value of the character.
205 */
206 if (1 === \strlen($chr)) {
207 /*
208 * While HTML supports far more named entities, the lowest common denominator
209 * has become HTML5's XML Serialisation which is restricted to the those named
210 * entities that XML supports. Using HTML entities would result in this error:
211 * XML Parsing Error: undefined entity
212 */
213 static $entityMap = [
214 34 => '&quot;',
215 /* quotation mark */
216 38 => '&amp;',
217 /* ampersand */
218 60 => '&lt;',
219 /* less-than sign */
220 62 => '&gt;',
221 ];
222 if (isset($entityMap[$ord])) {
223 return $entityMap[$ord];
224 }
225 return \sprintf('&#x%02X;', $ord);
226 }
227 /*
228 * Per OWASP recommendations, we'll use hex entities for any other
229 * characters where a named entity does not exist.
230 */
231 return \sprintf('&#x%04X;', \mb_ord($chr, 'UTF-8'));
232 }, $string);
233 if ('UTF-8' !== $charset) {
234 $string = \iconv('UTF-8', $charset, $string);
235 }
236 return $string;
237 case 'url':
238 return \rawurlencode($string);
239 default:
240 if (\array_key_exists($strategy, $this->escapers)) {
241 return $this->escapers[$strategy]($string, $charset);
242 }
243 $validStrategies = \implode('", "', \array_merge(['html', 'js', 'url', 'css', 'html_attr'], \array_keys($this->escapers)));
244 throw new RuntimeError(\sprintf('Invalid escaping strategy "%s" (valid ones: "%s").', $strategy, $validStrategies));
245 }
246 }
247 private function convertEncoding(string $string, string $to, string $from)
248 {
249 if (!\function_exists('iconv')) {
250 throw new RuntimeError('Unable to convert encoding: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.');
251 }
252 return \iconv($from, $to, $string);
253 }
254 }
255