From eb8479b3de039292292886ae5cca6321b09e2be5 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 8 Feb 2012 20:54:14 +0100 Subject: [PATCH] Added support for static includes --- .../template/TemplateCompiler.class.php | 15 ++- .../system/template/TemplateEngine.class.php | 35 +++--- .../TemplateScriptingCompiler.class.php | 105 ++++++++++++++++-- 3 files changed, 127 insertions(+), 28 deletions(-) diff --git a/wcfsetup/install/files/lib/system/template/TemplateCompiler.class.php b/wcfsetup/install/files/lib/system/template/TemplateCompiler.class.php index 568e9ec31d..6c4ecd20e9 100644 --- a/wcfsetup/install/files/lib/system/template/TemplateCompiler.class.php +++ b/wcfsetup/install/files/lib/system/template/TemplateCompiler.class.php @@ -34,9 +34,20 @@ class TemplateCompiler extends TemplateScriptingCompiler { $file->close(); // write meta data to file - $file = new File($metaData['filename']); + $this->saveMetaData($templateName, $metaData['filename'], $compiledContent['meta']); + } + + /** + * Saves meta data for given template. + * + * @param string $templateName + * @param string $filename + * @param string $content + */ + public function saveMetaData($templateName, $filename, $content) { + $file = new File($filename); $file->write("\n"); - $file->write(serialize($compiledContent['meta'])); + $file->write(serialize($content)); $file->close(); } diff --git a/wcfsetup/install/files/lib/system/template/TemplateEngine.class.php b/wcfsetup/install/files/lib/system/template/TemplateEngine.class.php index 01436f5774..2165c7aa32 100644 --- a/wcfsetup/install/files/lib/system/template/TemplateEngine.class.php +++ b/wcfsetup/install/files/lib/system/template/TemplateEngine.class.php @@ -304,12 +304,12 @@ class TemplateEngine extends SingletonFactory { $metaData = $this->getMetaData($templateName, $metaDataFilename); // check if compilation is necessary - if (!$this->isCompiled($templateName, $sourceFilename, $compiledFilename, $metaData)) { + if (($metaData === null) || !$this->isCompiled($templateName, $sourceFilename, $compiledFilename, $packageID, $metaData)) { // compile $this->compileTemplate($templateName, $sourceFilename, $compiledFilename, array( 'data' => $metaData, 'filename' => $metaDataFilename, - 'packageID' => $packageID + 'packageID' => $tplPackageID )); } @@ -417,11 +417,12 @@ class TemplateEngine extends SingletonFactory { * @param string $templateName * @param string $sourceFilename * @param string $compiledFilename + * @param integer $packageID * @param array $metaData * @return boolean $isCompiled */ - protected function isCompiled($templateName, $sourceFilename, $compiledFilename, array $metaData) { - if ($this->forceCompile || !file_exists($compiledFilename) || !file_exists($metaDataFilename)) { + protected function isCompiled($templateName, $sourceFilename, $compiledFilename, $packageID, array $metaData) { + if ($this->forceCompile || !file_exists($compiledFilename)) { return false; } else { @@ -434,8 +435,9 @@ class TemplateEngine extends SingletonFactory { else { // check for meta data if (!empty($metaData['include'])) { - foreach ($metaData['include'] as $template) { - $includedTemplateFilename = $this->getSourceFilename($template['templateName'], $packageID); + foreach ($metaData['include'] as $includedTemplate) { + $tplPackageID = $this->getPackageID($includedTemplate, $packageID); + $includedTemplateFilename = $this->getSourceFilename($includedTemplate, $tplPackageID); $includedMTime = @filemtime($includedTemplateFilename); if ($includedMTime >= $compileMTime) { @@ -468,16 +470,11 @@ class TemplateEngine extends SingletonFactory { * @param array $metaData */ protected function compileTemplate($templateName, $sourceFilename, $compiledFilename, array $metaData) { - // get compiler - if (!($this->compilerObj instanceof TemplateCompiler)) { - $this->compilerObj = $this->getCompiler(); - } - // get source $sourceContent = $this->getSourceContent($sourceFilename); // compile template - $this->compilerObj->compile($templateName, $sourceContent, $compiledFilename, $metaData); + $this->getCompiler()->compile($templateName, $sourceContent, $compiledFilename, $metaData); } /** @@ -485,8 +482,12 @@ class TemplateEngine extends SingletonFactory { * * @return wcf\system\template\TemplateCompiler */ - protected function getCompiler() { - return new TemplateCompiler($this); + public function getCompiler() { + if ($this->compilerObj === null) { + $this->compilerObj = new TemplateCompiler($this); + } + + return $this->compilerObj; } /** @@ -809,7 +810,9 @@ class TemplateEngine extends SingletonFactory { // find first newline $position = strpos($contents, "\n"); - if ($position === false) throw new SystemException("Unable to load meta data for template '".$templateName."'"); + if ($position === false) { + return null; + } // cut contents $contents = substr($contents, $position + 1); @@ -817,7 +820,7 @@ class TemplateEngine extends SingletonFactory { // read serializes data $data = @unserialize($contents); if ($data === false || !is_array($data)) { - throw new SystemException("Invalid meta data for template '".$templateName."'"); + return null; } return $data; diff --git a/wcfsetup/install/files/lib/system/template/TemplateScriptingCompiler.class.php b/wcfsetup/install/files/lib/system/template/TemplateScriptingCompiler.class.php index d71a8aa7e0..47bee243ec 100644 --- a/wcfsetup/install/files/lib/system/template/TemplateScriptingCompiler.class.php +++ b/wcfsetup/install/files/lib/system/template/TemplateScriptingCompiler.class.php @@ -167,6 +167,18 @@ class TemplateScriptingCompiler { */ protected $rdq; + /** + * list of static includes per template + * @var array + */ + protected $staticIncludes = array(); + + /** + * list of previously included namespaces + * @var array + */ + protected $includedNamespaces = array(); + /** * Creates a new TemplateScriptingCompiler object. * @@ -189,9 +201,24 @@ class TemplateScriptingCompiler { * @param string $identifier * @param string $sourceContent * @param array $metaData + * @param boolean $isolated * @return string */ - public function compileString($identifier, $sourceContent, array $metaData = array()) { + public function compileString($identifier, $sourceContent, array $metaData = array(), $isolated = false) { + if ($isolated) { + $previousData = array( + 'autoloadPlugins' => $this->autoloadPlugins, + 'currentIdentifier' => $this->currentIdentifier, + 'currentLineNo' => $this->currentLineNo, + 'literalStack' => $this->literalStack, + 'stringStack' => $this->stringStack, + 'tagStack' => $this->tagStack + ); + } + else { + $this->includedNamespaces = $this->staticIncludes = array(); + } + // reset vars $this->autoloadPlugins = $this->tagStack = $this->stringStack = $this->literalStack = array(); $this->currentIdentifier = $identifier; @@ -221,7 +248,7 @@ class TemplateScriptingCompiler { $compiledTags = array(); for ($i = 0, $j = count($templateTags); $i < $j; $i++) { $this->currentLineNo += StringUtil::countSubstring($textBlocks[$i], "\n"); - $compiledTags[] = $this->compileTag($templateTags[$i], $metaData); + $compiledTags[] = $this->compileTag($templateTags[$i], $identifier, $metaData); $this->currentLineNo += StringUtil::countSubstring($templateTags[$i], "\n"); } @@ -255,7 +282,14 @@ class TemplateScriptingCompiler { if (count($this->autoloadPlugins) > 0) { $compiledAutoloadPlugins = "autoloadPlugins as $className) { - $compiledAutoloadPlugins .= "use ".$className.";\n"; + // prevent multiple use on the same namespace + if (!in_array($className, $this->includedNamespaces)) { + // TODO: We're using the classes with prepended namespace, why should we first + // import them with "use" for no reason? + //$compiledAutoloadPlugins .= "use ".$className.";\n"; + $this->includedNamespaces[] = $className; + } + $compiledAutoloadPlugins .= "if (!isset(\$this->pluginObjects['$className'])) {\n"; $compiledAutoloadPlugins .= "\$this->pluginObjects['$className'] = new $className;\n"; $compiledAutoloadPlugins .= "}\n"; @@ -263,16 +297,32 @@ class TemplateScriptingCompiler { $compiledAutoloadPlugins .= "?>"; } - return $compiledAutoloadPlugins.$compiledContent; + // restore data + if ($isolated) { + $this->autoloadPlugins = $previousData['autoloadPlugins']; + $this->currentIdentifier = $previousData['currentIdentifier']; + $this->currentLineNo = $previousData['currentLineNo']; + $this->literalStack = $previousData['literalStack']; + $this->stringStack = $previousData['stringStack']; + $this->tagStack = $previousData['tagStack']; + } + + return array( + 'meta' => array( + 'include' => $this->staticIncludes + ), + 'template' => $compiledAutoloadPlugins.$compiledContent + ); } /** * Compiles a template tag. * * @param string $tag + * @param string $identifier * @param array $metaData */ - protected function compileTag($tag, array &$metaData) { + protected function compileTag($tag, $identifier, array &$metaData) { if (preg_match('~^'.$this->outputPattern.'~s', $tag)) { // variable output return $this->compileOutputTag($tag); @@ -319,7 +369,7 @@ class TemplateScriptingCompiler { return ''; case 'include': - return $this->compileIncludeTag($tagArgs, $metaData); + return $this->compileIncludeTag($tagArgs, $identifier, $metaData); case 'foreach': $this->pushTag('foreach'); @@ -676,10 +726,11 @@ class TemplateScriptingCompiler { * Compiles an include tag. * * @param string $includeTag + * @param string $identifier * @param array $metaData * @return string phpCode */ - protected function compileIncludeTag($includeTag, array &$metaData) { + protected function compileIncludeTag($includeTag, $identifier, array &$metaData) { $args = $this->parseTagArgs($includeTag, 'include'); $append = false; @@ -717,10 +768,44 @@ class TemplateScriptingCompiler { unset($args['once']); } + $templateName = substr($file, 1, -1); // check for static includes - if ($sandbox === false && $assignVar === false && $once === false) { - $content = WCF::getTPL()->fetch($file, array(), false, $metaData['packageID']); - return $metaData; + if ($sandbox === 'false' && $assignVar === false && $once === false) { + $phpCode = ''; + if (!in_array($templateName, $this->staticIncludes)) { + $this->staticIncludes[] = $templateName; + } + + // pass remaining tag args as variables + $variables = array(); + if (!empty($args)) { + foreach ($args as $variable => $value) { + if (substr($value, 0, 1) == "'") { + $phpCode .= "\$this->v['".$variable."'] = ".$value.";\n"; + } + else { + if (preg_match('~^\$this->v\[\'(.*)\'\]$~U', $value, $matches)) { + $phpCode .= "\$this->v['".$matches[1]."'] = ".$value.";\n"; + } + else { + throw new SystemException("Could not resolve variable type for value '".$value."'"); + } + } + } + } + if (!empty($phpCode)) $phpCode = ""; + + $tplPackageID = WCF::getTPL()->getPackageID($templateName, $metaData['packageID']); + $sourceFilename = WCF::getTPL()->getSourceFilename($templateName, $tplPackageID); + $metaDataFilename = WCF::getTPL()->getMetaDataFilename($templateName, $tplPackageID); + + $data = $this->compileString($identifier, file_get_contents($sourceFilename), array( + 'data' => null, + 'filename' => '', + 'packageID' => $tplPackageID + ), true); + + return $phpCode . $data['template']; } // make argument string -- 2.20.1