Fabien Potencier authored on 21/05/2019 14:03:11
Showing 3 changed files
... ...
@@ -14,17 +14,17 @@ use Twig\FileExtensionEscapingStrategy;
14 14
 use Twig\NodeVisitor\EscaperNodeVisitor;
15 15
 use Twig\TokenParser\AutoEscapeTokenParser;
16 16
 use Twig\TwigFilter;
17
-use Twig\Environment;
18
-use Twig\Error\RuntimeError;
19
-use Twig\Extension\CoreExtension;
20
-use Twig\Markup;
21 17
 
22 18
 final class EscaperExtension extends AbstractExtension
23 19
 {
24 20
     private $defaultStrategy;
25 21
     private $escapers = [];
26
-    private $safeClasses = [];
27
-    private $safeLookup = [];
22
+
23
+    /** @internal */
24
+    public $safeClasses = [];
25
+
26
+    /** @internal */
27
+    public $safeLookup = [];
28 28
 
29 29
     /**
30 30
      * @param string|false|callable $defaultStrategy An escaping strategy
... ...
@@ -49,8 +49,8 @@ final class EscaperExtension extends AbstractExtension
49 49
     public function getFilters()
50 50
     {
51 51
         return [
52
-            new TwigFilter('escape', [$this, 'escape'], ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
53
-            new TwigFilter('e', [$this, 'escape'], ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
52
+            new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
53
+            new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
54 54
             new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]),
55 55
         ];
56 56
     }
... ...
@@ -132,287 +132,281 @@ final class EscaperExtension extends AbstractExtension
132 132
             $this->safeLookup[$strategy][$class] = true;
133 133
         }
134 134
     }
135
+}
135 136
 
136
-    /**
137
-     * Escapes a string.
138
-     *
139
-     * @param mixed  $string     The value to be escaped
140
-     * @param string $strategy   The escaping strategy
141
-     * @param string $charset    The charset
142
-     * @param bool   $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
143
-     *
144
-     * @return string
145
-     */
146
-    function escape(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
147
-    {
148
-        if ($autoescape && $string instanceof Markup) {
149
-            return $string;
150
-        }
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
+}
151 160
 
152
-        if (!\is_string($string)) {
153
-            if (\is_object($string) && method_exists($string, '__toString')) {
154
-                if ($autoescape) {
155
-                    $c = get_class($string);
156
-                    if (!isset($this->safeClasses[$c])) {
157
-                        $this->safeClasses[$c] = [];
158
-                        foreach (class_parents($string) + class_implements($string) as $class) {
159
-                            if (isset($this->safeClasses[$class])) {
160
-                                $this->safeClasses[$c] = array_unique(array_merge($this->safeClasses[$c], $this->safeClasses[$class]));
161
-                                foreach ($this->safeClasses[$class] as $s) {
162
-                                    $this->safeLookup[$s][$c] = true;
163
-                                }
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;
164 189
                             }
165 190
                         }
166 191
                     }
167
-                    if (isset($this->safeLookup[$strategy][$c]) || isset($this->safeLookup['all'][$c])) {
168
-                        return (string) $string;
169
-                    }
170 192
                 }
171
-
172
-                $string = (string) $string;
173
-            } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) {
174
-                return $string;
193
+                if (isset($ext->safeLookup[$strategy][$c]) || isset($ext->safeLookup['all'][$c])) {
194
+                    return (string) $string;
195
+                }
175 196
             }
176
-        }
177 197
 
178
-        if ('' === $string) {
179
-            return '';
198
+            $string = (string) $string;
199
+        } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) {
200
+            return $string;
180 201
         }
202
+    }
181 203
 
182
-        if (null === $charset) {
183
-            $charset = $env->getCharset();
184
-        }
204
+    if ('' === $string) {
205
+        return '';
206
+    }
185 207
 
186
-        switch ($strategy) {
187
-            case 'html':
188
-                // see https://secure.php.net/htmlspecialchars
189
-
190
-                // Using a static variable to avoid initializing the array
191
-                // each time the function is called. Moving the declaration on the
192
-                // top of the function slow downs other escaping strategies.
193
-                static $htmlspecialcharsCharsets = [
194
-                    'ISO-8859-1' => true, 'ISO8859-1' => true,
195
-                    'ISO-8859-15' => true, 'ISO8859-15' => true,
196
-                    'utf-8' => true, 'UTF-8' => true,
197
-                    'CP866' => true, 'IBM866' => true, '866' => true,
198
-                    'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
199
-                    '1251' => true,
200
-                    'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
201
-                    'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
202
-                    'BIG5' => true, '950' => true,
203
-                    'GB2312' => true, '936' => true,
204
-                    'BIG5-HKSCS' => true,
205
-                    'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
206
-                    'EUC-JP' => true, 'EUCJP' => true,
207
-                    'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
208
-                ];
208
+    if (null === $charset) {
209
+        $charset = $env->getCharset();
210
+    }
209 211
 
210
-                if (isset($htmlspecialcharsCharsets[$charset])) {
211
-                    return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
212
-                }
212
+    switch ($strategy) {
213
+        case 'html':
214
+            // see https://secure.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
+            }
213 239
 
214
-                if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
215
-                    // cache the lowercase variant for future iterations
216
-                    $htmlspecialcharsCharsets[$charset] = true;
240
+            if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
241
+                // cache the lowercase variant for future iterations
242
+                $htmlspecialcharsCharsets[$charset] = true;
217 243
 
218
-                    return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
219
-                }
244
+                return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
245
+            }
220 246
 
221
-                $string = iconv($charset, 'UTF-8', $string);
222
-                $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
247
+            $string = iconv($charset, 'UTF-8', $string);
248
+            $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
223 249
 
224
-                return iconv('UTF-8', $charset, $string);
250
+            return iconv('UTF-8', $charset, $string);
225 251
 
226
-            case 'js':
227
-                // escape all non-alphanumeric characters
228
-                // into their \x or \uHHHH representations
229
-                if ('UTF-8' !== $charset) {
230
-                    $string = iconv($charset, 'UTF-8', $string);
231
-                }
252
+        case 'js':
253
+            // escape all non-alphanumeric characters
254
+            // into their \x or \uHHHH representations
255
+            if ('UTF-8' !== $charset) {
256
+                $string = iconv($charset, 'UTF-8', $string);
257
+            }
232 258
 
233
-                if (!preg_match('//u', $string)) {
234
-                    throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
235
-                }
259
+            if (!preg_match('//u', $string)) {
260
+                throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
261
+            }
236 262
 
237
-                $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) {
238
-                    $char = $matches[0];
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 ommitted.
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
+                ];
239 280
 
240
-                    /*
241
-                    * A few characters have short escape sequences in JSON and JavaScript.
242
-                    * Escape sequences supported only by JavaScript, not JSON, are ommitted.
243
-                    * \" is also supported but omitted, because the resulting string is not HTML safe.
244
-                    */
245
-                    static $shortMap = [
246
-                        '\\' => '\\\\',
247
-                        '/' => '\\/',
248
-                        "\x08" => '\b',
249
-                        "\x0C" => '\f',
250
-                        "\x0A" => '\n',
251
-                        "\x0D" => '\r',
252
-                        "\x09" => '\t',
253
-                    ];
281
+                if (isset($shortMap[$char])) {
282
+                    return $shortMap[$char];
283
+                }
254 284
 
255
-                    if (isset($shortMap[$char])) {
256
-                        return $shortMap[$char];
257
-                    }
285
+                // \uHHHH
286
+                $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
287
+                $char = strtoupper(bin2hex($char));
258 288
 
259
-                    // \uHHHH
260
-                    $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
261
-                    $char = strtoupper(bin2hex($char));
289
+                if (4 >= \strlen($char)) {
290
+                    return sprintf('\u%04s', $char);
291
+                }
262 292
 
263
-                    if (4 >= \strlen($char)) {
264
-                        return sprintf('\u%04s', $char);
265
-                    }
293
+                return sprintf('\u%04s\u%04s', substr($char, 0, -4), substr($char, -4));
294
+            }, $string);
266 295
 
267
-                    return sprintf('\u%04s\u%04s', substr($char, 0, -4), substr($char, -4));
268
-                }, $string);
296
+            if ('UTF-8' !== $charset) {
297
+                $string = iconv('UTF-8', $charset, $string);
298
+            }
269 299
 
270
-                if ('UTF-8' !== $charset) {
271
-                    $string = iconv('UTF-8', $charset, $string);
272
-                }
300
+            return $string;
273 301
 
274
-                return $string;
302
+        case 'css':
303
+            if ('UTF-8' !== $charset) {
304
+                $string = iconv($charset, 'UTF-8', $string);
305
+            }
275 306
 
276
-            case 'css':
277
-                if ('UTF-8' !== $charset) {
278
-                    $string = iconv($charset, 'UTF-8', $string);
279
-                }
307
+            if (!preg_match('//u', $string)) {
308
+                throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
309
+            }
280 310
 
281
-                if (!preg_match('//u', $string)) {
282
-                    throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
283
-                }
311
+            $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) {
312
+                $char = $matches[0];
284 313
 
285
-                $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) {
286
-                    $char = $matches[0];
314
+                return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8'));
315
+            }, $string);
287 316
 
288
-                    return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8'));
289
-                }, $string);
317
+            if ('UTF-8' !== $charset) {
318
+                $string = iconv('UTF-8', $charset, $string);
319
+            }
290 320
 
291
-                if ('UTF-8' !== $charset) {
292
-                    $string = iconv('UTF-8', $charset, $string);
293
-                }
321
+            return $string;
294 322
 
295
-                return $string;
323
+        case 'html_attr':
324
+            if ('UTF-8' !== $charset) {
325
+                $string = iconv($charset, 'UTF-8', $string);
326
+            }
296 327
 
297
-            case 'html_attr':
298
-                if ('UTF-8' !== $charset) {
299
-                    $string = iconv($charset, 'UTF-8', $string);
300
-                }
328
+            if (!preg_match('//u', $string)) {
329
+                throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
330
+            }
301 331
 
302
-                if (!preg_match('//u', $string)) {
303
-                    throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
332
+            $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) {
333
+                /**
334
+                 * This function is adapted from code coming from Zend Framework.
335
+                 *
336
+                 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
337
+                 * @license   https://framework.zend.com/license/new-bsd New BSD License
338
+                 */
339
+                $chr = $matches[0];
340
+                $ord = \ord($chr);
341
+
342
+                /*
343
+                 * The following replaces characters undefined in HTML with the
344
+                 * hex entity for the Unicode replacement character.
345
+                 */
346
+                if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) {
347
+                    return '&#xFFFD;';
304 348
                 }
305 349
 
306
-                $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) {
307
-                    /**
308
-                     * This function is adapted from code coming from Zend Framework.
309
-                     *
310
-                     * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
311
-                     * @license   https://framework.zend.com/license/new-bsd New BSD License
312
-                     */
313
-                    $chr = $matches[0];
314
-                    $ord = \ord($chr);
315
-
316
-                    /*
317
-                    * The following replaces characters undefined in HTML with the
318
-                    * hex entity for the Unicode replacement character.
319
-                    */
320
-                    if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) {
321
-                        return '&#xFFFD;';
322
-                    }
323
-
350
+                /*
351
+                 * Check if the current character to escape has a name entity we should
352
+                 * replace it with while grabbing the hex value of the character.
353
+                 */
354
+                if (1 === \strlen($chr)) {
324 355
                     /*
325
-                    * Check if the current character to escape has a name entity we should
326
-                    * replace it with while grabbing the hex value of the character.
327
-                    */
328
-                    if (1 === \strlen($chr)) {
329
-                        /*
330
-                        * While HTML supports far more named entities, the lowest common denominator
331
-                        * has become HTML5's XML Serialisation which is restricted to the those named
332
-                        * entities that XML supports. Using HTML entities would result in this error:
333
-                        *     XML Parsing Error: undefined entity
334
-                        */
335
-                        static $entityMap = [
336
-                            34 => '&quot;', /* quotation mark */
337
-                            38 => '&amp;',  /* ampersand */
338
-                            60 => '&lt;',   /* less-than sign */
339
-                            62 => '&gt;',   /* greater-than sign */
340
-                        ];
341
-
342
-                        if (isset($entityMap[$ord])) {
343
-                            return $entityMap[$ord];
344
-                        }
356
+                     * While HTML supports far more named entities, the lowest common denominator
357
+                     * has become HTML5's XML Serialisation which is restricted to the those named
358
+                     * entities that XML supports. Using HTML entities would result in this error:
359
+                     *     XML Parsing Error: undefined entity
360
+                     */
361
+                    static $entityMap = [
362
+                        34 => '&quot;', /* quotation mark */
363
+                        38 => '&amp;',  /* ampersand */
364
+                        60 => '&lt;',   /* less-than sign */
365
+                        62 => '&gt;',   /* greater-than sign */
366
+                    ];
345 367
 
346
-                        return sprintf('&#x%02X;', $ord);
368
+                    if (isset($entityMap[$ord])) {
369
+                        return $entityMap[$ord];
347 370
                     }
348 371
 
349
-                    /*
350
-                    * Per OWASP recommendations, we'll use hex entities for any other
351
-                    * characters where a named entity does not exist.
352
-                    */
353
-                    return sprintf('&#x%04X;', mb_ord($chr, 'UTF-8'));
354
-                }, $string);
355
-
356
-                if ('UTF-8' !== $charset) {
357
-                    $string = iconv('UTF-8', $charset, $string);
372
+                    return sprintf('&#x%02X;', $ord);
358 373
                 }
359 374
 
360
-                return $string;
361
-
362
-            case 'url':
363
-                return rawurlencode($string);
364
-
365
-            default:
366
-                static $escapers;
375
+                /*
376
+                 * Per OWASP recommendations, we'll use hex entities for any other
377
+                 * characters where a named entity does not exist.
378
+                 */
379
+                return sprintf('&#x%04X;', mb_ord($chr, 'UTF-8'));
380
+            }, $string);
367 381
 
368
-                if (null === $escapers) {
369
-                    // merge the ones set on CoreExtension for BC (to be removed in 3.0)
370
-                    $escapers = array_merge(
371
-                        $env->getExtension(CoreExtension::class)->getEscapers(false),
372
-                        $env->getExtension(EscaperExtension::class)->getEscapers()
373
-                    );
374
-                }
382
+            if ('UTF-8' !== $charset) {
383
+                $string = iconv('UTF-8', $charset, $string);
384
+            }
375 385
 
376
-                if (isset($escapers[$strategy])) {
377
-                    return $escapers[$strategy]($env, $string, $charset);
378
-                }
386
+            return $string;
379 387
 
380
-                $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers)));
388
+        case 'url':
389
+            return rawurlencode($string);
381 390
 
382
-                throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
383
-        }
384
-    }
385
-}
391
+        default:
392
+            static $escapers;
386 393
 
387
-class_alias('Twig\Extension\EscaperExtension', 'Twig_Extension_Escaper');
388
-}
389
-
390
-namespace {
391
-use Twig\Environment;
392
-use Twig\Extension\EscaperExtension;
393
-use Twig\Node\Expression\ConstantExpression;
394
-use Twig\Node\Node;
394
+            if (null === $escapers) {
395
+                // merge the ones set on CoreExtension for BC (to be removed in 3.0)
396
+                $escapers = array_merge(
397
+                    $env->getExtension(CoreExtension::class)->getEscapers(false),
398
+                    $env->getExtension(EscaperExtension::class)->getEscapers()
399
+                );
400
+            }
395 401
 
396
-/**
397
- * Marks a variable as being safe.
398
- *
399
- * @param string $string A PHP variable
400
- *
401
- * @return string
402
- */
403
-function twig_raw_filter($string)
404
-{
405
-    return $string;
406
-}
402
+            if (isset($escapers[$strategy])) {
403
+                return $escapers[$strategy]($env, $string, $charset);
404
+            }
407 405
 
408
-/**
409
- * @deprecated since Twig 2.11, to be removed in 3.0; use EscaperExtension::escape instead
410
- */
411
-function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
412
-{
413
-    @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.11; use "%s::escape" instead.', __METHOD__, EscaperExtension::class), E_USER_DEPRECATED);
406
+            $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers)));
414 407
 
415
-    return $env->getExtension(EscaperExtension::class)->escape($env, $string, $strategy, $charset, $autoescape);
408
+            throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
409
+    }
416 410
 }
417 411
 
418 412
 /**
... ...
@@ -158,7 +158,7 @@ class Twig_Tests_Extension_EscaperTest extends \PHPUnit\Framework\TestCase
158 158
     {
159 159
         $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
160 160
         foreach ($this->htmlSpecialChars as $key => $value) {
161
-            $this->assertEquals($value, $twig->getExtension(EscaperExtension::class)->escape($twig, $key, 'html'), 'Failed to escape: '.$key);
161
+            $this->assertEquals($value, twig_escape_filter($twig, $key, 'html'), 'Failed to escape: '.$key);
162 162
         }
163 163
     }
164 164
 
... ...
@@ -166,7 +166,7 @@ class Twig_Tests_Extension_EscaperTest extends \PHPUnit\Framework\TestCase
166 166
     {
167 167
         $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
168 168
         foreach ($this->htmlAttrSpecialChars as $key => $value) {
169
-            $this->assertEquals($value, $twig->getExtension(EscaperExtension::class)->escape($twig, $key, 'html_attr'), 'Failed to escape: '.$key);
169
+            $this->assertEquals($value, twig_escape_filter($twig, $key, 'html_attr'), 'Failed to escape: '.$key);
170 170
         }
171 171
     }
172 172
 
... ...
@@ -174,47 +174,47 @@ class Twig_Tests_Extension_EscaperTest extends \PHPUnit\Framework\TestCase
174 174
     {
175 175
         $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
176 176
         foreach ($this->jsSpecialChars as $key => $value) {
177
-            $this->assertEquals($value, $twig->getExtension(EscaperExtension::class)->escape($twig, $key, 'js'), 'Failed to escape: '.$key);
177
+            $this->assertEquals($value, twig_escape_filter($twig, $key, 'js'), 'Failed to escape: '.$key);
178 178
         }
179 179
     }
180 180
 
181 181
     public function testJavascriptEscapingReturnsStringIfZeroLength()
182 182
     {
183 183
         $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
184
-        $this->assertEquals('', $twig->getExtension(EscaperExtension::class)->escape($twig, '', 'js'));
184
+        $this->assertEquals('', twig_escape_filter($twig, '', 'js'));
185 185
     }
186 186
 
187 187
     public function testJavascriptEscapingReturnsStringIfContainsOnlyDigits()
188 188
     {
189 189
         $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
190
-        $this->assertEquals('123', $twig->getExtension(EscaperExtension::class)->escape($twig, '123', 'js'));
190
+        $this->assertEquals('123', twig_escape_filter($twig, '123', 'js'));
191 191
     }
192 192
 
193 193
     public function testCssEscapingConvertsSpecialChars()
194 194
     {
195 195
         $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
196 196
         foreach ($this->cssSpecialChars as $key => $value) {
197
-            $this->assertEquals($value, $twig->getExtension(EscaperExtension::class)->escape($twig, $key, 'css'), 'Failed to escape: '.$key);
197
+            $this->assertEquals($value, twig_escape_filter($twig, $key, 'css'), 'Failed to escape: '.$key);
198 198
         }
199 199
     }
200 200
 
201 201
     public function testCssEscapingReturnsStringIfZeroLength()
202 202
     {
203 203
         $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
204
-        $this->assertEquals('', $twig->getExtension(EscaperExtension::class)->escape($twig, '', 'css'));
204
+        $this->assertEquals('', twig_escape_filter($twig, '', 'css'));
205 205
     }
206 206
 
207 207
     public function testCssEscapingReturnsStringIfContainsOnlyDigits()
208 208
     {
209 209
         $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
210
-        $this->assertEquals('123', $twig->getExtension(EscaperExtension::class)->escape($twig, '123', 'css'));
210
+        $this->assertEquals('123', twig_escape_filter($twig, '123', 'css'));
211 211
     }
212 212
 
213 213
     public function testUrlEscapingConvertsSpecialChars()
214 214
     {
215 215
         $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
216 216
         foreach ($this->urlSpecialChars as $key => $value) {
217
-            $this->assertEquals($value, $twig->getExtension(EscaperExtension::class)->escape($twig, $key, 'url'), 'Failed to escape: '.$key);
217
+            $this->assertEquals($value, twig_escape_filter($twig, $key, 'url'), 'Failed to escape: '.$key);
218 218
         }
219 219
     }
220 220
 
... ...
@@ -276,15 +276,15 @@ class Twig_Tests_Extension_EscaperTest extends \PHPUnit\Framework\TestCase
276 276
             || $chr >= 0x41 && $chr <= 0x5A
277 277
             || $chr >= 0x61 && $chr <= 0x7A) {
278 278
                 $literal = $this->codepointToUtf8($chr);
279
-                $this->assertEquals($literal, $twig->getExtension(EscaperExtension::class)->escape($twig, $literal, 'js'));
279
+                $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'js'));
280 280
             } else {
281 281
                 $literal = $this->codepointToUtf8($chr);
282 282
                 if (\in_array($literal, $immune)) {
283
-                    $this->assertEquals($literal, $twig->getExtension(EscaperExtension::class)->escape($twig, $literal, 'js'));
283
+                    $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'js'));
284 284
                 } else {
285 285
                     $this->assertNotEquals(
286 286
                         $literal,
287
-                        $twig->getExtension(EscaperExtension::class)->escape($twig, $literal, 'js'),
287
+                        twig_escape_filter($twig, $literal, 'js'),
288 288
                         "$literal should be escaped!");
289 289
                 }
290 290
             }
... ...
@@ -300,15 +300,15 @@ class Twig_Tests_Extension_EscaperTest extends \PHPUnit\Framework\TestCase
300 300
             || $chr >= 0x41 && $chr <= 0x5A
301 301
             || $chr >= 0x61 && $chr <= 0x7A) {
302 302
                 $literal = $this->codepointToUtf8($chr);
303
-                $this->assertEquals($literal, $twig->getExtension(EscaperExtension::class)->escape($twig, $literal, 'html_attr'));
303
+                $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'html_attr'));
304 304
             } else {
305 305
                 $literal = $this->codepointToUtf8($chr);
306 306
                 if (\in_array($literal, $immune)) {
307
-                    $this->assertEquals($literal, $twig->getExtension(EscaperExtension::class)->escape($twig, $literal, 'html_attr'));
307
+                    $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'html_attr'));
308 308
                 } else {
309 309
                     $this->assertNotEquals(
310 310
                         $literal,
311
-                        $twig->getExtension(EscaperExtension::class)->escape($twig, $literal, 'html_attr'),
311
+                        twig_escape_filter($twig, $literal, 'html_attr'),
312 312
                         "$literal should be escaped!");
313 313
                 }
314 314
             }
... ...
@@ -324,12 +324,12 @@ class Twig_Tests_Extension_EscaperTest extends \PHPUnit\Framework\TestCase
324 324
             || $chr >= 0x41 && $chr <= 0x5A
325 325
             || $chr >= 0x61 && $chr <= 0x7A) {
326 326
                 $literal = $this->codepointToUtf8($chr);
327
-                $this->assertEquals($literal, $twig->getExtension(EscaperExtension::class)->escape($twig, $literal, 'css'));
327
+                $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'css'));
328 328
             } else {
329 329
                 $literal = $this->codepointToUtf8($chr);
330 330
                 $this->assertNotEquals(
331 331
                     $literal,
332
-                    $twig->getExtension(EscaperExtension::class)->escape($twig, $literal, 'css'),
332
+                    twig_escape_filter($twig, $literal, 'css'),
333 333
                     "$literal should be escaped!");
334 334
             }
335 335
         }
... ...
@@ -343,7 +343,7 @@ class Twig_Tests_Extension_EscaperTest extends \PHPUnit\Framework\TestCase
343 343
         $twig = new Environment($this->getMockBuilder(LoaderInterface::class)->getMock());
344 344
         $twig->getExtension(EscaperExtension::class)->setEscaper('foo', 'foo_escaper_for_test');
345 345
 
346
-        $this->assertSame($expected, $twig->getExtension(EscaperExtension::class)->escape($twig, $string, $strategy));
346
+        $this->assertSame($expected, twig_escape_filter($twig, $string, $strategy));
347 347
     }
348 348
 
349 349
     public function provideCustomEscaperCases()
... ...
@@ -360,8 +360,7 @@ class Twig_Tests_Extension_EscaperTest extends \PHPUnit\Framework\TestCase
360 360
      */
361 361
     public function testUnknownCustomEscaper()
362 362
     {
363
-        $twig = new Environment($this->getMockBuilder(LoaderInterface::class)->getMock());
364
-        $twig->getExtension(EscaperExtension::class)->escape($twig, 'foo', 'bar');
363
+        twig_escape_filter(new Environment($this->getMockBuilder(LoaderInterface::class)->getMock()), 'foo', 'bar');
365 364
     }
366 365
 
367 366
     /**
... ...
@@ -372,9 +371,10 @@ class Twig_Tests_Extension_EscaperTest extends \PHPUnit\Framework\TestCase
372 371
         $obj = new Twig_Tests_Extension_TestClass();
373 372
         $twig = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock());
374 373
         $twig->getExtension('\Twig\Extension\EscaperExtension')->setSafeClasses($safeClasses);
375
-        $this->assertSame($escapedHtml, $twig->getExtension(EscaperExtension::class)->escape($twig, $obj, 'html', null, true));
376
-        $this->assertSame($escapedJs, $twig->getExtension(EscaperExtension::class)->escape($twig, $obj, 'js', null, true));
374
+        $this->assertSame($escapedHtml, twig_escape_filter($twig, $obj, 'html', null, true));
375
+        $this->assertSame($escapedJs, twig_escape_filter($twig, $obj, 'js', null, true));
377 376
     }
377
+
378 378
     public function provideObjectsForEscaping()
379 379
     {
380 380
         return [
... ...
@@ -22,7 +22,6 @@ use Twig\TokenParser\AbstractTokenParser;
22 22
 use Twig\TwigFilter;
23 23
 use Twig\TwigFunction;
24 24
 use Twig\TwigTest;
25
-use Twig\Extension\EscaperExtension;
26 25
 
27 26
 // This function is defined to check that escaping strategies
28 27
 // like html works even if a function with the same name is defined.
... ...
@@ -204,7 +203,7 @@ class TwigTestExtension extends AbstractExtension
204 203
      */
205 204
     public function escape_and_nl2br($env, $value, $sep = '<br />')
206 205
     {
207
-        return $this->nl2br($env->getExtension(EscaperExtension::class)->escape($env, $value, 'html'), $sep);
206
+        return $this->nl2br(twig_escape_filter($env, $value, 'html'), $sep);
208 207
     }
209 208
 
210 209
     /**