先看看手冊是怎麼說的:
declare 結構用來設定一段代碼的執行指令。declare 的語法和其它流程控制結構類似:php
declare (directive) statement
directive 部分容許設定 declare 代碼段的行爲。目前只認識兩個指令:ticks(更多信息見下面 ticks 指令)以及 encoding(更多信息見下面 encoding指令)。
Note: 函數
ticks 指令在 PHP 5.3.0 中是過期指令,將會從 PHP 6.0.0 移除。性能
encoding 是 PHP 5.3.0 新增指令。測試
Tick 是一個在 declare 代碼段中解釋器每執行 N 條低級語句就會發生的事件。N 的值是在 declare 中的 directive 部分用 ticks=N
來指定的。 網站
在每一個 tick 中出現的事件是由 register_tick_function() 來指定的。更多細節見下面的例子。注意每一個 tick 中能夠出現多個事件。
看完手冊仍是以爲雲裏霧裏,再看看別人是怎麼描述:spa
根據代碼解析:
.net
<?php function doTicks () { echo 'Ticks'; } register_tick_function('doTicks'); declare(ticks = 1) { for ($x = 1; $x < 10; ++ $x) { echo $x * $x . '<br />'; } } ?>
運算結果:
調試
1 TicksTicks4 TicksTicks9 TicksTicks16 TicksTicks25 TicksTicks36 TicksTicks49 TicksTicks64 TicksTicks81 TicksTicksTicksTicks
產生三個疑問:code
(1)爲何先輸出1以後才輸出「Ticks」? orm
(2)爲何在輸出81後還輸出四個Ticks ?
(3)declare中的for循環怎麼分解成低級語句(low-level)?
這是每一個初次接觸ticks的人都會碰到的問題。首先register_tick_function函數定義了每一個tick事件發生時的處理函數。那麼什麼是tick事件呢?先看手冊上對ticks的解釋:
A tick is an event that occurs for every N low-level statements executed by the parser within the declare block. The value for N is specified using ticks=N within the declare blocks's directive section. The event(s) that occur on each tick are specified using the register_tick_function().
這個解釋有三層意思:
(1) tick是一個事件。
(2) tick事件在PHP每執行N條低級語句就發生一次,N由declare語句指定。
(3)能夠用register_tick_function()來指定tick事件發生時應該執行的操做。
很明顯,理解上面的輸出結果最關鍵的是瞭解什麼是低級語句(low-level statements),它又是如何進行計數的。咱們首先仍是將上面的程序經過OPDUMP編譯成OPCODEs:
1: <?php 0 NOP 2: 3: function doTicks () 4: { 5: echo 'Ticks'; 0 ECHO 'Ticks' 6: } 1 RETURN null 7: register_tick_function('doTicks'); 1 SEND_VAL 'doTicks' 2 DO_FCALL 'register_tick_function' [extval:1] 8: declare(ticks = 1) { 9: for ($x = 1; $x < 10; ++ $x) { 3 ASSIGN !0, 1 4 IS_SMALLER !0, 10 =>RES[~2] 5 JMPZNZ ~2, ->14 [extval:8] 6 PRE_INC !0 7 JMP ->4 10: echo $x * $x . '<br />'; 8 MUL !0, !0 =>RES[~4] 9 CONCAT ~4, '<br />' =>RES[~5] 10 ECHO ~5 11 TICKS 1 =>RES[] 11: } 12 TICKS 1 =>RES[] 13 JMP ->6 14 TICKS 1 =>RES[] 12: } 15 TICKS 1 =>RES[] 16 RETURN 1
很明顯,PHP的編譯過程已經在編譯後每條語句的OPCODE序列中插入了TICKS指令用於處理tick事件。那麼這些TICKS是根據什麼規則來插入的呢?
咱們仍是從PHP Zend Engine的源代碼中尋找答案。
經過簡單的文本搜索咱們能夠知道生成ZEND_TICKS指令的惟一函數是zend_do_ticks(該函數的實如今zend_compile.c中)。
如今再從PHP的語法分析文件zend_language_parser.y(PHP使用bison來作語法分析,全部的語法規則均定義在zend_language_parser.y中)中尋找調用zend_do_ticks的地方。
再一次使用簡單的文本搜索,咱們能夠獲得調用zend_do_ticks的三條語法規則:
statement: unticked_statement { zend_do_ticks(TSRMLS_C); } | ... ; function_declaration_statement: unticked_function_declaration_statement { zend_do_ticks(TSRMLS_C); } ; class_declaration_statement: unticked_class_declaration_statement { zend_do_ticks(TSRMLS_C); } ;
也就是說,PHP編譯會在statement(語句), function_declaration_statement(函數定義語句), class_declaration_statement後插入TICKS處理函數,即它會在每條statement,函數聲明,類(實際上還包括接口)聲明後插入一條TICKS指令。
函數與類聲明語句比較好理解,根據unticked_function_declaration_statement的語法定義:
unticked_function_declaration_statement: function is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); } '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } ;
能夠知道function_declaration_statement是完整的函數聲明,也就是說,一個函數的定義包括完整的函數原形及函數體算一條function_declaration_statement。
一樣從unticked_class_declaration_statement的語法定義:
unticked_class_declaration_statement: class_entry_type T_STRING extends_from { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } implements_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); } | interface_entry T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } interface_extends_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); } ;
也能夠知道,完整的class或interface定義算是一個class_declration_statement。
最複雜的是statement,它的核心是下面的定義:
unticked_statement: '{' inner_statement_list '}' | T_IF '(' expr ')' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } statement { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } elseif_list else_single { zend_do_if_end(TSRMLS_C); } | T_IF '(' expr ')' ':' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } new_elseif_list new_else_single T_ENDIF ';' { zend_do_if_end(TSRMLS_C); } | T_WHILE '(' { $1.u.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' { zend_do_while_cond(&$4, &$5 TSRMLS_CC); } while_statement { zend_do_while_end(&$1, &$5 TSRMLS_CC); } | T_DO { $1.u.opline_num = get_next_op_number(CG(active_op_array)); zend_do_do_while_begin(TSRMLS_C); } statement T_WHILE '(' { $5.u.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' ';' { zend_do_do_while_end(&$1, &$5, &$7 TSRMLS_CC); } | T_FOR '(' for_expr ';' { zend_do_free(&$3 TSRMLS_CC); $4.u.opline_num = get_next_op_number(CG(active_op_array)); } for_expr ';' { zend_do_extended_info(TSRMLS_C); zend_do_for_cond(&$6, &$7 TSRMLS_CC); } for_expr ')' { zend_do_free(&$9 TSRMLS_CC); zend_do_for_before_statement(&$4, &$7 TSRMLS_CC); } for_statement { zend_do_for_end(&$7 TSRMLS_CC); } | T_SWITCH '(' expr ')' { zend_do_switch_cond(&$3 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$6 TSRMLS_CC); } | T_BREAK ';' { zend_do_brk_cont(ZEND_BRK, NULL TSRMLS_CC); } | T_BREAK expr ';' { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); } | T_CONTINUE ';' { zend_do_brk_cont(ZEND_CONT, NULL TSRMLS_CC); } | T_CONTINUE expr ';' { zend_do_brk_cont(ZEND_CONT, &$2 TSRMLS_CC); } | T_RETURN ';' { zend_do_return(NULL, 0 TSRMLS_CC); } | T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); } | T_RETURN variable ';' { zend_do_return(&$2, 1 TSRMLS_CC); } | T_GLOBAL global_var_list ';' | T_STATIC static_var_list ';' | T_ECHO echo_expr_list ';' | T_INLINE_HTML { zend_do_echo(&$1 TSRMLS_CC); } | expr ';' { zend_do_free(&$1 TSRMLS_CC); } | T_UNSET '(' unset_variables ')' ';' | T_FOREACH '(' variable T_AS { zend_do_foreach_begin(&$1, &$2, &$3, &$4, 1 TSRMLS_CC); } foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); } | T_FOREACH '(' expr_without_variable T_AS { zend_do_foreach_begin(&$1, &$2, &$3, &$4, 0 TSRMLS_CC); } variable foreach_optional_arg ')' { zend_check_writable_variable(&$6); zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); } | T_DECLARE { $1.u.opline_num = get_next_op_number(CG(active_op_array)); zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement { zend_do_declare_end(&$1 TSRMLS_CC); } | ';' /* empty statement */ | T_TRY { zend_do_try(&$1 TSRMLS_CC); } '{' inner_statement_list '}' T_CATCH '(' { zend_initialize_try_catch_element(&$1 TSRMLS_CC); } fully_qualified_class_name { zend_do_first_catch(&$7 TSRMLS_CC); } T_VARIABLE ')' { zend_do_begin_catch(&$1, &$9, &$11, &$7 TSRMLS_CC); } '{' inner_statement_list '}' { zend_do_end_catch(&$1 TSRMLS_CC); } additional_catches { zend_do_mark_last_catch(&$7, &$18 TSRMLS_CC); } | T_THROW expr ';' { zend_do_throw(&$2 TSRMLS_CC); } | T_GOTO T_STRING ';' { zend_do_goto(&$2 TSRMLS_CC); } ;
根據上面的定義,咱們知道,statement包括:
(1) 簡單語句:空語句(就一個;號),return,break,continue,throw, goto,global,static,unset,echo, 內置的HTML文本,分號結束的表達式等均算一個語句。
(2) 複合語句:完整的if/elseif,while,do...while,for,foreach,switch,try...catch等算一個語句。
(3) 語句塊:{} 括出來的語句塊。
(4) 最後特別的:declare塊自己也算一個語句(按道理declare塊也算是複合語句,但此處特地將其獨立出來)。
全部的statement, function_declare_statement, class_declare_statement就構成了所謂的低級語句(low-level statement)。
如今再來看開始的例子就比較好理解了:
首先完整的for循環算一個語句,但必需要等循環結束纔算,所以在編譯時for循環裏面的echo 算第一個語句。
因此第一個doTicks是在第一個echo後執行的,也就是1輸出後才發生第一個tick事件。
在$x 從1到9的循環中,每一個循環包括兩個語句,一個echo, 一個for循環。在81輸出後,由於echo是一條語句,所以輸出第一個ticks。
同時$x=9的這個for循環也結束了,這又是一條語句,輸出第二個ticks;開始$x=10的循環,但這時已不知足循環條件,for循環執行結束,這個循環又是一個語句,這時輸出第三個ticks。
最後declare自己也算一條語句,因此又輸出第四個ticks。
說了半天,ticks到底有什麼用?實際上可用tick來進行調試,性能測試,實現簡單的多任務,或者作一些後臺的I/O操做等等。
更多例子能夠查看手冊或者官方網站http://www.php.net/manual/en/control-structures.declare.php