1 & 2 Sounds good to me, item 3 is what would normally occur in 90% of the cases, except for the example I have shown. The unassigned attribute "body" (CHtml::link) is a special case in which I allow the helper to capture the output of its body as a passed in value. So if you want to use an image for a link you could do something like
I think the parser should not handle special cases (e.g. CHtml) inside. Make it as general as possible. If we want something like: <helper:CHtml:link …><helper:CHtml:image …/></helper:CHtml:link>, we should create image widget and use <com:> tag to accomplish the goal.
<h2>
Welcome, <?php echo Yii::app()->user->username; ?>!
</h2>
<p>
This is the homepage of <em><?php echo Yii::app()->name; ?></em>. You may modify the following files to customize the conent of this page:
</p>
<dl>
<dt><?php echo Yii::app()->controllerPath . DIRECTORY_SEPARATOR . 'SiteController.php'; ?></dt>
<dd>This file contains the <tt>SiteController</tt> class which is
the default application controller. Its default <tt>index</tt> action
renders the content of the following two files.
</dd>
<dt><?php echo __FILE__; ?></dt>
<dd>This is the view file that contains the body content of this page.</dd>
<dt><?php echo Yii::app()->layoutPath . DIRECTORY_SEPARATOR . 'main.php'; ?></dt>
<dd>This is the layout file that contains common presentation (such as header, footer) shared by all view files.</dd>
</dl>
<com:CCaptcha ButtonType="link" ShowRefreshButton="{true}">
<br />
</com:CCaptcha>
<com:CMultiFileUpload Name="mup"/>
Output code
<h2>
Welcome, <?php /** Line:2-2*/ echo Yii::app()->user->username; ?>!
</h2>
<p>
This is the homepage of <em><?php /** Line:5-5*/ echo Yii::app()->name; ?></em>. You may modify the following files to customize the conent of this page:
</p>
<dl>
<dt><?php /** Line:8-8*/ echo Yii::app()->controllerPath . DIRECTORY_SEPARATOR . 'SiteController.php'; ?></dt>
<dd>This file contains the <tt>SiteController</tt> class which is
the default application controller. Its default <tt>index</tt> action
renders the content of the following two files.
</dd>
<dt><?php /** Line:13-13*/ echo __FILE__; ?></dt>
<dd>This is the view file that contains the body content of this page.</dd>
<dt><?php /** Line:15-15*/ echo Yii::app()->layoutPath . DIRECTORY_SEPARATOR . 'main.php'; ?></dt>
<dd>This is the layout file that contains common presentation (such as header, footer) shared by all view files.</dd>
</dl>
<?php /** Line:20-20*/ $this->beginWidget('CCaptcha',array( "buttonType"=>'link',"showRefreshButton"=>true,)); ?>
<br />
<?php /** Line:22-22*/ $this->endWidget('CCaptcha'); ?>
<?php /** Line:23-23*/ $this->beginWidget('CMultiFileUpload',array( "name"=>'mup',)); $this->endWidget('CMultiFileUpload'); ?>
(For widgets the first letter of the attribute name is lower cased automatically)
The widget code looks good to me. Maybe <com:Widget /> can be translated to <?php $this->widget('Widget'); ?> instead of (beginWidget, endWidget) pair?
Also, maybe support translation from <%= expr; %> to <?php echo expr; ?> ?
Really nice work! Can I take a look at how your code about renderFile() method? Do not include the parsing part.
The <%= %> are already there but my sample did not include them, Ill add in the widget thing…
here is the renderFile methods, is the implementation about right ?
/**
* Called by the context to render the file,
* calls the getViewFileInternal to interrupt the working file
*/
public function renderFile($context,$file,$data,$return) {
// Call the context to render the template file
return $context->renderInternal($this->getViewFileInternal($file),$data,$return);
}
/**
* Looks for the view file in the work folder.
* it is assumed the $viewFile points to a valid file.
*
* @param $viewFile the template file
* @param $chmod The folder attributes to set if folders need to be created
* @param $force True if you want to force the recompile of the view
*
* @return returns a file path to the "working" file.
*/
public function getViewFileInternal($viewFile, $chmod=755, $force=false)
{
// Convert the view file to the work dir folder
$newFilePath = $this->_workPath . substr($viewFile,$this->_basePathLen);
if (!is_file($newFilePath) ||
constant('YII_DEBUG')===true ||
$force) {
// Check to see if directory exists
preg_match('`^(.+)[/\\]([a-zA-Z0-9]+.[a-z]+)$`i', $newFilePath, $matches);
$directory = $matches[1];
if (!is_dir($directory)){
if (!mkdir($directory, $chmod, 1)){
return FALSE;
}
}
// If in debug mode check the dates on the file
if (constant('YII_DEBUG')===true && is_file($newFilePath)) {
if (filemtime($viewFile)>filemtime($newFilePath)) {
// Call the parser to generate the file
$this->generateFile($viewFile,$newFilePath);
}
}
else {
// Call the parser to generate the file
$this->generateFile($viewFile,$newFilePath);
}
}
// Return the working file path
return $newFilePath;
}
Yeah, that's what I expected, but a slight difference.
I was thinking that the generated files should be put under the runtime directory since it is already configured to be writable. The directory structure under the runtime should look like the following
where '23ac234ef' and '423aef3ec' are the CRC encoding of the directory part of the corresponding view files. For example, if we are rendering 'view1' of SiteController, then the CRC path would be generated using:
sprintf('%x',crc32(dirname($sourceViewFilePath)))
I also realized that using this parsing approach, we changed the value of FILE. So it needs to be careful. Maybe a substitution of FILE, too?
Okay I modified the code to default the work path to the runtime/views folder
public function getViewFileInternal($viewFile, $chmod=755, $force=false)
{
// Convert the view file to the work dir folder
$pathName = dirname($viewFile);
$fileName = basename($viewFile);
$newFilePath = $this->_workPath . DIRECTORY_SEPARATOR .sprintf('%x', crc32($pathName)) .
DIRECTORY_SEPARATOR . $fileName;
if (!is_file($newFilePath) ||
I also replaced the FILE constant as I rendered the php code on the template, and I added a Visibility attribute to components or helpers
I was thinking of adding a couple of more tags like <view: and <cache: and have them call the beginContent/endContent and beginCache/endCache but after looking at the CBaseController I see those method calls simply pass the calls to the beginWidget / endWidget methods. So (if I added those two extra tags) should the generated code invoke the widgets directly or should I still call methods in the CBaseController ?
yeah, adding some new tags seems to be a great idea.
I would suggest using <yii:cache>, <yii:content> and <yii:clip>. You should call the corresponding CBaseController methods instead of widget methods. Also, pay attention to the <yii:cache> tag as its usage involves an "if" statement. It would be great the parser can automatically insert the if statement.
A quick question: how do you handle quote escaping? For example, if a property value contains both single and double quotes, will you parser handle this?
<h2>
Welcome, <%= Yii::app()->user->name; %>!
</h2>
<p>
This is the homepage of <em><%= Yii::app()->name; %></em>. You may modify the following files to customize the conent of this page:
</p>
<yii:cache id="MainCache">
Cached data
</yii:cache>
<dl>
<dt><%= Yii::app()->controllerPath . DIRECTORY_SEPARATOR . 'SiteController.php'; %></dt>
<dd>This file contains the <tt>SiteController</tt> class which is
the default application controller. Its default <tt>index</tt> action
renders the content of the following two files.
</dd>
<dt><%= __FILE__ %></dt>
<dd>This is the view file that contains the body content of this page.</dd>
<dt><%= Yii::app()->layoutPath . DIRECTORY_SEPARATOR . 'main.php'; %></dt>
<dd>This is the layout file that contains common presentation (such as header, footer) shared by all view files.</dd>
</dl>
outside of clip
<yii:clip id="myClip" Visibility="{true}">
<yii:view name="/testview">
inside
</yii:view>
</yii:clip>
out of clip
<com:system.web.widgets.CCaptcha
ButtonType="link"
ShowRefreshButton="{true}">
<br />
</com:system.web.widgets.CCaptcha>
<com:CMultiFileUpload Name="mup" Visibility="{false}"/>
<%= $this->clips['myClip'] %>
output
<?php /* Source template file C:tempxamppxampplitehtdocstestdriveprotectedviewssiteindex.php */ ?><h2>
Welcome, <?php /** Line:2-2*/ echo Yii::app()->user->name; ; ?>!
</h2>
<p>
This is the homepage of <em><?php /** Line:5-5*/ echo Yii::app()->name; ; ?></em>. You may modify the following files to customize the conent of this page:
</p>
<?php /** Line:7-7*/ if ($this->beginCache('MainCache',array()) ) { ?>
Cached data
<?php /** Line:9-9*/ $this->endCache(); } ?>
<dl>
<dt><?php /** Line:11-11*/ echo Yii::app()->controllerPath . DIRECTORY_SEPARATOR . 'SiteController.php'; ; ?></dt>
<dd>This file contains the <tt>SiteController</tt> class which is
the default application controller. Its default <tt>index</tt> action
renders the content of the following two files.
</dd>
<dt><?php /** Line:16-16*/ echo 'C:\temp\xampp\xampplite\htdocs\testdrive\protected\views\site\index.php' ; ?></dt>
<dd>This is the view file that contains the body content of this page.</dd>
<dt><?php /** Line:18-18*/ echo Yii::app()->layoutPath . DIRECTORY_SEPARATOR . 'main.php'; ; ?></dt>
<dd>This is the layout file that contains common presentation (such as header, footer) shared by all view files.</dd>
</dl>
outside of clip
<?php /** Line:22-22*/if (true===true) { $this->beginClip('myClip',array()) ; ?>
<?php /** Line:23-23*/ $this->beginContent('/testview',array()) ; ?>
inside
<?php /** Line:25-25*/ $this->endContent(); ?>
<?php /** Line:26-26*/ $this->endClip(); } ?>
out of clip
<?php /** Line:29-31*/ $this->beginWidget('system.web.widgets.CCaptcha',array( "buttonType"=>'link',"showRefreshButton"=>true,)); ?>
<br />
<?php /** Line:33-33*/ $this->endWidget('system.web.widgets.CCaptcha'); ?>
<?php /** Line:34-34*/if (false===true) { $this->beginWidget('CMultiFileUpload',array( "name"=>'mup',)); $this->endWidget('CMultiFileUpload'); } ?>
<?php /** Line:35-35*/ echo $this->clips['myClip'] ; ?>
The only "bad" issue is (as you pointed out before) if the compiled file has a bad attribute set it the error could be misleading as to which physical view file is at fault.
Without obfuscating the folder, you would end up with deeply nested directories. And if on windows, this would be very tricky if you attempt to keep the original directory structures.
One more suggestion, maybe we should use name={expr} instead of name="{expr}" ? The quotes are just extra typing that is not really necessary.