diff --git a/core/composer.json b/core/composer.json index 0835d4c..0d83258 100644 --- a/core/composer.json +++ b/core/composer.json @@ -17,7 +17,7 @@ "symfony/validator": "2.7.*", "symfony/process": "2.7.*", "symfony/yaml": "2.7.*", - "twig/twig": "~1.20", + "twig/twig": "1.x-dev", "doctrine/common": "2.5.*", "doctrine/annotations": "1.2.*", "guzzlehttp/guzzle": "~6.1", diff --git a/core/composer.lock b/core/composer.lock index b8465b2..db609ae 100644 --- a/core/composer.lock +++ b/core/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "29476e978819bb447be96d99cbfc7ca1", + "hash": "f193c94c598d7fc173f5e1e86d7393e4", "packages": [ { "name": "doctrine/annotations", @@ -1003,12 +1003,12 @@ "version": "v2.7.4", "source": { "type": "git", - "url": "https://github.com/symfony/Console.git", + "url": "git@github.com:symfony/Console.git", "reference": "9ff9032151186bd66ecee727d728f1319f52d1d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/9ff9032151186bd66ecee727d728f1319f52d1d8", + "url": "https://api.github.com/repos/symfony/Console/zipball/f1f7376766394f409b9b33a57a5675a52f8aad93", "reference": "9ff9032151186bd66ecee727d728f1319f52d1d8", "shasum": "" }, @@ -1778,16 +1778,16 @@ }, { "name": "twig/twig", - "version": "v1.21.2", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "ddce1136beb8db29b9cd7dffa8ab518b978c9db3" + "reference": "b673f65d345e501afdedb55e0b3d2f5fd557b7bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/ddce1136beb8db29b9cd7dffa8ab518b978c9db3", - "reference": "ddce1136beb8db29b9cd7dffa8ab518b978c9db3", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/b673f65d345e501afdedb55e0b3d2f5fd557b7bb", + "reference": "b673f65d345e501afdedb55e0b3d2f5fd557b7bb", "shasum": "" }, "require": { @@ -1800,7 +1800,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.21-dev" + "dev-master": "1.22-dev" } }, "autoload": { @@ -1835,7 +1835,7 @@ "keywords": [ "templating" ], - "time": "2015-09-09 05:28:51" + "time": "2015-09-13 11:17:46" }, { "name": "zendframework/zend-diactoros", @@ -3372,6 +3372,7 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { + "twig/twig": 20, "behat/mink-goutte-driver": 20 }, "prefer-stable": true, diff --git a/core/lib/Drupal/Core/Template/Loader/ThemeRegistryLoader.php b/core/lib/Drupal/Core/Template/Loader/ThemeRegistryLoader.php index fe1ae25..7f74cb0 100644 --- a/core/lib/Drupal/Core/Template/Loader/ThemeRegistryLoader.php +++ b/core/lib/Drupal/Core/Template/Loader/ThemeRegistryLoader.php @@ -38,6 +38,8 @@ public function __construct(Registry $theme_registry) { * * @param string $name * The name of the template to load. + * @param bool $throw + * Whether to throw an exception when an error occurs * * @return string * The path to the template. @@ -45,7 +47,7 @@ public function __construct(Registry $theme_registry) { * @throws \Twig_Error_Loader * Thrown if a template matching $name cannot be found. */ - protected function findTemplate($name) { + protected function findTemplate($name, $throw = TRUE) { // Allow for loading based on the Drupal theme registry. $hook = str_replace('.html.twig', '', strtr($name, '-', '_')); $theme_registry = $this->themeRegistry->getRuntime(); @@ -63,7 +65,9 @@ protected function findTemplate($name) { } } - throw new \Twig_Error_Loader(sprintf('Unable to find template "%s" in the Drupal theme registry.', $name)); + if ($throw) { + throw new \Twig_Error_Loader(sprintf('Unable to find template "%s" in the Drupal theme registry.', $name)); + } } } diff --git a/core/lib/Drupal/Core/Template/TwigCache.php b/core/lib/Drupal/Core/Template/TwigCache.php new file mode 100644 index 0000000..e092164 --- /dev/null +++ b/core/lib/Drupal/Core/Template/TwigCache.php @@ -0,0 +1,7 @@ +cache_object = $cache; $this->templateCacheFilenamePrefix = $twig_extension_hash; // Ensure that twig.engine is loaded, given that it is needed to render a @@ -82,120 +66,15 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension 'auto_reload' => NULL, ); // Ensure autoescaping is always on. - $options['autoescape'] = TRUE; + $options['autoescape'] = 'html'; - $this->loader = $loader; - parent::__construct($this->loader, $options); - } - - /** - * Checks if the compiled template needs an update. - */ - protected function isFresh($cache_filename, $name) { - $cid = 'twig:' . $cache_filename; - $obj = $this->cache_object->get($cid); - $mtime = isset($obj->data) ? $obj->data : FALSE; - return $mtime === FALSE || $this->isTemplateFresh($name, $mtime); - } - - /** - * Compile the source and write the compiled template to disk. - */ - public function updateCompiledTemplate($cache_filename, $name) { - $source = $this->loader->getSource($name); - $compiled_source = $this->compileSource($source, $name); - $this->storage()->save($cache_filename, $compiled_source); - // Save the last modification time - $cid = 'twig:' . $cache_filename; - $this->cache_object->set($cid, REQUEST_TIME); - } - - /** - * {@inheritdoc} - */ - public function getCacheFilename($name) { - // We override the cache filename in order to avoid issues with not using - // shared filesystems. The Twig templates for example rely on available Twig - // extensions, so we use the twig extension hash which varies by extensions - // and their mtime. - // @see \Drupal\Core\DependencyInjection\Compiler\TwigExtensionPass - - if (!$this->cache) { - return FALSE; + if ($options['cache'] === TRUE) { + // @todo Try to use $this->setCache()? + $options['cache'] = new TwigPhpStorageCache($cache, $twig_extension_hash); } - $class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix)); - - // The first part is what is invalidated. - return $this->templateCacheFilenamePrefix . '_' . basename($name) . '_' . $class; - } - - /** - * Implements Twig_Environment::loadTemplate(). - * - * We need to overwrite this function to integrate with drupal_php_storage(). - * - * This is a straight copy from loadTemplate() changed to use - * drupal_php_storage(). - * - * @param string $name - * The template name or the string which should be rendered as template. - * @param int $index - * The index if it is an embedded template. - * - * @return \Twig_TemplateInterface - * A template instance representing the given template name. - * - * @throws \Twig_Error_Loader - * When the template cannot be found. - * @throws \Twig_Error_Syntax - * When an error occurred during compilation. - */ - public function loadTemplate($name, $index = NULL) { - $cls = $this->getTemplateClass($name, $index); - - if (isset($this->loadedTemplates[$cls])) { - return $this->loadedTemplates[$cls]; - } - - if (!class_exists($cls, FALSE)) { - $cache_filename = $this->getCacheFilename($name); - - if ($cache_filename !== FALSE) { - // If autoreload is on, check that the template has not been - // modified since the last compilation. - if ($this->isAutoReload() && !$this->isFresh($cache_filename, $name)) { - $this->updateCompiledTemplate($cache_filename, $name); - } - - if (!$this->storage()->load($cache_filename)) { - $this->updateCompiledTemplate($cache_filename, $name); - $this->storage()->load($cache_filename); - } - } - if (!class_exists($cls, FALSE)) { - $compiled_source = $this->compileSource($this->loader->getSource($name), $name); - eval('?' . '>' . $compiled_source); - } - } - - if (!$this->runtimeInitialized) { - $this->initRuntime(); - } - - return $this->loadedTemplates[$cls] = new $cls($this); - } - - /** - * Gets the PHP code storage object to use for the compiled Twig files. - * - * @return \Drupal\Component\PhpStorage\PhpStorageInterface - */ - protected function storage() { - if (!isset($this->storage)) { - $this->storage = PhpStorageFactory::get('twig'); - } - return $this->storage; + $this->loader = $loader; + parent::__construct($this->loader, $options); } /** @@ -216,7 +95,7 @@ public function getTemplateClass($name, $index = NULL) { // node.html.twig for the output of each node and the same compiled class. $cache_index = $name . (NULL === $index ? '' : '_' . $index); if (!isset($this->templateClasses[$cache_index])) { - $this->templateClasses[$cache_index] = $this->templateClassPrefix . hash('sha256', $this->loader->getCacheKey($name)) . (NULL === $index ? '' : '_' . $index); + $this->templateClasses[$cache_index] = $this->getTemplateClassPrefix() . hash('sha256', $this->loader->getCacheKey($name)) . (NULL === $index ? '' : '_' . $index); } return $this->templateClasses[$cache_index]; } diff --git a/core/lib/Drupal/Core/Template/TwigNodeTrans.php b/core/lib/Drupal/Core/Template/TwigNodeTrans.php index a7b723a..fe2753a 100644 --- a/core/lib/Drupal/Core/Template/TwigNodeTrans.php +++ b/core/lib/Drupal/Core/Template/TwigNodeTrans.php @@ -25,7 +25,7 @@ class TwigNodeTrans extends \Twig_Node { /** * {@inheritdoc} */ - public function __construct(\Twig_NodeInterface $body, \Twig_NodeInterface $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node_Expression $options = NULL, $lineno, $tag = NULL) { + public function __construct(\Twig_Node $body, \Twig_Node $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node_Expression $options = NULL, $lineno, $tag = NULL) { parent::__construct(array( 'count' => $count, 'body' => $body, @@ -91,7 +91,7 @@ public function compile(\Twig_Compiler $compiler) { /** * Extracts the text and tokens for the "trans" tag. * - * @param \Twig_NodeInterface $body + * @param \Twig_Node $body * The node to compile. * * @return array @@ -101,7 +101,7 @@ public function compile(\Twig_Compiler $compiler) { * - array $tokens * The extracted tokens as new \Twig_Node_Expression_Name instances. */ - protected function compileString(\Twig_NodeInterface $body) { + protected function compileString(\Twig_Node $body) { if ($body instanceof \Twig_Node_Expression_Name || $body instanceof \Twig_Node_Expression_Constant || $body instanceof \Twig_Node_Expression_TempName) { return array($body, array()); } diff --git a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php index ac1e676..860cc56 100644 --- a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php +++ b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php @@ -16,19 +16,19 @@ * * @see twig_render */ -class TwigNodeVisitor implements \Twig_NodeVisitorInterface { +class TwigNodeVisitor extends \Twig_BaseNodeVisitor { /** * {@inheritdoc} */ - public function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env) { + protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) { return $node; } /** * {@inheritdoc} */ - public function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) { + protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) { // We use this to inject a call to render_var -> TwigExtension->renderVar() // before anything is printed. if ($node instanceof \Twig_Node_Print) { diff --git a/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php b/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php new file mode 100644 index 0000000..22d33b0 --- /dev/null +++ b/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php @@ -0,0 +1,120 @@ +cache_object = $cache; + $this->templateCacheFilenamePrefix = $twig_extension_hash; + } + + /** + * Gets the PHP code storage object to use for the compiled Twig files. + * + * @return \Drupal\Component\PhpStorage\PhpStorageInterface + */ + protected function storage() { + if (!isset($this->storage)) { + $this->storage = PhpStorageFactory::get('twig'); + } + return $this->storage; + } + + /** + * {@inheritdoc} + */ + public function generateKey($name, $className) { + // We override the cache filename in order to avoid issues with not using + // shared filesystems. The Twig templates for example rely on available Twig + // extensions, so we use the twig extension hash which varies by extensions + // and their mtime. + // @see \Drupal\Core\DependencyInjection\Compiler\TwigExtensionPass + $hash = hash('sha256', $className); + + // The first part is what is invalidated. + return $this->templateCacheFilenamePrefix . '_' . basename($name) . '_' . $hash; + } + + /** + * {@inheritdoc} + */ + public function has($key) { + return $this->storage()->exists($key); + } + + /** + * {@inheritdoc} + */ + public function load($key) { + if ($this->has($key)) { + $this->storage()->load($key); + } + else { + // @todo Add race condition handling stuff around here? + // Compile and eval? How do we get to the Twig env from here? + // Maybe we can just fall back to the null cache somehow or incorporate this into write(). + } + } + + /** + * {@inheritdoc} + */ + public function write($key, $content) { + $this->storage()->save($key, $content); + // Save the last modification time. + $cid = 'twig:' . $key; + $this->cache_object->set($cid, REQUEST_TIME); + } + + /** + * {@inheritdoc} + */ + public function getTimestamp($key) { + $cid = 'twig:' . $key; + if ($cache = $this->cache_object->get($cid)) { + return $cache->data; + } + } + +} diff --git a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php index c67f463..009cc9c 100644 --- a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php +++ b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php @@ -82,14 +82,14 @@ public function getTag() { /** * Ensure that any nodes that are parsed are only of allowed types. * - * @param \Twig_NodeInterface $body + * @param \Twig_Node $body * The expression to check. * @param integer $lineno * The source line. * * @throws \Twig_Error_Syntax */ - protected function checkTransString(\Twig_NodeInterface $body, $lineno) { + protected function checkTransString(\Twig_Node $body, $lineno) { foreach ($body as $node) { if ( $node instanceof \Twig_Node_Text diff --git a/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php b/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php index 3c2b851..c4c6025 100644 --- a/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php +++ b/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php @@ -12,7 +12,7 @@ /** * A test Twig extension that adds a custom function and a custom filter. */ -class TestExtension extends TwigExtension { +class TestExtension extends \Twig_Extension { /** * Generates a list of all Twig functions that this extension defines. @@ -27,9 +27,9 @@ class TestExtension extends TwigExtension { * The value is a standard PHP callback that defines what the function does. */ public function getFunctions() { - return array( - 'testfunc' => new \Twig_Function_Function(array('Drupal\twig_extension_test\TwigExtension\TestExtension', 'testFunction')), - ); + return [ + new \Twig_SimpleFunction('testfunc', [$this, 'testFunction']), + ]; } /** @@ -45,9 +45,9 @@ public function getFunctions() { * The value is a standard PHP callback that defines what the filter does. */ public function getFilters() { - return array( - 'testfilter' => new \Twig_Filter_Function(array('Drupal\twig_extension_test\TwigExtension\TestExtension', 'testFilter')), - ); + return [ + new \Twig_SimpleFilter('testfilter', [$this, 'testFilter']), + ]; } /** diff --git a/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml index 491d1e8..8784c0f 100644 --- a/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml +++ b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml @@ -1,6 +1,5 @@ services: twig_extension_test.twig.test_extension: - arguments: ['@renderer'] class: Drupal\twig_extension_test\TwigExtension\TestExtension tags: - { name: twig.extension } diff --git a/core/tests/Drupal/Tests/Core/Template/AttributeTest.php b/core/tests/Drupal/Tests/Core/Template/AttributeTest.php index b4a192d..7060401 100644 --- a/core/tests/Drupal/Tests/Core/Template/AttributeTest.php +++ b/core/tests/Drupal/Tests/Core/Template/AttributeTest.php @@ -10,6 +10,7 @@ use Drupal\Core\Template\Attribute; use Drupal\Core\Template\AttributeArray; use Drupal\Core\Template\AttributeString; +use Drupal\Core\Template\Loader\StringLoader; use Drupal\Tests\UnitTestCase; /** @@ -253,7 +254,7 @@ public function testChainAddRemoveClasses() { * @covers ::addClass */ public function testTwigAddRemoveClasses($template, $expected, $seed_attributes = array()) { - $loader = new \Twig_Loader_String(); + $loader = new StringLoader(); $twig = new \Twig_Environment($loader); $data = array('attributes' => new Attribute($seed_attributes)); $result = $twig->render($template, $data); diff --git a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php index 37c7255..7d55719 100644 --- a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php +++ b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php @@ -12,6 +12,7 @@ use Drupal\Core\Render\RendererInterface; use Drupal\Core\Template\TwigEnvironment; use Drupal\Core\Template\TwigExtension; +use Drupal\Core\Template\Loader\StringLoader; use Drupal\Tests\UnitTestCase; /** @@ -30,10 +31,11 @@ class TwigExtensionTest extends UnitTestCase { */ public function testEscaping($template, $expected) { $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); - $twig = new \Twig_Environment(NULL, array( + $loader = new \Twig_Loader_Filesystem(); + $twig = new \Twig_Environment($loader, array( 'debug' => TRUE, 'cache' => FALSE, - 'autoescape' => TRUE, + 'autoescape' => 'html', 'optimizations' => 0 )); $twig->addExtension((new TwigExtension($renderer))->setUrlGenerator($this->getMock('Drupal\Core\Routing\UrlGeneratorInterface'))); @@ -96,7 +98,7 @@ public function testActiveTheme() { ->willReturn($active_theme); $extension->setThemeManager($theme_manager); - $loader = new \Twig_Loader_String(); + $loader = new StringLoader(); $twig = new \Twig_Environment($loader); $twig->addExtension($extension); $result = $twig->render('{{ active_theme() }}'); @@ -110,10 +112,11 @@ public function testActiveTheme() { */ public function testSafeStringEscaping() { $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); - $twig = new \Twig_Environment(NULL, array( + $loader = new \Twig_Loader_Filesystem(); + $twig = new \Twig_Environment($loader, array( 'debug' => TRUE, 'cache' => FALSE, - 'autoescape' => TRUE, + 'autoescape' => 'html', 'optimizations' => 0 )); $twig_extension = new TwigExtension($renderer); diff --git a/core/vendor/composer/ClassLoader.php b/core/vendor/composer/ClassLoader.php index 5e1469e..4e05d3b 100644 --- a/core/vendor/composer/ClassLoader.php +++ b/core/vendor/composer/ClassLoader.php @@ -351,7 +351,7 @@ private function findFileWithExtension($class, $ext) foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { if (0 === strpos($class, $prefix)) { foreach ($this->prefixDirsPsr4[$prefix] as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } @@ -361,7 +361,7 @@ private function findFileWithExtension($class, $ext) // PSR-4 fallback dirs foreach ($this->fallbackDirsPsr4 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } @@ -380,7 +380,7 @@ private function findFileWithExtension($class, $ext) foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } @@ -390,7 +390,7 @@ private function findFileWithExtension($class, $ext) // PSR-0 fallback dirs foreach ($this->fallbackDirsPsr0 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json index b539d27..04f353d 100644 --- a/core/vendor/composer/installed.json +++ b/core/vendor/composer/installed.json @@ -2507,12 +2507,12 @@ "version_normalized": "2.7.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/Console.git", + "url": "git@github.com:symfony/Console.git", "reference": "9ff9032151186bd66ecee727d728f1319f52d1d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/9ff9032151186bd66ecee727d728f1319f52d1d8", + "url": "https://api.github.com/repos/symfony/Console/zipball/f1f7376766394f409b9b33a57a5675a52f8aad93", "reference": "9ff9032151186bd66ecee727d728f1319f52d1d8", "shasum": "" }, @@ -3354,129 +3354,129 @@ "homepage": "https://symfony.com" }, { - "name": "twig/twig", - "version": "v1.21.2", - "version_normalized": "1.21.2.0", + "name": "symfony/translation", + "version": "v2.7.4", + "version_normalized": "2.7.4.0", "source": { "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "ddce1136beb8db29b9cd7dffa8ab518b978c9db3" + "url": "https://github.com/symfony/Translation.git", + "reference": "485877661835e188cd78345c6d4eef1290d17571" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/ddce1136beb8db29b9cd7dffa8ab518b978c9db3", - "reference": "ddce1136beb8db29b9cd7dffa8ab518b978c9db3", + "url": "https://api.github.com/repos/symfony/Translation/zipball/485877661835e188cd78345c6d4eef1290d17571", + "reference": "485877661835e188cd78345c6d4eef1290d17571", "shasum": "" }, "require": { - "php": ">=5.2.7" + "php": ">=5.3.9" + }, + "conflict": { + "symfony/config": "<2.7" }, "require-dev": { - "symfony/debug": "~2.7", - "symfony/phpunit-bridge": "~2.7" + "psr/log": "~1.0", + "symfony/config": "~2.7", + "symfony/intl": "~2.4", + "symfony/phpunit-bridge": "~2.7", + "symfony/yaml": "~2.2" }, - "time": "2015-09-09 05:28:51", + "suggest": { + "psr/log": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "time": "2015-09-06 08:36:38", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.21-dev" + "dev-master": "2.7-dev" } }, "installation-source": "dist", "autoload": { - "psr-0": { - "Twig_": "lib/" + "psr-4": { + "Symfony\\Component\\Translation\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" + "email": "fabien@symfony.com" }, { - "name": "Twig Team", - "homepage": "http://twig.sensiolabs.org/contributors", - "role": "Contributors" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "http://twig.sensiolabs.org", - "keywords": [ - "templating" - ] + "description": "Symfony Translation Component", + "homepage": "https://symfony.com" }, { - "name": "symfony/translation", - "version": "v2.7.4", - "version_normalized": "2.7.4.0", + "name": "twig/twig", + "version": "1.x-dev", + "version_normalized": "1.9999999.9999999.9999999-dev", "source": { "type": "git", - "url": "https://github.com/symfony/Translation.git", - "reference": "485877661835e188cd78345c6d4eef1290d17571" + "url": "https://github.com/twigphp/Twig.git", + "reference": "b673f65d345e501afdedb55e0b3d2f5fd557b7bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Translation/zipball/485877661835e188cd78345c6d4eef1290d17571", - "reference": "485877661835e188cd78345c6d4eef1290d17571", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/b673f65d345e501afdedb55e0b3d2f5fd557b7bb", + "reference": "b673f65d345e501afdedb55e0b3d2f5fd557b7bb", "shasum": "" }, "require": { - "php": ">=5.3.9" - }, - "conflict": { - "symfony/config": "<2.7" + "php": ">=5.2.7" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.7", - "symfony/intl": "~2.4", - "symfony/phpunit-bridge": "~2.7", - "symfony/yaml": "~2.2" - }, - "suggest": { - "psr/log": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "symfony/debug": "~2.7", + "symfony/phpunit-bridge": "~2.7" }, - "time": "2015-09-06 08:36:38", + "time": "2015-09-13 11:17:46", "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "1.22-dev" } }, "installation-source": "dist", "autoload": { - "psr-4": { - "Symfony\\Component\\Translation\\": "" + "psr-0": { + "Twig_": "lib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + }, + { + "name": "Twig Team", + "homepage": "http://twig.sensiolabs.org/contributors", + "role": "Contributors" } ], - "description": "Symfony Translation Component", - "homepage": "https://symfony.com" + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ] } ] diff --git a/core/vendor/twig/twig/CHANGELOG b/core/vendor/twig/twig/CHANGELOG index ee447b0..b808f17 100644 --- a/core/vendor/twig/twig/CHANGELOG +++ b/core/vendor/twig/twig/CHANGELOG @@ -1,3 +1,13 @@ +* 1.22.0 (2015-XX-XX) + + * added an option to force PHP bytecode invalidation when writing a compiled template into the cache + * fixed the profiler duration for the root node + * changed template cache names to take into account enabled extensions + * deprecated Twig_Environment::clearCacheFiles(), Twig_Environment::getCacheFilename(), + Twig_Environment::writeCacheFile(), and Twig_Environment::getTemplateClassPrefix() + * added a way to override the filesystem template cache system + * added a way to get the original template source from Twig_Template + * 1.21.2 (2015-09-09) * fixed variable names for the deprecation triggering code diff --git a/core/vendor/twig/twig/composer.json b/core/vendor/twig/twig/composer.json index 2d3438e..5d31ac1 100644 --- a/core/vendor/twig/twig/composer.json +++ b/core/vendor/twig/twig/composer.json @@ -40,7 +40,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.21-dev" + "dev-master": "1.22-dev" } } } diff --git a/core/vendor/twig/twig/doc/deprecated.rst b/core/vendor/twig/twig/doc/deprecated.rst index 1489174..23b22de 100644 --- a/core/vendor/twig/twig/doc/deprecated.rst +++ b/core/vendor/twig/twig/doc/deprecated.rst @@ -117,7 +117,7 @@ Loaders ------- * As of Twig 1.x, ``Twig_Loader_String`` is deprecated and will be removed in - 2.0. + 2.0. You can render a string via ``Twig_Environment::createTemplate()``. Node Visitors ------------- @@ -140,8 +140,9 @@ Globals Miscellaneous ------------- -* As of Twig 1.x, ``Twig_Environment::clearTemplateCache()`` is deprecated and - will be removed in 2.0. +* As of Twig 1.x, ``Twig_Environment::clearTemplateCache()``, ``Twig_Environment::writeCacheFile()``, + ``Twig_Environment::clearCacheFiles()``, ``Twig_Environment::getCacheFilename()``, and + ``Twig_Environment::getTemplateClassPrefix()`` are 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 diff --git a/core/vendor/twig/twig/doc/recipes.rst b/core/vendor/twig/twig/doc/recipes.rst index 450ce55..310a15a 100644 --- a/core/vendor/twig/twig/doc/recipes.rst +++ b/core/vendor/twig/twig/doc/recipes.rst @@ -337,24 +337,33 @@ Refreshing modified Templates when OPcache or APC is enabled When using OPcache with ``opcache.validate_timestamps`` set to ``0`` or APC with ``apc.stat`` set to ``0`` and Twig cache enabled, clearing the template -cache won't update the cache. To get around this, one can extend -``Twig_Environment`` and force the update of the cache when Twig rewrites the -cache:: +cache won't update the cache. - class Twig_Environment_APC extends Twig_Environment - { - protected function writeCacheFile($file, $content) - { - parent::writeCacheFile($file, $content); +To get around this, force Twig to invalidate the bytecode cache:: + + $twig = new Twig_Environment($loader, array( + 'cache' => new Twig_Cache_Filesystem('/some/cache/path', Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION), + // ... + )); - // Compile cached file into bytecode cache - if (extension_loaded('Zend OPcache') && ini_get('opcache.enable')) { - opcache_invalidate($file); - } elseif (extension_loaded('apc') && ini_get('apc.enabled')) { - apc_compile_file($file); +.. note:: + + Before Twig 1.22, you should extend ``Twig_Environment`` instead:: + + class OpCacheAwareTwigEnvironment extends Twig_Environment + { + protected function writeCacheFile($file, $content) + { + parent::writeCacheFile($file, $content); + + // Compile cached file into bytecode cache + if (function_exists('opcache_invalidate') && ini_get('opcache.enable')) { + opcache_invalidate($file); + } elseif (function_exists('apc_compile_file') && ini_get('apc.enabled')) { + apc_compile_file($file); + } } } - } Reusing a stateful Node Visitor ------------------------------- @@ -485,4 +494,25 @@ logical name, and not the path from the filesystem:: Now that the ``base.twig`` templates is defined in an array loader, you can remove it from the database, and everything else will still work as before. +Loading a Template from a String +-------------------------------- + +From a template, you can easily load a template stored in a string via the +``template_from_string`` function (available as of Twig 1.11 via the +``Twig_Extension_StringLoader`` extension):: + +.. code-block:: jinja + + {{ include(template_from_string("Hello {{ name }}")) }} + +From PHP, it's also possible to load a template stored in a string via +``Twig_Environment::createTemplate()`` (available as of Twig 1.18):: + + $template = $twig->createTemplate('hello {{ name }}'); + echo $template->render(array('name' => 'Fabien')); + +.. note:: + + Never use the ``Twig_Loader_String`` loader, which has severe limitations. + .. _callback: http://www.php.net/manual/en/function.is-callable.php diff --git a/core/vendor/twig/twig/ext/twig/php_twig.h b/core/vendor/twig/twig/ext/twig/php_twig.h index 27be05b..6abe4a8 100644 --- a/core/vendor/twig/twig/ext/twig/php_twig.h +++ b/core/vendor/twig/twig/ext/twig/php_twig.h @@ -15,7 +15,7 @@ #ifndef PHP_TWIG_H #define PHP_TWIG_H -#define PHP_TWIG_VERSION "1.21.2" +#define PHP_TWIG_VERSION "1.22.0-DEV" #include "php.h" diff --git a/core/vendor/twig/twig/lib/Twig/Cache/Filesystem.php b/core/vendor/twig/twig/lib/Twig/Cache/Filesystem.php new file mode 100644 index 0000000..8675e07 --- /dev/null +++ b/core/vendor/twig/twig/lib/Twig/Cache/Filesystem.php @@ -0,0 +1,103 @@ + + */ +class Twig_Cache_Filesystem implements Twig_CacheInterface +{ + const FORCE_BYTECODE_INVALIDATION = 1; + + private $directory; + private $invalidateBytecode; + + /** + * @param $directory string The root cache directory + * @param $options int A set of options + */ + public function __construct($directory, $options = 0) + { + $this->directory = $directory; + $this->options = $options; + } + + /** + * {@inheritdoc} + */ + public function generateKey($name, $className) + { + $hash = hash('sha256', $className); + + return $this->directory.'/'.$hash[0].'/'.$hash[1].'/'.$hash.'.php'; + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + return is_file($key); + } + + /** + * {@inheritdoc} + */ + public function load($key) + { + require_once $key; + } + + /** + * {@inheritdoc} + */ + public function write($key, $content) + { + $dir = dirname($key); + if (!is_dir($dir)) { + if (false === @mkdir($dir, 0777, true)) { + clearstatcache(false, $dir); + if (!is_dir($dir)) { + throw new RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir)); + } + } + } elseif (!is_writable($dir)) { + throw new RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir)); + } + + $tmpFile = tempnam($dir, basename($key)); + if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $key)) { + @chmod($key, 0666 & ~umask()); + + if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) { + // Compile cached file into bytecode cache + if (function_exists('opcache_invalidate') && ini_get('opcache.enable')) { + opcache_invalidate($key); + } elseif (function_exists('apc_compile_file') && ini_get('apc.enabled')) { + apc_compile_file($key); + } + } + + return; + } + + throw new RuntimeException(sprintf('Failed to write cache file "%s".', $key)); + } + + /** + * {@inheritdoc} + */ + public function getTimestamp($key) + { + return filemtime($key); + } +} diff --git a/core/vendor/twig/twig/lib/Twig/Cache/Null.php b/core/vendor/twig/twig/lib/Twig/Cache/Null.php new file mode 100644 index 0000000..918e4ce --- /dev/null +++ b/core/vendor/twig/twig/lib/Twig/Cache/Null.php @@ -0,0 +1,58 @@ + + */ +class Twig_Cache_Null implements Twig_CacheInterface +{ + /** + * {@inheritdoc} + */ + public function generateKey($name, $className) + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function write($key, $content) + { + eval('?>'.$content); + } + + /** + * {@inheritdoc} + */ + public function load($key) + { + } + + /** + * {@inheritdoc} + */ + public function getTimestamp($key) + { + // never called as has() always returns false + return 0; + } +} diff --git a/core/vendor/twig/twig/lib/Twig/CacheInterface.php b/core/vendor/twig/twig/lib/Twig/CacheInterface.php new file mode 100644 index 0000000..fedd366 --- /dev/null +++ b/core/vendor/twig/twig/lib/Twig/CacheInterface.php @@ -0,0 +1,65 @@ + + */ +interface Twig_CacheInterface +{ + /** + * Generates a cache key for the given template class name. + * + * @param string $name The template name + * @param string $className The template class name + * + * @return string + */ + public function generateKey($name, $className); + + /** + * Checks if the cache key exists. + * + * @param string $key The cache key + * + * @return bool true if the cache key exists, false otherwise + */ + public function has($key); + + /** + * Writes the compiled template to cache. + * + * @param string $key The cache key + * @param string $content The template representation as a PHP class + */ + public function write($key, $content); + + /** + * Loads a template from the cache. + * + * @param string $key The cache key + */ + public function load($key); + + /** + * Returns the modification timestamp of a key. + * + * @param string $key The cache key + * + * @return int + */ + public function getTimestamp($key); +} diff --git a/core/vendor/twig/twig/lib/Twig/Environment.php b/core/vendor/twig/twig/lib/Twig/Environment.php index 6813a30..54f08ab 100644 --- a/core/vendor/twig/twig/lib/Twig/Environment.php +++ b/core/vendor/twig/twig/lib/Twig/Environment.php @@ -16,7 +16,7 @@ */ class Twig_Environment { - const VERSION = '1.21.2'; + const VERSION = '1.22.0-DEV'; protected $charset; protected $loader; @@ -45,6 +45,10 @@ class Twig_Environment protected $filterCallbacks = array(); protected $staging; + private $originalCache; + private $bcWriteCacheFile = false; + private $bcGetCacheFilename = false; + /** * Constructor. * @@ -58,8 +62,9 @@ class Twig_Environment * * base_template_class: The base template class to use for generated * templates (default to Twig_Template). * - * * cache: An absolute path where to store the compiled templates, or - * false to disable compilation cache (default). + * * cache: An absolute path where to store the compiled templates, + * a Twig_Cache_Interface implementation, + * or false to disable compilation cache (default). * * * auto_reload: Whether to reload the template if the original source changed. * If you don't provide the auto_reload option, it will be @@ -112,6 +117,23 @@ public function __construct(Twig_LoaderInterface $loader = null, $options = arra $this->addExtension(new Twig_Extension_Escaper($options['autoescape'])); $this->addExtension(new Twig_Extension_Optimizer($options['optimizations'])); $this->staging = new Twig_Extension_Staging(); + + // For BC + if (is_string($this->originalCache)) { + $r = new ReflectionMethod($this, 'writeCacheFile'); + if ($r->getDeclaringClass()->getName() !== __CLASS__) { + @trigger_error('The Twig_Environment::writeCacheFile method is deprecated and will be removed in Twig 2.0.', E_USER_DEPRECATED); + + $this->bcWriteCacheFile = true; + } + + $r = new ReflectionMethod($this, 'getCacheFilename'); + if ($r->getDeclaringClass()->getName() !== __CLASS__) { + @trigger_error('The Twig_Environment::getCacheFilename method is deprecated and will be removed in Twig 2.0.', E_USER_DEPRECATED); + + $this->bcGetCacheFilename = true; + } + } } /** @@ -213,24 +235,39 @@ public function isStrictVariables() } /** - * Gets the cache directory or false if cache is disabled. + * Gets the current cache implementation. * - * @return string|false + * @param bool $original Whether to return the original cache option or the real cache instance + * + * @return Twig_CacheInterface|string|false A Twig_CacheInterface implementation, + * an absolute path to the compiled templates, + * or false to disable cache */ - public function getCache() + public function getCache($original = true) { - return $this->cache; + return $original ? $this->originalCache : $this->cache; } /** - * Sets the cache directory or false if cache is disabled. + * Sets the current cache implementation. * - * @param string|false $cache The absolute path to the compiled templates, - * or false to disable cache + * @param Twig_CacheInterface|string|false $cache A Twig_CacheInterface implementation, + * an absolute path to the compiled templates, + * or false to disable cache */ public function setCache($cache) { - $this->cache = $cache ? $cache : false; + if (is_string($cache)) { + $this->originalCache = $cache; + $this->cache = new Twig_Cache_Filesystem($cache); + } elseif (false === $cache) { + $this->originalCache = $cache; + $this->cache = new Twig_Cache_Null(); + } elseif ($cache instanceof Twig_CacheInterface) { + $this->originalCache = $this->cache = $cache; + } else { + throw new LogicException(sprintf('Cache can only be a string, false, or a Twig_CacheInterface implementation.')); + } } /** @@ -239,16 +276,16 @@ public function setCache($cache) * @param string $name The template name * * @return string|false The cache file name or false when caching is disabled + * + * @deprecated since 1.22 (to be removed in 2.0) */ public function getCacheFilename($name) { - if (false === $this->cache) { - return false; - } + @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); - $class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix)); + $key = $this->cache->generateKey($name, $this->getTemplateClass($name)); - return $this->getCache().'/'.$class[0].'/'.$class[1].'/'.$class.'.php'; + return !$key ? false : $key; } /** @@ -261,16 +298,22 @@ public function getCacheFilename($name) */ public function getTemplateClass($name, $index = null) { - return $this->templateClassPrefix.hash('sha256', $this->getLoader()->getCacheKey($name)).(null === $index ? '' : '_'.$index); + $key = $this->getLoader()->getCacheKey($name).'__'.implode('__', array_keys($this->extensions)); + + return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '_'.$index); } /** * Gets the template class prefix. * * @return string The template class prefix + * + * @deprecated since 1.22 (to be removed in 2.0) */ public function getTemplateClassPrefix() { + @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); + return $this->templateClassPrefix; } @@ -326,15 +369,21 @@ public function loadTemplate($name, $index = null) } if (!class_exists($cls, false)) { - if (false === $cache = $this->getCacheFilename($name)) { - eval('?>'.$this->compileSource($this->getLoader()->getSource($name), $name)); + if ($this->bcGetCacheFilename) { + $key = $this->getCacheFilename($name); } else { - if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) { - $this->writeCacheFile($cache, $this->compileSource($this->getLoader()->getSource($name), $name)); - } + $key = $this->cache->generateKey($name, $cls); + } - require_once $cache; + if (!$this->cache->has($key) || ($this->isAutoReload() && !$this->isTemplateFresh($name, $this->cache->getTimestamp($key)))) { + if ($this->bcWriteCacheFile) { + $this->writeCacheFile($key, $this->compileSource($this->getLoader()->getSource($name), $name)); + } else { + $this->cache->write($key, $this->compileSource($this->getLoader()->getSource($name), $name)); + } } + + $this->cache->load($key); } if (!$this->runtimeInitialized) { @@ -453,16 +502,18 @@ public function clearTemplateCache() /** * Clears the template cache files on the filesystem. + * + * @deprecated since 1.22 (to be removed in 2.0) */ public function clearCacheFiles() { - if (false === $this->cache) { - return; - } + @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); - foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->cache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { - if ($file->isFile()) { - @unlink($file->getPathname()); + if (is_string($this->originalCache)) { + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->originalCache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if ($file->isFile()) { + @unlink($file->getPathname()); + } } } } @@ -593,7 +644,13 @@ public function compile(Twig_NodeInterface $node) public function compileSource($source, $name = null) { try { - return $this->compile($this->parse($this->tokenize($source, $name))); + $compiled = $this->compile($this->parse($this->tokenize($source, $name)), $source); + + if (isset($source[0])) { + $compiled .= '/* '.str_replace(array('*/', "\r\n", "\n", "\r"), array('*//*', "\n", "*/\n/* ", "*/\n/* "), $source)."*/\n"; + } + + return $compiled; } catch (Twig_Error $e) { $e->setTemplateFile($name); throw $e; @@ -1272,30 +1329,11 @@ protected function initExtension(Twig_ExtensionInterface $extension) } } + /** + * @deprecated since 1.22 (to be removed in 2.0) + */ protected function writeCacheFile($file, $content) { - $dir = dirname($file); - if (!is_dir($dir)) { - if (false === @mkdir($dir, 0777, true)) { - clearstatcache(false, $dir); - if (!is_dir($dir)) { - throw new RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir)); - } - } - } elseif (!is_writable($dir)) { - throw new RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir)); - } - - $tmpFile = tempnam($dir, basename($file)); - if (false !== @file_put_contents($tmpFile, $content)) { - // rename does not work on Win32 before 5.2.6 - if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) { - @chmod($file, 0666 & ~umask()); - - return; - } - } - - throw new RuntimeException(sprintf('Failed to write cache file "%s".', $file)); + $this->cache->write($file, $content); } } diff --git a/core/vendor/twig/twig/lib/Twig/Extension/Core.php b/core/vendor/twig/twig/lib/Twig/Extension/Core.php index 4980ceb..3716f02 100644 --- a/core/vendor/twig/twig/lib/Twig/Extension/Core.php +++ b/core/vendor/twig/twig/lib/Twig/Extension/Core.php @@ -536,12 +536,17 @@ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = nu return $date; } + if (null === $date || 'now' === $date) { + return new DateTime($date, false !== $timezone ? $timezone : $env->getExtension('core')->getTimezone()); + } + $asString = (string) $date; if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) { - $date = '@'.$date; + $date = new DateTime('@'.$date); + } else { + $date = new DateTime($date, $env->getExtension('core')->getTimezone()); } - $date = new DateTime($date, $env->getExtension('core')->getTimezone()); if (false !== $timezone) { $date->setTimezone($timezone); } diff --git a/core/vendor/twig/twig/lib/Twig/Loader/Chain.php b/core/vendor/twig/twig/lib/Twig/Loader/Chain.php index 7919eda..81d57ad 100644 --- a/core/vendor/twig/twig/lib/Twig/Loader/Chain.php +++ b/core/vendor/twig/twig/lib/Twig/Loader/Chain.php @@ -60,7 +60,7 @@ public function getSource($name) } } - throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(', ', $exceptions))); + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); } /** @@ -112,7 +112,7 @@ public function getCacheKey($name) } } - throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions))); + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); } /** @@ -133,6 +133,6 @@ public function isFresh($name, $time) } } - throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions))); + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); } } diff --git a/core/vendor/twig/twig/lib/Twig/Loader/Filesystem.php b/core/vendor/twig/twig/lib/Twig/Loader/Filesystem.php index a68876c..1bc75a1 100644 --- a/core/vendor/twig/twig/lib/Twig/Loader/Filesystem.php +++ b/core/vendor/twig/twig/lib/Twig/Loader/Filesystem.php @@ -233,7 +233,7 @@ protected function parseName($name, $default = self::MAIN_NAMESPACE) protected function normalizeName($name) { - return preg_replace('#/{2,}#', '/', strtr((string) $name, '\\', '/')); + return preg_replace('#/{2,}#', '/', str_replace('\\', '/', (string) $name)); } protected function validateName($name) diff --git a/core/vendor/twig/twig/lib/Twig/Loader/String.php b/core/vendor/twig/twig/lib/Twig/Loader/String.php index e7fff3e..9bfd856 100644 --- a/core/vendor/twig/twig/lib/Twig/Loader/String.php +++ b/core/vendor/twig/twig/lib/Twig/Loader/String.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -@trigger_error('The Twig_Loader_String class is deprecated since version 1.18.1 and will be removed in 2.0. Use Twig_Loader_Array instead.', E_USER_DEPRECATED); +@trigger_error('The Twig_Loader_String class is deprecated since version 1.18.1 and will be removed in 2.0. Use Twig_Loader_Array instead or Twig_Environment::createTemplate().', E_USER_DEPRECATED); /** * Loads a template from a string. diff --git a/core/vendor/twig/twig/lib/Twig/Node/For.php b/core/vendor/twig/twig/lib/Twig/Node/For.php index 36e9de8..a8d199a 100644 --- a/core/vendor/twig/twig/lib/Twig/Node/For.php +++ b/core/vendor/twig/twig/lib/Twig/Node/For.php @@ -39,8 +39,7 @@ public function compile(Twig_Compiler $compiler) { $compiler ->addDebugInfo($this) - // the (array) cast bypasses a PHP 5.2.6 bug - ->write("\$context['_parent'] = (array) \$context;\n") + ->write("\$context['_parent'] = \$context;\n") ->write("\$context['_seq'] = twig_ensure_traversable(") ->subcompile($this->getNode('seq')) ->raw(");\n") diff --git a/core/vendor/twig/twig/lib/Twig/Profiler/Profile.php b/core/vendor/twig/twig/lib/Twig/Profiler/Profile.php index ec9e254..104bc05 100644 --- a/core/vendor/twig/twig/lib/Twig/Profiler/Profile.php +++ b/core/vendor/twig/twig/lib/Twig/Profiler/Profile.php @@ -86,6 +86,16 @@ public function addProfile(Twig_Profiler_Profile $profile) */ public function getDuration() { + if ($this->isRoot() && $this->profiles) { + // for the root node with children, duration is the sum of all child durations + $duration = 0; + foreach ($this->profiles as $profile) { + $duration += $profile->getDuration(); + } + + return $duration; + } + return isset($this->ends['wt']) && isset($this->starts['wt']) ? $this->ends['wt'] - $this->starts['wt'] : 0; } diff --git a/core/vendor/twig/twig/lib/Twig/Template.php b/core/vendor/twig/twig/lib/Twig/Template.php index e2ab05a..a854138 100644 --- a/core/vendor/twig/twig/lib/Twig/Template.php +++ b/core/vendor/twig/twig/lib/Twig/Template.php @@ -305,6 +305,33 @@ public function getBlocks() } /** + * Returns the template source code. + * + * @return string|null The template source code or null if it is not available + */ + public function getSource() + { + $reflector = new ReflectionClass($this); + $file = $reflector->getFileName(); + + if (!file_exists($file)) { + return; + } + + $source = file($file, FILE_IGNORE_NEW_LINES); + array_splice($source, 0, $reflector->getEndLine()); + + $i = 0; + while (isset($source[$i]) && '/* */' === substr_replace($source[$i], '', 3, -2)) { + $source[$i] = str_replace('*//*', '*/', substr($source[$i], 3, -2)); + ++$i; + } + array_splice($source, $i); + + return implode("\n", $source); + } + + /** * {@inheritdoc} */ public function display(array $context, array $blocks = array()) diff --git a/core/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php b/core/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php index e4bef74..7d78179 100644 --- a/core/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php +++ b/core/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php @@ -138,17 +138,26 @@ public function testGlobals() */ } + public function testCompileSourceInlinesSource() + { + $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + + $source = "\r\nbar\n"; + $expected = "/* */\n/* bar*/\n/* */\n"; + + $this->assertContains($expected, $twig->compileSource($source, 'index')); + } + public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate() { - $options = array('cache' => sys_get_temp_dir().'/twig', 'auto_reload' => false, 'debug' => false); + $cache = new Twig_Cache_Filesystem($dir = sys_get_temp_dir().'/twig'); + $options = array('cache' => $cache, 'auto_reload' => false, 'debug' => false); // force compilation $twig = new Twig_Environment($loader = new Twig_Loader_Array(array('index' => '{{ foo }}')), $options); - $cache = $twig->getCacheFilename('index'); - if (!is_dir(dirname($cache))) { - mkdir(dirname($cache), 0777, true); - } - file_put_contents($cache, $twig->compileSource('{{ foo }}', 'index')); + + $key = $cache->generateKey('index', $twig->getTemplateClass('index')); + $cache->write($key, $twig->compileSource('{{ foo }}', 'index')); // check that extensions won't be initialized when rendering a template that is already in the cache $twig = $this @@ -164,7 +173,7 @@ public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate() $output = $twig->render('index', array('foo' => 'bar')); $this->assertEquals('bar', $output); - unlink($cache); + unlink($key); } public function testAddExtension() diff --git a/core/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php b/core/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php index 6f5bfee..14e0144 100644 --- a/core/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php +++ b/core/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php @@ -38,6 +38,9 @@ public function tearDown() $this->removeDir($this->tmpDir); } + /** + * @group legacy + */ public function testWritingCacheFiles() { $name = 'index'; @@ -48,6 +51,9 @@ public function testWritingCacheFiles() $this->fileName = $cacheFileName; } + /** + * @group legacy + */ public function testClearingCacheFiles() { $name = 'index2'; diff --git a/core/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test b/core/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test index 08ecfd4..2143a86 100644 --- a/core/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test +++ b/core/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test @@ -3,6 +3,6 @@ --TEMPLATE-- {{ 'test %foo%'|replace(stdClass) }} --DATA-- -return array('stdClass' => new \stdClass()) +return array('stdClass' => new stdClass()) --EXCEPTION-- Twig_Error_Runtime: The "replace" filter expects an array or "Traversable" as replace values, got "stdClass" in "index.twig" at line 2. diff --git a/core/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php b/core/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php index b289592..b2c6fa4 100644 --- a/core/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php +++ b/core/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php @@ -51,7 +51,7 @@ public function getTests() $tests[] = array($node, <<getVariableGetter('items')}); foreach (\$context['_seq'] as \$context["key"] => \$context["item"]) { echo {$this->getVariableGetter('foo')}; @@ -73,7 +73,7 @@ public function getTests() $tests[] = array($node, <<getVariableGetter('values')}); \$context['loop'] = array( 'parent' => \$context['_parent'], @@ -116,7 +116,7 @@ public function getTests() $tests[] = array($node, <<getVariableGetter('values')}); \$context['loop'] = array( 'parent' => \$context['_parent'], @@ -149,7 +149,7 @@ public function getTests() $tests[] = array($node, <<getVariableGetter('values')}); \$context['_iterated'] = false; \$context['loop'] = array( diff --git a/core/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php b/core/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php index 5688af8..19a4be9 100644 --- a/core/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php +++ b/core/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php @@ -46,7 +46,7 @@ public function getTests() getMock('Twig_LoaderInterface')), false); + + $this->assertSame("\n", $template->getSource()); + } + /** * @dataProvider getGetAttributeWithSandbox */ @@ -463,6 +470,8 @@ public function getAttribute($object, $item, array $arguments = array(), $type = } } } +/* */ +/* */ class Twig_TemplateArrayAccessObject implements ArrayAccess {