dedecms代码研究六

今天讲的是dedecms最关键的东西,模板分析啦。也就是dedetag.class.php 里面的ParseTemplet方法 模板解析方法

先看看一个dedecms标签,大家心里有个数:

{dede:arclist row=10 orderby=pubdate type='image.' imgwidth='143' imgheight='106'}

<li><a href="[field:arcurl/]">[field:image/]<span class="title">[field:title/]</span></a></li>

{/dede:arclist}

参考上面标签我们就可以进一步分析啦。

这里假定,你已经了解了dedecms的标签形式,标签格式,和标签种类。

下面我们展开分析

先看方法前面初始化一些最基本的变量:

1)标签起始符号和结束符号。如:“{”和"}"

$TagStartWord = $this->TagStartWord;

$TagEndWord = $this->TagEndWord;

2)设置临时变量,用于临时存储查找到的新标签在模板中的起始位置和结束位置。

$sPos = 0; $ePos = 0;

3)设定完整标签起始字符串和结束字符串。比如:“{dede:”这种形式

$FullTagStartWord =  $TagStartWord.$this->NameSpace.":";

$sTagEndWord =  $TagStartWord."/".$this->NameSpace.":";

$eTagEndWord = "/".$TagEndWord;

这里值得注意的是结束部分分两种,一种是类似于{aa:ff /}单体结构标签,一种是类似于{aa:fff}{/aa:fff}符合结构标签

4)获取标签其实字符串({dede:)长度和整个模板的长度

$tsLen = strlen($FullTagStartWord);

$sourceLen=strlen($this->SourceString);

 

上面就是初始变量设置部分啦。

接下来是个小判断,如果整个模板的长度不大于标签起始字符串的长度加3,就退出。

if( $sourceLen <= ($tsLen + 3) ){

    return;

}

为什么要加3(也就是模板长度最少应该是标签起始字符串长度加4)呢?

我们看看我们能写出的最短标签:

{dede:a/}

冒号后面是可能出现的最短字符串,就是3个,所以这里如果小于3就连最起码的一个标签都无法完整,所以要做这个判断,至于等于嘛,我个人认为是没必要的。

好继续往下看下面两句:

$cAtt = new DedeAttributeParse();

$cAtt->charToLow = $this->CharToLow;

创建了一个DedeAttributeParse类,并设定了CharToLow属性,这个类看名字应该是标签属性分析类,charToLow就是是否把字符串自动转化为小写。

接下来就是一个长长的for循环了,遍历模板字符串的每个字符进行分析,提取模板中的标签。

for($i=0; $i < $sourceLen; $i++)

下面我们就来看看这个for循环里面是怎么分析的吧

先定义一个临时变量,存储当前找到的标签的名字

$tTagName = '';

下面是一个判断,注释写得很清楚,但我们现在还看不懂,所以先知道有这么个判断就行啦

//如果不进行此判断,将无法识别相连的两个标记

if($i-1 >= 0){

    $ss = $i-1;

}else{

    $ss = 0;

}

设定了一个变量$ss,后面留意一下就是了。

下面就是查找标签了

$sPos = strpos($this->SourceString,$FullTagStartWord,$ss);

$isTag = $sPos;

找到在模板字符串中从$ss指定的位置开始,第一个类似“{dede:”这种标签头的位置,并把$isTag变量设置为strpos的返回值,这是个偷懒的写法,应该明确指出查到标签了,就是true,而不是任意字符。

我们看到这里用到了$ss,作用是设定查找的起始位置。

我们继续往下看吧

下面一个if语句好像是对第一个字符开始就是标签的情况下的一种补充?

搞不懂了,本来就能找到的,加这句什么意思呢?多余哦,这个肯定有更好方法的。不多说这句了。

在下来的if就是如果没找到标签就不循环了,不解释。

再下来,一个子循环

for($j=($sPos+$tsLen);$j<($sPos+$tsLen+$this->TagMaxLen);$j++)

$tsLen我们之前说了,是标签头(类似{dede:)长度

那这个for的解释就是遍历从标签头的下一个字符开始到标签最大长度位置结束这中间的所有字符,看来是要找标签名字啦

再看看for循环里面,很简单的几句,就是找出标签的名字,如何找出来的呢?

if($j>($sourceLen-1)){break;}else if( ereg("[/ \t\r\n]",$this->SourceString[$j]) || $this->SourceString[$j] == $this->TagEndWord ){break;}else{$tTagName .= $this->SourceString[$j];}

这个for里面的if语句,两种情况下名字结束,一种是字符位置到模板的字后一个位置,另一种是发现了空格、断行、tab符、/等或找到了标签结束符(如:"}")

通过这个for循环,标签的名字就弄出来了,保存在变量$tTagName中。

下面是一个极其长的if语句啦,判断$tTagName变量是否为空,如果是空则跳出循环(标签出错了嘛),不过跳出前还设置$i,有什么用?看不懂。

接下来重点就是找到标签名字的情况啦。

先是设置几个变量

$i = $sPos+$tsLen;

$endPos = -1;

$fullTagEndWordThis = $sTagEndWord.$tTagName.$TagEndWord;

把循环模板字符串的指针$i跳到标签名字开始的地方。然后设置变量$endPos 为-1,组合出一种标签结束符({/dede:xxx})

 

接下来是查找三个位置:$eTagEndWord(/})、$FullTagStartWord({dede:)、$fullTagEndWordThis({/dede:xxx})

$e1 = strpos($this->SourceString,$eTagEndWord, $i);

$e2 = strpos($this->SourceString,$FullTagStartWord, $i);

$e3 = strpos($this->SourceString,$fullTagEndWordThis,$i);

$e1就是在标签名字找到后第一个"/}"出现的位置,$e2就是第一个“{dede:”出现的位置,$e3就是第一个{/dede:xxx}出现的位置。这里注意,获取$e3值的时候,$fullTagEndWordThis是以当前找到的标签为名字的结束字符串。

 

在下面几句是统一$e1 $e2 $e3的值,使这三个变量如果找到要找的标签字符串就保存位置,找不到就保存-1

$e1 = trim($e1); $e2 = trim($e2); $e3 = trim($e3);

$e1 = ($e1=='' ? '-1' : $e1);

$e2 = ($e2=='' ? '-1' : $e2);

$e3 = ($e3=='' ? '-1' : $e3);

 

接下来就要根据这三个值进行一些处理啦。处理什么呢?我们先看看这段代码吧:

//not found '{/tag:'if($e3==-1) {$endPos = $e1;$elen = $endPos + strlen($eTagEndWord);}//not found '/}'else if($e1==-1) {$endPos = $e3;$elen = $endPos + strlen($fullTagEndWordThis);}//found '/}' and found '{/dede:'else{//if '/}' more near '{dede:'、'{/dede:' , end tag is '/}', else is '{/dede:'if($e1 < $e2 &&  $e1 < $e3 ){$endPos = $e1;$elen = $endPos + strlen($eTagEndWord);}else{$endPos = $e3;$elen = $endPos + strlen($fullTagEndWordThis);}}

我们知道,dedecms标签结束有两种方式,一种是(/})这种方式,还有一种是({/dede:xxx}),除此之外没有他选,如果没有这两种结束,只能说明一个问题,模板内的标签不完整。这个if语句做了一个假设,就是两种标签结束方式一定是有一种存在的。

 

if的第一个分支,假设$e3为-1,也就是(/})这种方式存在,所以设置了标签结束符位置变量$endPos为变量$e1的值,而此时,标签最终结束位置就知道了,是$endPos加上(/})的长度。

if语句的第二个分支和第一个类似,只是假定找到了({/dede:xxx})。

if语句的else部分,是假定两个都找到了(有这种可能吗?),那么就要进一步分析啦,如果(/})这种结束符出现的位置比下个标签起始位置靠前,而且还比$e3的结束符({/dede:xxx})位置靠前,说明当前找到的(/})就是当前标签的结束符;否则一定是({/dede:xxx})这种啦。

上面通过$e1 $e2 $e3的变量设置和一个if语句,最终是要得到两个变量:$endPos和$elen,当前标签结束符开始的位置和结束位置。

下面又是一个if语句,很简单,通过endPos是否为-1判断当前标签是否正确结束。如果没有正确结束则打印一段文字,然后就退出循环。这块设计的是否可以再好点呢,比如把这块出错的标签替换为一个错误信息,或在做模板分析前,统一检查语法正确性,以保证更快速分析模板。

再继续往下看,又是设置了两个变量。

$i = $elen;

$ePos = $endPos;

由于找到当前循环要找的标签,所以,设置主循环for的循环变量$i到下个标签的起始位置。

设置当前标签的结束符起始位置$ePos。

 

当前标签的开始位置和结束位置都确定了,接下来就可以分析标签的属性了,我们继续。

$attStr = '';

$innerText = '';

$startInner = 0;

三个变量,我们了解到,标签内部有两种东西,一种是属性字符串,还有一种是内容字符串。$startInner 变量指示内容字符串是否开始(奇怪为什么不用布尔值呢)。

下面一个for循环开始提取这些字符串,从标签名称后面到结束符开始之前的部分。

for($j=($sPos+$tsLen);$j < $ePos;$j++)

 

看看循环里面是怎么提取属性字符串和内容字符串的。

if($startInner==0 && ($this->SourceString[$j]==$TagEndWord && $this->SourceString[$j-1]!="\\") ){$startInner=1;continue;}if($startInner==0){$attStr .= $this->SourceString[$j];}else{$innerText .= $this->SourceString[$j];}

嗯,用了两个if语句,第一个语句是用来判断内容字符串是否开始的。第二个if语句根据内容字符串开始指示符判断,分别读取内容字符串和属性字符串。

个人认为,通过特殊标识符截字更快一些。

这里面还有个问题就是,是否内容字符串开始是如何判断的呢?

我们看看第一个if

if($startInner==0 && ($this->SourceString[$j]==$TagEndWord && $this->SourceString[$j-1]!="\\") )

$startInner==0这句就是做个过滤,当读取内容字符串的时候就不会再走这个if了,关键是&&后面括号里面的内容。

如果当前字符为标签结束符$TagEndWord(})而且结束符的前一个字符不是反斜杠的时候,就是属性部分结束了,如果是反斜杠说明是一些模板内容之类的了。

 

通过上面的for循环我们就提取出了当前标签的属性和内容,接下来就开始分析属性和内容啦

$cAtt->SetSource($attStr);if($cAtt->cAttributes->GetTagName()!=''){$this->Count++;$CDTag = new DedeTag();$CDTag->TagName = $cAtt->cAttributes->GetTagName();$CDTag->StartPos = $sPos;$CDTag->EndPos = $i;$CDTag->CAttribute = $cAtt->cAttributes;$CDTag->IsReplace = FALSE;$CDTag->TagID = $this->Count;$CDTag->InnerText = $innerText;$this->CTags[$this->Count] = $CDTag;}

通过属性分析类来进行分析啦,然后创建DedeTag标签类实例(就是创建一个标签对象),然后把当前标签的属性都放进这个标签对象。

包括标签名称、起始位置、结束位置、属性数组、内部字符串等。

然后,把这个新的标签对象放到DedeTagParse类的CTags数组中。

 

这样一个标签就分析完了,也结束了一次最外层的for循环。原来每循环一次只能分析出一个标签,有多少个标签就 有可能循环多少次。

 

整个模板分析结束后,如果允许缓存再调用SaveCache方法,把当前模板的标签信息保存到缓存文件或者叫中间信息文件。

ps:其实整个模板解析方法也只是把模板标签的信息记录了下来放到新建的dedetag对象中,然后保存到当前DedeParse类的CTags数组中(类似于上一节我们从缓存文件分析的过程)

分析到这里,大家可能有个疑问?

织梦系统是怎么通过标签获取到数据的呢?这就是我们下部分需要分析的,其实细心的朋友应该已经知道,肯定是我们之前在视图类里面的MakeOneTag这个方法里面

模板分析就讲完啦,这样该有的信息就都有了,我们又可以回到LoadTemplate方法继续啦。 阚荣华博客:www.kanronghua.com 欢迎学习和交流

转载于:https://www.cnblogs.com/ronghua/p/5936084.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/457176.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django-session的存放位置

django-session 存放位置 设置session的保存位置&#xff0c;有三种方法&#xff1a; 保存在关系数据库(db)保存在缓存数据库(cache) 或者 关系缓存数据库(cache_db) 保存在文件系统中(file) 第一种 保存在数据库中&#xff1a;需要在 INSTALL_APPS 中添加&#xff1a;djan…

允许多域名跨域 php,PHP设置多域名允许跨域访问

针对 PHP 语言设置多域名允许跨域访问服务器变量&#xff1a;服务器变量存储在 $_SERVER 数组中&#xff0c;在这个数组中有一个特殊的键值&#xff1a;HTTP_ORIGIN。这个键只在跨域的时候才会存在值&#xff0c;同源时为空字符串响应头设置允许某域名访问&#xff1a;access-c…

蚂蚁分类信息系统 5.8 信息浏览量后台自定义设置

mymps 蚂蚁分类信息是一款基于PHPMySQL的建站系统,为在各种服务器上架设分类信息以及地方门户网站提供完美的解决方案. mymps5.8 下载 蚂蚁分类系统 5.8下载 蚂蚁分类系统下载 mymps下载 蚂蚁分类信息系统 5.8 原信息浏览量后台无法自定义&#xff0c;现增加后台自定义浏览量…

MS SQL-Server快捷键

快捷键 功能 CtrlShiftB 生成解决方案 CtrlF7 生成编译 CtrlO 打开文件 CtrlShiftC 显示类视图窗口 CtrlShiftO 打开项目 F4 显示属性窗口 ShiftF4 …

python编写四位数验证码

def verifycode(request):#引入绘图模块from PIL import Image, ImageDraw, ImageFont#引入随机函数模块import random#定义变量&#xff0c;用于画面的背景色、宽、高bgcolor (random.randrange(20, 100), random.randrange(20, 100), random.randrange(20, 100))width 100h…

php 计算数据偏离度,关于偏离度的测算方法

2015年6月技术总结——关于偏离度的测算方法研究院公用事业部 路璐引言《原理》中说“偏离度是指每一种偿债来源与财富创造能力的距离&#xff0c;所体现的是偿债来源对债务安全的保障程度&#xff0c;唯有通过揭示偿债来源与财富创造能力偏离度才能真正区别每一种偿债来源的风…

Django中celery配置总结

情景&#xff1a; 用户发起request&#xff0c;并等待response返回。在本些views中&#xff0c;可能需要执行一段耗时的程序&#xff0c;那么用户就会等待很长时间&#xff0c; 造成不好的用户体验&#xff0c;比如发送邮件、手机验证码等。 使用celery后&#xff0c;情况就不…

AngularJs ng-repeat限制循环次数

重复数组&#xff1a;<ul ng-initname[1,2,3,3]><li ng-repeat"name in name track by $index">{{name}}</li> </ul> 对象&#xff1a; <ul ng-initobj{ a:"aaa", b:"bbb", c:"ccc" }><li ng-repeat…

qml demo分析(customgeometry-贝塞尔曲线)

一、效果展示 本篇文章还是带来一个简单的qt示例分析&#xff0c;且看图1效果。 图1 贝塞尔曲线 二、源码分析 该示例代码所在目录quick\scenegraph\customgeometry&#xff0c;感兴趣的同学可以自己去找&#xff0c;这篇文章我还是主要讲解源码&#xff0c;不涉及其他方面。 废…

php 打包上传的文件,PHP单文件上传原理及上传函数的打包

PHP单文件上传原理及上传函数的封装服务器(临时文件)——>指定目录,当文件进入服务器时它就是临时文件了,这时操作中要用临时文件的名称tmp_name。//在客户端设置上传文件的限制(文件类型和大小)是不安全的&#xff0c;因为客户能通过源代码修改限制&#xff0c;所以在服务端…

celery AttributeError: 'str' object has no attribute 'items'

AttributeError: str object has no attribute items python manage.py celery worker --loglevelinfo 查了好久发现原来是因为以前下的redis版本过高&#xff08; 3.0.1 &#xff09;&#xff0c;重装redis pip install redis2.10.6

equals和==的区别(转)

基本数据类型&#xff0c;也称原始数据类型。byte,short,char,int,long,float,double,boolean&#xff0c;他们之间的比较&#xff0c;应用双等号&#xff08;&#xff09;,比较的是他们的值。 复合数据类型(类) 当他们用&#xff08;&#xff09;进行比较的时候&#xff0c;…

Python 中的range,以及numpy包中的arange函数

range函数函数说明&#xff1a; range(start, stop[, step]) -> range object&#xff0c;根据start与stop指定的范围以及step设定的步长&#xff0c;生成一个序列。  参数含义&#xff1a;start:计数从start开始。默认是从0开始。例如range&#xff08;5&#xff09;等价…

php角色权限安全,php – 安全的chmod权限?

新创建的文件和目录的默认权限由umask环境变量设置.文件的所有者和root可以更改权限.如果您不需要在应用程序中使用chmod,请将其保留在禁用列表中.你应该看看安全性的方式是&#xff1a;现在比我更聪明的人让chmod成为我应用程序中更安全的部分之一.因此,我将花费我的时间使其他…

namespace! 报错

在工程文件中url 定义namespace需要 from django.contrib import admin from django.urls import path,include from django.conf.urls import url urlpatterns [path(admin/, admin.site.urls),url(r^liu/,include(myApp.urls,namespaceapp)), ] 在myApp 的urls中 加这句 a…

test.php.bak,MongoDB热备份工具:解决官方版备份缺陷

贺春旸&#xff0c;凡普金科DBA团队负责人&#xff0c;《MySQL管理之道&#xff1a;性能调优、高可用与监控》第一、二版作者&#xff0c;曾任职于中国移动飞信、安卓机锋网。致力于MariaDB、MongoDB等开源技术的研究&#xff0c;主要负责数据库性能调优、监控和架构设计。工具…

zookeeper工作原理、安装配置、工具命令简介

1 Zookeeper简介Zookeeper 是分布式服务框架&#xff0c;主要是用来解决分布式应用中经常遇到的一些数据管理问题&#xff0c;如&#xff1a;统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等等。 ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序…

流式大数据处理的三种框架:Storm,Spark和Samza

许多分布式计算系统都可以实时或接近实时地处理大数据流。本文将对三种Apache框架分别进行简单介绍&#xff0c;然后尝试快速、高度概述其异同。 Apache Storm 在Storm中&#xff0c;先要设计一个用于实时计算的图状结构&#xff0c;我们称之为拓扑&#xff08;topology&#x…

models.ForeignKey( ,on_delete=models.CASCADE)    # 关联外键

# 关联外键 sgrade models.ForeignKey("Grades",on_deletemodels.CASCADE) 版本跟新后忘记加on_deletemodels.CASCADE 报错困扰了很久 多读报错信息 from django.db import models# Create your models here. class Grades(models.Model):gname models.Cha…

java 产生无重复的随机数,Java创建无重复的随机数

小编典典最简单的方法是创建一个可能数字的列表(1..20或任何数字)&#xff0c;然后用对其进行混洗Collections.shuffle。然后&#xff0c;只需考虑你想要的许多元素。如果你的范围最终等于你需要的元素数量(例如&#xff0c;用于洗牌的卡片)&#xff0c;则这非常好。如果你想要…