diff options
Diffstat (limited to 'inc/Twig/Extension')
-rw-r--r-- | inc/Twig/Extension/Core.php | 1355 | ||||
-rw-r--r-- | inc/Twig/Extension/Debug.php | 71 | ||||
-rw-r--r-- | inc/Twig/Extension/Escaper.php | 107 | ||||
-rw-r--r-- | inc/Twig/Extension/Optimizer.php | 35 | ||||
-rw-r--r-- | inc/Twig/Extension/Sandbox.php | 112 | ||||
-rw-r--r-- | inc/Twig/Extension/Staging.php | 113 | ||||
-rw-r--r-- | inc/Twig/Extension/StringLoader.php | 64 |
7 files changed, 1857 insertions, 0 deletions
diff --git a/inc/Twig/Extension/Core.php b/inc/Twig/Extension/Core.php new file mode 100644 index 00000000..e68687b4 --- /dev/null +++ b/inc/Twig/Extension/Core.php | |||
@@ -0,0 +1,1355 @@ | |||
1 | <?php | ||
2 | |||
3 | if (!defined('ENT_SUBSTITUTE')) { | ||
4 | define('ENT_SUBSTITUTE', 8); | ||
5 | } | ||
6 | |||
7 | /* | ||
8 | * This file is part of Twig. | ||
9 | * | ||
10 | * (c) 2009 Fabien Potencier | ||
11 | * | ||
12 | * For the full copyright and license information, please view the LICENSE | ||
13 | * file that was distributed with this source code. | ||
14 | */ | ||
15 | class Twig_Extension_Core extends Twig_Extension | ||
16 | { | ||
17 | protected $dateFormats = array('F j, Y H:i', '%d days'); | ||
18 | protected $numberFormat = array(0, '.', ','); | ||
19 | protected $timezone = null; | ||
20 | |||
21 | /** | ||
22 | * Sets the default format to be used by the date filter. | ||
23 | * | ||
24 | * @param string $format The default date format string | ||
25 | * @param string $dateIntervalFormat The default date interval format string | ||
26 | */ | ||
27 | public function setDateFormat($format = null, $dateIntervalFormat = null) | ||
28 | { | ||
29 | if (null !== $format) { | ||
30 | $this->dateFormats[0] = $format; | ||
31 | } | ||
32 | |||
33 | if (null !== $dateIntervalFormat) { | ||
34 | $this->dateFormats[1] = $dateIntervalFormat; | ||
35 | } | ||
36 | } | ||
37 | |||
38 | /** | ||
39 | * Gets the default format to be used by the date filter. | ||
40 | * | ||
41 | * @return array The default date format string and the default date interval format string | ||
42 | */ | ||
43 | public function getDateFormat() | ||
44 | { | ||
45 | return $this->dateFormats; | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * Sets the default timezone to be used by the date filter. | ||
50 | * | ||
51 | * @param DateTimeZone|string $timezone The default timezone string or a DateTimeZone object | ||
52 | */ | ||
53 | public function setTimezone($timezone) | ||
54 | { | ||
55 | $this->timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * Gets the default timezone to be used by the date filter. | ||
60 | * | ||
61 | * @return DateTimeZone The default timezone currently in use | ||
62 | */ | ||
63 | public function getTimezone() | ||
64 | { | ||
65 | if (null === $this->timezone) { | ||
66 | $this->timezone = new DateTimeZone(date_default_timezone_get()); | ||
67 | } | ||
68 | |||
69 | return $this->timezone; | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Sets the default format to be used by the number_format filter. | ||
74 | * | ||
75 | * @param integer $decimal The number of decimal places to use. | ||
76 | * @param string $decimalPoint The character(s) to use for the decimal point. | ||
77 | * @param string $thousandSep The character(s) to use for the thousands separator. | ||
78 | */ | ||
79 | public function setNumberFormat($decimal, $decimalPoint, $thousandSep) | ||
80 | { | ||
81 | $this->numberFormat = array($decimal, $decimalPoint, $thousandSep); | ||
82 | } | ||
83 | |||
84 | /** | ||
85 | * Get the default format used by the number_format filter. | ||
86 | * | ||
87 | * @return array The arguments for number_format() | ||
88 | */ | ||
89 | public function getNumberFormat() | ||
90 | { | ||
91 | return $this->numberFormat; | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Returns the token parser instance to add to the existing list. | ||
96 | * | ||
97 | * @return array An array of Twig_TokenParser instances | ||
98 | */ | ||
99 | public function getTokenParsers() | ||
100 | { | ||
101 | return array( | ||
102 | new Twig_TokenParser_For(), | ||
103 | new Twig_TokenParser_If(), | ||
104 | new Twig_TokenParser_Extends(), | ||
105 | new Twig_TokenParser_Include(), | ||
106 | new Twig_TokenParser_Block(), | ||
107 | new Twig_TokenParser_Use(), | ||
108 | new Twig_TokenParser_Filter(), | ||
109 | new Twig_TokenParser_Macro(), | ||
110 | new Twig_TokenParser_Import(), | ||
111 | new Twig_TokenParser_From(), | ||
112 | new Twig_TokenParser_Set(), | ||
113 | new Twig_TokenParser_Spaceless(), | ||
114 | new Twig_TokenParser_Flush(), | ||
115 | new Twig_TokenParser_Do(), | ||
116 | new Twig_TokenParser_Embed(), | ||
117 | ); | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * Returns a list of filters to add to the existing list. | ||
122 | * | ||
123 | * @return array An array of filters | ||
124 | */ | ||
125 | public function getFilters() | ||
126 | { | ||
127 | $filters = array( | ||
128 | // formatting filters | ||
129 | new Twig_SimpleFilter('date', 'twig_date_format_filter', array('needs_environment' => true)), | ||
130 | new Twig_SimpleFilter('date_modify', 'twig_date_modify_filter', array('needs_environment' => true)), | ||
131 | new Twig_SimpleFilter('format', 'sprintf'), | ||
132 | new Twig_SimpleFilter('replace', 'strtr'), | ||
133 | new Twig_SimpleFilter('number_format', 'twig_number_format_filter', array('needs_environment' => true)), | ||
134 | new Twig_SimpleFilter('abs', 'abs'), | ||
135 | |||
136 | // encoding | ||
137 | new Twig_SimpleFilter('url_encode', 'twig_urlencode_filter'), | ||
138 | new Twig_SimpleFilter('json_encode', 'twig_jsonencode_filter'), | ||
139 | new Twig_SimpleFilter('convert_encoding', 'twig_convert_encoding'), | ||
140 | |||
141 | // string filters | ||
142 | new Twig_SimpleFilter('title', 'twig_title_string_filter', array('needs_environment' => true)), | ||
143 | new Twig_SimpleFilter('capitalize', 'twig_capitalize_string_filter', array('needs_environment' => true)), | ||
144 | new Twig_SimpleFilter('upper', 'strtoupper'), | ||
145 | new Twig_SimpleFilter('lower', 'strtolower'), | ||
146 | new Twig_SimpleFilter('striptags', 'strip_tags'), | ||
147 | new Twig_SimpleFilter('trim', 'trim'), | ||
148 | new Twig_SimpleFilter('nl2br', 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))), | ||
149 | |||
150 | // array helpers | ||
151 | new Twig_SimpleFilter('join', 'twig_join_filter'), | ||
152 | new Twig_SimpleFilter('split', 'twig_split_filter'), | ||
153 | new Twig_SimpleFilter('sort', 'twig_sort_filter'), | ||
154 | new Twig_SimpleFilter('merge', 'twig_array_merge'), | ||
155 | new Twig_SimpleFilter('batch', 'twig_array_batch'), | ||
156 | |||
157 | // string/array filters | ||
158 | new Twig_SimpleFilter('reverse', 'twig_reverse_filter', array('needs_environment' => true)), | ||
159 | new Twig_SimpleFilter('length', 'twig_length_filter', array('needs_environment' => true)), | ||
160 | new Twig_SimpleFilter('slice', 'twig_slice', array('needs_environment' => true)), | ||
161 | new Twig_SimpleFilter('first', 'twig_first', array('needs_environment' => true)), | ||
162 | new Twig_SimpleFilter('last', 'twig_last', array('needs_environment' => true)), | ||
163 | |||
164 | // iteration and runtime | ||
165 | new Twig_SimpleFilter('default', '_twig_default_filter', array('node_class' => 'Twig_Node_Expression_Filter_Default')), | ||
166 | new Twig_SimpleFilter('keys', 'twig_get_array_keys_filter'), | ||
167 | |||
168 | // escaping | ||
169 | new Twig_SimpleFilter('escape', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')), | ||
170 | new Twig_SimpleFilter('e', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')), | ||
171 | ); | ||
172 | |||
173 | if (function_exists('mb_get_info')) { | ||
174 | $filters[] = new Twig_SimpleFilter('upper', 'twig_upper_filter', array('needs_environment' => true)); | ||
175 | $filters[] = new Twig_SimpleFilter('lower', 'twig_lower_filter', array('needs_environment' => true)); | ||
176 | } | ||
177 | |||
178 | return $filters; | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * Returns a list of global functions to add to the existing list. | ||
183 | * | ||
184 | * @return array An array of global functions | ||
185 | */ | ||
186 | public function getFunctions() | ||
187 | { | ||
188 | return array( | ||
189 | new Twig_SimpleFunction('range', 'range'), | ||
190 | new Twig_SimpleFunction('constant', 'twig_constant'), | ||
191 | new Twig_SimpleFunction('cycle', 'twig_cycle'), | ||
192 | new Twig_SimpleFunction('random', 'twig_random', array('needs_environment' => true)), | ||
193 | new Twig_SimpleFunction('date', 'twig_date_converter', array('needs_environment' => true)), | ||
194 | new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true, 'is_safe' => array('all'))), | ||
195 | ); | ||
196 | } | ||
197 | |||
198 | /** | ||
199 | * Returns a list of tests to add to the existing list. | ||
200 | * | ||
201 | * @return array An array of tests | ||
202 | */ | ||
203 | public function getTests() | ||
204 | { | ||
205 | return array( | ||
206 | new Twig_SimpleTest('even', null, array('node_class' => 'Twig_Node_Expression_Test_Even')), | ||
207 | new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')), | ||
208 | new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')), | ||
209 | new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')), | ||
210 | new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')), | ||
211 | new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')), | ||
212 | new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')), | ||
213 | new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')), | ||
214 | new Twig_SimpleTest('empty', 'twig_test_empty'), | ||
215 | new Twig_SimpleTest('iterable', 'twig_test_iterable'), | ||
216 | ); | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * Returns a list of operators to add to the existing list. | ||
221 | * | ||
222 | * @return array An array of operators | ||
223 | */ | ||
224 | public function getOperators() | ||
225 | { | ||
226 | return array( | ||
227 | array( | ||
228 | 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'), | ||
229 | '-' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Neg'), | ||
230 | '+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'), | ||
231 | ), | ||
232 | array( | ||
233 | 'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
234 | 'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
235 | 'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
236 | 'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
237 | 'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
238 | '==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
239 | '!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
240 | '<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
241 | '>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
242 | '>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
243 | '<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
244 | 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
245 | 'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
246 | '..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
247 | '+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
248 | '-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
249 | '~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
250 | '*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
251 | '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
252 | '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
253 | '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
254 | 'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
255 | 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), | ||
256 | '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT), | ||
257 | ), | ||
258 | ); | ||
259 | } | ||
260 | |||
261 | public function parseNotTestExpression(Twig_Parser $parser, $node) | ||
262 | { | ||
263 | return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine()); | ||
264 | } | ||
265 | |||
266 | public function parseTestExpression(Twig_Parser $parser, $node) | ||
267 | { | ||
268 | $stream = $parser->getStream(); | ||
269 | $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); | ||
270 | $arguments = null; | ||
271 | if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { | ||
272 | $arguments = $parser->getExpressionParser()->parseArguments(true); | ||
273 | } | ||
274 | |||
275 | $class = $this->getTestNodeClass($parser, $name, $node->getLine()); | ||
276 | |||
277 | return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine()); | ||
278 | } | ||
279 | |||
280 | protected function getTestNodeClass(Twig_Parser $parser, $name, $line) | ||
281 | { | ||
282 | $env = $parser->getEnvironment(); | ||
283 | $testMap = $env->getTests(); | ||
284 | if (!isset($testMap[$name])) { | ||
285 | $message = sprintf('The test "%s" does not exist', $name); | ||
286 | if ($alternatives = $env->computeAlternatives($name, array_keys($env->getTests()))) { | ||
287 | $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); | ||
288 | } | ||
289 | |||
290 | throw new Twig_Error_Syntax($message, $line, $parser->getFilename()); | ||
291 | } | ||
292 | |||
293 | if ($testMap[$name] instanceof Twig_SimpleTest) { | ||
294 | return $testMap[$name]->getNodeClass(); | ||
295 | } | ||
296 | |||
297 | return $testMap[$name] instanceof Twig_Test_Node ? $testMap[$name]->getClass() : 'Twig_Node_Expression_Test'; | ||
298 | } | ||
299 | |||
300 | /** | ||
301 | * Returns the name of the extension. | ||
302 | * | ||
303 | * @return string The extension name | ||
304 | */ | ||
305 | public function getName() | ||
306 | { | ||
307 | return 'core'; | ||
308 | } | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * Cycles over a value. | ||
313 | * | ||
314 | * @param ArrayAccess|array $values An array or an ArrayAccess instance | ||
315 | * @param integer $position The cycle position | ||
316 | * | ||
317 | * @return string The next value in the cycle | ||
318 | */ | ||
319 | function twig_cycle($values, $position) | ||
320 | { | ||
321 | if (!is_array($values) && !$values instanceof ArrayAccess) { | ||
322 | return $values; | ||
323 | } | ||
324 | |||
325 | return $values[$position % count($values)]; | ||
326 | } | ||
327 | |||
328 | /** | ||
329 | * Returns a random value depending on the supplied parameter type: | ||
330 | * - a random item from a Traversable or array | ||
331 | * - a random character from a string | ||
332 | * - a random integer between 0 and the integer parameter | ||
333 | * | ||
334 | * @param Twig_Environment $env A Twig_Environment instance | ||
335 | * @param Traversable|array|integer|string $values The values to pick a random item from | ||
336 | * | ||
337 | * @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is). | ||
338 | * | ||
339 | * @return mixed A random value from the given sequence | ||
340 | */ | ||
341 | function twig_random(Twig_Environment $env, $values = null) | ||
342 | { | ||
343 | if (null === $values) { | ||
344 | return mt_rand(); | ||
345 | } | ||
346 | |||
347 | if (is_int($values) || is_float($values)) { | ||
348 | return $values < 0 ? mt_rand($values, 0) : mt_rand(0, $values); | ||
349 | } | ||
350 | |||
351 | if ($values instanceof Traversable) { | ||
352 | $values = iterator_to_array($values); | ||
353 | } elseif (is_string($values)) { | ||
354 | if ('' === $values) { | ||
355 | return ''; | ||
356 | } | ||
357 | if (null !== $charset = $env->getCharset()) { | ||
358 | if ('UTF-8' != $charset) { | ||
359 | $values = twig_convert_encoding($values, 'UTF-8', $charset); | ||
360 | } | ||
361 | |||
362 | // unicode version of str_split() | ||
363 | // split at all positions, but not after the start and not before the end | ||
364 | $values = preg_split('/(?<!^)(?!$)/u', $values); | ||
365 | |||
366 | if ('UTF-8' != $charset) { | ||
367 | foreach ($values as $i => $value) { | ||
368 | $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8'); | ||
369 | } | ||
370 | } | ||
371 | } else { | ||
372 | return $values[mt_rand(0, strlen($values) - 1)]; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | if (!is_array($values)) { | ||
377 | return $values; | ||
378 | } | ||
379 | |||
380 | if (0 === count($values)) { | ||
381 | throw new Twig_Error_Runtime('The random function cannot pick from an empty array.'); | ||
382 | } | ||
383 | |||
384 | return $values[array_rand($values, 1)]; | ||
385 | } | ||
386 | |||
387 | /** | ||
388 | * Converts a date to the given format. | ||
389 | * | ||
390 | * <pre> | ||
391 | * {{ post.published_at|date("m/d/Y") }} | ||
392 | * </pre> | ||
393 | * | ||
394 | * @param Twig_Environment $env A Twig_Environment instance | ||
395 | * @param DateTime|DateInterval|string $date A date | ||
396 | * @param string $format A format | ||
397 | * @param DateTimeZone|string $timezone A timezone | ||
398 | * | ||
399 | * @return string The formatted date | ||
400 | */ | ||
401 | function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null) | ||
402 | { | ||
403 | if (null === $format) { | ||
404 | $formats = $env->getExtension('core')->getDateFormat(); | ||
405 | $format = $date instanceof DateInterval ? $formats[1] : $formats[0]; | ||
406 | } | ||
407 | |||
408 | if ($date instanceof DateInterval) { | ||
409 | return $date->format($format); | ||
410 | } | ||
411 | |||
412 | return twig_date_converter($env, $date, $timezone)->format($format); | ||
413 | } | ||
414 | |||
415 | /** | ||
416 | * Returns a new date object modified | ||
417 | * | ||
418 | * <pre> | ||
419 | * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }} | ||
420 | * </pre> | ||
421 | * | ||
422 | * @param Twig_Environment $env A Twig_Environment instance | ||
423 | * @param DateTime|string $date A date | ||
424 | * @param string $modifier A modifier string | ||
425 | * | ||
426 | * @return DateTime A new date object | ||
427 | */ | ||
428 | function twig_date_modify_filter(Twig_Environment $env, $date, $modifier) | ||
429 | { | ||
430 | $date = twig_date_converter($env, $date, false); | ||
431 | $date->modify($modifier); | ||
432 | |||
433 | return $date; | ||
434 | } | ||
435 | |||
436 | /** | ||
437 | * Converts an input to a DateTime instance. | ||
438 | * | ||
439 | * <pre> | ||
440 | * {% if date(user.created_at) < date('+2days') %} | ||
441 | * {# do something #} | ||
442 | * {% endif %} | ||
443 | * </pre> | ||
444 | * | ||
445 | * @param Twig_Environment $env A Twig_Environment instance | ||
446 | * @param DateTime|string $date A date | ||
447 | * @param DateTimeZone|string $timezone A timezone | ||
448 | * | ||
449 | * @return DateTime A DateTime instance | ||
450 | */ | ||
451 | function twig_date_converter(Twig_Environment $env, $date = null, $timezone = null) | ||
452 | { | ||
453 | // determine the timezone | ||
454 | if (!$timezone) { | ||
455 | $defaultTimezone = $env->getExtension('core')->getTimezone(); | ||
456 | } elseif (!$timezone instanceof DateTimeZone) { | ||
457 | $defaultTimezone = new DateTimeZone($timezone); | ||
458 | } else { | ||
459 | $defaultTimezone = $timezone; | ||
460 | } | ||
461 | |||
462 | if ($date instanceof DateTime) { | ||
463 | $date = clone $date; | ||
464 | if (false !== $timezone) { | ||
465 | $date->setTimezone($defaultTimezone); | ||
466 | } | ||
467 | |||
468 | return $date; | ||
469 | } | ||
470 | |||
471 | $asString = (string) $date; | ||
472 | if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) { | ||
473 | $date = '@'.$date; | ||
474 | } | ||
475 | |||
476 | $date = new DateTime($date, $defaultTimezone); | ||
477 | if (false !== $timezone) { | ||
478 | $date->setTimezone($defaultTimezone); | ||
479 | } | ||
480 | |||
481 | return $date; | ||
482 | } | ||
483 | |||
484 | /** | ||
485 | * Number format filter. | ||
486 | * | ||
487 | * All of the formatting options can be left null, in that case the defaults will | ||
488 | * be used. Supplying any of the parameters will override the defaults set in the | ||
489 | * environment object. | ||
490 | * | ||
491 | * @param Twig_Environment $env A Twig_Environment instance | ||
492 | * @param mixed $number A float/int/string of the number to format | ||
493 | * @param integer $decimal The number of decimal points to display. | ||
494 | * @param string $decimalPoint The character(s) to use for the decimal point. | ||
495 | * @param string $thousandSep The character(s) to use for the thousands separator. | ||
496 | * | ||
497 | * @return string The formatted number | ||
498 | */ | ||
499 | function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null) | ||
500 | { | ||
501 | $defaults = $env->getExtension('core')->getNumberFormat(); | ||
502 | if (null === $decimal) { | ||
503 | $decimal = $defaults[0]; | ||
504 | } | ||
505 | |||
506 | if (null === $decimalPoint) { | ||
507 | $decimalPoint = $defaults[1]; | ||
508 | } | ||
509 | |||
510 | if (null === $thousandSep) { | ||
511 | $thousandSep = $defaults[2]; | ||
512 | } | ||
513 | |||
514 | return number_format((float) $number, $decimal, $decimalPoint, $thousandSep); | ||
515 | } | ||
516 | |||
517 | /** | ||
518 | * URL encodes a string as a path segment or an array as a query string. | ||
519 | * | ||
520 | * @param string|array $url A URL or an array of query parameters | ||
521 | * @param bool $raw true to use rawurlencode() instead of urlencode | ||
522 | * | ||
523 | * @return string The URL encoded value | ||
524 | */ | ||
525 | function twig_urlencode_filter($url, $raw = false) | ||
526 | { | ||
527 | if (is_array($url)) { | ||
528 | return http_build_query($url, '', '&'); | ||
529 | } | ||
530 | |||
531 | if ($raw) { | ||
532 | return rawurlencode($url); | ||
533 | } | ||
534 | |||
535 | return urlencode($url); | ||
536 | } | ||
537 | |||
538 | if (version_compare(PHP_VERSION, '5.3.0', '<')) { | ||
539 | /** | ||
540 | * JSON encodes a variable. | ||
541 | * | ||
542 | * @param mixed $value The value to encode. | ||
543 | * @param integer $options Not used on PHP 5.2.x | ||
544 | * | ||
545 | * @return mixed The JSON encoded value | ||
546 | */ | ||
547 | function twig_jsonencode_filter($value, $options = 0) | ||
548 | { | ||
549 | if ($value instanceof Twig_Markup) { | ||
550 | $value = (string) $value; | ||
551 | } elseif (is_array($value)) { | ||
552 | array_walk_recursive($value, '_twig_markup2string'); | ||
553 | } | ||
554 | |||
555 | return json_encode($value); | ||
556 | } | ||
557 | } else { | ||
558 | /** | ||
559 | * JSON encodes a variable. | ||
560 | * | ||
561 | * @param mixed $value The value to encode. | ||
562 | * @param integer $options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT | ||
563 | * | ||
564 | * @return mixed The JSON encoded value | ||
565 | */ | ||
566 | function twig_jsonencode_filter($value, $options = 0) | ||
567 | { | ||
568 | if ($value instanceof Twig_Markup) { | ||
569 | $value = (string) $value; | ||
570 | } elseif (is_array($value)) { | ||
571 | array_walk_recursive($value, '_twig_markup2string'); | ||
572 | } | ||
573 | |||
574 | return json_encode($value, $options); | ||
575 | } | ||
576 | } | ||
577 | |||
578 | function _twig_markup2string(&$value) | ||
579 | { | ||
580 | if ($value instanceof Twig_Markup) { | ||
581 | $value = (string) $value; | ||
582 | } | ||
583 | } | ||
584 | |||
585 | /** | ||
586 | * Merges an array with another one. | ||
587 | * | ||
588 | * <pre> | ||
589 | * {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %} | ||
590 | * | ||
591 | * {% set items = items|merge({ 'peugeot': 'car' }) %} | ||
592 | * | ||
593 | * {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #} | ||
594 | * </pre> | ||
595 | * | ||
596 | * @param array $arr1 An array | ||
597 | * @param array $arr2 An array | ||
598 | * | ||
599 | * @return array The merged array | ||
600 | */ | ||
601 | function twig_array_merge($arr1, $arr2) | ||
602 | { | ||
603 | if (!is_array($arr1) || !is_array($arr2)) { | ||
604 | throw new Twig_Error_Runtime('The merge filter only works with arrays or hashes.'); | ||
605 | } | ||
606 | |||
607 | return array_merge($arr1, $arr2); | ||
608 | } | ||
609 | |||
610 | /** | ||
611 | * Slices a variable. | ||
612 | * | ||
613 | * @param Twig_Environment $env A Twig_Environment instance | ||
614 | * @param mixed $item A variable | ||
615 | * @param integer $start Start of the slice | ||
616 | * @param integer $length Size of the slice | ||
617 | * @param Boolean $preserveKeys Whether to preserve key or not (when the input is an array) | ||
618 | * | ||
619 | * @return mixed The sliced variable | ||
620 | */ | ||
621 | function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false) | ||
622 | { | ||
623 | if ($item instanceof Traversable) { | ||
624 | $item = iterator_to_array($item, false); | ||
625 | } | ||
626 | |||
627 | if (is_array($item)) { | ||
628 | return array_slice($item, $start, $length, $preserveKeys); | ||
629 | } | ||
630 | |||
631 | $item = (string) $item; | ||
632 | |||
633 | if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) { | ||
634 | return mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset); | ||
635 | } | ||
636 | |||
637 | return null === $length ? substr($item, $start) : substr($item, $start, $length); | ||
638 | } | ||
639 | |||
640 | /** | ||
641 | * Returns the first element of the item. | ||
642 | * | ||
643 | * @param Twig_Environment $env A Twig_Environment instance | ||
644 | * @param mixed $item A variable | ||
645 | * | ||
646 | * @return mixed The first element of the item | ||
647 | */ | ||
648 | function twig_first(Twig_Environment $env, $item) | ||
649 | { | ||
650 | $elements = twig_slice($env, $item, 0, 1, false); | ||
651 | |||
652 | return is_string($elements) ? $elements[0] : current($elements); | ||
653 | } | ||
654 | |||
655 | /** | ||
656 | * Returns the last element of the item. | ||
657 | * | ||
658 | * @param Twig_Environment $env A Twig_Environment instance | ||
659 | * @param mixed $item A variable | ||
660 | * | ||
661 | * @return mixed The last element of the item | ||
662 | */ | ||
663 | function twig_last(Twig_Environment $env, $item) | ||
664 | { | ||
665 | $elements = twig_slice($env, $item, -1, 1, false); | ||
666 | |||
667 | return is_string($elements) ? $elements[0] : current($elements); | ||
668 | } | ||
669 | |||
670 | /** | ||
671 | * Joins the values to a string. | ||
672 | * | ||
673 | * The separator between elements is an empty string per default, you can define it with the optional parameter. | ||
674 | * | ||
675 | * <pre> | ||
676 | * {{ [1, 2, 3]|join('|') }} | ||
677 | * {# returns 1|2|3 #} | ||
678 | * | ||
679 | * {{ [1, 2, 3]|join }} | ||
680 | * {# returns 123 #} | ||
681 | * </pre> | ||
682 | * | ||
683 | * @param array $value An array | ||
684 | * @param string $glue The separator | ||
685 | * | ||
686 | * @return string The concatenated string | ||
687 | */ | ||
688 | function twig_join_filter($value, $glue = '') | ||
689 | { | ||
690 | if ($value instanceof Traversable) { | ||
691 | $value = iterator_to_array($value, false); | ||
692 | } | ||
693 | |||
694 | return implode($glue, (array) $value); | ||
695 | } | ||
696 | |||
697 | /** | ||
698 | * Splits the string into an array. | ||
699 | * | ||
700 | * <pre> | ||
701 | * {{ "one,two,three"|split(',') }} | ||
702 | * {# returns [one, two, three] #} | ||
703 | * | ||
704 | * {{ "one,two,three,four,five"|split(',', 3) }} | ||
705 | * {# returns [one, two, "three,four,five"] #} | ||
706 | * | ||
707 | * {{ "123"|split('') }} | ||
708 | * {# returns [1, 2, 3] #} | ||
709 | * | ||
710 | * {{ "aabbcc"|split('', 2) }} | ||
711 | * {# returns [aa, bb, cc] #} | ||
712 | * </pre> | ||
713 | * | ||
714 | * @param string $value A string | ||
715 | * @param string $delimiter The delimiter | ||
716 | * @param integer $limit The limit | ||
717 | * | ||
718 | * @return array The split string as an array | ||
719 | */ | ||
720 | function twig_split_filter($value, $delimiter, $limit = null) | ||
721 | { | ||
722 | if (empty($delimiter)) { | ||
723 | return str_split($value, null === $limit ? 1 : $limit); | ||
724 | } | ||
725 | |||
726 | return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit); | ||
727 | } | ||
728 | |||
729 | // The '_default' filter is used internally to avoid using the ternary operator | ||
730 | // which costs a lot for big contexts (before PHP 5.4). So, on average, | ||
731 | // a function call is cheaper. | ||
732 | function _twig_default_filter($value, $default = '') | ||
733 | { | ||
734 | if (twig_test_empty($value)) { | ||
735 | return $default; | ||
736 | } | ||
737 | |||
738 | return $value; | ||
739 | } | ||
740 | |||
741 | /** | ||
742 | * Returns the keys for the given array. | ||
743 | * | ||
744 | * It is useful when you want to iterate over the keys of an array: | ||
745 | * | ||
746 | * <pre> | ||
747 | * {% for key in array|keys %} | ||
748 | * {# ... #} | ||
749 | * {% endfor %} | ||
750 | * </pre> | ||
751 | * | ||
752 | * @param array $array An array | ||
753 | * | ||
754 | * @return array The keys | ||
755 | */ | ||
756 | function twig_get_array_keys_filter($array) | ||
757 | { | ||
758 | if (is_object($array) && $array instanceof Traversable) { | ||
759 | return array_keys(iterator_to_array($array)); | ||
760 | } | ||
761 | |||
762 | if (!is_array($array)) { | ||
763 | return array(); | ||
764 | } | ||
765 | |||
766 | return array_keys($array); | ||
767 | } | ||
768 | |||
769 | /** | ||
770 | * Reverses a variable. | ||
771 | * | ||
772 | * @param Twig_Environment $env A Twig_Environment instance | ||
773 | * @param array|Traversable|string $item An array, a Traversable instance, or a string | ||
774 | * @param Boolean $preserveKeys Whether to preserve key or not | ||
775 | * | ||
776 | * @return mixed The reversed input | ||
777 | */ | ||
778 | function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false) | ||
779 | { | ||
780 | if (is_object($item) && $item instanceof Traversable) { | ||
781 | return array_reverse(iterator_to_array($item), $preserveKeys); | ||
782 | } | ||
783 | |||
784 | if (is_array($item)) { | ||
785 | return array_reverse($item, $preserveKeys); | ||
786 | } | ||
787 | |||
788 | if (null !== $charset = $env->getCharset()) { | ||
789 | $string = (string) $item; | ||
790 | |||
791 | if ('UTF-8' != $charset) { | ||
792 | $item = twig_convert_encoding($string, 'UTF-8', $charset); | ||
793 | } | ||
794 | |||
795 | preg_match_all('/./us', $item, $matches); | ||
796 | |||
797 | $string = implode('', array_reverse($matches[0])); | ||
798 | |||
799 | if ('UTF-8' != $charset) { | ||
800 | $string = twig_convert_encoding($string, $charset, 'UTF-8'); | ||
801 | } | ||
802 | |||
803 | return $string; | ||
804 | } | ||
805 | |||
806 | return strrev((string) $item); | ||
807 | } | ||
808 | |||
809 | /** | ||
810 | * Sorts an array. | ||
811 | * | ||
812 | * @param array $array An array | ||
813 | */ | ||
814 | function twig_sort_filter($array) | ||
815 | { | ||
816 | asort($array); | ||
817 | |||
818 | return $array; | ||
819 | } | ||
820 | |||
821 | /* used internally */ | ||
822 | function twig_in_filter($value, $compare) | ||
823 | { | ||
824 | if (is_array($compare)) { | ||
825 | return in_array($value, $compare, is_object($value)); | ||
826 | } elseif (is_string($compare)) { | ||
827 | if (!strlen($value)) { | ||
828 | return empty($compare); | ||
829 | } | ||
830 | |||
831 | return false !== strpos($compare, (string) $value); | ||
832 | } elseif ($compare instanceof Traversable) { | ||
833 | return in_array($value, iterator_to_array($compare, false), is_object($value)); | ||
834 | } | ||
835 | |||
836 | return false; | ||
837 | } | ||
838 | |||
839 | /** | ||
840 | * Escapes a string. | ||
841 | * | ||
842 | * @param Twig_Environment $env A Twig_Environment instance | ||
843 | * @param string $string The value to be escaped | ||
844 | * @param string $strategy The escaping strategy | ||
845 | * @param string $charset The charset | ||
846 | * @param Boolean $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false) | ||
847 | */ | ||
848 | function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) | ||
849 | { | ||
850 | if ($autoescape && $string instanceof Twig_Markup) { | ||
851 | return $string; | ||
852 | } | ||
853 | |||
854 | if (!is_string($string)) { | ||
855 | if (is_object($string) && method_exists($string, '__toString')) { | ||
856 | $string = (string) $string; | ||
857 | } else { | ||
858 | return $string; | ||
859 | } | ||
860 | } | ||
861 | |||
862 | if (null === $charset) { | ||
863 | $charset = $env->getCharset(); | ||
864 | } | ||
865 | |||
866 | switch ($strategy) { | ||
867 | case 'html': | ||
868 | // see http://php.net/htmlspecialchars | ||
869 | |||
870 | // Using a static variable to avoid initializing the array | ||
871 | // each time the function is called. Moving the declaration on the | ||
872 | // top of the function slow downs other escaping strategies. | ||
873 | static $htmlspecialcharsCharsets = array( | ||
874 | 'ISO-8859-1' => true, 'ISO8859-1' => true, | ||
875 | 'ISO-8859-15' => true, 'ISO8859-15' => true, | ||
876 | 'utf-8' => true, 'UTF-8' => true, | ||
877 | 'CP866' => true, 'IBM866' => true, '866' => true, | ||
878 | 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true, | ||
879 | '1251' => true, | ||
880 | 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true, | ||
881 | 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true, | ||
882 | 'BIG5' => true, '950' => true, | ||
883 | 'GB2312' => true, '936' => true, | ||
884 | 'BIG5-HKSCS' => true, | ||
885 | 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true, | ||
886 | 'EUC-JP' => true, 'EUCJP' => true, | ||
887 | 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true, | ||
888 | ); | ||
889 | |||
890 | if (isset($htmlspecialcharsCharsets[$charset])) { | ||
891 | return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset); | ||
892 | } | ||
893 | |||
894 | if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) { | ||
895 | // cache the lowercase variant for future iterations | ||
896 | $htmlspecialcharsCharsets[$charset] = true; | ||
897 | |||
898 | return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset); | ||
899 | } | ||
900 | |||
901 | $string = twig_convert_encoding($string, 'UTF-8', $charset); | ||
902 | $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); | ||
903 | |||
904 | return twig_convert_encoding($string, $charset, 'UTF-8'); | ||
905 | |||
906 | case 'js': | ||
907 | // escape all non-alphanumeric characters | ||
908 | // into their \xHH or \uHHHH representations | ||
909 | if ('UTF-8' != $charset) { | ||
910 | $string = twig_convert_encoding($string, 'UTF-8', $charset); | ||
911 | } | ||
912 | |||
913 | if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) { | ||
914 | throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); | ||
915 | } | ||
916 | |||
917 | $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string); | ||
918 | |||
919 | if ('UTF-8' != $charset) { | ||
920 | $string = twig_convert_encoding($string, $charset, 'UTF-8'); | ||
921 | } | ||
922 | |||
923 | return $string; | ||
924 | |||
925 | case 'css': | ||
926 | if ('UTF-8' != $charset) { | ||
927 | $string = twig_convert_encoding($string, 'UTF-8', $charset); | ||
928 | } | ||
929 | |||
930 | if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) { | ||
931 | throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); | ||
932 | } | ||
933 | |||
934 | $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string); | ||
935 | |||
936 | if ('UTF-8' != $charset) { | ||
937 | $string = twig_convert_encoding($string, $charset, 'UTF-8'); | ||
938 | } | ||
939 | |||
940 | return $string; | ||
941 | |||
942 | case 'html_attr': | ||
943 | if ('UTF-8' != $charset) { | ||
944 | $string = twig_convert_encoding($string, 'UTF-8', $charset); | ||
945 | } | ||
946 | |||
947 | if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) { | ||
948 | throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); | ||
949 | } | ||
950 | |||
951 | $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string); | ||
952 | |||
953 | if ('UTF-8' != $charset) { | ||
954 | $string = twig_convert_encoding($string, $charset, 'UTF-8'); | ||
955 | } | ||
956 | |||
957 | return $string; | ||
958 | |||
959 | case 'url': | ||
960 | // hackish test to avoid version_compare that is much slower, this works unless PHP releases a 5.10.* | ||
961 | // at that point however PHP 5.2.* support can be removed | ||
962 | if (PHP_VERSION < '5.3.0') { | ||
963 | return str_replace('%7E', '~', rawurlencode($string)); | ||
964 | } | ||
965 | |||
966 | return rawurlencode($string); | ||
967 | |||
968 | default: | ||
969 | throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: html, js, url, css, and html_attr).', $strategy)); | ||
970 | } | ||
971 | } | ||
972 | |||
973 | /* used internally */ | ||
974 | function twig_escape_filter_is_safe(Twig_Node $filterArgs) | ||
975 | { | ||
976 | foreach ($filterArgs as $arg) { | ||
977 | if ($arg instanceof Twig_Node_Expression_Constant) { | ||
978 | return array($arg->getAttribute('value')); | ||
979 | } | ||
980 | |||
981 | return array(); | ||
982 | } | ||
983 | |||
984 | return array('html'); | ||
985 | } | ||
986 | |||
987 | if (function_exists('mb_convert_encoding')) { | ||
988 | function twig_convert_encoding($string, $to, $from) | ||
989 | { | ||
990 | return mb_convert_encoding($string, $to, $from); | ||
991 | } | ||
992 | } elseif (function_exists('iconv')) { | ||
993 | function twig_convert_encoding($string, $to, $from) | ||
994 | { | ||
995 | return iconv($from, $to, $string); | ||
996 | } | ||
997 | } else { | ||
998 | function twig_convert_encoding($string, $to, $from) | ||
999 | { | ||
1000 | throw new Twig_Error_Runtime('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).'); | ||
1001 | } | ||
1002 | } | ||
1003 | |||
1004 | function _twig_escape_js_callback($matches) | ||
1005 | { | ||
1006 | $char = $matches[0]; | ||
1007 | |||
1008 | // \xHH | ||
1009 | if (!isset($char[1])) { | ||
1010 | return '\\x'.strtoupper(substr('00'.bin2hex($char), -2)); | ||
1011 | } | ||
1012 | |||
1013 | // \uHHHH | ||
1014 | $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8'); | ||
1015 | |||
1016 | return '\\u'.strtoupper(substr('0000'.bin2hex($char), -4)); | ||
1017 | } | ||
1018 | |||
1019 | function _twig_escape_css_callback($matches) | ||
1020 | { | ||
1021 | $char = $matches[0]; | ||
1022 | |||
1023 | // \xHH | ||
1024 | if (!isset($char[1])) { | ||
1025 | $hex = ltrim(strtoupper(bin2hex($char)), '0'); | ||
1026 | if (0 === strlen($hex)) { | ||
1027 | $hex = '0'; | ||
1028 | } | ||
1029 | |||
1030 | return '\\'.$hex.' '; | ||
1031 | } | ||
1032 | |||
1033 | // \uHHHH | ||
1034 | $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8'); | ||
1035 | |||
1036 | return '\\'.ltrim(strtoupper(bin2hex($char)), '0').' '; | ||
1037 | } | ||
1038 | |||
1039 | /** | ||
1040 | * This function is adapted from code coming from Zend Framework. | ||
1041 | * | ||
1042 | * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) | ||
1043 | * @license http://framework.zend.com/license/new-bsd New BSD License | ||
1044 | */ | ||
1045 | function _twig_escape_html_attr_callback($matches) | ||
1046 | { | ||
1047 | /* | ||
1048 | * While HTML supports far more named entities, the lowest common denominator | ||
1049 | * has become HTML5's XML Serialisation which is restricted to the those named | ||
1050 | * entities that XML supports. Using HTML entities would result in this error: | ||
1051 | * XML Parsing Error: undefined entity | ||
1052 | */ | ||
1053 | static $entityMap = array( | ||
1054 | 34 => 'quot', /* quotation mark */ | ||
1055 | 38 => 'amp', /* ampersand */ | ||
1056 | 60 => 'lt', /* less-than sign */ | ||
1057 | 62 => 'gt', /* greater-than sign */ | ||
1058 | ); | ||
1059 | |||
1060 | $chr = $matches[0]; | ||
1061 | $ord = ord($chr); | ||
1062 | |||
1063 | /** | ||
1064 | * The following replaces characters undefined in HTML with the | ||
1065 | * hex entity for the Unicode replacement character. | ||
1066 | */ | ||
1067 | if (($ord <= 0x1f && $chr != "\t" && $chr != "\n" && $chr != "\r") || ($ord >= 0x7f && $ord <= 0x9f)) { | ||
1068 | return '�'; | ||
1069 | } | ||
1070 | |||
1071 | /** | ||
1072 | * Check if the current character to escape has a name entity we should | ||
1073 | * replace it with while grabbing the hex value of the character. | ||
1074 | */ | ||
1075 | if (strlen($chr) == 1) { | ||
1076 | $hex = strtoupper(substr('00'.bin2hex($chr), -2)); | ||
1077 | } else { | ||
1078 | $chr = twig_convert_encoding($chr, 'UTF-16BE', 'UTF-8'); | ||
1079 | $hex = strtoupper(substr('0000'.bin2hex($chr), -4)); | ||
1080 | } | ||
1081 | |||
1082 | $int = hexdec($hex); | ||
1083 | if (array_key_exists($int, $entityMap)) { | ||
1084 | return sprintf('&%s;', $entityMap[$int]); | ||
1085 | } | ||
1086 | |||
1087 | /** | ||
1088 | * Per OWASP recommendations, we'll use hex entities for any other | ||
1089 | * characters where a named entity does not exist. | ||
1090 | */ | ||
1091 | |||
1092 | return sprintf('&#x%s;', $hex); | ||
1093 | } | ||
1094 | |||
1095 | // add multibyte extensions if possible | ||
1096 | if (function_exists('mb_get_info')) { | ||
1097 | /** | ||
1098 | * Returns the length of a variable. | ||
1099 | * | ||
1100 | * @param Twig_Environment $env A Twig_Environment instance | ||
1101 | * @param mixed $thing A variable | ||
1102 | * | ||
1103 | * @return integer The length of the value | ||
1104 | */ | ||
1105 | function twig_length_filter(Twig_Environment $env, $thing) | ||
1106 | { | ||
1107 | return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing); | ||
1108 | } | ||
1109 | |||
1110 | /** | ||
1111 | * Converts a string to uppercase. | ||
1112 | * | ||
1113 | * @param Twig_Environment $env A Twig_Environment instance | ||
1114 | * @param string $string A string | ||
1115 | * | ||
1116 | * @return string The uppercased string | ||
1117 | */ | ||
1118 | function twig_upper_filter(Twig_Environment $env, $string) | ||
1119 | { | ||
1120 | if (null !== ($charset = $env->getCharset())) { | ||
1121 | return mb_strtoupper($string, $charset); | ||
1122 | } | ||
1123 | |||
1124 | return strtoupper($string); | ||
1125 | } | ||
1126 | |||
1127 | /** | ||
1128 | * Converts a string to lowercase. | ||
1129 | * | ||
1130 | * @param Twig_Environment $env A Twig_Environment instance | ||
1131 | * @param string $string A string | ||
1132 | * | ||
1133 | * @return string The lowercased string | ||
1134 | */ | ||
1135 | function twig_lower_filter(Twig_Environment $env, $string) | ||
1136 | { | ||
1137 | if (null !== ($charset = $env->getCharset())) { | ||
1138 | return mb_strtolower($string, $charset); | ||
1139 | } | ||
1140 | |||
1141 | return strtolower($string); | ||
1142 | } | ||
1143 | |||
1144 | /** | ||
1145 | * Returns a titlecased string. | ||
1146 | * | ||
1147 | * @param Twig_Environment $env A Twig_Environment instance | ||
1148 | * @param string $string A string | ||
1149 | * | ||
1150 | * @return string The titlecased string | ||
1151 | */ | ||
1152 | function twig_title_string_filter(Twig_Environment $env, $string) | ||
1153 | { | ||
1154 | if (null !== ($charset = $env->getCharset())) { | ||
1155 | return mb_convert_case($string, MB_CASE_TITLE, $charset); | ||
1156 | } | ||
1157 | |||
1158 | return ucwords(strtolower($string)); | ||
1159 | } | ||
1160 | |||
1161 | /** | ||
1162 | * Returns a capitalized string. | ||
1163 | * | ||
1164 | * @param Twig_Environment $env A Twig_Environment instance | ||
1165 | * @param string $string A string | ||
1166 | * | ||
1167 | * @return string The capitalized string | ||
1168 | */ | ||
1169 | function twig_capitalize_string_filter(Twig_Environment $env, $string) | ||
1170 | { | ||
1171 | if (null !== ($charset = $env->getCharset())) { | ||
1172 | return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset). | ||
1173 | mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset); | ||
1174 | } | ||
1175 | |||
1176 | return ucfirst(strtolower($string)); | ||
1177 | } | ||
1178 | } | ||
1179 | // and byte fallback | ||
1180 | else { | ||
1181 | /** | ||
1182 | * Returns the length of a variable. | ||
1183 | * | ||
1184 | * @param Twig_Environment $env A Twig_Environment instance | ||
1185 | * @param mixed $thing A variable | ||
1186 | * | ||
1187 | * @return integer The length of the value | ||
1188 | */ | ||
1189 | function twig_length_filter(Twig_Environment $env, $thing) | ||
1190 | { | ||
1191 | return is_scalar($thing) ? strlen($thing) : count($thing); | ||
1192 | } | ||
1193 | |||
1194 | /** | ||
1195 | * Returns a titlecased string. | ||
1196 | * | ||
1197 | * @param Twig_Environment $env A Twig_Environment instance | ||
1198 | * @param string $string A string | ||
1199 | * | ||
1200 | * @return string The titlecased string | ||
1201 | */ | ||
1202 | function twig_title_string_filter(Twig_Environment $env, $string) | ||
1203 | { | ||
1204 | return ucwords(strtolower($string)); | ||
1205 | } | ||
1206 | |||
1207 | /** | ||
1208 | * Returns a capitalized string. | ||
1209 | * | ||
1210 | * @param Twig_Environment $env A Twig_Environment instance | ||
1211 | * @param string $string A string | ||
1212 | * | ||
1213 | * @return string The capitalized string | ||
1214 | */ | ||
1215 | function twig_capitalize_string_filter(Twig_Environment $env, $string) | ||
1216 | { | ||
1217 | return ucfirst(strtolower($string)); | ||
1218 | } | ||
1219 | } | ||
1220 | |||
1221 | /* used internally */ | ||
1222 | function twig_ensure_traversable($seq) | ||
1223 | { | ||
1224 | if ($seq instanceof Traversable || is_array($seq)) { | ||
1225 | return $seq; | ||
1226 | } | ||
1227 | |||
1228 | return array(); | ||
1229 | } | ||
1230 | |||
1231 | /** | ||
1232 | * Checks if a variable is empty. | ||
1233 | * | ||
1234 | * <pre> | ||
1235 | * {# evaluates to true if the foo variable is null, false, or the empty string #} | ||
1236 | * {% if foo is empty %} | ||
1237 | * {# ... #} | ||
1238 | * {% endif %} | ||
1239 | * </pre> | ||
1240 | * | ||
1241 | * @param mixed $value A variable | ||
1242 | * | ||
1243 | * @return Boolean true if the value is empty, false otherwise | ||
1244 | */ | ||
1245 | function twig_test_empty($value) | ||
1246 | { | ||
1247 | if ($value instanceof Countable) { | ||
1248 | return 0 == count($value); | ||
1249 | } | ||
1250 | |||
1251 | return '' === $value || false === $value || null === $value || array() === $value; | ||
1252 | } | ||
1253 | |||
1254 | /** | ||
1255 | * Checks if a variable is traversable. | ||
1256 | * | ||
1257 | * <pre> | ||
1258 | * {# evaluates to true if the foo variable is an array or a traversable object #} | ||
1259 | * {% if foo is traversable %} | ||
1260 | * {# ... #} | ||
1261 | * {% endif %} | ||
1262 | * </pre> | ||
1263 | * | ||
1264 | * @param mixed $value A variable | ||
1265 | * | ||
1266 | * @return Boolean true if the value is traversable | ||
1267 | */ | ||
1268 | function twig_test_iterable($value) | ||
1269 | { | ||
1270 | return $value instanceof Traversable || is_array($value); | ||
1271 | } | ||
1272 | |||
1273 | /** | ||
1274 | * Renders a template. | ||
1275 | * | ||
1276 | * @param string $template The template to render | ||
1277 | * @param array $variables The variables to pass to the template | ||
1278 | * @param Boolean $with_context Whether to pass the current context variables or not | ||
1279 | * @param Boolean $ignore_missing Whether to ignore missing templates or not | ||
1280 | * @param Boolean $sandboxed Whether to sandbox the template or not | ||
1281 | * | ||
1282 | * @return string The rendered template | ||
1283 | */ | ||
1284 | function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false) | ||
1285 | { | ||
1286 | if ($withContext) { | ||
1287 | $variables = array_merge($context, $variables); | ||
1288 | } | ||
1289 | |||
1290 | if ($isSandboxed = $sandboxed && $env->hasExtension('sandbox')) { | ||
1291 | $sandbox = $env->getExtension('sandbox'); | ||
1292 | if (!$alreadySandboxed = $sandbox->isSandboxed()) { | ||
1293 | $sandbox->enableSandbox(); | ||
1294 | } | ||
1295 | } | ||
1296 | |||
1297 | try { | ||
1298 | return $env->resolveTemplate($template)->render($variables); | ||
1299 | } catch (Twig_Error_Loader $e) { | ||
1300 | if (!$ignoreMissing) { | ||
1301 | throw $e; | ||
1302 | } | ||
1303 | } | ||
1304 | |||
1305 | if ($isSandboxed && !$alreadySandboxed) { | ||
1306 | $sandbox->disableSandbox(); | ||
1307 | } | ||
1308 | } | ||
1309 | |||
1310 | /** | ||
1311 | * Provides the ability to get constants from instances as well as class/global constants. | ||
1312 | * | ||
1313 | * @param string $constant The name of the constant | ||
1314 | * @param null|object $object The object to get the constant from | ||
1315 | * | ||
1316 | * @return string | ||
1317 | */ | ||
1318 | function twig_constant($constant, $object = null) | ||
1319 | { | ||
1320 | if (null !== $object) { | ||
1321 | $constant = get_class($object).'::'.$constant; | ||
1322 | } | ||
1323 | |||
1324 | return constant($constant); | ||
1325 | } | ||
1326 | |||
1327 | /** | ||
1328 | * Batches item. | ||
1329 | * | ||
1330 | * @param array $items An array of items | ||
1331 | * @param integer $size The size of the batch | ||
1332 | * @param string $fill A string to fill missing items | ||
1333 | * | ||
1334 | * @return array | ||
1335 | */ | ||
1336 | function twig_array_batch($items, $size, $fill = null) | ||
1337 | { | ||
1338 | if ($items instanceof Traversable) { | ||
1339 | $items = iterator_to_array($items, false); | ||
1340 | } | ||
1341 | |||
1342 | $size = ceil($size); | ||
1343 | |||
1344 | $result = array_chunk($items, $size, true); | ||
1345 | |||
1346 | if (null !== $fill) { | ||
1347 | $last = count($result) - 1; | ||
1348 | $result[$last] = array_merge( | ||
1349 | $result[$last], | ||
1350 | array_fill(0, $size - count($result[$last]), $fill) | ||
1351 | ); | ||
1352 | } | ||
1353 | |||
1354 | return $result; | ||
1355 | } | ||
diff --git a/inc/Twig/Extension/Debug.php b/inc/Twig/Extension/Debug.php new file mode 100644 index 00000000..e3a85bfe --- /dev/null +++ b/inc/Twig/Extension/Debug.php | |||
@@ -0,0 +1,71 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2011 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 | class Twig_Extension_Debug extends Twig_Extension | ||
12 | { | ||
13 | /** | ||
14 | * Returns a list of global functions to add to the existing list. | ||
15 | * | ||
16 | * @return array An array of global functions | ||
17 | */ | ||
18 | public function getFunctions() | ||
19 | { | ||
20 | // dump is safe if var_dump is overridden by xdebug | ||
21 | $isDumpOutputHtmlSafe = extension_loaded('xdebug') | ||
22 | // false means that it was not set (and the default is on) or it explicitly enabled | ||
23 | && (false === ini_get('xdebug.overload_var_dump') || ini_get('xdebug.overload_var_dump')) | ||
24 | // false means that it was not set (and the default is on) or it explicitly enabled | ||
25 | // xdebug.overload_var_dump produces HTML only when html_errors is also enabled | ||
26 | && (false === ini_get('html_errors') || ini_get('html_errors')) | ||
27 | || 'cli' === php_sapi_name() | ||
28 | ; | ||
29 | |||
30 | return array( | ||
31 | new Twig_SimpleFunction('dump', 'twig_var_dump', array('is_safe' => $isDumpOutputHtmlSafe ? array('html') : array(), 'needs_context' => true, 'needs_environment' => true)), | ||
32 | ); | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * Returns the name of the extension. | ||
37 | * | ||
38 | * @return string The extension name | ||
39 | */ | ||
40 | public function getName() | ||
41 | { | ||
42 | return 'debug'; | ||
43 | } | ||
44 | } | ||
45 | |||
46 | function twig_var_dump(Twig_Environment $env, $context) | ||
47 | { | ||
48 | if (!$env->isDebug()) { | ||
49 | return; | ||
50 | } | ||
51 | |||
52 | ob_start(); | ||
53 | |||
54 | $count = func_num_args(); | ||
55 | if (2 === $count) { | ||
56 | $vars = array(); | ||
57 | foreach ($context as $key => $value) { | ||
58 | if (!$value instanceof Twig_Template) { | ||
59 | $vars[$key] = $value; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | var_dump($vars); | ||
64 | } else { | ||
65 | for ($i = 2; $i < $count; $i++) { | ||
66 | var_dump(func_get_arg($i)); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | return ob_get_clean(); | ||
71 | } | ||
diff --git a/inc/Twig/Extension/Escaper.php b/inc/Twig/Extension/Escaper.php new file mode 100644 index 00000000..c9a7f68e --- /dev/null +++ b/inc/Twig/Extension/Escaper.php | |||
@@ -0,0 +1,107 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2009 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 | class Twig_Extension_Escaper extends Twig_Extension | ||
12 | { | ||
13 | protected $defaultStrategy; | ||
14 | |||
15 | public function __construct($defaultStrategy = 'html') | ||
16 | { | ||
17 | $this->setDefaultStrategy($defaultStrategy); | ||
18 | } | ||
19 | |||
20 | /** | ||
21 | * Returns the token parser instances to add to the existing list. | ||
22 | * | ||
23 | * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances | ||
24 | */ | ||
25 | public function getTokenParsers() | ||
26 | { | ||
27 | return array(new Twig_TokenParser_AutoEscape()); | ||
28 | } | ||
29 | |||
30 | /** | ||
31 | * Returns the node visitor instances to add to the existing list. | ||
32 | * | ||
33 | * @return array An array of Twig_NodeVisitorInterface instances | ||
34 | */ | ||
35 | public function getNodeVisitors() | ||
36 | { | ||
37 | return array(new Twig_NodeVisitor_Escaper()); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * Returns a list of filters to add to the existing list. | ||
42 | * | ||
43 | * @return array An array of filters | ||
44 | */ | ||
45 | public function getFilters() | ||
46 | { | ||
47 | return array( | ||
48 | new Twig_SimpleFilter('raw', 'twig_raw_filter', array('is_safe' => array('all'))), | ||
49 | ); | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * Sets the default strategy to use when not defined by the user. | ||
54 | * | ||
55 | * The strategy can be a valid PHP callback that takes the template | ||
56 | * "filename" as an argument and returns the strategy to use. | ||
57 | * | ||
58 | * @param mixed $defaultStrategy An escaping strategy | ||
59 | */ | ||
60 | public function setDefaultStrategy($defaultStrategy) | ||
61 | { | ||
62 | // for BC | ||
63 | if (true === $defaultStrategy) { | ||
64 | $defaultStrategy = 'html'; | ||
65 | } | ||
66 | |||
67 | $this->defaultStrategy = $defaultStrategy; | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Gets the default strategy to use when not defined by the user. | ||
72 | * | ||
73 | * @param string $filename The template "filename" | ||
74 | * | ||
75 | * @return string The default strategy to use for the template | ||
76 | */ | ||
77 | public function getDefaultStrategy($filename) | ||
78 | { | ||
79 | // disable string callables to avoid calling a function named html or js, | ||
80 | // or any other upcoming escaping strategy | ||
81 | if (!is_string($this->defaultStrategy) && is_callable($this->defaultStrategy)) { | ||
82 | return call_user_func($this->defaultStrategy, $filename); | ||
83 | } | ||
84 | |||
85 | return $this->defaultStrategy; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * Returns the name of the extension. | ||
90 | * | ||
91 | * @return string The extension name | ||
92 | */ | ||
93 | public function getName() | ||
94 | { | ||
95 | return 'escaper'; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * Marks a variable as being safe. | ||
101 | * | ||
102 | * @param string $string A PHP variable | ||
103 | */ | ||
104 | function twig_raw_filter($string) | ||
105 | { | ||
106 | return $string; | ||
107 | } | ||
diff --git a/inc/Twig/Extension/Optimizer.php b/inc/Twig/Extension/Optimizer.php new file mode 100644 index 00000000..013fcb62 --- /dev/null +++ b/inc/Twig/Extension/Optimizer.php | |||
@@ -0,0 +1,35 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 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 | class Twig_Extension_Optimizer extends Twig_Extension | ||
12 | { | ||
13 | protected $optimizers; | ||
14 | |||
15 | public function __construct($optimizers = -1) | ||
16 | { | ||
17 | $this->optimizers = $optimizers; | ||
18 | } | ||
19 | |||
20 | /** | ||
21 | * {@inheritdoc} | ||
22 | */ | ||
23 | public function getNodeVisitors() | ||
24 | { | ||
25 | return array(new Twig_NodeVisitor_Optimizer($this->optimizers)); | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * {@inheritdoc} | ||
30 | */ | ||
31 | public function getName() | ||
32 | { | ||
33 | return 'optimizer'; | ||
34 | } | ||
35 | } | ||
diff --git a/inc/Twig/Extension/Sandbox.php b/inc/Twig/Extension/Sandbox.php new file mode 100644 index 00000000..bf76c11a --- /dev/null +++ b/inc/Twig/Extension/Sandbox.php | |||
@@ -0,0 +1,112 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2009 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 | class Twig_Extension_Sandbox extends Twig_Extension | ||
12 | { | ||
13 | protected $sandboxedGlobally; | ||
14 | protected $sandboxed; | ||
15 | protected $policy; | ||
16 | |||
17 | public function __construct(Twig_Sandbox_SecurityPolicyInterface $policy, $sandboxed = false) | ||
18 | { | ||
19 | $this->policy = $policy; | ||
20 | $this->sandboxedGlobally = $sandboxed; | ||
21 | } | ||
22 | |||
23 | /** | ||
24 | * Returns the token parser instances to add to the existing list. | ||
25 | * | ||
26 | * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances | ||
27 | */ | ||
28 | public function getTokenParsers() | ||
29 | { | ||
30 | return array(new Twig_TokenParser_Sandbox()); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Returns the node visitor instances to add to the existing list. | ||
35 | * | ||
36 | * @return array An array of Twig_NodeVisitorInterface instances | ||
37 | */ | ||
38 | public function getNodeVisitors() | ||
39 | { | ||
40 | return array(new Twig_NodeVisitor_Sandbox()); | ||
41 | } | ||
42 | |||
43 | public function enableSandbox() | ||
44 | { | ||
45 | $this->sandboxed = true; | ||
46 | } | ||
47 | |||
48 | public function disableSandbox() | ||
49 | { | ||
50 | $this->sandboxed = false; | ||
51 | } | ||
52 | |||
53 | public function isSandboxed() | ||
54 | { | ||
55 | return $this->sandboxedGlobally || $this->sandboxed; | ||
56 | } | ||
57 | |||
58 | public function isSandboxedGlobally() | ||
59 | { | ||
60 | return $this->sandboxedGlobally; | ||
61 | } | ||
62 | |||
63 | public function setSecurityPolicy(Twig_Sandbox_SecurityPolicyInterface $policy) | ||
64 | { | ||
65 | $this->policy = $policy; | ||
66 | } | ||
67 | |||
68 | public function getSecurityPolicy() | ||
69 | { | ||
70 | return $this->policy; | ||
71 | } | ||
72 | |||
73 | public function checkSecurity($tags, $filters, $functions) | ||
74 | { | ||
75 | if ($this->isSandboxed()) { | ||
76 | $this->policy->checkSecurity($tags, $filters, $functions); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | public function checkMethodAllowed($obj, $method) | ||
81 | { | ||
82 | if ($this->isSandboxed()) { | ||
83 | $this->policy->checkMethodAllowed($obj, $method); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | public function checkPropertyAllowed($obj, $method) | ||
88 | { | ||
89 | if ($this->isSandboxed()) { | ||
90 | $this->policy->checkPropertyAllowed($obj, $method); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | public function ensureToStringAllowed($obj) | ||
95 | { | ||
96 | if (is_object($obj)) { | ||
97 | $this->policy->checkMethodAllowed($obj, '__toString'); | ||
98 | } | ||
99 | |||
100 | return $obj; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * Returns the name of the extension. | ||
105 | * | ||
106 | * @return string The extension name | ||
107 | */ | ||
108 | public function getName() | ||
109 | { | ||
110 | return 'sandbox'; | ||
111 | } | ||
112 | } | ||
diff --git a/inc/Twig/Extension/Staging.php b/inc/Twig/Extension/Staging.php new file mode 100644 index 00000000..8ab0f459 --- /dev/null +++ b/inc/Twig/Extension/Staging.php | |||
@@ -0,0 +1,113 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2012 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 | |||
12 | /** | ||
13 | * Internal class. | ||
14 | * | ||
15 | * This class is used by Twig_Environment as a staging area and must not be used directly. | ||
16 | * | ||
17 | * @author Fabien Potencier <fabien@symfony.com> | ||
18 | */ | ||
19 | class Twig_Extension_Staging extends Twig_Extension | ||
20 | { | ||
21 | protected $functions = array(); | ||
22 | protected $filters = array(); | ||
23 | protected $visitors = array(); | ||
24 | protected $tokenParsers = array(); | ||
25 | protected $globals = array(); | ||
26 | protected $tests = array(); | ||
27 | |||
28 | public function addFunction($name, $function) | ||
29 | { | ||
30 | $this->functions[$name] = $function; | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * {@inheritdoc} | ||
35 | */ | ||
36 | public function getFunctions() | ||
37 | { | ||
38 | return $this->functions; | ||
39 | } | ||
40 | |||
41 | public function addFilter($name, $filter) | ||
42 | { | ||
43 | $this->filters[$name] = $filter; | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * {@inheritdoc} | ||
48 | */ | ||
49 | public function getFilters() | ||
50 | { | ||
51 | return $this->filters; | ||
52 | } | ||
53 | |||
54 | public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) | ||
55 | { | ||
56 | $this->visitors[] = $visitor; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * {@inheritdoc} | ||
61 | */ | ||
62 | public function getNodeVisitors() | ||
63 | { | ||
64 | return $this->visitors; | ||
65 | } | ||
66 | |||
67 | public function addTokenParser(Twig_TokenParserInterface $parser) | ||
68 | { | ||
69 | $this->tokenParsers[] = $parser; | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * {@inheritdoc} | ||
74 | */ | ||
75 | public function getTokenParsers() | ||
76 | { | ||
77 | return $this->tokenParsers; | ||
78 | } | ||
79 | |||
80 | public function addGlobal($name, $value) | ||
81 | { | ||
82 | $this->globals[$name] = $value; | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * {@inheritdoc} | ||
87 | */ | ||
88 | public function getGlobals() | ||
89 | { | ||
90 | return $this->globals; | ||
91 | } | ||
92 | |||
93 | public function addTest($name, $test) | ||
94 | { | ||
95 | $this->tests[$name] = $test; | ||
96 | } | ||
97 | |||
98 | /** | ||
99 | * {@inheritdoc} | ||
100 | */ | ||
101 | public function getTests() | ||
102 | { | ||
103 | return $this->tests; | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * {@inheritdoc} | ||
108 | */ | ||
109 | public function getName() | ||
110 | { | ||
111 | return 'staging'; | ||
112 | } | ||
113 | } | ||
diff --git a/inc/Twig/Extension/StringLoader.php b/inc/Twig/Extension/StringLoader.php new file mode 100644 index 00000000..20f3f994 --- /dev/null +++ b/inc/Twig/Extension/StringLoader.php | |||
@@ -0,0 +1,64 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2012 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 | class Twig_Extension_StringLoader extends Twig_Extension | ||
12 | { | ||
13 | /** | ||
14 | * {@inheritdoc} | ||
15 | */ | ||
16 | public function getFunctions() | ||
17 | { | ||
18 | return array( | ||
19 | new Twig_SimpleFunction('template_from_string', 'twig_template_from_string', array('needs_environment' => true)), | ||
20 | ); | ||
21 | } | ||
22 | |||
23 | /** | ||
24 | * {@inheritdoc} | ||
25 | */ | ||
26 | public function getName() | ||
27 | { | ||
28 | return 'string_loader'; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | /** | ||
33 | * Loads a template from a string. | ||
34 | * | ||
35 | * <pre> | ||
36 | * {{ include(template_from_string("Hello {{ name }}")) }} | ||
37 | * </pre> | ||
38 | * | ||
39 | * @param Twig_Environment $env A Twig_Environment instance | ||
40 | * @param string $template A template as a string | ||
41 | * | ||
42 | * @return Twig_Template A Twig_Template instance | ||
43 | */ | ||
44 | function twig_template_from_string(Twig_Environment $env, $template) | ||
45 | { | ||
46 | static $loader; | ||
47 | |||
48 | if (null === $loader) { | ||
49 | $loader = new Twig_Loader_String(); | ||
50 | } | ||
51 | |||
52 | $current = $env->getLoader(); | ||
53 | $env->setLoader($loader); | ||
54 | try { | ||
55 | $template = $env->loadTemplate($template); | ||
56 | } catch (Exception $e) { | ||
57 | $env->setLoader($current); | ||
58 | |||
59 | throw $e; | ||
60 | } | ||
61 | $env->setLoader($current); | ||
62 | |||
63 | return $template; | ||
64 | } | ||