首页 > 后端开发 > php教程 > PHP如何执行 - 从源代码到渲染

PHP如何执行 - 从源代码到渲染

Jennifer Aniston
发布: 2025-02-10 10:11:11
原创
785 人浏览过

PHP如何执行 - 从源代码到渲染

本文由Younes Rafie进行了同行评审。感谢SitePoint所有的同行评审员制作SitePoint内容的最佳状态!


>受Ruby代码如何执行的最新文章的启发,本文涵盖了PHP代码的执行过程。

PHP如何执行 - 从源代码到渲染钥匙要点

PHP代码的执行涉及四个阶段:Lexing,解析,编译和解释。每个阶段在将PHP源代码转换为机器可读代码的过程中至关重要。

> Lexing或令牌化是将字符串(PHP源代码)变成一个令牌序列的过程。每个令牌是其匹配值的命名标识符。此阶段还存储lexeme和匹配令牌的行号。> 解析阶段
    验证令牌顺序的有效性,并生成抽象语法树(AST)。 AST是在编译阶段使用的源代码的树视图。 汇编阶段通过遍历AST并进行优化,例如使用字面参数和折叠恒定的数学表达式进行优化。可以使用OPCACHE,VLD和PHPDBG检查此阶段的输出。
  • 解释阶段是在Zend Engine(ZE)VM上运行Opcodes的最后阶段。此阶段的输出是您的PHP脚本通过echo,print,var_dump等命令输出的内容。
  • >
  • 简介
  • >当我们执行PHP代码时,引擎盖下发生了很多事情。从广义上讲,执行代码时,PHP解释器将经历四个阶段:>
  • Lexing
  • 解析

汇编

解释

  1. >本文将浏览这些阶段,并展示我们如何查看每个阶段的输出,以真正查看发生了什么。请注意,虽然某些使用的扩展程序应该已经是您的PHP安装的一部分(例如Tokenizer和opcache),但需要手动安装和启用其他扩展程序(例如,PHP-ast和VLD)。
  2. 阶段1 - Lexing
  3. Lexing(或令牌化)是将字符串(在这种情况下为PHP源代码)转换为令牌序列的过程。令牌只是其匹配的值的命名标识符。 PHP使用re2c从zend_language_scanner.l定义文件。
  4. >我们可以通过令牌扩展名看到Lexing阶段的输出:
  5. >输出:
  6. $code = <<<'code'
    <span><span><?php
    </span></span><span><span>$a = 1;
    </span></span><span>code<span>;
    </span></span><span>
    </span><span><span>$tokens = token_get_all($code);
    </span></span><span>
    </span><span><span>foreach ($tokens as $token) {
    </span></span><span>    <span>if (is_array($token)) {
    </span></span><span>        <span>echo "Line <span><span>{$token[2]}</span>: "</span>, token_name($token[0]), " ('<span><span>{$token[1]}</span>')"</span>, PHP_EOL;
    </span></span><span>    <span>} else {
    </span></span><span>        <span>var_dump($token);
    </span></span><span>    <span>}
    </span></span><span><span>}
    </span></span>
    登录后复制
    登录后复制

    >从上述输出中有几个值得注意的点。第一个点是,并非所有源代码的所有部分都命名为令牌。相反,某些符号本身被视为令牌(例如=,;,:,?等)。第二点是,Lexer实际上做的不只是简单地输出一个令牌流。在大多数情况下,它也存储了lexeme(由令牌匹配的值)和匹配令牌的行号(用于堆栈跟踪之类的内容)。

    阶段2 - 解析

    也生成了解析器,这次是通过BNF语法文件与野牛一起生成的。 PHP使用LALR(1)(向前,从左到右)无上下文语法。前面的外观仅意味着解析器能够在解析时可能会遇到的歧义。从左到右的部分意味着它从左到右解析令牌流。

    >

    >生成的解析器阶段将令牌流从Lexer作为输入中获取,并有两个作业。首先,它通过尝试将其与BNF语法文件中定义的任何语法规则相匹配,从而验证令牌顺序的有效性。这样可以确保令牌流中的代币形成有效的语言构造。解析器的第二个作业是生成

    抽象语法树

    (AST) - 下一阶段将使用的源代码的树视图(汇编)。 我们可以使用php-ast扩展名来查看的一种

    。内部AST并没有直接暴露,因为与之合作(在一致性和一般可用性方面)并不是特别“干净”,因此PHP-AST扩展对其进行了一些转换以使其可以更好地使用。 >让我们看一下基本代码的AST:> >输出:

    Line 1: T_OPEN_TAG ('<?php
    ')
    Line 2: T_VARIABLE ('$a')
    Line 2: T_WHITESPACE (' ')
    string(1) "="
    Line 2: T_WHITESPACE (' ')
    Line 2: T_LNUMBER ('1')
    string(1) ";"
    
    登录后复制
    登录后复制
    树节点(通常是类型的astnode)具有多个属性:

    $code = <<<'code'
    <span><span><?php
    </span></span><span><span>$a = 1;
    </span></span><span>code<span>;
    </span></span><span>
    </span><span><span>print_r(ast<span>\parse_code</span>($code, 30));
    </span></span>
    登录后复制
    登录后复制
    类型 - 描绘节点类型的整数值;每个都有相应的常数(例如AST_STMT_LIST => 132,AST_ASSIGN => 517,AST_VAR => 256)

    >标志 - 一个指定过载行为的整数(例如,ASTAST_BINARD_OP节点将具有区分发生哪些二进制操作的标志)

    Lineno
    lineno - 线号,从较早的令牌信息中可以看出
  • 儿童 - 子节点,通常会进一步分解该节点的一部分(例如,功能节点将具有孩子:参数,返回类型,身体等)
  • >此阶段的AST输出很方便用于诸如静态代码分析仪(例如phan)之类的工具。

    阶段3 - 汇编

    汇编阶段消耗了AST,它通过递归穿越树来发出opcods。这个阶段还进行了一些优化。这些包括通过字面论据(例如strlen(“ ABC”)到int(3))和折叠持续的数学表达式(例如60 * 60 * 24 to int(86400))。

    >我们可以在此阶段以多种方式检查OpCode输出,包括OPCACHE,VLD和PHPDBG。我将使用VLD为此,因为我觉得输出更友好。

    >让我们看看以下file.php脚本的输出是什么:

    执行以下命令:
    $code = <<<'code'
    <span><span><?php
    </span></span><span><span>$a = 1;
    </span></span><span>code<span>;
    </span></span><span>
    </span><span><span>$tokens = token_get_all($code);
    </span></span><span>
    </span><span><span>foreach ($tokens as $token) {
    </span></span><span>    <span>if (is_array($token)) {
    </span></span><span>        <span>echo "Line <span><span>{$token[2]}</span>: "</span>, token_name($token[0]), " ('<span><span>{$token[1]}</span>')"</span>, PHP_EOL;
    </span></span><span>    <span>} else {
    </span></span><span>        <span>var_dump($token);
    </span></span><span>    <span>}
    </span></span><span><span>}
    </span></span>
    登录后复制
    登录后复制
    >

    我们的输出是:
    Line 1: T_OPEN_TAG ('<?php
    ')
    Line 2: T_VARIABLE ('$a')
    Line 2: T_WHITESPACE (' ')
    string(1) "="
    Line 2: T_WHITESPACE (' ')
    Line 2: T_LNUMBER ('1')
    string(1) ";"
    
    登录后复制
    登录后复制

    > Opcodes类似于原始源代码,足以与基本操作一起进行。 (我不会深入研究本文中的opcodes的详细信息,因为这本身将需要几个整个文章。)在上面脚本中没有在OpCode级别上应用优化,但是正如我们所看到的,编译阶段通过解决恒定条件(php_version ==='7.1.0-dev')来做出一些。
    $code = <<<'code'
    <span><span><?php
    </span></span><span><span>$a = 1;
    </span></span><span>code<span>;
    </span></span><span>
    </span><span><span>print_r(ast<span>\parse_code</span>($code, 30));
    </span></span>
    登录后复制
    登录后复制
    > OPCACHE不仅可以简单地缓存OPCODE(因此绕过Lexing,解析和编译阶段)。它还包含许多不同级别的优化。让我们将优化级别提高到四个传球,以查看出来的内容:

    >

    >命令:

    >输出:

    ast\Node Object (
        [kind] => 132
        [flags] => 0
        [lineno] => 1
        [children] => Array (
            [0] => ast\Node Object (
                [kind] => 517
                [flags] => 0
                [lineno] => 2
                [children] => Array (
                    [var] => ast\Node Object (
                        [kind] => 256
                        [flags] => 0
                        [lineno] => 2
                        [children] => Array (
                            [name] => a
                        )
                    )
                    [expr] => 1
                )
            )
        )
    )
    
    登录后复制

    >我们可以看到恒定条件已被删除,并且两个回声指令已被压缩到单个指令中。这些只是对Opcache在脚本的Opcodes上进行的许多优化的味道。不过,我不会浏览本文的各种优化级别,因为这本身也是一篇文章。

    >阶段4 - 解释
    <span>if (PHP_VERSION === '7.1.0-dev') {
    </span>    <span>echo 'Yay', PHP_EOL;
    </span><span>}
    </span>
    登录后复制

    >最后阶段是对opcodes的解释。这是Opcodes在Zend Engine(ZE)VM上运行的地方。对于这个阶段,实际上几乎没有什么可说的(至少从高级角度来看)。输出几乎是您通过echo,print,var_dump等命令输出输出的任何内容。

    >因此,这是一个有趣的事实,而不是在此阶段挖掘任何复杂的事实:PHP在生成自己的VM时需要自己作为依赖性。这是因为VM是由PHP脚本生成的,因为它更简单并且更易于维护。

    结论

    >我们已经简要介绍了PHP解释器在运行PHP代码时通过的四个阶段。这涉及使用各种扩展(包括令牌,PHP-ast,opcache和vld)来操纵和查看每个阶段的输出。

    >我希望本文能够帮助您对PHP的解释器有更好的整体理解,并显示了Opcache扩展的重要性(用于其缓存和优化能力)。

    经常询问有关PHP执行过程的问题(常见问题解答)

    > PHP解释器在执行过程中的作用是什么?它负责将PHP源代码转换为机器可读代码。解释器逐行读取PHP脚本,解释每行并执行必要的操作。它还负责在执行过程中处理错误和例外。 PHP解释器是PHP运行时环境的关键组成部分,其中还包括Web服务器和PHP扩展。 PHP执行过程。它负责解析PHP脚本,将其编译到字节码中,然后执行字节码。 PHP引擎使用两步过程来执行PHP脚本。首先,它解析PHP脚本并将其转换为抽象语法树(AST)。然后,将AST编译到字节码中并执行。 PHP引擎还包括一个内存管理器和一个垃圾收集器,以在执行过程中管理内存使用量。

    >

    > PHP的命令行接口和Web服务器接口之间有什么区别? -line接口(CLI)和Web服务器接口是运行PHP脚本的两种不同方法。 CLI用于从命令行运行PHP脚本,而Web服务器接口则用于响应Web请求来运行PHP脚本。两个接口之间的主要区别是它们处理输入和输出的方式。在CLI中,从命令行读取输入,并将输出写入控制台。在Web服务器接口中,从HTTP请求读取输入,并将输出写入HTTP响应。

    >

    PHP在执行过程中如何处理错误?

    php具有强大的错误处理。允许其在执行过程中处理错误的机制。发生错误时,PHP会生成错误消息并将其发送到错误处理程序。错误处理程序可以根据错误报告设置显示错误消息,将其记录或忽略。 PHP还支持异常处理,这使其可以以更结构化和易于管理的方式处理错误。>

    > php扩展在执行过程中的作用是什么?

    php扩展是在PHP语言中添加新功能和功能的模块。在执行过程中,它们被加载到PHP运行时环境中,可用于执行从数据库访问到图像处理的广泛任务。 PHP扩展名为C编写,并编译为机器代码,这使其非常快速有效。它们是PHP生态系统的关键组成部分,并有助于其灵活性和功率。

    >

    > PHP如何优化执行过程?

    PHP使用多种技术来优化执行过程。这些技术之一是OpCode缓存,其中涉及将PHP引擎生成的字节码存储在内存中,以便可以在后续执行中重复使用。这消除了每次执行PHP脚本的必要性,从而大大提高了性能。 PHP还使用Just-In-time(JIT)汇编,其中涉及在运行时将字节码编译到机器代码中以进一步提高性能。

    >

    PHP在执行过程中如何处理内存管理? PHP具有内置内存管理器,可以在执行过程中处理内存分配和Deallocation。内存管理器根据需要为变量和数据结构分配内存,并在不再需要内存时对内存进行交易。 PHP还具有一个垃圾收集器,该垃圾收集器会自动释放不再使用的内存。这有助于防止内存泄漏并将内存使用控制在控制之下。

    > Web服务器在PHP执行过程中的作用是什么?

    >

    > Web服务器在PHP执行中起关键作用过程。它负责处理HTTP请求,对这些请求运行PHP脚本,然后将HTTP响应发送回客户端。 Web服务器与PHP解释器和PHP引擎紧密合作,以执行PHP脚本并生成动态网页。 PHP最常用的Web服务器是Apache和Nginx。 MySQL,PostgreSQL和SQLite。它在执行过程中使用数据库特异性扩展与这些数据库进行交互。这些扩展提供了一组功能,可用于连接到数据库,执行SQL查询,获取结果并处理错误。 PHP还支持PDO(PHP数据对象)扩展名,该扩展提供了数据库交互的数据库-Nostic接口。

    > PHP在执行过程中如何处理会话管理?

    PHP对会话管理具有内置支持,这使其可以在不同的HTTP请求之间维护状态。启动会话后,PHP会创建唯一的会话ID,并将其存储在客户端浏览器上的cookie中。然后,此会话ID通过每个后续请求发送回服务器,允许PHP识别客户端并检索相应的会话数据。 PHP的会话管理功能使在Web应用程序中实现用户身份验证,购物车和其他状态功能变得易于实现。

以上是PHP如何执行 - 从源代码到渲染的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板