哈哈 我又到2007来啦~有什么办法呢,老板就等于老大嘛。今天研究了一下phpcms2007中在后台点击"生成图片"后系统内部的动作~请听我一一道来~
首先,点击“生成图片”后,其目标URL为http://127.0.0.1/phpcms2007_sp6_gbk/phpcms/admin.php?mod=picture&file=createhtml&action=create_show&channelid=2&referer=something。由此可以看出,执行权其实又回到了admin.php中。那么这个文件中必然存在处理这些参数的代码。跟昨天写phpcms修改模版的内部实现一样,我仍然没能在该文件中看到明显的处理代码,但同样在admin.php中发现了“if(!@include $filepath) showmessage($LANG['illegal_operation']);”,根据上下文可以知道此时的$filepath="PHPCMS_ROOT/phpcms/module/picture/include/createhtml"。
进入继续观察,里面是一个以action为条件的switch语句。由于上面URL中action=create_show,因此我进入与此对应的switch语句块。代码如下:
case 'create_show'://图片
if(empty($pictureids))
{
showmessage($LANG['select_picture_return'], 'goback');
}
if(is_array($pictureids))
{
foreach($pictureids as $pictureid)
{
createhtml("show");
}
}
else
{
$pictureid = $pictureids;
createhtml("show");
}
showmessage($LANG['picture_update_success'],$referer);
break;
这里并没有什么高深的地方。无非是判断一下$pictureids数组的值。如果数组为空,则出错返回;如果$pictureids有一个以上的元素,则通过foreach语句分别对每一个元素调用createhtml("show");。如果数组只有一个元素,则直接为该元素调用createhtml("show");分析到这,其实程序的执行方向又一次跳转了。显而易见,我们接下去该进入包含createhtml函数的文件看一下。
正如基本上可全局共享的其它函数一样,它同样位于/include/global.func.php文件中。定位到该函数,找到我感兴趣的一句:" include $mod_root.'/include/createhtml/'.$filename.'.php';"。同样是一个include语句。别想了,跟进去看看再说。于是我又来到了/module/picture/include/createhtml/show.php。
这个文件比较另人头痛,但是我看到了我心爱的file_put_contents函数。根据三天的经验,大概到这里,程序不会再乱跳了~~找到文件最后的for循环,它在逻辑上被分成了两部分,这样做是为了实现分页的效果。对我现在的目的来说,我大可以只看其中的一块,即$ii==0那部分。
里面出现了四个函数:ob_start(),ob_get_contents(),ob_clean(),file_put_contents()。其中,前三个函数用的很巧妙,我以前前所未闻。ob_start()打开浏览器缓冲区,之后除了页面头信息之外的所有输出均会被写在该缓冲区内。其后紧接着一个include template($mod, $templateid),很明显,这也算是一种对外输出。因此输出的内容被写入了缓冲区内。然后又接一个$data=ob_get_contents()用于从缓冲区内获得数据,并赋于$data。之后用ob_clean()关闭缓冲区。最后再用file_put_contents()将读出来的内容写入到相应在文件中。
到这里,程序的工作是完成了。我们可以去picture/2009/0303目录下观察,其中已经生成了我们所需的文件。但我的思维并没能随之停下。原因是在我以为大有事情可做之时,系统却已经执行到的最后一步。小究一下,其实问题并不难,接下去的工作其实都让template()给抢了。
找到template的定义(在global.func.php):
function template($module = 'phpcms', $template = 'index')
{
global $CONFIG;
$compiledtplfile = $CONFIG['templatescachedir'].$module.'_'.$template.'.tpl.php';
if($CONFIG['templaterefresh'])
{
$tplfile = PHPCMS_ROOT.'/templates/'.$CONFIG['defaulttemplate'].'/'.$module.'/'.$template.'.html';
if(!file_exists($compiledtplfile) || @filemtime($tplfile) > @filemtime($compiledtplfile))
{
require_once PHPCMS_ROOT.'/include/template.func.php';
template_refresh($tplfile, $compiledtplfile);
}
}
return $compiledtplfile;
}
该函数在性能上进行了优化。$compiledtplfile代表缓存中的模版文件(在data/templatescache下)。系统会尽可能的用该文件生成所需文件。只有当$CONFIG['templaterefresh']为真,并在此前提下缓存模版不存在或templates/default/picture/下的相应模版文件要比缓存文件新时,才会使用非缓存模版。最后返回缓存文件,它被include包含。
接下去的问题是当缓存文件不存在,或模板文件比缓存文件新时,template()如何“刷新”模版。一切尽在下面两句:
require_once PHPCMS_ROOT.'/include/template.func.php';
template_refresh($tplfile, $compiledtplfile);
很明显,我们这时应把开template.func.php看看其中的template_refresh()函数。定义如下:
function template_refresh($tplfile,$compiledtplfile)
{
$str = file_get_contents($tplfile);
$str = template_parse($str);
$strlen = file_put_contents($compiledtplfile, $str);
@chmod($compiledtplfile, 0777);
return $strlen;
}
这个函数很明了,但却一点都不妨碍它的强大。首先,它调用file_get_contents($tplfile),将模版中的内容读到$str中,然后调用template_parse($str),将模版内{}之间的内容翻译成纯PHP的形式,接着调用file_put_contents($compiledtplfile, $str)将转义好的模版写入到缓存中。
于是我们才看到,在template中,无论是否存在缓存模板,它最后总是返回$compiledtplfile。因为即使,它不存在,在经过template_refresh调用后,缓存模版又会被写入缓存文件夹中。