PHP Lex Engine Sourcecode Analysis(undone)
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)的更多相关文章
- Nginx Parsing HTTP Package、header/post/files/args Sourcecode Analysis
catalog . Nginx源码结构 . HTTP Request Header解析流程 . HTTP Request Body解析流程 1. Nginx源码结构 . core:Nginx的核心源代 ...
- NetLink Communication Mechanism And Netlink Sourcecode Analysis
catalog . Netlink简介 . Netlink Function API Howto . Generic Netlink HOWTO kernel API . RFC Linux Netl ...
- BROOTKIT Pinciple、Code Analysis(undone)
目录 . Rootkit相关知识 . BROOTKIT源码分析 . 关键技术点 . 防御策略 1. Rootkit相关知识 关于rootkit的相关其他知识,请参阅以下文章 http://www.cn ...
- Linux Kernel File IO Syscall Kernel-Source-Code Analysis(undone)
目录 . 引言 . open() syscall . close() syscall 0. 引言 在linux的哲学中,所有的磁盘文件.目录.外设设备.驱动设备全部被抽象为了"文件" ...
- SQLChop、SQLWall(Druid)、PHP Syntax Parser Analysis
catalog . introduction . sqlchop sourcecode analysis . SQLWall(Druid) . PHP Syntax Parser . SQL Pars ...
- TOMOYO Linux(undone)
目录 . TOMOYO Introduction . TOMOYO Sourcecode Analysis 1. Introduction TOMOYO是一款基于LSM Framework实现的LSM ...
- Java资源大全中文版(Awesome最新版)
Awesome系列的Java资源整理.awesome-java 就是akullpp发起维护的Java资源列表,内容包括:构建工具.数据库.框架.模板.安全.代码分析.日志.第三方库.书籍.Java 站 ...
- Linux LSM(Linux Security Modules) Hook Technology
目录 . 引言 . Linux Security Module Framework Introduction . LSM Sourcecode Analysis . LSMs Hook Engine: ...
- awesome-java
Awesome Java A curated list of awesome Java frameworks, libraries and software. Awesome Java Ancient ...
随机推荐
- QT 对话框二
QMessageBox类 information()函数,主要是提示功能,不需要用户选择 StandardButton QMessageBox::information ( QWidget *pare ...
- git的安装以及遇到的问题
git安装以及遇到的问题 之前没有学会如何在Ubuntu下使用git,国庆放假回来后,完成了git的安装,补回来了之前没有学会的东西. 以下是我安装的过程以及遇到问题.解决问题的过程. 这次安装git ...
- SQL Server Management Studio无法记住密码
用sa账户登录sql server 2008,勾选了“记住密码”,但重新登录时,SQL Server Management Studio无法记住密码. 后来发现,在重新登录时,登录名显示的并非是s ...
- sqlserver查询所有表名、字段名、类型、长度和存储过程、视图的创建语句
-- 获得存储过程创建语句 select o.xtype,o.name,cm.text from syscomments cm inner join sysobjects o on o.id=cm.i ...
- window 运行指令(1)
添加或删除程序 appwiz.cpl 管理工具 control admintools Bluetooth文件传送向导 fsquirt 计算器 calc 证书管理控制台 certmgr.msc 字符映射 ...
- 很简单的多线程访问python嘿嘿嘿
import urllib import socket from threading import * url = "http://www.baidu.com/s?ie=UTF-8& ...
- 0924Linux常用命令
写的不错 转载了,有需要的同学们,可以多多学习,适合小白哦 http://blog.csdn.net/xiaoguaihai/article/details/8705992/ 关于最后增加一点 tar ...
- ubuntu14.04完全卸载mysql
1.删除 mysql1 sudo apt-get autoremove --purge mysql-server-5.0 2 sudo apt-get remove mysql-server 3 su ...
- java设计模式(六) 命令模式
[命令模式]将"请求"封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象,命令模式也支持可撤销的操作. 1,定义命令接口 package com.pattern.comm ...
- 关于如何调用苹果自带的地图APP
CLGeocoder *geoCoder = [[CLGeocoder alloc] init]; // 通过地理编码,得到位置, CLLocation *loc = [[CLLocation all ...