Merge branch 'feature/production-filesystem-loader' into dev
This commit is contained in:
+20
-7
@@ -605,7 +605,7 @@ class Mustache_Engine
|
|||||||
* This method must be updated any time options are added which make it so
|
* This method must be updated any time options are added which make it so
|
||||||
* the same template could be parsed and compiled multiple different ways.
|
* the same template could be parsed and compiled multiple different ways.
|
||||||
*
|
*
|
||||||
* @param string $source
|
* @param string|Mustache_Source $source
|
||||||
*
|
*
|
||||||
* @return string Mustache Template class name
|
* @return string Mustache Template class name
|
||||||
*/
|
*/
|
||||||
@@ -619,17 +619,26 @@ class Mustache_Engine
|
|||||||
// 'default' escapes.
|
// 'default' escapes.
|
||||||
//
|
//
|
||||||
// Keep this list in alphabetical order :)
|
// Keep this list in alphabetical order :)
|
||||||
$options = array(
|
$chunks = array(
|
||||||
'charset' => $this->charset,
|
'charset' => $this->charset,
|
||||||
'delimiters' => $this->delimiters,
|
'delimiters' => $this->delimiters,
|
||||||
'entityFlags' => $this->entityFlags,
|
'entityFlags' => $this->entityFlags,
|
||||||
'escape' => isset($this->escape) ? 'custom' : 'default',
|
'escape' => isset($this->escape) ? 'custom' : 'default',
|
||||||
|
'key' => ($source instanceof Mustache_Source) ? $source->getKey() : 'source',
|
||||||
'pragmas' => $this->getPragmas(),
|
'pragmas' => $this->getPragmas(),
|
||||||
'strictCallables' => $this->strictCallables,
|
'strictCallables' => $this->strictCallables,
|
||||||
'version' => self::VERSION,
|
'version' => self::VERSION,
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->templateClassPrefix . md5(json_encode($options) . "\n" . $source);
|
$key = json_encode($chunks);
|
||||||
|
|
||||||
|
// Template Source instances have already provided their own source key. For strings, just include the whole
|
||||||
|
// source string in the md5 hash.
|
||||||
|
if (!$source instanceof Mustache_Source) {
|
||||||
|
$key .= "\n" . $source;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->templateClassPrefix . md5($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -706,8 +715,8 @@ class Mustache_Engine
|
|||||||
* @see Mustache_Engine::loadPartial
|
* @see Mustache_Engine::loadPartial
|
||||||
* @see Mustache_Engine::loadLambda
|
* @see Mustache_Engine::loadLambda
|
||||||
*
|
*
|
||||||
* @param string $source
|
* @param string|Mustache_Source $source
|
||||||
* @param Mustache_Cache $cache (default: null)
|
* @param Mustache_Cache $cache (default: null)
|
||||||
*
|
*
|
||||||
* @return Mustache_Template
|
* @return Mustache_Template
|
||||||
*/
|
*/
|
||||||
@@ -775,13 +784,12 @@ class Mustache_Engine
|
|||||||
*
|
*
|
||||||
* @see Mustache_Compiler::compile
|
* @see Mustache_Compiler::compile
|
||||||
*
|
*
|
||||||
* @param string $source
|
* @param string|Mustache_Source $source
|
||||||
*
|
*
|
||||||
* @return string generated Mustache template class code
|
* @return string generated Mustache template class code
|
||||||
*/
|
*/
|
||||||
private function compile($source)
|
private function compile($source)
|
||||||
{
|
{
|
||||||
$tree = $this->parse($source);
|
|
||||||
$name = $this->getTemplateClassName($source);
|
$name = $this->getTemplateClassName($source);
|
||||||
|
|
||||||
$this->log(
|
$this->log(
|
||||||
@@ -790,6 +798,11 @@ class Mustache_Engine
|
|||||||
array('className' => $name)
|
array('className' => $name)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($source instanceof Mustache_Source) {
|
||||||
|
$source = $source->getSource();
|
||||||
|
}
|
||||||
|
$tree = $this->parse($source);
|
||||||
|
|
||||||
$compiler = $this->getCompiler();
|
$compiler = $this->getCompiler();
|
||||||
$compiler->setPragmas($this->getPragmas());
|
$compiler->setPragmas($this->getPragmas());
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ interface Mustache_Loader
|
|||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
*
|
*
|
||||||
* @return string Mustache Template source
|
* @return string|Mustache_Source Mustache Template source
|
||||||
*/
|
*/
|
||||||
public function load($name);
|
public function load($name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Mustache.php.
|
||||||
|
*
|
||||||
|
* (c) 2010-2015 Justin Hileman
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mustache Template production filesystem Loader implementation.
|
||||||
|
*
|
||||||
|
* A production-ready FilesystemLoader, which doesn't require reading a file if it already exists in the template cache.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
class Mustache_Loader_ProductionFilesystemLoader extends Mustache_Loader_FilesystemLoader
|
||||||
|
{
|
||||||
|
private $statProps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mustache production filesystem Loader constructor.
|
||||||
|
*
|
||||||
|
* Passing an $options array allows overriding certain Loader options during instantiation:
|
||||||
|
*
|
||||||
|
* $options = array(
|
||||||
|
* // The filename extension used for Mustache templates. Defaults to '.mustache'
|
||||||
|
* 'extension' => '.ms',
|
||||||
|
* 'stat_props' => array('size', 'mtime'),
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* Specifying 'stat_props' overrides the stat properties used to invalidate the template cache. By default, this
|
||||||
|
* uses 'mtime' and 'size', but this can be set to any of the properties supported by stat():
|
||||||
|
*
|
||||||
|
* http://php.net/manual/en/function.stat.php
|
||||||
|
*
|
||||||
|
* You can also disable filesystem stat entirely:
|
||||||
|
*
|
||||||
|
* $options = array('stat_props' => null);
|
||||||
|
*
|
||||||
|
* But with great power comes great responsibility. Namely, if you disable stat-based cache invalidation,
|
||||||
|
* YOU MUST CLEAR THE TEMPLATE CACHE YOURSELF when your templates change. Make it part of your build or deploy
|
||||||
|
* process so you don't forget!
|
||||||
|
*
|
||||||
|
* @throws Mustache_Exception_RuntimeException if $baseDir does not exist.
|
||||||
|
*
|
||||||
|
* @param string $baseDir Base directory containing Mustache template files.
|
||||||
|
* @param array $options Array of Loader options (default: array())
|
||||||
|
*/
|
||||||
|
public function __construct($baseDir, array $options = array())
|
||||||
|
{
|
||||||
|
parent::__construct($baseDir, $options);
|
||||||
|
|
||||||
|
if (array_key_exists('stat_props', $options)) {
|
||||||
|
if (empty($options['stat_props'])) {
|
||||||
|
$this->statProps = array();
|
||||||
|
} else {
|
||||||
|
$this->statProps = $options['stat_props'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->statProps = array('size', 'mtime');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for loading a Mustache file by name.
|
||||||
|
*
|
||||||
|
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return Mustache_Source Mustache Template source
|
||||||
|
*/
|
||||||
|
protected function loadFile($name)
|
||||||
|
{
|
||||||
|
$fileName = $this->getFileName($name);
|
||||||
|
|
||||||
|
if (!file_exists($fileName)) {
|
||||||
|
throw new Mustache_Exception_UnknownTemplateException($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Mustache_Source_FilesystemSource($fileName, $this->statProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Mustache.php.
|
||||||
|
*
|
||||||
|
* (c) 2010-2015 Justin Hileman
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mustache template Source interface.
|
||||||
|
*/
|
||||||
|
interface Mustache_Source
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the Source key (used to generate the compiled class name).
|
||||||
|
*
|
||||||
|
* This must return a distinct key for each template source. For example, an
|
||||||
|
* MD5 hash of the template contents would probably do the trick. The
|
||||||
|
* ProductionFilesystemLoader uses mtime and file path. If your production
|
||||||
|
* source directory is under version control, you could use the current Git
|
||||||
|
* rev and the file path...
|
||||||
|
*
|
||||||
|
* @throws RuntimeException when a source file cannot be read
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the template Source.
|
||||||
|
*
|
||||||
|
* @throws RuntimeException when a source file cannot be read
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSource();
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Mustache.php.
|
||||||
|
*
|
||||||
|
* (c) 2010-2015 Justin Hileman
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mustache template Filesystem Source.
|
||||||
|
*
|
||||||
|
* This template Source uses stat() to generate the Source key, so that using
|
||||||
|
* pre-compiled templates doesn't require hitting the disk to read the source.
|
||||||
|
* It is more suitable for production use, and is used by default in the
|
||||||
|
* ProductionFilesystemLoader.
|
||||||
|
*/
|
||||||
|
class Mustache_Source_FilesystemSource implements Mustache_Source
|
||||||
|
{
|
||||||
|
private $fileName;
|
||||||
|
private $statProps;
|
||||||
|
private $stat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filesystem Source constructor.
|
||||||
|
*
|
||||||
|
* @param string $fileName
|
||||||
|
* @param array $statProps
|
||||||
|
*/
|
||||||
|
public function __construct($fileName, array $statProps)
|
||||||
|
{
|
||||||
|
$this->fileName = $fileName;
|
||||||
|
$this->statProps = $statProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Source key (used to generate the compiled class name).
|
||||||
|
*
|
||||||
|
* @throws RuntimeException when a source file cannot be read
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getKey()
|
||||||
|
{
|
||||||
|
$chunks = array(
|
||||||
|
'fileName' => $this->fileName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!empty($this->statProps)) {
|
||||||
|
if (!isset($this->stat)) {
|
||||||
|
$this->stat = stat($this->fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->stat === false) {
|
||||||
|
throw new RuntimeException(sprintf('Failed to read source file "%s".', $this->fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->statProps as $prop) {
|
||||||
|
$chunks[$prop] = $this->stat[$prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_encode($chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the template Source.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSource()
|
||||||
|
{
|
||||||
|
return file_get_contents($this->fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -331,6 +331,15 @@ class Mustache_Test_EngineTest extends Mustache_Test_FunctionalTestCase
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCompileFromMustacheSourceInstance()
|
||||||
|
{
|
||||||
|
$baseDir = realpath(dirname(__FILE__) . '/../../fixtures/templates');
|
||||||
|
$mustache = new Mustache_Engine(array(
|
||||||
|
'loader' => new Mustache_Loader_ProductionFilesystemLoader($baseDir),
|
||||||
|
));
|
||||||
|
$this->assertEquals('one contents', $mustache->render('one'));
|
||||||
|
}
|
||||||
|
|
||||||
private function getLoggedMustache($level = Mustache_Logger::ERROR)
|
private function getLoggedMustache($level = Mustache_Logger::ERROR)
|
||||||
{
|
{
|
||||||
$name = tempnam(sys_get_temp_dir(), 'mustache-test');
|
$name = tempnam(sys_get_temp_dir(), 'mustache-test');
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Mustache.php.
|
||||||
|
*
|
||||||
|
* (c) 2010-2015 Justin Hileman
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group unit
|
||||||
|
*/
|
||||||
|
class Mustache_Test_Loader_ProductionFilesystemLoaderTest extends PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testConstructor()
|
||||||
|
{
|
||||||
|
$baseDir = realpath(dirname(__FILE__) . '/../../../fixtures/templates');
|
||||||
|
$loader = new Mustache_Loader_ProductionFilesystemLoader($baseDir, array('extension' => '.ms'));
|
||||||
|
$this->assertInstanceOf('Mustache_Source', $loader->load('alpha'));
|
||||||
|
$this->assertEquals('alpha contents', $loader->load('alpha')->getSource());
|
||||||
|
$this->assertInstanceOf('Mustache_Source', $loader->load('beta.ms'));
|
||||||
|
$this->assertEquals('beta contents', $loader->load('beta.ms')->getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTrailingSlashes()
|
||||||
|
{
|
||||||
|
$baseDir = dirname(__FILE__) . '/../../../fixtures/templates/';
|
||||||
|
$loader = new Mustache_Loader_ProductionFilesystemLoader($baseDir);
|
||||||
|
$this->assertEquals('one contents', $loader->load('one')->getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructorWithProtocol()
|
||||||
|
{
|
||||||
|
$baseDir = realpath(dirname(__FILE__) . '/../../../fixtures/templates');
|
||||||
|
|
||||||
|
$loader = new Mustache_Loader_ProductionFilesystemLoader('file://' . $baseDir, array('extension' => '.ms'));
|
||||||
|
$this->assertEquals('alpha contents', $loader->load('alpha')->getSource());
|
||||||
|
$this->assertEquals('beta contents', $loader->load('beta.ms')->getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLoadTemplates()
|
||||||
|
{
|
||||||
|
$baseDir = realpath(dirname(__FILE__) . '/../../../fixtures/templates');
|
||||||
|
$loader = new Mustache_Loader_ProductionFilesystemLoader($baseDir);
|
||||||
|
$this->assertEquals('one contents', $loader->load('one')->getSource());
|
||||||
|
$this->assertEquals('two contents', $loader->load('two.mustache')->getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEmptyExtensionString()
|
||||||
|
{
|
||||||
|
$baseDir = realpath(dirname(__FILE__) . '/../../../fixtures/templates');
|
||||||
|
|
||||||
|
$loader = new Mustache_Loader_ProductionFilesystemLoader($baseDir, array('extension' => ''));
|
||||||
|
$this->assertEquals('one contents', $loader->load('one.mustache')->getSource());
|
||||||
|
$this->assertEquals('alpha contents', $loader->load('alpha.ms')->getSource());
|
||||||
|
|
||||||
|
$loader = new Mustache_Loader_ProductionFilesystemLoader($baseDir, array('extension' => null));
|
||||||
|
$this->assertEquals('two contents', $loader->load('two.mustache')->getSource());
|
||||||
|
$this->assertEquals('beta contents', $loader->load('beta.ms')->getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Mustache_Exception_RuntimeException
|
||||||
|
*/
|
||||||
|
public function testMissingBaseDirThrowsException()
|
||||||
|
{
|
||||||
|
new Mustache_Loader_ProductionFilesystemLoader(dirname(__FILE__) . '/not_a_directory');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Mustache_Exception_UnknownTemplateException
|
||||||
|
*/
|
||||||
|
public function testMissingTemplateThrowsException()
|
||||||
|
{
|
||||||
|
$baseDir = realpath(dirname(__FILE__) . '/../../../fixtures/templates');
|
||||||
|
$loader = new Mustache_Loader_ProductionFilesystemLoader($baseDir);
|
||||||
|
|
||||||
|
$loader->load('fake');
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user