mirror of
https://github.com/vichan-devel/vichan.git
synced 2024-11-28 09:20:58 +01:00
Merge
This commit is contained in:
commit
d779b1599e
26
boards.php
26
boards.php
@ -12,6 +12,7 @@ $boards = listBoards();
|
||||
$all_tags = array();
|
||||
$total_posts_hour = 0;
|
||||
$total_posts = 0;
|
||||
$write_maxes = false;
|
||||
|
||||
function to_tag($str) {
|
||||
$str = trim($str);
|
||||
@ -20,14 +21,15 @@ function to_tag($str) {
|
||||
return $str;
|
||||
}
|
||||
|
||||
if (!file_exists('maxes.txt') || filemtime('maxes.txt') < (time() - (60*60))) {
|
||||
$fp = fopen('maxes.txt', 'w+');
|
||||
$write_maxes = true;
|
||||
}
|
||||
|
||||
foreach ($boards as $i => $board) {
|
||||
|
||||
//$query = prepare(sprintf("SELECT (SELECT MAX(id) from ``posts_%s``) AS max, (SELECT MAX(id) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) < DATE_SUB(NOW(), INTERVAL 1 HOUR)) AS oldmax, (SELECT MAX(id) from ``posts_%s``) AS max_d, (SELECT MAX(id) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) < DATE_SUB(NOW(), INTERVAL 1 DAY)) AS oldmax_d, (SELECT count(id) FROM ``posts_%s``) AS count;", $board['uri'], $board['uri'], $board['uri'], $board['uri'], $board['uri']));
|
||||
|
||||
$query = prepare(sprintf("
|
||||
SELECT MAX(id) max, (SELECT COUNT(*) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) > DATE_SUB(NOW(), INTERVAL 1 DAY)) ppd,
|
||||
SELECT IFNULL(MAX(id),0) max,
|
||||
(SELECT COUNT(*) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) > DATE_SUB(NOW(), INTERVAL 1 HOUR)) pph,
|
||||
(SELECT count(id) FROM ``posts_%s``) count,
|
||||
(SELECT COUNT(DISTINCT ip) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) > DATE_SUB(NOW(), INTERVAL 3 DAY)) uniq_ip
|
||||
FROM ``posts_%s``
|
||||
", $board['uri'], $board['uri'], $board['uri'], $board['uri'], $board['uri']));
|
||||
@ -52,17 +54,19 @@ SELECT MAX(id) max, (SELECT COUNT(*) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time)
|
||||
}
|
||||
|
||||
$pph = $r['pph'];
|
||||
$ppd = $r['ppd'];
|
||||
|
||||
$total_posts_hour += $pph;
|
||||
$total_posts += $r['max'];
|
||||
|
||||
$boards[$i]['pph'] = $pph;
|
||||
$boards[$i]['ppd'] = $ppd;
|
||||
$boards[$i]['ppd'] = $pph*24;
|
||||
$boards[$i]['max'] = $r['max'];
|
||||
$boards[$i]['uniq_ip'] = $r['uniq_ip'];
|
||||
$boards[$i]['tags'] = $tags;
|
||||
|
||||
if ($write_maxes) fwrite($fp, $board['uri'] . ':' . $boards[$i]['max'] . "\n");
|
||||
}
|
||||
if ($write_maxes) fclose($fp);
|
||||
|
||||
usort($boards,
|
||||
function ($a, $b) {
|
||||
@ -117,14 +121,16 @@ $config['additional_javascript'] = array('js/jquery.min.js', 'js/jquery.tablesor
|
||||
$body = Element("8chan/boards-tags.html", array("config" => $config, "n_boards" => $n_boards, "t_boards" => $t_boards, "hidden_boards_total" => $hidden_boards_total, "total_posts" => $total_posts, "total_posts_hour" => $total_posts_hour, "boards" => $boards, "last_update" => date('r'), "uptime_p" => shell_exec('uptime -p'), 'tags' => $all_tags, 'top2k' => false));
|
||||
|
||||
$html = Element("page.html", array("config" => $config, "body" => $body, "title" => "Boards on ∞chan"));
|
||||
array_splice($boards, 2000);
|
||||
$boards = array_values($boards);
|
||||
$body = Element("8chan/boards-tags.html", array("config" => $config, "n_boards" => $n_boards, "t_boards" => $t_boards, "hidden_boards_total" => $hidden_boards_total, "total_posts" => $total_posts, "total_posts_hour" => $total_posts_hour, "boards" => $boards, "last_update" => date('r'), "uptime_p" => shell_exec('uptime -p'), 'tags' => $all_tags, 'top2k' => true));
|
||||
$boards_top2k = $boards;
|
||||
array_splice($boards_top2k, 2000);
|
||||
$boards_top2k = array_values($boards_top2k);
|
||||
$body = Element("8chan/boards-tags.html", array("config" => $config, "n_boards" => $n_boards, "t_boards" => $t_boards, "hidden_boards_total" => $hidden_boards_total, "total_posts" => $total_posts, "total_posts_hour" => $total_posts_hour, "boards" => $boards_top2k, "last_update" => date('r'), "uptime_p" => shell_exec('uptime -p'), 'tags' => $all_tags, 'top2k' => true));
|
||||
$html_top2k = Element("page.html", array("config" => $config, "body" => $body, "title" => "Boards on ∞chan"));
|
||||
|
||||
if ($admin) {
|
||||
echo $html;
|
||||
} else {
|
||||
foreach ($boards as $i => &$b) { unset($b['img']); }
|
||||
file_write("boards.json", json_encode($boards));
|
||||
file_write("tags.json", json_encode($all_tags));
|
||||
foreach ($boards as $i => $b) {
|
||||
|
27
faq.php
27
faq.php
@ -103,14 +103,14 @@ $body = <<<EOT
|
||||
<li>~~strikethrough~~ -> <s>strikethrough</s></li>
|
||||
<li>[aa] tags for ASCII/JIS art (escape formatting)</li>
|
||||
<li>[code] tags if enabled by board owner</li>
|
||||
<li>[tex] tags if enabled by board owner (currently globally disabled)</li>
|
||||
<li>$$ and \( \) LaTeX tags if enabled by board owner</li>
|
||||
</ul>
|
||||
|
||||
<h2>How are featured boards chosen?</h2>
|
||||
<p>Top twenty-five boards excluding /meta/, /b/ and /news+/.</p>
|
||||
<p>Top twenty-five boards excluding /meta/, /b/, /operate/, /boards/ and /news+/.</p>
|
||||
|
||||
<h2>Who owns /meta/, /b/ and /news+/?</h2>
|
||||
<p>No one, so they are <em>de facto</em> property of the administration.</p>
|
||||
<h2>Who owns boards like /b/, /news+/ and /operate/?</h2>
|
||||
<p>No one, so they are <em>de facto</em> managed by the administration.</p>
|
||||
|
||||
<h2>Why does <a href="/banned">https://8ch.net/banned</a> say that I'm banned? I can still use the boards?</h2>
|
||||
<p>8chan is centered around user created boards. That's a board with CSS that makes it look like the ban page, not an official page. You've been tricked. 8chan has no official ban check page.</p>
|
||||
@ -124,6 +124,25 @@ $body = <<<EOT
|
||||
<p><s>There isn't one yet and there will never be an official archive.</s></p>
|
||||
<p>Given that archives are inevitable and will be created anyway via <a href="https://archive.today">archive.today</a>, Google cache, and anyone who installs Asagi, I'm softening my stance on this. Currently, 8archive.moe provides our archive, and I may set up an official one. <strong>All archives officially partnered with us will be opt-in by our board owners, not opt-out. Archives who archive boards that have not opted in will be considered pirate archives, and legal action may be taken.</strong></p>
|
||||
|
||||
<h2>Can I have a list of all API endpoints for getting raw data from 8chan?</h2>
|
||||
<p>
|
||||
Assuming the /b/ board, they are as follows:</p>
|
||||
<ul>
|
||||
<li><a href="https://8ch.net/b/index.rss">https://8ch.net/b/index.rss</a> - RSS formatted index so that you can watch smaller boards and get updates when they get new posts using a feed reader like Thunderbird or Feedly.</li>
|
||||
<li><a href="https://8ch.net/b/0.json">https://8ch.net/b/0.json</a> - Index of all threads on page 0 of /b/.</li>
|
||||
<li><a href="https://8ch.net/b/res/1.json">https://8ch.net/b/res/1.json</a> - All replies of thread 1 on /b/.</li>
|
||||
<li><a href="https://8ch.net/b/threads.json">https://8ch.net/b/threads.json</a> - Thread index of all 15 pages of /b/.</li>
|
||||
</ul>
|
||||
|
||||
<p>There are also endpoints for getting information about 8chan's boards:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://8ch.net/boards.json">https://8ch.net/boards.json</a> - Boards on 8chan (warning, 1MB+)</a></li>
|
||||
<li><a href="https://8ch.net/settings.php?board=b">https://8ch.net/settings.php?board=b</a> - Board settings of /b/ (JSON format)</li>
|
||||
</ul>
|
||||
<p>Just read the data to get an idea of what is exposed and under what attribute names. It should be self explanatory.</p>
|
||||
<p><strong>Endpoints not listed here, like post.php, catalog.json or boards-top20.json are subject to change or removal at any time!</strong></p>
|
||||
|
||||
<h2>I got an email from an @8chan.co email address, is that you?</h2>
|
||||
<p>8chan.co uses <a href="https://cock.li">cock.li</a> to manage our domain's email. cock.li allows anyone to create an email account @8chan.co.</p>
|
||||
<p>That said, we have quite a few official 8chan.co email addresses. They are:</p>
|
||||
|
@ -2410,33 +2410,19 @@ function shell_exec_error($command, $suppress_stdout = false) {
|
||||
*/
|
||||
function diceRoller($post) {
|
||||
global $config;
|
||||
if(strpos(strtolower($post->email), 'dice%20') === 0) {
|
||||
$dicestr = str_split(substr($post->email, strlen('dice%20')));
|
||||
|
||||
if (isset($_POST['dx'], $_POST['dy'], $_POST['dz']) && !empty($_POST['dy'])) {
|
||||
// Get params
|
||||
$diceX = '';
|
||||
$diceY = '';
|
||||
$diceZ = '';
|
||||
|
||||
$curd = 'diceX';
|
||||
for($i = 0; $i < count($dicestr); $i ++) {
|
||||
if(is_numeric($dicestr[$i])) {
|
||||
$$curd .= $dicestr[$i];
|
||||
} else if($dicestr[$i] == 'd') {
|
||||
$curd = 'diceY';
|
||||
} else if($dicestr[$i] == '-' || $dicestr[$i] == '+') {
|
||||
$curd = 'diceZ';
|
||||
$$curd = $dicestr[$i];
|
||||
}
|
||||
}
|
||||
$diceX = $_POST['dx'];
|
||||
$diceY = $_POST['dy'];
|
||||
$diceZ = $_POST['dz'];
|
||||
|
||||
// Default values for X and Z
|
||||
if($diceX == '') {
|
||||
$diceX = '1';
|
||||
$diceX = 1;
|
||||
}
|
||||
|
||||
if($diceZ == '') {
|
||||
$diceZ = '+0';
|
||||
$diceZ = 0;
|
||||
}
|
||||
|
||||
// Intify them
|
||||
@ -2453,6 +2439,10 @@ function diceRoller($post) {
|
||||
$diceX = 200;
|
||||
}
|
||||
|
||||
if (abs($diceZ) > 1000000) {
|
||||
$diceZ = 0;
|
||||
}
|
||||
|
||||
// Continue only if we have valid values
|
||||
if($diceX > 0 && $diceY > 0) {
|
||||
$dicerolls = array();
|
||||
@ -2466,7 +2456,8 @@ function diceRoller($post) {
|
||||
// Prepend the result to the post body
|
||||
$modifier = ($diceZ != 0) ? ((($diceZ < 0) ? ' - ' : ' + ') . abs($diceZ)) : '';
|
||||
$dicesum = ($diceX > 1) ? ' = ' . $dicesum : '';
|
||||
$post->body = '<table class="diceroll"><tr><td><img src="'.$config['dir']['static'].'d10.svg" alt="Dice roll" width="24"></td><td>Rolled ' . implode(', ', $dicerolls) . $modifier . $dicesum . '</td></tr></table><br/>' . $post->body;
|
||||
$rollstring = "{$diceX}d{$diceY}";
|
||||
$post->body = '<table class="diceroll"><tr><td><img src="'.$config['dir']['static'].'d10.svg" alt="Dice roll" width="24"></td><td>Rolled ' . implode(', ', $dicerolls) . $modifier . $dicesum . " ($rollstring)</td></tr></table><br/>" . $post->body;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,13 +137,13 @@
|
||||
$config['additional_javascript'][] = 'js/catalog-search.js';
|
||||
$config['additional_javascript'][] = 'js/thread-stats.js';
|
||||
$config['additional_javascript'][] = 'js/quote-selection.js';
|
||||
$config['additional_javascript'][] = 'js/twemoji/twemoji.js';
|
||||
$config['additional_javascript'][] = 'js/flag-previews.js';
|
||||
$config['additional_javascript'][] = 'js/post-menu.js';
|
||||
$config['additional_javascript'][] = 'js/post-filter.js';
|
||||
$config['additional_javascript'][] = 'js/fix-report-delete-submit.js';
|
||||
$config['additional_javascript'][] = 'js/image-hover.js';
|
||||
$config['additional_javascript'][] = 'js/auto-scroll.js';
|
||||
$config['additional_javascript'][] = 'js/twemoji/twemoji.js';
|
||||
|
||||
//$config['font_awesome_css'] = '/netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css';
|
||||
|
||||
|
@ -19,14 +19,14 @@ class Twig_Autoloader
|
||||
/**
|
||||
* Registers Twig_Autoloader as an SPL autoloader.
|
||||
*
|
||||
* @param Boolean $prepend Whether to prepend the autoloader or not.
|
||||
* @param bool $prepend Whether to prepend the autoloader or not.
|
||||
*/
|
||||
public static function register($prepend = false)
|
||||
{
|
||||
if (version_compare(phpversion(), '5.3.0', '>=')) {
|
||||
spl_autoload_register(array(new self, 'autoload'), true, $prepend);
|
||||
if (PHP_VERSION_ID < 50300) {
|
||||
spl_autoload_register(array(__CLASS__, 'autoload'));
|
||||
} else {
|
||||
spl_autoload_register(array(new self, 'autoload'));
|
||||
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
* Compiles a node.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to compile
|
||||
* @param integer $indentation The current indentation
|
||||
* @param int $indentation The current indentation
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
@ -74,6 +74,7 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
{
|
||||
$this->lastLine = null;
|
||||
$this->source = '';
|
||||
$this->debugInfo = array();
|
||||
$this->sourceOffset = 0;
|
||||
// source code starts at 1 (as we then increment it when we encounter new lines)
|
||||
$this->sourceLine = 1;
|
||||
@ -181,14 +182,14 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
} elseif (is_array($value)) {
|
||||
$this->raw('array(');
|
||||
$first = true;
|
||||
foreach ($value as $key => $value) {
|
||||
foreach ($value as $key => $v) {
|
||||
if (!$first) {
|
||||
$this->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
$this->repr($key);
|
||||
$this->raw(' => ');
|
||||
$this->repr($value);
|
||||
$this->repr($v);
|
||||
}
|
||||
$this->raw(')');
|
||||
} else {
|
||||
@ -208,7 +209,7 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
public function addDebugInfo(Twig_NodeInterface $node)
|
||||
{
|
||||
if ($node->getLine() != $this->lastLine) {
|
||||
$this->write("// line {$node->getLine()}\n");
|
||||
$this->write(sprintf("// line %d\n", $node->getLine()));
|
||||
|
||||
// when mbstring.func_overload is set to 2
|
||||
// mb_substr_count() replaces substr_count()
|
||||
@ -230,13 +231,15 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
|
||||
public function getDebugInfo()
|
||||
{
|
||||
ksort($this->debugInfo);
|
||||
|
||||
return $this->debugInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indents the generated code.
|
||||
*
|
||||
* @param integer $step The number of indentation to add
|
||||
* @param int $step The number of indentation to add
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
@ -250,9 +253,11 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
/**
|
||||
* Outdents the generated code.
|
||||
*
|
||||
* @param integer $step The number of indentation to remove
|
||||
* @param int $step The number of indentation to remove
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*
|
||||
* @throws LogicException When trying to outdent too much so the indentation would become negative
|
||||
*/
|
||||
public function outdent($step = 1)
|
||||
{
|
||||
@ -265,4 +270,9 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVarName()
|
||||
{
|
||||
return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@
|
||||
* Interface implemented by compiler classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*
|
||||
* @deprecated since 1.12 (to be removed in 3.0)
|
||||
*/
|
||||
interface Twig_CompilerInterface
|
||||
{
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
class Twig_Environment
|
||||
{
|
||||
const VERSION = '1.14.0-DEV';
|
||||
const VERSION = '1.18.1-DEV';
|
||||
|
||||
protected $charset;
|
||||
protected $loader;
|
||||
@ -44,7 +44,6 @@ class Twig_Environment
|
||||
protected $functionCallbacks;
|
||||
protected $filterCallbacks;
|
||||
protected $staging;
|
||||
protected $templateClasses;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -62,9 +61,9 @@ class Twig_Environment
|
||||
* * cache: An absolute path where to store the compiled templates, or
|
||||
* false to disable compilation cache (default).
|
||||
*
|
||||
* * auto_reload: Whether to reload the template is the original source changed.
|
||||
* * auto_reload: Whether to reload the template if the original source changed.
|
||||
* If you don't provide the auto_reload option, it will be
|
||||
* determined automatically base on the debug value.
|
||||
* determined automatically based on the debug value.
|
||||
*
|
||||
* * strict_variables: Whether to ignore invalid variables in templates
|
||||
* (default to false).
|
||||
@ -73,6 +72,7 @@ class Twig_Environment
|
||||
* * false: disable auto-escaping
|
||||
* * true: equivalent to html
|
||||
* * html, js: set the autoescaping to one of the supported strategies
|
||||
* * filename: set the autoescaping strategy based on the template filename extension
|
||||
* * PHP callback: a PHP callback that returns an escaping strategy based on the template "filename"
|
||||
*
|
||||
* * optimizations: A flag that indicates which optimizations to apply
|
||||
@ -108,7 +108,6 @@ class Twig_Environment
|
||||
$this->setCache($options['cache']);
|
||||
$this->functionCallbacks = array();
|
||||
$this->filterCallbacks = array();
|
||||
$this->templateClasses = array();
|
||||
|
||||
$this->addExtension(new Twig_Extension_Core());
|
||||
$this->addExtension(new Twig_Extension_Escaper($options['autoescape']));
|
||||
@ -156,7 +155,7 @@ class Twig_Environment
|
||||
/**
|
||||
* Checks if debug mode is enabled.
|
||||
*
|
||||
* @return Boolean true if debug mode is enabled, false otherwise
|
||||
* @return bool true if debug mode is enabled, false otherwise
|
||||
*/
|
||||
public function isDebug()
|
||||
{
|
||||
@ -182,7 +181,7 @@ class Twig_Environment
|
||||
/**
|
||||
* Checks if the auto_reload option is enabled.
|
||||
*
|
||||
* @return Boolean true if auto_reload is enabled, false otherwise
|
||||
* @return bool true if auto_reload is enabled, false otherwise
|
||||
*/
|
||||
public function isAutoReload()
|
||||
{
|
||||
@ -208,7 +207,7 @@ class Twig_Environment
|
||||
/**
|
||||
* Checks if the strict_variables option is enabled.
|
||||
*
|
||||
* @return Boolean true if strict_variables is enabled, false otherwise
|
||||
* @return bool true if strict_variables is enabled, false otherwise
|
||||
*/
|
||||
public function isStrictVariables()
|
||||
{
|
||||
@ -225,12 +224,12 @@ class Twig_Environment
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache directory or false if cache is disabled.
|
||||
*
|
||||
* @param string|false $cache The absolute path to the compiled templates,
|
||||
* or false to disable cache
|
||||
*/
|
||||
/**
|
||||
* Sets the cache directory or false if cache is disabled.
|
||||
*
|
||||
* @param string|false $cache The absolute path to the compiled templates,
|
||||
* or false to disable cache
|
||||
*/
|
||||
public function setCache($cache)
|
||||
{
|
||||
$this->cache = $cache ? $cache : false;
|
||||
@ -241,7 +240,7 @@ class Twig_Environment
|
||||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return string The cache file name
|
||||
* @return string|false The cache file name or false when caching is disabled
|
||||
*/
|
||||
public function getCacheFilename($name)
|
||||
{
|
||||
@ -257,20 +256,14 @@ class Twig_Environment
|
||||
/**
|
||||
* Gets the template class associated with the given string.
|
||||
*
|
||||
* @param string $name The name for which to calculate the template class name
|
||||
* @param integer $index The index if it is an embedded template
|
||||
* @param string $name The name for which to calculate the template class name
|
||||
* @param int $index The index if it is an embedded template
|
||||
*
|
||||
* @return string The template class name
|
||||
*/
|
||||
public function getTemplateClass($name, $index = null)
|
||||
{
|
||||
$suffix = null === $index ? '' : '_'.$index;
|
||||
$cls = $name.$suffix;
|
||||
if (isset($this->templateClasses[$cls])) {
|
||||
return $this->templateClasses[$cls];
|
||||
}
|
||||
|
||||
return $this->templateClasses[$cls] = $this->templateClassPrefix.hash('sha256', $this->getLoader()->getCacheKey($name)).$suffix;
|
||||
return $this->templateClassPrefix.hash('sha256', $this->getLoader()->getCacheKey($name)).(null === $index ? '' : '_'.$index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -290,6 +283,10 @@ class Twig_Environment
|
||||
* @param array $context An array of parameters to pass to the template
|
||||
*
|
||||
* @return string The rendered template
|
||||
*
|
||||
* @throws Twig_Error_Loader When the template cannot be found
|
||||
* @throws Twig_Error_Syntax When an error occurred during compilation
|
||||
* @throws Twig_Error_Runtime When an error occurred during rendering
|
||||
*/
|
||||
public function render($name, array $context = array())
|
||||
{
|
||||
@ -301,6 +298,10 @@ class Twig_Environment
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param array $context An array of parameters to pass to the template
|
||||
*
|
||||
* @throws Twig_Error_Loader When the template cannot be found
|
||||
* @throws Twig_Error_Syntax When an error occurred during compilation
|
||||
* @throws Twig_Error_Runtime When an error occurred during rendering
|
||||
*/
|
||||
public function display($name, array $context = array())
|
||||
{
|
||||
@ -310,10 +311,13 @@ class Twig_Environment
|
||||
/**
|
||||
* Loads a template by name.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param integer $index The index if it is an embedded template
|
||||
* @param string $name The template name
|
||||
* @param int $index The index if it is an embedded template
|
||||
*
|
||||
* @return Twig_TemplateInterface A template instance representing the given template name
|
||||
*
|
||||
* @throws Twig_Error_Loader When the template cannot be found
|
||||
* @throws Twig_Error_Syntax When an error occurred during compilation
|
||||
*/
|
||||
public function loadTemplate($name, $index = null)
|
||||
{
|
||||
@ -342,6 +346,41 @@ class Twig_Environment
|
||||
return $this->loadedTemplates[$cls] = new $cls($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a template from source.
|
||||
*
|
||||
* This method should not be used as a generic way to load templates.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param int $index The index if it is an embedded template
|
||||
*
|
||||
* @return Twig_Template A template instance representing the given template name
|
||||
*
|
||||
* @throws Twig_Error_Loader When the template cannot be found
|
||||
* @throws Twig_Error_Syntax When an error occurred during compilation
|
||||
*/
|
||||
public function createTemplate($template)
|
||||
{
|
||||
$name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), true), false));
|
||||
|
||||
$loader = new Twig_Loader_Chain(array(
|
||||
new Twig_Loader_Array(array($name => $template)),
|
||||
$current = $this->getLoader(),
|
||||
));
|
||||
|
||||
$this->setLoader($loader);
|
||||
try {
|
||||
$template = $this->loadTemplate($name);
|
||||
} catch (Exception $e) {
|
||||
$this->setLoader($current);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
$this->setLoader($current);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the template is still fresh.
|
||||
*
|
||||
@ -349,10 +388,10 @@ class Twig_Environment
|
||||
* this method also checks if the enabled extensions have
|
||||
* not changed.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param timestamp $time The last modification time of the cached template
|
||||
* @param string $name The template name
|
||||
* @param int $time The last modification time of the cached template
|
||||
*
|
||||
* @return Boolean true if the template is fresh, false otherwise
|
||||
* @return bool true if the template is fresh, false otherwise
|
||||
*/
|
||||
public function isTemplateFresh($name, $time)
|
||||
{
|
||||
@ -366,6 +405,19 @@ class Twig_Environment
|
||||
return $this->getLoader()->isFresh($name, $time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load a template consecutively from an array.
|
||||
*
|
||||
* Similar to loadTemplate() but it also accepts Twig_TemplateInterface instances and an array
|
||||
* of templates where each is tried to be loaded.
|
||||
*
|
||||
* @param string|Twig_Template|array $names A template or an array of templates to try consecutively
|
||||
*
|
||||
* @return Twig_Template
|
||||
*
|
||||
* @throws Twig_Error_Loader When none of the templates can be found
|
||||
* @throws Twig_Error_Syntax When an error occurred during compilation
|
||||
*/
|
||||
public function resolveTemplate($names)
|
||||
{
|
||||
if (!is_array($names)) {
|
||||
@ -445,6 +497,8 @@ class Twig_Environment
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return Twig_TokenStream A Twig_TokenStream instance
|
||||
*
|
||||
* @throws Twig_Error_Syntax When the code is syntactically wrong
|
||||
*/
|
||||
public function tokenize($source, $name = null)
|
||||
{
|
||||
@ -476,15 +530,17 @@ class Twig_Environment
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a token stream.
|
||||
* Converts a token stream to a node tree.
|
||||
*
|
||||
* @param Twig_TokenStream $tokens A Twig_TokenStream instance
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
*
|
||||
* @return Twig_Node_Module A Node tree
|
||||
* @return Twig_Node_Module A node tree
|
||||
*
|
||||
* @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
|
||||
*/
|
||||
public function parse(Twig_TokenStream $tokens)
|
||||
public function parse(Twig_TokenStream $stream)
|
||||
{
|
||||
return $this->getParser()->parse($tokens);
|
||||
return $this->getParser()->parse($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -512,7 +568,7 @@ class Twig_Environment
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a Node.
|
||||
* Compiles a node and returns the PHP code.
|
||||
*
|
||||
* @param Twig_NodeInterface $node A Twig_NodeInterface instance
|
||||
*
|
||||
@ -530,6 +586,8 @@ class Twig_Environment
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return string The compiled PHP source code
|
||||
*
|
||||
* @throws Twig_Error_Syntax When there was an error during tokenizing, parsing or compiling
|
||||
*/
|
||||
public function compileSource($source, $name = null)
|
||||
{
|
||||
@ -539,7 +597,7 @@ class Twig_Environment
|
||||
$e->setTemplateFile($name);
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e);
|
||||
throw new Twig_Error_Syntax(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,7 +662,7 @@ class Twig_Environment
|
||||
*
|
||||
* @param string $name The extension name
|
||||
*
|
||||
* @return Boolean Whether the extension is registered or not
|
||||
* @return bool Whether the extension is registered or not
|
||||
*/
|
||||
public function hasExtension($name)
|
||||
{
|
||||
@ -772,11 +830,11 @@ class Twig_Environment
|
||||
$filter = $name;
|
||||
$name = $filter->getName();
|
||||
}
|
||||
|
||||
|
||||
if ($this->extensionInitialized) {
|
||||
throw new LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $name));
|
||||
}
|
||||
|
||||
|
||||
$this->staging->addFilter($name, $filter);
|
||||
}
|
||||
|
||||
@ -861,7 +919,7 @@ class Twig_Environment
|
||||
$test = $name;
|
||||
$name = $test->getName();
|
||||
}
|
||||
|
||||
|
||||
if ($this->extensionInitialized) {
|
||||
throw new LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $name));
|
||||
}
|
||||
@ -919,11 +977,11 @@ class Twig_Environment
|
||||
$function = $name;
|
||||
$name = $function->getName();
|
||||
}
|
||||
|
||||
|
||||
if ($this->extensionInitialized) {
|
||||
throw new LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $name));
|
||||
}
|
||||
|
||||
|
||||
$this->staging->addFunction($name, $function);
|
||||
}
|
||||
|
||||
@ -1210,14 +1268,17 @@ class Twig_Environment
|
||||
{
|
||||
$dir = dirname($file);
|
||||
if (!is_dir($dir)) {
|
||||
if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
|
||||
throw new RuntimeException(sprintf("Unable to create the cache directory (%s).", $dir));
|
||||
if (false === @mkdir($dir, 0777, true)) {
|
||||
clearstatcache(false, $dir);
|
||||
if (!is_dir($dir)) {
|
||||
throw new RuntimeException(sprintf("Unable to create the cache directory (%s).", $dir));
|
||||
}
|
||||
}
|
||||
} elseif (!is_writable($dir)) {
|
||||
throw new RuntimeException(sprintf("Unable to write in the cache directory (%s).", $dir));
|
||||
}
|
||||
|
||||
$tmpFile = tempnam(dirname($file), basename($file));
|
||||
$tmpFile = tempnam($dir, basename($file));
|
||||
if (false !== @file_put_contents($tmpFile, $content)) {
|
||||
// rename does not work on Win32 before 5.2.6
|
||||
if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) {
|
||||
|
@ -51,13 +51,13 @@ class Twig_Error extends Exception
|
||||
* By default, automatic guessing is enabled.
|
||||
*
|
||||
* @param string $message The error message
|
||||
* @param integer $lineno The template line where the error occurred
|
||||
* @param int $lineno The template line where the error occurred
|
||||
* @param string $filename The template file name where the error occurred
|
||||
* @param Exception $previous The previous exception
|
||||
*/
|
||||
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
|
||||
if (PHP_VERSION_ID < 50300) {
|
||||
$this->previous = $previous;
|
||||
parent::__construct('');
|
||||
} else {
|
||||
@ -111,7 +111,7 @@ class Twig_Error extends Exception
|
||||
/**
|
||||
* Gets the template line where the error occurred.
|
||||
*
|
||||
* @return integer The template line
|
||||
* @return int The template line
|
||||
*/
|
||||
public function getTemplateLine()
|
||||
{
|
||||
@ -121,7 +121,7 @@ class Twig_Error extends Exception
|
||||
/**
|
||||
* Sets the template line where the error occurred.
|
||||
*
|
||||
* @param integer $lineno The template line
|
||||
* @param int $lineno The template line
|
||||
*/
|
||||
public function setTemplateLine($lineno)
|
||||
{
|
||||
@ -188,7 +188,7 @@ class Twig_Error extends Exception
|
||||
$template = null;
|
||||
$templateClass = null;
|
||||
|
||||
if (version_compare(phpversion(), '5.3.6', '>=')) {
|
||||
if (PHP_VERSION_ID >= 50306) {
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
|
||||
} else {
|
||||
$backtrace = debug_backtrace();
|
||||
@ -217,6 +217,11 @@ class Twig_Error extends Exception
|
||||
$r = new ReflectionObject($template);
|
||||
$file = $r->getFileName();
|
||||
|
||||
// hhvm has a bug where eval'ed files comes out as the current directory
|
||||
if (is_dir($file)) {
|
||||
$file = '';
|
||||
}
|
||||
|
||||
$exceptions = array($e = $this);
|
||||
while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) {
|
||||
$exceptions[] = $e;
|
||||
@ -224,6 +229,8 @@ class Twig_Error extends Exception
|
||||
|
||||
while ($e = array_pop($exceptions)) {
|
||||
$traces = $e->getTrace();
|
||||
array_unshift($traces, array('file' => $e->getFile(), 'line' => $e->getLine()));
|
||||
|
||||
while ($trace = array_shift($traces)) {
|
||||
if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
|
||||
continue;
|
||||
|
@ -13,7 +13,8 @@
|
||||
* Adds an exists() method for loaders.
|
||||
*
|
||||
* @author Florin Patan <florinpatan@gmail.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*
|
||||
* @deprecated since 1.12 (to be removed in 3.0)
|
||||
*/
|
||||
interface Twig_ExistsLoaderInterface
|
||||
{
|
||||
@ -22,7 +23,7 @@ interface Twig_ExistsLoaderInterface
|
||||
*
|
||||
* @param string $name The name of the template to check if we can load
|
||||
*
|
||||
* @return boolean If the template source code is handled by this loader or not
|
||||
* @return bool If the template source code is handled by this loader or not
|
||||
*/
|
||||
public function exists($name);
|
||||
}
|
||||
|
@ -86,18 +86,15 @@ class Twig_ExpressionParser
|
||||
|
||||
protected function parseConditionalExpression($expr)
|
||||
{
|
||||
while ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '?')) {
|
||||
$this->parser->getStream()->next();
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
while ($this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, '?')) {
|
||||
if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$expr2 = $this->parseExpression();
|
||||
if ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$this->parser->getStream()->next();
|
||||
if ($this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$expr3 = $this->parseExpression();
|
||||
} else {
|
||||
$expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
} else {
|
||||
$this->parser->getStream()->next();
|
||||
$expr2 = $expr;
|
||||
$expr3 = $this->parseExpression();
|
||||
}
|
||||
@ -161,13 +158,36 @@ class Twig_ExpressionParser
|
||||
$node = $this->parseStringExpression();
|
||||
break;
|
||||
|
||||
case Twig_Token::OPERATOR_TYPE:
|
||||
if (preg_match(Twig_Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) {
|
||||
// in this context, string operators are variable names
|
||||
$this->parser->getStream()->next();
|
||||
$node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine());
|
||||
break;
|
||||
} elseif (isset($this->unaryOperators[$token->getValue()])) {
|
||||
$class = $this->unaryOperators[$token->getValue()]['class'];
|
||||
|
||||
$ref = new ReflectionClass($class);
|
||||
$negClass = 'Twig_Node_Expression_Unary_Neg';
|
||||
$posClass = 'Twig_Node_Expression_Unary_Pos';
|
||||
if (!(in_array($ref->getName(), array($negClass, $posClass)) || $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass))) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unexpected unary operator "%s"', $token->getValue()), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
|
||||
$this->parser->getStream()->next();
|
||||
$expr = $this->parsePrimaryExpression();
|
||||
|
||||
$node = new $class($expr, $token->getLine());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) {
|
||||
$node = $this->parseArrayExpression();
|
||||
} elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) {
|
||||
$node = $this->parseHashExpression();
|
||||
} else {
|
||||
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType(), $token->getLine()), $token->getValue()), $token->getLine(), $this->parser->getFilename());
|
||||
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,12 +202,10 @@ class Twig_ExpressionParser
|
||||
// a string cannot be followed by another string in a single expression
|
||||
$nextCanBeString = true;
|
||||
while (true) {
|
||||
if ($stream->test(Twig_Token::STRING_TYPE) && $nextCanBeString) {
|
||||
$token = $stream->next();
|
||||
if ($nextCanBeString && $token = $stream->nextIf(Twig_Token::STRING_TYPE)) {
|
||||
$nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
$nextCanBeString = false;
|
||||
} elseif ($stream->test(Twig_Token::INTERPOLATION_START_TYPE)) {
|
||||
$stream->next();
|
||||
} elseif ($stream->nextIf(Twig_Token::INTERPOLATION_START_TYPE)) {
|
||||
$nodes[] = $this->parseExpression();
|
||||
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
|
||||
$nextCanBeString = true;
|
||||
@ -253,15 +271,14 @@ class Twig_ExpressionParser
|
||||
// * a string -- 'a'
|
||||
// * a name, which is equivalent to a string -- a
|
||||
// * an expression, which must be enclosed in parentheses -- (1 + 2)
|
||||
if ($stream->test(Twig_Token::STRING_TYPE) || $stream->test(Twig_Token::NAME_TYPE) || $stream->test(Twig_Token::NUMBER_TYPE)) {
|
||||
$token = $stream->next();
|
||||
if (($token = $stream->nextIf(Twig_Token::STRING_TYPE)) || ($token = $stream->nextIf(Twig_Token::NAME_TYPE)) || $token = $stream->nextIf(Twig_Token::NUMBER_TYPE)) {
|
||||
$key = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
} elseif ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$key = $this->parseExpression();
|
||||
} else {
|
||||
$current = $stream->getCurrent();
|
||||
|
||||
throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($current->getType(), $current->getLine()), $current->getValue()), $current->getLine(), $this->parser->getFilename());
|
||||
throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
|
||||
@ -316,23 +333,23 @@ class Twig_ExpressionParser
|
||||
throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line, $this->parser->getFilename());
|
||||
}
|
||||
|
||||
return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : new Twig_Node_Expression_Array(array(), $line), Twig_Template::ANY_CALL, $line);
|
||||
return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : null, Twig_Template::ANY_CALL, $line);
|
||||
default:
|
||||
$args = $this->parseArguments(true);
|
||||
if (null !== $alias = $this->parser->getImportedSymbol('macro', $name)) {
|
||||
return new Twig_Node_Expression_MacroCall($alias['node'], $alias['name'], $this->createArrayFromArguments($args), $line);
|
||||
}
|
||||
|
||||
try {
|
||||
$class = $this->getFunctionNodeClass($name, $line);
|
||||
} catch (Twig_Error_Syntax $e) {
|
||||
if (!$this->parser->hasMacro($name)) {
|
||||
throw $e;
|
||||
if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {
|
||||
$arguments = new Twig_Node_Expression_Array(array(), $line);
|
||||
foreach ($this->parseArguments() as $n) {
|
||||
$arguments->addElement($n);
|
||||
}
|
||||
|
||||
return new Twig_Node_Expression_MacroCall(new Twig_Node_Expression_Name('_self', $line), $name, $this->createArrayFromArguments($args), $line);
|
||||
$node = new Twig_Node_Expression_MethodCall($alias['node'], $alias['name'], $arguments, $line);
|
||||
$node->setAttribute('safe', true);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
$args = $this->parseArguments(true);
|
||||
$class = $this->getFunctionNodeClass($name, $line);
|
||||
|
||||
return new $class($name, $args, $line);
|
||||
}
|
||||
}
|
||||
@ -354,6 +371,13 @@ class Twig_ExpressionParser
|
||||
($token->getType() == Twig_Token::OPERATOR_TYPE && preg_match(Twig_Lexer::REGEX_NAME, $token->getValue()))
|
||||
) {
|
||||
$arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
|
||||
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$type = Twig_TemplateInterface::METHOD_CALL;
|
||||
foreach ($this->parseArguments() as $n) {
|
||||
$arguments->addElement($n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename());
|
||||
}
|
||||
@ -363,14 +387,10 @@ class Twig_ExpressionParser
|
||||
throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s")', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
|
||||
$arguments = $this->createArrayFromArguments($this->parseArguments(true));
|
||||
$node = new Twig_Node_Expression_MethodCall($node, 'get'.$arg->getAttribute('value'), $arguments, $lineno);
|
||||
$node->setAttribute('safe', true);
|
||||
|
||||
return new Twig_Node_Expression_MacroCall($node, $arg->getAttribute('value'), $arguments, $lineno);
|
||||
}
|
||||
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$type = Twig_Template::METHOD_CALL;
|
||||
$arguments = $this->createArrayFromArguments($this->parseArguments());
|
||||
return $node;
|
||||
}
|
||||
} else {
|
||||
$type = Twig_Template::ARRAY_CALL;
|
||||
@ -384,9 +404,8 @@ class Twig_ExpressionParser
|
||||
$arg = $this->parseExpression();
|
||||
}
|
||||
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
if ($stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$slice = true;
|
||||
$stream->next();
|
||||
}
|
||||
|
||||
if ($slice) {
|
||||
@ -447,10 +466,8 @@ class Twig_ExpressionParser
|
||||
/**
|
||||
* Parses arguments.
|
||||
*
|
||||
* @param Boolean $namedArguments Whether to allow named arguments or not
|
||||
* @param Boolean $definition Whether we are parsing arguments for a function definition
|
||||
*
|
||||
* @return Twig_Node
|
||||
* @param bool $namedArguments Whether to allow named arguments or not
|
||||
* @param bool $definition Whether we are parsing arguments for a function definition
|
||||
*/
|
||||
public function parseArguments($namedArguments = false, $definition = false)
|
||||
{
|
||||
@ -471,8 +488,7 @@ class Twig_ExpressionParser
|
||||
}
|
||||
|
||||
$name = null;
|
||||
if ($namedArguments && $stream->test(Twig_Token::OPERATOR_TYPE, '=')) {
|
||||
$token = $stream->next();
|
||||
if ($namedArguments && $token = $stream->nextIf(Twig_Token::OPERATOR_TYPE, '=')) {
|
||||
if (!$value instanceof Twig_Node_Expression_Name) {
|
||||
throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given', get_class($value)), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
@ -482,26 +498,25 @@ class Twig_ExpressionParser
|
||||
$value = $this->parsePrimaryExpression();
|
||||
|
||||
if (!$this->checkConstantExpression($value)) {
|
||||
throw new Twig_Error_Syntax('A default value for an argument must be a constant (a boolean, a string, a number, or an array).', $token->getLine(), $this->parser->getFilename());
|
||||
throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
} else {
|
||||
$value = $this->parseExpression();
|
||||
}
|
||||
}
|
||||
|
||||
if ($definition && null === $name) {
|
||||
$name = $value->getAttribute('name');
|
||||
$value = new Twig_Node_Expression_Constant(null, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
if (null === $name) {
|
||||
$args[] = $value;
|
||||
} else {
|
||||
if ($definition && isset($args[$name])) {
|
||||
throw new Twig_Error_Syntax(sprintf('Arguments cannot contain the same argument name more than once ("%s" is defined twice).', $name), $token->getLine(), $this->parser->getFilename());
|
||||
if ($definition) {
|
||||
if (null === $name) {
|
||||
$name = $value->getAttribute('name');
|
||||
$value = new Twig_Node_Expression_Constant(null, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
$args[$name] = $value;
|
||||
} else {
|
||||
if (null === $name) {
|
||||
$args[] = $value;
|
||||
} else {
|
||||
$args[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
|
||||
@ -519,10 +534,9 @@ class Twig_ExpressionParser
|
||||
}
|
||||
$targets[] = new Twig_Node_Expression_AssignName($token->getValue(), $token->getLine());
|
||||
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
|
||||
return new Twig_Node($targets);
|
||||
@ -533,10 +547,9 @@ class Twig_ExpressionParser
|
||||
$targets = array();
|
||||
while (true) {
|
||||
$targets[] = $this->parseExpression();
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
|
||||
return new Twig_Node($targets);
|
||||
@ -585,7 +598,9 @@ class Twig_ExpressionParser
|
||||
// checks that the node only contains "constant" elements
|
||||
protected function checkConstantExpression(Twig_NodeInterface $node)
|
||||
{
|
||||
if (!($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array)) {
|
||||
if (!($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array
|
||||
|| $node instanceof Twig_Node_Expression_Unary_Neg || $node instanceof Twig_Node_Expression_Unary_Pos
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -597,15 +612,4 @@ class Twig_ExpressionParser
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function createArrayFromArguments(Twig_Node $arguments, $line = null)
|
||||
{
|
||||
$line = null === $line ? $arguments->getLine() : $line;
|
||||
$array = new Twig_Node_Expression_Array(array(), $line);
|
||||
foreach ($arguments as $key => $value) {
|
||||
$array->addElement($value, new Twig_Node_Expression_Constant($key, $value->getLine()));
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
|
@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ENT_SUBSTITUTE')) {
|
||||
define('ENT_SUBSTITUTE', 8);
|
||||
// use 0 as hhvm does not support several flags yet
|
||||
define('ENT_SUBSTITUTE', 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -17,6 +18,28 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
protected $dateFormats = array('F j, Y H:i', '%d days');
|
||||
protected $numberFormat = array(0, '.', ',');
|
||||
protected $timezone = null;
|
||||
protected $escapers = array();
|
||||
|
||||
/**
|
||||
* Defines a new escaper to be used via the escape filter.
|
||||
*
|
||||
* @param string $strategy The strategy name that should be used as a strategy in the escape call
|
||||
* @param callable $callable A valid PHP callable
|
||||
*/
|
||||
public function setEscaper($strategy, $callable)
|
||||
{
|
||||
$this->escapers[$strategy] = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all defined escapers.
|
||||
*
|
||||
* @return array An array of escapers
|
||||
*/
|
||||
public function getEscapers()
|
||||
{
|
||||
return $this->escapers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default format to be used by the date filter.
|
||||
@ -72,9 +95,9 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
/**
|
||||
* Sets the default format to be used by the number_format filter.
|
||||
*
|
||||
* @param integer $decimal The number of decimal places to use.
|
||||
* @param string $decimalPoint The character(s) to use for the decimal point.
|
||||
* @param string $thousandSep The character(s) to use for the thousands separator.
|
||||
* @param int $decimal The number of decimal places to use.
|
||||
* @param string $decimalPoint The character(s) to use for the decimal point.
|
||||
* @param string $thousandSep The character(s) to use for the thousands separator.
|
||||
*/
|
||||
public function setNumberFormat($decimal, $decimalPoint, $thousandSep)
|
||||
{
|
||||
@ -94,7 +117,7 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
/**
|
||||
* Returns the token parser instance to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParser instances
|
||||
* @return Twig_TokenParser[] An array of Twig_TokenParser instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
@ -132,6 +155,7 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
new Twig_SimpleFilter('replace', 'strtr'),
|
||||
new Twig_SimpleFilter('number_format', 'twig_number_format_filter', array('needs_environment' => true)),
|
||||
new Twig_SimpleFilter('abs', 'abs'),
|
||||
new Twig_SimpleFilter('round', 'twig_round'),
|
||||
|
||||
// encoding
|
||||
new Twig_SimpleFilter('url_encode', 'twig_urlencode_filter'),
|
||||
@ -149,7 +173,7 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
|
||||
// array helpers
|
||||
new Twig_SimpleFilter('join', 'twig_join_filter'),
|
||||
new Twig_SimpleFilter('split', 'twig_split_filter'),
|
||||
new Twig_SimpleFilter('split', 'twig_split_filter', array('needs_environment' => true)),
|
||||
new Twig_SimpleFilter('sort', 'twig_sort_filter'),
|
||||
new Twig_SimpleFilter('merge', 'twig_array_merge'),
|
||||
new Twig_SimpleFilter('batch', 'twig_array_batch'),
|
||||
@ -186,12 +210,15 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
public function getFunctions()
|
||||
{
|
||||
return array(
|
||||
new Twig_SimpleFunction('max', 'max'),
|
||||
new Twig_SimpleFunction('min', 'min'),
|
||||
new Twig_SimpleFunction('range', 'range'),
|
||||
new Twig_SimpleFunction('constant', 'twig_constant'),
|
||||
new Twig_SimpleFunction('cycle', 'twig_cycle'),
|
||||
new Twig_SimpleFunction('random', 'twig_random', array('needs_environment' => true)),
|
||||
new Twig_SimpleFunction('date', 'twig_date_converter', array('needs_environment' => true)),
|
||||
new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true, 'is_safe' => array('all'))),
|
||||
new Twig_SimpleFunction('source', 'twig_source', array('needs_environment' => true, 'is_safe' => array('all'))),
|
||||
);
|
||||
}
|
||||
|
||||
@ -207,9 +234,11 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')),
|
||||
new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')),
|
||||
new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
|
||||
new Twig_SimpleTest('same as', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
|
||||
new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
|
||||
new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
|
||||
new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
|
||||
new Twig_SimpleTest('divisible by', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
|
||||
new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')),
|
||||
new Twig_SimpleTest('empty', 'twig_test_empty'),
|
||||
new Twig_SimpleTest('iterable', 'twig_test_iterable'),
|
||||
@ -230,65 +259,89 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
'+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'),
|
||||
),
|
||||
array(
|
||||
'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
|
||||
'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'matches' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Matches', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'starts with' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_StartsWith', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'ends with' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_EndsWith', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function parseNotTestExpression(Twig_Parser $parser, $node)
|
||||
public function parseNotTestExpression(Twig_Parser $parser, Twig_NodeInterface $node)
|
||||
{
|
||||
return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
public function parseTestExpression(Twig_Parser $parser, $node)
|
||||
public function parseTestExpression(Twig_Parser $parser, Twig_NodeInterface $node)
|
||||
{
|
||||
$stream = $parser->getStream();
|
||||
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
$name = $this->getTestName($parser, $node->getLine());
|
||||
$class = $this->getTestNodeClass($parser, $name);
|
||||
$arguments = null;
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$arguments = $parser->getExpressionParser()->parseArguments(true);
|
||||
}
|
||||
|
||||
$class = $this->getTestNodeClass($parser, $name, $node->getLine());
|
||||
|
||||
return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
protected function getTestNodeClass(Twig_Parser $parser, $name, $line)
|
||||
protected function getTestName(Twig_Parser $parser, $line)
|
||||
{
|
||||
$stream = $parser->getStream();
|
||||
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
$env = $parser->getEnvironment();
|
||||
$testMap = $env->getTests();
|
||||
|
||||
if (isset($testMap[$name])) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if ($stream->test(Twig_Token::NAME_TYPE)) {
|
||||
// try 2-words tests
|
||||
$name = $name.' '.$parser->getCurrentToken()->getValue();
|
||||
|
||||
if (isset($testMap[$name])) {
|
||||
$parser->getStream()->next();
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
$message = sprintf('The test "%s" does not exist', $name);
|
||||
if ($alternatives = $env->computeAlternatives($name, array_keys($testMap))) {
|
||||
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
|
||||
}
|
||||
|
||||
throw new Twig_Error_Syntax($message, $line, $parser->getFilename());
|
||||
}
|
||||
|
||||
protected function getTestNodeClass(Twig_Parser $parser, $name)
|
||||
{
|
||||
$env = $parser->getEnvironment();
|
||||
$testMap = $env->getTests();
|
||||
if (!isset($testMap[$name])) {
|
||||
$message = sprintf('The test "%s" does not exist', $name);
|
||||
if ($alternatives = $env->computeAlternatives($name, array_keys($env->getTests()))) {
|
||||
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
|
||||
}
|
||||
|
||||
throw new Twig_Error_Syntax($message, $line, $parser->getFilename());
|
||||
}
|
||||
|
||||
if ($testMap[$name] instanceof Twig_SimpleTest) {
|
||||
return $testMap[$name]->getNodeClass();
|
||||
@ -312,7 +365,7 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
* Cycles over a value.
|
||||
*
|
||||
* @param ArrayAccess|array $values An array or an ArrayAccess instance
|
||||
* @param integer $position The cycle position
|
||||
* @param int $position The cycle position
|
||||
*
|
||||
* @return string The next value in the cycle
|
||||
*/
|
||||
@ -332,7 +385,7 @@ function twig_cycle($values, $position)
|
||||
* - a random integer between 0 and the integer parameter
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param Traversable|array|integer|string $values The values to pick a random item from
|
||||
* @param Traversable|array|int|string $values The values to pick a random item from
|
||||
*
|
||||
* @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is).
|
||||
*
|
||||
@ -348,7 +401,7 @@ function twig_random(Twig_Environment $env, $values = null)
|
||||
return $values < 0 ? mt_rand($values, 0) : mt_rand(0, $values);
|
||||
}
|
||||
|
||||
if (is_object($values) && $values instanceof Traversable) {
|
||||
if ($values instanceof Traversable) {
|
||||
$values = iterator_to_array($values);
|
||||
} elseif (is_string($values)) {
|
||||
if ('' === $values) {
|
||||
@ -391,10 +444,10 @@ function twig_random(Twig_Environment $env, $values = null)
|
||||
* {{ post.published_at|date("m/d/Y") }}
|
||||
* </pre>
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param DateTime|DateInterval|string $date A date
|
||||
* @param string $format A format
|
||||
* @param DateTimeZone|string $timezone A timezone
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param DateTime|DateTimeInterface|DateInterval|string $date A date
|
||||
* @param string|null $format The target format, null to use the default
|
||||
* @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
|
||||
*
|
||||
* @return string The formatted date
|
||||
*/
|
||||
@ -428,9 +481,12 @@ function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $
|
||||
function twig_date_modify_filter(Twig_Environment $env, $date, $modifier)
|
||||
{
|
||||
$date = twig_date_converter($env, $date, false);
|
||||
$date->modify($modifier);
|
||||
$resultDate = $date->modify($modifier);
|
||||
|
||||
return $date;
|
||||
// This is a hack to ensure PHP 5.2 support and support for DateTimeImmutable
|
||||
// DateTime::modify does not return the modified DateTime object < 5.3.0
|
||||
// and DateTimeImmutable does not modify $date.
|
||||
return null === $resultDate ? $date : $resultDate;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -442,27 +498,32 @@ function twig_date_modify_filter(Twig_Environment $env, $date, $modifier)
|
||||
* {% endif %}
|
||||
* </pre>
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param DateTime|string $date A date
|
||||
* @param DateTimeZone|string $timezone A timezone
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param DateTime|DateTimeInterface|string|null $date A date
|
||||
* @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
|
||||
*
|
||||
* @return DateTime A DateTime instance
|
||||
*/
|
||||
function twig_date_converter(Twig_Environment $env, $date = null, $timezone = null)
|
||||
{
|
||||
// determine the timezone
|
||||
if (!$timezone) {
|
||||
$defaultTimezone = $env->getExtension('core')->getTimezone();
|
||||
} elseif (!$timezone instanceof DateTimeZone) {
|
||||
$defaultTimezone = new DateTimeZone($timezone);
|
||||
} else {
|
||||
$defaultTimezone = $timezone;
|
||||
if (false !== $timezone) {
|
||||
if (null === $timezone) {
|
||||
$timezone = $env->getExtension('core')->getTimezone();
|
||||
} elseif (!$timezone instanceof DateTimeZone) {
|
||||
$timezone = new DateTimeZone($timezone);
|
||||
}
|
||||
}
|
||||
|
||||
if ($date instanceof DateTime) {
|
||||
// immutable dates
|
||||
if ($date instanceof DateTimeImmutable) {
|
||||
return false !== $timezone ? $date->setTimezone($timezone) : $date;
|
||||
}
|
||||
|
||||
if ($date instanceof DateTime || $date instanceof DateTimeInterface) {
|
||||
$date = clone $date;
|
||||
if (false !== $timezone) {
|
||||
$date->setTimezone($defaultTimezone);
|
||||
$date->setTimezone($timezone);
|
||||
}
|
||||
|
||||
return $date;
|
||||
@ -473,14 +534,36 @@ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = nu
|
||||
$date = '@'.$date;
|
||||
}
|
||||
|
||||
$date = new DateTime($date, $defaultTimezone);
|
||||
$date = new DateTime($date, $env->getExtension('core')->getTimezone());
|
||||
if (false !== $timezone) {
|
||||
$date->setTimezone($defaultTimezone);
|
||||
$date->setTimezone($timezone);
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds a number.
|
||||
*
|
||||
* @param int|float $value The value to round
|
||||
* @param int|float $precision The rounding precision
|
||||
* @param string $method The method to use for rounding
|
||||
*
|
||||
* @return int|float The rounded number
|
||||
*/
|
||||
function twig_round($value, $precision = 0, $method = 'common')
|
||||
{
|
||||
if ('common' == $method) {
|
||||
return round($value, $precision);
|
||||
}
|
||||
|
||||
if ('ceil' != $method && 'floor' != $method) {
|
||||
throw new Twig_Error_Runtime('The round filter only supports the "common", "ceil", and "floor" methods.');
|
||||
}
|
||||
|
||||
return $method($value * pow(10, $precision)) / pow(10, $precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number format filter.
|
||||
*
|
||||
@ -490,7 +573,7 @@ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = nu
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param mixed $number A float/int/string of the number to format
|
||||
* @param integer $decimal The number of decimal points to display.
|
||||
* @param int $decimal The number of decimal points to display.
|
||||
* @param string $decimalPoint The character(s) to use for the decimal point.
|
||||
* @param string $thousandSep The character(s) to use for the thousands separator.
|
||||
*
|
||||
@ -515,32 +598,31 @@ function twig_number_format_filter(Twig_Environment $env, $number, $decimal = nu
|
||||
}
|
||||
|
||||
/**
|
||||
* URL encodes a string as a path segment or an array as a query string.
|
||||
* URL encodes (RFC 3986) a string as a path segment or an array as a query string.
|
||||
*
|
||||
* @param string|array $url A URL or an array of query parameters
|
||||
* @param bool $raw true to use rawurlencode() instead of urlencode
|
||||
*
|
||||
* @return string The URL encoded value
|
||||
*/
|
||||
function twig_urlencode_filter($url, $raw = false)
|
||||
function twig_urlencode_filter($url)
|
||||
{
|
||||
if (is_array($url)) {
|
||||
if (defined('PHP_QUERY_RFC3986')) {
|
||||
return http_build_query($url, '', '&', PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
return http_build_query($url, '', '&');
|
||||
}
|
||||
|
||||
if ($raw) {
|
||||
return rawurlencode($url);
|
||||
}
|
||||
|
||||
return urlencode($url);
|
||||
return rawurlencode($url);
|
||||
}
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
|
||||
if (PHP_VERSION_ID < 50300) {
|
||||
/**
|
||||
* JSON encodes a variable.
|
||||
*
|
||||
* @param mixed $value The value to encode.
|
||||
* @param integer $options Not used on PHP 5.2.x
|
||||
* @param mixed $value The value to encode.
|
||||
* @param int $options Not used on PHP 5.2.x
|
||||
*
|
||||
* @return mixed The JSON encoded value
|
||||
*/
|
||||
@ -558,8 +640,8 @@ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
|
||||
/**
|
||||
* JSON encodes a variable.
|
||||
*
|
||||
* @param mixed $value The value to encode.
|
||||
* @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
|
||||
* @param mixed $value The value to encode.
|
||||
* @param int $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
|
||||
*
|
||||
* @return mixed The JSON encoded value
|
||||
*/
|
||||
@ -601,7 +683,7 @@ function _twig_markup2string(&$value)
|
||||
function twig_array_merge($arr1, $arr2)
|
||||
{
|
||||
if (!is_array($arr1) || !is_array($arr2)) {
|
||||
throw new Twig_Error_Runtime('The merge filter only works with arrays or hashes.');
|
||||
throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or hashes; %s and %s given.', gettype($arr1), gettype($arr2)));
|
||||
}
|
||||
|
||||
return array_merge($arr1, $arr2);
|
||||
@ -612,16 +694,28 @@ function twig_array_merge($arr1, $arr2)
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param mixed $item A variable
|
||||
* @param integer $start Start of the slice
|
||||
* @param integer $length Size of the slice
|
||||
* @param Boolean $preserveKeys Whether to preserve key or not (when the input is an array)
|
||||
* @param int $start Start of the slice
|
||||
* @param int $length Size of the slice
|
||||
* @param bool $preserveKeys Whether to preserve key or not (when the input is an array)
|
||||
*
|
||||
* @return mixed The sliced variable
|
||||
*/
|
||||
function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false)
|
||||
{
|
||||
if (is_object($item) && $item instanceof Traversable) {
|
||||
$item = iterator_to_array($item, false);
|
||||
if ($item instanceof Traversable) {
|
||||
if ($item instanceof IteratorAggregate) {
|
||||
$item = $item->getIterator();
|
||||
}
|
||||
|
||||
if ($start >= 0 && $length >= 0 && $item instanceof Iterator) {
|
||||
try {
|
||||
return iterator_to_array(new LimitIterator($item, $start, $length === null ? -1 : $length), $preserveKeys);
|
||||
} catch (OutOfBoundsException $exception) {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
$item = iterator_to_array($item, $preserveKeys);
|
||||
}
|
||||
|
||||
if (is_array($item)) {
|
||||
@ -631,10 +725,10 @@ function twig_slice(Twig_Environment $env, $item, $start, $length = null, $prese
|
||||
$item = (string) $item;
|
||||
|
||||
if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) {
|
||||
return mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset);
|
||||
return (string) mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset);
|
||||
}
|
||||
|
||||
return null === $length ? substr($item, $start) : substr($item, $start, $length);
|
||||
return (string) (null === $length ? substr($item, $start) : substr($item, $start, $length));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -649,7 +743,7 @@ function twig_first(Twig_Environment $env, $item)
|
||||
{
|
||||
$elements = twig_slice($env, $item, 0, 1, false);
|
||||
|
||||
return is_string($elements) ? $elements[0] : current($elements);
|
||||
return is_string($elements) ? $elements : current($elements);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -664,7 +758,7 @@ function twig_last(Twig_Environment $env, $item)
|
||||
{
|
||||
$elements = twig_slice($env, $item, -1, 1, false);
|
||||
|
||||
return is_string($elements) ? $elements[0] : current($elements);
|
||||
return is_string($elements) ? $elements : current($elements);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -687,7 +781,7 @@ function twig_last(Twig_Environment $env, $item)
|
||||
*/
|
||||
function twig_join_filter($value, $glue = '')
|
||||
{
|
||||
if (is_object($value) && $value instanceof Traversable) {
|
||||
if ($value instanceof Traversable) {
|
||||
$value = iterator_to_array($value, false);
|
||||
}
|
||||
|
||||
@ -713,17 +807,35 @@ function twig_join_filter($value, $glue = '')
|
||||
*
|
||||
* @param string $value A string
|
||||
* @param string $delimiter The delimiter
|
||||
* @param integer $limit The limit
|
||||
* @param int $limit The limit
|
||||
*
|
||||
* @return array The split string as an array
|
||||
*/
|
||||
function twig_split_filter($value, $delimiter, $limit = null)
|
||||
function twig_split_filter(Twig_Environment $env, $value, $delimiter, $limit = null)
|
||||
{
|
||||
if (empty($delimiter)) {
|
||||
if (!empty($delimiter)) {
|
||||
return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
|
||||
}
|
||||
|
||||
if (!function_exists('mb_get_info') || null === $charset = $env->getCharset()) {
|
||||
return str_split($value, null === $limit ? 1 : $limit);
|
||||
}
|
||||
|
||||
return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
|
||||
if ($limit <= 1) {
|
||||
return preg_split('/(?<!^)(?!$)/u', $value);
|
||||
}
|
||||
|
||||
$length = mb_strlen($value, $charset);
|
||||
if ($length < $limit) {
|
||||
return array($value);
|
||||
}
|
||||
|
||||
$r = array();
|
||||
for ($i = 0; $i < $length; $i += $limit) {
|
||||
$r[] = mb_substr($value, $i, $limit, $charset);
|
||||
}
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
// The '_default' filter is used internally to avoid using the ternary operator
|
||||
@ -771,7 +883,7 @@ function twig_get_array_keys_filter($array)
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param array|Traversable|string $item An array, a Traversable instance, or a string
|
||||
* @param Boolean $preserveKeys Whether to preserve key or not
|
||||
* @param bool $preserveKeys Whether to preserve key or not
|
||||
*
|
||||
* @return mixed The reversed input
|
||||
*/
|
||||
@ -822,15 +934,11 @@ function twig_sort_filter($array)
|
||||
function twig_in_filter($value, $compare)
|
||||
{
|
||||
if (is_array($compare)) {
|
||||
return in_array($value, $compare, is_object($value));
|
||||
} elseif (is_string($compare)) {
|
||||
if (!strlen($value)) {
|
||||
return empty($compare);
|
||||
}
|
||||
|
||||
return false !== strpos($compare, (string) $value);
|
||||
} elseif (is_object($compare) && $compare instanceof Traversable) {
|
||||
return in_array($value, iterator_to_array($compare, false), is_object($value));
|
||||
return in_array($value, $compare, is_object($value) || is_resource($value));
|
||||
} elseif (is_string($compare) && (is_string($value) || is_int($value) || is_float($value))) {
|
||||
return '' === $value || false !== strpos($compare, (string) $value);
|
||||
} elseif ($compare instanceof Traversable) {
|
||||
return in_array($value, iterator_to_array($compare, false), is_object($value) || is_resource($value));
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -843,7 +951,7 @@ function twig_in_filter($value, $compare)
|
||||
* @param string $string The value to be escaped
|
||||
* @param string $strategy The escaping strategy
|
||||
* @param string $charset The charset
|
||||
* @param Boolean $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
|
||||
* @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
|
||||
*/
|
||||
function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
|
||||
{
|
||||
@ -870,22 +978,30 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
|
||||
// Using a static variable to avoid initializing the array
|
||||
// each time the function is called. Moving the declaration on the
|
||||
// top of the function slow downs other escaping strategies.
|
||||
static $htmlspecialcharsCharsets = array(
|
||||
'ISO-8859-1' => true, 'ISO8859-1' => true,
|
||||
'ISO-8859-15' => true, 'ISO8859-15' => true,
|
||||
'utf-8' => true, 'UTF-8' => true,
|
||||
'CP866' => true, 'IBM866' => true, '866' => true,
|
||||
'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
|
||||
'1251' => true,
|
||||
'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
|
||||
'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
|
||||
'BIG5' => true, '950' => true,
|
||||
'GB2312' => true, '936' => true,
|
||||
'BIG5-HKSCS' => true,
|
||||
'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
|
||||
'EUC-JP' => true, 'EUCJP' => true,
|
||||
'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
|
||||
);
|
||||
static $htmlspecialcharsCharsets;
|
||||
|
||||
if (null === $htmlspecialcharsCharsets) {
|
||||
if (defined('HHVM_VERSION')) {
|
||||
$htmlspecialcharsCharsets = array('utf-8' => true, 'UTF-8' => true);
|
||||
} else {
|
||||
$htmlspecialcharsCharsets = array(
|
||||
'ISO-8859-1' => true, 'ISO8859-1' => true,
|
||||
'ISO-8859-15' => true, 'ISO8859-15' => true,
|
||||
'utf-8' => true, 'UTF-8' => true,
|
||||
'CP866' => true, 'IBM866' => true, '866' => true,
|
||||
'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
|
||||
'1251' => true,
|
||||
'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
|
||||
'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
|
||||
'BIG5' => true, '950' => true,
|
||||
'GB2312' => true, '936' => true,
|
||||
'BIG5-HKSCS' => true,
|
||||
'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
|
||||
'EUC-JP' => true, 'EUCJP' => true,
|
||||
'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($htmlspecialcharsCharsets[$charset])) {
|
||||
return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
|
||||
@ -957,16 +1073,26 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
|
||||
return $string;
|
||||
|
||||
case 'url':
|
||||
// hackish test to avoid version_compare that is much slower, this works unless PHP releases a 5.10.*
|
||||
// at that point however PHP 5.2.* support can be removed
|
||||
if (PHP_VERSION < '5.3.0') {
|
||||
if (PHP_VERSION_ID < 50300) {
|
||||
return str_replace('%7E', '~', rawurlencode($string));
|
||||
}
|
||||
|
||||
return rawurlencode($string);
|
||||
|
||||
default:
|
||||
throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: html, js, url, css, and html_attr).', $strategy));
|
||||
static $escapers;
|
||||
|
||||
if (null === $escapers) {
|
||||
$escapers = $env->getExtension('core')->getEscapers();
|
||||
}
|
||||
|
||||
if (isset($escapers[$strategy])) {
|
||||
return call_user_func($escapers[$strategy], $env, $string, $charset);
|
||||
}
|
||||
|
||||
$validStrategies = implode(', ', array_merge(array('html', 'js', 'url', 'css', 'html_attr'), array_keys($escapers)));
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1088,7 +1214,6 @@ function _twig_escape_html_attr_callback($matches)
|
||||
* Per OWASP recommendations, we'll use hex entities for any other
|
||||
* characters where a named entity does not exist.
|
||||
*/
|
||||
|
||||
return sprintf('&#x%s;', $hex);
|
||||
}
|
||||
|
||||
@ -1100,7 +1225,7 @@ if (function_exists('mb_get_info')) {
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param mixed $thing A variable
|
||||
*
|
||||
* @return integer The length of the value
|
||||
* @return int The length of the value
|
||||
*/
|
||||
function twig_length_filter(Twig_Environment $env, $thing)
|
||||
{
|
||||
@ -1184,7 +1309,7 @@ else {
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param mixed $thing A variable
|
||||
*
|
||||
* @return integer The length of the value
|
||||
* @return int The length of the value
|
||||
*/
|
||||
function twig_length_filter(Twig_Environment $env, $thing)
|
||||
{
|
||||
@ -1240,7 +1365,7 @@ function twig_ensure_traversable($seq)
|
||||
*
|
||||
* @param mixed $value A variable
|
||||
*
|
||||
* @return Boolean true if the value is empty, false otherwise
|
||||
* @return bool true if the value is empty, false otherwise
|
||||
*/
|
||||
function twig_test_empty($value)
|
||||
{
|
||||
@ -1263,7 +1388,7 @@ function twig_test_empty($value)
|
||||
*
|
||||
* @param mixed $value A variable
|
||||
*
|
||||
* @return Boolean true if the value is traversable
|
||||
* @return bool true if the value is traversable
|
||||
*/
|
||||
function twig_test_iterable($value)
|
||||
{
|
||||
@ -1273,16 +1398,18 @@ function twig_test_iterable($value)
|
||||
/**
|
||||
* Renders a template.
|
||||
*
|
||||
* @param string $template The template to render
|
||||
* @param array $variables The variables to pass to the template
|
||||
* @param Boolean $with_context Whether to pass the current context variables or not
|
||||
* @param Boolean $ignore_missing Whether to ignore missing templates or not
|
||||
* @param Boolean $sandboxed Whether to sandbox the template or not
|
||||
* @param string|array $template The template to render or an array of templates to try consecutively
|
||||
* @param array $variables The variables to pass to the template
|
||||
* @param bool $with_context Whether to pass the current context variables or not
|
||||
* @param bool $ignore_missing Whether to ignore missing templates or not
|
||||
* @param bool $sandboxed Whether to sandbox the template or not
|
||||
*
|
||||
* @return string The rendered template
|
||||
*/
|
||||
function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
|
||||
{
|
||||
$alreadySandboxed = false;
|
||||
$sandbox = null;
|
||||
if ($withContext) {
|
||||
$variables = array_merge($context, $variables);
|
||||
}
|
||||
@ -1307,6 +1434,18 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a template content without rendering it.
|
||||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return string The template source
|
||||
*/
|
||||
function twig_source(Twig_Environment $env, $name)
|
||||
{
|
||||
return $env->getLoader()->getSource($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the ability to get constants from instances as well as class/global constants.
|
||||
*
|
||||
@ -1328,14 +1467,14 @@ function twig_constant($constant, $object = null)
|
||||
* Batches item.
|
||||
*
|
||||
* @param array $items An array of items
|
||||
* @param integer $size The size of the batch
|
||||
* @param int $size The size of the batch
|
||||
* @param mixed $fill A value used to fill missing items
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function twig_array_batch($items, $size, $fill = null)
|
||||
{
|
||||
if (is_object($items) && $items instanceof Traversable) {
|
||||
if ($items instanceof Traversable) {
|
||||
$items = iterator_to_array($items, false);
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ class Twig_Extension_Escaper extends Twig_Extension
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
@ -64,6 +64,10 @@ class Twig_Extension_Escaper extends Twig_Extension
|
||||
$defaultStrategy = 'html';
|
||||
}
|
||||
|
||||
if ('filename' === $defaultStrategy) {
|
||||
$defaultStrategy = array('Twig_FileExtensionEscapingStrategy', 'guess');
|
||||
}
|
||||
|
||||
$this->defaultStrategy = $defaultStrategy;
|
||||
}
|
||||
|
||||
|
52
inc/lib/Twig/Extension/Profiler.php
Normal file
52
inc/lib/Twig/Extension/Profiler.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2015 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
class Twig_Extension_Profiler extends Twig_Extension
|
||||
{
|
||||
private $actives;
|
||||
|
||||
public function __construct(Twig_Profiler_Profile $profile)
|
||||
{
|
||||
$this->actives = array($profile);
|
||||
}
|
||||
|
||||
public function enter(Twig_Profiler_Profile $profile)
|
||||
{
|
||||
$this->actives[0]->addProfile($profile);
|
||||
array_unshift($this->actives, $profile);
|
||||
}
|
||||
|
||||
public function leave(Twig_Profiler_Profile $profile)
|
||||
{
|
||||
$profile->leave();
|
||||
array_shift($this->actives);
|
||||
|
||||
if (1 === count($this->actives)) {
|
||||
$this->actives[0]->leave();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return array(new Twig_Profiler_NodeVisitor_Profiler($this->getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'profiler';
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ class Twig_Extension_Sandbox extends Twig_Extension
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
@ -93,7 +93,7 @@ class Twig_Extension_Sandbox extends Twig_Extension
|
||||
|
||||
public function ensureToStringAllowed($obj)
|
||||
{
|
||||
if (is_object($obj)) {
|
||||
if ($this->isSandboxed() && is_object($obj)) {
|
||||
$this->policy->checkMethodAllowed($obj, '__toString');
|
||||
}
|
||||
|
||||
|
@ -43,22 +43,5 @@ class Twig_Extension_StringLoader extends Twig_Extension
|
||||
*/
|
||||
function twig_template_from_string(Twig_Environment $env, $template)
|
||||
{
|
||||
$name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), true), false));
|
||||
|
||||
$loader = new Twig_Loader_Chain(array(
|
||||
new Twig_Loader_Array(array($name => $template)),
|
||||
$current = $env->getLoader(),
|
||||
));
|
||||
|
||||
$env->setLoader($loader);
|
||||
try {
|
||||
$template = $env->loadTemplate($name);
|
||||
} catch (Exception $e) {
|
||||
$env->setLoader($current);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
$env->setLoader($current);
|
||||
|
||||
return $template;
|
||||
return $env->createTemplate($template);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ interface Twig_ExtensionInterface
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors();
|
||||
|
||||
|
49
inc/lib/Twig/FileExtensionEscapingStrategy.php
Normal file
49
inc/lib/Twig/FileExtensionEscapingStrategy.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2015 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default autoescaping strategy based on file names.
|
||||
*
|
||||
* This strategy sets the HTML as the default autoescaping strategy,
|
||||
* but changes it based on the filename.
|
||||
*
|
||||
* Note that there is no runtime performance impact as the
|
||||
* default autoescaping strategy is set at compilation time.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_FileExtensionEscapingStrategy
|
||||
{
|
||||
/**
|
||||
* Guesses the best autoescaping strategy based on the file name.
|
||||
*
|
||||
* @param string $filename The template file name
|
||||
*
|
||||
* @return string The escaping strategy name to use
|
||||
*/
|
||||
public static function guess($filename)
|
||||
{
|
||||
if (!preg_match('{\.(js|css|txt)(?:\.[^/\\\\]+)?$}', $filename, $match)) {
|
||||
return 'html';
|
||||
}
|
||||
|
||||
switch ($match[1]) {
|
||||
case 'js':
|
||||
return 'js';
|
||||
|
||||
case 'css':
|
||||
return 'css';
|
||||
|
||||
case 'txt':
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
31
inc/lib/Twig/LICENSE
Normal file
31
inc/lib/Twig/LICENSE
Normal file
@ -0,0 +1,31 @@
|
||||
Copyright (c) 2009-2014 by the Twig Team.
|
||||
|
||||
Some rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -73,18 +73,15 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes a source code.
|
||||
*
|
||||
* @param string $code The source code
|
||||
* @param string $filename A unique identifier for the source code
|
||||
*
|
||||
* @return Twig_TokenStream A token stream instance
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tokenize($code, $filename = null)
|
||||
{
|
||||
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
|
||||
$mbEncoding = mb_internal_encoding();
|
||||
mb_internal_encoding('ASCII');
|
||||
} else {
|
||||
$mbEncoding = null;
|
||||
}
|
||||
|
||||
$this->code = str_replace(array("\r\n", "\r"), "\n", $code);
|
||||
@ -135,7 +132,7 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
|
||||
}
|
||||
|
||||
if (isset($mbEncoding)) {
|
||||
if ($mbEncoding) {
|
||||
mb_internal_encoding($mbEncoding);
|
||||
}
|
||||
|
||||
@ -233,7 +230,7 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
|
||||
// operators
|
||||
if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::OPERATOR_TYPE, $match[0]);
|
||||
$this->pushToken(Twig_Token::OPERATOR_TYPE, preg_replace('/\s+/', ' ', $match[0]));
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// names
|
||||
@ -320,13 +317,10 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
$this->pushToken(Twig_Token::INTERPOLATION_START_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->pushState(self::STATE_INTERPOLATION);
|
||||
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, null, $this->cursor) && strlen($match[0]) > 0) {
|
||||
$this->pushToken(Twig_Token::STRING_TYPE, stripcslashes($match[0]));
|
||||
$this->moveCursor($match[0]);
|
||||
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
|
||||
|
||||
list($expect, $lineno) = array_pop($this->brackets);
|
||||
if ($this->code[$this->cursor] != '"') {
|
||||
throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
|
||||
@ -382,10 +376,15 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
// an operator that ends with a character must be followed by
|
||||
// a whitespace or a parenthesis
|
||||
if (ctype_alpha($operator[$length - 1])) {
|
||||
$regex[] = preg_quote($operator, '/').'(?=[\s()])';
|
||||
$r = preg_quote($operator, '/').'(?=[\s()])';
|
||||
} else {
|
||||
$regex[] = preg_quote($operator, '/');
|
||||
$r = preg_quote($operator, '/');
|
||||
}
|
||||
|
||||
// an operator with a space can be any amount of whitespaces
|
||||
$r = preg_replace('/\s+/', '\s+', $r);
|
||||
|
||||
$regex[] = $r;
|
||||
}
|
||||
|
||||
return '/'.implode('|', $regex).'/A';
|
||||
|
@ -13,7 +13,8 @@
|
||||
* Interface implemented by lexer classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*
|
||||
* @deprecated since 1.12 (to be removed in 3.0)
|
||||
*/
|
||||
interface Twig_LexerInterface
|
||||
{
|
||||
@ -24,6 +25,8 @@ interface Twig_LexerInterface
|
||||
* @param string $filename A unique identifier for the source code
|
||||
*
|
||||
* @return Twig_TokenStream A token stream instance
|
||||
*
|
||||
* @throws Twig_Error_Syntax When the code is syntactically wrong
|
||||
*/
|
||||
public function tokenize($code, $filename = null);
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
* source code of the template). If you don't want to see your cache grows out of
|
||||
* control, you need to take care of clearing the old cache file by yourself.
|
||||
*
|
||||
* This loader should only be used for unit testing.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
|
@ -143,7 +143,8 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
||||
*/
|
||||
public function exists($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
$name = $this->normalizeName($name);
|
||||
|
||||
if (isset($this->cache[$name])) {
|
||||
return true;
|
||||
}
|
||||
@ -167,10 +168,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
||||
|
||||
protected function findTemplate($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
// normalize name
|
||||
$name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/'));
|
||||
$name = $this->normalizeName($name);
|
||||
|
||||
if (isset($this->cache[$name])) {
|
||||
return $this->cache[$name];
|
||||
@ -178,16 +176,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
||||
|
||||
$this->validateName($name);
|
||||
|
||||
$namespace = self::MAIN_NAMESPACE;
|
||||
$shortname = $name;
|
||||
if (isset($name[0]) && '@' == $name[0]) {
|
||||
if (false === $pos = strpos($name, '/')) {
|
||||
throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
|
||||
}
|
||||
|
||||
$namespace = substr($name, 1, $pos - 1);
|
||||
$shortname = substr($name, $pos + 1);
|
||||
}
|
||||
list($namespace, $shortname) = $this->parseName($name);
|
||||
|
||||
if (!isset($this->paths[$namespace])) {
|
||||
throw new Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace));
|
||||
@ -195,6 +184,10 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
||||
|
||||
foreach ($this->paths[$namespace] as $path) {
|
||||
if (is_file($path.'/'.$shortname)) {
|
||||
if (false !== $realpath = realpath($path.'/'.$shortname)) {
|
||||
return $this->cache[$name] = $realpath;
|
||||
}
|
||||
|
||||
return $this->cache[$name] = $path.'/'.$shortname;
|
||||
}
|
||||
}
|
||||
@ -202,6 +195,27 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
||||
throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace])));
|
||||
}
|
||||
|
||||
protected function parseName($name, $default = self::MAIN_NAMESPACE)
|
||||
{
|
||||
if (isset($name[0]) && '@' == $name[0]) {
|
||||
if (false === $pos = strpos($name, '/')) {
|
||||
throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
|
||||
}
|
||||
|
||||
$namespace = substr($name, 1, $pos - 1);
|
||||
$shortname = substr($name, $pos + 1);
|
||||
|
||||
return array($namespace, $shortname);
|
||||
}
|
||||
|
||||
return array($default, $name);
|
||||
}
|
||||
|
||||
protected function normalizeName($name)
|
||||
{
|
||||
return preg_replace('#/{2,}#', '/', strtr((string) $name, '\\', '/'));
|
||||
}
|
||||
|
||||
protected function validateName($name)
|
||||
{
|
||||
if (false !== strpos($name, "\0")) {
|
||||
|
@ -12,15 +12,15 @@
|
||||
/**
|
||||
* Loads a template from a string.
|
||||
*
|
||||
* This loader should only be used for unit testing as it has many limitations
|
||||
* (for instance, the include or extends tag does not make any sense for a string
|
||||
* loader).
|
||||
* This loader should NEVER be used. It only exists for Twig internal purposes.
|
||||
*
|
||||
* When using this loader with a cache mechanism, you should know that a new cache
|
||||
* key is generated each time a template content "changes" (the cache key being the
|
||||
* source code of the template). If you don't want to see your cache grows out of
|
||||
* control, you need to take care of clearing the old cache file by yourself.
|
||||
*
|
||||
* @deprecated since 1.18.1 (to be removed in 2.0)
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
|
@ -44,7 +44,7 @@ interface Twig_LoaderInterface
|
||||
* @param string $name The template name
|
||||
* @param timestamp $time The last modification time of the cached template
|
||||
*
|
||||
* @return Boolean true if the template is fresh, false otherwise
|
||||
* @return bool true if the template is fresh, false otherwise
|
||||
*
|
||||
* @throws Twig_Error_Loader When $name is not found
|
||||
*/
|
||||
|
@ -28,10 +28,10 @@ class Twig_Node implements Twig_NodeInterface
|
||||
* The nodes are automatically made available as properties ($this->node).
|
||||
* The attributes are automatically made available as array items ($this['name']).
|
||||
*
|
||||
* @param array $nodes An array of named nodes
|
||||
* @param array $attributes An array of attributes (should not be nodes)
|
||||
* @param integer $lineno The line number
|
||||
* @param string $tag The tag name associated with the Node
|
||||
* @param array $nodes An array of named nodes
|
||||
* @param array $attributes An array of attributes (should not be nodes)
|
||||
* @param int $lineno The line number
|
||||
* @param string $tag The tag name associated with the Node
|
||||
*/
|
||||
public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null)
|
||||
{
|
||||
@ -69,6 +69,9 @@ class Twig_Node implements Twig_NodeInterface
|
||||
return implode("\n", $repr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.16.1 (to be removed in 2.0)
|
||||
*/
|
||||
public function toXml($asDom = false)
|
||||
{
|
||||
$dom = new DOMDocument('1.0', 'UTF-8');
|
||||
@ -121,7 +124,7 @@ class Twig_Node implements Twig_NodeInterface
|
||||
*
|
||||
* @param string The attribute name
|
||||
*
|
||||
* @return Boolean true if the attribute is defined, false otherwise
|
||||
* @return bool true if the attribute is defined, false otherwise
|
||||
*/
|
||||
public function hasAttribute($name)
|
||||
{
|
||||
@ -170,7 +173,7 @@ class Twig_Node implements Twig_NodeInterface
|
||||
*
|
||||
* @param string The node name
|
||||
*
|
||||
* @return Boolean true if the node with the given name exists, false otherwise
|
||||
* @return bool true if the node with the given name exists, false otherwise
|
||||
*/
|
||||
public function hasNode($name)
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ class Twig_Node_AutoEscape extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ class Twig_Node_Block extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ class Twig_Node_BlockReference extends Twig_Node implements Twig_NodeOutputInter
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
78
inc/lib/Twig/Node/CheckSecurity.php
Normal file
78
inc/lib/Twig/Node/CheckSecurity.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2015 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_CheckSecurity extends Twig_Node
|
||||
{
|
||||
protected $usedFilters;
|
||||
protected $usedTags;
|
||||
protected $usedFunctions;
|
||||
|
||||
public function __construct(array $usedFilters, array $usedTags, array $usedFunctions)
|
||||
{
|
||||
$this->usedFilters = $usedFilters;
|
||||
$this->usedTags = $usedTags;
|
||||
$this->usedFunctions = $usedFunctions;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$tags = $filters = $functions = array();
|
||||
foreach (array('tags', 'filters', 'functions') as $type) {
|
||||
foreach ($this->{'used'.ucfirst($type)} as $name => $node) {
|
||||
if ($node instanceof Twig_Node) {
|
||||
${$type}[$name] = $node->getLine();
|
||||
} else {
|
||||
${$type}[$node] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("\$tags = ")->repr(array_filter($tags))->raw(";\n")
|
||||
->write("\$filters = ")->repr(array_filter($filters))->raw(";\n")
|
||||
->write("\$functions = ")->repr(array_filter($functions))->raw(";\n\n")
|
||||
->write("try {\n")
|
||||
->indent()
|
||||
->write("\$this->env->getExtension('sandbox')->checkSecurity(\n")
|
||||
->indent()
|
||||
->write(!$tags ? "array(),\n" : "array('".implode("', '", array_keys($tags))."'),\n")
|
||||
->write(!$filters ? "array(),\n" : "array('".implode("', '", array_keys($filters))."'),\n")
|
||||
->write(!$functions ? "array()\n" : "array('".implode("', '", array_keys($functions))."')\n")
|
||||
->outdent()
|
||||
->write(");\n")
|
||||
->outdent()
|
||||
->write("} catch (Twig_Sandbox_SecurityError \$e) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateFile(\$this->getTemplateName());\n\n")
|
||||
->write("if (\$e instanceof Twig_Sandbox_SecurityNotAllowedTagError && isset(\$tags[\$e->getTagName()])) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n")
|
||||
->outdent()
|
||||
->write("} elseif (\$e instanceof Twig_Sandbox_SecurityNotAllowedFilterError && isset(\$filters[\$e->getFilterName()])) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateLine(\$filters[\$e->getFilterName()]);\n")
|
||||
->outdent()
|
||||
->write("} elseif (\$e instanceof Twig_Sandbox_SecurityNotAllowedFunctionError && isset(\$functions[\$e->getFunctionName()])) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateLine(\$functions[\$e->getFunctionName()]);\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
->write("throw \$e;\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ class Twig_Node_Do extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -28,9 +28,13 @@ class Twig_Node_Embed extends Twig_Node_Include
|
||||
protected function addGetTemplate(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("\$this->env->loadTemplate(")
|
||||
->write("\$this->loadTemplate(")
|
||||
->string($this->getAttribute('filename'))
|
||||
->raw(', ')
|
||||
->repr($compiler->getFilename())
|
||||
->raw(', ')
|
||||
->repr($this->getLine())
|
||||
->raw(', ')
|
||||
->string($this->getAttribute('index'))
|
||||
->raw(")")
|
||||
;
|
||||
|
@ -63,7 +63,7 @@ class Twig_Node_Expression_Array extends Twig_Node_Expression
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ abstract class Twig_Node_Expression_Binary extends Twig_Node_Expression
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
30
inc/lib/Twig/Node/Expression/Binary/EndsWith.php
Normal file
30
inc/lib/Twig/Node/Expression/Binary/EndsWith.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2013 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_EndsWith extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$left = $compiler->getVarName();
|
||||
$right = $compiler->getVarName();
|
||||
$compiler
|
||||
->raw(sprintf('(is_string($%s = ', $left))
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(sprintf(') && is_string($%s = ', $right))
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(sprintf(') && (\'\' === $%2$s || $%2$s === substr($%1$s, -strlen($%2$s))))', $left, $right))
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('');
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ class Twig_Node_Expression_Binary_FloorDiv extends Twig_Node_Expression_Binary
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ class Twig_Node_Expression_Binary_In extends Twig_Node_Expression_Binary
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
28
inc/lib/Twig/Node/Expression/Binary/Matches.php
Normal file
28
inc/lib/Twig/Node/Expression/Binary/Matches.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2013 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_Matches extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('preg_match(')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('');
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ class Twig_Node_Expression_Binary_NotIn extends Twig_Node_Expression_Binary
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ class Twig_Node_Expression_Binary_Power extends Twig_Node_Expression_Binary
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ class Twig_Node_Expression_Binary_Range extends Twig_Node_Expression_Binary
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
30
inc/lib/Twig/Node/Expression/Binary/StartsWith.php
Normal file
30
inc/lib/Twig/Node/Expression/Binary/StartsWith.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2013 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_StartsWith extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$left = $compiler->getVarName();
|
||||
$right = $compiler->getVarName();
|
||||
$compiler
|
||||
->raw(sprintf('(is_string($%s = ', $left))
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(sprintf(') && is_string($%s = ', $right))
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(sprintf(') && (\'\' === $%2$s || 0 === strpos($%1$s, $%2$s)))', $left, $right))
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('');
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ class Twig_Node_Expression_BlockReference extends Twig_Node_Expression
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -12,10 +12,8 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
||||
{
|
||||
protected function compileCallable(Twig_Compiler $compiler)
|
||||
{
|
||||
$callable = $this->getAttribute('callable');
|
||||
|
||||
$closingParenthesis = false;
|
||||
if ($callable) {
|
||||
if ($this->hasAttribute('callable') && $callable = $this->getAttribute('callable')) {
|
||||
if (is_string($callable)) {
|
||||
$compiler->raw($callable);
|
||||
} elseif (is_array($callable) && $callable[0] instanceof Twig_ExtensionInterface) {
|
||||
@ -92,6 +90,9 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
||||
|
||||
protected function getArguments($callable, $arguments)
|
||||
{
|
||||
$callType = $this->getAttribute('type');
|
||||
$callName = $this->getAttribute('name');
|
||||
|
||||
$parameters = array();
|
||||
$named = false;
|
||||
foreach ($arguments as $name => $node) {
|
||||
@ -99,7 +100,7 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
||||
$named = true;
|
||||
$name = $this->normalizeName($name);
|
||||
} elseif ($named) {
|
||||
throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName));
|
||||
}
|
||||
|
||||
$parameters[$name] = $node;
|
||||
@ -110,7 +111,7 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
||||
}
|
||||
|
||||
if (!$callable) {
|
||||
throw new LogicException(sprintf('Named arguments are not supported for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
throw new LogicException(sprintf('Named arguments are not supported for %s "%s".', $callType, $callName));
|
||||
}
|
||||
|
||||
// manage named arguments
|
||||
@ -119,6 +120,8 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
||||
} elseif (is_object($callable) && !$callable instanceof Closure) {
|
||||
$r = new ReflectionObject($callable);
|
||||
$r = $r->getMethod('__invoke');
|
||||
} elseif (is_string($callable) && false !== strpos($callable, '::')) {
|
||||
$r = new ReflectionMethod($callable);
|
||||
} else {
|
||||
$r = new ReflectionFunction($callable);
|
||||
}
|
||||
@ -140,32 +143,61 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
||||
}
|
||||
|
||||
$arguments = array();
|
||||
$names = array();
|
||||
$missingArguments = array();
|
||||
$optionalArguments = array();
|
||||
$pos = 0;
|
||||
foreach ($definition as $param) {
|
||||
$name = $this->normalizeName($param->name);
|
||||
$names[] = $name = $this->normalizeName($param->name);
|
||||
|
||||
if (array_key_exists($name, $parameters)) {
|
||||
if (array_key_exists($pos, $parameters)) {
|
||||
throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName));
|
||||
}
|
||||
|
||||
if (!empty($missingArguments)) {
|
||||
throw new Twig_Error_Syntax(sprintf(
|
||||
'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".',
|
||||
$name, $callType, $callName, implode(', ', $names), count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments))
|
||||
);
|
||||
}
|
||||
|
||||
$arguments = array_merge($arguments, $optionalArguments);
|
||||
$arguments[] = $parameters[$name];
|
||||
unset($parameters[$name]);
|
||||
$optionalArguments = array();
|
||||
} elseif (array_key_exists($pos, $parameters)) {
|
||||
$arguments = array_merge($arguments, $optionalArguments);
|
||||
$arguments[] = $parameters[$pos];
|
||||
unset($parameters[$pos]);
|
||||
$optionalArguments = array();
|
||||
++$pos;
|
||||
} elseif ($param->isDefaultValueAvailable()) {
|
||||
$arguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1);
|
||||
$optionalArguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1);
|
||||
} elseif ($param->isOptional()) {
|
||||
break;
|
||||
if (empty($parameters)) {
|
||||
break;
|
||||
} else {
|
||||
$missingArguments[] = $name;
|
||||
}
|
||||
} else {
|
||||
throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName));
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($parameters)) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unknown argument%s "%s" for %s "%s".', count($parameters) > 1 ? 's' : '' , implode('", "', array_keys($parameters)), $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
$unknownParameter = null;
|
||||
foreach ($parameters as $parameter) {
|
||||
if ($parameter instanceof Twig_Node) {
|
||||
$unknownParameter = $parameter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Twig_Error_Syntax(sprintf(
|
||||
'Unknown argument%s "%s" for %s "%s(%s)".',
|
||||
count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
|
||||
), $unknownParameter ? $unknownParameter->getLine() : -1);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
|
@ -24,7 +24,7 @@ class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -11,7 +11,7 @@
|
||||
*/
|
||||
class Twig_Node_Expression_GetAttr extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_Node_Expression_Array $arguments, $type, $lineno)
|
||||
public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_Node_Expression $arguments = null, $type, $lineno)
|
||||
{
|
||||
parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false), $lineno);
|
||||
}
|
||||
@ -32,20 +32,30 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression
|
||||
|
||||
$compiler->raw(', ')->subcompile($this->getNode('attribute'));
|
||||
|
||||
if (count($this->getNode('arguments')) || Twig_Template::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', ')->subcompile($this->getNode('arguments'));
|
||||
// only generate optional arguments when needed (to make generated code more readable)
|
||||
$needFourth = $this->getAttribute('ignore_strict_check');
|
||||
$needThird = $needFourth || $this->getAttribute('is_defined_test');
|
||||
$needSecond = $needThird || Twig_Template::ANY_CALL !== $this->getAttribute('type');
|
||||
$needFirst = $needSecond || null !== $this->getNode('arguments');
|
||||
|
||||
if (Twig_Template::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', ')->repr($this->getAttribute('type'));
|
||||
if ($needFirst) {
|
||||
if (null !== $this->getNode('arguments')) {
|
||||
$compiler->raw(', ')->subcompile($this->getNode('arguments'));
|
||||
} else {
|
||||
$compiler->raw(', array()');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', '.($this->getAttribute('is_defined_test') ? 'true' : 'false'));
|
||||
}
|
||||
if ($needSecond) {
|
||||
$compiler->raw(', ')->repr($this->getAttribute('type'));
|
||||
}
|
||||
|
||||
if ($this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', '.($this->getAttribute('ignore_strict_check') ? 'true' : 'false'));
|
||||
}
|
||||
if ($needThird) {
|
||||
$compiler->raw(', ')->repr($this->getAttribute('is_defined_test'));
|
||||
}
|
||||
|
||||
if ($needFourth) {
|
||||
$compiler->raw(', ')->repr($this->getAttribute('ignore_strict_check'));
|
||||
}
|
||||
|
||||
$compiler->raw(')');
|
||||
|
@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2012 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a macro call node.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*/
|
||||
class Twig_Node_Expression_MacroCall extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $template, $name, Twig_Node_Expression_Array $arguments, $lineno)
|
||||
{
|
||||
parent::__construct(array('template' => $template, 'arguments' => $arguments), array('name' => $name), $lineno);
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$namedNames = array();
|
||||
$namedCount = 0;
|
||||
$positionalCount = 0;
|
||||
foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) {
|
||||
$name = $pair['key']->getAttribute('value');
|
||||
if (!is_int($name)) {
|
||||
$namedCount++;
|
||||
$namedNames[$name] = 1;
|
||||
} elseif ($namedCount > 0) {
|
||||
throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for macro "%s".', $this->getAttribute('name')), $this->lineno);
|
||||
} else {
|
||||
$positionalCount++;
|
||||
}
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('$this->callMacro(')
|
||||
->subcompile($this->getNode('template'))
|
||||
->raw(', ')->repr($this->getAttribute('name'))
|
||||
->raw(', ')->subcompile($this->getNode('arguments'))
|
||||
;
|
||||
|
||||
if ($namedCount > 0) {
|
||||
$compiler
|
||||
->raw(', ')->repr($namedNames)
|
||||
->raw(', ')->repr($namedCount)
|
||||
->raw(', ')->repr($positionalCount)
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
@ -26,6 +26,8 @@ class Twig_Node_Expression_Name extends Twig_Node_Expression
|
||||
{
|
||||
$name = $this->getAttribute('name');
|
||||
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
if ($this->getAttribute('is_defined_test')) {
|
||||
if ($this->isSpecial()) {
|
||||
$compiler->repr(true);
|
||||
@ -44,7 +46,7 @@ class Twig_Node_Expression_Name extends Twig_Node_Expression
|
||||
// remove the non-PHP 5.4 version when PHP 5.3 support is dropped
|
||||
// as the non-optimized version is just a workaround for slow ternary operator
|
||||
// when the context has a lot of variables
|
||||
if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
|
||||
if (PHP_VERSION_ID >= 50400) {
|
||||
// PHP 5.4 ternary operator performance was optimized
|
||||
$compiler
|
||||
->raw('(isset($context[')
|
||||
|
@ -25,7 +25,7 @@ class Twig_Node_Expression_Parent extends Twig_Node_Expression
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -13,7 +13,7 @@
|
||||
* Checks if a variable is divisible by a number.
|
||||
*
|
||||
* <pre>
|
||||
* {% if loop.index is divisibleby(3) %}
|
||||
* {% if loop.index is divisible by(3) %}
|
||||
* </pre>
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
|
@ -18,12 +18,9 @@ abstract class Twig_Node_Expression_Unary extends Twig_Node_Expression
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->raw('(');
|
||||
$compiler->raw(' ');
|
||||
$this->operator($compiler);
|
||||
$compiler
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(')')
|
||||
;
|
||||
$compiler->subcompile($this->getNode('node'));
|
||||
}
|
||||
|
||||
abstract public function operator(Twig_Compiler $compiler);
|
||||
|
@ -24,7 +24,7 @@ class Twig_Node_Flush extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -33,7 +33,7 @@ class Twig_Node_For extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ class Twig_Node_ForLoop extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -25,12 +25,12 @@ class Twig_Node_If extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
for ($i = 0; $i < count($this->getNode('tests')); $i += 2) {
|
||||
for ($i = 0, $count = count($this->getNode('tests')); $i < $count; $i += 2) {
|
||||
if ($i > 0) {
|
||||
$compiler
|
||||
->outdent()
|
||||
|
@ -24,7 +24,7 @@ class Twig_Node_Import extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
@ -39,8 +39,12 @@ class Twig_Node_Import extends Twig_Node
|
||||
$compiler->raw("\$this");
|
||||
} else {
|
||||
$compiler
|
||||
->raw('$this->env->loadTemplate(')
|
||||
->raw('$this->loadTemplate(')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(', ')
|
||||
->repr($compiler->getFilename())
|
||||
->raw(', ')
|
||||
->repr($this->getLine())
|
||||
->raw(")")
|
||||
;
|
||||
}
|
||||
|
@ -19,13 +19,13 @@ class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('expr' => $expr, 'variables' => $variables), array('only' => (Boolean) $only, 'ignore_missing' => (Boolean) $ignoreMissing), $lineno, $tag);
|
||||
parent::__construct(array('expr' => $expr, 'variables' => $variables), array('only' => (bool) $only, 'ignore_missing' => (bool) $ignoreMissing), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
@ -60,40 +60,29 @@ class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface
|
||||
|
||||
protected function addGetTemplate(Twig_Compiler $compiler)
|
||||
{
|
||||
if ($this->getNode('expr') instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler
|
||||
->write("\$this->env->loadTemplate(")
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(")")
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->write("\$template = \$this->env->resolveTemplate(")
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(");\n")
|
||||
->write('$template')
|
||||
;
|
||||
}
|
||||
$compiler
|
||||
->write("\$this->loadTemplate(")
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(', ')
|
||||
->repr($compiler->getFilename())
|
||||
->raw(', ')
|
||||
->repr($this->getLine())
|
||||
->raw(")")
|
||||
;
|
||||
}
|
||||
|
||||
protected function addTemplateArguments(Twig_Compiler $compiler)
|
||||
{
|
||||
if (false === $this->getAttribute('only')) {
|
||||
if (null === $this->getNode('variables')) {
|
||||
$compiler->raw('$context');
|
||||
} else {
|
||||
$compiler
|
||||
->raw('array_merge($context, ')
|
||||
->subcompile($this->getNode('variables'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
if (null === $this->getNode('variables')) {
|
||||
$compiler->raw(false === $this->getAttribute('only') ? '$context' : 'array()');
|
||||
} elseif (false === $this->getAttribute('only')) {
|
||||
$compiler
|
||||
->raw('array_merge($context, ')
|
||||
->subcompile($this->getNode('variables'))
|
||||
->raw(')')
|
||||
;
|
||||
} else {
|
||||
if (null === $this->getNode('variables')) {
|
||||
$compiler->raw('array()');
|
||||
} else {
|
||||
$compiler->subcompile($this->getNode('variables'));
|
||||
}
|
||||
$compiler->subcompile($this->getNode('variables'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,26 +18,26 @@ class Twig_Node_Macro extends Twig_Node
|
||||
{
|
||||
public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface $arguments, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name, 'method' => 'get'.ucfirst($name)), $lineno, $tag);
|
||||
parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("public function %s(", $this->getAttribute('method')))
|
||||
->write(sprintf("public function get%s(", $this->getAttribute('name')))
|
||||
;
|
||||
|
||||
$count = count($this->getNode('arguments'));
|
||||
$pos = 0;
|
||||
foreach ($this->getNode('arguments') as $name => $default) {
|
||||
$compiler
|
||||
->raw('$_'.$name.' = ')
|
||||
->raw('$__'.$name.'__ = ')
|
||||
->subcompile($default)
|
||||
;
|
||||
|
||||
@ -64,7 +64,7 @@ class Twig_Node_Macro extends Twig_Node
|
||||
$compiler
|
||||
->write('')
|
||||
->string($name)
|
||||
->raw(' => $_'.$name)
|
||||
->raw(' => $__'.$name.'__')
|
||||
->raw(",\n")
|
||||
;
|
||||
}
|
||||
|
@ -13,6 +13,10 @@
|
||||
/**
|
||||
* Represents a module node.
|
||||
*
|
||||
* Consider this class as being final. If you need to customize the behavior of
|
||||
* the generated class, consider adding nodes to the following nodes: display_start,
|
||||
* display_end, constructor_start, constructor_end, and class_end.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Module extends Twig_Node
|
||||
@ -20,7 +24,22 @@ class Twig_Node_Module extends Twig_Node
|
||||
public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $filename)
|
||||
{
|
||||
// embedded templates are set as attributes so that they are only visited once by the visitors
|
||||
parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename, 'index' => null, 'embedded_templates' => $embeddedTemplates), 1);
|
||||
parent::__construct(array(
|
||||
'parent' => $parent,
|
||||
'body' => $body,
|
||||
'blocks' => $blocks,
|
||||
'macros' => $macros,
|
||||
'traits' => $traits,
|
||||
'display_start' => new Twig_Node(),
|
||||
'display_end' => new Twig_Node(),
|
||||
'constructor_start' => new Twig_Node(),
|
||||
'constructor_end' => new Twig_Node(),
|
||||
'class_end' => new Twig_Node(),
|
||||
), array(
|
||||
'filename' => $filename,
|
||||
'index' => null,
|
||||
'embedded_templates' => $embeddedTemplates,
|
||||
), 1);
|
||||
}
|
||||
|
||||
public function setIndex($index)
|
||||
@ -31,7 +50,7 @@ class Twig_Node_Module extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
@ -50,17 +69,20 @@ class Twig_Node_Module extends Twig_Node
|
||||
|
||||
$this->compileClassHeader($compiler);
|
||||
|
||||
if (count($this->getNode('blocks')) || count($this->getNode('traits')) || null === $this->getNode('parent') || $this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
|
||||
if (
|
||||
count($this->getNode('blocks'))
|
||||
|| count($this->getNode('traits'))
|
||||
|| null === $this->getNode('parent')
|
||||
|| $this->getNode('parent') instanceof Twig_Node_Expression_Constant
|
||||
|| count($this->getNode('constructor_start'))
|
||||
|| count($this->getNode('constructor_end'))
|
||||
) {
|
||||
$this->compileConstructor($compiler);
|
||||
}
|
||||
|
||||
$this->compileGetParent($compiler);
|
||||
|
||||
$this->compileDisplayHeader($compiler);
|
||||
|
||||
$this->compileDisplayBody($compiler);
|
||||
|
||||
$this->compileDisplayFooter($compiler);
|
||||
$this->compileDisplay($compiler);
|
||||
|
||||
$compiler->subcompile($this->getNode('blocks'));
|
||||
|
||||
@ -77,22 +99,27 @@ class Twig_Node_Module extends Twig_Node
|
||||
|
||||
protected function compileGetParent(Twig_Compiler $compiler)
|
||||
{
|
||||
if (null === $this->getNode('parent')) {
|
||||
if (null === $parent = $this->getNode('parent')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("protected function doGetParent(array \$context)\n", "{\n")
|
||||
->indent()
|
||||
->addDebugInfo($parent)
|
||||
->write("return ")
|
||||
;
|
||||
|
||||
if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler->subcompile($this->getNode('parent'));
|
||||
if ($parent instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler->subcompile($parent);
|
||||
} else {
|
||||
$compiler
|
||||
->raw("\$this->env->resolveTemplate(")
|
||||
->subcompile($this->getNode('parent'))
|
||||
->raw("\$this->loadTemplate(")
|
||||
->subcompile($parent)
|
||||
->raw(', ')
|
||||
->repr($compiler->getFilename())
|
||||
->raw(', ')
|
||||
->repr($this->getNode('parent')->getLine())
|
||||
->raw(")")
|
||||
;
|
||||
}
|
||||
@ -104,20 +131,6 @@ class Twig_Node_Module extends Twig_Node
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileDisplayBody(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
|
||||
if (null !== $this->getNode('parent')) {
|
||||
if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler->write("\$this->parent");
|
||||
} else {
|
||||
$compiler->write("\$this->getParent(\$context)");
|
||||
}
|
||||
$compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileClassHeader(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
@ -136,17 +149,23 @@ class Twig_Node_Module extends Twig_Node
|
||||
$compiler
|
||||
->write("public function __construct(Twig_Environment \$env)\n", "{\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('constructor_start'))
|
||||
->write("parent::__construct(\$env);\n\n")
|
||||
;
|
||||
|
||||
// parent
|
||||
if (null === $this->getNode('parent')) {
|
||||
if (null === $parent = $this->getNode('parent')) {
|
||||
$compiler->write("\$this->parent = false;\n\n");
|
||||
} elseif ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
|
||||
} elseif ($parent instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler
|
||||
->write("\$this->parent = \$this->env->loadTemplate(")
|
||||
->subcompile($this->getNode('parent'))
|
||||
->raw(");\n\n")
|
||||
->addDebugInfo($parent)
|
||||
->write("\$this->parent = \$this->loadTemplate(")
|
||||
->subcompile($parent)
|
||||
->raw(', ')
|
||||
->repr($compiler->getFilename())
|
||||
->raw(', ')
|
||||
->repr($this->getNode('parent')->getLine())
|
||||
->raw(");\n")
|
||||
;
|
||||
}
|
||||
|
||||
@ -170,6 +189,18 @@ class Twig_Node_Module extends Twig_Node
|
||||
|
||||
foreach ($trait->getNode('targets') as $key => $value) {
|
||||
$compiler
|
||||
->write(sprintf("if (!isset(\$_trait_%s_blocks[", $i))
|
||||
->string($key)
|
||||
->raw("])) {\n")
|
||||
->indent()
|
||||
->write("throw new Twig_Error_Runtime(sprintf('Block ")
|
||||
->string($key)
|
||||
->raw(" is not defined in trait ")
|
||||
->subcompile($trait->getNode('template'))
|
||||
->raw(".'));\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
|
||||
->write(sprintf("\$_trait_%s_blocks[", $i))
|
||||
->subcompile($value)
|
||||
->raw(sprintf("] = \$_trait_%s_blocks[", $i))
|
||||
@ -233,57 +264,36 @@ class Twig_Node_Module extends Twig_Node
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write(");\n\n")
|
||||
;
|
||||
|
||||
// macro information
|
||||
$compiler
|
||||
->write("\$this->macros = array(\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
foreach ($this->getNode('macros') as $name => $node) {
|
||||
$compiler
|
||||
->addIndentation()->repr($name)->raw(" => array(\n")
|
||||
->indent()
|
||||
->write("'method' => ")->repr($node->getAttribute('method'))->raw(",\n")
|
||||
->write("'arguments' => array(\n")
|
||||
->indent()
|
||||
;
|
||||
foreach ($node->getNode('arguments') as $argument => $value) {
|
||||
$compiler->addIndentation()->repr($argument)->raw (' => ')->subcompile($value)->raw(",\n");
|
||||
}
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("),\n")
|
||||
->outdent()
|
||||
->write("),\n")
|
||||
;
|
||||
}
|
||||
$compiler
|
||||
->outdent()
|
||||
->write(");\n")
|
||||
;
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->subcompile($this->getNode('constructor_end'))
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileDisplayHeader(Twig_Compiler $compiler)
|
||||
protected function compileDisplay(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('display_start'))
|
||||
->subcompile($this->getNode('body'))
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileDisplayFooter(Twig_Compiler $compiler)
|
||||
{
|
||||
if (null !== $parent = $this->getNode('parent')) {
|
||||
$compiler->addDebugInfo($parent);
|
||||
if ($parent instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler->write("\$this->parent");
|
||||
} else {
|
||||
$compiler->write("\$this->getParent(\$context)");
|
||||
}
|
||||
$compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
|
||||
}
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('display_end'))
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
@ -292,6 +302,7 @@ class Twig_Node_Module extends Twig_Node
|
||||
protected function compileClassFooter(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->subcompile($this->getNode('class_end'))
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
@ -382,8 +393,12 @@ class Twig_Node_Module extends Twig_Node
|
||||
{
|
||||
if ($node instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler
|
||||
->write(sprintf("%s = \$this->env->loadTemplate(", $var))
|
||||
->write(sprintf("%s = \$this->loadTemplate(", $var))
|
||||
->subcompile($node)
|
||||
->raw(', ')
|
||||
->repr($compiler->getFilename())
|
||||
->raw(', ')
|
||||
->repr($node->getLine())
|
||||
->raw(");\n")
|
||||
;
|
||||
} else {
|
||||
@ -394,7 +409,12 @@ class Twig_Node_Module extends Twig_Node
|
||||
->write(sprintf("if (!%s", $var))
|
||||
->raw(" instanceof Twig_Template) {\n")
|
||||
->indent()
|
||||
->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var))
|
||||
->write(sprintf("%s = \$this->loadTemplate(%s")
|
||||
->raw(', ')
|
||||
->repr($compiler->getFilename())
|
||||
->raw(', ')
|
||||
->repr($node->getLine())
|
||||
->raw(");\n", $var, $var))
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
|
@ -25,7 +25,7 @@ class Twig_Node_Print extends Twig_Node implements Twig_NodeOutputInterface
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ class Twig_Node_Sandbox extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a module node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_SandboxedModule extends Twig_Node_Module
|
||||
{
|
||||
protected $usedFilters;
|
||||
protected $usedTags;
|
||||
protected $usedFunctions;
|
||||
|
||||
public function __construct(Twig_Node_Module $node, array $usedFilters, array $usedTags, array $usedFunctions)
|
||||
{
|
||||
parent::__construct($node->getNode('body'), $node->getNode('parent'), $node->getNode('blocks'), $node->getNode('macros'), $node->getNode('traits'), $node->getAttribute('embedded_templates'), $node->getAttribute('filename'), $node->getLine(), $node->getNodeTag());
|
||||
|
||||
$this->setAttribute('index', $node->getAttribute('index'));
|
||||
|
||||
$this->usedFilters = $usedFilters;
|
||||
$this->usedTags = $usedTags;
|
||||
$this->usedFunctions = $usedFunctions;
|
||||
}
|
||||
|
||||
protected function compileDisplayBody(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->write("\$this->checkSecurity();\n");
|
||||
|
||||
parent::compileDisplayBody($compiler);
|
||||
}
|
||||
|
||||
protected function compileDisplayFooter(Twig_Compiler $compiler)
|
||||
{
|
||||
parent::compileDisplayFooter($compiler);
|
||||
|
||||
$compiler
|
||||
->write("protected function checkSecurity()\n", "{\n")
|
||||
->indent()
|
||||
->write("\$this->env->getExtension('sandbox')->checkSecurity(\n")
|
||||
->indent()
|
||||
->write(!$this->usedTags ? "array(),\n" : "array('".implode('\', \'', $this->usedTags)."'),\n")
|
||||
->write(!$this->usedFilters ? "array(),\n" : "array('".implode('\', \'', $this->usedFilters)."'),\n")
|
||||
->write(!$this->usedFunctions ? "array()\n" : "array('".implode('\', \'', $this->usedFunctions)."')\n")
|
||||
->outdent()
|
||||
->write(");\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ class Twig_Node_SandboxedPrint extends Twig_Node_Print
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -39,7 +39,7 @@ class Twig_Node_Set extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ class Twig_Node_Spaceless extends Twig_Node
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ class Twig_Node_Text extends Twig_Node implements Twig_NodeOutputInterface
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
|
@ -13,14 +13,15 @@
|
||||
* Represents a node in the AST.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*
|
||||
* @deprecated since 1.12 (to be removed in 3.0)
|
||||
*/
|
||||
interface Twig_NodeInterface extends Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
* @param Twig_Compiler $compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler);
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
/**
|
||||
* Twig_NodeTraverser is a node traverser.
|
||||
*
|
||||
* It visits all nodes and their children and call the given visitor for each.
|
||||
* It visits all nodes and their children and calls the given visitor for each.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
@ -24,8 +24,8 @@ class Twig_NodeTraverser
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param array $visitors An array of Twig_NodeVisitorInterface instances
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param Twig_NodeVisitorInterface[] $visitors An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function __construct(Twig_Environment $env, array $visitors = array())
|
||||
{
|
||||
@ -70,7 +70,7 @@ class Twig_NodeTraverser
|
||||
protected function traverseForVisitor(Twig_NodeVisitorInterface $visitor, Twig_NodeInterface $node = null)
|
||||
{
|
||||
if (null === $node) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
$node = $visitor->enterNode($node, $this->env);
|
||||
|
@ -28,6 +28,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
const OPTIMIZE_VAR_ACCESS = 8;
|
||||
|
||||
protected $loops = array();
|
||||
protected $loopsTargets = array();
|
||||
protected $optimizers;
|
||||
protected $prependedNodes = array();
|
||||
protected $inABody = false;
|
||||
@ -35,11 +36,11 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param integer $optimizers The optimizer mode
|
||||
* @param int $optimizers The optimizer mode
|
||||
*/
|
||||
public function __construct($optimizers = -1)
|
||||
{
|
||||
if (!is_int($optimizers) || $optimizers > 2) {
|
||||
if (!is_int($optimizers) || $optimizers > (self::OPTIMIZE_FOR | self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_VAR_ACCESS)) {
|
||||
throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers));
|
||||
}
|
||||
|
||||
@ -55,7 +56,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
$this->enterOptimizeFor($node, $env);
|
||||
}
|
||||
|
||||
if (!version_compare(phpversion(), '5.4.0RC1', '>=') && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
|
||||
if (PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
|
||||
if ($this->inABody) {
|
||||
if (!$node instanceof Twig_Node_Expression) {
|
||||
if (get_class($node) !== 'Twig_Node') {
|
||||
@ -108,7 +109,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
return $node;
|
||||
}
|
||||
|
||||
protected function optimizeVariables($node, $env)
|
||||
protected function optimizeVariables(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
|
||||
$this->prependedNodes[0][] = $node->getAttribute('name');
|
||||
@ -129,7 +130,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
* @param Twig_NodeInterface $node A Node
|
||||
* @param Twig_Environment $env The current Twig environment
|
||||
*/
|
||||
protected function optimizePrintNode($node, $env)
|
||||
protected function optimizePrintNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if (!$node instanceof Twig_Node_Print) {
|
||||
return $node;
|
||||
@ -153,7 +154,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
* @param Twig_NodeInterface $node A Node
|
||||
* @param Twig_Environment $env The current Twig environment
|
||||
*/
|
||||
protected function optimizeRawFilter($node, $env)
|
||||
protected function optimizeRawFilter(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) {
|
||||
return $node->getNode('node');
|
||||
@ -168,12 +169,14 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
* @param Twig_NodeInterface $node A Node
|
||||
* @param Twig_Environment $env The current Twig environment
|
||||
*/
|
||||
protected function enterOptimizeFor($node, $env)
|
||||
protected function enterOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_For) {
|
||||
// disable the loop variable by default
|
||||
$node->setAttribute('with_loop', false);
|
||||
array_unshift($this->loops, $node);
|
||||
array_unshift($this->loopsTargets, $node->getNode('value_target')->getAttribute('name'));
|
||||
array_unshift($this->loopsTargets, $node->getNode('key_target')->getAttribute('name'));
|
||||
} elseif (!$this->loops) {
|
||||
// we are outside a loop
|
||||
return;
|
||||
@ -183,9 +186,15 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
|
||||
// the loop variable is referenced for the current loop
|
||||
elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) {
|
||||
$node->setAttribute('always_defined', true);
|
||||
$this->addLoopToCurrent();
|
||||
}
|
||||
|
||||
// optimize access to loop targets
|
||||
elseif ($node instanceof Twig_Node_Expression_Name && in_array($node->getAttribute('name'), $this->loopsTargets)) {
|
||||
$node->setAttribute('always_defined', true);
|
||||
}
|
||||
|
||||
// block reference
|
||||
elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) {
|
||||
$this->addLoopToCurrent();
|
||||
@ -196,6 +205,16 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
$this->addLoopToAll();
|
||||
}
|
||||
|
||||
// include function without the with_context=false parameter
|
||||
elseif ($node instanceof Twig_Node_Expression_Function
|
||||
&& 'include' === $node->getAttribute('name')
|
||||
&& (!$node->getNode('arguments')->hasNode('with_context')
|
||||
|| false !== $node->getNode('arguments')->getNode('with_context')->getAttribute('value')
|
||||
)
|
||||
) {
|
||||
$this->addLoopToAll();
|
||||
}
|
||||
|
||||
// the loop variable is referenced via an attribute
|
||||
elseif ($node instanceof Twig_Node_Expression_GetAttr
|
||||
&& (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant
|
||||
@ -217,10 +236,12 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
* @param Twig_NodeInterface $node A Node
|
||||
* @param Twig_Environment $env The current Twig environment
|
||||
*/
|
||||
protected function leaveOptimizeFor($node, $env)
|
||||
protected function leaveOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_For) {
|
||||
array_shift($this->loops);
|
||||
array_shift($this->loopsTargets);
|
||||
array_shift($this->loopsTargets);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,12 +13,20 @@ class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
|
||||
public function getSafe(Twig_NodeInterface $node)
|
||||
{
|
||||
$hash = spl_object_hash($node);
|
||||
if (isset($this->data[$hash])) {
|
||||
foreach ($this->data[$hash] as $bucket) {
|
||||
if ($bucket['key'] === $node) {
|
||||
return $bucket['value'];
|
||||
}
|
||||
if (!isset($this->data[$hash])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->data[$hash] as $bucket) {
|
||||
if ($bucket['key'] !== $node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array('html_attr', $bucket['value'])) {
|
||||
$bucket['value'][] = 'html';
|
||||
}
|
||||
|
||||
return $bucket['value'];
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,8 +97,6 @@ class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
|
||||
} else {
|
||||
$this->setSafe($node, array());
|
||||
}
|
||||
} elseif ($node instanceof Twig_Node_Expression_MacroCall) {
|
||||
$this->setSafe($node, array('all'));
|
||||
} elseif ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name) {
|
||||
$name = $node->getNode('node')->getAttribute('name');
|
||||
// attributes on template instances are safe
|
||||
|
@ -40,18 +40,18 @@ class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface
|
||||
return $node;
|
||||
} elseif ($this->inAModule) {
|
||||
// look for tags
|
||||
if ($node->getNodeTag()) {
|
||||
$this->tags[] = $node->getNodeTag();
|
||||
if ($node->getNodeTag() && !isset($this->tags[$node->getNodeTag()])) {
|
||||
$this->tags[$node->getNodeTag()] = $node;
|
||||
}
|
||||
|
||||
// look for filters
|
||||
if ($node instanceof Twig_Node_Expression_Filter) {
|
||||
$this->filters[] = $node->getNode('filter')->getAttribute('value');
|
||||
if ($node instanceof Twig_Node_Expression_Filter && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) {
|
||||
$this->filters[$node->getNode('filter')->getAttribute('value')] = $node;
|
||||
}
|
||||
|
||||
// look for functions
|
||||
if ($node instanceof Twig_Node_Expression_Function) {
|
||||
$this->functions[] = $node->getAttribute('name');
|
||||
if ($node instanceof Twig_Node_Expression_Function && !isset($this->functions[$node->getAttribute('name')])) {
|
||||
$this->functions[$node->getAttribute('name')] = $node;
|
||||
}
|
||||
|
||||
// wrap print to check __toString() calls
|
||||
@ -76,7 +76,7 @@ class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface
|
||||
if ($node instanceof Twig_Node_Module) {
|
||||
$this->inAModule = false;
|
||||
|
||||
return new Twig_Node_SandboxedModule($node, array_unique($this->filters), array_unique($this->tags), array_unique($this->functions));
|
||||
$node->setNode('display_start', new Twig_Node(array(new Twig_Node_CheckSecurity($this->filters, $this->tags, $this->functions), $node->getNode('display_start'))));
|
||||
}
|
||||
|
||||
return $node;
|
||||
|
@ -41,7 +41,7 @@ interface Twig_NodeVisitorInterface
|
||||
*
|
||||
* Priority should be between -10 and 10 (0 is the default).
|
||||
*
|
||||
* @return integer The priority level
|
||||
* @return int The priority level
|
||||
*/
|
||||
public function getPriority();
|
||||
}
|
||||
|
@ -58,11 +58,7 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a token stream to a node tree.
|
||||
*
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
*
|
||||
* @return Twig_Node_Module A node tree
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false)
|
||||
{
|
||||
@ -246,7 +242,7 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
return $this->blocks[$name];
|
||||
}
|
||||
|
||||
public function setBlock($name, $value)
|
||||
public function setBlock($name, Twig_Node_Block $value)
|
||||
{
|
||||
$this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getLine());
|
||||
}
|
||||
@ -384,7 +380,7 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
}
|
||||
|
||||
foreach ($node as $k => $n) {
|
||||
if (null !== $n && null === $n = $this->filterBodyNodes($n)) {
|
||||
if (null !== $n && null === $this->filterBodyNodes($n)) {
|
||||
$node->removeNode($k);
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@
|
||||
* Interface implemented by parser classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*
|
||||
* @deprecated since 1.12 (to be removed in 3.0)
|
||||
*/
|
||||
interface Twig_ParserInterface
|
||||
{
|
||||
@ -23,6 +24,8 @@ interface Twig_ParserInterface
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
*
|
||||
* @return Twig_Node_Module A node tree
|
||||
*
|
||||
* @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
|
||||
*/
|
||||
public function parse(Twig_TokenStream $stream);
|
||||
}
|
||||
|
68
inc/lib/Twig/Profiler/Dumper/Blackfire.php
Normal file
68
inc/lib/Twig/Profiler/Dumper/Blackfire.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2015 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Profiler_Dumper_Blackfire
|
||||
{
|
||||
public function dump(Twig_Profiler_Profile $profile)
|
||||
{
|
||||
$data = array();
|
||||
$this->dumpProfile('main()', $profile, $data);
|
||||
$this->dumpChildren('main()', $profile, $data);
|
||||
|
||||
$start = microtime(true);
|
||||
$str = <<<EOF
|
||||
file-format: BlackfireProbe
|
||||
cost-dimensions: wt mu pmu
|
||||
request-start: {$start}
|
||||
|
||||
|
||||
EOF;
|
||||
|
||||
foreach ($data as $name => $values) {
|
||||
$str .= "{$name}//{$values['ct']} {$values['wt']} {$values['mu']} {$values['pmu']}\n";
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
private function dumpChildren($parent, Twig_Profiler_Profile $profile, &$data)
|
||||
{
|
||||
foreach ($profile as $p) {
|
||||
if ($p->isTemplate()) {
|
||||
$name = $p->getTemplate();
|
||||
} else {
|
||||
$name = sprintf('%s::%s(%s)', $p->getTemplate(), $p->getType(), $p->getName());
|
||||
}
|
||||
$this->dumpProfile(sprintf('%s==>%s', $parent, $name), $p, $data);
|
||||
$this->dumpChildren($name, $p, $data);
|
||||
}
|
||||
}
|
||||
|
||||
private function dumpProfile($edge, Twig_Profiler_Profile $profile, &$data)
|
||||
{
|
||||
if (isset($data[$edge])) {
|
||||
$data[$edge]['ct'] += 1;
|
||||
$data[$edge]['wt'] += floor($profile->getDuration() * 1000000);
|
||||
$data[$edge]['mu'] += $profile->getMemoryUsage();
|
||||
$data[$edge]['pmu'] += $profile->getPeakMemoryUsage();
|
||||
} else {
|
||||
$data[$edge] = array(
|
||||
'ct' => 1,
|
||||
'wt' => floor($profile->getDuration() * 1000000),
|
||||
'mu' => $profile->getMemoryUsage(),
|
||||
'pmu' => $profile->getPeakMemoryUsage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
43
inc/lib/Twig/Profiler/Dumper/Html.php
Normal file
43
inc/lib/Twig/Profiler/Dumper/Html.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2015 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Profiler_Dumper_Html extends Twig_Profiler_Dumper_Text
|
||||
{
|
||||
static private $colors = array(
|
||||
'block' => '#dfd',
|
||||
'macro' => '#ddf',
|
||||
'template' => '#ffd',
|
||||
'big' => '#d44',
|
||||
);
|
||||
|
||||
public function dump(Twig_Profiler_Profile $profile)
|
||||
{
|
||||
return '<pre>'.parent::dump($profile).'</pre>';
|
||||
}
|
||||
|
||||
protected function formatTemplate(Twig_Profiler_Profile $profile, $prefix)
|
||||
{
|
||||
return sprintf('%s└ <span style="background-color: %s">%s</span>', $prefix, self::$colors['template'], $profile->getTemplate());
|
||||
}
|
||||
|
||||
protected function formatNonTemplate(Twig_Profiler_Profile $profile, $prefix)
|
||||
{
|
||||
return sprintf('%s└ %s::%s(<span style="background-color: %s">%s</span>)', $prefix, $profile->getTemplate(), $profile->getType(), isset(self::$colors[$profile->getType()]) ? self::$colors[$profile->getType()] : 'auto', $profile->getName());
|
||||
}
|
||||
|
||||
protected function formatTime(Twig_Profiler_Profile $profile, $percent)
|
||||
{
|
||||
return sprintf('<span style="color: %s">%.2fms/%.0f%%</span>', $percent > 20 ? self::$colors['big'] : 'auto', $profile->getDuration() * 1000, $percent);
|
||||
}
|
||||
}
|
68
inc/lib/Twig/Profiler/Dumper/Text.php
Normal file
68
inc/lib/Twig/Profiler/Dumper/Text.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2015 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Profiler_Dumper_Text
|
||||
{
|
||||
private $root;
|
||||
|
||||
public function dump(Twig_Profiler_Profile $profile)
|
||||
{
|
||||
return $this->dumpProfile($profile);
|
||||
}
|
||||
|
||||
protected function formatTemplate(Twig_Profiler_Profile $profile, $prefix)
|
||||
{
|
||||
return sprintf('%s└ %s', $prefix, $profile->getTemplate());
|
||||
}
|
||||
|
||||
protected function formatNonTemplate(Twig_Profiler_Profile $profile, $prefix)
|
||||
{
|
||||
return sprintf('%s└ %s::%s(%s)', $prefix, $profile->getTemplate(), $profile->getType(), $profile->getName());
|
||||
}
|
||||
|
||||
protected function formatTime(Twig_Profiler_Profile $profile, $percent)
|
||||
{
|
||||
return sprintf('%.2fms/%.0f%%', $profile->getDuration() * 1000, $percent);
|
||||
}
|
||||
|
||||
private function dumpProfile(Twig_Profiler_Profile $profile, $prefix = '', $sibling = false)
|
||||
{
|
||||
if ($profile->isRoot()) {
|
||||
$this->root = $profile->getDuration();
|
||||
$start = $profile->getName();
|
||||
} else {
|
||||
if ($profile->isTemplate()) {
|
||||
$start = $this->formatTemplate($profile, $prefix);
|
||||
} else {
|
||||
$start = $this->formatNonTemplate($profile, $prefix);
|
||||
}
|
||||
$prefix .= $sibling ? '│ ' : ' ';
|
||||
}
|
||||
|
||||
$percent = $this->root ? $profile->getDuration() / $this->root * 100 : 0;
|
||||
|
||||
if ($profile->getDuration() * 1000 < 1) {
|
||||
$str = $start."\n";
|
||||
} else {
|
||||
$str = sprintf("%s %s\n", $start, $this->formatTime($profile, $percent));
|
||||
}
|
||||
|
||||
$nCount = count($profile->getProfiles());
|
||||
foreach ($profile as $i => $p) {
|
||||
$str .= $this->dumpProfile($p, $prefix, $i + 1 !== $nCount);
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
}
|
40
inc/lib/Twig/Profiler/Node/EnterProfile.php
Normal file
40
inc/lib/Twig/Profiler/Node/EnterProfile.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2015 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a profile enter node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Profiler_Node_EnterProfile extends Twig_Node
|
||||
{
|
||||
public function __construct($extensionName, $type, $name, $varName)
|
||||
{
|
||||
parent::__construct(array(), array('extension_name' => $extensionName, 'name' => $name, 'type' => $type, 'var_name' => $varName));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write(sprintf("\$%s = \$this->env->getExtension(", $this->getAttribute('var_name')))
|
||||
->repr($this->getAttribute('extension_name'))
|
||||
->raw(");\n")
|
||||
->write(sprintf("\$%s->enter(\$%s = new Twig_Profiler_Profile(\$this->getTemplateName(), ", $this->getAttribute('var_name'), $this->getAttribute('var_name').'_prof'))
|
||||
->repr($this->getAttribute('type'))
|
||||
->raw(", ")
|
||||
->repr($this->getAttribute('name'))
|
||||
->raw("));\n\n")
|
||||
;
|
||||
}
|
||||
}
|
34
inc/lib/Twig/Profiler/Node/LeaveProfile.php
Normal file
34
inc/lib/Twig/Profiler/Node/LeaveProfile.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2015 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a profile leave node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Profiler_Node_LeaveProfile extends Twig_Node
|
||||
{
|
||||
public function __construct($varName)
|
||||
{
|
||||
parent::__construct(array(), array('var_name' => $varName));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("\n")
|
||||
->write(sprintf("\$%s->leave(\$%s);\n\n", $this->getAttribute('var_name'), $this->getAttribute('var_name').'_prof'))
|
||||
;
|
||||
}
|
||||
}
|
72
inc/lib/Twig/Profiler/NodeVisitor/Profiler.php
Normal file
72
inc/lib/Twig/Profiler/NodeVisitor/Profiler.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2015 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Profiler_NodeVisitor_Profiler implements Twig_NodeVisitorInterface
|
||||
{
|
||||
private $extensionName;
|
||||
|
||||
public function __construct($extensionName)
|
||||
{
|
||||
$this->extensionName = $extensionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Module) {
|
||||
$varName = $this->getVarName();
|
||||
$node->setNode('display_start', new Twig_Node(array(new Twig_Profiler_Node_EnterProfile($this->extensionName, Twig_Profiler_Profile::TEMPLATE, $node->getAttribute('filename'), $varName), $node->getNode('display_start'))));
|
||||
$node->setNode('display_end', new Twig_Node(array(new Twig_Profiler_Node_LeaveProfile($varName), $node->getNode('display_end'))));
|
||||
} elseif ($node instanceof Twig_Node_Block) {
|
||||
$varName = $this->getVarName();
|
||||
$node->setNode('body', new Twig_Node_Body(array(
|
||||
new Twig_Profiler_Node_EnterProfile($this->extensionName, Twig_Profiler_Profile::BLOCK, $node->getAttribute('name'), $varName),
|
||||
$node->getNode('body'),
|
||||
new Twig_Profiler_Node_LeaveProfile($varName),
|
||||
)));
|
||||
} elseif ($node instanceof Twig_Node_Macro) {
|
||||
$varName = $this->getVarName();
|
||||
$node->setNode('body', new Twig_Node_Body(array(
|
||||
new Twig_Profiler_Node_EnterProfile($this->extensionName, Twig_Profiler_Profile::MACRO, $node->getAttribute('name'), $varName),
|
||||
$node->getNode('body'),
|
||||
new Twig_Profiler_Node_LeaveProfile($varName),
|
||||
)));
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getVarName()
|
||||
{
|
||||
return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPriority()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
150
inc/lib/Twig/Profiler/Profile.php
Normal file
150
inc/lib/Twig/Profiler/Profile.php
Normal file
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2015 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Profiler_Profile implements IteratorAggregate, Serializable
|
||||
{
|
||||
const ROOT = 'ROOT';
|
||||
const BLOCK = 'block';
|
||||
const TEMPLATE = 'template';
|
||||
const MACRO = 'macro';
|
||||
|
||||
private $template;
|
||||
private $name;
|
||||
private $type;
|
||||
private $starts = array();
|
||||
private $ends = array();
|
||||
private $profiles = array();
|
||||
|
||||
public function __construct($template = 'main', $type = Twig_Profiler_Profile::ROOT, $name = 'main')
|
||||
{
|
||||
$this->template = $template;
|
||||
$this->type = $type;
|
||||
$this->name = 0 === strpos($name, '__internal_') ? 'INTERNAL' : $name;
|
||||
$this->enter();
|
||||
}
|
||||
|
||||
public function getTemplate()
|
||||
{
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function isRoot()
|
||||
{
|
||||
return self::ROOT === $this->type;
|
||||
}
|
||||
|
||||
public function isTemplate()
|
||||
{
|
||||
return self::TEMPLATE === $this->type;
|
||||
}
|
||||
|
||||
public function isBlock()
|
||||
{
|
||||
return self::BLOCK === $this->type;
|
||||
}
|
||||
|
||||
public function isMacro()
|
||||
{
|
||||
return self::MACRO === $this->type;
|
||||
}
|
||||
|
||||
public function getProfiles()
|
||||
{
|
||||
return $this->profiles;
|
||||
}
|
||||
|
||||
public function addProfile(Twig_Profiler_Profile $profile)
|
||||
{
|
||||
$this->profiles[] = $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the duration in microseconds.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDuration()
|
||||
{
|
||||
return isset($this->ends['wt']) && isset($this->starts['wt']) ? $this->ends['wt'] - $this->starts['wt'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the memory usage in bytes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMemoryUsage()
|
||||
{
|
||||
return isset($this->ends['mu']) && isset($this->starts['mu']) ? $this->ends['mu'] - $this->starts['mu'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the peak memory usage in bytes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPeakMemoryUsage()
|
||||
{
|
||||
return isset($this->ends['pmu']) && isset($this->starts['pmu']) ? $this->ends['pmu'] - $this->starts['pmu'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the profiling.
|
||||
*/
|
||||
public function enter()
|
||||
{
|
||||
$this->starts = array(
|
||||
'wt' => microtime(true),
|
||||
'mu' => memory_get_usage(),
|
||||
'pmu' => memory_get_peak_usage(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the profiling.
|
||||
*/
|
||||
public function leave()
|
||||
{
|
||||
$this->ends = array(
|
||||
'wt' => microtime(true),
|
||||
'mu' => memory_get_usage(),
|
||||
'pmu' => memory_get_peak_usage(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->profiles);
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(array($this->template, $this->name, $this->type, $this->starts, $this->ends, $this->profiles));
|
||||
}
|
||||
|
||||
public function unserialize($data)
|
||||
{
|
||||
list($this->template, $this->name, $this->type, $this->starts, $this->ends, $this->profiles) = unserialize($data);
|
||||
}
|
||||
}
|
31
inc/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php
Normal file
31
inc/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when a not allowed filter is used in a template.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*/
|
||||
class Twig_Sandbox_SecurityNotAllowedFilterError extends Twig_Sandbox_SecurityError
|
||||
{
|
||||
private $filterName;
|
||||
|
||||
public function __construct($message, $functionName, $lineno = -1, $filename = null, Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $lineno, $filename, $previous);
|
||||
$this->filterName = $functionName;
|
||||
}
|
||||
|
||||
public function getFilterName()
|
||||
{
|
||||
return $this->filterName;
|
||||
}
|
||||
}
|
31
inc/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php
Normal file
31
inc/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when a not allowed function is used in a template.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*/
|
||||
class Twig_Sandbox_SecurityNotAllowedFunctionError extends Twig_Sandbox_SecurityError
|
||||
{
|
||||
private $functionName;
|
||||
|
||||
public function __construct($message, $functionName, $lineno = -1, $filename = null, Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $lineno, $filename, $previous);
|
||||
$this->functionName = $functionName;
|
||||
}
|
||||
|
||||
public function getFunctionName()
|
||||
{
|
||||
return $this->functionName;
|
||||
}
|
||||
}
|
31
inc/lib/Twig/Sandbox/SecurityNotAllowedTagError.php
Normal file
31
inc/lib/Twig/Sandbox/SecurityNotAllowedTagError.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when a not allowed tag is used in a template.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*/
|
||||
class Twig_Sandbox_SecurityNotAllowedTagError extends Twig_Sandbox_SecurityError
|
||||
{
|
||||
private $tagName;
|
||||
|
||||
public function __construct($message, $tagName, $lineno = -1, $filename = null, Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $lineno, $filename, $previous);
|
||||
$this->tagName = $tagName;
|
||||
}
|
||||
|
||||
public function getTagName()
|
||||
{
|
||||
return $this->tagName;
|
||||
}
|
||||
}
|
@ -63,19 +63,19 @@ class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterfac
|
||||
{
|
||||
foreach ($tags as $tag) {
|
||||
if (!in_array($tag, $this->allowedTags)) {
|
||||
throw new Twig_Sandbox_SecurityError(sprintf('Tag "%s" is not allowed.', $tag));
|
||||
throw new Twig_Sandbox_SecurityNotAllowedTagError(sprintf('Tag "%s" is not allowed.', $tag), $tag);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($filters as $filter) {
|
||||
if (!in_array($filter, $this->allowedFilters)) {
|
||||
throw new Twig_Sandbox_SecurityError(sprintf('Filter "%s" is not allowed.', $filter));
|
||||
throw new Twig_Sandbox_SecurityNotAllowedFilterError(sprintf('Filter "%s" is not allowed.', $filter), $filter);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($functions as $function) {
|
||||
if (!in_array($function, $this->allowedFunctions)) {
|
||||
throw new Twig_Sandbox_SecurityError(sprintf('Function "%s" is not allowed.', $function));
|
||||
throw new Twig_Sandbox_SecurityNotAllowedFunctionError(sprintf('Function "%s" is not allowed.', $function), $function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,10 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
protected static $cache = array();
|
||||
|
||||
protected $parent;
|
||||
protected $parents;
|
||||
protected $parents = array();
|
||||
protected $env;
|
||||
protected $blocks;
|
||||
protected $traits;
|
||||
protected $macros;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -36,7 +35,6 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
$this->env = $env;
|
||||
$this->blocks = array();
|
||||
$this->traits = array();
|
||||
$this->macros = array();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,15 +66,25 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
$parent = $this->doGetParent($context);
|
||||
if (false === $parent) {
|
||||
return false;
|
||||
} elseif ($parent instanceof Twig_Template) {
|
||||
$name = $parent->getTemplateName();
|
||||
$this->parents[$name] = $parent;
|
||||
$parent = $name;
|
||||
} elseif (!isset($this->parents[$parent])) {
|
||||
$this->parents[$parent] = $this->env->loadTemplate($parent);
|
||||
try {
|
||||
$parent = $this->doGetParent($context);
|
||||
|
||||
if (false === $parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($parent instanceof Twig_Template) {
|
||||
return $this->parents[$parent->getTemplateName()] = $parent;
|
||||
}
|
||||
|
||||
if (!isset($this->parents[$parent])) {
|
||||
$this->parents[$parent] = $this->loadTemplate($parent);
|
||||
}
|
||||
} catch (Twig_Error_Loader $e) {
|
||||
$e->setTemplateFile(null);
|
||||
$e->guess();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this->parents[$parent];
|
||||
@ -107,9 +115,9 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
$name = (string) $name;
|
||||
|
||||
if (isset($this->traits[$name])) {
|
||||
$this->traits[$name][0]->displayBlock($name, $context, $blocks);
|
||||
$this->traits[$name][0]->displayBlock($name, $context, $blocks, false);
|
||||
} elseif (false !== $parent = $this->getParent($context)) {
|
||||
$parent->displayBlock($name, $context, $blocks);
|
||||
$parent->displayBlock($name, $context, $blocks, false);
|
||||
} else {
|
||||
throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
|
||||
}
|
||||
@ -121,22 +129,36 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
* This method is for internal use only and should never be called
|
||||
* directly.
|
||||
*
|
||||
* @param string $name The block name to display
|
||||
* @param array $context The context
|
||||
* @param array $blocks The current set of blocks
|
||||
* @param string $name The block name to display
|
||||
* @param array $context The context
|
||||
* @param array $blocks The current set of blocks
|
||||
* @param bool $useBlocks Whether to use the current set of blocks
|
||||
*/
|
||||
public function displayBlock($name, array $context, array $blocks = array())
|
||||
public function displayBlock($name, array $context, array $blocks = array(), $useBlocks = true)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
if (isset($blocks[$name])) {
|
||||
$b = $blocks;
|
||||
unset($b[$name]);
|
||||
call_user_func($blocks[$name], $context, $b);
|
||||
if ($useBlocks && isset($blocks[$name])) {
|
||||
$template = $blocks[$name][0];
|
||||
$block = $blocks[$name][1];
|
||||
} elseif (isset($this->blocks[$name])) {
|
||||
call_user_func($this->blocks[$name], $context, $blocks);
|
||||
$template = $this->blocks[$name][0];
|
||||
$block = $this->blocks[$name][1];
|
||||
} else {
|
||||
$template = null;
|
||||
$block = null;
|
||||
}
|
||||
|
||||
if (null !== $template) {
|
||||
try {
|
||||
$template->$block($context, $blocks);
|
||||
} catch (Twig_Error $e) {
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getTemplateName(), $e);
|
||||
}
|
||||
} elseif (false !== $parent = $this->getParent($context)) {
|
||||
$parent->displayBlock($name, $context, array_merge($this->blocks, $blocks));
|
||||
$parent->displayBlock($name, $context, array_merge($this->blocks, $blocks), false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,16 +188,17 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
* This method is for internal use only and should never be called
|
||||
* directly.
|
||||
*
|
||||
* @param string $name The block name to render
|
||||
* @param array $context The context
|
||||
* @param array $blocks The current set of blocks
|
||||
* @param string $name The block name to render
|
||||
* @param array $context The context
|
||||
* @param array $blocks The current set of blocks
|
||||
* @param bool $useBlocks Whether to use the current set of blocks
|
||||
*
|
||||
* @return string The rendered block
|
||||
*/
|
||||
public function renderBlock($name, array $context, array $blocks = array())
|
||||
public function renderBlock($name, array $context, array $blocks = array(), $useBlocks = true)
|
||||
{
|
||||
ob_start();
|
||||
$this->displayBlock($name, $context, $blocks);
|
||||
$this->displayBlock($name, $context, $blocks, $useBlocks);
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
@ -195,7 +218,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
*
|
||||
* @param string $name The block name
|
||||
*
|
||||
* @return Boolean true if the block exists, false otherwise
|
||||
* @return bool true if the block exists, false otherwise
|
||||
*/
|
||||
public function hasBlock($name)
|
||||
{
|
||||
@ -217,6 +240,30 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
return array_keys($this->blocks);
|
||||
}
|
||||
|
||||
protected function loadTemplate($template, $templateName = null, $line = null, $index = null)
|
||||
{
|
||||
try {
|
||||
if (is_array($template)) {
|
||||
return $this->env->resolveTemplate($template);
|
||||
}
|
||||
|
||||
if ($template instanceof Twig_Template) {
|
||||
return $template;
|
||||
}
|
||||
|
||||
return $this->env->loadTemplate($template, $index);
|
||||
} catch (Twig_Error $e) {
|
||||
$e->setTemplateFile($templateName ? $templateName : $this->getTemplateName());
|
||||
if (!$line) {
|
||||
$e->guess();
|
||||
} else {
|
||||
$e->setTemplateLine($line);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all blocks.
|
||||
*
|
||||
@ -237,7 +284,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
*/
|
||||
public function display(array $context, array $blocks = array())
|
||||
{
|
||||
$this->displayWithErrorHandling($this->env->mergeGlobals($context), $blocks);
|
||||
$this->displayWithErrorHandling($this->env->mergeGlobals($context), array_merge($this->blocks, $blocks));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -278,7 +325,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, null, $e);
|
||||
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getTemplateName(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,9 +348,9 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
* access for versions of PHP before 5.4. This is not a way to override
|
||||
* the way to get a variable value.
|
||||
*
|
||||
* @param array $context The context
|
||||
* @param string $item The variable to return from the context
|
||||
* @param Boolean $ignoreStrictCheck Whether to ignore the strict variable check or not
|
||||
* @param array $context The context
|
||||
* @param string $item The variable to return from the context
|
||||
* @param bool $ignoreStrictCheck Whether to ignore the strict variable check or not
|
||||
*
|
||||
* @return The content of the context variable
|
||||
*
|
||||
@ -313,7 +360,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
{
|
||||
if (!array_key_exists($item, $context)) {
|
||||
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this->getTemplateName());
|
||||
@ -325,12 +372,12 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
/**
|
||||
* Returns the attribute value for a given array/object.
|
||||
*
|
||||
* @param mixed $object The object or array from where to get the item
|
||||
* @param mixed $item The item to get from the array or object
|
||||
* @param array $arguments An array of arguments to pass if the item is an object method
|
||||
* @param string $type The type of attribute (@see Twig_Template constants)
|
||||
* @param Boolean $isDefinedTest Whether this is only a defined check
|
||||
* @param Boolean $ignoreStrictCheck Whether to ignore the strict attribute check or not
|
||||
* @param mixed $object The object or array from where to get the item
|
||||
* @param mixed $item The item to get from the array or object
|
||||
* @param array $arguments An array of arguments to pass if the item is an object method
|
||||
* @param string $type The type of attribute (@see Twig_Template constants)
|
||||
* @param bool $isDefinedTest Whether this is only a defined check
|
||||
* @param bool $ignoreStrictCheck Whether to ignore the strict attribute check or not
|
||||
*
|
||||
* @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
|
||||
*
|
||||
@ -358,18 +405,26 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
}
|
||||
|
||||
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_object($object)) {
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $arrayItem, get_class($object)), -1, $this->getTemplateName());
|
||||
if ($object instanceof ArrayAccess) {
|
||||
$message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist', $arrayItem, get_class($object));
|
||||
} elseif (is_object($object)) {
|
||||
$message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface', $item, get_class($object));
|
||||
} elseif (is_array($object)) {
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))), -1, $this->getTemplateName());
|
||||
if (empty($object)) {
|
||||
$message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem);
|
||||
} else {
|
||||
$message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
|
||||
}
|
||||
} elseif (Twig_Template::ARRAY_CALL === $type) {
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
|
||||
$message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
|
||||
} else {
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
|
||||
$message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
|
||||
}
|
||||
|
||||
throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,14 +434,12 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
}
|
||||
|
||||
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
|
||||
}
|
||||
|
||||
$class = get_class($object);
|
||||
|
||||
// object property
|
||||
if (Twig_Template::METHOD_CALL !== $type) {
|
||||
if (isset($object->$item) || array_key_exists((string) $item, $object)) {
|
||||
@ -402,11 +455,14 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
}
|
||||
}
|
||||
|
||||
$class = get_class($object);
|
||||
|
||||
// object method
|
||||
if (!isset(self::$cache[$class]['methods'])) {
|
||||
self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
|
||||
}
|
||||
|
||||
$call = false;
|
||||
$lcItem = strtolower($item);
|
||||
if (isset(self::$cache[$class]['methods'][$lcItem])) {
|
||||
$method = (string) $item;
|
||||
@ -416,13 +472,14 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
$method = 'is'.$item;
|
||||
} elseif (isset(self::$cache[$class]['methods']['__call'])) {
|
||||
$method = (string) $item;
|
||||
$call = true;
|
||||
} else {
|
||||
if ($isDefinedTest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
|
||||
@ -436,7 +493,16 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
$this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
|
||||
}
|
||||
|
||||
$ret = call_user_func_array(array($object, $method), $arguments);
|
||||
// Some objects throw exceptions when they have __call, and the method we try
|
||||
// to call is not supported. If ignoreStrictCheck is true, we should return null.
|
||||
try {
|
||||
$ret = call_user_func_array(array($object, $method), $arguments);
|
||||
} catch (BadMethodCallException $e) {
|
||||
if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
|
||||
return;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// useful when calling a template method from a template
|
||||
// this is not supported but unfortunately heavily used in the Symfony profiler
|
||||
@ -446,72 +512,4 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls macro in a template.
|
||||
*
|
||||
* @param Twig_Template $template The template
|
||||
* @param string $macro The name of macro
|
||||
* @param array $arguments The arguments of macro
|
||||
* @param array $namedNames An array of names of arguments as keys
|
||||
* @param integer $namedCount The count of named arguments
|
||||
* @param integer $positionalCount The count of positional arguments
|
||||
*
|
||||
* @return string The content of a macro
|
||||
*
|
||||
* @throws Twig_Error_Runtime if the macro is not defined
|
||||
* @throws Twig_Error_Runtime if the argument is defined twice
|
||||
* @throws Twig_Error_Runtime if the argument is unknown
|
||||
*/
|
||||
protected function callMacro(Twig_Template $template, $macro, array $arguments, array $namedNames = array(), $namedCount = 0, $positionalCount = -1)
|
||||
{
|
||||
if (!isset($template->macros[$macro]['reflection'])) {
|
||||
if (!isset($template->macros[$macro])) {
|
||||
throw new Twig_Error_Runtime(sprintf('Macro "%s" is not defined in the template "%s".', $macro, $template->getTemplateName()));
|
||||
}
|
||||
|
||||
$template->macros[$macro]['reflection'] = new ReflectionMethod($template, $template->macros[$macro]['method']);
|
||||
}
|
||||
|
||||
if ($namedCount < 1) {
|
||||
return $template->macros[$macro]['reflection']->invokeArgs($template, $arguments);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$args = array();
|
||||
foreach ($template->macros[$macro]['arguments'] as $name => $value) {
|
||||
if (isset($namedNames[$name])) {
|
||||
if ($i < $positionalCount) {
|
||||
throw new Twig_Error_Runtime(sprintf('Argument "%s" is defined twice for macro "%s" defined in the template "%s".', $name, $macro, $template->getTemplateName()));
|
||||
}
|
||||
|
||||
$args[] = $arguments[$name];
|
||||
if (--$namedCount < 1) {
|
||||
break;
|
||||
}
|
||||
} elseif ($i < $positionalCount) {
|
||||
$args[] = $arguments[$i];
|
||||
} else {
|
||||
$args[] = $value;
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($namedCount > 0) {
|
||||
$parameters = array_keys(array_diff_key($namedNames, $template->macros[$macro]['arguments']));
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Unknown argument%s "%s" for macro "%s" defined in the template "%s".', count($parameters) > 1 ? 's' : '' , implode('", "', $parameters), $macro, $template->getTemplateName()));
|
||||
}
|
||||
|
||||
return $template->macros[$macro]['reflection']->invokeArgs($template, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only useful when testing Twig. Do not use it.
|
||||
*/
|
||||
public static function clearCache()
|
||||
{
|
||||
self::$cache = array();
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@
|
||||
* Interface implemented by all compiled templates.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*
|
||||
* @deprecated since 1.12 (to be removed in 3.0)
|
||||
*/
|
||||
interface Twig_TemplateInterface
|
||||
{
|
||||
|
@ -123,7 +123,7 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase
|
||||
|
||||
if (false !== $exception) {
|
||||
list($class, ) = explode(':', $exception);
|
||||
$this->assertThat(NULL, new PHPUnit_Framework_Constraint_Exception($class));
|
||||
$this->assertThat(null, new PHPUnit_Framework_Constraint_Exception($class));
|
||||
}
|
||||
|
||||
$expected = trim($match[3], "\n ");
|
||||
|
@ -38,13 +38,15 @@ abstract class Twig_Test_NodeTestCase extends PHPUnit_Framework_TestCase
|
||||
return new Twig_Environment();
|
||||
}
|
||||
|
||||
protected function getVariableGetter($name)
|
||||
protected function getVariableGetter($name, $line = false)
|
||||
{
|
||||
if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
|
||||
return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name);
|
||||
$line = $line > 0 ? "// line {$line}\n" : '';
|
||||
|
||||
if (PHP_VERSION_ID >= 50400) {
|
||||
return sprintf('%s(isset($context["%s"]) ? $context["%s"] : null)', $line, $name, $name);
|
||||
}
|
||||
|
||||
return sprintf('$this->getContext($context, "%s")', $name);
|
||||
return sprintf('%s$this->getContext($context, "%s")', $line, $name);
|
||||
}
|
||||
|
||||
protected function getAttributeGetter()
|
||||
|
@ -38,9 +38,9 @@ class Twig_Token
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param integer $type The type of the token
|
||||
* @param string $value The token value
|
||||
* @param integer $lineno The line position in the source
|
||||
* @param int $type The type of the token
|
||||
* @param string $value The token value
|
||||
* @param int $lineno The line position in the source
|
||||
*/
|
||||
public function __construct($type, $value, $lineno)
|
||||
{
|
||||
@ -56,7 +56,7 @@ class Twig_Token
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('%s(%s)', self::typeToString($this->type, true, $this->lineno), $this->value);
|
||||
return sprintf('%s(%s)', self::typeToString($this->type, true), $this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,10 +67,10 @@ class Twig_Token
|
||||
* * type and value (or array of possible values)
|
||||
* * just value (or array of possible values) (NAME_TYPE is used as type)
|
||||
*
|
||||
* @param array|integer $type The type to test
|
||||
* @param array|int $type The type to test
|
||||
* @param array|string|null $values The token value
|
||||
*
|
||||
* @return Boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function test($type, $values = null)
|
||||
{
|
||||
@ -89,7 +89,7 @@ class Twig_Token
|
||||
/**
|
||||
* Gets the line.
|
||||
*
|
||||
* @return integer The source line
|
||||
* @return int The source line
|
||||
*/
|
||||
public function getLine()
|
||||
{
|
||||
@ -99,7 +99,7 @@ class Twig_Token
|
||||
/**
|
||||
* Gets the token type.
|
||||
*
|
||||
* @return integer The token type
|
||||
* @return int The token type
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
@ -119,13 +119,12 @@ class Twig_Token
|
||||
/**
|
||||
* Returns the constant representation (internal) of a given type.
|
||||
*
|
||||
* @param integer $type The type as an integer
|
||||
* @param Boolean $short Whether to return a short representation or not
|
||||
* @param integer $line The code line
|
||||
* @param int $type The type as an integer
|
||||
* @param bool $short Whether to return a short representation or not
|
||||
*
|
||||
* @return string The string representation
|
||||
*/
|
||||
public static function typeToString($type, $short = false, $line = -1)
|
||||
public static function typeToString($type, $short = false)
|
||||
{
|
||||
switch ($type) {
|
||||
case self::EOF_TYPE:
|
||||
@ -177,12 +176,11 @@ class Twig_Token
|
||||
/**
|
||||
* Returns the english representation of a given type.
|
||||
*
|
||||
* @param integer $type The type as an integer
|
||||
* @param integer $line The code line
|
||||
* @param int $type The type as an integer
|
||||
*
|
||||
* @return string The string representation
|
||||
*/
|
||||
public static function typeToEnglish($type, $line = -1)
|
||||
public static function typeToEnglish($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case self::EOF_TYPE:
|
||||
|
@ -41,12 +41,10 @@ class Twig_TokenParser_Block extends Twig_TokenParser
|
||||
$this->parser->pushLocalScope();
|
||||
$this->parser->pushBlockStack($name);
|
||||
|
||||
if ($stream->test(Twig_Token::BLOCK_END_TYPE)) {
|
||||
$stream->next();
|
||||
|
||||
if ($stream->nextIf(Twig_Token::BLOCK_END_TYPE)) {
|
||||
$body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
|
||||
if ($stream->test(Twig_Token::NAME_TYPE)) {
|
||||
$value = $stream->next()->getValue();
|
||||
if ($token = $stream->nextIf(Twig_Token::NAME_TYPE)) {
|
||||
$value = $token->getValue();
|
||||
|
||||
if ($value != $name) {
|
||||
throw new Twig_Error_Syntax(sprintf("Expected endblock for block '$name' (but %s given)", $value), $stream->getCurrent()->getLine(), $stream->getFilename());
|
||||
|
@ -39,8 +39,7 @@ class Twig_TokenParser_For extends Twig_TokenParser
|
||||
$seq = $this->parser->getExpressionParser()->parseExpression();
|
||||
|
||||
$ifexpr = null;
|
||||
if ($stream->test(Twig_Token::NAME_TYPE, 'if')) {
|
||||
$stream->next();
|
||||
if ($stream->nextIf(Twig_Token::NAME_TYPE, 'if')) {
|
||||
$ifexpr = $this->parser->getExpressionParser()->parseExpression();
|
||||
}
|
||||
|
||||
|
@ -36,19 +36,15 @@ class Twig_TokenParser_From extends Twig_TokenParser
|
||||
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
|
||||
$alias = $name;
|
||||
if ($stream->test('as')) {
|
||||
$stream->next();
|
||||
|
||||
if ($stream->nextIf('as')) {
|
||||
$alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
}
|
||||
|
||||
$targets[$name] = $alias;
|
||||
|
||||
if (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
if (!$stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
|
||||
$stream->next();
|
||||
} while (true);
|
||||
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
@ -56,7 +52,7 @@ class Twig_TokenParser_From extends Twig_TokenParser
|
||||
$node = new Twig_Node_Import($macro, new Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()), $token->getLine(), $this->getTag());
|
||||
|
||||
foreach ($targets as $name => $alias) {
|
||||
$this->parser->addImportedSymbol('macro', $alias, $name, $node->getNode('var'));
|
||||
$this->parser->addImportedSymbol('function', $alias, 'get'.$name, $node->getNode('var'));
|
||||
}
|
||||
|
||||
return $node;
|
||||
|
@ -42,24 +42,19 @@ class Twig_TokenParser_Include extends Twig_TokenParser
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$ignoreMissing = false;
|
||||
if ($stream->test(Twig_Token::NAME_TYPE, 'ignore')) {
|
||||
$stream->next();
|
||||
if ($stream->nextIf(Twig_Token::NAME_TYPE, 'ignore')) {
|
||||
$stream->expect(Twig_Token::NAME_TYPE, 'missing');
|
||||
|
||||
$ignoreMissing = true;
|
||||
}
|
||||
|
||||
$variables = null;
|
||||
if ($stream->test(Twig_Token::NAME_TYPE, 'with')) {
|
||||
$stream->next();
|
||||
|
||||
if ($stream->nextIf(Twig_Token::NAME_TYPE, 'with')) {
|
||||
$variables = $this->parser->getExpressionParser()->parseExpression();
|
||||
}
|
||||
|
||||
$only = false;
|
||||
if ($stream->test(Twig_Token::NAME_TYPE, 'only')) {
|
||||
$stream->next();
|
||||
|
||||
if ($stream->nextIf(Twig_Token::NAME_TYPE, 'only')) {
|
||||
$only = true;
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,8 @@ class Twig_TokenParser_Macro extends Twig_TokenParser
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
$this->parser->pushLocalScope();
|
||||
$body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
|
||||
if ($stream->test(Twig_Token::NAME_TYPE)) {
|
||||
$value = $stream->next()->getValue();
|
||||
if ($token = $stream->nextIf(Twig_Token::NAME_TYPE)) {
|
||||
$value = $token->getValue();
|
||||
|
||||
if ($value != $name) {
|
||||
throw new Twig_Error_Syntax(sprintf("Expected endmacro for macro '$name' (but %s given)", $value), $stream->getCurrent()->getLine(), $stream->getFilename());
|
||||
|
@ -42,8 +42,7 @@ class Twig_TokenParser_Set extends Twig_TokenParser
|
||||
$names = $this->parser->getExpressionParser()->parseAssignmentExpression();
|
||||
|
||||
$capture = false;
|
||||
if ($stream->test(Twig_Token::OPERATOR_TYPE, '=')) {
|
||||
$stream->next();
|
||||
if ($stream->nextIf(Twig_Token::OPERATOR_TYPE, '=')) {
|
||||
$values = $this->parser->getExpressionParser()->parseMultitargetExpression();
|
||||
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
|
@ -42,26 +42,20 @@ class Twig_TokenParser_Use extends Twig_TokenParser
|
||||
}
|
||||
|
||||
$targets = array();
|
||||
if ($stream->test('with')) {
|
||||
$stream->next();
|
||||
|
||||
if ($stream->nextIf('with')) {
|
||||
do {
|
||||
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
|
||||
$alias = $name;
|
||||
if ($stream->test('as')) {
|
||||
$stream->next();
|
||||
|
||||
if ($stream->nextIf('as')) {
|
||||
$alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
}
|
||||
|
||||
$targets[$name] = new Twig_Node_Expression_Constant($alias, -1);
|
||||
|
||||
if (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
if (!$stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
|
||||
$stream->next();
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,8 @@ interface Twig_TokenParserInterface
|
||||
* @param Twig_Token $token A Twig_Token instance
|
||||
*
|
||||
* @return Twig_NodeInterface A Twig_NodeInterface instance
|
||||
*
|
||||
* @throws Twig_Error_Syntax
|
||||
*/
|
||||
public function parse(Twig_Token $token);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user