$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
));
}
* @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 {
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) {
* @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);
}
/**
*
* @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;
}
/**
// 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);
// 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;
*/
protected $rdq;
+ /**
+ * list of static includes per template
+ * @var array<string>
+ */
+ protected $staticIncludes = array();
+
+ /**
+ * list of previously included namespaces
+ * @var array<string>
+ */
+ protected $includedNamespaces = array();
+
/**
* Creates a new TemplateScriptingCompiler object.
*
* @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;
$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");
}
if (count($this->autoloadPlugins) > 0) {
$compiledAutoloadPlugins = "<?php\n";
foreach ($this->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";
$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);
return '<?php } ?>';
case 'include':
- return $this->compileIncludeTag($tagArgs, $metaData);
+ return $this->compileIncludeTag($tagArgs, $identifier, $metaData);
case 'foreach':
$this->pushTag('foreach');
* 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;
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 = "<?php\n".$phpCode."\n?>";
+
+ $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