252 changed files with 280 additions and 28499 deletions
@ -1,5 +1,48 @@ |
|||
{ |
|||
"name": "towards-a-new-leftypol/leftypol_lainchan", |
|||
"description": "leftypol imageboard", |
|||
"type": "project", |
|||
"require": { |
|||
"phpunit/phpunit": "^5" |
|||
} |
|||
"twig/twig": "^1.44.2", |
|||
"lifo/ip": "^1.0", |
|||
"gettext/gettext": "^1.0", |
|||
"mrclay/minify": "^2.1.6", |
|||
"geoip/geoip": "^1.17" |
|||
}, |
|||
"autoload": { |
|||
"classmap": ["inc/"], |
|||
"files": [ |
|||
"inc/bootstrap.php", |
|||
"inc/display.php", |
|||
"inc/template.php", |
|||
"inc/database.php", |
|||
"inc/events.php", |
|||
"inc/api.php", |
|||
"inc/mod/auth.php", |
|||
"inc/lock.php", |
|||
"inc/queue.php", |
|||
"inc/polyfill.php", |
|||
"inc/error.php", |
|||
"inc/functions.php" |
|||
] |
|||
}, |
|||
"license": "Tinyboard + vichan", |
|||
"authors": [ |
|||
{ |
|||
"name": "tinyboard contributors", |
|||
"homepage": "https://github.com/savetheinternet/Tinyboard" |
|||
}, |
|||
{ |
|||
"name": "vichan contributors", |
|||
"homepage": "https://github.com/vichan-devel/vichan/" |
|||
}, |
|||
{ |
|||
"name": "lainchan contributors", |
|||
"homepage": "https://github.com/lainchan/lainchan/" |
|||
}, |
|||
{ |
|||
"name": "leftypol contributors", |
|||
"homepage": "https://github.com/towards-a-new-leftypol/leftypol_lainchan/" |
|||
} |
|||
] |
|||
} |
|||
|
File diff suppressed because it is too large
@ -0,0 +1,3 @@ |
|||
<?php |
|||
define('TINYBOARD', 'xD'); |
|||
require_once('vendor/autoload.php'); |
@ -1,54 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
@trigger_error('The Twig_Autoloader class is deprecated since version 1.21 and will be removed in 2.0. Use Composer instead.', E_USER_DEPRECATED); |
|||
|
|||
/** |
|||
* Autoloads Twig classes. |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
* |
|||
* @deprecated since 1.21 and will be removed in 2.0. Use Composer instead. 2.0. |
|||
*/ |
|||
class Twig_Autoloader |
|||
{ |
|||
/** |
|||
* Registers Twig_Autoloader as an SPL autoloader. |
|||
* |
|||
* @param bool $prepend whether to prepend the autoloader or not |
|||
*/ |
|||
public static function register($prepend = false) |
|||
{ |
|||
@trigger_error('Using Twig_Autoloader is deprecated since version 1.21. Use Composer instead.', E_USER_DEPRECATED); |
|||
|
|||
if (PHP_VERSION_ID < 50300) { |
|||
spl_autoload_register(array(__CLASS__, 'autoload')); |
|||
} else { |
|||
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Handles autoloading of classes. |
|||
* |
|||
* @param string $class a class name |
|||
*/ |
|||
public static function autoload($class) |
|||
{ |
|||
if (0 !== strpos($class, 'Twig')) { |
|||
return; |
|||
} |
|||
|
|||
if (is_file($file = dirname(__FILE__).'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php')) { |
|||
require $file; |
|||
} |
|||
} |
|||
} |
@ -1,54 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Twig_BaseNodeVisitor can be used to make node visitors compatible with Twig 1.x and 2.x. |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
*/ |
|||
abstract class Twig_BaseNodeVisitor implements Twig_NodeVisitorInterface |
|||
{ |
|||
final public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) |
|||
{ |
|||
if (!$node instanceof Twig_Node) { |
|||
throw new LogicException('Twig_BaseNodeVisitor only supports Twig_Node instances.'); |
|||
} |
|||
|
|||
return $this->doEnterNode($node, $env); |
|||
} |
|||
|
|||
final public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) |
|||
{ |
|||
if (!$node instanceof Twig_Node) { |
|||
throw new LogicException('Twig_BaseNodeVisitor only supports Twig_Node instances.'); |
|||
} |
|||
|
|||
return $this->doLeaveNode($node, $env); |
|||
} |
|||
|
|||
/** |
|||
* Called before child nodes are visited. |
|||
* |
|||
* @return Twig_Node The modified node |
|||
*/ |
|||
abstract protected function doEnterNode(Twig_Node $node, Twig_Environment $env); |
|||
|
|||
/** |
|||
* Called after child nodes are visited. |
|||
* |
|||
* @return Twig_Node|false The modified node or false if the node must be removed |
|||
*/ |
|||
abstract protected function doLeaveNode(Twig_Node $node, Twig_Environment $env); |
|||
} |
|||
|
|||
class_alias('Twig_BaseNodeVisitor', 'Twig\NodeVisitor\AbstractNodeVisitor', false); |
|||
class_exists('Twig_Environment'); |
|||
class_exists('Twig_Node'); |
@ -1,93 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Implements a cache on the filesystem. |
|||
* |
|||
* @author Andrew Tch <andrew@noop.lv> |
|||
*/ |
|||
class Twig_Cache_Filesystem implements Twig_CacheInterface |
|||
{ |
|||
const FORCE_BYTECODE_INVALIDATION = 1; |
|||
|
|||
private $directory; |
|||
private $options; |
|||
|
|||
/** |
|||
* @param $directory string The root cache directory |
|||
* @param $options int A set of options |
|||
*/ |
|||
public function __construct($directory, $options = 0) |
|||
{ |
|||
$this->directory = rtrim($directory, '\/').'/'; |
|||
$this->options = $options; |
|||
} |
|||
|
|||
public function generateKey($name, $className) |
|||
{ |
|||
$hash = hash('sha256', $className); |
|||
|
|||
return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php'; |
|||
} |
|||
|
|||
public function load($key) |
|||
{ |
|||
if (file_exists($key)) { |
|||
@include_once $key; |
|||
} |
|||
} |
|||
|
|||
public function write($key, $content) |
|||
{ |
|||
$dir = dirname($key); |
|||
if (!is_dir($dir)) { |
|||
if (false === @mkdir($dir, 0777, true)) { |
|||
if (PHP_VERSION_ID >= 50300) { |
|||
clearstatcache(true, $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($dir, basename($key)); |
|||
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $key)) { |
|||
@chmod($key, 0666 & ~umask()); |
|||
|
|||
if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) { |
|||
// Compile cached file into bytecode cache |
|||
if (function_exists('opcache_invalidate')) { |
|||
opcache_invalidate($key, true); |
|||
} elseif (function_exists('apc_compile_file')) { |
|||
apc_compile_file($key); |
|||
} |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
throw new RuntimeException(sprintf('Failed to write cache file "%s".', $key)); |
|||
} |
|||
|
|||
public function getTimestamp($key) |
|||
{ |
|||
if (!file_exists($key)) { |
|||
return 0; |
|||
} |
|||
|
|||
return (int) @filemtime($key); |
|||
} |
|||
} |
|||
|
|||
class_alias('Twig_Cache_Filesystem', 'Twig\Cache\FilesystemCache', false); |
@ -1,40 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Implements a no-cache strategy. |
|||
* |
|||
* @final |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
*/ |
|||
class Twig_Cache_Null implements Twig_CacheInterface |
|||
{ |
|||
public function generateKey($name, $className) |
|||
{ |
|||
return ''; |
|||
} |
|||
|
|||
public function write($key, $content) |
|||
{ |
|||
} |
|||
|
|||
public function load($key) |
|||
{ |
|||
} |
|||
|
|||
public function getTimestamp($key) |
|||
{ |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
class_alias('Twig_Cache_Null', 'Twig\Cache\NullCache', false); |
@ -1,58 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Interface implemented by cache classes. |
|||
* |
|||
* It is highly recommended to always store templates on the filesystem to |
|||
* benefit from the PHP opcode cache. This interface is mostly useful if you |
|||
* need to implement a custom strategy for storing templates on the filesystem. |
|||
* |
|||
* @author Andrew Tch <andrew@noop.lv> |
|||
*/ |
|||
interface Twig_CacheInterface |
|||
{ |
|||
/** |
|||
* Generates a cache key for the given template class name. |
|||
* |
|||
* @param string $name The template name |
|||
* @param string $className The template class name |
|||
* |
|||
* @return string |
|||
*/ |
|||
public function generateKey($name, $className); |
|||
|
|||
/** |
|||
* Writes the compiled template to cache. |
|||
* |
|||
* @param string $key The cache key |
|||
* @param string $content The template representation as a PHP class |
|||
*/ |
|||
public function write($key, $content); |
|||
|
|||
/** |
|||
* Loads a template from the cache. |
|||
* |
|||
* @param string $key The cache key |
|||
*/ |
|||
public function load($key); |
|||
|
|||
/** |
|||
* Returns the modification timestamp of a key. |
|||
* |
|||
* @param string $key The cache key |
|||
* |
|||
* @return int |
|||
*/ |
|||
public function getTimestamp($key); |
|||
} |
|||
|
|||
class_alias('Twig_CacheInterface', 'Twig\Cache\CacheInterface', false); |
@ -1,286 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* (c) Armin Ronacher |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Compiles a node to PHP code. |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
*/ |
|||
class Twig_Compiler implements Twig_CompilerInterface |
|||
{ |
|||
protected $lastLine; |
|||
protected $source; |
|||
protected $indentation; |
|||
protected $env; |
|||
protected $debugInfo = array(); |
|||
protected $sourceOffset; |
|||
protected $sourceLine; |
|||
protected $filename; |
|||
private $varNameSalt = 0; |
|||
|
|||
public function __construct(Twig_Environment $env) |
|||
{ |
|||
$this->env = $env; |
|||
} |
|||
|
|||
/** |
|||
* @deprecated since 1.25 (to be removed in 2.0) |
|||
*/ |
|||
public function getFilename() |
|||
{ |
|||
@trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED); |
|||
|
|||
return $this->filename; |
|||
} |
|||
|
|||
/** |
|||
* Returns the environment instance related to this compiler. |
|||
* |
|||
* @return Twig_Environment |
|||
*/ |
|||
public function getEnvironment() |
|||
{ |
|||
return $this->env; |
|||
} |
|||
|
|||
/** |
|||
* Gets the current PHP code after compilation. |
|||
* |
|||
* @return string The PHP code |
|||
*/ |
|||
public function getSource() |
|||
{ |
|||
return $this->source; |
|||
} |
|||
|
|||
/** |
|||
* Compiles a node. |
|||
* |
|||
* @param Twig_NodeInterface $node The node to compile |
|||
* @param int $indentation The current indentation |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function compile(Twig_NodeInterface $node, $indentation = 0) |
|||
{ |
|||
$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; |
|||
$this->indentation = $indentation; |
|||
$this->varNameSalt = 0; |
|||
|
|||
if ($node instanceof Twig_Node_Module) { |
|||
// to be removed in 2.0 |
|||
$this->filename = $node->getTemplateName(); |
|||
} |
|||
|
|||
$node->compile($this); |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
public function subcompile(Twig_NodeInterface $node, $raw = true) |
|||
{ |
|||
if (false === $raw) { |
|||
$this->source .= str_repeat(' ', $this->indentation * 4); |
|||
} |
|||
|
|||
$node->compile($this); |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Adds a raw string to the compiled code. |
|||
* |
|||
* @param string $string The string |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function raw($string) |
|||
{ |
|||
$this->source .= $string; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Writes a string to the compiled code by adding indentation. |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function write() |
|||
{ |
|||
$strings = func_get_args(); |
|||
foreach ($strings as $string) { |
|||
$this->source .= str_repeat(' ', $this->indentation * 4).$string; |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Appends an indentation to the current PHP code after compilation. |
|||
* |
|||
* @return $this |
|||
* |
|||
* @deprecated since 1.27 (to be removed in 2.0). |
|||
*/ |
|||
public function addIndentation() |
|||
{ |
|||
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use write(\'\') instead.', E_USER_DEPRECATED); |
|||
|
|||
$this->source .= str_repeat(' ', $this->indentation * 4); |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Adds a quoted string to the compiled code. |
|||
* |
|||
* @param string $value The string |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function string($value) |
|||
{ |
|||
$this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\")); |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Returns a PHP representation of a given value. |
|||
* |
|||
* @param mixed $value The value to convert |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function repr($value) |
|||
{ |
|||
if (is_int($value) || is_float($value)) { |
|||
if (false !== $locale = setlocale(LC_NUMERIC, '0')) { |
|||
setlocale(LC_NUMERIC, 'C'); |
|||
} |
|||
|
|||
$this->raw($value); |
|||
|
|||
if (false !== $locale) { |
|||
setlocale(LC_NUMERIC, $locale); |
|||
} |
|||
} elseif (null === $value) { |
|||
$this->raw('null'); |
|||
} elseif (is_bool($value)) { |
|||
$this->raw($value ? 'true' : 'false'); |
|||
} elseif (is_array($value)) { |
|||
$this->raw('array('); |
|||
$first = true; |
|||
foreach ($value as $key => $v) { |
|||
if (!$first) { |
|||
$this->raw(', '); |
|||
} |
|||
$first = false; |
|||
$this->repr($key); |
|||
$this->raw(' => '); |
|||
$this->repr($v); |
|||
} |
|||
$this->raw(')'); |
|||
} else { |
|||
$this->string($value); |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Adds debugging information. |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function addDebugInfo(Twig_NodeInterface $node) |
|||
{ |
|||
if ($node->getTemplateLine() != $this->lastLine) { |
|||
$this->write(sprintf("// line %d\n", $node->getTemplateLine())); |
|||
|
|||
// when mbstring.func_overload is set to 2 |
|||
// mb_substr_count() replaces substr_count() |
|||
// but they have different signatures! |
|||
if (((int) ini_get('mbstring.func_overload')) & 2) { |
|||
@trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED); |
|||
|
|||
// this is much slower than the "right" version |
|||
$this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n"); |
|||
} else { |
|||
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset); |
|||
} |
|||
$this->sourceOffset = strlen($this->source); |
|||
$this->debugInfo[$this->sourceLine] = $node->getTemplateLine(); |
|||
|
|||
$this->lastLine = $node->getTemplateLine(); |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
public function getDebugInfo() |
|||
{ |
|||
ksort($this->debugInfo); |
|||
|
|||
return $this->debugInfo; |
|||
} |
|||
|
|||
/** |
|||
* Indents the generated code. |
|||
* |
|||
* @param int $step The number of indentation to add |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function indent($step = 1) |
|||
{ |
|||
$this->indentation += $step; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Outdents the generated code. |
|||
* |
|||
* @param int $step The number of indentation to remove |
|||
* |
|||
* @return $this |
|||
* |
|||
* @throws LogicException When trying to outdent too much so the indentation would become negative |
|||
*/ |
|||
public function outdent($step = 1) |
|||
{ |
|||
// can't outdent by more steps than the current indentation level |
|||
if ($this->indentation < $step) { |
|||
throw new LogicException('Unable to call outdent() as the indentation would become negative.'); |
|||
} |
|||
|
|||
$this->indentation -= $step; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
public function getVarName() |
|||
{ |
|||
return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->varNameSalt++)); |
|||
} |
|||
} |
|||
|
|||
class_alias('Twig_Compiler', 'Twig\Compiler', false); |
|||
class_exists('Twig_Node'); |
@ -1,34 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Interface implemented by compiler classes. |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
* |
|||
* @deprecated since 1.12 (to be removed in 3.0) |
|||
*/ |
|||
interface Twig_CompilerInterface |
|||
{ |
|||
/** |
|||
* Compiles a node. |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function compile(Twig_NodeInterface $node); |
|||
|
|||
/** |
|||
* Gets the current PHP code after compilation. |
|||
* |
|||
* @return string The PHP code |
|||
*/ |
|||
public function getSource(); |
|||
} |
@ -1,39 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
use Psr\Container\ContainerInterface; |
|||
|
|||
/** |
|||
* Lazily loads Twig runtime implementations from a PSR-11 container. |
|||
* |
|||
* Note that the runtime services MUST use their class names as identifiers. |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
* @author Robin Chalas <robin.chalas@gmail.com> |
|||
*/ |
|||
class Twig_ContainerRuntimeLoader implements Twig_RuntimeLoaderInterface |
|||
{ |
|||
private $container; |
|||
|
|||
public function __construct(ContainerInterface $container) |
|||
{ |
|||
$this->container = $container; |
|||
} |
|||
|
|||
public function load($class) |
|||
{ |
|||
if ($this->container->has($class)) { |
|||
return $this->container->get($class); |
|||
} |
|||
} |
|||
} |
|||
|
|||
class_alias('Twig_ContainerRuntimeLoader', 'Twig\RuntimeLoader\ContainerRuntimeLoader', false); |
File diff suppressed because it is too large
@ -1,363 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Twig base exception. |
|||
* |
|||
* This exception class and its children must only be used when |
|||
* an error occurs during the loading of a template, when a syntax error |
|||
* is detected in a template, or when rendering a template. Other |
|||
* errors must use regular PHP exception classes (like when the template |
|||
* cache directory is not writable for instance). |
|||
* |
|||
* To help debugging template issues, this class tracks the original template |
|||
* name and line where the error occurred. |
|||
* |
|||
* Whenever possible, you must set these information (original template name |
|||
* and line number) yourself by passing them to the constructor. If some or all |
|||
* these information are not available from where you throw the exception, then |
|||
* this class will guess them automatically (when the line number is set to -1 |
|||
* and/or the name is set to null). As this is a costly operation, this |
|||
* can be disabled by passing false for both the name and the line number |
|||
* when creating a new instance of this class. |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
*/ |
|||
class Twig_Error extends Exception |
|||
{ |
|||
protected $lineno; |
|||
// to be renamed to name in 2.0 |
|||
protected $filename; |
|||
protected $rawMessage; |
|||
protected $previous; |
|||
|
|||
private $sourcePath; |
|||
private $sourceCode; |
|||
|
|||
/** |
|||
* Constructor. |
|||
* |
|||
* Set both the line number and the name to false to |
|||
* disable automatic guessing of the original template name |
|||
* and line number. |
|||
* |
|||
* Set the line number to -1 to enable its automatic guessing. |
|||
* Set the name to null to enable its automatic guessing. |
|||
* |
|||
* By default, automatic guessing is enabled. |
|||
* |
|||
* @param string $message The error message |
|||
* @param int $lineno The template line where the error occurred |
|||
* @param Twig_Source|string|null $source The source context where the error occurred |
|||
* @param Exception $previous The previous exception |
|||
*/ |
|||
public function __construct($message, $lineno = -1, $source = null, Exception $previous = null) |
|||
{ |
|||
if (null === $source) { |
|||
$name = null; |
|||
} elseif (!$source instanceof Twig_Source) { |
|||
// for compat with the Twig C ext., passing the template name as string is accepted |
|||
$name = $source; |
|||
} else { |
|||
$name = $source->getName(); |
|||
$this->sourceCode = $source->getCode(); |
|||
$this->sourcePath = $source->getPath(); |
|||
} |
|||
if (PHP_VERSION_ID < 50300) { |
|||
$this->previous = $previous; |
|||
parent::__construct(''); |
|||
} else { |
|||
parent::__construct('', 0, $previous); |
|||
} |
|||
|
|||
$this->lineno = $lineno; |
|||
$this->filename = $name; |
|||
|
|||
if (-1 === $lineno || null === $name || null === $this->sourcePath) { |
|||
$this->guessTemplateInfo(); |
|||
} |
|||
|
|||
$this->rawMessage = $message; |
|||
|
|||
$this->updateRepr(); |
|||
} |
|||
|
|||
/** |
|||
* Gets the raw message. |
|||
* |
|||
* @return string The raw message |
|||
*/ |
|||
public function getRawMessage() |
|||
{ |
|||
return $this->rawMessage; |
|||
} |
|||
|
|||
/** |
|||
* Gets the logical name where the error occurred. |
|||
* |
|||
* @return string The name |
|||
* |
|||
* @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead. |
|||
*/ |
|||
public function getTemplateFile() |
|||
{ |
|||
@trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); |
|||
|
|||
return $this->filename; |
|||
} |
|||
|
|||
/** |
|||
* Sets the logical name where the error occurred. |
|||
* |
|||
* @param string $name The name |
|||
* |
|||
* @deprecated since 1.27 (to be removed in 2.0). Use setSourceContext() instead. |
|||
*/ |
|||
public function setTemplateFile($name) |
|||
{ |
|||
@trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); |
|||
|
|||
$this->filename = $name; |
|||
|
|||
$this->updateRepr(); |
|||
} |
|||
|
|||
/** |
|||
* Gets the logical name where the error occurred. |
|||
* |
|||
* @return string The name |
|||
* |
|||
* @deprecated since 1.29 (to be removed in 2.0). Use getSourceContext() instead. |
|||
*/ |
|||
public function getTemplateName() |
|||
{ |
|||
@trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); |
|||
|
|||
return $this->filename; |
|||
} |
|||
|
|||
/** |
|||
* Sets the logical name where the error occurred. |
|||
* |
|||
* @param string $name The name |
|||
* |
|||
* @deprecated since 1.29 (to be removed in 2.0). Use setSourceContext() instead. |
|||
*/ |
|||
public function setTemplateName($name) |
|||
{ |
|||
@trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); |
|||
|
|||
$this->filename = $name; |
|||
$this->sourceCode = $this->sourcePath = null; |
|||
|
|||
$this->updateRepr(); |
|||
} |
|||
|
|||
/** |
|||
* Gets the template line where the error occurred. |
|||
* |
|||
* @return int The template line |
|||
*/ |
|||
public function getTemplateLine() |
|||
{ |
|||
return $this->lineno; |
|||
} |
|||
|
|||
/** |
|||
* Sets the template line where the error occurred. |
|||
* |
|||
* @param int $lineno The template line |
|||
*/ |
|||
public function setTemplateLine($lineno) |
|||
{ |
|||
$this->lineno = $lineno; |
|||
|
|||
$this->updateRepr(); |
|||
} |
|||
|
|||
/** |
|||
* Gets the source context of the Twig template where the error occurred. |
|||
* |
|||
* @return Twig_Source|null |
|||
*/ |
|||
public function getSourceContext() |
|||
{ |
|||
return $this->filename ? new Twig_Source($this->sourceCode, $this->filename, $this->sourcePath) : null; |
|||
} |
|||
|
|||
/** |
|||
* Sets the source context of the Twig template where the error occurred. |
|||
*/ |
|||
public function setSourceContext(Twig_Source $source = null) |
|||
{ |
|||
if (null === $source) { |
|||
$this->sourceCode = $this->filename = $this->sourcePath = null; |
|||
} else { |
|||
$this->sourceCode = $source->getCode(); |
|||
$this->filename = $source->getName(); |
|||
$this->sourcePath = $source->getPath(); |
|||
} |
|||
|
|||
$this->updateRepr(); |
|||
} |
|||
|
|||
public function guess() |
|||
{ |
|||
$this->guessTemplateInfo(); |
|||
$this->updateRepr(); |
|||
} |
|||
|
|||
/** |
|||
* For PHP < 5.3.0, provides access to the getPrevious() method. |
|||
* |
|||
* @param string $method The method name |
|||
* @param array $arguments The parameters to be passed to the method |
|||
* |
|||
* @return Exception The previous exception or null |
|||
* |
|||
* @throws BadMethodCallException |
|||
*/ |
|||
public function __call($method, $arguments) |
|||
{ |
|||
if ('getprevious' == strtolower($method)) { |
|||
return $this->previous; |
|||
} |
|||
|
|||
throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method)); |
|||
} |
|||
|
|||
public function appendMessage($rawMessage) |
|||
{ |
|||
$this->rawMessage .= $rawMessage; |
|||
$this->updateRepr(); |
|||
} |
|||
|
|||
/** |
|||
* @internal |
|||
*/ |
|||
protected function updateRepr() |
|||
{ |
|||
$this->message = $this->rawMessage; |
|||
|
|||
if ($this->sourcePath && $this->lineno > 0) { |
|||
$this->file = $this->sourcePath; |
|||
$this->line = $this->lineno; |
|||
|
|||
return; |
|||
} |
|||
|
|||
$dot = false; |
|||
if ('.' === substr($this->message, -1)) { |
|||
$this->message = substr($this->message, 0, -1); |
|||
$dot = true; |
|||
} |
|||
|
|||
$questionMark = false; |
|||
if ('?' === substr($this->message, -1)) { |
|||
$this->message = substr($this->message, 0, -1); |
|||
$questionMark = true; |
|||
} |
|||
|
|||
if ($this->filename) { |
|||
if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) { |
|||
$name = sprintf('"%s"', $this->filename); |
|||
} else { |
|||
$name = json_encode($this->filename); |
|||
} |
|||
$this->message .= sprintf(' in %s', $name); |
|||
} |
|||
|
|||
if ($this->lineno && $this->lineno >= 0) { |
|||
$this->message .= sprintf(' at line %d', $this->lineno); |
|||
} |
|||
|
|||
if ($dot) { |
|||
$this->message .= '.'; |
|||
} |
|||
|
|||
if ($questionMark) { |
|||
$this->message .= '?'; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @internal |
|||
*/ |
|||
protected function guessTemplateInfo() |
|||
{ |
|||
$template = null; |
|||
$templateClass = null; |
|||
|
|||
if (PHP_VERSION_ID >= 50306) { |
|||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); |
|||
} else { |
|||
$backtrace = debug_backtrace(); |
|||
} |
|||
|
|||
foreach ($backtrace as $trace) { |
|||
if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) { |
|||
$currentClass = get_class($trace['object']); |
|||
$isEmbedContainer = 0 === strpos($templateClass, $currentClass); |
|||
if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) { |
|||
$template = $trace['object']; |
|||
$templateClass = get_class($trace['object']); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// update template name |
|||
if (null !== $template && null === $this->filename) { |
|||
$this->filename = $template->getTemplateName(); |
|||
} |
|||
|
|||
// update template path if any |
|||
if (null !== $template && null === $this->sourcePath) { |
|||
$src = $template->getSourceContext(); |
|||
$this->sourceCode = $src->getCode(); |
|||
$this->sourcePath = $src->getPath(); |
|||
} |
|||
|
|||
if (null === $template || $this->lineno > -1) { |
|||
return; |
|||
} |
|||
|
|||
$r = new ReflectionObject($template); |
|||
$file = $r->getFileName(); |
|||
|
|||
$exceptions = array($e = $this); |
|||
while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) { |
|||
$exceptions[] = $e; |
|||
} |
|||
|
|||
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; |
|||
} |
|||
|
|||
foreach ($template->getDebugInfo() as $codeLine => $templateLine) { |
|||
if ($codeLine <= $trace['line']) { |
|||
// update template line |
|||
$this->lineno = $templateLine; |
|||
|
|||
return; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
class_alias('Twig_Error', 'Twig\Error\Error', false); |
|||
class_exists('Twig_Source'); |
@ -1,40 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Exception thrown when an error occurs during template loading. |
|||
* |
|||
* Automatic template information guessing is always turned off as |
|||
* if a template cannot be loaded, there is nothing to guess. |
|||
* However, when a template is loaded from another one, then, we need |
|||
* to find the current context and this is automatically done by |
|||
* Twig_Template::displayWithErrorHandling(). |
|||
* |
|||
* This strategy makes Twig_Environment::resolveTemplate() much faster. |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
*/ |
|||
class Twig_Error_Loader extends Twig_Error |
|||
{ |
|||
public function __construct($message, $lineno = -1, $source = null, Exception $previous = null) |
|||
{ |
|||
if (PHP_VERSION_ID < 50300) { |
|||
$this->previous = $previous; |
|||
Exception::__construct(''); |
|||
} else { |
|||
Exception::__construct('', 0, $previous); |
|||
} |
|||
$this->appendMessage($message); |
|||
$this->setTemplateLine(false); |
|||
} |
|||
} |
|||
|
|||
class_alias('Twig_Error_Loader', 'Twig\Error\LoaderError', false); |
@ -1,22 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* (c) Armin Ronacher |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Exception thrown when an error occurs at runtime. |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
*/ |
|||
class Twig_Error_Runtime extends Twig_Error |
|||
{ |
|||
} |
|||
|
|||
class_alias('Twig_Error_Runtime', 'Twig\Error\RuntimeError', false); |
@ -1,55 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* (c) Armin Ronacher |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Exception thrown when a syntax error occurs during lexing or parsing of a template. |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
*/ |
|||
class Twig_Error_Syntax extends Twig_Error |
|||
{ |
|||
/** |
|||
* Tweaks the error message to include suggestions. |
|||
* |
|||
* @param string $name The original name of the item that does not exist |
|||
* @param array $items An array of possible items |
|||
*/ |
|||
public function addSuggestions($name, array $items) |
|||
{ |
|||
if (!$alternatives = self::computeAlternatives($name, $items)) { |
|||
return; |
|||
} |
|||
|
|||
$this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', $alternatives))); |
|||
} |
|||
|
|||
/** |
|||
* @internal |
|||
* |
|||
* To be merged with the addSuggestions() method in 2.0. |
|||
*/ |
|||
public static function computeAlternatives($name, $items) |
|||
{ |
|||
$alternatives = array(); |
|||
foreach ($items as $item) { |
|||
$lev = levenshtein($name, $item); |
|||
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { |
|||
$alternatives[$item] = $lev; |
|||
} |
|||
} |
|||
asort($alternatives); |
|||
|
|||
return array_keys($alternatives); |
|||
} |
|||
} |
|||
|
|||
class_alias('Twig_Error_Syntax', 'Twig\Error\SyntaxError', false); |
@ -1,31 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Adds an exists() method for loaders. |
|||
* |
|||
* @author Florin Patan <florinpatan@gmail.com> |
|||
* |
|||
* @deprecated since 1.12 (to be removed in 3.0) |
|||
*/ |
|||
interface Twig_ExistsLoaderInterface |
|||
{ |
|||
/** |
|||
* Check if we have the source code of a template, given its name. |
|||
* |
|||
* @param string $name The name of the template to check if we can load |
|||
* |
|||
* @return bool If the template source code is handled by this loader or not |
|||
*/ |
|||
public function exists($name); |
|||
} |
|||
|
|||
class_alias('Twig_ExistsLoaderInterface', 'Twig\Loader\ExistsLoaderInterface', false); |
@ -1,744 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Twig. |
|||
* |
|||
* (c) Fabien Potencier |
|||
* (c) Armin Ronacher |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
/** |
|||
* Parses expressions. |
|||
* |
|||
* This parser implements a "Precedence climbing" algorithm. |
|||
* |
|||
* @see https://www.engr.mun.ca/~theo/Misc/exp_parsing.htm |
|||
* @see https://en.wikipedia.org/wiki/Operator-precedence_parser |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
* |
|||
* @internal |
|||
*/ |
|||
class Twig_ExpressionParser |
|||
{ |
|||
const OPERATOR_LEFT = 1; |
|||
const OPERATOR_RIGHT = 2; |
|||
|
|||
protected $parser; |
|||
protected $unaryOperators; |
|||
protected $binaryOperators; |
|||
|
|||
private $env; |
|||
|
|||
public function __construct(Twig_Parser $parser, $env = null) |
|||
{ |
|||
$this->parser = $parser; |
|||
|
|||
if ($env instanceof Twig_Environment) { |
|||
$this->env = $env; |
|||
$this->unaryOperators = $env->getUnaryOperators(); |
|||
$this->binaryOperators = $env->getBinaryOperators(); |
|||
} else { |
|||
@trigger_error('Passing the operators as constructor arguments to '.__METHOD__.' is deprecated since version 1.27. Pass the environment instead.', E_USER_DEPRECATED); |
|||
|
|||
$this->env = $parser->getEnvironment(); |
|||
$this->unaryOperators = func_get_arg(1); |
|||
$this->binaryOperators = func_get_arg(2); |
|||
} |
|||
} |
|||
|
|||
public function parseExpression($precedence = 0) |
|||
{ |
|||
$expr = $this->getPrimary(); |
|||
$token = $this->parser->getCurrentToken(); |
|||
while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) { |
|||
$op = $this->binaryOperators[$token->getValue()]; |
|||
$this->parser->getStream()->next(); |
|||
|
|||
if ('is not' === $token->getValue()) { |
|||
$expr = $this->parseNotTestExpression($expr); |
|||
} elseif ('is' === $token->getValue()) { |
|||
$expr = $this->parseTestExpression($expr); |
|||
} elseif (isset($op['callable'])) { |
|||
$expr = call_user_func($op['callable'], $this->parser, $expr); |
|||
} else { |
|||
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); |
|||
$class = $op['class']; |
|||
$expr = new $class($expr, $expr1, $token->getLine()); |
|||
} |
|||
|
|||
$token = $this->parser->getCurrentToken(); |
|||
} |
|||
|
|||
if (0 === $precedence) { |
|||
return $this->parseConditionalExpression($expr); |
|||
} |
|||
|
|||
return $expr; |
|||
} |
|||
|
|||
protected function getPrimary() |
|||
{ |
|||
$token = $this->parser->getCurrentToken(); |
|||
|
|||
if ($this->isUnary($token)) { |
|||
$operator = $this->unaryOperators[$token->getValue()]; |
|||
$this->parser->getStream()->next(); |
|||
$expr = $this->parseExpression($operator['precedence']); |
|||
$class = $operator['class']; |
|||
|
|||
return $this->parsePostfixExpression(new $class($expr, $token->getLine())); |
|||
} elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '(')) { |
|||
$this->parser->getStream()->next(); |
|||
$expr = $this->parseExpression(); |
|||
$this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed'); |
|||
|
|||
return $this->parsePostfixExpression($expr); |
|||
} |
|||
|
|||
return $this->parsePrimaryExpression(); |
|||
} |
|||
|
|||
protected function parseConditionalExpression($expr) |
|||
{ |
|||
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()->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) { |
|||
$expr3 = $this->parseExpression(); |
|||
} else { |
|||
$expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine()); |
|||
} |
|||
} else { |
|||
$expr2 = $expr; |
|||
$expr3 = $this->parseExpression(); |
|||
} |
|||
|
|||
$expr = new Twig_Node_Expression_Conditional($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine()); |
|||
} |
|||
|
|||
return $expr; |
|||
} |
|||
|
|||
protected function isUnary(Twig_Token $token) |
|||
{ |
|||
return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]); |
|||
} |
|||
|
|||
protected function isBinary(Twig_Token $token) |
|||
{ |
|||
return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]); |
|||
} |
|||
|
|||
public function parsePrimaryExpression() |
|||
{ |
|||
$token = $this->parser->getCurrentToken(); |
|||
switch ($token->getType()) { |
|||
case Twig_Token::NAME_TYPE: |
|||
$this->parser->getStream()->next(); |
|||
switch ($token->getValue()) { |
|||
case 'true': |
|||
case 'TRUE': |
|||
$node = new Twig_Node_Expression_Constant(true, $token->getLine()); |
|||
break; |
|||
|
|||
case 'false': |
|||
case 'FALSE': |
|||
$node = new Twig_Node_Expression_Constant(false, $token->getLine()); |
|||
break; |
|||
|
|||
case 'none': |
|||
case 'NONE': |
|||
case 'null': |
|||
case 'NULL': |
|||
$node = new Twig_Node_Expression_Constant(null, $token->getLine()); |
|||
break; |
|||
|
|||
default: |
|||
if ('(' === $this->parser->getCurrentToken()->getValue()) { |
|||
$node = $this->getFunctionNode($token->getValue(), $token->getLine()); |
|||
} else { |
|||
$node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine()); |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case Twig_Token::NUMBER_TYPE: |
|||
$this->parser->getStream()->next(); |
|||
$node = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); |
|||
break; |
|||
|
|||
case Twig_Token::STRING_TYPE: |
|||
case Twig_Token::INTERPOLATION_START_TYPE: |
|||
$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->getStream()->getSourceContext()); |
|||
} |
|||
|
|||
$this->parser->getStream()->next(); |
|||
$expr = $this->parsePrimaryExpression(); |
|||
|
|||
$node = new $class($expr, $token->getLine()); |
|||
break; |
|||
} |
|||
|
|||
// no break |
|||
default: |
|||
if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) { |
|||
$node = $this->parseArrayExpression(); |
|||
} elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) { |
|||
$node = $this->parseHashExpression(); |
|||
} elseif ($token->test(Twig_Token::OPERATOR_TYPE, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) { |
|||
throw new Twig_Error_Syntax(sprintf('Unexpected operator of value "%s". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead.', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); |
|||
} else { |
|||
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); |
|||
} |
|||
} |
|||
|
|||
return $this->parsePostfixExpression($node); |
|||
} |
|||
|
|||
public function parseStringExpression() |
|||
{ |
|||
$stream = $this->parser->getStream(); |
|||
|
|||
$nodes = array(); |
|||
// a string cannot be followed by another string in a single expression |
|||
$nextCanBeString = true; |
|||
while (true) { |
|||
if ($nextCanBeString && $token = $stream->nextIf(Twig_Token::STRING_TYPE)) { |
|||
$nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); |
|||
$nextCanBeString = false; |
|||
} elseif ($stream->nextIf(Twig_Token::INTERPOLATION_START_TYPE)) { |
|||
$nodes[] = $this->parseExpression(); |
|||
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE); |
|||
$nextCanBeString = true; |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
|
|||
$expr = array_shift($nodes); |
|||
foreach ($nodes as $node) { |
|||
$expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getTemplateLine()); |
|||
} |
|||
|
|||
return $expr; |
|||
} |
|||
|
|||
public function parseArrayExpression() |
|||
{ |
|||
$stream = $this->parser->getStream(); |
|||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '[', 'An array element was expected'); |
|||
|
|||
$node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine()); |
|||
$first = true; |
|||
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { |
|||
if (!$first) { |
|||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma'); |
|||
|
|||
// trailing ,? |
|||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { |
|||
break; |
|||
} |
|||
} |
|||
$first = false; |
|||
|
|||
$node->addElement($this->parseExpression()); |
|||
} |
|||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ']', 'An opened array is not |