PHP chạy như nào? Từ Source code đến render

Giới thiệu

Khi chúng ta chạy một đoạn code PHP, có rất nhiều thứ xảy ra sâu bên dưới mà ta không nhìn thấy. Một cách khái quát, bộ thông dịch PHP trải qua 4 giai đoạn khi nó thực thi 1 đoạn code:

  • Lexing

  • Parsing

  • BIên dịch (compilation)

  • Thông dịch (intepretation)

Bài viết này sẽ tập trung vào các giai đoạn này, biểu diễn các output trong mỗi giai đoạn để hiểu bản chất. Cần chú ý rằng một số extension được cài đặt mặc định cùng PHP ( tokenizer, OPcache, …), trong khi đó nhiều extension bắt buộc phải cài đặt và khởi động 1 cách thủ công ( php-ast và VLD ).

Giai đoạn 1: LEXING

Lexing (tokenizing) là quá trình chuyển một đoạn string (mã nguồnPHP) thành 1 chuỗi các token. Một token đơn giản là một định danh gắn với một giá trị. PHP sử dụng re2c để sinh ra các lexer của nó từ file khai báo zend_language_scanner.

Hãy xem output trong giai đoạn Lexing thông qua tokenizer extension:

$code = <<<'code'
<?php
$a = 1;
code;

$tokens = token_get_all($code);

foreach ($tokens as $token) {
    if (is_array($token)) {
        echo "Line {$token[2]}: ", token_name($token[0]), " ('{$token[1]}')", PHP_EOL;
    } else {
        var_dump($token);
    }
}

Outputs:

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) ";"

Có một vài điểm cần chú ý khi nhìn vào những dòng output trên. Đầu tiên, không phải tất cả các dòng code đều được định nghĩa (tokenize). Các kí hiệu như =, ;, :, ? vẫn được giữ nguyên. Thứ hai, lexing không đơn giản chỉ output ra một dãy các token. Trong phần lớn trường hợp, nó cũng lưu trữ các lexeme (giá trị tương ứng của mỗi định danh) và số thứ tự hàng (sẽ được ứng dụng với các stack trace).

Giai đoạn 2: PARSING

Bộ phân tích cú phsp (parser) được tạo ra với Bison qua file grammar BNF. PHP sử dụng một cấu trúc gọi là LALR (Look Ahead Left to Right - từ trên xuống, từ trái sang). Nó có nghĩa là bộ parser có khả năng tìm các token n (trong trường hợp này, là 1) để giải quyết các vấn đề không rõ ràng trong khi phân tích cú pháp. Phần Left-to-Right có nghĩa đơn giản là bộ parser sẽ phân tích từ trái sang phải.

 

Giai đoạn phân tích cú pháp này sẽ nhận các luồng token từ lexer như các biến đầu vào và thực hiện 2 công việc. Đầu tiên, nó sẽ xác định tính hợp lệ của các token bằng việc cố gắng so khớp chúng với từng quy tắc ngữ pháp định nghĩa trong tập tin ngữ pháp BNF của nó. Việc này sẽ làm đảm bảo cấu trúc ngôn ngữ hợp lệ theo dạng của nó trong các luồng stream. Công việc thứ hai của bộ parser là để tạo ra cây cú pháp trừu tượng (AST) - mã nguồn sẽ hiển thị dưới dạng cây, dùng trong giai đoạn kế tiếp ( biên dịch ).

Chúng ta có thể xem 1 form dạng AST tạo ra bởi bộ parser sử dụng php - ast extension. AST bên trong không thấy được 1 cách trực tiếp vì nó không phải thực sự " clean" để làm việc ( liên quan đến tính nhất quán và tính dễ sử dụng ), và vì thế php - ast extension thực hiện một vài biến đổi để làm nó trở nên dễ dàng hơn.

Dưới đây là một AST:

$code = <<<'code'
<?php
$a = 1;
code;

print_r(ast\parse_code($code, 30));

Outputs:

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
            )
        )
    )
)

Các nút (ast/Nodes) có một số thuộc tính:

  • Kind: giá trị integer để mô tả loại nút ; mỗi cái có hằng số tương ứng

  • Flag: số nguyên xác định hành vi quá tải ( chẳng hạn như nút ast\AST_BINARY_OP sẽ có flag để phân biệt phép toán nhị phân nào màng xảy ra )

  • Lineno - số dòng

  • Childredn: nhóm nút.

Giai đoạn 3: COMPILING - Biên dịch

Giai đoạn biên dịch sử dụng AST, nó sẽ phát ra các mã tác vụ bằng cách duyệt cây theo phương pháp đệ quy. Giai đoạn này cũng thực hiện một vài tối ưu hoá. Những việc này bao gồm giải quyết một số lời gọi hàm với đối số tường minhtrựcư là strlen("abc") hay int(3),..

Chúng ta có thể kiểm tra đầu ra của các đoạn mã được tối ưu hóa bằng nhiều cách, với OPcache, VLD, và PHPDBG. Bài viết này sử dụng VLD, vì nó tạo ra các đoạn mã output dễ đọc hơn.

Hãy xem output trong đoạn file.php:

if (PHP_VERSION === '7.1.0-dev') { 

echo 'Yay', PHP_EOL; 

} 

Đoạn mã lệnh để thực thi:

php -dopcache.enable_cli=1 -dopcache.optimization_level=0 -dvld.active=1 -dvld.execute=0 file.php

Output:

line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   3     0  E > > JMPZ                                                     <true>, ->3
   4     1    >   ECHO                                                     'Yay'
         2        ECHO                                                     '%0A'
   7     3    > > RETURN                                                   1

Mã tác vụ sắp xếp giống mã nguồn gốc, có thể thực hiện các thao tác cơ bản.Ở đây không sử dụng phương pháp tối ưu hoá Không được áp dụng tại mức mã tác vụ trong kịch bản bên trên - nhưng như đã thấy, giai đoạn biên dịch đã gán cho hằng số điều kiện ( PHP_VERSION = = ' 7.1.0 - dev ' ) thành true.

OPcache thực hiện nhiều tác vụ hơn là chỉ lưu trữ tạm vào bộ nhớ cache mã tác vụ ( do đó bỏ qua các giai đoạn tránhg, phân tích cú pháp, và biên dịch ). Nó cũng được tối ưu hóa theo nhiều cấp độ khác nhau.

Câu lệnh:

php -dopcache.enable_cli=1 -dopcache.optimization_level=1111 -dvld.active=-1 -dvld.execute=0 file.php 

 

Output:

line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   4     0  E >   ECHO                                                     'Yay%0A'
   7     1      > RETURN                                                   1

Chúng ta có thể thấy rằng hằng số điều kiện đã bị loại bỏ, và 2 dòng ECHO đã hợp thành một lệnh. Đây chỉ là một trong nhiều cách mà OPcache thực hiện tối ưu hoá khi thực hiện chuyển qua mã tác vụ của kịch bản.

Giai đoạn 4: INTEPRETER - Thông dịch

Giai đoạn cuối là thông dịch mã tác vụ. Đây là nơi mã tác vụ đang chạy trên Zen Engine VM. Giai đoạn này hầu như rất ngắn. Đầu ra gần như tương tự với kết quả khi ta sử dụng các câu lệnh PHP như echo, var_dump,…

Thay vì đào sâu thêm vào những thứ phức tạp trong giai đoạn này, hãy cùng xem một sự thật thú vị: PHP yêu cầu chính nó như 1 dependency khi sản sinh ra chính VM của nó. Bởi vì VM được sinh ra bởi mã PHP, do đó nó trở nên đơn giản hơn để viết và dễ dàng hơn để bảo trì.

Kết luận

Như vậy thì chúng ta đã có một cách nhìn thoáng qua về 4 giai đoạn hoạt động của PHP interpreter khi nó biên dịch code PHP. Chúng ta cũng đã sử dụng các extendsions từ bên ngoài như tokenizer, php-ast, OPcache, và VLD để xem các trạng thái của nó khi chạy.

Tôi hy vọng bài viết này đã giúp cung cấp cho bạn một sự hiểu biết toàn diện hơn về PHP interpreter, cũng như tầm quan trọng của việc mở rộng OPcache (cho cả bộ nhớ đệm của nó và khả năng tối ưu hóa).

Nguồn: Sitepoint và bản dịch Đinh Thiên Phúc - học viên trung tâm Techmaster.vn

Có thể bạn muốn đọc
Với 5 mẹo này sẽ giúp bạn cải thiện được khả năng lập trình logic

Hãy luôn ghi nhớ rằng bạn đang trong quá trình học hỏi và cải thiện trình độ của mình. Mọi thứ bạn làm sẽ hơi chậm, nhưng tiến trình sẽ được cải thiện. Não bộ của bạn sẽ được “đào tạo” bài bản để tìm câu trả lời cho các câu hỏi khác nhau.

Các yêu cầu bạn cần có khi học ngành công nghệ thông tin

Công nghệ thông tin đang nổi lên là một ngành phát triển, có vai trò quan trọng trong sự phát triển của một xã hội hiện nay và có rất nhiều quyết tâm theo đuổi nó. Thế nhưng, đã có một ai thực sự tìm hiểu về khái niệm công nghệ thông tin là gì?

10 bí quyết giúp học và ghi nhớ nhanh mọi thứ bạn muốn

Nếu đang gặp khó khăn khi phải thu nạp kiến thức mới, thông tin mới hay rèn luyện một kỹ năng mới thì bài viết này sẽ gỡ rối cho bạn.

Hướng dẫn đăng kí và cách kiếm Pi nhanh chóng.

Pi network là một cryptocurency mới và được khai thác chỉ trên điện thoại, nhưng đây là loại hình đào coin kỹ thuật số mới không tốn tài nguyên CPU như những app đào coin khác. Những app đào coin khác như ETN nó sẻ sử dụng CPU để giải mã thuật toán ( ETN sử dụng thuật toán cryptonight) Còn đây khi đào coin đt không hề cảm thấy nóng. Bạn cũng có thể tắt app, tắt mạng đi cũng có thể đào được chỉ cần sau mỗi 24h vào app và nhấn vào dấu Power ( dấu sấm sét ấy) là tiếp tục kiếm tiền thôi. Mình sẻ không nói nhiều về vấn đề này nữa.

Thủ thuật tìm kiếm trên Google

Trong quá trình làm việc và giảng dạy của mình, tôi nhận thấy hầu hết các bạn đang còn thiếu một kỹ năng rất quan trọng trong thời đại thông tin số bùng nổ như hiện nay, đó chính là kỹ năng ứng dụng các thủ thuật tìm kiếm trên Google. Hiện nay Google đang chiếm khoảng ~85% lượng người dùng tìm kiếm thông tin trên toàn thế giới (*), có khoảng ~95% người dùng tìm kiếm trên Google ở Việt Nam (**). Những con số trên cho ta thấy sự bành trướng của gã khổng lồ Google, ta cũng không thể phủ nhận những tiện ích, thông tin mà Google mang lại cho chúng ta. – Nếu bạn là học sinh, sinh viên: hàng ngày bạn phải dùng Google để tìm kiếm thông tin phục vụ cho quá trình học tập của mình. – Nếu bạn là người đi làm: hàng ngày bạn dùng Google để tìm kiếm thông tin đối tác, khách hàng… – Nếu bạn là dân SEO: hàng ngày bạn dùng Google để tìm website để building link, tìm tài liệu… – Dù bạn là ai thì tôi chắc chắn bạn đã dùng Google để tìm kiếm thông tin (nhất là khi bạn đang đọc bài viết này bằng các tìm kiếm trên Google).

Tự học lập trình web bắt đầu từ đâu?

Lập trình web là ngành nghề xu hướng cực HOT trong thời đại số hiện nay. Vì thế theo thống kê ngày càng có nhiều người “lựa chọn” nghề để phát triển. Tuy nhiên với những người mới học thường khá bối rối,