深入理解PHP原理之Opcodes

转载自大神风雪之偶的博客,自己记录一下!

最近要给Yahoo的同事们做一个关于PHP和Apache处理请求的内部机制的讲座,刚好写了些关于Opcodes的文字,就发上来了,这个文章基于 Sara Golemon大师的《Understanding OPcode》

目前编程语言可以分为两大类:

第一类是像C/C++, .NET, Java之类的编译型语言, 它们的共性是: 运行之前必须对源代码进行编译,然后运行编译后的目标文件。

第二类比如:PHP, Javascript, Ruby, Python这些解释型语言, 他们都无需经过编译即可"运行",虽然可以理解为直接运行,

但它们并不是真的直接就被能被机器理解, 机器只能理解机器语言,那这些语言是怎么被执行的呢, 一般这些语言都需要一个解释器, 由解释器来执行这些源码, 实际上这些语言还是会经过编译环节, 只不过它们一般会在运行的时候实时进行编译。为了效率,并不是所有语言在每次执行的时候都会重新编译一遍, 比如PHP的各种opcode缓存扩展(如APC, xcache, eAccelerator等),比如Python会将编译的中间文件保存成pyc/pyo文件, 避免每次运行重新进行编译所带来的性能损失。

PHP的脚本的执行也需要一个解释器, 比如命令行下的php程序,或者apache的mod_php模块等等。 前一节提到了PHP的SAPI接口, 下面就以PHP命令行程序为例解释PHP脚本是怎么被执行的。 例如如下的这段PHP脚本:

<?php $str = "Hello, Tipi!\n"; echo $str;

假设上面的代码保存在名为hello.php的文件中, 用PHP命令行程序执行这个脚本:

$ php ./hello.php

这段代码的输出显然是Hello, Tipi!, 那么在执行脚本的时候PHP/Zend都做了些什么呢? 这些语句是怎么样让php输出这段话的呢? 下面将一步一步的进行介绍。

程序的执行

如上例中, 传递给php程序需要执行的文件, php程序完成基本的准备工作后启动PHP及Zend引擎, 加载注册的扩展模块。

初始化完成后读取脚本文件,Zend引擎对脚本文件进行词法分析,语法分析。然后编译成opcode执行。 如果安装了apc之类的opcode缓存, 编译环节可能会被跳过而直接从缓存中读取opcode执行。

Opcode是一种PHP脚本编译后的中间语言,就像Java的ByteCode,或者.NET的MSL,举个例子,比如你写下了如下的PHP代码:

 <?php

   echo "Hello World";

   $a = 1 + 1;

   echo $a;

?>

PHP执行这段代码会经过如下4个步骤(确切的来说,应该是PHP的语言引擎Zend)

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

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

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

4.Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。

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

那什么是Lexing? 学过编译原理的同学都应该对编译原理中的词法分析步骤有所了解,Lex就是一个词法分析的依据表。 Zend/zend_language_scanner.c会根据Zend/zend_language_scanner.l(Lex文件),来输入的 PHP代码进行词法分析,从而得到一个一个的“词”,PHP4.2开始提供了一个函数叫token_get_all,这个函数就可以讲一段PHP代码 Scanning成Tokens;
如果用这个函数处理我们开头提到的PHP代码,将会得到如下结果:

Array

(

    [0] => Array

        (

           [0] => 367

           [1] => Array

        (

            [0] => 316

            [1] => echo

        )

    [2] => Array

        (

            [0] => 370

            [1] =>

        )

    [3] => Array

        (

            [0] => 315

            [1] => "Hello World"

        )

    [4] => ;

    [5] => Array

        (

            [0] => 370

            [1] =>

        )

    [6] => =

    [7] => Array

        (

            [0] => 370

            [1] =>

        )

    [8] => Array

        (

            [0] => 305

            [1] => 1

        )

    [9] => Array

        (

            [0] => 370

            [1] =>

        )

    [10] => +

    [11] => Array

        (

            [0] => 370

            [1] =>

        )

    [12] => Array

        (

            [0] => 305

            [1] => 1

        )

    [13] => ;

    [14] => Array

        (

            [0] => 370

            [1] =>

        )

    [15] => Array

        (

            [0] => 316

            [1] => echo

        )

    [16] => Array

        (

            [0] => 370

            [1] =>

        )

    [17] => ;

)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgfzsg.html