深入理解PHP原理之实现自己的PHP语法
来源:互联网 发布:淘宝网店经营范围 编辑:程序博客网 时间:2024/06/05 09:25
前面的文章中已经讲过PHP的词法分析、语法分析、opcodes编译,有了上面的基础,我们可以通过修改PHP源码,实现自己的PHP语法,示例如下:
<?php $demo = 'tipi'; echo var_name($demo); //执行结果,输出:demo?>
其执行过程如下:
该过程为词法分析–>语法分析–>opcodes编译–>执行,下面我们看看每一步对源码有哪些修改。
1.词法分析和语法分析
我们知道词法分析和语法分析的文件分别为zend_language_scanner.l和zend_language_parser.y。首先我们需要加入新的Token,即在文件zend_language_scanner.l中加入以下内容:
"var_name" { return T_VARIABLE_NAME;}
也就是在词分析阶段遇到var_name这个字符串的时候会被标记为我们定义的T_VARIABLE_NAME token。同样,在 zend_language_parser.y 也需要加入对这个token进行响应的逻辑处理。我们要实现的语法和PHP内置的echo print结构类似,所以我们把这个处理放到 internal_functions_in_yacc规则里面:
| T_VARIABLE_NAME '(' T_VARIABLE ')' { zend_do_variable_name(&$$, &$3 TSRMLS_CC); }| T_VARIABLE_NAME T_VARIABLE { zend_do_variable_name(&$$, &$2 TSRMLS_CC); }
第一个参数是当前表达式的返回值(编辑器不能连续打两个美元符号),&$3表是第三个表达式的值,也就是T_VARIABLE上,上面的两条规则分别对于类似:
<?phpecho var_name($varname);echo var_name $varname;
2.opcodes编译
opcode在PHP中通常是一个数字唯一标识,首先,我们在Zend/zend_vm_opcodes.h 为我们的新opcode 加入一个宏定义,这个数字要求在0-255之间,并且不能与现有opcode重复:
#define ZEND_VARIABLE_NAME 154
第二步,在Zend/zend_compile.c中加入我们对opcode的处理,也就是将代码操作转化为op_array放入到opline中:
void zend_do_variable_name(znode *result, znode *variable TSRMLS_DC){ // 生成一条zend_op zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); // 因为我们需要有返回值,并且返回值只作为中间值.所以就是一个临时变量 opline->result.op_type = IS_TMP_VAR; opline->result.u.var = get_temporary_variable(CG(active_op_array)); opline->opcode = ZEND_VARIABLE_NAME; opline->op1 = *variable; // 我们只需要一个操作数就好了 SET_UNUSED(opline->op2); *result = opline->result;}
这样,我们就完成了对opcode的编译。
3.内部处理逻辑的编写
前面只是基本语法处理与编译,这部分才是核心,包括如何处理自定义的opcode,以及编写具体的代码逻辑。前面我们提到 Zend/zend_vm_execute.h中的zend_vm_get_opcode_handler()函数,这个函数是用来获取opcode的执行函数,其对应关系通过公式计算,公式如下:
return zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];
从这个公式我们可以看出,最终的处理函数与参数类型有关,根据计算,我们要满足所有类型的映射,尽管我们可以可以使用同一函数进行处理, 于是我们在zend_opcode_handlers这个数组的结尾,加上25个相同的函数定义:
void zend_init_opcodes_handlers(void){ static const opcode_handler_t labels[] = { .... ZEND_VARIABLE_NAME_HANDLER, .... ZEND_VARIABLE_NAME_HANDLER}
如果我们不想支持某类型的数据,只需要将类型代入公式计算出的数字做为索引,使
opcode_handler_t中相应的项为:ZEND_NULL_HANDLER。最后,我们在Zend/zend_vm_def.h 中增加相应的处理函数,增加代码如下:
static int ZEND_FASTCALL ZEND_VARIABLE_NAME_HANDLER(ZEND_OPCODE_HANDLER_ARGS){ zend_op *opline = EX(opline); // PHP中所有的变量在内部都是存储在zval结构中的. zval *result = &EX_T(opline->result.u.var).tmp_var; // 把变量的名字赋给临时返回值 Z_STRVAL(*result) = estrndup(opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len); Z_STRLEN(*result) = opline->op1.u.constant.value.str.len; Z_TYPE(EX_T(opline->result.u.var).tmp_var) = IS_STRING; ZEND_VM_NEXT_OPCODE();}
进行完上面的修改之后,我们要删除r2ec&flex已经编译好的原文件,即删除Zend/zend_language*.c文件以使新的语法规则生效。 这样我们再次对PHP源码进行make时,会自动生成新的编译好的语法规则处理程序,不过编译环境要安装有lex&yacc和re2c。
参考: http://www.php-internals.com/
- 深入理解PHP原理之实现自己的PHP语法
- 深入理解PHP原理之--echo的实现
- 【PHP原理学习】之深入理解PHP原理之Opcodes
- 深入理解PHP原理之PHP脚本执行原理(1)
- 深入理解PHP原理之PHP脚本执行原理(2)
- 深入理解PHP原理之Opcodes
- 深入理解PHP原理之异常机制
- 深入理解PHP原理之Opcodes
- 深入理解PHP原理之Opcodes
- 深入理解PHP原理之Opcodes
- 深入理解PHP原理之Opcodes
- 深入理解PHP原理之异常机制
- 深入理解PHP原理之Opcodes
- 深入理解PHP原理之Opcodes
- 深入理解PHP原理之Opcodes
- 深入理解PHP原理之Opcodes
- 深入理解PHP原理之Opcodes
- 深入理解PHP原理之foreach
- 网页特殊符号HTML代码大全
- (转载)HTTP头------ETag
- VS2015软件设置
- c~c++的基础知识
- C++ replace() 函数用法详解
- 深入理解PHP原理之实现自己的PHP语法
- 今天去面试遇到了一个问题,左连接查询的问题
- Java基础之集合List-ArrayList、LinkedList、Vector的底层实现和区别
- hdu1081 To The Max
- CentOS7/Red Hat7 NTP服务无法开机自启动问题解决
- 集合(三)Map
- Android菜单不显示ICON解决方案
- Chrome的console
- js学习笔记:操作符