찾다
백엔드 개발PHP 튜토리얼深入理解PHP代码的执行的过程_PHP教程

一、前言

语言是人们进行沟通和交流的表达符号,每种语言都有专属于自己的符号,表达方式和规则。 就编程语言来说,它也是由特定的符号,特定的表达方式和规则组成。语言的作用是沟通,不管是自然语言,还是编程语言,它们的区别在于自然语言是人与人之间沟通的工具, 而编程语言是人与机器之间的沟通渠道。

就PHP语言来说,它也是一组符合一定规则的约定的指令。 在编程人员将自己的想法以PHP语言实现后,通过PHP的虚拟机(确切的来说应该是PHP的语言引擎Zend)将这些PHP指令转变成C语言 (可以理解为更底层的一种指令集)指令,而C语言又会转变成汇编语言, 最后汇编语言将根据处理器的规则转变成机器码执行。这是一个更高层次抽象的不断具体化,不断细化的过程。

从一种语言到另一种语言的转化称之为编译,这两种语言分别可以称之为源语言和目标语言。 这种编译过程通过发生在目标语言比源语言更低级(或者说更底层)。 语言转化的编译过程是由编译器来完成, 编码器通常被分为一系列的过程:词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成等。 前面几个阶段(词法分析、语法分析和语义分析)的作用是分析源程序,我们可以称之为编译器的前端。 后面的几个阶段(中间代码生成、代码优化和目标代码生成)的作用是构造目标程序,我们可以称之为编译器的后端。 一种语言被称为编译类语言,一般是由于在程序执行之前有一个翻译的过程, 其中关键点是有一个形式上完全不同的等价程序生成。 而PHP之所以被称为解释类语言,就是因为并没有这样的一个程序生成, 它生成的是中间代码Opcode,这只是PHP的一种内部数据结构。

二、 PHP代码的执行的过程

比如我们写一个简单的程序

<?php
	echo "Hello World!";
	$a = 1 + 1;
	echo $a;
?>
这个简单的程序他执行过程是怎样的呢?其实,执行过程也正如我们前面所说分为4个步骤。(这里只是指PHP语言引擎Zend执行过程,不包含Web服务器的执行过程。)

1.Scanning(Lexing) ,将PHP代码转换为语言片段(Tokens)
2.Parsing, 将Tokens转换成简单而有意义的表达式
3.Compilation, 将表达式编译成Opocdes
4.Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。


注1:Opcode是一种PHP脚本编译后的中间语言,就像Java的ByteCode,或者.NET的MSL

注2:现在有的Cache比如APC,可以使得PHP缓存住Opcodes,这样,每次有请求来临的时候,就不需要重复执行前面3步,从而能大幅的提高PHP的执行速度。

1. Scanning(Lexing),将PHP代码转换为语言片段(Tokens)

那什么是Lexing? 学过编译原理的同学都应该对编译原理中的词法分析步骤有所了解,Lex就是一个词法分析的依据表。

对于PHP在开始使用的是Flex,之后改为re2c, MySQL的词法分析使用的Flex,除此之外还有作为UNIX系统标准词法分析器的Lex等。 这些工具都会读进一个代表词法分析器规则的输入字符串流,然后输出以C语言实做的词法分析器源代码。 这里我们只介绍PHP的现版词法分析器,re2c。 在源码目录下的Zend/zend_language_scanner.l 文件是re2c的规则文件, 如果需要修改该规则文件需要安装re2c才能重新编译,生成新的规则文件。Zend/zend_language_scanner.c会根据Zend/zend_language_scanner.l,来输入的 PHP代码进行词法分析,从而得到一个一个的“词”。

从PHP4.2开始提供了一个函数叫token_get_all,这个函数就可以将一段PHP代码 Scanning成Tokens;

我们用下面的代码使用token_get_all函数处理我们开头提到的PHP代码。

";
$phpcode = << $token) {
	$tokens[$key][0] = token_name($token[0]);
}
print_r($tokens);
?>

注:为了便于理解和查看,我使用token_name函数将解析器代号修改成了符号名称说明。

如果有的童鞋想要看原始的,可以将上面代码中的第10,11行代码注释去掉。

解释器代号列表详见:http://www.php.net/manual/zh/tokens.php

得到的结果如下:

Array
(
    [0] => Array
        (
            [0] => T_OPEN_TAG
            [1] =>  1
        )

    [1] => Array
        (
            [0] => T_WHITESPACE
            [1] => 	
            [2] => 2
        )

    [2] => Array
        (
            [0] => T_ECHO
            [1] => echo
            [2] => 2
        )

    [3] => Array
        (
            [0] => T_WHITESPACE
            [1] =>  
            [2] => 2
        )

    [4] => Array
        (
            [0] => T_CONSTANT_ENCAPSED_STRING
            [1] => "Hello World!"
            [2] => 2
        )

    [5] => 
    [6] => Array
        (
            [0] => T_WHITESPACE
            [1] => 
	 
            [2] => 2
        )

    [7] => 
    [8] => Array
        (
            [0] => T_WHITESPACE
            [1] =>  
            [2] => 3
        )

    [9] => Array
        (
            [0] => T_LNUMBER
            [1] => 1
            [2] => 3
        )

    [10] => Array
        (
            [0] => T_WHITESPACE
            [1] =>  
            [2] => 3
        )

    [11] => 
    [12] => Array
        (
            [0] => T_WHITESPACE
            [1] =>  
            [2] => 3
        )

    [13] => Array
        (
            [0] => T_LNUMBER
            [1] => 1
            [2] => 3
        )

    [14] => 
    [15] => Array
        (
            [0] => T_WHITESPACE
            [1] => 
	
            [2] => 3
        )

    [16] => Array
        (
            [0] => T_ECHO
            [1] => echo
            [2] => 4
        )

    [17] => Array
        (
            [0] => T_WHITESPACE
            [1] =>  
            [2] => 4
        )

    [18] => 
    [19] => Array
        (
            [0] => T_WHITESPACE
            [1] => 

            [2] => 4
        )

    [20] => Array
        (
            [0] => T_CLOSE_TAG
            [1] => ?>
            [2] => 5
        )

)

分析这个返回结果我们可以发现,源码中的字符串,字符,空格都会原样返回。

每个源代码中的字符,都会出现在相应的顺序处。

而其他的,比如标签,操作符,语句,都会被转换成一个包含三部分的

1、Token ID解释器代号 (也就是在Zend内部的改Token的对应码,比如,T_ECHO,T_STRING)

2、源码中的原来的内容

3、该词在源码中是第几行。

2. Parsing, 将Tokens转换成简单而有意义的表达式

接下来,就是Parsing阶段了,Parsing首先会丢弃Tokens Array中的多于的空格,

然后将剩余的Tokens转换成一个一个的简单的表达式

1.echo a constant string
2.add two numbers together
3.store the result of the prior expression to a variable
4.echo a variable

Bison是一种通用目的的分析器生成器。它将LALR(1)上下文无关文法的描述转化成分析该文法的C程序。 使用它可以生成解释器,编译器,协议实现等多种程序。 Bison向上兼容Yacc,所有书写正确的Yacc语法都应该可以不加修改地在Bison下工作。 它不但与Yacc兼容还具有许多Yacc不具备的特性。

Bison分析器文件是定义了名为yyparse并且实现了某个语法的函数的C代码。 这个函数并不是一个可以完成所有的语法分析任务的C程序。 除此这外我们还必须提供额外的一些函数: 如词法分析器、分析器报告错误时调用的错误报告函数等等。 我们知道一个完整的C程序必须以名为main的函数开头,如果我们要生成一个可执行文件,并且要运行语法解析器, 那么我们就需要有main函数,并且在某个地方直接或间接调用yyparse,否则语法分析器永远都不会运行。

在PHP源码中,词法分析器的最终是调用re2c规则定义的lex_scan函数,而提供给Bison的函数则为zendlex。 而yyparse被zendparse代替。

3. Compilation, 将表达式编译成Opocdes

之后就是Compilation阶段了,它会把Tokens编译成一个个op_array, 每个op_arrayd包含如下5个部分

在PHP实现内部,opcode由如下的结构体表如下:

struct _zend_op {
opcode_handler_t handler; // 执行该opcode时调用的处理函数
znode result;
znode op1;
znode op2;
ulong extended_value;
uint lineno;
zend_uchar opcode; // opcode代码
};

和CPU的指令类似,有一个标示指令的opcode字段,以及这个opcode所操作的操作数。

PHP不像汇编那么底层, 在脚本实际执行的时候可能还需要其他更多的信息,extended_value字段就保存了这类信息。

其中的result域则是保存该指令执行完成后的结果。

PHP脚本编译为opcode保存在op_array中,其内部存储的结构如下:

struct _zend_op_array {
	/* Common elements */
	zend_uchar type;
	char *function_name; // 如果是用户定义的函数则,这里将保存函数的名字
	zend_class_entry *scope;
	zend_uint fn_flags;
	union _zend_function *prototype;
	zend_uint num_args;
	zend_uint required_num_args;
	zend_arg_info *arg_info;
	zend_bool pass_rest_by_reference;
	unsigned char return_reference;
	/* END of common elements */
	zend_bool done_pass_two;
	zend_uint *refcount;
	zend_op *opcodes; // opcode数组
	zend_uint last,size;
	zend_compiled_variable *vars;
	int last_var,size_var;
	// ...
}

如上面的注释,opcodes保存在这里,在执行的时候由下面的execute函数执行:

ZEND_API void execute(zend_op_array *op_array TSRMLS_DC)
{
	// ... 循环执行op_array中的opcode或者执行其他op_array中的opcode
}

前面提到每条opcode都有一个opcode_handler_t的函数指针字段,用于执行该opcode。

PHP有三种方式来进行opcode的处理:CALL,SWITCH和GOTO。

PHP默认使用CALL的方式,也就是函数调用的方式, 由于opcode执行是每个PHP程序频繁需要进行的操作,

可以使用SWITCH或者GOTO的方式来分发, 通常GOTO的效率相对会高一些,

不过效率是否提高依赖于不同的CPU。

在我们上面的例子中,我们的PHP代码会被Parsing成:

* ZEND_ECHO     'Hello World%21'
* ZEND_ADD       ~0 1 1
* ZEND_ASSIGN  !0 ~0
* ZEND_ECHO     !0
* ZEND_RETURN  1
你可能会问了,我们的$a去那里了?这个要介绍操作数了,每个操作数都是由以下俩个部分组成:
a)op_type : 为IS_CONST, IS_TMP_VAR, IS_VAR, IS_UNUSED, or IS_CV
 
b)u,一个联合体,根据op_type的不同,分别用不同的类型保存了这个操作数的值(const)或者左值(var)

而对于var来说,每个var也不一样 IS_TMP_VAR, 顾名思义,这个是一个临时变量,保存一些op_array的结果,以便接下来的op_array使用, 这种的操作数的u保存着一个指向变量表的一个句柄(整数),这种操作数一般用~开头。 比如~0,表示变量表的0号未知的临时变量 IS_VAR 这种就是我们一般意义上的变量了,他们以$开头表示
IS_CV 表示ZE2.1/PHP5.1以后的编译器使用的一种cache机制, 这种变量保存着被它引用的变量的地址,当一个变量第一次被引用的时候,就会被CV起来, 以后对这个变量的引用就不需要再次去查找active符号表了,CV变量以!开头表示。

这么看来,我们的$a被优化成!0了。
比如我们使用VLD来查看opcodes显示如下: html
(注:因为鸟哥的博文是08年的,本文的数据虽然和鸟哥有些相似,PHP发展到现在已经有了不少改变, 所以大家看到鄙人的博文中程序运行结果以及相关的说明与鸟哥的不同, 请不要吃惊,鄙人的结果都是运行验证过的,PHP版本为5.4)
TIPI:http://www.php-internals.com/

排版老是乱,改了几次了- -。





www.bkjia.comtruehttp://www.bkjia.com/PHPjc/755777.htmlTechArticle一、前言 语言是人们进行沟通和交流的表达符号,每种语言都有专属于自己的符号,表达方式和规则。 就编程语言来说,它也是由特定的...
성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
Python 文本终端 GUI 框架,太酷了Python 文本终端 GUI 框架,太酷了Apr 12, 2023 pm 12:52 PM

Curses首先出场的是 Curses[1]。CurseCurses 是一个能提供基于文本终端窗口功能的动态库,它可以: 使用整个屏幕 创建和管理一个窗口 使用 8 种不同的彩色 为程序提供鼠标支持 使用键盘上的功能键Curses 可以在任何遵循 ANSI/POSIX 标准的 Unix/Linux 系统上运行。Windows 上也可以运行,不过需要额外安装 windows-curses 库:pip install windows-curses 上面图片,就是一哥们用 Curses 写的 俄罗斯

五个方便好用的Python自动化脚本五个方便好用的Python自动化脚本Apr 11, 2023 pm 07:31 PM

相比大家都听过自动化生产线、自动化办公等词汇,在没有人工干预的情况下,机器可以自己完成各项任务,这大大提升了工作效率。编程世界里有各种各样的自动化脚本,来完成不同的任务。尤其Python非常适合编写自动化脚本,因为它语法简洁易懂,而且有丰富的第三方工具库。这次我们使用Python来实现几个自动化场景,或许可以用到你的工作中。1、自动化阅读网页新闻这个脚本能够实现从网页中抓取文本,然后自动化语音朗读,当你想听新闻的时候,这是个不错的选择。代码分为两大部分,第一通过爬虫抓取网页文本呢,第二通过阅读工

用Python写了个小工具,再复杂的文件夹,分分钟帮你整理!用Python写了个小工具,再复杂的文件夹,分分钟帮你整理!Apr 11, 2023 pm 08:19 PM

糟透了我承认我不是一个爱整理桌面的人,因为我觉得乱糟糟的桌面,反而容易找到文件。哈哈,可是最近桌面实在是太乱了,自己都看不下去了,几乎占满了整个屏幕。虽然一键整理桌面的软件很多,但是对于其他路径下的文件,我同样需要整理,于是我想到使用Python,完成这个需求。效果展示我一共为将文件分为9个大类,分别是图片、视频、音频、文档、压缩文件、常用格式、程序脚本、可执行程序和字体文件。# 不同文件组成的嵌套字典 file_dict = { '图片': ['jpg','png','gif','webp

用 WebAssembly 在浏览器中运行 Python用 WebAssembly 在浏览器中运行 PythonApr 11, 2023 pm 09:43 PM

长期以来,Python 社区一直在讨论如何使 Python 成为网页浏览器中流行的编程语言。然而网络浏览器实际上只支持一种编程语言:JavaScript。随着网络技术的发展,我们已经把越来越多的程序应用在网络上,如游戏、数据科学可视化以及音频和视频编辑软件。这意味着我们已经把繁重的计算带到了网络上——这并不是JavaScript的设计初衷。所有这些挑战提出了对新编程语言的需求,这种语言可以提供快速、可移植、紧凑和安全的代码执行。因此,主要的浏览器供应商致力于实现这个想法,并在2017年向世界推出

从头开始构建,DeepMind新论文用伪代码详解Transformer从头开始构建,DeepMind新论文用伪代码详解TransformerApr 09, 2023 pm 08:31 PM

2017 年 Transformer 横空出世,由谷歌在论文《Attention is all you need》中引入。这篇论文抛弃了以往深度学习任务里面使用到的 CNN 和 RNN。这一开创性的研究颠覆了以往序列建模和 RNN 划等号的思路,如今被广泛用于 NLP。大热的 GPT、BERT 等都是基于 Transformer 构建的。Transformer 自推出以来,研究者已经提出了许多变体。但大家对 Transformer 的描述似乎都是以口头形式、图形解释等方式介绍该架构。关于 Tra

一文读懂层次聚类(Python代码)一文读懂层次聚类(Python代码)Apr 11, 2023 pm 09:13 PM

首先要说,聚类属于机器学习的无监督学习,而且也分很多种方法,比如大家熟知的有K-means。层次聚类也是聚类中的一种,也很常用。下面我先简单回顾一下K-means的基本原理,然后慢慢引出层次聚类的定义和分层步骤,这样更有助于大家理解。层次聚类和K-means有什么不同?K-means 工作原理可以简要概述为: 决定簇数(k) 从数据中随机选取 k 个点作为质心 将所有点分配到最近的聚类质心 计算新形成的簇的质心 重复步骤 3 和 4这是一个迭代过程,直到新形成的簇的质心不变,或者达到最大迭代次数

用 Python 实现导弹自动追踪,超燃!用 Python 实现导弹自动追踪,超燃!Apr 12, 2023 am 08:04 AM

大家好,我是J哥。这个没有点数学基础是很难算出来的。但是我们有了计算机就不一样了,依靠计算机极快速的运算速度,我们利用微分的思想,加上一点简单的三角学知识,就可以实现它。好,话不多说,我们来看看它的算法原理,看图:由于待会要用pygame演示,它的坐标系是y轴向下,所以这里我们也用y向下的坐标系。算法总的思想就是根据上图,把时间t分割成足够小的片段(比如1/1000,这个时间片越小越精确),每一个片段分别构造如上三角形,计算出导弹下一个时间片走的方向(即∠a)和走的路程(即vt=|AC|),这时

集成GPT-4的Cursor让编写代码和聊天一样简单,用自然语言编写代码的新时代已来集成GPT-4的Cursor让编写代码和聊天一样简单,用自然语言编写代码的新时代已来Apr 04, 2023 pm 12:15 PM

集成GPT-4的Github Copilot X还在小范围内测中,而集成GPT-4的Cursor已公开发行。Cursor是一个集成GPT-4的IDE,可以用自然语言编写代码,让编写代码和聊天一样简单。 GPT-4和GPT-3.5在处理和编写代码的能力上差别还是很大的。官网的一份测试报告。前两个是GPT-4,一个采用文本输入,一个采用图像输入;第三个是GPT3.5,可以看出GPT-4的代码能力相较于GPT-3.5有较大能力的提升。集成GPT-4的Github Copilot X还在小范围内测中,而

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

mPDF

mPDF

mPDF는 UTF-8로 인코딩된 HTML에서 PDF 파일을 생성할 수 있는 PHP 라이브러리입니다. 원저자인 Ian Back은 자신의 웹 사이트에서 "즉시" PDF 파일을 출력하고 다양한 언어를 처리하기 위해 mPDF를 작성했습니다. HTML2FPDF와 같은 원본 스크립트보다 유니코드 글꼴을 사용할 때 속도가 느리고 더 큰 파일을 생성하지만 CSS 스타일 등을 지원하고 많은 개선 사항이 있습니다. RTL(아랍어, 히브리어), CJK(중국어, 일본어, 한국어)를 포함한 거의 모든 언어를 지원합니다. 중첩된 블록 수준 요소(예: P, DIV)를 지원합니다.

MinGW - Windows용 미니멀리스트 GNU

MinGW - Windows용 미니멀리스트 GNU

이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

SublimeText3 영어 버전

SublimeText3 영어 버전

권장 사항: Win 버전, 코드 프롬프트 지원!

DVWA

DVWA

DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

VSCode Windows 64비트 다운로드

VSCode Windows 64비트 다운로드

Microsoft에서 출시한 강력한 무료 IDE 편집기