博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
php script 的生命周期
阅读量:4920 次
发布时间:2019-06-11

本文共 7691 字,大约阅读时间需要 25 分钟。

原文地址:

The Execution Life-cycle

 

  PHP is a scripting language, which most people take to mean that it is not compiled. While this is true in the traditional sense in that we are not calling a gcc or javac; instead we are compiling every time the script is requested. In fact, the PHP and Java compilation life cycles are pretty similar, because they both compile to an intermediary instruction set (opcodes, or bytecodes) which are then run in a virtual machine (Zend VM or JVM).

The parsing and compilation phases is slow. When we add an opcache, we short-circuit this process by storing the result of the parsing and compilation phases, leaving just the execution to run dynamically as always. In effect, we are closer to the Java life-cycle now; with the main differences being that we saved to shared memory instead of a file, and can automatically re-compile if changes occur to the script.

 

 

Tokens & OpCodes

 

Once PHP gets ahold of your code, it creates two representations of your code.  The first is tokens; this is a way to break down your code into consumable chunks for the engine to compile into it’s second representation, opcodes. The opcodes are the actual instructions for the Zend VM to execute.

The Worst Hello World Ever

 

Taking a simple code example, a vastly over-engineered hello world example, lets look at both tokens, and opcodes.

sayHello("World");?>

  

Tokenizing

 

The first part of the compilation process parses the code into tokens. These are passed to the compiler to create OpCodes.

 

Token Name

Value

T_OPEN_TAG

<?php

T_CLASS

class

T_WHITESPACE

 

T_STRING

  Greeting

T_WHITESPACE

 
 

  {

T_WHITESPACE

 

T_PUBLIC

    public

T_WHITESPACE

 

T_FUNCTION

      function

T_WHITESPACE

 

T_STRING

        sayHello

 

        (

T_VARIABLE

          $to

 

        )

T_WHITESPACE

 
 

      {

T_WHITESPACE

 

T_ECHO

        echo

T_WHITESPACE

 
 

          "

T_ENCAPSED_AND_WHITESPACE

            Hello

T_VARIABLE

            $to

 

          "

 

        ;

T_WHITESPACE

 
 

      }

T_WHITESPACE

 
 

}

T_WHITESPACE

 

T_VARIABLE

$greeter

T_WHITESPACE

 
 

  =

T_WHITESPACE

 

T_NEW

    new

T_WHITESPACE

 

T_STRING

      Greeting

 

      (

 

      )

 

      ;

T_WHITESPACE

 

T_VARIABLE

$greeter

T_OBJECT_OPERATOR

  ->

T_STRING

    sayHello

 

    (

T_CONSTANT_ENCAPSED_STRING

      "World"

 

    )

 

;

T_WHITESPACE

 

T_CLOSE_TAG

?>

 

As you can see, most discrete piece of the code is given a name, starting with T_ and then a descriptive name. This is where the infamous T_PAAMAYIM_NEKUDOTAYIM error comes from: it represents the double colon.

 

Some tokens do not have a T_ name, this is because they are a single character — it would obviously wasteful to then assign them a much larger name — T_DOUBLE_QUOTE or T_SEMICOLON don’t make much sense.

 

It is also interesting to see the difference interpolation of variables makes; take the two strings, both double quotes, "Hello $to" and "World", the first, to account for the interpolated variables is split into 4 unique opcodes:

 

 

 

          "

T_ENCAPSED_AND_WHITESPACE

            Hello

T_VARIABLE

            $to

 

          "

 

and the non-interpolated string is simply one:

 

T_CONSTANT_ENCAPSED_STRING

      "World"

 

With this view we can start to see how small choices we make start to affect performance in miniscule ways (and frankly, especiallyin this case, as with single Vs double quotes, it’s not worth caring about!)

 

You can see the script that was used to generate this list of tokens .

OpCodes

 

Next, lets look at how this looks like once we have compiled to opcodes. This is what the OpCode caches store.

 

To get the opcodes, we run the script through VLD, the Vulcan Logic Dumper, a pecl extension for PHP. To install it simple run:

 

$ pecl install vld-beta

 

And make sure the following is added to your PHP config:

  extension=vld.so
 hosted with ❤ by 

Once you’ve done this, you can dump the opcodes for any code using the following on the command line:

 

$ php -dvld.active=1 -dvld.execute=0 <file>

 

VLD dumps global code (main script), global functions, and then class functions. However we’re going to look at our class functions first so as follow the same flow as the code itself.

Understanding VLD Dumps

A VLD dump is typically multiple dumps, one for the main script, then one for each global function and class function. Each dump is identical in structure.

 

First is the header which lists (if applicable) the class, and the function; then it lists the filename. Next it lists the function name (again, and only if applicable).

  Class Greeting:
  Function sayhello:
  filename: ./Greeting.php
  function name: sayHello
 hosted with ❤ by 

Next, it lists the total number of opcodes in the dump:

  number of ops: 8
 hosted with ❤ by 

And then it lists the compiled variables (see below for details):

  compiled vars: !0 = $to
 hosted with ❤ by 

This is particularly important to take notice of.

Finally, it actually lists the opcodes, one per line, under the heading row:

  line # * op fetch ext return operands
  ------------------------------------------------------------------------
 hosted with ❤ by 

Each opcode has the following:

 

  • line: The line number in the source file
  • #: The opcode number
  • *: entry (left aligned) and exit points (right aligned), indicated by greater than symbols (>)
  • op: The opcode name
  • fetch: Details on global variable fetches (super globals, or the use of the global keyword)
  • ext: Extra data associated with the opcode, for example the opcode to which it should JMP
  • return: The location where return data from the operation is stored
  • operands: the operands used by the opcode (e.g. two variables to concat)

 

Note: Not all columns are applicable to all opcodes.

Variables

There are multiple types of variables within the Zend Engine. All variables use numeric identifiers.

  1. Variable prefixed with an exclamation point (!) are compiled variables (CVs) — these are pointers to userland variables
  2. Variables prefixed with a tilde (~) are temporary variables used for temporary storage (TMP_VARs) of in-process operations
  3. Variables prefixed with a dollar ($) are another type of temporary variables (VARs) which are tied to userland variables like CVs and therefore require things like refcounting.
  4. Variables prefixed with a colon (:) are  temporary variables used for the storage of the result of lookups in the class hashtable

Dumping Hello World

  Class Greeting:
  Function sayhello:
  number of ops: 8
  compiled vars: !0 = $to
   
  line # * op fetch ext return operands
  ----------------------------------------------------------------------------
  3 0 > EXT_NOP
  1 RECV !0
  5 2 EXT_STMT
  3 ADD_STRING ~0 'Hello+'
  4 ADD_VAR ~0 ~0, !0
  5 ECHO ~0
  6 6 EXT_STMT
  7 > RETURN null
 hosted with ❤ by 

Reading this, we see that we’re in the function sayHello of the class Greeting, and that we have one compiled variable, $to, which is identified by !0.

 

Next, following the list of opcodes (ignoring the no-op), we can see that:

 

  • RECV: The function receives a value which is assigned to !0 (which represents $to)
  • ADD_STRING: Next we create a temporary variable identified by a ~~0 and assign the static string, 'Hello+', where the +represents a space
  • ADD_VAR: After this, we concat the contents our variable, !0 to our temporary variable, ~0
  • ECHO: Then we echo that temporary variable
  • RETURN: Finally we return nothing as the function ends

 

Next, lets look at the main script:

Here we see one compiled variable, $greeter, identified by !0. So lets walk through this, again we’ll ignore the no-op.

 

  • FETCH_CLASS: First we lookup the class, Greeter; we store this reference in :1
  • NEW: Then we instantiate an instance of the class (:1) and assign it to a VAR$2, and
  • DO_FCALL_BY_NAME: call the constructor
  • ASSIGN: Next we assign the resulting object (in VAR $2) to our CV (!0)
  • INIT_METHOD_CALL: We start calling the sayHello method, and
  • SEND_VAL: pass in 'World' to the method, and
  • DO_FCALL_BY_NAME: Actually execute the method
  • RETURN: The script ends successfully, with an implicit return of 1.

转载于:https://www.cnblogs.com/oxspirt/p/5507533.html

你可能感兴趣的文章
[bzoj3527][Zjoi2014]力_FFT
查看>>
评分卡模型建模的各步骤中的一些方法工具、开发要点、优化方向、调研总结...
查看>>
python selenium 滚动条处理、页面拖动
查看>>
轻松配置httpd的虚拟主机
查看>>
Perl处理数据(一):s替换、split和join
查看>>
CSS缎带效果
查看>>
JDK 动态代理分析
查看>>
LSTM(long short term memory)长短期记忆网络
查看>>
Linux NFS服务器的安装与配置
查看>>
BZOJ2242 [SDOI2011]计算器 【BSGS】
查看>>
第一次冲刺阶段(八)
查看>>
环境变量相关错误
查看>>
Linux 文件基本属性(三)
查看>>
BZOJ 4196 软件包管理器
查看>>
XML解析错误:xml处理指令不在外部实体的开始部分(转)
查看>>
Python递归查找文件(os.walk, os.path, fnmatch)
查看>>
Codeforces Round #312 (Div. 2) ABC题解
查看>>
BeanUtils数据封装与表单JavaBean
查看>>
使用Heartbeat实现双机热备
查看>>
Android-Launcher开发之ShortCut(1)
查看>>