-
Notifications
You must be signed in to change notification settings - Fork 178
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Major overhaul, with many breaking changes and new features.
Now possible to share data across templates. Now possible to preassign data to specific templates. Templates variables are now accessed without the $this pseudo-variable. Improvements to how extensions are registered. Now possible to add one-off template functions (without using an extension). New folder theme mode (missing folder templates will fall back to the default folder). New optional compiler adds automatic escaping and a cleaner template syntax.
- Loading branch information
Showing
13 changed files
with
793 additions
and
362 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
<?php | ||
|
||
namespace League\Plates\Compiler; | ||
|
||
/** | ||
* Optional compiler that enables automatic escaping and shorter template syntax. | ||
*/ | ||
class Compiler | ||
{ | ||
/** | ||
* Instance of the template engine. | ||
* @var League\Plates\Engine | ||
*/ | ||
private $engine; | ||
|
||
/** | ||
* Path to compiled template cache directory. | ||
* @var string | ||
*/ | ||
private $cacheDirectory; | ||
|
||
/** | ||
* Create new Compiler instance. | ||
* @param Engine $engine | ||
* @param string|null $cacheDirectory | ||
*/ | ||
public function __construct(\League\Plates\Engine $engine, $cacheDirectory = null) | ||
{ | ||
$this->engine = $engine; | ||
$this->setCacheDirectory($cacheDirectory); | ||
} | ||
|
||
/** | ||
* Set path to cache directory. | ||
* @param string|null $cacheDirectory Pass null to disable caching. | ||
* @return Compiler | ||
*/ | ||
public function setCacheDirectory($cacheDirectory) | ||
{ | ||
if (!is_null($cacheDirectory) and !is_string($cacheDirectory)) { | ||
throw new \LogicException( | ||
'The cache directory must be a string or null, ' . gettype($cacheDirectory) . ' given.' | ||
); | ||
} | ||
|
||
if (is_string($cacheDirectory) and !is_dir($cacheDirectory)) { | ||
throw new \LogicException('The specified cache directory "' . $cacheDirectory . '" does not exist.'); | ||
} | ||
|
||
$this->cacheDirectory = $cacheDirectory; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Compile template based on compiling flags. | ||
* @param string $name | ||
* @return string | ||
*/ | ||
public function compile($sourcePath) | ||
{ | ||
$destinationPath = $this->getCompiledPath($sourcePath); | ||
|
||
if ($this->isCacheExpired($sourcePath, $destinationPath)) { | ||
try { | ||
|
||
// Get source code from template | ||
$sourceCode = file_get_contents($sourcePath); | ||
|
||
// Convert short open tags "<?" to normal open tags "<?php" | ||
$sourceCode = preg_replace('/<\?(?!xml|php|=)/s', '<?php', $sourceCode); | ||
|
||
// Parse | ||
$parser = new \PhpParser\Parser(new \PhpParser\Lexer); | ||
$statements = $parser->parse($sourceCode); | ||
|
||
// Modify | ||
$traverser = new \PhpParser\NodeTraverser; | ||
$traverser->addVisitor( | ||
new NodeVisitor( | ||
$this->getTemplateFunctions(), | ||
$this->getRawTemplateFunctions() | ||
) | ||
); | ||
$statements = $traverser->traverse($statements); | ||
|
||
// Generate | ||
$prettyPrinter = new \PhpParser\PrettyPrinter\Standard; | ||
$sourceCode = '<?php ' . $prettyPrinter->prettyPrint($statements) . ' ?>'; | ||
|
||
// Save to disk | ||
file_put_contents($destinationPath, $sourceCode); | ||
} catch (\PhpParser\Error $e) { | ||
echo 'Parse Error: ', $e->getMessage(); | ||
} | ||
} | ||
|
||
return $destinationPath; | ||
} | ||
|
||
/** | ||
* Get the compiled template path based on the template name. | ||
* @param string $name | ||
* @return string | ||
*/ | ||
public function getCompiledPath($sourcePath) | ||
{ | ||
if ($this->cacheDirectory) { | ||
return $this->cacheDirectory . DIRECTORY_SEPARATOR . md5($sourcePath); | ||
} else { | ||
return tempnam(sys_get_temp_dir(), 'plates_'); | ||
} | ||
} | ||
|
||
/** | ||
* Determine if the template cache has expired. | ||
* @param string $sourcePath | ||
* @param string $destinationPath | ||
* @return bool | ||
*/ | ||
public function isCacheExpired($sourcePath, $destinationPath) | ||
{ | ||
if (is_null($this->cacheDirectory)) { | ||
return true; | ||
} | ||
|
||
if (!is_file($destinationPath)) { | ||
return true; | ||
} | ||
|
||
return filemtime($sourcePath) >= filemtime($destinationPath); | ||
} | ||
|
||
/** | ||
* Get array of all template function names. | ||
* @return array | ||
*/ | ||
public function getTemplateFunctions() | ||
{ | ||
return array_merge(array_keys($this->engine->getAllFunctions()), array('layout', 'start', 'stop', 'section')); | ||
} | ||
|
||
/** | ||
* Get array of all raw template function names. | ||
* @return array | ||
*/ | ||
public function getRawTemplateFunctions() | ||
{ | ||
$functions = array('section'); | ||
|
||
foreach ($this->engine->getAllFunctions() as $function) { | ||
if ($function->isRaw()) { | ||
$functions[] = $function->getName(); | ||
} | ||
} | ||
|
||
return $functions; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<?php | ||
|
||
namespace League\Plates\Compiler; | ||
|
||
class NodeVisitor extends \PhpParser\NodeVisitorAbstract | ||
{ | ||
private $functions; | ||
|
||
private $rawFunctions; | ||
|
||
public function __construct(array $functions = array(), array $rawFunctions = array()) | ||
{ | ||
$this->functions = $functions; | ||
$this->rawFunctions = $rawFunctions; | ||
} | ||
|
||
public function enterNode(\PhpParser\Node $node) | ||
{ | ||
if ($node instanceof \PhpParser\Node\Stmt\Echo_) { | ||
|
||
// Is the raw() function | ||
$isTheRawFunction = ( | ||
$node->exprs[0] instanceof \PhpParser\Node\Expr\FuncCall and | ||
$node->exprs[0]->name == 'raw' | ||
); | ||
|
||
// Is the raw() method | ||
$isTheRawMethod = ( | ||
$node->exprs[0] instanceof \PhpParser\Node\Expr\MethodCall and | ||
$node->exprs[0]->var->name == 'this' and | ||
$node->exprs[0]->name === 'raw' | ||
); | ||
|
||
// Is a "raw function" | ||
$isARawFunction = ( | ||
$node->exprs[0] instanceof \PhpParser\Node\Expr\FuncCall and | ||
in_array($node->exprs[0]->name, $this->rawFunctions) | ||
); | ||
|
||
// Is a "raw method" | ||
$isARawMethod = ( | ||
$node->exprs[0] instanceof \PhpParser\Node\Expr\MethodCall and | ||
$node->exprs[0]->var->name == 'this' and | ||
in_array($node->exprs[0]->name, $this->rawFunctions) | ||
); | ||
|
||
if ($isTheRawFunction or $isTheRawMethod) { | ||
|
||
// Remove raw() function/method | ||
$node->exprs = $node->exprs[0]->args; | ||
|
||
} elseif (!$isARawFunction and !$isARawMethod) { | ||
|
||
// Insert escape method | ||
$node->exprs = array( | ||
new \PhpParser\Node\Expr\MethodCall( | ||
new \PhpParser\Node\Expr\Variable('this'), | ||
new \PhpParser\Node\Name('escape'), | ||
$node->exprs | ||
) | ||
); | ||
} | ||
} | ||
} | ||
|
||
public function leaveNode(\PhpParser\Node $node) | ||
{ | ||
if ($node instanceof \PhpParser\Node\Expr\FuncCall and in_array($node->name, $this->functions)) { | ||
|
||
// Convert template functions to methods | ||
return new \PhpParser\Node\Expr\MethodCall( | ||
new \PhpParser\Node\Expr\Variable('this'), | ||
new \PhpParser\Node\Name($node->name), | ||
$node->args | ||
); | ||
} | ||
} | ||
} |
Oops, something went wrong.