diff --git a/src/Mustache/Loader/InlineLoader.php b/src/Mustache/Loader/InlineLoader.php new file mode 100644 index 0000000..835fbf1 --- /dev/null +++ b/src/Mustache/Loader/InlineLoader.php @@ -0,0 +1,121 @@ +load('hello'); + * $goodbye = $loader->load('goodbye'); + * + * __halt_compiler(); + * + * @@ hello + * Hello, {{ planet }}! + * + * @@ goodbye + * Goodbye, cruel {{ planet }} + * + * Templates are deliniated by lines containing only `@@ name`. + * + * The InlineLoader is well-suited to micro-frameworks such as Silex: + * + * $app->register(new MustacheServiceProvider, array( + * 'mustache.loader' => new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__) + * )); + * + * $app->get('/{name}', function() use ($app) { + * return $app['mustache']->render('hello', compact('name')); + * }) + * ->value('name', 'world'); + * + * __halt_compiler(); + * + * @@ hello + * Hello, {{ name }}! + * + */ +class Mustache_Loader_InlineLoader implements Mustache_Loader +{ + protected $fileName; + protected $offset; + protected $templates; + + /** + * The InlineLoader requires a filename and offset to process templates. + * The magic constants `__FILE__` and `__COMPILER_HALT_OFFSET__` are usually + * perfectly suited to the job: + * + * $loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__); + * + * Note that this only works if the loader is instantiated inside the same + * file as the inline templates. If the templates are located in another + * file, it would be necessary to manually specify the filename and offset. + * + * @param string $fileName The file to parse for inline templates + * @param int $offset A string offset for the start of the templates. + * This usually coincides with the `__halt_compiler` + * call, and the `__COMPILER_HALT_OFFSET__`. + */ + public function __construct($fileName, $offset) + { + if (!is_file($fileName)) { + throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid filename.'); + } + + if (!is_int($offset) || $offset < 0) { + throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid file offset.'); + } + + $this->fileName = $fileName; + $this->offset = $offset; + } + + /** + * Load a Template by name. + * + * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. + * + * @param string $name + * + * @return string Mustache Template source + */ + public function load($name) + { + $this->loadTemplates(); + + if (!array_key_exists($name, $this->templates)) { + throw new Mustache_Exception_UnknownTemplateException($name); + } + + return $this->templates[$name]; + } + + /** + * Parse and load templates from the end of a source file. + */ + protected function loadTemplates() + { + if ($this->templates === null) { + $this->templates = array(); + $data = file_get_contents($this->fileName, false, null, $this->offset); + foreach (preg_split("/^@@(?= [\w\d\.]+$)/m", $data, -1) as $chunk) { + if (trim($chunk)) { + list($name, $content) = explode("\n", $chunk, 2); + $this->templates[trim($name)] = trim($content); + } + } + } + } +} diff --git a/test/Mustache/Test/Loader/InlineLoaderTest.php b/test/Mustache/Test/Loader/InlineLoaderTest.php new file mode 100644 index 0000000..52a24bd --- /dev/null +++ b/test/Mustache/Test/Loader/InlineLoaderTest.php @@ -0,0 +1,56 @@ +assertEquals('{{ foo }}', $loader->load('foo')); + $this->assertEquals('{{#bar}}BAR{{/bar}}', $loader->load('bar')); + } + + /** + * @expectedException Mustache_Exception_UnknownTemplateException + */ + public function testMissingTemplatesThrowExceptions() + { + $loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__); + $loader->load('not_a_real_template'); + } + + /** + * @expectedException Mustache_Exception_InvalidArgumentException + */ + public function testInvalidOffsetThrowsException() + { + $loader = new Mustache_Loader_InlineLoader(__FILE__, 'notanumber'); + } + + /** + * @expectedException Mustache_Exception_InvalidArgumentException + */ + public function testInvalidFileThrowsException() + { + $loader = new Mustache_Loader_InlineLoader('notarealfile', __COMPILER_HALT_OFFSET__); + } +} + +__halt_compiler(); + +@@ foo +{{ foo }} + +@@ bar +{{#bar}}BAR{{/bar}}