1
0
mirror of https://github.com/vichan-devel/vichan.git synced 2024-11-28 09:20:58 +01:00
This commit is contained in:
8chan 2015-03-10 23:21:31 +00:00
commit d779b1599e
141 changed files with 3450 additions and 1522 deletions

View File

@ -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 &infin;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 &infin;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
View File

@ -103,14 +103,14 @@ $body = <<<EOT
<li>~~strikethrough~~ -&gt; <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>

View File

@ -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;
}
}
}

View File

@ -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';

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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
{

View File

@ -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))) {

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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()
{

View File

@ -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);
}

View File

@ -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;
}

View 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';
}
}

View File

@ -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');
}

View File

@ -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);
}

View File

@ -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();

View 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
View 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.

View File

@ -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';

View File

@ -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);
}

View File

@ -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

View File

@ -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")) {

View File

@ -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

View File

@ -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
*/

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View 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")
;
}
}

View File

@ -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)
{

View File

@ -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(")")
;

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View 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('');
}
}

View File

@ -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)
{

View File

@ -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)
{

View 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('');
}
}

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View 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('');
}
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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)
{

View File

@ -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(')');

View File

@ -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(')')
;
}
}

View File

@ -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[')

View File

@ -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)
{

View File

@ -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>

View File

@ -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);

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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()

View File

@ -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(")")
;
}

View File

@ -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'));
}
}
}

View File

@ -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")
;
}

View File

@ -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")
;

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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")
;
}
}

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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);
}

View 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(),
);
}
}
}

View 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);
}
}

View 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;
}
}

View 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")
;
}
}

View 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'))
;
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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
{

View File

@ -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 ");

View File

@ -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()

View File

@ -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:

View File

@ -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());

View File

@ -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();
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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());

View File

@ -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);

View File

@ -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);
}

View File

@ -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