注意:此文和UCHome的Manyou开发模式不同,Manyou是利用Discuz的开放平台MYOP开发公共插件,可供所有基于UCHome的网站使用;而此文涉及的开发模式与Manyou不同,是在本地开发,以插件的形式扩展UCHome原有功能。
UCHome是Discuz的一款SNS程序,能让每个网站都用拥有自己的Facebook/校内。Discuz也提供了MYOP开发者平台,所有开发者都可以在此平台上开发自己的应用程序供其他UCHome网站使用,不但可以为自己的网站增加功能,同时也能将自己网站的功能推广到数以万计的UCHome网站中,可谓一举两得。
有时候,开发者并不一定希望使用MYOP开发应用,一是由于产品的需要,并不希望开发出来的应用被其他网站使用;二者MYOP的开发,必须处理联网模式,不能本地开发,较为繁琐。因此,很多有能力的站长会在UCHome的基础之上,以修改UCHome代码的方式增加功能。
但UCHome发展速度很快,经常会有版本更新,往往这个时候,修改了UCHome源码的站长就会面临一个两难的问题:是升级程序?还是为了保留以往的修改,而不升级?在我看来,如果前期做好开发规范,是完全可以避免这个问题的。下面介绍一下我在UCHome Apps开发过程中的一些经验。
想要能够随着官方的程序发布实现平滑的代码升级,无非一点:尽量的少改UCHome原有代码。
少改,但不是不改。一点不改程序的源代码是不可能的。但如何能实现少改呢?我定制了了下面的一些规则:
基本原则
除template目录下的模板文件,其余uchome原有文件尽量不要改动。如需要改动,需汇报项目管理人员记录后才可实施修改。
即使需要对系统原有文件进行修改,尽量不要在文件中增加逻辑代码,也不要修改原有逻辑,使用include的方式来增加逻辑。
例如,需要在系统首页增加growth的表单,设计到两个系统原有文件的修改:
source/space_feed.php
template/space_feed.htm
但请不要直接在这两个文件中增加逻辑代码,对于模板文件,可以使用<!-{template apps/growth/tpl/widget/form.htm}–>的方式加载一个外部文件;同样,针对程序文件,也可以使用include ‘apps/growth/widget/form.php’的方式。
采用这样的方式,即使以后程序文件被覆盖,也不会增加太多工作量。
文件结构
<!--[if !supportLists]-->· <!--[endif]-->以apps.php作为入口文件,所有应用都使用类似apps.php?do=growth&ac=list这样的URL访问应用程序。其中do为应用名称,ac为动作(执行的脚本名)。例如上面的例子,实际是以apps.php为入口,再加载apps/growth/list.php。
<!--[if !supportLists]-->· <!--[endif]-->应用程序统一放在apps目录之下,以应用的名称创建文件夹。例如,成长曲线的名称为growth,则创建apps/growth/目录,所有成长曲线的应用程序都放在此文件夹下。其中tpl文件夹存放模板文件,image文件夹存放图片,可根据应用需求安排apps/growth/内的文件结构。
<!--[if !supportLists]-->· <!--[endif]-->growth/doc 目录用于存放当前应用的文档,例如开发文档、api手册、数据库脚本等。
<!--[if !supportLists]-->· <!--[endif]-->growth/api.php为growth为外部程序提供的接口。文件内的代码是一个类,命名为AppsGrowthApi,类中方法均为静态方法,方法名采用驼峰格式,例如getGrowthPointByUid,方法名称要足以描述其功能。
<!--[if !supportLists]-->· <!--[endif]-->apps/common文件存放一些应用程序公用的文件,例如公用的js库,function库,类库等。
命名规范
程序命名规范
<!--[if !supportLists]-->· <!--[endif]-->文件名一律小写,单词间以下划线分隔
<!--[if !supportLists]-->· <!--[endif]-->类名采用首字母大写的驼峰命名方式
<!--[if !supportLists]-->· <!--[endif]-->类中的方法名采用首字母小写的驼峰方式命名
<!--[if !supportLists]-->· <!--[endif]-->类中的私有方法和私有变量以下划线开头
<!--[if !supportLists]-->· <!--[endif]-->函数名全小写,单词间以下划线分隔
<!--[if !supportLists]-->· <!--[endif]-->变量名同函数名
数据库命名规范
<!--[if !supportLists]-->· <!--[endif]-->数据表以uchome_app_作为前缀,例如uchome_app_growth_point
<!--[if !supportLists]-->· <!--[endif]-->字段名全为小写,以下划线分隔单词
<!--[if !supportLists]-->· <!--[endif]-->建议在建表时注明备注,方便理解字段用处
<!--[if !supportLists]-->· <!--[endif]-->对数据表结构的更改,请在当前应用的doc/sql目录下以日期为单位新建文件,记录更改。
【UCHome二次开发】模板机制
首先告诉大家一个小技巧,就是如何让系统每次自动更新缓存,免去每次修改模板后都要到后台更新缓存操作,记住,在解决完所有问题后,要在改回来,否则会大幅度提高服务器负担.
打开source/function_common.php文件,找到:
if(!file_exists($objfile)) {
include_once(S_ROOT.’./source/function_template.php’);
parse_template($tpl);
}
修改成:
//if(!file_exists($objfile)) {
include_once(S_ROOT.’./source/function_template.php’);
parse_template($tpl);
//}
这样修改模板后就不用到后台更新缓存了,改成这样的意思是无论是否存在缓存文件都将包含模板文件并生成新的缓存文件。
下面开始讲一下模板中常用的标签及变量输出。
一、变量:
如我们在PHP文件中定义了一个变量并赋值了,如:
$siteUrl=’http://www.demi.cn’;
$siteName=’祝君成功网站建设’;
那么如何在模板中输出呢?其实这个很简单:
站点地址:$siteUrl
站点名称:$siteName
就可以输出结果:
站点地址:http://www.demi.cn
站点名称:祝君成功网站建设
二、标签
UCHOME模板中一般含有以下标签
if标签,else标签,loop标签 ,eval标签
1、if标签和 else标签主要是判断标签,格式如下
<!—{if 条件}–>
//Your code here…
<!—{/if}–>
和
<!—{if 条件}–>
//Your code here…
<!—{else}–>
//Your code here…
<!—{/if}–>
以上的条件可以是变量及合法的PHP函数及自定义函数。
2、 loop标签
loop标签是循环输出数组的,如果你不懂数组,那么请自行参考相关资料。
格式一:
<!–{loop $s $key $value}–>
$value
<!–{/loop}–>
相当于PHP代码中
foreach($s as $key=>$value){
echo $value
}
格式二:
<!–{loop $s $value}–>
$value
<!–{/loop}–>
相当于PHP代码中
//住$s必须是二维数组。
foreach($s as $value){
echo $value
}
3、eval标签
eval标签主要是用来在模板中执行php语法,在eval后必须紧跟合法的PHP语法,如:
<!–{eval echo ‘这是PHP语法’;}–>
基本上UCHOME模板中的标签就这么几个,也是最常用的几个了
【UCHome二次开发】数据字典|数据库结构
数据字典即数据库中的表和字段的说明。找了好久才找到的,共享一下。
此版本的UCHome数据字典是基于UCenter Home 2.0beta,最后修订日期为2009.08.08。
uchome 2.0数据字典
数据字典形式为word文档,点击 UCHome 2.0 数据字典下载。
【UCHome二次开发】模板解析
UCHome模板文件位于/template文件夹下,每个模板文件单独一个文件夹,默认模板文件夹为default。
1、模板的使用配置
在根目录下的config.php中进行配
$_SC['template'] = 'default'; //选择模板目录
2、模板的处理
程序中使用到模板文件时,先去模板缓存目录/data/tpl_cache/下查找是否存储模板缓存文件。模板缓存文件命名合适为“template_模板目录名_模板文件名.php”。如存在则直接使用该缓存的模板文件;如不存在,则先解析对应的模板文件,生成模板缓存文件再进行使用。
3、模板的解析
模板解析是调用/source目录下的function_template.php文件中的parse_template函数来实现的。
解析过程并不复杂,主要是读取模板文件(.htm),用正则表达式替换标记为对应的PHP代码,最终生成一个标准的PHP文件,保存到模板缓存目录/data/tpl_cache/供后续使用。
具体的模板解析过程不做说明,直接查看代码即可。
置,确定系统使用的模板,如下:
function parse_template($tpl) {
global $_SGLOBAL;
//包含模板
$_SGLOBAL['sub_tpls'] = array($tpl);
$tplfile = S_ROOT.'./'.$tpl.'.htm';
$objfile = S_ROOT.'./data/tpl_cache/'.str_replace('/','_',$tpl).'.php';
//read
$template = sreadfile($tplfile);
if(empty($template)) {
exit("Template file : $tplfile Not found or have no access!");
}
//模板
$template = preg_replace("/\<\!\-\-\{template\s+([a-z0-9_\/]+)\}\-\-\>/ie", "readtemplate('\\1')", $template);
//处理子页面中的代码
$template = preg_replace("/\<\!\-\-\{template\s+([a-z0-9_\/]+)\}\-\-\>/ie", "readtemplate('\\1')", $template);
//解析模块调用
$template = preg_replace("/\<\!\-\-\{block\/(.+?)\}\-\-\>/ie", "blocktags('\\1')", $template);
//解析广告
$template = preg_replace("/\<\!\-\-\{ad\/(.+?)\}\-\-\>/ie", "adtags('\\1')", $template);
//时间处理
$template = preg_replace("/\<\!\-\-\{date\((.+?)\)\}\-\-\>/ie", "datetags('\\1')", $template);
//头像处理
$template = preg_replace("/\<\!\-\-\{avatar\((.+?)\)\}\-\-\>/ie", "avatartags('\\1')", $template);
//PHP代码
$template = preg_replace("/\<\!\-\-\{eval\s+(.+?)\s*\}\-\-\>/ies", "evaltags('\\1')", $template);
//开始处理
//变量
$var_regexp = "((\\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(\[[a-zA-Z0-9_\-\.\"\'\[\]\$\x7f-\xff]+\])*)";
$template = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}", $template);
$template = preg_replace("/([\n\r]+)\t+/s", "\\1", $template);
$template = preg_replace("/(\\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff]+)\.([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/s", "\\1['\\2']", $template);
$template = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\.\x7f-\xff]+)\}/s", "<?=\\1?>", $template);
$template = preg_replace("/$var_regexp/es", "addquote('<?=\\1?>')", $template);
$template = preg_replace("/\<\?\=\<\?\=$var_regexp\?\>\?\>/es", "addquote('<?=\\1?>')", $template);
//逻辑
$template = preg_replace("/\{elseif\s+(.+?)\}/ies", "stripvtags('<?php } elseif(\\1) { ?>','')", $template);
$template = preg_replace("/\{else\}/is", "<?php } else { ?>", $template);
//循环
for($i = 0; $i < 5; $i++) {
$template = preg_replace("/\{loop\s+(\S+)\s+(\S+)\}(.+?)\{\/loop\}/ies", "stripvtags('<?php if(is_array(\\1)) { foreach(\\1 as \\2) { ?>','\\3<?php } } ?>')", $template);
$template = preg_replace("/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}(.+?)\{\/loop\}/ies", "stripvtags('<?php if(is_array(\\1)) { foreach(\\1 as \\2 => \\3) { ?>','\\4<?php } } ?>')", $template);
$template = preg_replace("/\{if\s+(.+?)\}(.+?)\{\/if\}/ies", "stripvtags('<?php if(\\1) { ?>','\\2<?php } ?>')", $template);
}
//常量
$template = preg_replace("/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/s", "<?=\\1?>", $template);
//替换
if(!empty($_SGLOBAL['block_search'])) {
$template = str_replace($_SGLOBAL['block_search'], $_SGLOBAL['block_replace'], $template);
}
//换行
$template = preg_replace("/ \?\>[\n\r]*\<\? /s", " ", $template);
//附加处理
$template = "<?php if(!defined('IN_UCHOME')) exit('Access Denied');?><?php subtplcheck('".implode('|', $_SGLOBAL['sub_tpls'])."', '$_SGLOBAL[timestamp]', '$tpl');?>$template<?php ob_out();?>";
//write
if(!swritefile($objfile, $template)) {
exit("File: $objfile can not be write!");
}
}