Update to Drupal 8.0.0-beta15. For more information, see: https://www.drupal.org/node/2563023

This commit is contained in:
Pantheon Automation 2015-09-04 13:20:09 -07:00 committed by Greg Anderson
parent 2720a9ec4b
commit f3791f1da3
1898 changed files with 54300 additions and 11481 deletions

View file

@ -351,7 +351,7 @@ class ClassLoader
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
@ -361,7 +361,7 @@ class ClassLoader
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
@ -380,7 +380,7 @@ class ClassLoader
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
@ -390,7 +390,7 @@ class ClassLoader
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}

21
core/vendor/composer/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
Copyright (c) 2015 Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -6,8 +6,8 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
$vendorDir . '/guzzlehttp/psr7/src/functions.php',
$vendorDir . '/guzzlehttp/promises/src/functions.php',
$vendorDir . '/guzzlehttp/promises/src/functions_include.php',
$vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
$vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
$baseDir . '/lib/Drupal.php',
);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
phpunit.xml
composer.phar
composer.lock
composer-test.lock
vendor/
build/artifacts/
artifacts/
docs/_build
docs/*.pyc
.idea
.DS_STORE

View file

@ -1,5 +1,9 @@
# CHANGELOG
## 1.0.2 - 2015-05-15
* Conditionally require functions.php.
## 1.0.1 - 2015-06-24
* Updating EachPromise to call next on the underlying promise iterator as late

View file

@ -21,7 +21,7 @@
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
},
"files": ["src/functions.php"]
"files": ["src/functions_include.php"]
},
"extra": {
"branch-alias": {

View file

@ -1,11 +1,6 @@
<?php
namespace GuzzleHttp\Promise;
// Don't redefine the functions if included multiple times.
if (function_exists('GuzzleHttp\Promise\promise_for')) {
return;
}
/**
* Get the global task queue used for promise resolution.
*

View file

@ -0,0 +1,6 @@
<?php
// Don't redefine the functions if included multiple times.
if (!function_exists('GuzzleHttp\Promise\promise_for')) {
require __DIR__ . '/functions.php';
}

11
core/vendor/guzzlehttp/psr7/.gitignore vendored Normal file
View file

@ -0,0 +1,11 @@
phpunit.xml
composer.phar
composer.lock
composer-test.lock
vendor/
build/artifacts/
artifacts/
docs/_build
docs/*.pyc
.idea
.DS_STORE

View file

@ -1,5 +1,14 @@
# CHANGELOG
## 1.2.0 - 2015-08-15
* Body as `"0"` is now properly added to a response.
* Now allowing forward seeking in CachingStream.
* Now properly parsing HTTP requests that contain proxy targets in
`parse_request`.
* functions.php is now conditionally required.
* user-info is no longer dropped when resolving URIs.
## 1.1.0 - 2015-06-24
* URIs can now be relative.

View file

@ -25,7 +25,7 @@
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": ["src/functions.php"]
"files": ["src/functions_include.php"]
},
"extra": {
"branch-alias": {

View file

@ -41,30 +41,33 @@ class CachingStream implements StreamInterface
$this->seek(0);
}
/**
* {@inheritdoc}
* @throws \RuntimeException When seeking with SEEK_END or when seeking
* past the total size of the buffer stream
*/
public function seek($offset, $whence = SEEK_SET)
{
if ($whence == SEEK_SET) {
$byte = $offset;
} elseif ($whence == SEEK_CUR) {
$byte = $offset + $this->tell();
} elseif ($whence == SEEK_END) {
$size = $this->remoteStream->getSize();
if ($size === null) {
$size = $this->cacheEntireStream();
}
// Because 0 is the first byte, we seek to size - 1.
$byte = $size - 1 - $offset;
} else {
throw new \RuntimeException('CachingStream::seek() supports SEEK_SET and SEEK_CUR');
throw new \InvalidArgumentException('Invalid whence');
}
// You cannot skip ahead past where you've read from the remote stream
if ($byte > $this->stream->getSize()) {
throw new \RuntimeException(
sprintf('Cannot seek to byte %d when the buffered stream only'
. ' contains %d bytes', $byte, $this->stream->getSize())
);
}
$diff = $byte - $this->stream->getSize();
$this->stream->seek($byte);
if ($diff > 0) {
// If the seek byte is greater the number of read bytes, then read
// the difference of bytes to cache the bytes and inherently seek.
$this->read($diff);
} else {
// We can just do a normal seek since we've already seen this byte.
$this->stream->seek($byte);
}
}
public function read($length)
@ -122,4 +125,12 @@ class CachingStream implements StreamInterface
{
$this->remoteStream->close() && $this->stream->close();
}
private function cacheEntireStream()
{
$target = new FnStream(['write' => 'strlen']);
copy_to_stream($this, $target);
return $this->tell();
}
}

View file

@ -93,7 +93,7 @@ class Response implements ResponseInterface
) {
$this->statusCode = (int) $status;
if ($body) {
if ($body !== null) {
$this->stream = stream_for($body);
}

View file

@ -123,44 +123,33 @@ class Uri implements UriInterface
return $base;
}
if ($rel instanceof UriInterface) {
$relParts = [
'scheme' => $rel->getScheme(),
'host' => $rel->getHost(),
'port' => $rel->getPort(),
'path' => $rel->getPath(),
'query' => $rel->getQuery(),
'fragment' => $rel->getFragment()
];
} else {
$relParts = parse_url($rel) + [
'scheme' => '',
'host' => '',
'port' => '',
'path' => '',
'query' => '',
'fragment' => ''
];
if (!($rel instanceof UriInterface)) {
$rel = new self($rel);
}
if (!empty($relParts['scheme']) && !empty($relParts['host'])) {
return $rel instanceof UriInterface
? $rel
: self::fromParts($relParts);
// Return the relative uri as-is if it has a scheme.
if ($rel->getScheme()) {
return $rel->withPath(static::removeDotSegments($rel->getPath()));
}
$parts = [
'scheme' => $base->getScheme(),
'host' => $base->getHost(),
'port' => $base->getPort(),
'path' => $base->getPath(),
'query' => $base->getQuery(),
'fragment' => $base->getFragment()
$relParts = [
'scheme' => $rel->getScheme(),
'authority' => $rel->getAuthority(),
'path' => $rel->getPath(),
'query' => $rel->getQuery(),
'fragment' => $rel->getFragment()
];
if (!empty($relParts['host'])) {
$parts['host'] = $relParts['host'];
$parts['port'] = $relParts['port'];
$parts = [
'scheme' => $base->getScheme(),
'authority' => $base->getAuthority(),
'path' => $base->getPath(),
'query' => $base->getQuery(),
'fragment' => $base->getFragment()
];
if (!empty($relParts['authority'])) {
$parts['authority'] = $relParts['authority'];
$parts['path'] = self::removeDotSegments($relParts['path']);
$parts['query'] = $relParts['query'];
$parts['fragment'] = $relParts['fragment'];
@ -170,7 +159,7 @@ class Uri implements UriInterface
$parts['query'] = $relParts['query'];
$parts['fragment'] = $relParts['fragment'];
} else {
if (!empty($parts['host']) && empty($parts['path'])) {
if (!empty($parts['authority']) && empty($parts['path'])) {
$mergedPath = '/';
} else {
$mergedPath = substr($parts['path'], 0, strrpos($parts['path'], '/') + 1);
@ -185,7 +174,13 @@ class Uri implements UriInterface
$parts['fragment'] = $relParts['fragment'];
}
return static::fromParts($parts);
return new self(static::createUriString(
$parts['scheme'],
$parts['authority'],
$parts['path'],
$parts['query'],
$parts['fragment']
));
}
/**

View file

@ -440,19 +440,22 @@ function readline(StreamInterface $stream, $maxLength = null)
function parse_request($message)
{
$data = _parse_message($message);
if (!preg_match('/^[a-zA-Z]+\s+\/.*/', $data['start-line'])) {
$matches = [];
if (!preg_match('/^[a-zA-Z]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
throw new \InvalidArgumentException('Invalid request string');
}
$parts = explode(' ', $data['start-line'], 3);
$version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
return new Request(
$request = new Request(
$parts[0],
_parse_request_uri($parts[1], $data['headers']),
$matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1],
$data['headers'],
$data['body'],
$version
);
return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
}
/**

View file

@ -0,0 +1,6 @@
<?php
// Don't redefine the functions if included multiple times.
if (!function_exists('GuzzleHttp\Psr7\str')) {
require __DIR__ . '/functions.php';
}

View file

@ -32,22 +32,43 @@ class CachingStreamTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(4, $caching->getSize());
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Cannot seek to byte 10
*/
public function testCannotSeekPastWhatHasBeenRead()
public function testReadsUntilCachedToByte()
{
$this->body->seek(10);
$this->body->seek(5);
$this->assertEquals('n', $this->body->read(1));
$this->body->seek(0);
$this->assertEquals('t', $this->body->read(1));
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage CachingStream::seek() supports SEEK_SET and SEEK_CUR
*/
public function testCannotUseSeekEnd()
public function testCanSeekNearEndWithSeekEnd()
{
$this->body->seek(2, SEEK_END);
$baseStream = Psr7\stream_for(implode('', range('a', 'z')));
$cached = new CachingStream($baseStream);
$cached->seek(1, SEEK_END);
$this->assertEquals(24, $baseStream->tell());
$this->assertEquals('y', $cached->read(1));
$this->assertEquals(26, $cached->getSize());
}
public function testCanSeekToEndWithSeekEnd()
{
$baseStream = Psr7\stream_for(implode('', range('a', 'z')));
$cached = new CachingStream($baseStream);
$cached->seek(0, SEEK_END);
$this->assertEquals(25, $baseStream->tell());
$this->assertEquals('z', $cached->read(1));
$this->assertEquals(26, $cached->getSize());
}
public function testCanUseSeekEndWithUnknownSize()
{
$baseStream = Psr7\stream_for('testing');
$decorated = Psr7\FnStream::decorate($baseStream, [
'getSize' => function () { return null; }
]);
$cached = new CachingStream($decorated);
$cached->seek(1, SEEK_END);
$this->assertEquals('ng', $cached->read(2));
}
public function testRewindUsesSeek()
@ -134,4 +155,12 @@ class CachingStreamTest extends \PHPUnit_Framework_TestCase
$d->close();
$this->assertFalse(is_resource($s));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresValidWhence()
{
$this->body->seek(10, -123456);
}
}

View file

@ -270,6 +270,18 @@ class FunctionsTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('http://foo.com/', (string) $request->getUri());
}
public function testParsesRequestMessagesWithFullUri()
{
$req = "GET https://www.google.com:443/search?q=foobar HTTP/1.1\r\nHost: www.google.com\r\n\r\n";
$request = Psr7\parse_request($req);
$this->assertEquals('GET', $request->getMethod());
$this->assertEquals('https://www.google.com:443/search?q=foobar', $request->getRequestTarget());
$this->assertEquals('1.1', $request->getProtocolVersion());
$this->assertEquals('www.google.com', $request->getHeaderLine('Host'));
$this->assertEquals('', (string) $request->getBody());
$this->assertEquals('https://www.google.com/search?q=foobar', (string) $request->getUri());
}
/**
* @expectedException \InvalidArgumentException
*/

View file

@ -136,4 +136,11 @@ class ResponseTest extends \PHPUnit_Framework_TestCase
$this->assertNotSame($r, $r2);
$this->assertEquals('Bam', $r2->getHeaderLine('Foo'));
}
public function testBodyConsistent()
{
$r = new Response(200, [], '0');
$this->assertEquals('0', (string)$r->getBody());
}
}

View file

@ -152,6 +152,8 @@ class UriTest extends \PHPUnit_Framework_TestCase
[self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'],
[self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'],
[self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'],
['http://u@a/b/c/d;p?q', '.', 'http://u@a/b/c/'],
['http://u:p@a/b/c/d;p?q', '.', 'http://u:p@a/b/c/'],
//[self::RFC3986_BASE, 'http:g', 'http:g'],
];
}

View file

@ -215,8 +215,8 @@ class ArgvInput extends Input
$option = $this->definition->getOption($name);
// Convert false values (from a previous call to substr()) to null
if (false === $value) {
// Convert empty values to null
if (!isset($value[0])) {
$value = null;
}

View file

@ -46,12 +46,9 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
{
$outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output';
$errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output';
parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter);
parent::__construct(fopen($outputStream, 'w'), $verbosity, $decorated, $formatter);
$this->stderr = new StreamOutput(fopen($errorStream, 'w'), $verbosity, $decorated, $this->getFormatter());
$this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter());
}
/**
@ -129,4 +126,24 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
{
return 'OS400' === php_uname('s');
}
/**
* @return resource
*/
private function openOutputStream()
{
$outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output';
return @fopen($outputStream, 'w') ?: fopen('php://output', 'w');
}
/**
* @return resource
*/
private function openErrorStream()
{
$errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output';
return fopen($errorStream, 'w');
}
}

View file

@ -162,7 +162,7 @@ class ChoiceQuestion extends Question
throw new \InvalidArgumentException(sprintf($errorMessage, $value));
}
$multiselectChoices[] = $choices[(string) $result];
$multiselectChoices[] = (string) $result;
}
if ($multiselect) {

View file

@ -379,7 +379,7 @@ class SymfonyStyle extends OutputStyle
{
$chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2);
if (false === $chars) {
if (!isset($chars[0])) {
return $this->newLine(); //empty history, so we should start with a new line.
}
//Prepend new line for each non LF chars (This means no blank line was output before)

View file

@ -36,15 +36,18 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase
$questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2');
$question->setMaxAttempts(1);
// first answer is an empty answer, we're supposed to receive the default value
$this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setMaxAttempts(1);
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setErrorMessage('Input "%s" is not a superhero!');
$question->setMaxAttempts(2);
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
rewind($output->getStream());
@ -61,6 +64,7 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase
}
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null);
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
@ -68,11 +72,13 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1');
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 ');
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
@ -227,6 +233,7 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
$question->setMaxAttempts(1);
$answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
$this->assertSame($expectedValue, $answer);

View file

@ -49,7 +49,7 @@ class XmlFileLoader extends FileLoader
$this->parseImports($xml, $path);
// parameters
$this->parseParameters($xml, $path);
$this->parseParameters($xml);
// extensions
$this->loadFromExtensions($xml);
@ -70,9 +70,8 @@ class XmlFileLoader extends FileLoader
* Parses parameters.
*
* @param \DOMDocument $xml
* @param string $file
*/
private function parseParameters(\DOMDocument $xml, $file)
private function parseParameters(\DOMDocument $xml)
{
if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) {
$this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter'));

View file

@ -11,6 +11,7 @@
namespace Symfony\Component\DependencyInjection\ParameterBag;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
/**
@ -25,6 +26,8 @@ interface ParameterBagInterface
/**
* Clears all parameters.
*
* @throws LogicException if the ParameterBagInterface can not be cleared
*
* @api
*/
public function clear();
@ -34,6 +37,8 @@ interface ParameterBagInterface
*
* @param array $parameters An array of parameters
*
* @throws LogicException if the parameter can not be added
*
* @api
*/
public function add(array $parameters);
@ -66,6 +71,8 @@ interface ParameterBagInterface
* @param string $name The parameter name
* @param mixed $value The parameter value
*
* @throws LogicException if the parameter can not be set
*
* @api
*/
public function set($name, $value);

View file

@ -57,4 +57,13 @@ class FrozenParameterBagTest extends \PHPUnit_Framework_TestCase
$bag = new FrozenParameterBag(array());
$bag->add(array());
}
/**
* @expectedException \LogicException
*/
public function testRemove()
{
$bag = new FrozenParameterBag(array('foo' => 'bar'));
$bag->remove('foo');
}
}

View file

@ -224,13 +224,13 @@ class Request
/**
* Constructor.
*
* @param array $query The GET parameters
* @param array $request The POST parameters
* @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
* @param array $cookies The COOKIE parameters
* @param array $files The FILES parameters
* @param array $server The SERVER parameters
* @param string $content The raw body data
* @param array $query The GET parameters
* @param array $request The POST parameters
* @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
* @param array $cookies The COOKIE parameters
* @param array $files The FILES parameters
* @param array $server The SERVER parameters
* @param string|resource $content The raw body data
*
* @api
*/
@ -244,13 +244,13 @@ class Request
*
* This method also re-initializes all properties.
*
* @param array $query The GET parameters
* @param array $request The POST parameters
* @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
* @param array $cookies The COOKIE parameters
* @param array $files The FILES parameters
* @param array $server The SERVER parameters
* @param string $content The raw body data
* @param array $query The GET parameters
* @param array $request The POST parameters
* @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
* @param array $cookies The COOKIE parameters
* @param array $files The FILES parameters
* @param array $server The SERVER parameters
* @param string|resource $content The raw body data
*
* @api
*/
@ -1563,16 +1563,38 @@ class Request
*/
public function getContent($asResource = false)
{
if (PHP_VERSION_ID < 50600 && (false === $this->content || (true === $asResource && null !== $this->content))) {
$currentContentIsResource = is_resource($this->content);
if (PHP_VERSION_ID < 50600 && false === $this->content) {
throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.');
}
if (true === $asResource) {
if ($currentContentIsResource) {
rewind($this->content);
return $this->content;
}
// Content passed in parameter (test)
if (is_string($this->content)) {
$resource = fopen('php://temp','r+');
fwrite($resource, $this->content);
rewind($resource);
return $resource;
}
$this->content = false;
return fopen('php://input', 'rb');
}
if ($currentContentIsResource) {
rewind($this->content);
return stream_get_contents($this->content);
}
if (null === $this->content) {
$this->content = file_get_contents('php://input');
}
@ -1902,7 +1924,8 @@ class Request
$requestUri = substr($requestUri, 0, $pos);
}
if (null !== $baseUrl && false === $pathInfo = substr($requestUri, strlen($baseUrl))) {
$pathInfo = substr($requestUri, strlen($baseUrl));
if (null !== $baseUrl && (false === $pathInfo || '' === $pathInfo)) {
// If substr() returns false then PATH_INFO is set to an empty string
return '/';
} elseif (null === $baseUrl) {

View file

@ -1242,7 +1242,7 @@ class Response
{
$status = ob_get_status(true);
$level = count($status);
$flags = PHP_VERSION_ID >= 50400 ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1;
$flags = defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1;
while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || $flags === ($s['flags'] & $flags) : $s['del'])) {
if ($flush) {

View file

@ -969,6 +969,26 @@ class RequestTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(feof($retval));
}
public function testGetContentReturnsResourceWhenContentSetInConstructor()
{
$req = new Request(array(), array(), array(), array(), array(), array(), 'MyContent');
$resource = $req->getContent(true);
$this->assertTrue(is_resource($resource));
$this->assertEquals('MyContent', stream_get_contents($resource));
}
public function testContentAsResource()
{
$resource = fopen('php://memory','r+');
fwrite($resource, 'My other content');
rewind($resource);
$req = new Request(array(), array(), array(), array(), array(), array(), $resource);
$this->assertEquals('My other content', stream_get_contents($req->getContent(true)));
$this->assertEquals('My other content', $req->getContent());
}
/**
* @expectedException \LogicException
* @dataProvider getContentCantBeCalledTwiceWithResourcesProvider
@ -1013,7 +1033,6 @@ class RequestTest extends \PHPUnit_Framework_TestCase
return array(
'Resource then fetch' => array(true, false),
'Resource then resource' => array(true, true),
'Fetch then resource' => array(false, true),
);
}

View file

@ -101,7 +101,7 @@ class Client extends BaseClient
$r = new \ReflectionClass('\\Symfony\\Component\\ClassLoader\\ClassLoader');
$requirePath = str_replace("'", "\\'", $r->getFileName());
$symfonyPath = str_replace("'", "\\'", realpath(__DIR__.'/../../..'));
$symfonyPath = str_replace("'", "\\'", dirname(dirname(dirname(__DIR__))));
$errorReporting = error_reporting();
$code = <<<EOF

View file

@ -140,7 +140,7 @@ class RouterListener implements EventSubscriberInterface
}
if (null !== $this->logger) {
$this->logger->info(sprintf('Matched route "%s".', $parameters['_route']), array(
$this->logger->info(sprintf('Matched route "%s".', isset($parameters['_route']) ? $parameters['_route'] : 'n/a'), array(
'route_parameters' => $parameters,
'request_uri' => $request->getUri(),
));

View file

@ -160,7 +160,11 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
*/
public function getSurrogate()
{
return $this->getEsi();
if (!$this->surrogate instanceof Esi) {
throw new \LogicException('This instance of HttpCache was not set up to use ESI as surrogate handler. You must overwrite and use createSurrogate');
}
return $this->surrogate;
}
/**
@ -176,11 +180,7 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
{
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the getSurrogate() method instead.', E_USER_DEPRECATED);
if (!$this->surrogate instanceof Esi) {
throw new \LogicException('This instance of HttpCache was not set up to use ESI as surrogate handler. You must overwrite and use createSurrogate');
}
return $this->surrogate;
return $this->getSurrogate();
}
/**

View file

@ -60,11 +60,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $startTime;
protected $loadClassCache;
const VERSION = '2.7.2';
const VERSION_ID = '20702';
const VERSION = '2.7.3';
const VERSION_ID = '20703';
const MAJOR_VERSION = '2';
const MINOR_VERSION = '7';
const RELEASE_VERSION = '2';
const RELEASE_VERSION = '3';
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '05/2018';

View file

@ -128,4 +128,34 @@ class RouterListenerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('GET', $context->getMethod());
}
/**
* @dataProvider getLoggingParameterData
*/
public function testLoggingParameter($parameter, $log)
{
$requestMatcher = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface');
$requestMatcher->expects($this->once())
->method('matchRequest')
->will($this->returnValue($parameter));
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger->expects($this->once())
->method('info')
->with($this->equalTo($log));
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$request = Request::create('http://localhost/');
$listener = new RouterListener($requestMatcher, new RequestContext(), $logger, $this->requestStack);
$listener->onKernelRequest(new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST));
}
public function getLoggingParameterData()
{
return array(
array(array('_route' => 'foo'), 'Matched route "foo".'),
array(array(), 'Matched route "n/a".'),
);
}
}

View file

@ -17,7 +17,7 @@
],
"require": {
"php": ">=5.3.9",
"symfony/event-dispatcher": "~2.5.9|~2.6,>=2.6.2",
"symfony/event-dispatcher": "~2.6,>=2.6.7",
"symfony/http-foundation": "~2.5,>=2.5.4",
"symfony/debug": "~2.6,>=2.6.2",
"psr/log": "~1.0"

View file

@ -272,19 +272,7 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
*/
protected function prepareForDenormalization($data)
{
if (is_array($data) || is_object($data) && $data instanceof \ArrayAccess) {
$normalizedData = $data;
} elseif (is_object($data)) {
$normalizedData = array();
foreach ($data as $attribute => $value) {
$normalizedData[$attribute] = $value;
}
} else {
$normalizedData = array();
}
return $normalizedData;
return (array) $data;
}
/**
@ -303,7 +291,7 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
*
* @throws RuntimeException
*/
protected function instantiateObject(array $data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes)
protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes)
{
if (
isset($context['object_to_populate']) &&

View file

@ -102,6 +102,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
$reflectionClass = new \ReflectionClass($class);
$object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes);
$classMethods = get_class_methods($object);
foreach ($normalizedData as $attribute => $value) {
if ($this->nameConverter) {
$attribute = $this->nameConverter->denormalize($attribute);
@ -113,7 +114,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
if ($allowed && !$ignored) {
$setter = 'set'.ucfirst($attribute);
if (method_exists($object, $setter)) {
if (in_array($setter, $classMethods)) {
$object->$setter($value);
}
}

View file

@ -228,6 +228,12 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('bar', $obj->getBar());
}
public function testConstructorWArgWithPrivateMutator()
{
$obj = $this->normalizer->denormalize(array('foo' => 'bar'), __NAMESPACE__.'\ObjectConstructorArgsWithPrivateMutatorDummy', 'any');
$this->assertEquals('bar', $obj->getFoo());
}
public function testGroupsNormalize()
{
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
@ -511,8 +517,8 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
public function testDenormalizeNonExistingAttribute()
{
$this->assertEquals(
new PropertyDummy(),
$this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\PropertyDummy')
new GetSetDummy(),
$this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\GetSetDummy')
);
}
@ -520,6 +526,12 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
{
$this->assertFalse($this->normalizer->supportsNormalization(new \ArrayObject()));
}
public function testPrivateSetter()
{
$obj = $this->normalizer->denormalize(array('foo' => 'foobar'), __NAMESPACE__.'\ObjectWithPrivateSetterDummy');
$this->assertEquals('bar', $obj->getFoo());
}
}
class GetSetDummy
@ -726,3 +738,37 @@ class GetCamelizedDummy
return $this->bar_foo;
}
}
class ObjectConstructorArgsWithPrivateMutatorDummy
{
private $foo;
public function __construct($foo)
{
$this->setFoo($foo);
}
public function getFoo()
{
return $this->foo;
}
private function setFoo($foo)
{
$this->foo = $foo;
}
}
class ObjectWithPrivateSetterDummy
{
private $foo = 'bar';
public function getFoo()
{
return $this->foo;
}
private function setFoo($foo)
{
}
}

View file

@ -175,7 +175,7 @@
<target>画像の高さが小さすぎます({{ height }}ピクセル)。{{ min_height }}ピクセル以上にしてください。</target>
</trans-unit>
<trans-unit id="47">
<source>This value should be the user current password.</source>
<source>This value should be the user's current password.</source>
<target>ユーザーの現在のパスワードでなければなりません。</target>
</trans-unit>
<trans-unit id="48">

View file

@ -175,7 +175,7 @@
<target>ความสูงของภาพไม่ได้ขนาด ({{ height }}px) อนุญาตให้สูงอย่างน้อยที่สุด {{ min_height }}px</target>
</trans-unit>
<trans-unit id="47">
<source>This value should be the user current password.</source>
<source>This value should be the user's current password.</source>
<target>ค่านี้ควรจะเป็นรหัสผ่านปัจจุบันของผู้ใช้</target>
</trans-unit>
<trans-unit id="48">

View file

@ -234,7 +234,7 @@ class Parser
}
// 1-liner optionally followed by newline(s)
if ($this->lines[0] === trim($value)) {
if (is_string($value) && $this->lines[0] === trim($value)) {
try {
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
} catch (ParseException $e) {
@ -356,7 +356,7 @@ class Parser
return;
}
if ($inSequence && $oldLineIndentation === $newIndent && '-' === $data[0][0]) {
if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) {
// the previous line contained a dash but no item content, this line is a sequence item with the same indentation
// and therefore no nested list or mapping
$this->moveToPreviousLine();

View file

@ -551,6 +551,21 @@ EOF
);
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage missing colon
*/
public function testScalarInSequence()
{
Yaml::parse(<<<EOF
foo:
- bar
"missing colon"
foo: bar
EOF
);
}
/**
* > It is an error for two equal keys to appear in the same mapping node.
* > In such a case the YAML processor may continue, ignoring the second

View file

@ -8,11 +8,9 @@ php:
- 5.6
- hhvm
- nightly
- hhvm-nightly
allow_failures:
- php: nightly
- php: hhvm-nightly
env:
- TWIG_EXT=no
@ -26,7 +24,5 @@ matrix:
exclude:
- php: hhvm
env: TWIG_EXT=yes
- php: hhvm-nightly
env: TWIG_EXT=yes
- php: nightly
env: TWIG_EXT=yes

View file

@ -1,3 +1,27 @@
* 1.20.0 (2015-08-12)
* forbid access to the Twig environment from templates and internal parts of Twig_Template
* fixed limited RCEs when in sandbox mode
* deprecated Twig_Template::getEnvironment()
* deprecated the _self variable for usage outside of the from and import tags
* added Twig_BaseNodeVisitor to ease the compatibility of node visitors
between 1.x and 2.x
* 1.19.0 (2015-07-31)
* fixed wrong error message when including an undefined template in a child template
* added support for variadic filters, functions, and tests
* added support for extra positional arguments in macros
* added ignore_missing flag to the source function
* fixed batch filter with zero items
* deprecated Twig_Environment::clearTemplateCache()
* fixed sandbox disabling when using the include function
* 1.18.2 (2015-06-06)
* fixed template/line guessing in exceptions for nested templates
* optimized the number of inodes and the size of realpath cache when using the cache
* 1.18.1 (2015-04-19)
* fixed memory leaks in the C extension

View file

@ -36,7 +36,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
"dev-master": "1.20-dev"
}
}
}

View file

@ -224,6 +224,23 @@ through your filter::
$filter = new Twig_SimpleFilter('somefilter', 'somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
Variadic Filters
~~~~~~~~~~~~~~~~
.. versionadded:: 1.19
Support for variadic filters was added in Twig 1.19.
When a filter should accept an arbitrary number of arguments, set the
``is_variadic`` option to ``true``; Twig will pass the extra arguments as the
last argument to the filter call as an array::
$filter = new Twig_SimpleFilter('thumbnail', function ($file, array $options = array()) {
// ...
}, array('is_variadic' => true));
Be warned that named arguments passed to a variadic filter cannot be checked
for validity as they will automatically end up in the option array.
Dynamic Filters
~~~~~~~~~~~~~~~
@ -331,6 +348,10 @@ The ``node`` sub-node will contain an expression of ``my_value``. Node-based
tests also have access to the ``arguments`` node. This node will contain the
various other arguments that have been provided to your test.
If you want to pass a variable number of positional or named arguments to the
test, set the ``is_variadic`` option to ``true``. Tests also support dynamic
name feature as filters and functions.
Tags
----

View file

@ -72,29 +72,43 @@ options as the constructor second argument::
The following options are available:
* ``debug``: When set to ``true``, the generated templates have a
* ``debug`` *boolean*
When set to ``true``, the generated templates have a
``__toString()`` method that you can use to display the generated nodes
(default to ``false``).
* ``charset``: The charset used by the templates (default to ``utf-8``).
* ``charset`` *string (default to ``utf-8``)*
* ``base_template_class``: The base template class to use for generated
templates (default to ``Twig_Template``).
The charset used by the templates.
* ``cache``: An absolute path where to store the compiled templates, or
* ``base_template_class`` *string (default to ``Twig_Template``)*
The base template class to use for generated
templates.
* ``cache`` *string|false*
An absolute path where to store the compiled templates, or
``false`` to disable caching (which is the default).
* ``auto_reload``: When developing with Twig, it's useful to recompile the
* ``auto_reload`` *boolean*
When developing with Twig, it's useful to recompile the
template whenever the source code changes. If you don't provide a value for
the ``auto_reload`` option, it will be determined automatically based on the
``debug`` value.
* ``strict_variables``: If set to ``false``, Twig will silently ignore invalid
* ``strict_variables`` *boolean*
If set to ``false``, Twig will silently ignore invalid
variables (variables and or attributes/methods that do not exist) and
replace them with a ``null`` value. When set to ``true``, Twig throws an
exception instead (default to ``false``).
* ``autoescape``: If set to ``true``, HTML auto-escaping will be enabled by
* ``autoescape`` *string|boolean*
If set to ``true``, HTML auto-escaping will be enabled by
default for all templates (default to ``true``).
As of Twig 1.8, you can set the escaping strategy to use (``html``, ``js``,
@ -110,7 +124,9 @@ The following options are available:
strategy does not incur any overhead at runtime as auto-escaping is done at
compilation time.)
* ``optimizations``: A flag that indicates which optimizations to apply
* ``optimizations`` *integer*
A flag that indicates which optimizations to apply
(default to ``-1`` -- all optimizations are enabled; set it to ``0`` to
disable).

View file

@ -107,9 +107,30 @@ Loaders
* As of Twig 1.x, ``Twig_Loader_String`` is deprecated and will be removed in
2.0.
Node Visitors
-------------
* Because of the removal of ``Twig_NodeInterface`` in 2.0, you need to extend
``Twig_BaseNodeVistor`` instead of implementing ``Twig_NodeVisitorInterface``
directly to make your node visitors compatible with both Twig 1.x and 2.x.
Globals
-------
* As of Twig 2.x, the ability to register a global variable after the runtime
or the extensions have been initialized is not possible anymore (but
changing the value of an already registered global is possible).
* As of Twig 1.x, the ``_self`` global variable is deprecated except for usage
in the ``from`` and the ``import`` tags. In Twig 2.0, ``_self`` is not
exposed anymore but still usable in the ``from`` and the ``import`` tags.
Miscellaneous
-------------
* As of Twig 1.x, ``Twig_Environment::clearTemplateCache()`` is deprecated and
will be removed in 2.0.
* As of Twig 1.x, ``Twig_Template::getEnvironment()`` and
``Twig_TemplateInterface::getEnvironment()`` are deprecated and will be
removed in 2.0.

View file

@ -43,3 +43,9 @@ The above example will be rendered as:
<td>No item</td>
</tr>
</table>
Arguments
---------
* ``size``: The size of the batch; fractional numbers will be rounded up
* ``fill``: Used to fill in missing items

View file

@ -4,6 +4,9 @@
.. versionadded:: 1.15
The ``source`` function was added in Twig 1.15.
.. versionadded:: 1.18.3
The ``ignore_missing`` flag was added in Twig 1.18.3.
The ``source`` function returns the content of a template without rendering it:
.. code-block:: jinja
@ -11,6 +14,13 @@ The ``source`` function returns the content of a template without rendering it:
{{ source('template.html') }}
{{ source(some_var) }}
When you set the ``ignore_missing`` flag, Twig will return an empty string if
the template does not exist:
.. code-block:: jinja
{{ source('template.html', ignore_missing = true) }}
The function uses the same template loaders as the ones used to include
templates. So, if you are using the filesystem loader, the templates are looked
for in the paths defined by it.
@ -19,3 +29,4 @@ Arguments
---------
* ``name``: The name of the template to read
* ``ignore_missing``: Whether to ignore missing templates or not

View file

@ -124,7 +124,7 @@ using)::
{
// line 1
echo "Hello ";
echo twig_escape_filter($this->env, $this->getContext($context, "name"), "html", null, true);
echo twig_escape_filter($this->env, isset($context["name"]) ? $context["name"] : null), "html", null, true);
}
// some more code

View file

@ -21,10 +21,14 @@ The key-features are...
* *Flexible*: Twig is powered by a flexible lexer and parser. This allows the
developer to define its own custom tags and filters, and create its own DSL.
Twig is used by many Open-Source projects like Symfony, Drupal8, eZPublish,
phpBB, Piwik, OroCRM, and many frameworks have support for it as well like
Slim, Yii, Laravel, Codeigniter, and Kohana, just to name a few.
Prerequisites
-------------
Twig needs at least **PHP 5.2.4** to run.
Twig needs at least **PHP 5.2.7** to run.
Installation
------------

View file

@ -37,8 +37,16 @@ You can also use ``not`` to check for values that evaluate to ``false``:
<p>You are not subscribed to our mailing list.</p>
{% endif %}
For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can use
more complex ``expressions`` there too:
For multiple conditions, ``and`` and ``or`` can be used:
.. code-block:: jinja
{% if temperature > 18 and temperature < 27 %}
<p>It's a nice day for a walk in the park.</p>
{% endif %}
For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can
use more complex ``expressions`` there too:
.. code-block:: jinja

View file

@ -20,6 +20,9 @@ Macros differs from native PHP functions in a few ways:
* Arguments of a macro are always optional.
* If extra positional arguments are passed to a macro, they end up in the
special ``varargs`` variable as a list of values.
But as with PHP functions, macros don't have access to the current template
variables.

View file

@ -33,5 +33,5 @@ quirks under some circumstances.
.. tip::
For more information on whitespace control, read the
:doc:`dedicated<../templates>` section of the documentation and learn how
:ref:`dedicated section <templates-whitespace-control>` of the documentation and learn how
you can also use the whitespace control modifier on your tags.

View file

@ -74,7 +74,7 @@ is ignored. To avoid name conflicts, you can rename imported blocks:
{% extends "base.html" %}
{% use "blocks.html" with sidebar as base_sidebar %}
{% use "blocks.html" with sidebar as base_sidebar, title as base_title %}
{% block sidebar %}{% endblock %}
{% block title %}{% endblock %}

View file

@ -93,7 +93,7 @@ access the variable attribute:
don't put the braces around them.
If a variable or attribute does not exist, you will receive a ``null`` value
when the ``strict_variables`` option is set to ``false``; alternatively, if ``strict_variables``
when the ``strict_variables`` option is set to ``false``; alternatively, if ``strict_variables``
is set, Twig will throw an error (see :ref:`environment options<environment_options>`).
.. sidebar:: Implementation
@ -124,7 +124,7 @@ Global Variables
The following variables are always available in templates:
* ``_self``: references the current template;
* ``_self``: references the current template (deprecated since Twig 1.20);
* ``_context``: references the current context;
* ``_charset``: references the current charset.
@ -541,6 +541,9 @@ macro call:
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
{% endmacro %}
If extra positional arguments are passed to a macro call, they end up in the
special ``varargs`` variable as a list of values.
.. _twig-expressions:
Expressions
@ -669,6 +672,10 @@ You can combine multiple expressions with the following operators:
Twig also support bitwise operators (``b-and``, ``b-xor``, and ``b-or``).
.. note::
Operators are case sensitive.
Comparisons
~~~~~~~~~~~
@ -801,6 +808,8 @@ inserted into the string:
{{ "foo #{bar} baz" }}
{{ "foo #{1 + 2} baz" }}
.. _templates-whitespace-control:
Whitespace Control
------------------

View file

@ -15,7 +15,7 @@
#ifndef PHP_TWIG_H
#define PHP_TWIG_H
#define PHP_TWIG_VERSION "1.18.1"
#define PHP_TWIG_VERSION "1.20.0"
#include "php.h"

View file

@ -55,7 +55,7 @@ ZEND_BEGIN_ARG_INFO_EX(twig_template_get_attribute_args, ZEND_SEND_BY_VAL, ZEND_
ZEND_END_ARG_INFO()
#ifndef PHP_FE_END
#define PHP_FE_END { NULL, NULL, NULL, 0, 0 }
#define PHP_FE_END { NULL, NULL, NULL}
#endif
static const zend_function_entry twig_functions[] = {
@ -609,6 +609,7 @@ static char *TWIG_GET_CLASS_NAME(zval *object TSRMLS_DC)
static int twig_add_method_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
{
zend_class_entry *ce;
zval *retval;
char *item;
size_t item_len;
@ -619,12 +620,23 @@ static int twig_add_method_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, v
return 0;
}
ce = *va_arg(args, zend_class_entry**);
retval = va_arg(args, zval*);
item_len = strlen(mptr->common.function_name);
item = estrndup(mptr->common.function_name, item_len);
php_strtolower(item, item_len);
if (strcmp("getenvironment", item) == 0) {
zend_class_entry **twig_template_ce;
if (zend_lookup_class("Twig_Template", strlen("Twig_Template"), &twig_template_ce TSRMLS_CC) == FAILURE) {
return 0;
}
if (instanceof_function(ce, *twig_template_ce TSRMLS_CC)) {
return 0;
}
}
add_assoc_stringl_ex(retval, item, item_len+1, item, item_len, 0);
return 0;
@ -670,7 +682,7 @@ static void twig_add_class_to_cache(zval *cache, zval *object, char *class_name
array_init(class_methods);
array_init(class_properties);
// add all methods to self::cache[$class]['methods']
zend_hash_apply_with_arguments(&class_ce->function_table APPLY_TSRMLS_CC, twig_add_method_to_class, 1, class_methods);
zend_hash_apply_with_arguments(&class_ce->function_table APPLY_TSRMLS_CC, twig_add_method_to_class, 2, &class_ce, class_methods);
zend_hash_apply_with_arguments(&class_ce->properties_info APPLY_TSRMLS_CC, twig_add_property_to_class, 2, &class_ce, class_properties);
add_assoc_zval(class_info, "methods", class_methods);
@ -779,12 +791,18 @@ PHP_FUNCTION(twig_template_get_attributes)
$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)) {
if (empty($object)) {
$message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem);
$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)));
$message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
}
} elseif (Twig_Template::ARRAY_CALL === $type) {
$message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
if (null === $object) {
$message = sprintf('Impossible to access a key ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
} elseif (null === $object) {
$message = sprintf('Impossible to access an attribute ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
@ -807,12 +825,21 @@ PHP_FUNCTION(twig_template_get_attributes)
} else {
char *type_name = zend_zval_type_name(object);
Z_ADDREF_P(object);
convert_to_string(object);
TWIG_RUNTIME_ERROR(template TSRMLS_CC,
(strcmp("array", type) == 0)
? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")"
: "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\")",
item, type_name, Z_STRVAL_P(object));
if (Z_TYPE_P(object) == IS_NULL) {
convert_to_string(object);
TWIG_RUNTIME_ERROR(template TSRMLS_CC,
(strcmp("array", type) == 0)
? "Impossible to access a key (\"%s\") on a %s variable"
: "Impossible to access an attribute (\"%s\") on a %s variable",
item, type_name);
} else {
convert_to_string(object);
TWIG_RUNTIME_ERROR(template TSRMLS_CC,
(strcmp("array", type) == 0)
? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")"
: "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\")",
item, type_name, Z_STRVAL_P(object));
}
zval_ptr_dtor(&object);
}
efree(item);
@ -836,7 +863,14 @@ PHP_FUNCTION(twig_template_get_attributes)
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
return null;
}
throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
if (null === $object) {
$message = sprintf('Impossible to invoke a method ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
}
*/
if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
@ -846,9 +880,15 @@ PHP_FUNCTION(twig_template_get_attributes)
type_name = zend_zval_type_name(object);
Z_ADDREF_P(object);
convert_to_string_ex(&object);
if (Z_TYPE_P(object) == IS_NULL) {
convert_to_string_ex(&object);
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\")", item, type_name, Z_STRVAL_P(object));
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable", item, type_name);
} else {
convert_to_string_ex(&object);
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\")", item, type_name, Z_STRVAL_P(object));
}
zval_ptr_dtor(&object);
efree(item);
@ -870,7 +910,7 @@ PHP_FUNCTION(twig_template_get_attributes)
/*
// object property
if (Twig_Template::METHOD_CALL !== $type) {
if (Twig_Template::METHOD_CALL !== $type && !$object instanceof Twig_Template) {
if (isset($object->$item) || array_key_exists((string) $item, $object)) {
if ($isDefinedTest) {
return true;
@ -884,7 +924,7 @@ PHP_FUNCTION(twig_template_get_attributes)
}
}
*/
if (strcmp("method", type) != 0) {
if (strcmp("method", type) != 0 && !TWIG_INSTANCE_OF_USERLAND(object, "Twig_Template" TSRMLS_CC)) {
zval *tmp_properties, *tmp_item;
tmp_properties = TWIG_GET_ARRAY_ELEMENT(tmp_class, "properties", strlen("properties") TSRMLS_CC);
@ -911,7 +951,23 @@ PHP_FUNCTION(twig_template_get_attributes)
/*
// object method
if (!isset(self::$cache[$class]['methods'])) {
self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
if ($object instanceof self) {
$ref = new ReflectionClass($class);
$methods = array();
foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod) {
$methodName = strtolower($refMethod->name);
// Accessing the environment from templates is forbidden to prevent untrusted changes to the environment
if ('getenvironment' !== $methodName) {
$methods[$methodName] = true;
}
}
self::$cache[$class]['methods'] = $methods;
} else {
self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
}
}
$call = false;

View file

@ -0,0 +1,62 @@
<?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
{
/**
* {@inheritdoc}
*/
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);
}
/**
* {@inheritdoc}
*/
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.
*
* @param Twig_Node $node The node to visit
* @param Twig_Environment $env The Twig environment instance
*
* @return Twig_Node The modified node
*/
abstract protected function doEnterNode(Twig_Node $node, Twig_Environment $env);
/**
* Called after child nodes are visited.
*
* @param Twig_Node $node The node to visit
* @param Twig_Environment $env The Twig environment instance
*
* @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);
}

View file

@ -16,7 +16,7 @@
*/
class Twig_Environment
{
const VERSION = '1.18.1';
const VERSION = '1.20.0';
protected $charset;
protected $loader;
@ -89,21 +89,21 @@ class Twig_Environment
}
$options = array_merge(array(
'debug' => false,
'charset' => 'UTF-8',
'debug' => false,
'charset' => 'UTF-8',
'base_template_class' => 'Twig_Template',
'strict_variables' => false,
'autoescape' => 'html',
'cache' => false,
'auto_reload' => null,
'optimizations' => -1,
'strict_variables' => false,
'autoescape' => 'html',
'cache' => false,
'auto_reload' => null,
'optimizations' => -1,
), $options);
$this->debug = (bool) $options['debug'];
$this->charset = strtoupper($options['charset']);
$this->baseTemplateClass = $options['base_template_class'];
$this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
$this->strictVariables = (bool) $options['strict_variables'];
$this->debug = (bool) $options['debug'];
$this->charset = strtoupper($options['charset']);
$this->baseTemplateClass = $options['base_template_class'];
$this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
$this->strictVariables = (bool) $options['strict_variables'];
$this->runtimeInitialized = false;
$this->setCache($options['cache']);
$this->functionCallbacks = array();
@ -250,7 +250,7 @@ class Twig_Environment
$class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix));
return $this->getCache().'/'.substr($class, 0, 2).'/'.substr($class, 2, 2).'/'.substr($class, 4).'.php';
return $this->getCache().'/'.$class[0].'/'.$class[1].'/'.$class.'.php';
}
/**
@ -351,8 +351,7 @@ class Twig_Environment
*
* 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
* @param string $template The template name
*
* @return Twig_Template A template instance representing the given template name
*
@ -444,6 +443,8 @@ class Twig_Environment
/**
* Clears the internal template cache.
*
* @deprecated since 1.18.3 (to be removed in 2.0)
*/
public function clearTemplateCache()
{
@ -483,7 +484,7 @@ class Twig_Environment
/**
* Sets the Lexer instance.
*
* @param Twig_LexerInterface A Twig_LexerInterface instance
* @param Twig_LexerInterface $lexer A Twig_LexerInterface instance
*/
public function setLexer(Twig_LexerInterface $lexer)
{
@ -522,7 +523,7 @@ class Twig_Environment
/**
* Sets the Parser instance.
*
* @param Twig_ParserInterface A Twig_ParserInterface instance
* @param Twig_ParserInterface $parser A Twig_ParserInterface instance
*/
public function setParser(Twig_ParserInterface $parser)
{
@ -1271,11 +1272,11 @@ class Twig_Environment
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));
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));
throw new RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir));
}
$tmpFile = tempnam($dir, basename($file));

View file

@ -315,7 +315,7 @@ class Twig_ExpressionParser
{
switch ($name) {
case 'parent':
$args = $this->parseArguments();
$this->parseArguments();
if (!count($this->parser->getBlockStack())) {
throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line, $this->parser->getFilename());
}
@ -387,7 +387,13 @@ 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());
}
$node = new Twig_Node_Expression_MethodCall($node, 'get'.$arg->getAttribute('value'), $arguments, $lineno);
$name = $arg->getAttribute('value');
if ($this->parser->isReservedMacroName($name)) {
throw new Twig_Error_Syntax(sprintf('"%s" cannot be called as macro as it is a reserved keyword', $name), $token->getLine(), $this->parser->getFilename());
}
$node = new Twig_Node_Expression_MethodCall($node, 'get'.$name, $arguments, $lineno);
$node->setAttribute('safe', true);
return $node;
@ -468,6 +474,10 @@ class Twig_ExpressionParser
*
* @param bool $namedArguments Whether to allow named arguments or not
* @param bool $definition Whether we are parsing arguments for a function definition
*
* @return Twig_Node
*
* @throws Twig_Error_Syntax
*/
public function parseArguments($namedArguments = false, $definition = false)
{

View file

@ -255,37 +255,37 @@ class Twig_Extension_Core extends Twig_Extension
return array(
array(
'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
'-' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Neg'),
'+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'),
'-' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Neg'),
'+' => 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),
'matches' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Matches', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'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),
'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),
),
);
}
@ -382,7 +382,7 @@ function twig_cycle($values, $position)
* Returns a random value depending on the supplied parameter type:
* - a random item from a Traversable or array
* - a random character from a string
* - a random integer between 0 and the integer parameter
* - a random integer between 0 and the integer parameter.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param Traversable|array|int|string $values The values to pick a random item from
@ -466,7 +466,7 @@ function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $
}
/**
* Returns a new date object modified
* Returns a new date object modified.
*
* <pre>
* {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
@ -921,7 +921,9 @@ function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false
/**
* Sorts an array.
*
* @param array $array An array
* @param array $array
*
* @return array
*/
function twig_sort_filter($array)
{
@ -952,6 +954,8 @@ function twig_in_filter($value, $compare)
* @param string $strategy The escaping strategy
* @param string $charset The charset
* @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
*
* @return string
*/
function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
{
@ -1186,7 +1190,7 @@ function _twig_escape_html_attr_callback($matches)
$chr = $matches[0];
$ord = ord($chr);
/**
/*
* The following replaces characters undefined in HTML with the
* hex entity for the Unicode replacement character.
*/
@ -1194,7 +1198,7 @@ function _twig_escape_html_attr_callback($matches)
return '&#xFFFD;';
}
/**
/*
* Check if the current character to escape has a name entity we should
* replace it with while grabbing the hex value of the character.
*/
@ -1210,7 +1214,7 @@ function _twig_escape_html_attr_callback($matches)
return sprintf('&%s;', $entityMap[$int]);
}
/**
/*
* Per OWASP recommendations, we'll use hex entities for any other
* characters where a named entity does not exist.
*/
@ -1398,11 +1402,13 @@ function twig_test_iterable($value)
/**
* Renders a template.
*
* @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
* @param Twig_Environment $env
* @param array $context
* @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 $withContext
* @param bool $ignoreMissing Whether to ignore missing templates or not
* @param bool $sandboxed Whether to sandbox the template or not
*
* @return string The rendered template
*/
@ -1421,10 +1427,15 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a
}
}
$result = null;
try {
return $env->resolveTemplate($template)->render($variables);
$result = $env->resolveTemplate($template)->render($variables);
} catch (Twig_Error_Loader $e) {
if (!$ignoreMissing) {
if ($isSandboxed && !$alreadySandboxed) {
$sandbox->disableSandbox();
}
throw $e;
}
}
@ -1432,18 +1443,27 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a
if ($isSandboxed && !$alreadySandboxed) {
$sandbox->disableSandbox();
}
return $result;
}
/**
* Returns a template content without rendering it.
*
* @param string $name The template name
* @param string $name The template name
* @param bool $ignoreMissing Whether to ignore missing templates or not
*
* @return string The template source
*/
function twig_source(Twig_Environment $env, $name)
function twig_source(Twig_Environment $env, $name, $ignoreMissing = false)
{
return $env->getLoader()->getSource($name);
try {
return $env->getLoader()->getSource($name);
} catch (Twig_Error_Loader $e) {
if (!$ignoreMissing) {
throw $e;
}
}
}
/**
@ -1482,7 +1502,7 @@ function twig_array_batch($items, $size, $fill = null)
$result = array_chunk($items, $size, true);
if (null !== $fill) {
if (null !== $fill && !empty($result)) {
$last = count($result) - 1;
if ($fillCount = $size - count($result[$last])) {
$result[$last] = array_merge(

View file

@ -62,7 +62,7 @@ function twig_var_dump(Twig_Environment $env, $context)
var_dump($vars);
} else {
for ($i = 2; $i < $count; $i++) {
for ($i = 2; $i < $count; ++$i) {
var_dump(func_get_arg($i));
}
}

View file

@ -104,6 +104,8 @@ class Twig_Extension_Escaper extends Twig_Extension
* Marks a variable as being safe.
*
* @param string $string A PHP variable
*
* @return string
*/
function twig_raw_filter($string)
{

View file

@ -16,7 +16,7 @@ class Twig_Extension_Sandbox extends Twig_Extension
public function __construct(Twig_Sandbox_SecurityPolicyInterface $policy, $sandboxed = false)
{
$this->policy = $policy;
$this->policy = $policy;
$this->sandboxedGlobally = $sandboxed;
}

View file

@ -15,6 +15,7 @@
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableInterface
@ -26,10 +27,10 @@ abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableI
{
$this->options = array_merge(array(
'needs_environment' => false,
'needs_context' => false,
'pre_escape' => null,
'preserves_safety' => null,
'callable' => null,
'needs_context' => false,
'pre_escape' => null,
'preserves_safety' => null,
'callable' => null,
), $options);
}

View file

@ -15,6 +15,7 @@
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Filter_Function extends Twig_Filter

View file

@ -15,6 +15,7 @@
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Filter_Method extends Twig_Filter

View file

@ -15,6 +15,7 @@
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Filter_Node extends Twig_Filter

View file

@ -15,6 +15,7 @@
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FilterCallableInterface

View file

@ -15,6 +15,7 @@
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FilterInterface

View file

@ -15,6 +15,7 @@
* Use Twig_SimpleFunction instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
abstract class Twig_Function implements Twig_FunctionInterface, Twig_FunctionCallableInterface
@ -26,8 +27,8 @@ abstract class Twig_Function implements Twig_FunctionInterface, Twig_FunctionCal
{
$this->options = array_merge(array(
'needs_environment' => false,
'needs_context' => false,
'callable' => null,
'needs_context' => false,
'callable' => null,
), $options);
}

View file

@ -16,6 +16,7 @@
* Use Twig_SimpleFunction instead.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Function_Function extends Twig_Function

View file

@ -16,6 +16,7 @@
* Use Twig_SimpleFunction instead.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Function_Method extends Twig_Function

View file

@ -15,6 +15,7 @@
* Use Twig_SimpleFunction instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Function_Node extends Twig_Function

View file

@ -15,6 +15,7 @@
* Use Twig_SimpleFunction instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FunctionCallableInterface

View file

@ -16,6 +16,7 @@
* Use Twig_SimpleFunction instead.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FunctionInterface

View file

@ -33,42 +33,42 @@ class Twig_Lexer implements Twig_LexerInterface
protected $positions;
protected $currentVarBlockLine;
const STATE_DATA = 0;
const STATE_BLOCK = 1;
const STATE_VAR = 2;
const STATE_STRING = 3;
const STATE_INTERPOLATION = 4;
const STATE_DATA = 0;
const STATE_BLOCK = 1;
const STATE_VAR = 2;
const STATE_STRING = 3;
const STATE_INTERPOLATION = 4;
const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
const REGEX_DQ_STRING_DELIM = '/"/A';
const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
const PUNCTUATION = '()[]{}?:.,|';
const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
const PUNCTUATION = '()[]{}?:.,|';
public function __construct(Twig_Environment $env, array $options = array())
{
$this->env = $env;
$this->options = array_merge(array(
'tag_comment' => array('{#', '#}'),
'tag_block' => array('{%', '%}'),
'tag_variable' => array('{{', '}}'),
'tag_comment' => array('{#', '#}'),
'tag_block' => array('{%', '%}'),
'tag_variable' => array('{{', '}}'),
'whitespace_trim' => '-',
'interpolation' => array('#{', '}'),
'interpolation' => array('#{', '}'),
), $options);
$this->regexes = array(
'lex_var' => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A',
'lex_block' => '/\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')\n?/A',
'lex_raw_data' => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*(?:end%s)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s',
'operator' => $this->getOperatorRegex(),
'lex_comment' => '/(?:'.preg_quote($this->options['whitespace_trim'], '/').preg_quote($this->options['tag_comment'][1], '/').'\s*|'.preg_quote($this->options['tag_comment'][1], '/').')\n?/s',
'lex_block_raw' => '/\s*(raw|verbatim)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As',
'lex_block_line' => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As',
'lex_tokens_start' => '/('.preg_quote($this->options['tag_variable'][0], '/').'|'.preg_quote($this->options['tag_block'][0], '/').'|'.preg_quote($this->options['tag_comment'][0], '/').')('.preg_quote($this->options['whitespace_trim'], '/').')?/s',
'lex_var' => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A',
'lex_block' => '/\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')\n?/A',
'lex_raw_data' => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*(?:end%s)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s',
'operator' => $this->getOperatorRegex(),
'lex_comment' => '/(?:'.preg_quote($this->options['whitespace_trim'], '/').preg_quote($this->options['tag_comment'][1], '/').'\s*|'.preg_quote($this->options['tag_comment'][1], '/').')\n?/s',
'lex_block_raw' => '/\s*(raw|verbatim)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As',
'lex_block_line' => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As',
'lex_tokens_start' => '/('.preg_quote($this->options['tag_variable'][0], '/').'|'.preg_quote($this->options['tag_block'][0], '/').'|'.preg_quote($this->options['tag_comment'][0], '/').')('.preg_quote($this->options['whitespace_trim'], '/').')?/s',
'interpolation_start' => '/'.preg_quote($this->options['interpolation'][0], '/').'\s*/A',
'interpolation_end' => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A',
'interpolation_end' => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A',
);
}

View file

@ -41,8 +41,9 @@ interface Twig_LoaderInterface
/**
* Returns true if the template is still fresh.
*
* @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 Timestamp of the last modification time of the
* cached template
*
* @return bool true if the template is fresh, false otherwise
*

View file

@ -122,7 +122,7 @@ class Twig_Node implements Twig_NodeInterface
/**
* Returns true if the attribute is defined.
*
* @param string The attribute name
* @param string $name The attribute name
*
* @return bool true if the attribute is defined, false otherwise
*/
@ -132,11 +132,11 @@ class Twig_Node implements Twig_NodeInterface
}
/**
* Gets an attribute.
* Gets an attribute value by name.
*
* @param string The attribute name
* @param string $name
*
* @return mixed The attribute value
* @return mixed
*/
public function getAttribute($name)
{
@ -148,10 +148,10 @@ class Twig_Node implements Twig_NodeInterface
}
/**
* Sets an attribute.
* Sets an attribute by name to a value.
*
* @param string The attribute name
* @param mixed The attribute value
* @param string $name
* @param mixed $value
*/
public function setAttribute($name, $value)
{
@ -159,9 +159,9 @@ class Twig_Node implements Twig_NodeInterface
}
/**
* Removes an attribute.
* Removes an attribute by name.
*
* @param string The attribute name
* @param string $name
*/
public function removeAttribute($name)
{
@ -169,11 +169,11 @@ class Twig_Node implements Twig_NodeInterface
}
/**
* Returns true if the node with the given identifier exists.
* Returns true if the node with the given name exists.
*
* @param string The node name
* @param string $name
*
* @return bool true if the node with the given name exists, false otherwise
* @return bool
*/
public function hasNode($name)
{
@ -183,9 +183,9 @@ class Twig_Node implements Twig_NodeInterface
/**
* Gets a node by name.
*
* @param string The node name
* @param string $name
*
* @return Twig_Node A Twig_Node instance
* @return Twig_Node
*/
public function getNode($name)
{
@ -199,8 +199,8 @@ class Twig_Node implements Twig_NodeInterface
/**
* Sets a node.
*
* @param string The node name
* @param Twig_Node A Twig_Node instance
* @param string $name
* @param Twig_Node $node
*/
public function setNode($name, $node = null)
{
@ -210,7 +210,7 @@ class Twig_Node implements Twig_NodeInterface
/**
* Removes a node by name.
*
* @param string The node name
* @param string $name
*/
public function removeNode($name)
{

View file

@ -41,9 +41,9 @@ class Twig_Node_CheckSecurity extends Twig_Node
}
$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('$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")

View file

@ -28,7 +28,7 @@ class Twig_Node_Embed extends Twig_Node_Include
protected function addGetTemplate(Twig_Compiler $compiler)
{
$compiler
->write("\$this->loadTemplate(")
->write('$this->loadTemplate(')
->string($this->getAttribute('filename'))
->raw(', ')
->repr($compiler->getFilename())
@ -36,7 +36,7 @@ class Twig_Node_Embed extends Twig_Node_Include
->repr($this->getLine())
->raw(', ')
->string($this->getAttribute('index'))
->raw(")")
->raw(')')
;
}
}

View file

@ -36,15 +36,15 @@ class Twig_Node_Expression_BlockReference extends Twig_Node_Expression
if ($this->getAttribute('output')) {
$compiler
->addDebugInfo($this)
->write("\$this->displayBlock(")
->write('$this->displayBlock(')
->subcompile($this->getNode('name'))
->raw(", \$context, \$blocks);\n")
;
} else {
$compiler
->raw("\$this->renderBlock(")
->raw('$this->renderBlock(')
->subcompile($this->getNode('name'))
->raw(", \$context, \$blocks)")
->raw(', $context, $blocks)')
;
}
}

View file

@ -106,12 +106,19 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
$parameters[$name] = $node;
}
if (!$named) {
$isVariadic = $this->hasAttribute('is_variadic') && $this->getAttribute('is_variadic');
if (!$named && !$isVariadic) {
return $parameters;
}
if (!$callable) {
throw new LogicException(sprintf('Named arguments are not supported for %s "%s".', $callType, $callName));
if ($named) {
$message = sprintf('Named arguments are not supported for %s "%s".', $callType, $callName);
} else {
$message = sprintf('Arbitrary positional arguments are not supported for %s "%s".', $callType, $callName);
}
throw new LogicException($message);
}
// manage named arguments
@ -141,6 +148,19 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
array_shift($definition);
}
}
if ($isVariadic) {
$argument = end($definition);
if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && array() === $argument->getDefaultValue()) {
array_pop($definition);
} else {
$callableName = $r->name;
if ($r->getDeclaringClass()) {
$callableName = $r->getDeclaringClass()->name.'::'.$callableName;
}
throw new LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = array()".', $callableName, $callType, $callName));
}
}
$arguments = array();
$names = array();
@ -185,6 +205,23 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
}
}
if ($isVariadic) {
$arbitraryArguments = new Twig_Node_Expression_Array(array(), -1);
foreach ($parameters as $key => $value) {
if (is_int($key)) {
$arbitraryArguments->addElement($value);
} else {
$arbitraryArguments->addElement($value, new Twig_Node_Expression_Constant($key, -1));
}
unset($parameters[$key]);
}
if ($arbitraryArguments->count()) {
$arguments = array_merge($arguments, $optionalArguments);
$arguments[] = $arbitraryArguments;
}
}
if (!empty($parameters)) {
$unknownParameter = null;
foreach ($parameters as $parameter) {

View file

@ -30,6 +30,9 @@ class Twig_Node_Expression_Filter extends Twig_Node_Expression_Call
if ($filter instanceof Twig_FilterCallableInterface || $filter instanceof Twig_SimpleFilter) {
$this->setAttribute('callable', $filter->getCallable());
}
if ($filter instanceof Twig_SimpleFilter) {
$this->setAttribute('is_variadic', $filter->isVariadic());
}
$this->compileCallable($compiler);
}

View file

@ -29,6 +29,9 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call
if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) {
$this->setAttribute('callable', $function->getCallable());
}
if ($function instanceof Twig_SimpleFunction) {
$this->setAttribute('is_variadic', $function->isVariadic());
}
$this->compileCallable($compiler);
}

View file

@ -12,7 +12,7 @@
class Twig_Node_Expression_Name extends Twig_Node_Expression
{
protected $specialVars = array(
'_self' => '$this',
'_self' => '$this',
'_context' => '$context',
'_charset' => '$this->env->getCharset()',
);

View file

@ -32,15 +32,15 @@ class Twig_Node_Expression_Parent extends Twig_Node_Expression
if ($this->getAttribute('output')) {
$compiler
->addDebugInfo($this)
->write("\$this->displayParentBlock(")
->write('$this->displayParentBlock(')
->string($this->getAttribute('name'))
->raw(", \$context, \$blocks);\n")
;
} else {
$compiler
->raw("\$this->renderParentBlock(")
->raw('$this->renderParentBlock(')
->string($this->getAttribute('name'))
->raw(", \$context, \$blocks)")
->raw(', $context, $blocks)')
;
}
}

View file

@ -26,6 +26,9 @@ class Twig_Node_Expression_Test extends Twig_Node_Expression_Call
if ($test instanceof Twig_TestCallableInterface || $test instanceof Twig_SimpleTest) {
$this->setAttribute('callable', $test->getCallable());
}
if ($test instanceof Twig_SimpleTest) {
$this->setAttribute('is_variadic', $test->isVariadic());
}
$this->compileCallable($compiler);
}

View file

@ -82,7 +82,7 @@ class Twig_Node_For extends Twig_Node
$compiler
->write("foreach (\$context['_seq'] as ")
->subcompile($this->getNode('key_target'))
->raw(" => ")
->raw(' => ')
->subcompile($this->getNode('value_target'))
->raw(") {\n")
->indent()

View file

@ -34,7 +34,7 @@ class Twig_Node_If extends Twig_Node
if ($i > 0) {
$compiler
->outdent()
->write("} elseif (")
->write('} elseif (')
;
} else {
$compiler

View file

@ -36,7 +36,7 @@ class Twig_Node_Import extends Twig_Node
;
if ($this->getNode('expr') instanceof Twig_Node_Expression_Name && '_self' === $this->getNode('expr')->getAttribute('name')) {
$compiler->raw("\$this");
$compiler->raw('$this');
} else {
$compiler
->raw('$this->loadTemplate(')
@ -45,7 +45,7 @@ class Twig_Node_Import extends Twig_Node
->repr($compiler->getFilename())
->raw(', ')
->repr($this->getLine())
->raw(")")
->raw(')')
;
}

View file

@ -61,13 +61,13 @@ class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface
protected function addGetTemplate(Twig_Compiler $compiler)
{
$compiler
->write("\$this->loadTemplate(")
->write('$this->loadTemplate(')
->subcompile($this->getNode('expr'))
->raw(', ')
->repr($compiler->getFilename())
->raw(', ')
->repr($this->getLine())
->raw(")")
->raw(')')
;
}

View file

@ -16,8 +16,16 @@
*/
class Twig_Node_Macro extends Twig_Node
{
const VARARGS_NAME = 'varargs';
public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface $arguments, $lineno, $tag = null)
{
foreach ($arguments as $argumentName => $argument) {
if (self::VARARGS_NAME === $argumentName) {
throw new Twig_Error_Syntax(sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getLine());
}
}
parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag);
}
@ -30,7 +38,7 @@ class Twig_Node_Macro extends Twig_Node
{
$compiler
->addDebugInfo($this)
->write(sprintf("public function get%s(", $this->getAttribute('name')))
->write(sprintf('public function get%s(', $this->getAttribute('name')))
;
$count = count($this->getNode('arguments'));
@ -46,36 +54,55 @@ class Twig_Node_Macro extends Twig_Node
}
}
if (PHP_VERSION_ID >= 50600) {
if ($count) {
$compiler->raw(', ');
}
$compiler->raw('...$__varargs__');
}
$compiler
->raw(")\n")
->write("{\n")
->indent()
;
if (!count($this->getNode('arguments'))) {
$compiler->write("\$context = \$this->env->getGlobals();\n\n");
} else {
$compiler
->write("\$context = \$this->env->mergeGlobals(array(\n")
->indent()
;
foreach ($this->getNode('arguments') as $name => $default) {
$compiler
->write('')
->string($name)
->raw(' => $__'.$name.'__')
->raw(",\n")
;
}
$compiler
->write("\$context = \$this->env->mergeGlobals(array(\n")
->indent()
;
foreach ($this->getNode('arguments') as $name => $default) {
$compiler
->outdent()
->write("));\n\n")
->addIndentation()
->string($name)
->raw(' => $__'.$name.'__')
->raw(",\n")
;
}
$compiler
->addIndentation()
->string(self::VARARGS_NAME)
->raw(' => ')
;
if (PHP_VERSION_ID >= 50600) {
$compiler->raw("\$__varargs__,\n");
} else {
$compiler
->raw('func_num_args() > ')
->repr($count)
->raw(' ? array_slice(func_get_args(), ')
->repr($count)
->raw(") : array(),\n")
;
}
$compiler
->outdent()
->write("));\n\n")
->write("\$blocks = array();\n\n")
->write("ob_start();\n")
->write("try {\n")

View file

@ -107,20 +107,20 @@ class Twig_Node_Module extends Twig_Node
->write("protected function doGetParent(array \$context)\n", "{\n")
->indent()
->addDebugInfo($parent)
->write("return ")
->write('return ')
;
if ($parent instanceof Twig_Node_Expression_Constant) {
$compiler->subcompile($parent);
} else {
$compiler
->raw("\$this->loadTemplate(")
->raw('$this->loadTemplate(')
->subcompile($parent)
->raw(', ')
->repr($compiler->getFilename())
->raw(', ')
->repr($this->getNode('parent')->getLine())
->raw(")")
->raw(')')
;
}
@ -136,7 +136,7 @@ class Twig_Node_Module extends Twig_Node
$compiler
->write("\n\n")
// if the filename contains */, add a blank to avoid a PHP parse error
->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n")
->write('/* '.str_replace('*/', '* /', $this->getAttribute('filename'))." */\n")
->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index')))
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
->write("{\n")
@ -159,7 +159,7 @@ class Twig_Node_Module extends Twig_Node
} elseif ($parent instanceof Twig_Node_Expression_Constant) {
$compiler
->addDebugInfo($parent)
->write("\$this->parent = \$this->loadTemplate(")
->write('$this->parent = $this->loadTemplate(')
->subcompile($parent)
->raw(', ')
->repr($compiler->getFilename())
@ -189,23 +189,23 @@ class Twig_Node_Module extends Twig_Node
foreach ($trait->getNode('targets') as $key => $value) {
$compiler
->write(sprintf("if (!isset(\$_trait_%s_blocks[", $i))
->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 ")
->raw(' is not defined in trait ')
->subcompile($trait->getNode('template'))
->raw(".'));\n")
->outdent()
->write("}\n\n")
->write(sprintf("\$_trait_%s_blocks[", $i))
->write(sprintf('$_trait_%s_blocks[', $i))
->subcompile($value)
->raw(sprintf("] = \$_trait_%s_blocks[", $i))
->raw(sprintf('] = $_trait_%s_blocks[', $i))
->string($key)
->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i))
->raw(sprintf(']; unset($_trait_%s_blocks[', $i))
->string($key)
->raw("]);\n\n")
;
@ -218,9 +218,9 @@ class Twig_Node_Module extends Twig_Node
->indent()
;
for ($i = 0; $i < $countTraits; $i++) {
for ($i = 0; $i < $countTraits; ++$i) {
$compiler
->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i))
->write(sprintf('$_trait_%s_blocks'.($i == $countTraits - 1 ? '' : ',')."\n", $i))
;
}
@ -285,9 +285,9 @@ class Twig_Node_Module extends Twig_Node
if (null !== $parent = $this->getNode('parent')) {
$compiler->addDebugInfo($parent);
if ($parent instanceof Twig_Node_Expression_Constant) {
$compiler->write("\$this->parent");
$compiler->write('$this->parent');
} else {
$compiler->write("\$this->getParent(\$context)");
$compiler->write('$this->getParent($context)');
}
$compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
}
@ -393,7 +393,7 @@ class Twig_Node_Module extends Twig_Node
{
if ($node instanceof Twig_Node_Expression_Constant) {
$compiler
->write(sprintf("%s = \$this->loadTemplate(", $var))
->write(sprintf('%s = $this->loadTemplate(', $var))
->subcompile($node)
->raw(', ')
->repr($compiler->getFilename())
@ -403,13 +403,13 @@ class Twig_Node_Module extends Twig_Node
;
} else {
$compiler
->write(sprintf("%s = ", $var))
->write(sprintf('%s = ', $var))
->subcompile($node)
->raw(";\n")
->write(sprintf("if (!%s", $var))
->write(sprintf('if (!%s', $var))
->raw(" instanceof Twig_Template) {\n")
->indent()
->write(sprintf("%s = \$this->loadTemplate(%s")
->write(sprintf('%s = $this->loadTemplate(%s')
->raw(', ')
->repr($compiler->getFilename())
->raw(', ')

View file

@ -47,6 +47,8 @@ class Twig_Node_SandboxedPrint extends Twig_Node_Print
* This is mostly needed when another visitor adds filters (like the escaper one).
*
* @param Twig_Node $node A Node
*
* @return Twig_Node
*/
protected function removeNodeFilter($node)
{

Some files were not shown because too many files have changed in this diff Show more