catalog

. PHP词法解析引擎Lex简介
. PHP标签解析

1. PHP词法解析引擎Lex简介

Relevant Link:

2. PHP标签解析

\php-5.4.41\Zend\zend_language_scanner.l

int lex_scan(zval *zendlval TSRMLS_DC)
{
restart:
//设置当前token的首位置为当前位置
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);
。。

0x1: 匹配PHP标签

1.1 <script language=php>、<script language='php'>、<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>") - )); //因为<script>标签本身是在html中的,所以判断当前是否在扫描html,如果是的话就跳转到inline_html去
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;
} //不然就将当前状态改为ST_IN_SCRIPTING并返回T_OPEN_TAG,表示这是一个php的标签。
HANDLE_NEWLINES(yytext, yyleng);
zendlval->value.str.val = yytext; /* no copying - intentional */
zendlval->value.str.len = yyleng;
zendlval->type = IS_STRING;
BEGIN(ST_IN_SCRIPTING);
return T_OPEN_TAG;
}

1.2 <%=、<%

<INITIAL>"<%"
{
//检查php.ini里面的asp_tags标签是否为On,如果是则表示进入脚本并返回T_OPEN_TAG
if (CG(asp_tags))
{
zendlval->value.str.val = yytext; /* no copying - intentional */
zendlval->value.str.len = yyleng;
zendlval->type = IS_STRING;
BEGIN(ST_IN_SCRIPTING);
return T_OPEN_TAG;
}
else
{
//否则就转到inline_char_handler去
goto inline_char_handler;
}
}

1.3 <?=、<?

//短标签<?=和<?
<INITIAL>"<?"
{
//判断short_open_tag是否为On
if (CG(short_tags))
{
zendlval->value.str.val = yytext; /* no copying - intentional */
zendlval->value.str.len = yyleng;
zendlval->type = IS_STRING;
BEGIN(ST_IN_SCRIPTING);
return T_OPEN_TAG;
}
else
{
goto inline_char_handler;
}
} //"<?=" 不需要short_open_tag标志打开
<INITIAL>"<?="
{
zendlval->value.str.val = yytext; /* no copying - intentional */
zendlval->value.str.len = yyleng;
zendlval->type = IS_STRING;
BEGIN(ST_IN_SCRIPTING);
return T_OPEN_TAG_WITH_ECHO;
}

1.4 <?php

<INITIAL>"<?php"([ \t]|{NEWLINE})
{
zendlval->value.str.val = yytext; /* no copying - intentional */
zendlval->value.str.len = yyleng;
zendlval->type = IS_STRING;
HANDLE_NEWLINE(yytext[yyleng-]);
BEGIN(ST_IN_SCRIPTING);
return T_OPEN_TAG;
}

1.5 inline_char_handler

如果以上的标签匹配都失败,就会匹配ANY_CHAR,判断是否扫描完了,是的话直接返回0,不是就接下去执行inline_char_handler和inline_html段的代码

<INITIAL>{ANY_CHAR}
{
if (YYCURSOR > YYLIMIT)
{
return ;
} inline_char_handler:
while ()
{
/*
inline_char_handler中的代码是对整个字符串扫描,memchr表示的是从YYCURSOR开始的YYLIMIT - YYCURSOR长度内的字符串中搜索'<'字符
1. 如果找到则匹配'?'、'%'、's'等字符,如果满足条件则结束循环
2. 而匹配到's'或'S'则将YYCURSOR往回退一格并重新开始php标签的匹配
*/
YYCTYPE *ptr = memchr(YYCURSOR, '<', YYLIMIT - YYCURSOR); YYCURSOR = ptr ? ptr + : YYLIMIT; if (YYCURSOR < YYLIMIT)
{
switch (*YYCURSOR)
{
case '?':
if (CG(short_tags) || !strncasecmp((char*)YYCURSOR + , "php", ) || (*(YYCURSOR + ) == '='))
{ /* Assume [ \t\n\r] follows "php" */
break;
}
continue;
case '%':
if (CG(asp_tags))
{
break;
}
continue;
case 's':
case 'S':
/* Probably NOT an opening PHP <script> tag, so don't end the HTML chunk yet
* If it is, the PHP <script> tag rule checks for any HTML scanned before it */
YYCURSOR--;
yymore();
default:
continue;
} YYCURSOR--;
} break;
}
..

1.6 inline_html

如果是inline_html的代码,直接复制这段代码(PHP引擎对HTML代码原样输出),随后返回T_INLINE_HTML

..
//inline_html扫描的是不在php标签里面的的代码,也就是说这些php代码可能夹杂在诸如html等代码中
inline_html:
yyleng = YYCURSOR - SCNG(yy_text); if (SCNG(output_filter))
{
int readsize;
size_t sz = ;
readsize = SCNG(output_filter)((unsigned char **)&(zendlval->value.str.val), &sz, (unsigned char *)yytext, (size_t)yyleng TSRMLS_CC);
zendlval->value.str.len = sz;
if (readsize < yyleng)
{
yyless(readsize);
}
}
else
{
zendlval->value.str.val = (char *) estrndup(yytext, yyleng);
zendlval->value.str.len = yyleng;
}
zendlval->type = IS_STRING;
HANDLE_NEWLINES(yytext, yyleng);
return T_INLINE_HTML;
}

从PHP源代码中我们也可以看到,PHP对结束闭合标签是可选的,解析器并不会强制要求一定要有结束闭合标签,PHP官方文档解释如下

The closing tag of a PHP block at the end of a file is optional, and in some cases omitting it is helpful when using include or require, so unwanted whitespace will not occur at the end of files, and you will still be able to add headers to the response later. It is also handy if you use output buffering, and would not like to see added unwanted whitespace at the end of the parts generated by the included files.

不使用结束闭合标签的好处有以下几个

. 如果这个是一个被别人包含的程序,没有这个结束符,可以减少很多很多问题,比如说:header, setcookie, session_start这些动作之前不能有输出,如果不小心在?> 后边加了不可见字符(多余的空格、换行符)等破坏页面显示,就会报"Header already sent"错误,不写的话不会有此问题。另,可以直接把光标移到最后,接着编程
. PHP闭合标签"?>"在PHP中对PHP的分析器是可选的。但是,如果使用闭合标签,任何由开发者,用户,或者FTP应用程序插入闭合标签后面的空格都有可能会引起多余的输出、php错误、之后的输出无法显示、空白页。因此,所有的php文件应该省略这个php闭合标签,并插入一段注释来标明这是文件的底部并定位这个文件在这个应用的相对路径。这样有利于你确定这个文件已经结束而不是被删节的

Relevant Link:

http://php.net/manual/en/language.basic-syntax.instruction-separation.php
http://doophp.sinaapp.com/archives/php/end-symbol.html
http://blog.csdn.net/yanhui_wei/article/details/7951424
http://blog.csdn.net/wuyangbotianshi/article/details/41728091

Copyright (c) 2014 LittleHann All rights reserved

PHP Lex Engine Sourcecode Analysis(undone)的更多相关文章

  1. Nginx Parsing HTTP Package、header/post/files/args Sourcecode Analysis

    catalog . Nginx源码结构 . HTTP Request Header解析流程 . HTTP Request Body解析流程 1. Nginx源码结构 . core:Nginx的核心源代 ...

  2. NetLink Communication Mechanism And Netlink Sourcecode Analysis

    catalog . Netlink简介 . Netlink Function API Howto . Generic Netlink HOWTO kernel API . RFC Linux Netl ...

  3. BROOTKIT Pinciple、Code Analysis(undone)

    目录 . Rootkit相关知识 . BROOTKIT源码分析 . 关键技术点 . 防御策略 1. Rootkit相关知识 关于rootkit的相关其他知识,请参阅以下文章 http://www.cn ...

  4. Linux Kernel File IO Syscall Kernel-Source-Code Analysis(undone)

    目录 . 引言 . open() syscall . close() syscall 0. 引言 在linux的哲学中,所有的磁盘文件.目录.外设设备.驱动设备全部被抽象为了"文件" ...

  5. SQLChop、SQLWall(Druid)、PHP Syntax Parser Analysis

    catalog . introduction . sqlchop sourcecode analysis . SQLWall(Druid) . PHP Syntax Parser . SQL Pars ...

  6. TOMOYO Linux(undone)

    目录 . TOMOYO Introduction . TOMOYO Sourcecode Analysis 1. Introduction TOMOYO是一款基于LSM Framework实现的LSM ...

  7. Java资源大全中文版(Awesome最新版)

    Awesome系列的Java资源整理.awesome-java 就是akullpp发起维护的Java资源列表,内容包括:构建工具.数据库.框架.模板.安全.代码分析.日志.第三方库.书籍.Java 站 ...

  8. Linux LSM(Linux Security Modules) Hook Technology

    目录 . 引言 . Linux Security Module Framework Introduction . LSM Sourcecode Analysis . LSMs Hook Engine: ...

  9. awesome-java

    Awesome Java A curated list of awesome Java frameworks, libraries and software. Awesome Java Ancient ...

随机推荐

  1. IE8中给HTML标签负值报错问题

    当通过JS给一个HTML标签设置高宽为负值的时候,会爆出一个“参数无效”的错误 chrome下不会报错,但是元素不会做任何关于负值的改变

  2. mac系统上使用压缩包版的mysql(非安装版)

    mac本换了块固态硬盘,一切重新装过,mysql嫌官网下载太慢,直接百度 "mysql mac",第一个就是: 不料下载完后,发现这是一个压缩包版,并没有安装程序.网上搜索了一下, ...

  3. nginx学习(1):编译、安装、启动

    一.下载 从官网http://nginx.org/en/download.html 下载稳定版(目前最新稳定版是1.6.2) 二.解压 tar zxf nginx-1.6.2.tar.gzcd ngi ...

  4. Alpha版本发布说明

    软件发布的同时,在团队博客上写一个发布说明     ▪ 列出这一版本的新功能     ▪ 这一版本修复的缺陷     ▪ 对运行环境的要求     ▪ 安装方法     ▪ 描述系统已知的问题和限制 ...

  5. 自己画WinForm 皮肤包括默认控件

    好久没来博客园,今天捣鼓到现在就是为了把之前的皮肤控件完善好, 之前也看了很多技术文章,大多数都是自己重写系统控件实现换肤,几乎没有像东日的(IrisSkin)控件一样 添加一个组件 把系统的皮肤全换 ...

  6. HTC Vive 与Leap Motion 出现位置错误的问题

    Leap Motion已经支持VR, 但是官方没有支持HTC Vive的例子. 按照官方的文档, 其实是有问题的: https://developer.leapmotion.com/documenta ...

  7. [POJ2404]Jogging Trails(中国旅行商问题)(一般图的匹配——状压DP)

    题目:http://poj.org/problem?id=2404 题意:有个n(n<=15)的点和m条无向边,每条边都有自己的权值.现在你要从某个点出发,每条边可以经过多次但要保证每条边至少走 ...

  8. mac 设置阿里企业邮箱

    接收邮件服务器:pop3.mxhichina.com或pop3.您的域名,端口:110 发送邮件服务器:smtp.mxhichina.com或smtp.您的域名,端口:25 IMAP协议设置 接收邮件 ...

  9. optiontransferselect例子

    Struts2 OptionTransferSelect标签 动态赋值: 1.html片面: <td class="td2"> <s:optiontransfer ...

  10. C#中的Where和Lambda表达式

    1 2 3 4 5 6 7 8 9 10 11 List<string> listString = new List<string>(); listString.Add(&qu ...