Press "Enter" to skip to content

定制自己的PHP语法-在PHP中实现unless

首先 ,插一句题外话: 从我发表第一篇PHP执行原理的文章到现在, 已经快2年了, 我很高兴的看到这俩年里PHP中文圈里 ,越来越多的人开始研究PHP源码, 也有越来越多的人开始从事PHP扩展开发.
关于PHP的执行原理, 我想我BLOG的读者都能脱口而出:

1. 词法分析, 去掉注释, 空白, 得到TOKEN
2. 语法分析, 在这个过程中生成Opcode array (op_array)
3. 解释执行, 执行op_array, 一条一条的解释执行Opline(SWITCH, CALL, GOTO)

今天有人问我, 说他看到有PHPer在说unless语句, 我很是纳闷, 后来才知道, 原来是一个国外的PHP大牛自己Hack了PHP的源码, 加入了一个unless语句.
很有意思, 今天, 我也就在这里为大家演示, 如何为我们自己的PHP加入unless语句..
如果你是不了解PHP的执行过程, 请先花点时间看看我之前的文章深入理解PHP原理之Opcodes:
我们的目标, 是要实现如下的语法(以php 5.2.11为基础):

<?php
unless(TRUE) {
//这不会被执行
}
unless(FALSE) {
//这会被执行
}

好得, 看起来unless是if的反义词了.. 那么就好办了, if怎么来, 我们相反的来就可以了..
首先, 词法分析阶段, 我们需要添加unless的TOKEN定义, 编辑Zend/zend_language_scanner.l:

//添加unless的Token定义
<st_IN_SCRIPTING>"unless" {
    return T_UNLESS;
}
<st_IN_SCRIPTING>"if" {
    return T_IF;
}
<st_IN_SCRIPTING>"elseif" {
    return T_ELSEIF;
}

这样, 词法分析器遇到unless的时候, 就会报告发现了一个T_UNLESS Token.
接下来就需要在语法分析阶段, 定义T_UNLESS的语法动作了, 编辑Zend/zend_language_parse.y

unticked_statement:
        '{' inner_statement_list '}'
    |   T_IF '(' expr ')' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); }
		statement { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); }
		elseif_list else_single { zend_do_if_end(TSRMLS_C); }
//添加T_UNLESS的语法动作
	|   T_UNLESS '(' expr ')' { zend_do_unless_cond(&$3, &$4 TSRMLS_CC);}
		statement {zend_do_if_after_statement(&$4, 1 TSRMLS_CC);}
				  {zend_do_if_end(TSRMLS_C)};

对的, 因为if和unless只是在条件的真假上不同, 所以我在if的基础上, 做了这个简单的hack, 接下来, 就应该定义zend_do_unless_cond了, 这个逻辑是用来生成OPCODE的. 还是从zend_do_if_cond为基础来做修改, Zend/zend_compile.c:

void zend_do_if_cond(znode *cond, znode *closing_bracket_token TSRMLS_DC)
{
    int if_cond_op_number = get_next_op_number(CG(active_op_array));
    zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
    opline->opcode = ZEND_JMPZ;  //如果为0则跳转
    opline->op1 = *cond;
    closing_bracket_token->u.opline_num = if_cond_op_number; //跳转地址
    SET_UNUSED(opline->op2);
    INC_BPC(CG(active_op_array));
}

OK, 那么我们的zend_do_unless_cond就可以这样定义:

void zend_do_unless_cond(znode *cond, znode *closing_bracket_token TSRMLS_DC)
{
    int if_cond_op_number = get_next_op_number(CG(active_op_array));
    zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
    opline->opcode = ZEND_JMPNZ;  //如果为不为0则跳转
    opline->op1 = *cond;
    closing_bracket_token->u.opline_num = if_cond_op_number;
    SET_UNUSED(opline->op2);
    INC_BPC(CG(active_op_array));
}

注意上面的OPCODE, 我们把ZEND_JMPZ变成了JMPNZ...
大功告成, 现在需要重新编译PHP了:

cd Zend;
rm zend_language_*.c;
cd phpsrc_dir;
make

编写测试脚本:

<?php
unless(FALSE) {
    echo "Laruence";
}

运行.....
最后, 举的这个例子,相对来说是简单的, 因为有if可以参照, 有兴趣的同学, 可以自己玩玩, 用PHP实现其它语言中的各种语法~~

25 Comments

  1. Arjuna Pulsa
    Arjuna Pulsa March 7, 2023

    如果你是不了解PHP的执行过程, 请先花点时间看看我之前的文章

  2. 四有青年
    四有青年 September 6, 2020

    可以用JS的语法实现PHP的功能吗

  3. jual pulsa murah
    jual pulsa murah March 17, 2019

    鸟哥自己写的文章最权威 深入理解PHP原理

  4. Bisnis pulsa murah
    Bisnis pulsa murah March 17, 2019

    鸟哥自己写的文章最权威:深入理解PHP原理

  5. Adelaida
    Adelaida January 10, 2016

    The famous temple town of Tarapith is in the Birbhum district of the Indian state of West Bengal.
    If the expensive travel packages make it difficult
    for you to plan your vacation, then it is time to relax.
    Add in the many perks that the work at home travel agent receives, from free swag to free airplane tickets or even vacations, also it almost sounds
    too good to be real. To know more, visit the website at travel
    to cleveland.

  6. 秋风
    秋风 July 9, 2013

    //添加T_UNLESS的语法动作
    | T_UNLESS ‘(‘ expr ‘)’ { zend_do_unless_cond(&$3, &$4 TSRMLS_CC);}
    statement {zend_do_if_after_statement(&$4, 1 TSRMLS_CC);}
    {zend_do_if_end(TSRMLS_C)};
    最后的分号,应该在大括号内吧?

  7. __river
    __river November 22, 2012

    文章很好,顶!
    刚才测试了一下,Zend/zend_language_parse.y这个文件中要加下面一行才编译成功(PHP 5.6.0-dev):
    %token T_UNLESS “unless (T_UNLESS)”

  8. yangson
    yangson May 4, 2012

    挺好用的,支持

  9. 雪候鸟
    雪候鸟 June 23, 2011

    那你就自己调用flex, 先把.l翻译成.c, 别让PHP自己找flex翻译了

  10. jerry
    jerry June 23, 2011

    我已经安了flex,而且安的是2.5.4版本,在php ./configure时依然报那样的错误,说找不到flex

  11. 雪候鸟
    雪候鸟 June 22, 2011

    @jerry 错误信息说了, 你没装flex, PHP需要用flex来吧zend_language_scanner.l翻译成zend_language_sanncer.c

  12. jerry
    jerry June 22, 2011

    鸟哥,按你的文章,我测试了一下,但是当删除zend_language_*.c后,./configure就出错,没法往下编译了:
    configure: error: flex not found. flex is required to generate the Zend/PHP parsers! Supported flex versions are: 2.5.4

  13. yuyang
    yuyang May 15, 2011

    鸟哥对php的理解让我们绝对的赞叹。还远不到你的境界

  14. Risun
    Risun July 27, 2010

    过来学习,离你的境界还太远啊,努力中

  15. 胖子
    胖子 July 17, 2010

    过来跟鸟哥学习

  16. Amaranth
    Amaranth July 16, 2010

    基本上php源码分析里的文章我都看不懂..咋整..

  17. gently
    gently July 16, 2010

    鸟哥的思路太令人称道了。好文!

  18. Skiyo
    Skiyo July 16, 2010

    鸟哥玩PHP已经炉火纯青了 这个想法超赞的。。
    我们可以定制一个自己的开源PHP。。

Comments are closed.