Skip to content

liuyao729/stupy

 
 

Repository files navigation

Stupy, A Template Engine Extension for PHP

简介
    Stupy是一个C语言开发的PHP模板引擎。

特点
    1. 直接生成opcode,简洁,高效
    2. 模板语法与PHP保持一致,无需太多额外的学习
    3. 兼容Smarty基础语法。

声明
    Stupy的词、语法分析基于Zend Compiler,View类的封装基于Yaf的Yaf_Simple_View

下载
    https://github.com/fallgold/stupy/archive/v1.0.0.zip
    开发版:git clone https://github.com/fallgold/stupy.git
    目前仅支持php 5.3, 5.4
    暂无Windows版本
    Stupy经过充分的单元测试,内存泄露检测及线上环境验证,当前版本为1.0.0 beta版

安装
    如果编译安装过别的php扩展,那么Stupy的安装也是类似的,没装过的话,请你保持镇定,很简单:
    cd stupy-1.0.0
    phpize
    ./configure
    make 
    make install
    最后php.ini里加上 extension=stupy.so 然后重启php

Quick Start
    php :
        <?php
            $view = new StupyTpl();
            $view->assign("content", "Hello world!");
            $view->display("main.tpl");
        ?>
    
    main.tpl:
        header
        {$content}
        footer
    
    output:
        header
        Hello World!
        footer

模板语法
    以下例子统一使用此php:(文档中的output可能与程序原始输出有空格或换行上的差异)
        <?php
            $view = new StupyTpl();
            $view->assign("arr", array(
                    111111,
                    "key1" => "value111",
                    "key2" => "value222",
                ));
            $view->assign("n", 10);
            // you can also assign a variable like this:
            $view->max = 100; 
            
            // you can set the tpl file path, it must be a absolute path
            // $view->setScriptPath("/path/to/tpl/");
            $view->display("main.tpl");
            // equal to:
            // echo $view->render("main.tpl");
            // echo $view->eval( file_get_contents("main.tpl") );
        ?>

1. Basics
    main.tpl:
        aa{* comment here *}bb    {* 注释 *}
        {$n}                      {* 输出变量。Stupy左右定界符为{和},不可自定义。*}
        {$arr[0]}                 {* 输出数组元素 *}
        {$arr["key1"]}
        {$arr.key2}               {* . (dot)在Stupy中用于hash数组的值引用,
                                        而不再是字符串连接符,
                                        这是语法上不同于PHP的地方之一 *}
        {literal}{$a}{if}{/literal} {* literal标签内的内容不进行解析 *}
        {lit}{$b}{else}{/lit}     {* lit 是 literal 的简写形式 *}
        {ldelim}ccc{rdelim}       {* 转义左右定界符,即打印{和} *}
        {   ldelim}               {* 左定界符后不允许出现空字符,否则当普通文本处理 *}

    output:
        aabb 
        10
        111111
        value111
        value222
        {$a}{if}  
        {$b}{else} 
        {ccc}
        {   ldelim}

2. Compound
    main.tpl:
        {$n + 1} 
        {$n * 2}
        {abs($n) + 10}
        {(3 > 4) ? $n : $max}      {* 任何有返回值的php表达式,均可以输出 *}
        {? $n += 10 }              {* 只赋值,不输出 *}
        {? $n += 10 ?}             {* 如果你喜欢对称 *}
        {? $n+=++$n+$n++; ?}       {* 如果是C,还可以有更多的+, 但事实上你可以在{??}中
                                      写下超过10M的PHP代码——假如键盘不冒烟的话,
                                      此标签等同于<?php ?> *}
        $n: {$n}                   {* $n 应该已经变为几几几了 *}

    output:
        11 
        20
        20
        100
        $n: 94

3. if else
    main.tpl:
        {if $n >= 100}
            data data
        {else if $n >= 10}
            data data data data data
        {else}
            no data
        {/if}

    output:
        data data data data data

4. foreach
    main.tpl:
        {foreach ($arr as $key => $item)}   {* 小括号可以省略 *}
            item-{$key}-{$item} 
        {else}
            {* you can use else for a foreach, 
                if $arr is equal to false, 
                then output 'arr is empty' in current context 
            *}
            arr is empty
        {/foreach}
    
    output:
        item-0-111111 
        item-key1-value111 
        item-key2-value222

5. for 
    main.tpl:
        {for ($i = 0; $i < 3; $i++)}      {* 小括号不可省略,没有else语句 *}
            $i({$i}) say hi to his little brother $j
        {/for}

    output:
        $i(0) say hi to his little brother $j
        $i(1) say hi to his little brother $j
        $i(2) say hi to his little brother $j
    
6. for-in 
    main.tpl:
        {for ($key => $item in $arr)}     {* 小括号可以省略 *}
            item-{$key}-{$item} 
        {else}
            {* you can use else for a for-in, 
                if $arr is equal to false, 
                then output 'arr is empty' in current context 
            *}
            arr is empty
        {/for}
    
    output:
        item-0-111111 
        item-key1-value111 
        item-key2-value222

7. modifier
    modifier 即通过管道的形式进行函数调用,
        | 右边为函数名,| 左边为参数1, 参数2, 3 ... 以冒号隔开追加在函数名后。
    Stupy 提供的 system modifer 包括:default, e, df 
    
    a). system modifier
        main.tpl:
            {$a|default:"aaaaa"}         {* if param1($a in current context) is empty 
                                               then output the param2("aaaaa" in current 
                                               context) of the modifier *}
            {$a|default:aaaaa}           {* Stupy中modifier的参数把常量解析为字符串,
                                               即字符串引号可省略 *}
        output:
            aaaaa 
            aaaaa 
        
        main.tpl:
            {? $s = "<html>'\"</html>"}
            {$s|e}                       {* means: htmlentities($s, ENT_QUOTES, "UTF-8"); *}
            {? $flag = ENT_NOQUOTES}     {* 因为Stupy中modifier的参数把常量解析为字符串,
                                               所以先赋值,用变量传值 *}
            {$s|e:$flag:"UTF-8"}         {* means htmlentities($s, $flag, "UTF-8"); *}
        output:
            &lt;html&gt;&#039;&quot;&lt;/html&gt;
            &lt;html&gt;'"&lt;/html&gt;
        
        main.tpl:
            {"1234567890"|df}                  {* 即date_format *}
            {"1234567890"|df:"Y-m-d"}          {* use date() format *}
            {"1234567890"|df:"%m-%d %H:%M"}    {* use strftime() format *}
        output:
            2009-02-14 07:31
            2009-02-14
            02-14 07:31
    
    b). 直接使用php内置函数
        main.tpl:
            {"http://localhost/index?a=a&b=b"|urlencode}
            {$arr|count}
            {"\n\n"|nl2br}
            {"\n"|nl2br|substr:1:2}      {* multi modifier *}
        output:
            http%3A%2F%2Flocalhost%2Findex%3Fa%3Da%26b%3Db
            3
            <br /><br />
            br
            
    c). 自定义modifier
        对于 {$arr|mod1:aaa:bbb},你需要提供名字为mod1或者stupy_mod_mod1的函数。
        函数原型:
            function stupy_mod_mod1($param1, $param2, $param3) {
                // $param1,$param2,$param3将依次接收到$arr, "aaa", "bbb"
            }
        返回:
            如果不是multi modifier调用或者当前modifier在最右边,
            函数返回值将作为输出使用,
            否则将作为下一个(即其右边的)modifier的param1
        自动加载:
            如果Stupy在解析的时候对应modifier的函数不存在,
            将根据static options配置进行自动加载,
            请参考后面的static options说明中的tpl_mod_pattern, tpl_mod_file_pattern。

8. function
    即函数调用。Stupy没有提供system function,此功能只是预留用于扩展模板功能。
    对于{funca p1="1111" p2="2222" p3="3333"},你需要提供名字为funca或者stupy_mod_funca的函数
    函数原型:
        function stupy_mod_funca($p) {
            // $p 将接收到 array("p1" => "1111", "p2" => "2222", "p3" => "3333" )
        }
    返回:
        函数返回值作为输出使用
    自动加载:
        同自定义modifier
    Advance: 
        Stupy模板语法与PHP保持一致,在function的处理上继承了PHP数组的灵活性,你可以这么调用:
            {funca "1111" "2222" "3333"} // $p 将接收到 array("1111", "2222", "3333" )
        还可以这么调用:
            {funca "1111" p2="2222" "3333"} // $p 将接收到 array("1111", "p2" => "2222", "3333" )
    注意:可以使用function的方式调用php内置函数,但不提倡这么做,适合场景也不常见
        比如:{count $arr} 将输出1, 而不是3, 因为 count接收到的是 array($arr).
        这种情况下,应该直接使用basics语法 {count($arr)},或者modifier语法 {$arr|count}

9. tag
    tag调用方式同function,可认为是function的一种特殊类型,这种类型的function不需要通常意义上的函数计算,只是一些子模板的解析。
    Stupy 提供的 system tag 包括:js, options
    a). system tag
        main.tpl:
            {js "www/js/funcs.js"}
            <select name="name1">
                {? $selected = "value2"}
                {options array("value1" => "label1", "value2" => "label2") $selected}
            </select>
        output:
            <script language="javascript" src="www/js/funcs.js"></script>
            <select name="name1">
                <option value="value1">label1</option>
                <option value="value2" selected="selected">label2</option>
            </select>
            
    b). 自定义tag
        StupyTpl::setTag(tag_name, tag_content);
        类似于function,在子模板(tag_content)中将接收到$p
        比如 system tag js 等同于:
            StupyTpl::setTag('js', '<script language="javascript" src="{$p[0]}"></script>');
            system tag options 等同于:
            StupyTpl::setTag('js', '{foreach $p[0] as $k => $v}
                    <option value="{$k}"{if $p[1] == $k} selected="selected"{/if}>{$v}</option>
                {/foreach}');

    c). 自定义system tag
        定义系统级别的tag,在php 模块初始化的时候加载,同一php进程里,每一次request的时候不需要重复定义。
        在php.ini中设置 stupy.tpl_tags = /path/stupy_tags.tpl
        tags文件格式参考源代码目录中的 tests/plugins/stupy_tags.tpl
    
    注意:tag优先级高于function,如果同时定义了同名tag和function,此名字相关的调用优先解析为tag
          自定义tag必须在stupy进行模板解析前定义,否则根据优先级,将接着寻找同名function,无同名function,则尝试function的自动加载

10. include
    header.tpl:
        header header header
    footer.tpl:
        footer footer footer
    main.tpl:
        {include "header.tpl"}
        {include file="header.tpl"}
            Hello world!
        {include "footer.tpl"}
    output:
        header header header
        header header header
            Hello world
        footer footer footer

11. function-t-z
    function-t-z是另一种特殊类型的function,函数名只能是从t至z这些字母中的一个。
    不同于普通function,function-t-z的参数不进行解析,所有函数名后的字符将作为一个字符串进行传递。
    function-t-z 适用于语言翻译,以及给可能的自定义解析程序预留接口。
    function-t-z 优先级高于function 和tag。

12. static options
    Stupy提供了一些设置选项,这些选项都是静态选项,影响的是StupyTpl类的全局,而不能针对单独的实例进行设置。
    StupyTpl::setStaticOption(option_name, option_value);
    所有option:
        tpl_mod_pattern, string
            default: stupy_mod_%s
            设置自定义modifier/function的函数名模式,必须包含(至多)一个%s,用于填充mod名。

        tpl_mod_file_pattern, string
            default: {当前执行的php文件所在目录}/stupy_mod_%s.php
            设置自定义modifier/function 加载的文件名,必须是绝对路径,可包含(至多)一个%s,用于填充mod名。

        tpl_opti_spaces, int
            default: 0
            是否自动优化多余的spaces字符

        tpl_include_dir, string
            default: {当前解析的tpl文件所在目录}
            include命令加载文件时使用的路径,StupyTpl::setScriptPath() 方法将同步修改此option为相同路径

        tpl_include_extend, int
            default: 1
            include命令的处理方式(0:include , 1:check+warning, 2:check, 3: extend)
            如果不启用opcode cache 模块,此选项无效
            如果启用了opcode cache 模块,则可能发生main tpl 的opcode被缓存,而后修改了sub tpl的情况,不同选项将影响修改结果是否能立即正确显示。
                    include: 每次重新编译,能正确显示
                    extend: sub tpl opcode与top tpl opcode 一起缓存,不能正确显示,直到opcode失效(重启php),或着touch top tpl
                    check: 每次request进行sub tpl filemtime check,能正确显示
            效率:extend > check > include
            注意:在启用opcode cache的情况下,修改此option有可能需要重启php才能达到完全的预期效果。
                    比如,修改前tpl_include_extend设置为3,此时sub tpl已经cached,不管tpl_include_extend修改为何值,在opcode失效(重启php)前,修改将不产生效果。

        use_request_time, int
            default: 1
            是否使用sapi的request time代替time()

        tpl_suppress, int
            default: E_NOTICE
            模板解析时,需要屏蔽的php错误类型

        tpl_tag_symbol, int
            default: 1
            tag_content excute时,是否使用独立的符号表

13. 已知问题
    a. 在php5.3中,render,eval时,tpl中使用了ob_start函数(包括在{? ?}或function中使用),将引起输出异常。
    

About

Stupy, An Template Engine Extension for PHP

Resources

License

Stars

Watchers

Forks

Packages

No packages published