前言

本文通过分析 "$a=1" 这个 PHP 语句的编译和执行来窥探 php-cli 解释执行逻辑

准备

  • 参考之前的系列文章,在 ubuntu 环境下下载,编译 PHP 源代码

  • 将代码导入 idea clion IDE 中

  • 编辑运行选项,增加运行参数:-f test.php

  • 设置断点开始调试

test.php 是一个测试脚本,放在 sapi/cli/ 目录下,test.php 中只包含一条简单的赋值语句:

<?php
$a = 1
?>

调用堆栈

参考之前的系列文章来了解 php-cli 启动过程以及语法分析和字节码生成的基本概念,这里直接给出调用堆栈:

我们尝试从 zend_compile_expr 函数说起

zend_compile_expr

赋值语句 is-a 表达式,zend_compile_expr新视觉影院 函数根据 ast 类型选择调用 zend_compile_assign:

// zend_compile.c

void zend_compile_expr(znode *result, zend_ast *ast) {
...
switch (ast->kind) {
...
case ZEND_AST_ASSIGN:
zend_compile_assign(result, ast);
break;
}
}

zend_compile_assign

赋值语句的 ast 包含两个 child ast,即 left hand side var(ast->child[0]) 和 right hand side expr(ast->child[1]),var_node 和 expr_node 两个 znode 类型的变量是生成字节码过程使用的中间变量

// zend_compile.c

void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
zend_ast *expr_ast = ast->child[1]; znode var_node, expr_node;
zend_op *opline;
uint32_t offset; if (is_this_fetch(var_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
} zend_ensure_writable_variable(var_ast);

然后我们来看看 switch case 语句

// zend_compile.c

switch (var_ast->kind) {
case ZEND_AST_VAR:
case ZEND_AST_STATIC_PROP:
offset = zend_delayed_compile_begin();
zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W);
zend_compile_expr(&expr_node, expr_ast);
zend_delayed_compile_end(offset);
zend_emit_op(result, ZEND_ASSIGN, &var_node, &expr_node);
return;
}

刚看到这段代码可能会觉得挺绕的:zend_delayed_xxx 函数是干啥的?最终生成的字节码又保存在哪呢?

zend_emit_op

emit 有 "发射,散播"的意思,所以光棍影院 zend_emit_op 可能和字节码保存相关:

// zend_compile.c

static zend_op *zend_emit_op(znode *result, zend_uchar opcode,
znode *op1, znode *op2) /* {{{ */
{
zend_op *opline = get_next_op(CG(active_op_array));
opline->opcode = opcode; if (op1 == NULL) {
SET_UNUSED(opline->op1);
} else {
SET_NODE(opline->op1, op1);
} if (op2 == NULL) {
SET_UNUSED(opline->op2);
} else {
SET_NODE(opline->op2, op2);
} zend_check_live_ranges(opline);
if (result) {
zend_make_var_result(result, opline);
}
return opline;
}

这里我们又遇到了全局变量 CG(compile globals),zend_emit_op 先调用 get_next_op 获取可用的 zend_op(虚拟机指令),然后设置 op1, op2 为 opline 的两个操作数

现在我们知道生成的字节码保存在 CG 的 active_op_array 数组里

总结

PHP-7.1 源代码学习:字节码生成 之 "$a = 1"的更多相关文章

  1. Java代理全攻略【有瑕疵:字节码生成部分没看到,最后两节没仔细看,累了】

    Java代理 1.代理模式 定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象. 其实就是委托.聚合.中间人. 为了保持行为的 ...

  2. 曹工说Spring Boot源码(26)-- 学习字节码也太难了,实在不能忍受了,写了个小小的字节码执行引擎

    曹工说Spring Boot源码(26)-- 学习字节码也太难了,实在不能忍受了,写了个小小的字节码执行引擎 写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean De ...

  3. [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析

    [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285 ...

  4. 谁还没遇上过NoClassDefFoundError咋地——浅谈字节码生成与热部署

    谁还没遇上过NoClassDefFoundError咋地--浅谈字节码生成与热部署 前言 在Java程序员的世界里,NoClassDefFoundError是一类相当令人厌恶的错误,因为这类错误通常非 ...

  5. 深入浅出Java探针技术2---java字节码生成框架ASM、Javassist和byte buddy的使用

    目前Java字节码生成框架大致有ASM.Javassist和byte buddy三种 ASM框架介绍及使用 1.ASM介绍 ASM是一种Java字节码操控框架,能够以二进制形式修改已有的类或是生成类, ...

  6. [WebKit内核] JavaScriptCore深度解析--基础篇(一)字节码生成及语法树的构建

    看到HorkeyChen写的文章<[WebKit] JavaScriptCore解析--基础篇(三)从脚本代码到JIT编译的代码实现>,写的很好,深受启发.想补充一些Horkey没有写到的 ...

  7. Kotlin字节码生成机制详尽分析

    通过注解修改Kotlin的class文件名: 对于Kotlin文件在编译之后生成的class文件名默认是有一定规则的,比如: 而其实这个生成字节码的文件名称是可以被改的,之前https://www.c ...

  8. 透过字节码生成审视Java动态代理运作机制

    对于动态代理我想应该大家都不陌生,就是可以动态去代理实现某个接口的类来干一些我们自己想要的功能,但是在字节码层面它的表现是如何的呢?既然目前刚好在研究字节码相关的东东,有必要对其从字节码角度来审视一下 ...

  9. JVM学习——字节码(学习过程)

    JVM--字节码 为什么要学字节码 字节码文件,有什么用? JVM虚拟机的特点:一处编译,多处运行. 多处运行,靠的是.class 字节码文件. JVM本身,并不是跨平台的.Java之所以跨平台,是因 ...

随机推荐

  1. css对应中文字的英文名称

    中文名 英文名 Unicode Unicode 2 Mac OS 华文细黑 STHeiti Light [STXihei] \534E\6587\7EC6\9ED1 华文细黑 华文黑体 STHeiti ...

  2. Lucene-如何编写Lucene程序

    Lucene版本:7.1 使用Lucene的关键点 创建文档(Document),添加文件(Field),保存了原始数据信息: 把文档加入IndexWriter: 使用QueryParser.pars ...

  3. 微软Coco Blockchain Framework:一键解决企业级区块链三大难题

    近年来,异军突起的“区块链”受到全行业的广泛关注,众多企业级用户在积极拥抱新技术的过程中却面临三大难题:性能.隐私和组织管理.如果不能很好地解决这些“顽固分子”,区块链技术就相对局限,很难发挥出应有的 ...

  4. AD 域复制FRS 迁移到DFSR

    假设您尝试将在先前版本的Windows Server上运行的某个Active Directory域控制器(DC)升级到Windows Server 2019. 您可能会看到以下错误: “副本验证失败. ...

  5. Ubuntu下编译C++ OpenCV程序并运行

            因为想试跑yolov3的缘故,所以装了ubuntu系统,直接通过U盘装的,并不像他们说的“折腾”,反而一切非常顺利,比装软件还简单.然后就是要用C++跑opencv的程序用于比赛,出于 ...

  6. Mybatis中关于OGNL表达式冲突

    注意设计表字段不能用bor  xor  and  band  eq  neq  lt  gt  lte  gte  shl  shr  ushr

  7. java基础—线程(一)

    一.线程的基本概念

  8. 汉明码(Hamming Code)原理及实现

    汉明码实现原理 汉明码(Hamming Code)是广泛用于内存和磁盘纠错的编码.汉明码不仅可以用来检测转移数据时发生的错误,还可以用来修正错误.(要注意的是,汉明码只能发现和修正一位错误,对于两位或 ...

  9. 伪基站SSRP

    伪基站的基本构成包括一台发射主机.一台笔记本电脑.一根天线.一部手机和电源.其中,手机用来测得频点,即某个区域运营商正规基站的频点,然后再把频点和短信内容输入到电脑安装好的程序,通过发射主机和天线进行 ...

  10. 中英文字符串截取函数msubstr

    Thinkphp内置了一个可以媲美smarty的模板引擎,给我们带来了很大的方便.调用函数也一样,可以和smarty一样调用自己需要的函数,而官方也内置了一些常用的函数供大家调用. 比如今天我们说的截 ...