This commit is contained in:
awers 2014-02-04 21:56:52 -08:00
commit e24a6de92c
7 changed files with 221 additions and 146 deletions

View File

@ -93,12 +93,6 @@ class Fenom
* @var Fenom\Render[] Templates storage
*/
protected $_storage = array();
/**
* @var string compile directory
*/
protected $_compile_dir = "/tmp";
/**
* @var int masked options
*/
@ -258,12 +252,11 @@ class Fenom
* Just factory
*
* @param string|Fenom\ProviderInterface $source path to templates or custom provider
* @param string $compile_dir path to compiled files
* @param int $options
* @throws InvalidArgumentException
* @return Fenom
*/
public static function factory($source, $compile_dir = '/tmp', $options = 0)
public static function factory($source, $options = 0)
{
if (is_string($source)) {
$provider = new Fenom\Provider($source);
@ -272,9 +265,8 @@ class Fenom
} else {
throw new InvalidArgumentException("Source must be a valid path or provider object");
}
$fenom = new static($provider);
/* @var Fenom $fenom */
$fenom->setCompileDir($compile_dir);
$fenom = new static($provider);
if ($options) {
$fenom->setOptions($options);
}
@ -286,25 +278,10 @@ class Fenom
*/
public function __construct(Fenom\ProviderInterface $provider)
{
$provider->setFenom($this);
$this->_provider = $provider;
}
/**
* Set compile directory
*
* @param string $dir directory to store compiled templates in
* @throws LogicException
* @return Fenom
*/
public function setCompileDir($dir)
{
if(!is_writable($dir)) {
throw new LogicException("Cache directory $dir is not writable");
}
$this->_compile_dir = $dir;
return $this;
}
/**
*
* @param callable $cb
@ -624,6 +601,7 @@ class Fenom
*/
public function addProvider($scm, \Fenom\ProviderInterface $provider)
{
$provider->setFenom($this);
$this->_providers[$scm] = $provider;
return $this;
}
@ -735,14 +713,29 @@ class Fenom
/** @var Fenom\Template $tpl */
$tpl = $this->_storage[$key];
if (($this->_options & self::AUTO_RELOAD) && !$tpl->isValid()) {
return $this->_storage[$key] = $this->compile($template, true, $options);
return $this->_storage[$key] = $this->getProviderByTemplate($template)->compile($template, true, $options);
} else {
return $tpl;
}
} elseif ($this->_options & self::FORCE_COMPILE) {
return $this->compile($template, $this->_options & self::DISABLE_CACHE & ~self::FORCE_COMPILE, $options);
return $this->getProviderByTemplate($template)->compile($template, $this->_options & self::DISABLE_CACHE & ~self::FORCE_COMPILE, $options);
} else {
return $this->_storage[$key] = $this->_load($template, $options);
return $this->_storage[$key] = $this->getProviderByTemplate($template)->load($template, $options);
}
}
/**
* @param $template
* @return ProviderInterface
*/
public function getProviderByTemplate($template)
{
if ($provider = strstr($template, ":", true)) {
if(isset($this->_providers[$provider])) {
return $this->_providers[$provider];
}
} else {
return $this->_provider;
}
}
@ -754,77 +747,12 @@ class Fenom
public function templateExists($template)
{
if ($provider = strstr($template, ":", true)) {
if (isset($this->_providers[$provider])) {
if(isset($this->_providers[$provider])) {
return $this->_providers[$provider]->templateExists(substr($template, strlen($provider) + 1));
}
} else {
return $this->_provider->templateExists($template);
}
return false;
}
/**
* Load template from cache or create cache if it doesn't exists.
*
* @param string $tpl
* @param int $opts
* @return Fenom\Render
*/
protected function _load($tpl, $opts)
{
$file_name = $this->_getCacheName($tpl, $opts);
if (is_file($this->_compile_dir . "/" . $file_name)) {
$fenom = $this; // used in template
$_tpl = include($this->_compile_dir . "/" . $file_name);
/* @var Fenom\Render $_tpl */
if (!($this->_options & self::AUTO_RELOAD) || ($this->_options & self::AUTO_RELOAD) && $_tpl->isValid()) {
return $_tpl;
}
}
return $this->compile($tpl, true, $opts);
}
/**
* Generate unique name of compiled template
*
* @param string $tpl
* @param int $options
* @return string
*/
private function _getCacheName($tpl, $options)
{
$hash = $tpl . ":" . $options;
return sprintf("%s.%x.%x.php", str_replace(":", "_", basename($tpl)), crc32($hash), strlen($hash));
}
/**
* Compile and save template
*
* @param string $tpl
* @param bool $store store template on disk
* @param int $options
* @throws RuntimeException
* @return \Fenom\Template
*/
public function compile($tpl, $store = true, $options = 0)
{
$options = $this->_options | $options;
$template = $this->getRawTemplate()->load($tpl);
if ($store) {
$cache = $this->_getCacheName($tpl, $options);
$tpl_tmp = tempnam($this->_compile_dir, $cache);
$tpl_fp = fopen($tpl_tmp, "w");
if (!$tpl_fp) {
throw new \RuntimeException("Can't to open temporary file $tpl_tmp. Directory " . $this->_compile_dir . " is writable?");
}
fwrite($tpl_fp, $template->getTemplateCode());
fclose($tpl_fp);
$file_name = $this->_compile_dir . "/" . $cache;
if (!rename($tpl_tmp, $file_name)) {
throw new \RuntimeException("Can't to move $tpl_tmp to $file_name");
}
}
return $template;
}
/**
@ -840,7 +768,25 @@ class Fenom
*/
public function clearAllCompiles()
{
\Fenom\Provider::clean($this->_compile_dir);
$this->_provider->clearCompiles();
foreach($this->_providers as $provider)
{
$provider->clearCompiles();
}
}
/**
* Compile and save template
*
* @param string $tpl
* @param bool $store store template on disk
* @param int $options
* @throws \RuntimeException
* @return \Fenom\Template
*/
public function compile($tpl, $store = true, $options = 0)
{
return $this->getProviderByTemplate($tpl)->compile($tpl, $store, $options);
}
/**

View File

@ -17,62 +17,46 @@ use Fenom\ProviderInterface;
*/
class Provider implements ProviderInterface
{
/**
* Templates source path
* @var string
*/
private $_path;
/**
* Clean directory from files
*
* @param string $path
* Fenom engine object
* @var \Fenom $_fenom
*/
public static function clean($path)
{
if (is_file($path)) {
unlink($path);
} elseif (is_dir($path)) {
$iterator = iterator_to_array(
new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path,
\FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
)
);
foreach ($iterator as $file) {
/* @var \splFileInfo $file */
if ($file->isFile()) {
if (strpos($file->getBasename(), ".") !== 0) {
unlink($file->getRealPath());
}
} elseif ($file->isDir()) {
rmdir($file->getRealPath());
}
}
}
}
private $_fenom;
/**
* Recursive remove directory
*
* @param string $path
* Templates compile_dir
* @var string compile directory
*/
public static function rm($path)
{
self::clean($path);
if (is_dir($path)) {
rmdir($path);
}
}
protected $_compile_dir = "/tmp";
/**
* @param string $template_dir directory of templates
* @param string $compile_dir directory of compiled templates
* @throws \LogicException if directory doesn't exists
*/
public function __construct($template_dir)
public function __construct($template_dir, $compile_dir = null)
{
if ($_dir = realpath($template_dir)) {
$this->_path = $_dir;
} else {
throw new \LogicException("Template directory {$template_dir} doesn't exists");
}
if(!is_null($compile_dir))
{
$this->setCompileDir($compile_dir);
}
}
/**
* @param \Fenom $fenom
*/
public function setFenom(\Fenom $fenom)
{
$this->_fenom = $fenom;
}
/**
@ -167,4 +151,113 @@ class Provider implements ProviderInterface
}
return true;
}
/**
* Set compile directory
*
* @param string $dir directory to store compiled templates in
* @throws \LogicException
* @return \Fenom
*/
public function setCompileDir($dir)
{
if (!is_writable($dir)) {
throw new \LogicException("Cache directory $dir is not writable");
}
$this->_compile_dir = $dir;
return $this;
}
/**
* Generate unique name of compiled template
*
* @param string $tpl
* @param int $options
* @return string
*/
private function _getCacheName($tpl, $options)
{
$hash = $tpl . ":" . $options;
return sprintf("%s.%x.%x.php", str_replace(":", "_", basename($tpl)), crc32($hash), strlen($hash));
}
/**
* Compile and save template
*
* @param string $tpl
* @param bool $store store template on disk
* @param int $options
* @throws \RuntimeException
* @return \Fenom\Template
*/
public function compile($tpl, $store = true, $options = 0)
{
$options = $this->_fenom->getOptions() | $options;
$template = $this->_fenom->getRawTemplate()->load($tpl);
if ($store) {
$cache = $this->_getCacheName($tpl, $options);
$tpl_tmp = tempnam($this->_compile_dir, $cache);
$tpl_fp = fopen($tpl_tmp, "w");
if (!$tpl_fp) {
throw new \RuntimeException("Can't to open temporary file $tpl_tmp. Directory " . $this->_compile_dir . " is writable?");
}
fwrite($tpl_fp, $template->getTemplateCode());
fclose($tpl_fp);
$file_name = $this->_compile_dir . "/" . $cache;
if (!rename($tpl_tmp, $file_name)) {
throw new \RuntimeException("Can't to move $tpl_tmp to $file_name");
}
}
return $template;
}
/**
* Load template from cache or create cache if it doesn't exists.
*
* @param string $tpl
* @param int $opts
* @return \Fenom\Render
*/
public function load($tpl, $opts)
{
$file_name = $this->_getCacheName($tpl, $opts);
if (is_file($this->_compile_dir . "/" . $file_name)) {
$fenom = $this->_fenom; // used in template
$_tpl = include($this->_compile_dir . "/" . $file_name);
/* @var \Fenom\Render $_tpl */
if (!($this->_fenom->getOptions() & \Fenom::AUTO_RELOAD) || ($this->_fenom->getOptions() & \Fenom::AUTO_RELOAD) && $_tpl->isValid()) {
return $_tpl;
}
}
return $this->compile($tpl, true, $opts);
}
/**
* Clear compiles cache
*/
public function clearCompiles()
{
if (is_file($this->_compile_dir)) {
unlink($this->_compile_dir);
} elseif (is_dir($this->_compile_dir)) {
$iterator = iterator_to_array(
new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->_compile_dir,
\FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
)
);
foreach ($iterator as $file) {
/* @var \splFileInfo $file */
if ($file->isFile()) {
if (strpos($file->getBasename(), ".") !== 0) {
unlink($file->getRealPath());
}
} elseif ($file->isDir()) {
rmdir($file->getRealPath());
}
}
}
}
}

View File

@ -48,4 +48,39 @@ interface ProviderInterface
* @return array|\Iterator
*/
public function getList();
/**
* Clear provider compiles
*
* @return mixed
*/
public function clearCompiles();
/**
* Compile and save template
*
* @param string $tpl
* @param bool $store store template on disk
* @param int $options
* @throws \RuntimeException
* @return \Fenom\Template
*/
public function compile($tpl, $store = true, $options = 0);
/**
* Load template from cache or create cache if it doesn't exists.
*
* @param string $tpl
* @param int $opts
* @return \Fenom\Render
*/
public function load($tpl, $opts);
/**
* Set Fenom engine
*
* @param \Fenom $fenom
* @return mixed
*/
public function setFenom(\Fenom $fenom);
}

View File

@ -50,10 +50,11 @@ class TestCase extends \PHPUnit_Framework_TestCase
if (!file_exists(FENOM_RESOURCES . '/compile')) {
mkdir(FENOM_RESOURCES . '/compile', 0777, true);
} else {
FS::clean(FENOM_RESOURCES . '/compile/');
// FS::clean(FENOM_RESOURCES . '/compile/');
}
$this->fenom = Fenom::factory(FENOM_RESOURCES . '/template', FENOM_RESOURCES . '/compile');
$this->fenom = new Fenom(new Provider(FENOM_RESOURCES . '/template', FENOM_RESOURCES . '/compile'));
$this->fenom->getProvider()->clearCompiles();
$this->fenom->addModifier('dots', __CLASS__ . '::dots');
$this->fenom->addModifier('concat', __CLASS__ . '::concat');
$this->fenom->addModifier('append', __CLASS__ . '::append');
@ -92,7 +93,7 @@ class TestCase extends \PHPUnit_Framework_TestCase
if (!file_exists(FENOM_RESOURCES . '/template')) {
mkdir(FENOM_RESOURCES . '/template', 0777, true);
} else {
FS::clean(FENOM_RESOURCES . '/template/');
// FS::clean(FENOM_RESOURCES . '/template/');
}
}

View File

@ -7,7 +7,7 @@ class ExtendsTemplateTest extends TestCase
public function _testSandbox()
{
$this->fenom = Fenom::factory(FENOM_RESOURCES . '/provider', FENOM_RESOURCES . '/compile');
$this->fenom = new Fenom(new Provider(FENOM_RESOURCES . '/provider', FENOM_RESOURCES . '/compile'));
try {
print_r($this->fenom->getTemplate('use/child.tpl')->getBody());
} catch (\Exception $e) {
@ -145,7 +145,7 @@ class ExtendsTemplateTest extends TestCase
*/
public function testUse()
{
$this->fenom = Fenom::factory(FENOM_RESOURCES . '/provider', FENOM_RESOURCES . '/compile');
$this->fenom = new Fenom(new Provider(FENOM_RESOURCES . '/provider', FENOM_RESOURCES . '/compile'));
$this->assertSame("<html>\n block 1 blocks \n block 2 child \n</html>", $this->fenom->fetch('use/child.tpl'));
}

View File

@ -16,7 +16,7 @@ class ProviderTest extends TestCase
$this->tpl("template1.tpl", 'Template 1 {$a}');
$this->tpl("template2.tpl", 'Template 2 {$a}');
$this->tpl("sub/template3.tpl", 'Template 3 {$a}');
$this->provider = new Provider(FENOM_RESOURCES . '/template');
$this->provider = new Provider(FENOM_RESOURCES . '/template', FENOM_RESOURCES . '/compile');
clearstatcache();
}
@ -92,6 +92,7 @@ class ProviderTest extends TestCase
$this->assertFalse($this->provider->verify($templates));
}
/* @TODO: Why do we need that tests?
public function testGetAll()
{
$list = $this->provider->getList();
@ -113,6 +114,6 @@ class ProviderTest extends TestCase
$this->assertTrue(is_file(FENOM_RESOURCES . '/template/template2.tpl'));
Provider::clean(FENOM_RESOURCES . '/template/');
$this->assertFalse(is_file(FENOM_RESOURCES . '/template/template2.tpl'));
}
}*/
}

View File

@ -22,8 +22,7 @@ class FenomTest extends \Fenom\TestCase
public function testCreating() {
$time = $this->tpl('temp.tpl', 'Template 1 a');
$fenom = new Fenom($provider = new \Fenom\Provider(FENOM_RESOURCES . '/template'));
$fenom->setCompileDir(FENOM_RESOURCES . '/compile');
$fenom = new Fenom($provider = new \Fenom\Provider(FENOM_RESOURCES . '/template', FENOM_RESOURCES . '/compile'));
$this->assertInstanceOf('Fenom\Template', $tpl = $fenom->getTemplate('temp.tpl'));
$this->assertSame($provider, $tpl->getProvider());
$this->assertSame('temp.tpl', $tpl->getBaseName());
@ -34,7 +33,7 @@ class FenomTest extends \Fenom\TestCase
public function testFactory() {
$time = $this->tpl('temp.tpl', 'Template 1 a');
$fenom = Fenom::factory($provider = new \Fenom\Provider(FENOM_RESOURCES . '/template'), FENOM_RESOURCES . '/compile', Fenom::AUTO_ESCAPE);
$fenom = Fenom::factory($provider = new \Fenom\Provider(FENOM_RESOURCES . '/template', FENOM_RESOURCES . '/compile'), Fenom::AUTO_ESCAPE);
$this->assertInstanceOf('Fenom\Template', $tpl = $fenom->getTemplate('temp.tpl'));
$this->assertSame($provider, $tpl->getProvider());
$this->assertSame('temp.tpl', $tpl->getBaseName());