baiyan

基本概念

在PHP7中,当一个脚本运行请求或到来时,PHP代码首先会被加载到内存中,随后进行词法分析和语法分析并生成抽象语法树(AST),然后进行深度优先遍历并生成opcodes,并在zend虚拟机中执行这些opcode,返回最终的执行结果。

词法分析:它是编译过程的第一个阶段。这个阶段会将代码从左到右按字符扫描并读入,然后将代码划分成一个又一个的词法单元(token),方便后续的语法分析以及编译优化等操作。这就好比用菜刀去切一块肉,将这块大肉切分成一小块一小块肉,方便人们后续食用。可以使用Re2c、lex等自动化词法分析工具,来帮助我们完成。

语法分析:语法分析是编译过程的一个逻辑阶段。语法分析的任务是在词法分析的基础上将词法单元token,组合成各类语法短语,如“程序”,“语句”,“表达式”等等,它能够判断源程序在结构上是否正确。

在PHP中,使用的词法分析器是Re2c,语法分析器是Bison。

其实进行词法分析和语法分析并生成某种数据结构的过程,就是一个解码的过程。

之所以需要做这种从字符串到数据结构(AST)的转换,是因为编译器是无法直接操作“1+2”这样的字符串的。实际上,代码的本质根本就不是字符串,它本来就是一个具有复杂拓扑的数据结构,就像电路一样。“1+2”这个字符串只是对这种数据结构的一种“编码”,就像ZIP或者JPEG只是对它们压缩的数据的编码一样。

这种编码可以方便你把代码存到磁盘上,方便你用文本编辑器来修改它们(对人友好,方便人们编写代码,但是对编译器不友好),然而你必须知道,文本并不是代码本身。所以从磁盘读取了文本之后,你必须先“解码”,才能方便地操作代码的数据结构。比如,如果上面的Java代码生成的AST节点叫node,你就可以用node.operator来访问加号ADD,用node.left来访问1,node.right来访问2。这是很方便的。对于程序语言,这种解码的动作就叫做parsing,用于解码的那段代码就叫做parser。

关于语法分析与词法分析的具体概念解释,这篇文章写得较好:对 Parser 的误解

我们先利用PHP内置函数token_get_all()来取出一段PHP代码的token:

$lan = '<?php $a = 1; echo $a';

$tokens = token_get_all($lan);

foreach ($tokens as $token) {

if (is_array($token)) {

echo "Line {$token[2]}: ", token_name($token[0]), " ('{$token[1]}')", PHP_EOL;

}

}

打印结果为:

Line 1: T_OPEN_TAG ('<?php ')

Line 1: T_VARIABLE ('$a')

Line 1: T_WHITESPACE (' ')

Line 1: T_WHITESPACE (' ')

Line 1: T_LNUMBER ('1')

Line 1: T_WHITESPACE (' ')

Line 1: T_ECHO ('echo')

Line 1: T_WHITESPACE (' ')

Line 1: T_VARIABLE ('$a')

观察以上结果,可以看有OPEN_TAG/VARIABLE/WHITESPACE等等词法单元token。

如何取出token

那么让我们你自己去设计一个算法,从一个字符串中识别并取出token,应该怎么做?

使用两个指针,一个标记开始位置,一个往后挪,然后回溯。(较麻烦)

使用正则表达式进行匹配

当用较简单的字符串匹配正则表达式的时候,可以用人眼很容易地看出来。但是如果用很复杂的字符串(成千上万行代码)去匹配一个正则,是相当麻烦并且非常慢的,编译原理中提出了这样一个概念用以解决这个问题:有穷状态机。

有穷状态机:必须有一个起始状态,用一个箭头加圆圈表示;也得有一个结束,用两个圆圈表示。 如果满足某个条件,就会从一个状态跃迁到另一个状态,也用箭头来表示。

例:观察下面这个正则表达式:

(a|b)*abb

根据这个正则表达式,我们可以画出它的有穷状态机:

- 对于a,只能到状态0或者1,不能到达结束的3,所以不匹配

- 对于abb,第一个a可以使状态0跃迁到1,第二个b可以从1跃迁到2,最后一个b结束,所以匹配

- 对于aabb,第一个a可以选择从0跃迁到0,第二个从0跃迁到1,后面两个b同上,匹配

- 对于cabb,第一个c就无法满足,不匹配

这里有个问题,输入第一个a的时候,可以从0跃迁到自己,也可以从0跃迁到1,所以这种状态机就叫不确定有穷状态机(NFA)

NFA是有缺陷的,比如aabb,有可能一直从0跃迁到0,共重复了4次这样的操作,也没有到达最终的结束状态3。这就会导致本应该符合匹配要求的字符串,在不确定有穷状态机中,错误地被判定为不符合匹配要求。解决此问题的办法就是将不确定有穷状态机转化为确定有穷状态机(DFA)。

这样一来,一个确定的输入就对应着一个确定的输出(假设如给一个a,一定跃迁到1;给一个b,一定跃迁回0),不存在歧义问题。

但是,将一个NFA转化成DFA是相当复杂的,所以有工具已经为我们做好了这个事情:Re2c。你只需要输入一个正则表达式,就能够为你生成一个确定有穷状态机(DFA),在Re2c工具中以C/C++代码体现,详情见:re2c中文手册

php 词法分析,【PHP7源码学习】2019-03-20 PHP词法分析相关推荐

  1. 一行一行分析JQ源码学习笔记-03

    rquickExpr: <p>aaaa   或  #div1 rsingieTag: rmsPrefix :/-ms-/  是否是ie rdashAlpha = 转大小写   数字 (-2 ...

  2. clion开发php,如何在 Mac 上用 Clion 调试 php7 源码

    一.背景 据2019年7月份的编程排行榜,php 依然在前10,表明了 php 还是世界上最好的语言.php 的优势很多,只要场景合适,在工作中能用就用了.为了更好的理解这门语言,这几天学习下 php ...

  3. srsLTE源码学习:度量中心:metrics_hub.h

    Table of Contents metrics_hub.h PS:<srsLTE源码学习:绑核创建线程threads.h, threads.c> metrics_hub.h lib\i ...

  4. h5制作 php 开源,PHP源码:2019最新仿易企秀V15.1完整版开源版源码,修复采集功能、新增同行站模板采集等...

    源码说明: 更新功能如下: 新增同行站模板采集 修复模板中心采集方法 修复自定义场景加载LOGO问题 预览H5不在是默认封面加载 可以自定义自己的网站LOGO 安装说明: 把源码包上传到网站根目录 然 ...

  5. JDK11源码学习05 | HashMap类

    JDK11源码学习05 | HashMap类 JDK11源码学习01 | Map接口 JDK11源码学习02 | AbstractMap抽象类 JDK11源码学习03 | Serializable接口 ...

  6. Electron源码学习: Electron组成与初始化流程

    Electron源码学习: Electron组成与结构 前言 ​ 最近研究学习Electron的源码结构已经有一些小的进展, 越接触Electron就越发现组成这个软件的大集合不得了.现在学习到的仍然 ...

  7. PHP 源码学习之线程安全

    PHP 源码学习之线程安全 了解线程安全之前,我们先回顾几点基础知识点,是我们后面分析学习的基础. 变量的作用域 从作用域上来说,C语言可以定义4种不同的变量:全局变量,静态全局变量,局部变量,静态局 ...

  8. 【Mybatis源码学习】概述

    [Mybatis源码学习]概述 1.怎样下载源码 1.1 下载地址 1.2 导入Idea 1.2.1 环境 1.2.2 部署与打包 2.源码架构 2.1 核心流程三大阶段 2.1.1 初始化 2.1. ...

  9. ERNIE源码学习与实践:为超越ChatGPT打下技术基础!

    ★★★ 本文源自AlStudio社区精品项目,[点击此处]查看更多精品内容 >>> ERNIE学习与实践:为超越ChatGPT打下技术基础! ERNIE是BERT相爱相杀的好基友,由 ...

最新文章

  1. linux下运行python unitest_Python unittest打印日志可以在Linux上运行,但在Windows上不行...
  2. 46 jQuery属性操作
  3. 【代码】CyclicBarrier栅栏使用示例
  4. BOJ 15 Panic Room
  5. 监听网络流量命令——tcpdump
  6. CUDA5.5入门文章:VS10设置
  7. iOS7下隐藏status bar的详细研究
  8. 关系型数据库性能优化总结(转)
  9. 再见 2017 你好 2018 ,程序员们的精彩年度总结
  10. 使用nodejs进行WEB开发
  11. 【干货】2020年研发运营安全白皮书.pdf(附下载链接)
  12. Voltage Keepsake CodeForces - 801C(二分)
  13. 基于SSM实现在线考试及题库管理系统
  14. oss上传判断_React实现阿里云OSS上传文件的示例
  15. HLS 开发学习(五) 稀疏矩阵向量乘法
  16. IntellijIdea2018 Liscense server激活码
  17. 20条非常实用的Python代码实例
  18. C# 10分钟完成百度翻译(机器翻译)——入门篇
  19. 《鸟哥的Linux私房菜》学习笔记
  20. [10] Linux系统日常运维

热门文章

  1. 死磕Java并发:J.U.C之Condition
  2. java冒泡排序程序流图_java实现选择排序和冒泡排序及执行流程图解
  3. 为什么request的页面与原页面不同_如何在不同页面上使用不同的Joomla CSS
  4. yolov5 代码版,去掉配置文件
  5. C++ 智能指针std::shared_ptr简单使用和理解
  6. Hypercolumns笔记
  7. ElasticSearch ected map for property [fields] on field [subject_id] but got a class java.lang
  8. error LNK2019: 无法解析的外部符号 _WinMain@16,该符号在函数 ___tmainCR... 2011年08月05日 09:08:15
  9. grabcut.cpp:380: error: (-215) !bgdSamples.empty() !fgdSamples.empty() in function initGMMs
  10. python数组改变维数