RainTPL模板

模板引擎

模板引擎是渲染生成HTML页面的工具程序,它可以把数据嵌入到html代码中,动态地生成完整的页面。许多人认为PHP本身就是模板引擎,因为它可以支持直接嵌入在html页面中书写代码。不过我认为还是有使用模板引擎的必要,因为直接嵌入的PHP代码如果不对数据做检查,就可能会被报出notice或者warning,如果加上检查数据类型的代码,又太难看了。

PHP老牌的模板引擎是Smarty,xts在选择模板引擎的时候也曾考虑使用它,不过由于Smarty性能相对比较差,就没有作为xts首选的模板引擎。xts选择了更轻更快的RainTPL作为模板引擎,为了更贴合xts中的使用现状,我将RainTPL加以修改,改写成了xts\HailStone类。

RainTPL模板语法

变量

变量的语法和Smarty很像,直接由花括号括起变量名就是。下面是Hello, world例子中的模板:

<h2>Hello, {$to}</h2>

RainTPL也支持对变量使用modifier:

<date>{$order.timestamp|date:%Y-%m-%d}</date>

可以输出带+、-、*、/、%运算符的变量表达式:

<div>{$a} + {$b} = {$a+$b}</div>

如果需要使用_SESSION, _GET, _POST, _SERVER等超全局变量,请使用{$GLOBALS}访问

注意:RainTPL不支持修改边界为其它字符,花括号是直接写死在代码里的

常量

好像模板引擎需要输出常量的情况不多,不过还是可以支持,用花括号加井号的语法:

{#X_PROJECT_ROOT#}

if语句

if语法和PHP的一样,可以直接地condition中使用PHP变量和表达式。

{if condition="$age < 20"}
    less than 20 years
{elseif condition="$age < 30"}
    less than 30 years
{else}
    30 or more
{/if}

循环

RainTPL只支持foreach循环

{loop="$items"}
    <a href="/show?id={$value.id}">{$value.name}</a>
{/loop}

在循环中,可以使用三个变量

  • $key 是当前数组元素的键
  • $value 是当前数组元素的值
  • $counter 是循环计数器,从0开始

引用其它模板

可以用include引入其它模板

{include="common/header_partial"}

引入其它模板的时候,模板文件扩展名可以省略,xts会默认使用.html当后缀

调用函数

可以在模板中调用PHP函数

{function="pagination($selected_page)"}

RainTPL会打印出函数运行的结果。

模板注释

模板注释的语法和Smarty一样

{* 这是注释 *}

还有一种写法更友善一些

{ignore}
    这里都是注释
{/ignore}

原样输出

写在{noparse}{/noparse}之间的东西会原样输出

<script type="text/javascript">
    {noparse}
    var f = {status: 5};
    {/noparse}
</script>

xts中的模板

xts中的模板需要实现xts\View接口。这个接口要求模板引擎实现setPagetTitle、setLayout等一系列方法。一般模板需要在每个页面include进来header和footer,xts的模板则反过来,在layout中include具体页面的模板,具体的模板名称通过render函数的参数传递。

X::view()
    ->setPageTitle('Hello world')
    ->render('index', array(
    'to' => 'world',
), '1');

在这个例子中,X::view()返回了模板引擎类,setPageTitle设置页面的标题。render函数第一个参数是模板名称,这里表示要渲染的模板名为index;第二个参数是赋给模板引擎的变量数组,这个数组里的Key会成为模板中的变量名称;第三个参数是模板渲染后缓存的key。

一般网站都会在各个页面共用header和footer,虽然模板引擎提供了include支持,但是在每个页面的模板上都include头尾非常不便。xts的模板采用了布局模板include内容模板的反向引用方式,在render函数中指定的模板名称是内容模板的名称,会在默认名为layout的模板中把它include进来。

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
    <style media="handheld" type="text/css"></style>
    <link rel="stylesheet" href="/static/css/common.css">
    <title>{$_page_title|escape}</title>
</head>
<body>
{include="$_content_template"}
</body>
</html>

上面是布局模板的范例,在<body>标签中引用了内容模板,内容模板的例子在下面。

<h2>Hello, {$to}</h2>

更改Layout

有的时候,网站中会出现部分页面布局不同的情况,header和footer和其它页面有所不同,这时候可以定义一个新的layout,然后直接用setLayout函数更新layout模板的名字。

X::view()->setLayout('new_layout')
    ->render('special_page');

在内容模板中输出外部css和js

内容页面常常需要引入一些外部的css文件以及js文件,需要把这些css引用写到layout的首部,把js引用写到layout的尾部。因为这些都是属于纯前端的东西,如果在PHP里assign一个变量然后输出会非常不友好。在把RainTPL移植到xts之后,我扩展了一个剪辑定义和输出的功能来解决这个问题。

可以在layout中留下剪辑输出的代码:

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
    <style media="handheld" type="text/css"></style>
    <link rel="stylesheet" href="/static/css/common.css">
    {clip="css"}
    <title>{$_page_title|escape}</title>
</head>
<body>
{include="$_content_template"}
{clip="js"}
</body>
</html>

在内容模板中可以定义剪辑的内容,clipdef可以支持多次定义,xts会追加处理:

{clipdef="css"}<link rel="stylesheet" href="/static/css/index.css">{/clipdef}
{clipdef="css"}<link rel="stylesheet" href="/static/css/nothing.css">{/clipdef}
<h2>Hello, {$to}</h2>
{clipdef="js"}
    <script src="/static/js/index.js"></script>
    <script src="/static/js/checker.js"></script>
{/clipdef}

clip和clipdef是HailStone扩展的语法,它们的位置可以在任何模板中使用。clipdef的内容会在HailStone渲染页面的时候处理生成,完成渲染后替换到同名的clip位置上。

注意:需要在配置文件里把enable_clip设置为true才能使用此功能。

取回渲染好的html

有的时候不并希望渲染好的页面直接输出到浏览器上,而是希望能把页面html以字符串的方式取回。可以调用renderFetch函数来达成这一目的

X::view()->renderFetch('index', array(
    'to' => 'world',
), '1');

renderFetch函数的参数和render函数完全相同,唯一的区别是返回值不同。

xts路由器

单入口PHP和路由

早期的PHP程序都使用CGI方式实现,根据Web服务器上定义的网站根目录找到对应的PHP文件开始执行,于是文件路径也就成为PHP程序的天然路由器。访问不同的URL,自然会运行不同的PHP程序,彼此隔离,互不干扰,就正是多入口程序系统。不过由于的确存在一些需要复用的代码,比如系统配置,公共函数等等,所以PHP提供了include或者require方式来引入另一个文件PHP文件。这样还是存在一个问题,就是几乎所有的PHP文件都要先include一下,非常不方便。后来PHP5时代提供了autoload方法,让大家可以不必四处写include了。不过autoload的性能实在不怎么样,每次调用它都需要扫描一遍inlcude_path,才能正确地把文件引入,再说它只能支持类引入,对函数就无能为力。

后来兴起的众多PHP框架都开始使用单入口方式。所有的请求都被Web服务器rewrite到一个PHP入口文件中,然后由它完成程序初始化工作,最后按照配置规则加载和运行开发人员编写代码。这样程序会有一个集中的地方加载需要的模块,而且能很自由地定义URL结构,甚至把一部分参数作为URL的一部分。

apple组件

xts提供了一个名为apple的组件来管理程序的路由入口。在protected/config/debug.php的组件配置部分可以找到如下片段:

'apple' => array(
    'class' => '\\xts\\Apple',
    'singleton' => true,
    'conf' => array(
        'actionDir' => X_PROJECT_ROOT.'/protected/action',
        'defaultAction' => '/index',
        'actionPrefix' => 'action_',
        'preAction' => '',
        'preActionFile' => '',
    ),
),

apple组件是一个xts\Apple类的单件实例,它除了提供路由功能之外,还提供程序应用程序终止、页面跳转以及JSON输出等支持功能。

action函数

我在为xts设计路由功能的时候,希望能尽可能简单一点,所以没有像其它MVC框架那样使用Controller/View两层结构,也没有提供基于正则表达式的路由规则配置。正如前面马上开始里Hello world的例子,xts路由会直接从protected/action目录里寻找最接近的匹配文件载入,并调用同名的函数作为action函数。这里的protected/action目录是被apple组件actionDir配置项定义的,如果修改此配置项的地址,可以将action函数文件放在其它地方。

xts路由功能会完成下面的几个步骤:

  1. 在actionDir中查找和引入匹配的PHP文件;
  2. 确定action函数
  3. 调用action函数

路由规则

xts的路由查找是根据$_SERVER['REQUEST_URI']变量进行的,在配置actionDir定义的目录里寻找,尽最大可能匹配目录和文件。xts在进行路由查找的时候,会去掉URL上的文件扩展名和参数,所以对于xts来说/a/b/c.html/a/b/c.htm以及/a/b/c.php?id=5的查找规则是一样的,都会当作/a/b/c来查找对应的文件。

我们以请求/a/b/c/d来说明xts的查找过程,xts会先尝试加载{{actionDir}}/a/b/c/d.php这个文件,如果此文件不存在,xts会再尝试{{actionDir}}/a/b/c.php,如果仍未找到,会再尝试{{actionDir}}/a/b.php,最后,xts会尝试{{actionDir}}/a.php。如果xts没有找到可以匹配的文件,就不会引入任何的文件。这意味着用户可以不把action函数写在路由规则对应的文件里。当这种情况发生时,xts会把URI最后一级的名称作为action函数的名称,前面的例子中会以d作为action函数名。

action函数前綴

在前面按规则路由的过程中,xts已经确定了action函数的基础名称,就是被引入文件的主文件名。不过有不少URL中常用的英文单词被列为PHP语言保留字,或者正好有同名的built-in函数存在。比如想在列表页使用list这个名字,可是list是PHP语言的保留字,用于将数组的值按位置赋给变量;再比如某个WebServiceAPI统计内容数量的接口想使用count,但count是PHP的内建函数,用于统计数组的元素个数。为了解决这一冲突的问题,xts允许为action函数在基础名称前面加一个前缀,路由的时候还是按前面的规则找匹配的文件,文件中的函数名则优先使用带前缀的版本。action函数的前缀在配置项actionPrefix中定义,默认是action_

有了action函数前缀的帮助,我们就可以在{{actionDir}}/article/list.php里定义action_list这个函数,xts会在用户访问/article/list的时候调用action_list函数。action函数前缀定义之后并不是非加不可,对于没有冲突的情况,依旧可以使用和文件名相同的基础名称作为函数名。比如用户访问URI /article/show的时候,还是可以在{{actionDir}}/article/show.php文件里定义名为show的函数,xts也会正确地调用它。带前缀的函数名优先于不带前缀的函数,所以如果同时定义了带前缀和不带前缀的两个函数,xts只会调用带前缀的那个。

不必担心用户会利用路由规则调用PHP的内建函数,xts会阻止所有的这种请求

智能参数映射

xts支持将GET参数映射到action函数的参数表中。比如设计用户请求的URI是/article/list?page=3&category=diary,那么action_list函数可以定义接收参数page和category。

function action_list($category='default', $page=3) {
    // 列出文章列表
}

这个例子中,在action_list函数内可以直接使用$page和$category参数,xts会把$_GET中的同名参数直接赋值进来。因为是按名字赋值,所以和用户访问时的参数顺序无关,请求URI改成/article/list?category=diary&page=3和前面是等价的。

如果action函数声明的参数不可省略,而请求的$_GET数组又没有同名参数,那么xts会返回一个400错误

除了命名参数可以自动赋值外,xts现在还支持另一种给action函数的参数赋值的方式——基于位置的参数。还是以例子说明,如果设计网站的用户个人主页URI是这样的格式/user/{{name}},其中{{name}}是用户的名字。那么可以在{{actionDir}}/user.php中定义user函数,接收name参数:

function user($name) {
    // 加载用户信息
}

基于位置的参数也可以支持多个参数,比如URI /article/list/diary/3可以对应到action_list($category, $page)函数上,diary是第一个参数,3是第二个参数。

注意:基于位置的参数和命名参数映射不能混合使用

fallback_action

如果依前面方法路由确定的action函数没有定义,xts会检查是否存在名为fallback_action的函数,如果存在就调用它。fallback_action可以用来输出自定义的404页面。或者根据请求给出一些提示。如果fallback_action也没有定义,xts会直接返回HTTP状态码404,并以404 Not Found作为页面内容输出。

preAction函数

经常会有一些需要在正式逻辑开始前的准备工作,比如检查用户的登录状态,检查权限什么的。preAction就是在所有action执行前运行的一个函数。这个函数存放的文件位置由preActionFile配置项定义,函数的名称由preAction配置项定义。这两个配置默认为空,表示不启用此功能。下面是一个检查用户登录的例子:

function check_login($action) {
    $guestAllowed = array('/index','/login','/register',);
    if (!in_array($action, $guestAllowed)) {
        $user = X::orange('user')->load($_SESSION['user_id']);
        if($user instanceof User)
            $_SESSION['current_user'] = $user;
        else
            X::apple()->redirect('/login')->end();
    }
}

页面跳转

页面跳转是很常用的功能,一般需要自己写一个header输出,xts提供了一个简单的封装,即xts\Apple::redirect函数。在前一小节preAction函数的例子中已经使用到了。除了可以指定跳转的地址外,redirect方法还可以接收第二个参数$statusCode。第二个是可选的参数,用于指定服务器的返回状态码,默认情况下是302。redirect函数返回Apple对象自身,支持链式调用。

终止程序运行

在action函数中可以直接return来终止程序的运行。如果在其它地方,可以调用X::apple()->end()方法。不建议使用PHP的die或者exit来结束程序,主要是出于兼容性和未来进行性能评估方便的考虑。调用end方法终止程序之后,index.php里后续的程序还是会运行的,比如设置性能评估终止标记,写入日志等等。

组件

组件

整个xts框架都由组件构成。xts提供的所有功能被封装在若干个组件对象里,组件可以通过配置绑定在XComponentFactory类上以方便调用,XComponentFactory类还有一个缩写,就是X类。xts的组件需要实现xts\IComponent接口,这个接口的定义了xts组件必需是可配置的,而且要支持Getter和Setter的类。

interface IComponent {
    /**
     * @param array $conf
     * @return void
     */
    public static function conf($conf=array());

    /**
     * @param string $name
     * @return mixed
     */
    public function __get($name);

    /**
     * @param string $name
     * @param mixed $value
     * @return void
     */
    public function __set($name, $value);

    /**
     * @param string $name
     * @return bool
     */
    public function __isset($name);

    /**
     * @return array
     */
    public function getConf();
}

xts本身已经提供了一批组件,涵盖小型项目需要的各项功能,包括:

  • apple——应用路由器
  • cache——内存缓存(支持使用memcache或者redis)
  • cc——文件缓存(本机缓存)
  • db——MySQL数据库查询器
  • orange——对象关系映射(ORM)类
  • view——RainTPL模板引擎
  • valley——表单数据验证器
  • redis——PhpRedis的xts封装
  • smarty——Smarty模板引擎的xts包装(需要用户自己下载smarty程序)

自定义组件

用户也可能开发自己的组件,或者把第三方库包装成xts组件。

开发自己的组件比较简单,可以直接继承xts\Component类,在这个类中已经实现了IComponent接口,完成了基本的Getter和Setter支持。如果要把第三方的库包装成xts组件,就需要用户自己实现IComponent接口。实现IComponent接口时可以参考xts\Component类中的代码,xts中类的属性Getter的命名格式应该是getFullName这样的驼峰命名。未来xts考虑提供一个trait来实现这个接口的基本功能。

组件工厂

XComponentFactory类能根据配置生成组件对象实例。它支持单例模式,配置单例模式后生成的实例会在组件工厂内保留一份引用,下次请求时会直接return,不会再重新new创建实例。下面的配置定义了一个view组件:

  1. 它是xts\Hail类的实例
  2. 初始化以前应该先require {{X_LIB_ROOT}}/hail.php文件(X_LIB_ROOT是xts框架的库目录路径常量)
  3. 它要求启用单例支持

    array( 'component' => array( 'view' => array( 'class' => '\xts\Hail', 'require' => X_LIB_ROOT.'/hail.php', 'singleton' => true, 'conf' => array( 'tpl_dir' => X_PROJECT_ROOT.'/protected/view', 'tpl_ext' => 'html', 'compile_dir' => X_RUNTIME_ROOT.'/compiled_template', 'enable_clip' => false, 'cacheId' => 'cc', // string to cache component id or false to disable cache 'cacheDuration' => 60, // page cache duration, second ) ), ), ),

在前面马上开始的例子里X::view()会触发xts\XComponentFactory类中的__callStatic魔术方法,这个魔术方法会取回配置文件里定义的组件ID为view的组件,在实例化之前,配置中conf数组会传递给组件类的conf静态方法。在前面xts配置的例子里,配置文件定义了apple、db、view三个组件,就可以用X::apple()X::db()X::view()的方式直接访问。

使用XComponentFactory初始化组件的时候,参数会传递给组件类的构造函数。比如下面的组件配置声明了一个不使用单件的orange组件,返回xts\Orange类的实例:

array(
    'component' => array(
        'orange' => array(
            'class' => '\\xts\\Orange',
            'singleton' => false,
            'conf' => array(
                'queryId' => 'db',
                'enableCacheByDefault' => false,
            ),
        ),
    ),
),

调用X::orange('user')的时候,组件工厂会返回相当于new Orange('user')的执行结果。

xts配置

Object Based Program

PHP最早和C语言一样,走面向过程的道路。作为一个典型的CGI应用,PHP程序从Web服务器请求.php文件开始,到返回页面HTML结束。开发PHP程序的主要任务有两种:验证数据,写入数据库;准备数据,组织页面。不论哪种程序的编写,用面向过程的方式都会非常直观和清晰。

Web项目越发复杂后,PHP从5.0开始提供比较强大的OOP机制,也出现了一批OOP的开发框架。但是很多PHP程序员都深刻感觉到,想跟踪框架的代码运行过程非常绕,非常困难。这是因为OOP开发更注重类和对象的概念,认为编程就是对象间彼此通信,发送消息,所以具体的代码被割裂到许多类的许多方法中。

OOP也有好处,它提供了一个比关联数组更严谨的操作机制。比如我们可以通过编写一个Getter来格式化数据库里取出的某个字段,甚至可以创造一个DataBase里没有字段在运行时使用。虽然关联数组也可以随时创建一个key保存任意的value,但分散于代码各处的写入和读取操作会给软件维护带来很大的麻烦。有一种观点是,面向对象机制其实给开发人员提供了一种逻辑上更贴近自然的代码分类、组织和管理的方式。

xts提倡基于对象开发的模式。保留PHP既有的过程化开发,同时对于一些可以内敛的地方用对象加以包装,提升代码复用率。xts不建议开发人员修改框架中的代码,可以直接简单的使用之。

覆盖式配置

在项目的protected/config目录里有两个配置文件,一个人是debug.php,另一个是release.php。xts默认使用debug.php中的配置。当运行build脚本时,xts会把webroot/xts.php文件中定义的X_DEBUG常量改为false,此时xts会使用release.php中的配置覆盖debug.php中的同名配置。

覆盖式配置的好处是相同的配置项无需写两遍,只需要在release.php中写上不同的配置项即可。我曾不止一次遇到需求要改配置即可完成,在debug配置里改了,release配置里忘了改,测试没有任何问题,上线就出bug。xts的等位覆盖机制可以完美避免此类问题。

下面是一个debug配置的例子:

<?php
return array(
    'component' => array(
        'apple' => array(
            'class' => '\\xts\\Apple',
            'singleton' => true,
            'conf' => array(
                'actionDir' => X_PROJECT_ROOT.'/protected/action',
                'defaultAction' => '/index',
                'actionPrefix' => 'action_',
                'preAction' => '',
                'preActionFile' => '',
            ),
        ),
        'db' => array(
            'class' => '\\xts\\Query',
            'singleton' => true,
            'conf' => array(
                'host' => 'localhost',
                'port' => 3306,
                'schema' => 'xts',
                'charset' => 'utf8',
                'user' => 'xts',
                'password' => 'xtstest',
                'persistent' => false,
            ),
        ),
        'view' => array(
            'class' => '\\xts\\Hail',
            'require' => X_LIB_ROOT.'/hail.php',
            'singleton' => true,
            'conf' => array(
                'tpl_dir' => X_PROJECT_ROOT.'/protected/view',
                'tpl_ext' => 'html',
                'compile_dir' => X_RUNTIME_ROOT.'/compiled_template',
                'enable_clip' => false,
                'cacheId' => 'cc', // string to cache component id or false to disable cache
                'cacheDuration' => 60, // page cache duration, second
            )
        ),
    ),
);

在release配置中,由于只有数据库的库名、登录密码、长连接的配置有所不同,所以可以只写这些:

<?php
return array(
    'component' => array(
        'db' => array(
            'conf' => array(
                'schema' => 'xts_example',
                'password' => 'dk23Jssk887^0',
                'persistent' => true,
            ),
        ),
    ),
);

xts在X_DEBUG置为false时,用release配置覆盖debug配置,合并成实际使用的配置数组。

马上开始

从xts-init初始化

虽然xts最早实现的是ORM功能,个人感觉最有价值的也是这块,但是入门还是能马上动手开始实践比较好。xts-init项目提供了一个可以快速开始的项目文件目录模板。直接clone这个项目并重命名就可以了:

git clone git@github.com:SyuTingSong/xts-init.git MyProject

因为xts项目是以git子模块的方式存在于xts-init项目中,所以还需要用相应命令取出它的代码。

cd MyProject
git submodule init
git submodule update

接下来还需要清理一下xts-init项目原本的git信息:

rm -rf .git .gitmodules framework/.git

现在整个xts框架目录就已经准备好了。

目录结构说明

从xts-init初始化好的目录结构如下:

.
├── CREDITS
├── LICENSE
├── README.md
├── build
├── build.tools
│   ├── turn-off-debug.patch
│   └── yuicompressor-2.4.8.jar
├── framework
│   ├── CJSON.php
│   ├── CREDITS
│   ├── LICENSE
│   ├── README.md
│   ├── apple.php
│   ├── base.php
│   ├── cache.php
│   ├── hail.php
│   ├── orange.php
│   ├── redis.php
│   ├── smarty.php
│   ├── valley.php
│   ├── view.php
│   └── x.php
├── protected
│   ├── action
│   │   └── index.php
│   ├── common.php
│   ├── config
│   │   ├── debug.php
│   │   └── release.php
│   ├── runtime
│   └── view
│       ├── index.html
│       └── layout.html
├── static
│   ├── css
│   │   └── common.css
│   ├── img
│   └── js
└── webroot
    ├── index.php
    ├── static -> ../static/
    └── xts.php

第一级主要有framework、protected、static、webroot和build.tools五个目录。framework目录里存放的是xts框架的源代码;protected目录用于存放我们的自己的程序代码、页面模板以及配置文件等等;static目录里保存静态的资源文件,包括css、JavaScript、图片、Web字体等等;webroot目录是Web服务器的根目录,可以直接被浏览器访问到,一般只有一个入口文件;build.tools是xts框架的编译工具,里面放置js压缩器还有关闭debug模式的patch。

webroot里有一个指向static的软链接,这样浏览器可以直接访问到static目录里的资源。把static目录放在webroot之外是为了方便地把static目录单独打包发布到CDN上。xts的build脚本可以把静态资源文件引用(就是HTML里的link还有img的src等等)替换成独立的静态服务器域名或者CDN域名。

protected目录里一般会有action、config、runtime、model、view几个目录。action目录用于存在controller代码;config里存在配置文件,xts的配置文件也是php程序,是一个大关联数组;runtime目录里存在一些运行时生成的数据,包括文件缓存,编译出来的模板,运行日志等等;model目录在xts-init里不会创建,这个目录是存在自定义数据库ORM对象的;view目录里存放xts的模板,一般以.html为后缀。

Hello, world

前面介绍了一大通xts的目录结构,现在马上开始试试写代码运行吧。打开protected/action/index.php文件,可以看到里面已经有Hello, world的Controller代码了。

<?php
function index() {
    X::view()
        ->setPageTitle('Hello world')
        ->render('index', array(
        'to' => 'world',
    ), '1');
}

每个xts的action都是一个函数,在protected/action目录里,存放在与之同名的.php文件中。xts的路由规则是尽最大可能匹配目录和文件。比如请求/a/b/c/d就会先尝试加载protected/action/a/b/c/d.php文件,如果不存在,会再尝试protected/action/a/b/c.php,如果仍未找到,会尝试protected/action/a/b.php,最后到protected/action/a.php。相应的,xts也会尝试调用d()、c()、b()最后是a()。调用的函数名总是和文件名相同例外情况

大写X是xts框架的一个静态类,上面绑定了根据配置文件加载的工具类。X::view()会返回一个RainTPL模板引擎的实例。当然,这里使用的RainTPL已经被修改以适合xts的整结构。这个action会通知模板引擎,渲染index模板,并传递参数$to = world。

打开protected/view/layout.htmlprotected/view/index.html文件,可以看到模板的代码。layout模板里写的是各页面公共的html片段,一般包括页面的css引用,header、footer、nav bar等等。

<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
    <style media="handheld" type="text/css"></style>
    <link rel="stylesheet" href="/static/css/common.css" />
    <title>{$_page_title|escape}</title>
    </head>
<body>
{include="$_content_template"}
</body>
</html>

index模板里写的是具体页面的内容,它会被嵌入到layout的{include="$_content_template"}位置。

<h2>Hello, {$to}</h2>

在index模板里,使用来自action函数中赋值的变量$to。

Web服务器的配置

xts是单入口的框架,需要在web服务器上配置rewrite把所有请求都rewrite给webroot/index.php脚本才能使用。以nginx为例,可以使用它的try_files。

try_files $uri $uri/ /index.php?$args;

总之需要确保站点访问所有基于xts的程序都交给index.php就好了。

推荐的IDE

推荐使用IDEA出口的PhpStorm来开发xts程序,能获得最好的自动完成提示、方法参数说明等。如果不想花钱,ActiveState出品的Komodo Edit也是一个不错的选择,虽然比PhpStorm要差一些。最好不要使用Vi之类的编辑器开发,因为xts框架的一个设计就是利用IDE的自动完成最高效地开发程序,用Vi效率降一半呀。

命名

xts框架在类的方法和属性中使用驼峰命名法,在数据库、函数、网页参数中使用帕斯卡命名法。