int lex_scan(zval *zendlval TSRMLS_DC)
{
//设置当前token的首位置为当前位置
restart:
SCNG(yy_text) = YYCURSOR;
yymore_restart:
//这段注释定义了各个类型的正则表达式匹配,在词法解析程序(如bison、re2c等)程序将本文件转化为c代码时会用到
/*!re2c
re2c:yyfill:check = 0;
LNUM [0-9]+
DNUM ([0-9]*"."[0-9]+)|([0-9]+"."[0-9]*)
EXPONENT_DNUM (({LNUM}|{DNUM})[eE][+-]?{LNUM})
HNUM "0x"[0-9a-fA-F]+
BNUM "0b"[01]+
LABEL [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
WHITESPACE [ \n\r\t]+
TABS_AND_SPACES [ \t]*
TOKENS [;:,.\[\]()|^&+-/*=%!~$<>?@]
ANY_CHAR [^]
NEWLINE ("\r"|"\n"|"\r\n")
/* compute yyleng before each rule */
<!*> := yyleng = YYCURSOR - SCNG(yy_text);
接下去就按照解析PHP标签、关键字、类和结构体、数字这几个方面来看lex_scan是如何解析PHP代码的。
1.匹配php标签
在zend_language_scanner.l文件里会匹配php的标签,并且匹配规则不止一种,打开了asp_tag开关还能兼容asp脚本,比较奇特。
1.1 <script language=php>
首先是匹配<script language=php>标签,源码如下,无论这里面有多少个空白字符全部无视,最后php也可以加上单引号或双引号:
<INITIAL>"<script"{WHITESPACE}+"language"{WHITESPACE}*"="{WHITESPACE}*("php"|"\"php\""|"'php'"){WHITESPACE}*">" {
YYCTYPE *bracket = (YYCTYPE*)zend_memrchr(yytext, '<', yyleng - (sizeof("script language=php>") - 1));
if (bracket != SCNG(yy_text)) {
/* Handle previously scanned HTML, as possible <script> tags found are assumed to not be PHP's */
YYCURSOR = bracket;
goto inline_html;
}
HANDLE_NEWLINES(yytext, yyleng);
ZVAL_STRINGL(zendlval, yytext, yyleng, 0); /* no copying - intentional */
BEGIN(ST_IN_SCRIPTING);
return T_OPEN_TAG;
}
因为<script>标签本身是在html中的,所以判断当前是否在扫描html,如果是的话就跳转到inline_html去,不然就将当前状态改为ST_IN_SCRIPTING并返回T_OPEN_TAG,表示这是一个php的标签。
1.2 <%=和<%
如果不是这个还会在匹配<%=和<%,匹配到时便会检查php.ini里面的asp_tags标签是否为On,如果是则表示进入脚本并返回T_OPEN_TAG,否则就转到inline_char_handler去执行,源码如下:
//<INITIAL>"<%=" {
<INITIAL>