Merge branch '1.x'

* 1.x:
  fixed CS
  Add test coverage for auto_reload
  fix template class name generation to prevent possible collisions and one inode per file
  Fix custom escaper null values
  remove duplicated phpdocs
  Remove deprecated return types from the PHPDocs.
  changed template cache names to take into account the Twig C extension
  Add test coverage for Twig_Cache_Filesystem
This commit is contained in:
Fabien Potencier
2015-10-04 10:43:21 +02:00
11 changed files with 295 additions and 40 deletions
+1 -1
View File
@@ -15,7 +15,7 @@
* 1.22.3 (2015-XX-XX)
* n/a
* changed template cache names to take into account the Twig C extension
* 1.22.2 (2015-09-22)
+1 -1
View File
@@ -38,7 +38,7 @@ class Twig_Cache_Filesystem implements Twig_CacheInterface
{
$hash = hash('sha256', $className);
return $this->directory.'/'.$hash[0].'/'.$hash[1].'/'.$hash.'.php';
return $this->directory.'/'.$hash[0].$hash[1].'/'.$hash.'.php';
}
/**
+11 -3
View File
@@ -248,14 +248,22 @@ class Twig_Environment
/**
* Gets the template class associated with the given string.
*
* @param string $name The name for which to calculate the template class name
* @param int $index The index if it is an embedded template
* The generated template class is based on the following parameters:
*
* * The cache key for the given template;
* * The currently enabled extensions;
* * Whether the Twig C extension is available or not.
*
* @param string $name The name for which to calculate the template class name
* @param int|null $index The index if it is an embedded template
*
* @return string The template class name
*/
public function getTemplateClass($name, $index = null)
{
$key = $this->getLoader()->getCacheKey($name).'__'.implode('__', array_keys($this->extensions));
$key = $this->getLoader()->getCacheKey($name);
$key .= json_encode(array_keys($this->extensions));
$key .= function_exists('twig_template_get_attributes');
return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '_'.$index);
}
+8 -26
View File
@@ -11,20 +11,14 @@
abstract class Twig_Extension implements Twig_ExtensionInterface
{
/**
* Initializes the runtime environment.
*
* This is where you can load some file that contains filter functions for instance.
*
* @param Twig_Environment $environment The current Twig_Environment instance
* {@inheritdoc}
*/
public function initRuntime(Twig_Environment $environment)
{
}
/**
* Returns the token parser instances to add to the existing list.
*
* @return array An array of Twig_TokenParserInterface instances
* {@inheritdoc}
*/
public function getTokenParsers()
{
@@ -32,9 +26,7 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
}
/**
* Returns the node visitor instances to add to the existing list.
*
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
* {@inheritdoc}
*/
public function getNodeVisitors()
{
@@ -42,9 +34,7 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
}
/**
* Returns a list of filters to add to the existing list.
*
* @return array An array of filters
* {@inheritdoc}
*/
public function getFilters()
{
@@ -52,9 +42,7 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
}
/**
* Returns a list of tests to add to the existing list.
*
* @return array An array of tests
* {@inheritdoc}
*/
public function getTests()
{
@@ -62,9 +50,7 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
}
/**
* Returns a list of functions to add to the existing list.
*
* @return array An array of functions
* {@inheritdoc}
*/
public function getFunctions()
{
@@ -72,9 +58,7 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
}
/**
* Returns a list of operators to add to the existing list.
*
* @return array An array of operators
* {@inheritdoc}
*/
public function getOperators()
{
@@ -82,9 +66,7 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
}
/**
* Returns a list of global variables to add to the existing list.
*
* @return array An array of global variables
* {@inheritdoc}
*/
public function getGlobals()
{
+1 -1
View File
@@ -966,7 +966,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
if (!is_string($string)) {
if (is_object($string) && method_exists($string, '__toString')) {
$string = (string) $string;
} else {
} elseif (in_array($strategy, array('html', 'js', 'css', 'html_attr', 'url'))) {
return $string;
}
}
+4 -4
View File
@@ -28,7 +28,7 @@ interface Twig_ExtensionInterface
/**
* Returns the token parser instances to add to the existing list.
*
* @return array An array of Twig_TokenParserInterface instances
* @return Twig_TokenParserInterface[]
*/
public function getTokenParsers();
@@ -42,21 +42,21 @@ interface Twig_ExtensionInterface
/**
* Returns a list of filters to add to the existing list.
*
* @return array An array of filters
* @return Twig_SimpleFilter[]
*/
public function getFilters();
/**
* Returns a list of tests to add to the existing list.
*
* @return array An array of tests
* @return Twig_SimpleTest[]
*/
public function getTests();
/**
* Returns a list of functions to add to the existing list.
*
* @return array An array of functions
* @return Twig_SimpleFunction[]
*/
public function getFunctions();
+1 -1
View File
@@ -25,7 +25,7 @@ abstract class Twig_Test_NodeTestCase extends PHPUnit_Framework_TestCase
$compiler = $this->getCompiler($environment);
$compiler->compile($node);
$this->assertEquals($source, trim($compiler->getSource()));
$this->assertStringMatchesFormat($source, trim($compiler->getSource()));
}
protected function getCompiler(Twig_Environment $environment = null)
+169
View File
@@ -0,0 +1,169 @@
<?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.
*/
class Twig_Tests_Cache_FilesystemTest extends PHPUnit_Framework_TestCase
{
private $nonce;
private $classname;
private $directory;
private $cache;
protected function setUp()
{
$this->nonce = hash('sha256', uniqid(mt_rand(), true));
$this->classname = '__Twig_Tests_Cache_FilesystemTest_Template_'.$this->nonce;
$this->directory = sys_get_temp_dir().'/twig-test-'.$this->nonce;
$this->cache = new Twig_Cache_Filesystem($this->directory);
}
protected function tearDown()
{
if (file_exists($this->directory)) {
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->directory), RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $filename => $fileInfo) {
if (!$iterator->isDot()) {
if ($fileInfo->isDir()) {
rmdir($filename);
} else {
unlink($filename);
}
}
}
rmdir($this->directory);
}
}
public function testLoad()
{
$key = $this->directory.'/cache/cachefile.php';
$dir = dirname($key);
@mkdir($dir, 0777, true);
$this->assertTrue(is_dir($dir));
$this->assertFalse(class_exists($this->classname, false));
$content = $this->generateSource();
file_put_contents($key, $content);
$this->cache->load($key);
$this->assertTrue(class_exists($this->classname, false));
}
public function testLoadMissing()
{
$key = $this->directory.'/cache/cachefile.php';
$this->assertFalse(class_exists($this->classname, false));
$this->cache->load($key);
$this->assertFalse(class_exists($this->classname, false));
}
public function testWrite()
{
$key = $this->directory.'/cache/cachefile.php';
$content = $this->generateSource();
$this->assertFalse(file_exists($key));
$this->assertFalse(file_exists($this->directory));
$this->cache->write($key, $content);
$this->assertTrue(file_exists($this->directory));
$this->assertTrue(file_exists($key));
$this->assertSame(file_get_contents($key), $content);
}
/**
* @expectedException RuntimeException
* @expectedExceptionMessageRegExp #^Unable to create the cache directory #
*/
public function testWriteFailMkdir()
{
$key = $this->directory.'/cache/cachefile.php';
$content = $this->generateSource();
$this->assertFalse(file_exists($key));
// Create read-only root directory.
@mkdir($this->directory, 0555, true);
$this->assertTrue(is_dir($this->directory));
$this->cache->write($key, $content);
}
/**
* @expectedException RuntimeException
* @expectedExceptionMessageRegExp #^Unable to write in the cache directory #
*/
public function testWriteFailDirWritable()
{
$key = $this->directory.'/cache/cachefile.php';
$content = $this->generateSource();
$this->assertFalse(file_exists($key));
// Create root directory.
@mkdir($this->directory, 0777, true);
// Create read-only subdirectory.
@mkdir($this->directory.'/cache' , 0555);
$this->assertTrue(is_dir($this->directory.'/cache'));
$this->cache->write($key, $content);
}
/**
* @expectedException RuntimeException
* @expectedExceptionMessageRegExp #^Failed to write cache file #
*/
public function testWriteFailWriteFile()
{
$key = $this->directory.'/cache/cachefile.php';
$content = $this->generateSource();
$this->assertFalse(file_exists($key));
// Create a directory in the place of the cache file.
@mkdir($key, 0777, true);
$this->assertTrue(is_dir($key));
$this->cache->write($key, $content);
}
public function testGetTimestamp()
{
$key = $this->directory.'/cache/cachefile.php';
$dir = dirname($key);
@mkdir($dir, 0777, true);
$this->assertTrue(is_dir($dir));
// Create the file with a specific modification time.
touch($key, 1234567890);
$this->assertSame(1234567890, $this->cache->getTimestamp($key));
}
public function testGetTimestampMissingFile()
{
$key = $this->directory.'/cache/cachefile.php';
$this->assertSame(0, $this->cache->getTimestamp($key));
}
private function generateSource()
{
return strtr('<?php class {{classname}} {}', array(
'{{classname}}' => $this->classname,
));
}
}
+94
View File
@@ -165,6 +165,85 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
unlink($key);
}
public function testAutoReloadCacheMiss()
{
$templateName = __FUNCTION__;
$templateContent = __FUNCTION__;
$cache = $this->getMock('Twig_CacheInterface');
$loader = $this->getMockLoader($templateName, $templateContent);
$twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false));
// Cache miss: getTimestamp returns 0 and as a result the load() is
// skipped.
$cache->expects($this->once())
->method('generateKey')
->will($this->returnValue('key'));
$cache->expects($this->once())
->method('getTimestamp')
->will($this->returnValue(0));
$loader->expects($this->never())
->method('isFresh');
$cache->expects($this->never())
->method('load');
$twig->loadTemplate($templateName);
}
public function testAutoReloadCacheHit()
{
$templateName = __FUNCTION__;
$templateContent = __FUNCTION__;
$cache = $this->getMock('Twig_CacheInterface');
$loader = $this->getMockLoader($templateName, $templateContent);
$twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false));
$now = time();
// Cache hit: getTimestamp returns something > extension timestamps and
// the loader returns true for isFresh().
$cache->expects($this->once())
->method('generateKey')
->will($this->returnValue('key'));
$cache->expects($this->once())
->method('getTimestamp')
->will($this->returnValue($now));
$loader->expects($this->once())
->method('isFresh')
->will($this->returnValue(true));
$cache->expects($this->once())
->method('load');
$twig->loadTemplate($templateName);
}
public function testAutoReloadOutdatedCacheHit()
{
$templateName = __FUNCTION__;
$templateContent = __FUNCTION__;
$cache = $this->getMock('Twig_CacheInterface');
$loader = $this->getMockLoader($templateName, $templateContent);
$twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false));
$now = time();
$cache->expects($this->once())
->method('generateKey')
->will($this->returnValue('key'));
$cache->expects($this->once())
->method('getTimestamp')
->will($this->returnValue($now));
$loader->expects($this->once())
->method('isFresh')
->will($this->returnValue(false));
$cache->expects($this->never())
->method('load');
$twig->loadTemplate($templateName);
}
public function testAddExtension()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
@@ -180,6 +259,21 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
$visitors = $twig->getNodeVisitors();
$this->assertEquals('Twig_Tests_EnvironmentTest_NodeVisitor', get_class($visitors[2]));
}
protected function getMockLoader($templateName, $templateContent)
{
$loader = $this->getMock('Twig_LoaderInterface');
$loader->expects($this->any())
->method('getSource')
->with($templateName)
->will($this->returnValue($templateContent));
$loader->expects($this->any())
->method('getCacheKey')
->with($templateName)
->will($this->returnValue($templateName));
return $loader;
}
}
class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension
+2
View File
@@ -121,6 +121,8 @@ class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase
$twig->getExtension('core')->setEscaper('foo', 'foo_escaper_for_test');
$this->assertEquals('fooUTF-8', twig_escape_filter($twig, 'foo', 'foo'));
$this->assertEquals('UTF-8', twig_escape_filter($twig, null, 'foo'));
$this->assertEquals('42UTF-8', twig_escape_filter($twig, 42, 'foo'));
}
/**
+3 -3
View File
@@ -46,7 +46,7 @@ class Twig_Tests_Node_ModuleTest extends Twig_Test_NodeTestCase
<?php
/* foo.twig */
class __TwigTemplate_5b706c6f89df245f237e41d0b65b7019c02dcd93c31ba69e652ac1da78575a09 extends Twig_Template
class __TwigTemplate_%x extends Twig_Template
{
public function __construct(Twig_Environment \$env)
{
@@ -87,7 +87,7 @@ EOF
<?php
/* foo.twig */
class __TwigTemplate_5b706c6f89df245f237e41d0b65b7019c02dcd93c31ba69e652ac1da78575a09 extends Twig_Template
class __TwigTemplate_%x extends Twig_Template
{
public function __construct(Twig_Environment \$env)
{
@@ -144,7 +144,7 @@ EOF
<?php
/* foo.twig */
class __TwigTemplate_5b706c6f89df245f237e41d0b65b7019c02dcd93c31ba69e652ac1da78575a09 extends Twig_Template
class __TwigTemplate_%x extends Twig_Template
{
protected function doGetParent(array \$context)
{