Feature to allow that changing the SSTemplateParser through the Injector system

The motivation for this was to allow module developers to change what parser is used to parse SilverStripe templates.
This change enables people to compile their own version of the SilverStripe template parser and use it without modifying core files.
This commit is contained in:
Cam Spiers 2013-10-17 21:28:12 +13:00
parent b6c16fcbf1
commit ac418ce99e
5 changed files with 102 additions and 39 deletions

View File

@ -634,6 +634,13 @@ class i18nTextCollector_Parser extends SSTemplateParser {
private static $currentEntity = array();
public function __construct($string) {
$this->string = $string;
$this->pos = 0;
$this->depth = 0;
$this->regexps = array();
}
public function Translate__construct(&$res) {
self::$currentEntity = array(null,null,null); //start with empty array
}

View File

@ -65,14 +65,20 @@ class SSTemplateParseException extends Exception {
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
* N: eats white space including newlines (using in legacy _t support)
*/
class SSTemplateParser extends Parser {
class SSTemplateParser extends Parser implements TemplateParser {
/**
* @var bool - Set true by SSTemplateParser::compileString if the template should include comments intended
* for debugging (template source, included files, etc)
*/
protected $includeDebuggingComments = false;
/**
* Override the Parser constructor to change the requirement of setting a string
*/
function __construct() {
}
/**
* Override the function that constructs the result arrays to also prepare a 'php' item in the array
*/
@ -1757,7 +1763,7 @@ class SSTemplateParser extends Parser {
/* CacheBlockArgument:
!( "if " | "unless " )
(
:DollarMarkedLookup |
:DollarMarkedLookup |
:QuotedString |
:Lookup
) */
@ -4548,7 +4554,8 @@ class SSTemplateParser extends Parser {
// non-dynamically calculated
$text = preg_replace(
'/href\s*\=\s*\"\#/',
'href="\' . (Config::inst()->get(\'SSViewer\', \'rewrite_hash_links\') ? strip_tags( $_SERVER[\'REQUEST_URI\'] ) : "") .
'href="\' . (Config::inst()->get(\'SSViewer\', \'rewrite_hash_links\') ?' .
' strip_tags( $_SERVER[\'REQUEST_URI\'] ) : "") .
\'#',
$text
);
@ -4563,29 +4570,28 @@ class SSTemplateParser extends Parser {
/**
* Compiles some passed template source code into the php code that will execute as per the template source.
*
* @static
* @throws SSTemplateParseException
* @param $string The source of the template
* @param string $templateName The name of the template, normally the filename the template source was loaded from
* @param bool $includeDebuggingComments True is debugging comments should be included in the output
* @return mixed|string The php that, when executed (via include or exec) will behave as per the template source
*/
static function compileString($string, $templateName = "", $includeDebuggingComments=false) {
public function compileString($string, $templateName = "", $includeDebuggingComments=false) {
if (!trim($string)) {
$code = '';
}
else {
// Construct a parser instance
$parser = new SSTemplateParser($string);
$parser->includeDebuggingComments = $includeDebuggingComments;
parent::__construct($string);
$this->includeDebuggingComments = $includeDebuggingComments;
// Ignore UTF8 BOM at begining of string. TODO: Confirm this is needed, make sure SSViewer handles UTF
// (and other encodings) properly
if(substr($string, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) $parser->pos = 3;
if(substr($string, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) $this->pos = 3;
// Match the source against the parser
$result = $parser->match_TopTemplate();
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $parser);
$result = $this->match_TopTemplate();
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $this);
// Get the result
$code = $result['php'];
@ -4593,7 +4599,7 @@ class SSTemplateParser extends Parser {
// Include top level debugging comments if desired
if($includeDebuggingComments && $templateName && stripos($code, "<?xml") === false) {
$code = $parser->includeDebuggingComments($code, $templateName);
$code = $this->includeDebuggingComments($code, $templateName);
}
return $code;
@ -4640,7 +4646,7 @@ class SSTemplateParser extends Parser {
* @param $template - A file path that contains template source code
* @return mixed|string - The php that, when executed (via include or exec) will behave as per the template source
*/
static function compileFile($template) {
return self::compileString(file_get_contents($template), $template);
public function compileFile($template) {
return $this->compileString(file_get_contents($template), $template);
}
}

View File

@ -86,14 +86,20 @@ class SSTemplateParseException extends Exception {
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
* N: eats white space including newlines (using in legacy _t support)
*/
class SSTemplateParser extends Parser {
class SSTemplateParser extends Parser implements TemplateParser {
/**
* @var bool - Set true by SSTemplateParser::compileString if the template should include comments intended
* for debugging (template source, included files, etc)
*/
protected $includeDebuggingComments = false;
/**
* Override the Parser constructor to change the requirement of setting a string
*/
function __construct() {
}
/**
* Override the function that constructs the result arrays to also prepare a 'php' item in the array
*/
@ -462,7 +468,7 @@ class SSTemplateParser extends Parser {
CacheBlockArgument:
!( "if " | "unless " )
(
:DollarMarkedLookup |
:DollarMarkedLookup |
:QuotedString |
:Lookup
)
@ -1018,29 +1024,28 @@ class SSTemplateParser extends Parser {
/**
* Compiles some passed template source code into the php code that will execute as per the template source.
*
* @static
* @throws SSTemplateParseException
* @param $string The source of the template
* @param string $templateName The name of the template, normally the filename the template source was loaded from
* @param bool $includeDebuggingComments True is debugging comments should be included in the output
* @return mixed|string The php that, when executed (via include or exec) will behave as per the template source
*/
static function compileString($string, $templateName = "", $includeDebuggingComments=false) {
public function compileString($string, $templateName = "", $includeDebuggingComments=false) {
if (!trim($string)) {
$code = '';
}
else {
// Construct a parser instance
$parser = new SSTemplateParser($string);
$parser->includeDebuggingComments = $includeDebuggingComments;
parent::__construct($string);
$this->includeDebuggingComments = $includeDebuggingComments;
// Ignore UTF8 BOM at begining of string. TODO: Confirm this is needed, make sure SSViewer handles UTF
// (and other encodings) properly
if(substr($string, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) $parser->pos = 3;
if(substr($string, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) $this->pos = 3;
// Match the source against the parser
$result = $parser->match_TopTemplate();
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $parser);
$result = $this->match_TopTemplate();
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $this);
// Get the result
$code = $result['php'];
@ -1048,7 +1053,7 @@ class SSTemplateParser extends Parser {
// Include top level debugging comments if desired
if($includeDebuggingComments && $templateName && stripos($code, "<?xml") === false) {
$code = $parser->includeDebuggingComments($code, $templateName);
$code = $this->includeDebuggingComments($code, $templateName);
}
return $code;
@ -1095,7 +1100,7 @@ class SSTemplateParser extends Parser {
* @param $template - A file path that contains template source code
* @return mixed|string - The php that, when executed (via include or exec) will behave as per the template source
*/
static function compileFile($template) {
return self::compileString(file_get_contents($template), $template);
public function compileFile($template) {
return $this->compileString(file_get_contents($template), $template);
}
}

View File

@ -614,6 +614,11 @@ class SSViewer {
*/
protected $includeRequirements = true;
/**
* @var TemplateParser
*/
protected $parser;
/**
* Create a template from a string instead of a .ss file
*
@ -691,7 +696,9 @@ class SSViewer {
* array('MySpecificPage', 'MyPage', 'Page')
* </code>
*/
public function __construct($templateList) {
public function __construct($templateList, TemplateParser $parser = null) {
$this->setParser($parser ?: Injector::inst()->get('SSTemplateParser'));
// flush template manifest cache if requested
if (isset($_GET['flush']) && $_GET['flush'] == 'all') {
if(Director::isDev() || Director::is_cli() || Permission::check('ADMIN')) {
@ -728,7 +735,25 @@ class SSViewer {
);
}
}
/**
* Set the template parser that will be used in template generation
* @param \TemplateParser $parser
*/
public function setParser(TemplateParser $parser)
{
$this->parser = $parser;
}
/**
* Returns the parser that is set for template generation
* @return \TemplateParser
*/
public function getParser()
{
return $this->parser;
}
/**
* Returns true if at least one of the listed templates exists.
*
@ -970,7 +995,7 @@ class SSViewer {
if(!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited || isset($_GET['flush'])) {
$content = file_get_contents($template);
$content = SSViewer::parseTemplateContent($content, $template);
$content = $this->parseTemplateContent($content, $template);
$fh = fopen($cacheFile,'w');
fwrite($fh, $content);
@ -983,7 +1008,7 @@ class SSViewer {
// through $Content and $Layout placeholders.
foreach(array('Content', 'Layout') as $subtemplate) {
if(isset($this->chosenTemplates[$subtemplate])) {
$subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate]);
$subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate], $this->parser);
$subtemplateViewer->includeRequirements(false);
$subtemplateViewer->setPartialCacheStore($this->getPartialCacheStore());
@ -1028,10 +1053,10 @@ class SSViewer {
return $v->process($data, $arguments, $scope);
}
public static function parseTemplateContent($content, $template="") {
return SSTemplateParser::compileString(
$content,
$template,
public function parseTemplateContent($content, $template="") {
return $this->parser->compileString(
$content,
$template,
Director::isDev() && Config::inst()->get('SSViewer', 'source_file_comments')
);
}
@ -1079,7 +1104,8 @@ class SSViewer {
class SSViewer_FromString extends SSViewer {
protected $content;
public function __construct($content) {
public function __construct($content, TemplateParser $parser = null) {
$this->setParser($parser ?: Injector::inst()->get('SSTemplateParser'));
$this->content = $content;
}
@ -1091,7 +1117,7 @@ class SSViewer_FromString extends SSViewer {
$arguments = null;
}
$template = SSViewer::parseTemplateContent($this->content, "string sha1=".sha1($this->content));
$template = $this->parseTemplateContent($this->content, "string sha1=".sha1($this->content));
$tmpFile = tempnam(TEMP_FOLDER,"");
$fh = fopen($tmpFile, 'w');

19
view/TemplateParser.php Normal file
View File

@ -0,0 +1,19 @@
<?php
/**
* This interface needs to be implemented by any template parser that is used in SSViewer
*
* @package framework
* @subpackage view
*/
interface TemplateParser {
/**
* Compiles some passed template source code into the php code that will execute as per the template source.
*
* @param $string The source of the template
* @param string $templateName The name of the template, normally the filename the template source was loaded from
* @param bool $includeDebuggingComments True is debugging comments should be included in the output
* @return mixed|string The php that, when executed (via include or exec) will behave as per the template source
*/
public function compileString($string, $templateName = "", $includeDebuggingComments = false);
}