ThinkSNS源码分析(一):模板渲染

May 30, 2015 at 10:13 pm

ThinkSNS是使用PHP开发的一款开源的微博系统,本系列打算借由这个微博系统的代码,较为全面地介绍Web开发中涉及到的点。初衷是借由此仿写一个ruby版,不过ruby版什么时候起头还是个问题,暂且搁置,还是先从源码分析走起。好了废话不多说进入正篇。 本系列的源码是ThinkSNS3.1版的,也没有兴趣对其它版本再进行研究了,这个版本已经够用。 其实在模板渲染之前还有很多细节是需要讲的,但是还是觉得把最终呈现给用户的东西先讲是最有代入感的。 我们知道PHP作为一门为Web而生的语言,一个很核心的特点就是以标签的方式放入代码,并且可以和html页面代码相互嵌套。设想当我们访问微博的主页index.php时,用户如果已经登录,那么现实的是带有内容的主页,否则,显示的则是登录页面。那么index.php就承载了判断用户是否已登录和根据这个信息呈现不同样子的html页面两个任务。从代码层面上就有判断用户是否登录、已登录的页面显示、未登录的页面显示三个部分,其中前者基本是纯的php代码,而后两者是大量的html代码和少量php代码的结合体。最糟糕的情况是这三部分代码还是三个不同的人写的,每个人都要在这个大文件里修改自己的部分。不仅如此,揉合在一起也会给调试、测试、修改造成不必要的麻烦。而且html和php是两种完全不同性质的语言,看代码时思维还要在这两者之间频繁地切换,非常不方便。无论如何,代码都需要作解耦处理。 最简单的处理方式就是将进行业务逻辑(所谓的模型)的代码和进行实际显示(所谓的视图)的代码分离开来: <? if($logged_in){ $ui->show_main_view(); } else{ $ui->show_login_view(); }?> 在此基础上,我们通常把含有大量html代码的这部分显示逻辑写入到文件中,需要时从文件中再去读取,这样也一定程度上节省了开支,如果页面不被访问,它的代码就常驻在文件中。这样的文件就是一个模板文件。在ThinkSNS中,大部分的模板文件都在apps/public/default文件夹中。 由于最终显示页面中包含来自于业务逻辑的数据,模板文件中就会包含相应的php代码。为了尽量让html和php两种语言分离,使得不懂php语言的界面人员可以较为方便地编写模板文件,我们要定义一种新的语言,它比php要来的更加简单,容易上手,这种语言最终由php代码编译转化为包含php代码的页面。这样做一方面可以减少模板文件的编写难度,降低编写门槛,使得界面人员方便编写,另一方面也可以更好地剥离html代码和php语言。我们把这种语言称为模板语言,我们把由模板语言编写成的模板文件经由后端代码编译生成最终页面的整个过程称为模板的渲染。 事实上,作为偏好于前端人员的产物,模板语言有很高的自由度。ruby语言的一种模板语言slim甚至为了加快页面编写移除了html的标签号,所有的html标签只需写个标签名即可。模板语言的设计好坏有时候影响到一个web框架的受欢迎程度(模板语言一定程度上独立于框架,但显然很难完全从框架中剥离,毕竟要经由框架提供编译功能,而且编写好的模板文件也有时也作为框架的一部分,所以通常都和框架一并出现)。 模板语言的设计应该来说是个大问题,毕竟是设计一门语言,这个问题超出了本系列的讨论范围。幸好ThinkSNS设计的模板语言非常简单,只是在原来的php代码和html标签的基础上做很小的修改。 模板的渲染工作由内核目录下的Template.class.php完成,Template类提供了load方法,它使用loadTemplate($template_file)方法载入、编译一个模板文件,再调用include关键字执行编译后的代码。从磁盘载入后的模板文件借由自身的complier方法(其实叫compile更合适)进行编译,编译除了调用parse方法对模板文件进行解析外还增加了少量的安全代码,这里就不讨论了。parse方法是真正意义上解析模板语言的代码。它首先预处理include语法标签。ThinkSNS模板语言中的所谓include语法如下: <include file="__THEME__/public_header" /> […]

javascript漫谈(一):javascript世界中的对象构造

May 16, 2015 at 8:57 pm

第一次写javascript代码应该是很多年以前的事了,最初写的时候都还没能把PHP和javascript之间的区别理解清楚。当年写的图书管理系统,わざわざ地模仿了教务管理系统中当鼠标放在按钮上时按钮会变动的效果,那算是真正意义上第一次写。再后来估计就没写过了,一直到BS课上,写了一个空有虚壳的购物系统,之后是C#版的ThinkSNS微博迁移计划,这个时期都是jquery了。最近因为某个契机被当了回“前端”,想着阿既然写了一点代码了不如再多学一点吧不能再写那么傻逼的拿不出去的javascript了。 第二次刷新对javascript的认识是在玩MongoDB的时候。那时候才发现阿原来javascript也可以不做前端来用阿,当时老大还问了“JS是不是不能读取文件的?”,这个问题我倒是记住了。那时可能略微瞥过一眼Nodejs,但是也就那么一瞥,除了多知道了一个名词外再无他了。 本系列漫谈主要是记录javascript世界中的知识,可能或多或少的会提到jquery,MongoDB,Nodejs之类的。说实话本系列我也不知道会走多远,毕竟“我!不!是!前!端......”,无论如何,本系列会着重强调javascript中和传统编程语言不一样的地方。 作为开篇,本篇讨论的是javascript中的对象构造。 javascript中的一切东西都是对象。 似乎很少有语言可以这么说,但是我觉得javascript说这句话问心无愧。(作为一个准python程序员,python中的类是对象吗?这似乎是一个我需要探究一下的问题。) 我们可以通过下面的方式创建两个对象: var oObject = new Object(); var oStringObject = new String(); 表面上看这和一般的面向对象语言并无不同。按照Java的说法,我们定义了一个Object类的对象和一个String类的对象,Ojbect()和String()是不带参数的构造函数。如果硬要说不同的话,javascript允许不带参数的构造函数省略括号,正如C++中那样(事实上在c++中带括号和不带括号行为是不同的,当然这里不扯这个)。但是等等,javascript中有这样一个事实。 javascript中没有类。 是的。javascript中没有class关键字,更不用说public,private了(访问控制的解决方案,呃,加个下划线什么的,一定要告诉你的客户不要改下划线前缀的成员orz)。javascript的对象可以在任意时刻增加成员,由于“一切都是对象”,所以函数也是一样。下面的代码展示了最原始的过程。 […]

LCA算法入门

May 15, 2015 at 5:25 pm

好久没有做题了,随便找了一题,思考了一会儿以后觉得最短路加LCA应该就可以解决了(结果是最短路也不需要,简单的LCA就可以了,但是应该求哪些点的LCA反倒困扰了很久,最后暴力遍历一下过了...),于是很开心地打开两年前的LCA总结,于是很开心地对这份总结进行了大刀阔斧。其实说实在的相对于其它的总结而言,这份若干年前的总结算是比较有参考意义的,至少RMQ的话直接就可以用了,LCA的话关于怎么转化也记录的相对清楚,唯一的缺点是只有RMQ的代码没有LCA的代码。于是找了份n年前的实现(惨不忍睹),将总结修订了一下。总结包含RMQ和LCA两个部分,这里就单独把LCA拿出来吧。 LCA(Lowest Common Ancestor),即最近公共祖先,指的是对于一棵有根树T,查询T的两个结点u, v,找它们离根最远的公共祖先结点,离根最远也等价于深度最大。 为了解决LCA问题,需要将它转化为RMQ问题。为此首先要给出欧拉序列的概念。沿着树T进行DFS时沿途经过的点构成欧拉序列,输出欧拉序列的示例代码如下: void DFS(int u){ printf("%d\n",u); for(int i: in Vertex){ if(graph[u][i]){ DFS(i); printf("%d\n",u); } } } […]