最近公司一个cms类型的项目,时间紧任务重。经过快速的决策后,选择了dedecms开发1.0版本,满足基本需求。以前从来没有接触过这个系统,而且此系统文档是相当的不全。所以分析源代码是最好的方式。学习一个系统,首要的是搞懂它的数据引擎、模板引擎。dedetag.class.php 这个文件就是核心底层模板解析引擎,其它的引擎都是在这个基础上增加了自己的函数而已。
1.DedeTag :最后存储的CTags的结构类,提供一些读取CTags属性的方法。(这个很重要)
2.DedeAttribute : 最后存储的CTags->attr 的属性结构类(count,Items)
3.DedeAttributeParse: 属性解析器,解析提供属性结构到CTags变量。
4.DedeTagParse :核心解析器
1 1.LoadTemplate 载入模板。 2 1. while($line = fgets($fp,1024)) //line 379循环读取文件赋值到变量 $this->SourceString .= $line; 3 2. if($this->LoadCache($filename))//line 384在有缓存的情况,读取缓存文件到 CTags 4 3. $this->ParseTemplet();//line 390 没有缓存则调用模板解析方法 5 2.ParseTemplet 解析模板 6 1.循环遍历,按正则匹配每个标签的位子和属性,并保存到 CTags 7 2. $this->SaveCache(); //line 952 ,如果开启缓存配置则保存到缓存文件 8 3.GetResult 输出模板 9 1. $this->AssignSysTag();// line 738 处理特殊标记(global,include,foreach,var,runphp)(global模板,能处理模板上的函数,和10步一样) 10 4.AssignSysTag 处理特殊标记(global,include,foreach,var,runphp)(global模板,能处理模板上的函数,和10步一样) 11 5.Assign 封装模板的类(arclist/channel/memeberlist.....)和字段的解析是channelunit.helper.php中MakeOneTag函数在处理, 12 $dtp->Assign($tagid,$funcname($ctag,$refObj)); // line 558 调动地底层模板解析引擎的赋值函数为便签赋值。
1 index.php调用arc.partview.class.php 2 1.$this->dtp = new DedeTagParse();//line 47 初始化底层模板解析引擎 3 2.$this->SetTemplet();//line 134 读取要解析的模板 4 3.$this->dtp->LoadTemplet($temp); //line 142 实际调用的是底层模板解析引擎 5 4.$this->ParseTemplet(); //line 149 封装的解析模板方法 6 5.MakeOneTag($this->dtp,$this); //line 224 这个函数放在 channelunit.func.php 文件中, 7 6.helper('channelunit');// line 29 调用helper工具加载对应的函数。channelunit.helper.php 8 7.if($tagname=='field' && $parfield=='Y') //line 515 解析field 模板 9 8.$funcname = 'lib_'.$tagname; //line 557 加载对应的封装模板的类(arclist/channel/memeberlist.....) 10 9.$dtp->Assign($tagid,$funcname($ctag,$refObj)); // line 558 调动地底层模板解析引擎的赋值函数为便签赋值。 11 10.$this->$dtp->EvalFunc($fieldvalue,$functionname,&$refObj); //line 965 动地底层模板解析引擎处理某字段的函数(模板中直接使用函数最终也是调用的此方法加载对应的函数返回值) 12 11.$this->dtp->GetResult();//line 169 调动地底层模板解析引擎的输出方法 13 12.$this->dtp->AssignSysTag(); //line 738 调动地底层模板解析引擎处理特殊标记(global,include,foreach,var,runphp)(global模板,能处理模板上的函数,和10步一样) 14 13.$this->dtp->CTags[$i]->TagValue = $str; 每一次解析模板赋值都会修改CTags的TagValue 15 16 总结:封面模板解析类是对 dedetag.class.php 的封装。加入了一些自己的处理逻辑
1 TypeID = $typeid; 29 $this->dsql = $GLOBALS['dsql']; 30 $this->dtp = new DedeTagParse(); 31 $this->dtp->SetNameSpace("dede", "{", "}"); 32 $this->dtp->SetRefObj($this); 33 $this->ftp = &$ftp; 34 $this->remoteDir = ''; 35 //加载模板->解析模板 36 $this->dtp->LoadTemplet(DEDETEMPLATE ."/default/test.htm"); 37 38 //字段,通过{dede:filed.myname}访问到。在display时,由$this->dtp->AssignSysTag()处理 39 //如果需要面包屑导航等信息,则要new TypeLink,原理也是往Fields中在赋值 40 if ($needtypelink) { 41 $this->TypeLink = new TypeLink($typeid); 42 if (is_array($this->TypeLink->TypeInfos)) { 43 foreach ($this->TypeLink->TypeInfos as $k=>$v) { 44 if (preg_match("/[^0-9]/", $k)) { 45 $this->Fields[$k] = $v; 46 } 47 } 48 } 49 $_sys_globals['curfile'] = 'partview'; 50 $_sys_globals['typename'] = $this->Fields['typename']; 51 52 //设置环境变量 53 SetSysEnv($this->TypeID, $this->Fields['typename'], 0, '', 'partview'); 54 $this->Fields['position'] = $this->TypeLink->GetPositionLink(true); 55 $this->Fields['title'] = $this->TypeLink->GetPositionLink(false); 56 } 57 $this->Fields['myname']="mian_wu"; 58 //全局变量,可以通过{dede:globel.testlist}访问到。在display时,由$this->dtp->AssignSysTag()处理 59 $GLOBALS['testlist']=array(1,2,3,4,5); 60 61 62 //解析框架自定义模板,arclist/channel/memeberlist..... 63 MakeOneTag($this->dtp, $this); 64 //解析自己定义模板,沒有自定义则此代码不需要。 65 foreach ($this->dtp->CTags as $tid => $ctag) { 66 if ($ctag->GetName()=="myfield") { 67 //自定义单标签 68 //$this->assgin() //去解析自定义模板 69 $this->dtp->Assign($tid, $this->Fields[$ctag->GetAtt('name')]); 70 } elseif ($ctag->GetName()=="mytag") { 71 //自定义复合标签 72 //自定义模板解析的两种方式 73 //1.可以写在taglib/tagName.lib.php的形式,模板会自动加载并解析。 74 //2.直接在CTags里面通过名字匹配到id,并assin进入值。 75 $this->dtp->Assign($tid, $this->Mytag($ctag->GetInnerText())); 76 } 77 } 78 } 79 public function Mytag($innertext) 80 { 81 //重新初始化底层解析模板。 82 $dtp2 = new DedeTagParse(); 83 $dtp2->SetNameSpace('field', '[', ']'); 84 $dtp2->LoadSource($innertext); 85 foreach ($dtp2->CTags as $k=>$ctag) { 86 if ($ctag->GetName()=='myname') { 87 //此处赋的值,自行处理 88 $dtp2->Assign($k, "mian_wu"); 89 } 90 } 91 return $dtp2->GetResult(); 92 } 93 public function Display() 94 { 95 //底层调用了$this->dtp->AssignSysTag();(global,include,foreach,var,runphp) 96 $this->dtp->Display(); 97 } 98 } 99 100 $a=new TestView(2); 101 $a->Display();
1 2 3 4 5 6{dede:global.cfg_webname/} 7 8 9 {dede:foreach array="testlist"} 10 [field:key /]- [field:value /] 11 {/dede:foreach} 12 1314 {dede:arclist limit='0,4' flag="p"} 15 16 {/dede:arclist}17
测试页面
1 common.inc.php 核心类分析 2 1.定义全局变量 3 2.过滤请求参数 4 3.加载数据库配置 require_once(DEDEDATA.'/common.inc.php');//line 152 5 4.设置模板目录变量 6 5.分页参数的设置 7 6.自动加载类库处理 8 7.引入数据库类 9 8.全局常用函数 require_once(DEDEINC.'/common.func.php'); //在line 368,做了兼容处理,扩展的函数可以直接写在extend.func.php中。 11 9.路由处理,请求分发 12 10.载入小助手配置 13 dedesqli.class.php
$sql="xxxx";
$dsql->Execute('name', $sql);
while ($row = $dsql->GetObject('name')) {
$arr[] = $row;
}
14 1.$dsql = $dsqli = $db = new DedeSqli(FALSE); //line 21 在工程所有文件中均不需要单独初始化这个类,可直接用 $dsql 或 $db 进行操作 15 2.SetQuery($sql) :会自动把SQL语句里的#@__替换为$this->dbPrefix(在配置文件中为$cfg_dbprefix)。并设置$this->queryString 16 3.Execute($id="me", $sql='') //如果sql不为空,内部会调用SetQuery,替换前缀。 18 $this->result[$id] = mysqli_query($this->linkID, $this->queryString); //line 315 执行sql返回执行结果。 19 4.GetOne($sql='',$acctype=MYSQLI_ASSOC) 20 if(!preg_match("/LIMIT/i",$sql)) $this->SetQuery(preg_replace("/[,;]$/i", '', trim($sql))." LIMIT 0,1;");//line 351 //内部通过正则加上了limit 0,1 21 $this->Execute("one"); //line 354 调用Execute 22 5.GetArray和GetObject
function GetObject($id="me")
{
if($this->result[$id]===0)
{
return FALSE;
}
else
{
return mysqli_fetch_object($this->result[$id]);
}
}
23 6.ExecuteNoneQuery($sql='')执行一个不返回结果的SQL语句,如update,delete,insert等
dedecms 就两个核心类 dedetag.class.php、common.inc.php。一行行读源码,就可以知道它得工作原理。二次开发时,需要哪部分就找到对应的源码进行分析。其它的就应该没什么困难了。
标签: dedecms
//包含连接数据库的公共文件
require_once("./conn.php");
//开启SESSION会话
session_start();
//判断用户是否登录
if(empty($_SESSION['username']))
{
//如果用户没有登录,则直接跳转到login.php
header("location:./login.php");
die();
}
//判断表单的来源是否合法
if(isset($_POST['token']) && $_POST['token']==$_SESSION['token'])
{
//**********************上传图片*******************************
//(1)判断上传图片是否有错误发生
if($_FILES['uploadFile']['error']!=0)
{
echo "
//(2)判断上传文件内容类型是不是图片
$arr1 = array("image/jpeg","image/png","image/gif");
//创建finfo的资源:获取文件内容类型,与扩展名无关
$finfo = finfo_open(FILEINFO_MIME_TYPE);
//获取文件内容的原始类型,不会随着扩展名改名而改变
$mime = finfo_file($finfo,$_FILES['uploadFile']['tmp_name']);
if(!in_array($mime,$arr1))
{
echo "
//(3)判断上传的文件扩展名是不是图片
$arr2 = array("jpg","gif","png");
$ext = pathinfo($_FILES['uploadFile']['name'],PATHINFO_EXTENSION); //文件扩展名
if(!in_array($ext,$arr2))
{
echo "
//(4)移动图片到 images目录中
$tmp_name = $_FILES['uploadFile']['tmp_name'];
$dst_name = "./images/".uniqid().".".$ext;
move_uploaded_file($tmp_name,$dst_name);
//***********************将表单提交数据保存到数据库****************************
//(1)获取表单提交数据
$title = $_POST['title'];
$intro = $_POST['intro'];
$imgsrc = $dst_name; //将图片路径保存到数据库
$addate = time();
//(2)判断记录是否添加成功
$sql = "INSERT INTO photos VALUES(null,'$title','$imgsrc','$intro',0,$addate)";
if(mysqli_query($link, $sql))
{
echo "