首頁 > 後端開發 > php教程 > PHP如何執行 - 從源代碼到渲染

PHP如何執行 - 從源代碼到渲染

Jennifer Aniston
發布: 2025-02-10 10:11:11
原創
808 人瀏覽過

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
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板