Hi
I know the reason for going away from templates is a speed issue, but consider this: If we "precompile" the template to php then from that point forward we use the "compiled" version of the php file we would be just as efficient (well the first ht would be slower but after that it would be fast)
I tried this by adding the following methods to CBaseController
public function getViewFileInternal($viewName, $viewFile) { if (constant('YII_DEBUG')===true) { // check to see if the file exists as a template if (is_file($viewFile.'.tpl')) { $fileOld = filemtime($viewFile.'.tpl'); $fileNew = 0; if (is_file($viewFile.'.php')) { $fileNew = filemtime($viewFile.'.php'); } if ($fileNew<$fileOld) { $this->generateFile($viewFile); } } } $viewFile .= '.php'; return is_file($viewFile) ? Yii::app()->findLocalizedFile($viewFile) : false; } const REGEX_RULES='/<(/?)com:(([w]+)(.[w]+)?)((?:s*[w.]+s*=s*'.*?'|s*[w.]+s*=s*".*?"|s*[w.]+s*=s*<%.*?%>)*)s*(/?)>/msS'; const REGEX_ATTRIBUTE_RULES='/(s*([w.]+)s*=s*"(.*?)")?/msS'; protected function generateFile($viewFile) { // Open a stream for creating the file into $template = file_get_contents($viewFile.".tpl"); // $xml = simplexml_load_string("<d>".$template."</d>"); $matches = array(); $n=preg_match_all(self::REGEX_RULES,$template,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE); $lastMatchStart = 0; $newTemplate=''; foreach($matches as $match) { $matchStart=$match[0][1]; $closure = $match[1][0]; $className = $match[3][0]; $methodName = $match[4][0]; $attributes = $match[5][0]; $endclosure = $match[6][0]; $attributeMap = false; if ($attributes) { // Map out the attributes $na=preg_match_all(self::REGEX_ATTRIBUTE_RULES,$attributes,$attributes,PREG_SET_ORDER|PREG_OFFSET_CAPTURE); $attributeMap = array(); foreach($attributes as $attribute) { $attributeMap[strtolower($attribute[2][0])] = $attribute[3][0]; } } $call = ''; if ($closure!=="/") { // Must be a static call if ($methodName) { $methodName = substr($methodName,1); $reflection = new ReflectionMethod ($className,$methodName); $params = $reflection->getParameters(); $call = "echo $className::$methodName( "; $ignoring = false; foreach($params as $param) { $name = strtolower($param->getName()); if (isset($attributeMap[$name])) { if ($ignoring!==false) { echo "Populated '$name' after optional '$ignoring' "; die(); } $call .=$attributeMap[$name] . ","; } else if ($param->isDefaultValueAvailable()) { $ignoring = $name; // $call .=$param->getDefaultValue() . ","; } else { echo "Missing required paramater $name "; die(); } } $call = substr($call,0,-1).")"; } $call = "<?" . "php $call ?".">"; } if ($endclosure==="/" || $closure==="/") { switch ($className) { case "CHtml" : $methodName = substr($methodName,1); switch ($methodName) { case "form" : $call .= "</form>"; default: break; } default: break; } } $newTemplate .= substr($template, $lastMatchStart, $matchStart-$lastMatchStart) . $call; $lastMatchStart=$matchStart+strlen($match[0][0]); } $newTemplate .= substr($template, $lastMatchStart); $file = fopen($viewFile.".php", "w"); fwrite($file,$newTemplate); fflush($file); fclose($file); }
Then I modified CWidget like (of course CController would also need to be modified)
public function getViewFile($viewName) { return $this->getViewFileInternal($viewName, $this->getViewPath().DIRECTORY_SEPARATOR.$viewName); }
The test case was the mainMenu view I renamed it to mainMenu.tpl and put in the following code
<ul> <?php foreach($items as $item): ?> <li> <com:CHtml.link Body="$item['label']" Url="$item['url']" htmlOptions="$item['active'] ? array('class'=>'active') : array()"/> </li> <?php endforeach; ?> </ul>
And then when I loaded the main page the application created a new file (in the same location as the template folder) with the following in it…
<ul> <?php foreach($items as $item): ?> <li> <?php echo CHtml::link( $item['label'],$item['url'],$item['active'] ? array('class'=>'active') : array()) ?> </li> <?php endforeach; ?> </ul>
Of course this should be extended to allow for any widget to be invoked through the "<com:" template but for now as a proof of concept it seems to work with CHtml objects.
The Pros
-
Most editors can now parse the document as XML (especially with "CHtml::form()")
-
Allows for editors to perform an auto complete operation on tags.
-
It really does not detract from performance except during that "first load"
-
Its more legible the using php directly to invoke widgets
The cons:
-
It does not save any typing
-
May be confusing to see a compiled version along with the template in the same folder
Thoughts ?
NZ