From ac418ce99e3efe242615660a02f15baecfab5dfd Mon Sep 17 00:00:00 2001 From: Cam Spiers Date: Thu, 17 Oct 2013 21:28:12 +1300 Subject: [PATCH] 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. --- i18n/i18nTextCollector.php | 7 ++++++ view/SSTemplateParser.php | 36 +++++++++++++++------------ view/SSTemplateParser.php.inc | 33 ++++++++++++++----------- view/SSViewer.php | 46 +++++++++++++++++++++++++++-------- view/TemplateParser.php | 19 +++++++++++++++ 5 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 view/TemplateParser.php diff --git a/i18n/i18nTextCollector.php b/i18n/i18nTextCollector.php index eb21c30f5..73b855598 100644 --- a/i18n/i18nTextCollector.php +++ b/i18n/i18nTextCollector.php @@ -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 } diff --git a/view/SSTemplateParser.php b/view/SSTemplateParser.php index f516aa6b0..f08bd945b 100644 --- a/view/SSTemplateParser.php +++ b/view/SSTemplateParser.php @@ -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, "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); } } diff --git a/view/SSTemplateParser.php.inc b/view/SSTemplateParser.php.inc index e90860953..4a6c2e30f 100644 --- a/view/SSTemplateParser.php.inc +++ b/view/SSTemplateParser.php.inc @@ -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, "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); } } diff --git a/view/SSViewer.php b/view/SSViewer.php index 9a21febfe..b94e86382 100644 --- a/view/SSViewer.php +++ b/view/SSViewer.php @@ -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') * */ - 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'); diff --git a/view/TemplateParser.php b/view/TemplateParser.php new file mode 100644 index 000000000..a256ef007 --- /dev/null +++ b/view/TemplateParser.php @@ -0,0 +1,19 @@ +