Commit 85ffc95c authored by Crise's avatar Crise

Twig 2.3.2

parent acd249cc
* 2.3.2 (2017-04-20)
* fixed edge case in the method cache for Twig attributes
* 2.3.1 (2017-04-18)
* fixed the empty() test
* 2.3.0 (2017-03-22)
* fixed a race condition handling when writing cache files
* "length" filter now returns string length when applied to an object that does
not implement \Countable but provides __toString()
* "empty" test will now consider the return value of the __toString() method for
objects implement __toString() but not \Countable
* fixed JS escaping for unicode characters with higher code points
* added error message when calling `parent()` in a block that doesn't exist in the parent template
* 2.2.0 (2017-02-26)
* added a PSR-11 compatible runtime loader
* added `side` argument to `trim` to allow left or right trimming only.
* 2.1.0 (2017-01-11)
* fixed twig_get_attribute()
......@@ -24,7 +47,34 @@
* improved the performance of the filesystem loader
* removed features that were deprecated in 1.x
* 1.31.0 (2017-XX-XX)
* 1.33.3 (2017-XX-XX)
* n/a
* 1.33.2 (2017-04-20)
* fixed edge case in the method cache for Twig attributes
* 1.33.1 (2017-04-18)
* fixed the empty() test
* 1.33.0 (2017-03-22)
* fixed a race condition handling when writing cache files
* "length" filter now returns string length when applied to an object that does
not implement \Countable but provides __toString()
* "empty" test will now consider the return value of the __toString() method for
objects implement __toString() but not \Countable
* fixed JS escaping for unicode characters with higher code points
* 1.32.0 (2017-02-26)
* fixed deprecation notice in Twig_Util_DeprecationCollector
* added a PSR-11 compatible runtime loader
* added `side` argument to `trim` to allow left or right trimming only.
* 1.31.0 (2017-01-11)
* added Twig_NodeCaptureInterface for nodes that capture all output
* fixed marking the environment as initialized too early
......
......@@ -49,8 +49,11 @@ class Twig_Cache_Filesystem implements Twig_CacheInterface
{
$dir = dirname($key);
if (!is_dir($dir)) {
if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
throw new RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir));
if (false === @mkdir($dir, 0777, true)) {
clearstatcache(true, $dir);
if (!is_dir($dir)) {
throw new RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir));
}
}
} elseif (!is_writable($dir)) {
throw new RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir));
......
......@@ -136,7 +136,7 @@ class Twig_Compiler
public function repr($value)
{
if (is_int($value) || is_float($value)) {
if (false !== $locale = setlocale(LC_NUMERIC, 0)) {
if (false !== $locale = setlocale(LC_NUMERIC, '0')) {
setlocale(LC_NUMERIC, 'C');
}
......
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Psr\Container\ContainerInterface;
/**
* Lazily loads Twig runtime implementations from a PSR-11 container.
*
* Note that the runtime services MUST use their class names as identifiers.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class Twig_ContainerRuntimeLoader implements Twig_RuntimeLoaderInterface
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function load($class)
{
if ($this->container->has($class)) {
return $this->container->get($class);
}
}
}
......@@ -16,11 +16,11 @@
*/
class Twig_Environment
{
const VERSION = '2.1.0';
const VERSION_ID = 20100;
const VERSION = '2.3.2';
const VERSION_ID = 20302;
const MAJOR_VERSION = 2;
const MINOR_VERSION = 1;
const RELEASE_VERSION = 0;
const MINOR_VERSION = 3;
const RELEASE_VERSION = 2;
const EXTRA_VERSION = '';
private $charset;
......@@ -359,7 +359,8 @@ class Twig_Environment
}
if (!class_exists($cls, false)) {
$content = $this->compileSource($this->getLoader()->getSourceContext($name));
$source = $this->getLoader()->getSourceContext($name);
$content = $this->compileSource($source);
$this->cache->write($key, $content);
$this->cache->load($key);
......@@ -371,10 +372,10 @@ class Twig_Environment
*/
eval('?>'.$content);
}
}
if (!class_exists($cls, false)) {
throw new Twig_Error_Runtime(sprintf('Failed to load Twig template "%s", index "%s": cache is corrupted.', $name, $index), -1, $source);
if (!class_exists($cls, false)) {
throw new Twig_Error_Runtime(sprintf('Failed to load Twig template "%s", index "%s": cache is corrupted.', $name, $index), -1, $source);
}
}
}
......
......@@ -155,7 +155,7 @@ final class Twig_Extension_Core extends Twig_Extension
new Twig_Filter('upper', 'twig_upper_filter', array('needs_environment' => true)),
new Twig_Filter('lower', 'twig_lower_filter', array('needs_environment' => true)),
new Twig_Filter('striptags', 'strip_tags'),
new Twig_Filter('trim', 'trim'),
new Twig_Filter('trim', 'twig_trim_filter'),
new Twig_Filter('nl2br', 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
// array helpers
......@@ -594,7 +594,7 @@ function twig_slice(Twig_Environment $env, $item, $start, $length = null, $prese
$item = (string) $item;
return (string) mb_substr($item, $start, null === $length ? mb_strlen($item, $env->getCharset()) - $start : $length, $env->getCharset());
return (string) mb_substr($item, $start, $length, $env->getCharset());
}
/**
......@@ -852,6 +852,31 @@ function twig_in_filter($value, $compare)
return false;
}
/**
* Returns a trimmed string.
*
* @return string
*
* @throws Twig_Error_Runtime When an invalid trimming side is used (not a string or not 'left', 'right', or 'both')
*/
function twig_trim_filter($string, $characterMask = null, $side = 'both')
{
if (null === $characterMask) {
$characterMask = " \t\n\r\0\x0B";
}
switch ($side) {
case 'both':
return trim($string, $characterMask);
case 'left':
return ltrim($string, $characterMask);
case 'right':
return rtrim($string, $characterMask);
default:
throw new Twig_Error_Runtime('Trimming side must be "left", "right" or "both".');
}
}
/**
* Escapes a string.
*
......@@ -950,8 +975,13 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
// \uHHHH
$char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
$char = strtoupper(bin2hex($char));
return '\\u'.strtoupper(substr('0000'.bin2hex($char), -4));
if (4 >= strlen($char)) {
return sprintf('\u%04s', $char);
}
return sprintf('\u%04s\u%04s', substr($char, 0, -4), substr($char, -4));
}, $string);
if ('UTF-8' !== $charset) {
......@@ -1114,7 +1144,15 @@ function twig_convert_encoding($string, $to, $from)
*/
function twig_length_filter(Twig_Environment $env, $thing)
{
return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing);
if (is_scalar($thing)) {
return mb_strlen($thing, $env->getCharset());
}
if (method_exists($thing, '__toString') && !$thing instanceof \Countable) {
return mb_strlen((string) $thing, $env->getCharset());
}
return count($thing);
}
/**
......@@ -1153,7 +1191,11 @@ function twig_lower_filter(Twig_Environment $env, $string)
*/
function twig_title_string_filter(Twig_Environment $env, $string)
{
return mb_convert_case($string, MB_CASE_TITLE, $env->getCharset());
if (null !== $charset = $env->getCharset()) {
return mb_convert_case($string, MB_CASE_TITLE, $charset);
}
return ucwords(strtolower($string));
}
/**
......@@ -1168,7 +1210,7 @@ function twig_capitalize_string_filter(Twig_Environment $env, $string)
{
$charset = $env->getCharset();
return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_strtolower(mb_substr($string, 1, 2147483647, $charset), $charset);
return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_strtolower(mb_substr($string, 1, null, $charset), $charset);
}
/**
......@@ -1203,6 +1245,10 @@ function twig_test_empty($value)
return 0 == count($value);
}
if (is_object($value) && method_exists($value, '__toString')) {
return '' === (string) $value;
}
return '' === $value || false === $value || null === $value || array() === $value;
}
......@@ -1269,12 +1315,6 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a
$sandbox->disableSandbox();
}
throw $e;
} catch (Exception $e) {
if ($isSandboxed && !$alreadySandboxed) {
$sandbox->disableSandbox();
}
throw $e;
}
......@@ -1507,12 +1547,15 @@ function twig_get_attribute(Twig_Environment $env, Twig_Source $source, $object,
continue;
}
if (!isset($classCache[$name])) {
$classCache[$name] = $method;
}
// skip get() and is() methods (in which case, $name is empty)
if ($name) {
if (!isset($classCache[$name])) {
$classCache[$name] = $method;
}
if (!isset($classCache[$lcName])) {
$classCache[$lcName] = $method;
if (!isset($classCache[$lcName])) {
$classCache[$lcName] = $method;
}
}
}
$cache[$class] = $classCache;
......
......@@ -65,7 +65,7 @@ final class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoader
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
return $this->templates[$name];
return $name.':'.$this->templates[$name];
}
public function isFresh($name, $time)
......
......@@ -20,7 +20,7 @@ final class Twig_Profiler_Dumper_Blackfire
$this->dumpProfile('main()', $profile, $data);
$this->dumpChildren('main()', $profile, $data);
$start = microtime(true);
$start = sprintf('%f', microtime(true));
$str = <<<EOF
file-format: BlackfireProbe
cost-dimensions: wt mu pmu
......
......@@ -204,6 +204,8 @@ abstract class Twig_Template
}
} elseif (false !== $parent = $this->getParent($context)) {
$parent->displayBlock($name, $context, array_merge($this->blocks, $blocks), false);
} elseif (isset($blocks[$name])) {
throw new Twig_Error_Runtime(sprintf('Block "%s" should not call parent() in "%s" as the block does not exist in the parent template "%s".', $name, $blocks[$name][0]->getTemplateName(), $this->getTemplateName()), -1, $blocks[$name][0]->getTemplateName());
} else {
throw new Twig_Error_Runtime(sprintf('Block "%s" on template "%s" does not exist.', $name, $this->getTemplateName()), -1, $this->getTemplateName());
}
......@@ -370,12 +372,6 @@ abstract class Twig_Template
ob_start();
try {
$this->display($context);
} catch (Exception $e) {
while (ob_get_level() > $level) {
ob_end_clean();
}
throw $e;
} catch (Throwable $e) {
while (ob_get_level() > $level) {
ob_end_clean();
......
......@@ -93,12 +93,6 @@ final class Twig_TemplateWrapper
ob_start();
try {
$this->template->displayBlock($name, $context);
} catch (Exception $e) {
while (ob_get_level() > $level) {
ob_end_clean();
}
throw $e;
} catch (Throwable $e) {
while (ob_get_level() > $level) {
ob_end_clean();
......
......@@ -121,6 +121,10 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase
protected function doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs)
{
if (!$outputs) {
$this->markTestSkipped('no legacy tests to run');
}
if ($condition) {
eval('$ret = '.$condition.';');
if (!$ret) {
......
......@@ -43,11 +43,11 @@ final class Twig_Util_DeprecationCollector
/**
* Returns deprecations for passed templates.
*
* @param Iterator $iterator An iterator of templates (where keys are template names and values the contents of the template)
* @param Traversable $iterator An iterator of templates (where keys are template names and values the contents of the template)
*
* @return array An array of deprecations
*/
public function collect(Iterator $iterator)
public function collect(Traversable $iterator)
{
$deprecations = array();
set_error_handler(function ($type, $msg) use (&$deprecations) {
......@@ -58,7 +58,7 @@ final class Twig_Util_DeprecationCollector
foreach ($iterator as $name => $contents) {
try {
$this->twig->parse($this->twig->tokenize($contents));
$this->twig->parse($this->twig->tokenize(new Twig_Source($contents, $name)));
} catch (Twig_Error_Syntax $e) {
// ignore templates containing syntax errors
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment