Merge pull request #237 from fenom-template/develop

2.11
This commit is contained in:
Ivan Shalganov 2016-06-09 22:46:27 +03:00 committed by GitHub
commit a3a84ea606
9 changed files with 786 additions and 397 deletions

View File

@ -10,7 +10,7 @@ php:
- 7.0 - 7.0
before_script: before_script:
- composer install --dev - composer update --dev
script: script:
- phpunit - phpunit

View File

@ -16,7 +16,7 @@
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "*", "phpunit/phpunit": "*",
"satooshi/php-coveralls": "dev-master" "satooshi/php-coveralls": "*"
}, },
"autoload": { "autoload": {
"psr-0": { "Fenom\\": "src/" }, "psr-0": { "Fenom\\": "src/" },

1036
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@
* For the full copyright and license information, please view the license.md * For the full copyright and license information, please view the license.md
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
use Fenom\Error\CompileException;
use Fenom\ProviderInterface; use Fenom\ProviderInterface;
use Fenom\Template; use Fenom\Template;
@ -844,16 +845,31 @@ class Fenom
} }
/** /**
* Add global accessor ($.) * Add global accessor as PHP code ($.)
* @param string $name * @param string $name
* @param mixed $accessor * @param mixed $accessor
* @param string $parser * @param string $parser
* @return Fenom * @return Fenom
*/ */
public function addAccessorSmart($name, $accessor, $parser) { public function addAccessorSmart($name, $accessor, $parser = self::ACCESSOR_VAR)
{
$this->_accessors[$name] = array( $this->_accessors[$name] = array(
"accessor" => $accessor, "accessor" => $accessor,
"parser" => $parser "parser" => $parser,
);
return $this;
}
/**
* Add global accessor handler as callback ($.X)
* @param string $name
* @param callable $callback
* @return Fenom
*/
public function addAccessorCallback($name, $callback)
{
$this->_accessors[$name] = array(
"callback" => $callback
); );
return $this; return $this;
} }
@ -872,11 +888,17 @@ class Fenom
/** /**
* Get an accessor * Get an accessor
* @param string $name * @param string $name
* @param string $key
* @return callable * @return callable
*/ */
public function getAccessor($name) { public function getAccessor($name, $key = null)
{
if(isset($this->_accessors[$name])) { if(isset($this->_accessors[$name])) {
return $this->_accessors[$name]; if($key) {
return $this->_accessors[$name][$key];
} else {
return $this->_accessors[$name];
}
} else { } else {
return false; return false;
} }
@ -888,7 +910,8 @@ class Fenom
* @param string $pattern * @param string $pattern
* @return $this * @return $this
*/ */
public function addCallFilter($pattern) { public function addCallFilter($pattern)
{
$this->call_filters[] = $pattern; $this->call_filters[] = $pattern;
return $this; return $this;
} }
@ -975,12 +998,7 @@ class Fenom
{ {
$options |= $this->_options; $options |= $this->_options;
if (is_array($template)) { if (is_array($template)) {
if(count($template) === 1) { $key = $options . "@" . implode(",", $template);
$template = current($template);
$key = $options . "@" . $template;
} else {
$key = $options . "@" . implode(",", $template);
}
} else { } else {
$key = $options . "@" . $template; $key = $options . "@" . $template;
} }
@ -1029,13 +1047,15 @@ class Fenom
*/ */
protected function _load($template, $opts) protected function _load($template, $opts)
{ {
$file_name = $this->_getCacheName($template, $opts); $file_name = $this->getCompileName($template, $opts);
if (is_file($this->_compile_dir . "/" . $file_name)) { if (is_file($this->_compile_dir . "/" . $file_name)) {
$fenom = $this; // used in template $fenom = $this; // used in template
$_tpl = include($this->_compile_dir . "/" . $file_name); $_tpl = include($this->_compile_dir . "/" . $file_name);
/* @var Fenom\Render $_tpl */ /* @var Fenom\Render $_tpl */
if (!($this->_options & self::AUTO_RELOAD) || ($this->_options & self::AUTO_RELOAD) && $_tpl->isValid()) { if (!($this->_options & self::AUTO_RELOAD) || ($this->_options & self::AUTO_RELOAD)
&& $_tpl instanceof Fenom\Render
&& $_tpl->isValid()) {
return $_tpl; return $_tpl;
} }
} }
@ -1045,12 +1065,13 @@ class Fenom
/** /**
* Generate unique name of compiled template * Generate unique name of compiled template
* *
* @param string $tpl * @param string|string[] $tpl
* @param int $options * @param int $options additional options
* @return string * @return string
*/ */
private function _getCacheName($tpl, $options) public function getCompileName($tpl, $options = 0)
{ {
$options = $this->_options | $options;
if (is_array($tpl)) { if (is_array($tpl)) {
$hash = implode(".", $tpl) . ":" . $options; $hash = implode(".", $tpl) . ":" . $options;
foreach ($tpl as &$t) { foreach ($tpl as &$t) {
@ -1069,12 +1090,11 @@ class Fenom
* @param string|array $tpl * @param string|array $tpl
* @param bool $store store template on disk * @param bool $store store template on disk
* @param int $options * @param int $options
* @throws RuntimeException * @throws CompileException
* @return \Fenom\Template * @return \Fenom\Template
*/ */
public function compile($tpl, $store = true, $options = 0) public function compile($tpl, $store = true, $options = 0)
{ {
$options = $this->_options | $options;
if (is_string($tpl)) { if (is_string($tpl)) {
$template = $this->getRawTemplate()->load($tpl); $template = $this->getRawTemplate()->load($tpl);
} else { } else {
@ -1084,17 +1104,15 @@ class Fenom
} }
} }
if ($store) { if ($store) {
$cache = $this->_getCacheName($tpl, $options); $cache_name = $this->getCompileName($tpl, $options);
$tpl_tmp = tempnam($this->_compile_dir, $cache); $compile_path = $this->_compile_dir . "/" . $cache_name . "." . mt_rand(0, 100000) . ".tmp";
$tpl_fp = fopen($tpl_tmp, "w"); if(!file_put_contents($compile_path, $template->getTemplateCode())) {
if (!$tpl_fp) { throw new CompileException("Can't to write to the file $compile_path. Directory " . $this->_compile_dir . " is writable?");
throw new \RuntimeException("Can't to open temporary file $tpl_tmp. Directory " . $this->_compile_dir . " is writable?");
} }
fwrite($tpl_fp, $template->getTemplateCode()); $cache_path = $this->_compile_dir . "/" . $cache_name;
fclose($tpl_fp); if (!rename($compile_path, $cache_path)) {
$file_name = $this->_compile_dir . "/" . $cache; unlink($compile_path);
if (!rename($tpl_tmp, $file_name)) { throw new CompileException("Can't to move the file $compile_path -> $cache_path");
throw new \RuntimeException("Can't to move $tpl_tmp to $file_name");
} }
} }
return $template; return $template;

View File

@ -964,7 +964,7 @@ class Compiler
return; return;
} }
$tokens->next(); $tokens->next();
if($tokens->is('(') || !$tokens->isNext(')')){ if ($tokens->is('(') || !$tokens->isNext(')')) {
$tokens->next(); $tokens->next();
while ($tokens->is(Tokenizer::MACRO_STRING, T_VARIABLE)) { while ($tokens->is(Tokenizer::MACRO_STRING, T_VARIABLE)) {
$param = $tokens->current(); $param = $tokens->current();

View File

@ -102,7 +102,7 @@ class Template extends Render
*/ */
private $_ignore = false; private $_ignore = false;
private $_before; private $_before = array();
private $_filters = array(); private $_filters = array();
@ -310,7 +310,7 @@ class Template extends Render
*/ */
public function before($code) public function before($code)
{ {
$this->_before .= $code; $this->_before[] = $code;
} }
/** /**
@ -405,7 +405,7 @@ class Template extends Render
*/ */
public function getTemplateCode() public function getTemplateCode()
{ {
$before = $this->_before ? $this->_before . "\n" : ""; $before = $this->_before ? implode("\n", $this->_before) . "\n" : "";
return "<?php \n" . return "<?php \n" .
"/** Fenom template '" . $this->_name . "' compiled at " . date('Y-m-d H:i:s') . " */\n" . "/** Fenom template '" . $this->_name . "' compiled at " . date('Y-m-d H:i:s') . " */\n" .
$before . // some code 'before' template $before . // some code 'before' template
@ -525,7 +525,7 @@ class Template extends Render
$parent = $this->_fenom->getRawTemplate()->load($tpl, false); $parent = $this->_fenom->getRawTemplate()->load($tpl, false);
$parent->blocks = & $this->blocks; $parent->blocks = & $this->blocks;
$parent->macros = & $this->macros; $parent->macros = & $this->macros;
$parent->_before = & $this->_before; $parent->_before = & $this->_before;
$parent->extended = $this->getName(); $parent->extended = $this->getName();
if (!$this->ext_stack) { if (!$this->ext_stack) {
$this->ext_stack[] = $this->getName(); $this->ext_stack[] = $this->getName();
@ -798,12 +798,14 @@ class Template extends Render
} }
$code = $this->parseScalar($tokens); $code = $this->parseScalar($tokens);
break; break;
/** @noinspection PhpMissingBreakStatementInspection */
case '$': case '$':
$code = $this->parseAccessor($tokens, $is_var); $code = $this->parseAccessor($tokens, $is_var);
if(!$is_var) { if(!$is_var) {
$code = $unary . $code; $code = $unary . $code;
break; break;
} }
/* no break */
case T_VARIABLE: case T_VARIABLE:
if(!isset($code)) { if(!isset($code)) {
$code = $this->parseVariable($tokens); $code = $this->parseVariable($tokens);
@ -1003,12 +1005,18 @@ class Template extends Render
$is_var = false; $is_var = false;
if($parser) { if($parser) {
if(is_array($parser)) { if(is_array($parser)) {
return call_user_func_array($parser['parser'], array($parser['accessor'], $tokens->next(), $this, &$is_var)); if(isset($parser['callback'])) {
$tokens->next();
return 'call_user_func($tpl->getStorage()->getAccessor('.var_export($accessor, true).
', "callback"), '.var_export($accessor, true).', $tpl, $var)';
} else {
return call_user_func_array($parser['parser'], array($parser['accessor'], $tokens->next(), $this, &$is_var));
}
} else { } else {
return call_user_func_array($parser, array($tokens->next(), $this, &$is_var)); return call_user_func_array($parser, array($tokens->next(), $this, &$is_var));
} }
} else { } else {
throw new \RuntimeException("Unknown accessor '$accessor'"); throw new \RuntimeException("Unknown accessor '\$.$accessor'");
} }
} }
@ -1025,7 +1033,7 @@ class Template extends Render
{ {
$empty = $tokens->is('?'); $empty = $tokens->is('?');
$tokens->next(); $tokens->next();
if ($tokens->is(":")) { if ($tokens->is(":", "?")) {
$tokens->next(); $tokens->next();
if ($empty) { if ($empty) {
if ($is_var) { if ($is_var) {

View File

@ -48,15 +48,20 @@ class TestCase extends \PHPUnit_Framework_TestCase
); );
} }
public function getCompilePath()
{
return FENOM_RESOURCES . '/compile';
}
public function setUp() public function setUp()
{ {
if (!file_exists(FENOM_RESOURCES . '/compile')) { if (!file_exists($this->getCompilePath())) {
mkdir(FENOM_RESOURCES . '/compile', 0777, true); mkdir($this->getCompilePath(), 0777, true);
} else { } else {
FS::clean(FENOM_RESOURCES . '/compile/'); FS::clean($this->getCompilePath());
} }
$this->fenom = Fenom::factory(FENOM_RESOURCES . '/' . $this->template_path, FENOM_RESOURCES . '/compile'); $this->fenom = Fenom::factory(FENOM_RESOURCES . '/' . $this->template_path, $this->getCompilePath());
$this->fenom->addProvider('persist', new Provider(FENOM_RESOURCES . '/provider')); $this->fenom->addProvider('persist', new Provider(FENOM_RESOURCES . '/provider'));
$this->fenom->addModifier('dots', __CLASS__ . '::dots'); $this->fenom->addModifier('dots', __CLASS__ . '::dots');
$this->fenom->addModifier('concat', __CLASS__ . '::concat'); $this->fenom->addModifier('concat', __CLASS__ . '::concat');

View File

@ -256,4 +256,21 @@ class AccessorTest extends TestCase
$this->fenom->addAccessorSmart($name, $accessor, $type); $this->fenom->addAccessorSmart($name, $accessor, $type);
$this->assertRender($code, $result, $this->getVars()); $this->assertRender($code, $result, $this->getVars());
} }
/**
* @group dev
*/
public function testCallbackAccessor() {
$index = 1;
$test = $this;
$this->fenom->addAccessorCallback('index', function($name, $template, $vars) use (&$index, $test) {
$test->assertInstanceOf('Fenom\Render', $template);
$test->assertSame(1, $vars['one']);
$test->assertSame('index', $name);
return $index++;
});
$this->assertRender('{$.index}, {$.index}, {$.index}', '1, 2, 3', $this->getVars());
}
} }

View File

@ -116,6 +116,19 @@ class FenomTest extends \Fenom\TestCase
$this->assertSame("Custom template (new)", $this->fenom->fetch('custom.tpl', array())); $this->assertSame("Custom template (new)", $this->fenom->fetch('custom.tpl', array()));
} }
/**
* @group dev
*/
public function testCompileIdAndName()
{
$this->fenom->setCompileId("iddqd.");
$this->tpl('custom.tpl', 'Custom template');
$this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array()));
$compile_name = $this->fenom->getCompileName('custom.tpl');
$this->assertFileExists($this->getCompilePath().'/'.$compile_name);
$this->assertStringStartsWith('iddqd.', $compile_name);
}
public function testSetModifier() public function testSetModifier()
{ {
$this->fenom->addModifier("mymod", "myMod"); $this->fenom->addModifier("mymod", "myMod");