php控制结构语句declare中的tick的详解[整理版]

来源:互联网 发布:如何选笔记本电脑 知乎 编辑:程序博客网 时间:2024/06/03 20:34

先看看手册是怎么说的:

declare 结构用来设定一段代码的执行指令。declare 的语法和其它流程控制结构相似:

1declare (directive)
2    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 中可以出现多个事件。

看完手册还是觉得云里雾里,再看看别人是怎么描述:


根据代码解析:

01<?php
02function doTicks ()
03{
04    echo 'Ticks';
05}
06register_tick_function('doTicks');
07declare(ticks = 1) {
08    for ($x = 1; $x < 10; ++ $x) {
09        echo $x $x '<br />';
10    }
11}
12?>

运算结果:

011
02TicksTicks4
03TicksTicks9
04TicksTicks16
05TicksTicks25
06TicksTicks36
07TicksTicks49
08TicksTicks64
09TicksTicks81
10TicksTicksTicksTicks

产生三个疑问:


(1)为什么先输出1之后才输出“Ticks”? 

(2)为什么在输出81后还输出四个Ticks ? 

(3)declare中的for循环怎么分解成低级语句(low-level)?

这是每个初次接触ticks的人都会碰到的问题。首先register_tick_function函数定义了每个tick事件发生时的处理函数。那么什么是tick事件呢?先看手册上对ticks的解释:

1A 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.
2The 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:

011: <?php
02        0  NOP                
032:
043: function doTicks ()
054: {
065:     echo 'Ticks';
07        0  ECHO                'Ticks'
086: }
09        1  RETURN              null
107: register_tick_function('doTicks');
11        1  SEND_VAL            'doTicks'
12        2  DO_FCALL            'register_tick_function' [extval:1]
138: declare(ticks = 1) {
149:     for ($x = 1; $x < 10; ++ $x) {
15        3  ASSIGN              !0, 1
16        4  IS_SMALLER          !0, 10 =>RES[~2]     
17        5  JMPZNZ              ~2, ->14 [extval:8]
18        6  PRE_INC             !0
19        7  JMP                 ->4
2010:         echo $x $x '<br />';
21        8  MUL                 !0, !0 =>RES[~4]     
22        9  CONCAT              ~4, '<br />' =>RES[~5]     
23       10  ECHO                ~5
24       11  TICKS               1 =>RES[]       
2511:     }
26       12  TICKS               1 =>RES[]       
27       13  JMP                 ->6
28       14  TICKS               1 =>RES[]       
2912: }
30       15  TICKS               1 =>RES[]       
31       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的三条语法规则:

01statement:
02  unticked_statement { zend_do_ticks(TSRMLS_C); }
03| ...
04;
05function_declaration_statement:
06  unticked_function_declaration_statement { zend_do_ticks(TSRMLS_C); }
07;
08class_declaration_statement:
09  unticked_class_declaration_statement { zend_do_ticks(TSRMLS_C); }
10;

    也就是说,PHP编译会在statement(语句), function_declaration_statement(函数定义语句), class_declaration_statement后插入TICKS处理函数,即它会在每条statement,函数声明,类(实际上还包括接口)声明后插入一条TICKS指令。

    函数与类声明语句比较好理解,根据unticked_function_declaration_statement的语法定义:

1unticked_function_declaration_statement:
2  function is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); }
3   '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1TSRMLS_CC); }
4;

    可以知道function_declaration_statement是完整的函数声明,也就是说,一个函数的定义包括完整的函数原形及函数体算一条function_declaration_statement。 

    同样从unticked_class_declaration_statement的语法定义:

01unticked_class_declaration_statement:
02  class_entry_type T_STRING extends_from
03   { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); }
04   implements_list
05   '{'
06    class_statement_list
07   '}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); }
08| interface_entry T_STRING
09   { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); }
10   interface_extends_list
11   '{'
12    class_statement_list
13   '}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); }
14;

    也可以知道,完整的class或interface定义算是一个class_declration_statement。

    最复杂的是statement,它的核心是下面的定义:

01unticked_statement:
02  '{' inner_statement_list '}'
03| 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); }
04| 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); }
05| 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, &$5TSRMLS_CC); }
06| 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, &$7TSRMLS_CC); }
07| T_FOR
08   '('
09    for_expr
10   ';' { zend_do_free(&$3 TSRMLS_CC); $4.u.opline_num = get_next_op_number(CG(active_op_array)); }
11    for_expr
12   ';' { zend_do_extended_info(TSRMLS_C); zend_do_for_cond(&$6, &$7 TSRMLS_CC); }
13    for_expr
14   ')' { zend_do_free(&$9 TSRMLS_CC); zend_do_for_before_statement(&$4, &$7 TSRMLS_CC); }
15   for_statement { zend_do_for_end(&$7 TSRMLS_CC); }
16| T_SWITCH '(' expr ')' { zend_do_switch_cond(&$3 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$6 TSRMLS_CC); }
17| T_BREAK ';'    { zend_do_brk_cont(ZEND_BRK, NULL TSRMLS_CC); }
18| T_BREAK expr ';'  { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); }
19| T_CONTINUE ';'   { zend_do_brk_cont(ZEND_CONT, NULL TSRMLS_CC); }
20| T_CONTINUE expr ';'  { zend_do_brk_cont(ZEND_CONT, &$2 TSRMLS_CC); }
21| T_RETURN ';'      { zend_do_return(NULL, 0 TSRMLS_CC); }
22| T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); }
23| T_RETURN variable ';'    { zend_do_return(&$2, 1 TSRMLS_CC); }
24| T_GLOBAL global_var_list ';'
25| T_STATIC static_var_list ';'
26| T_ECHO echo_expr_list ';'
27| T_INLINE_HTML   { zend_do_echo(&$1 TSRMLS_CC); }
28| expr ';'    { zend_do_free(&$1 TSRMLS_CC); }
29| T_UNSET '(' unset_variables ')' ';'
30| T_FOREACH '(' variable T_AS
31  { zend_do_foreach_begin(&$1, &$2, &$3, &$4, 1 TSRMLS_CC); }
32  foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7TSRMLS_CC); }
33  foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); }
34| T_FOREACH '(' expr_without_variable T_AS
35  { zend_do_foreach_begin(&$1, &$2, &$3, &$4, 0 TSRMLS_CC); }
36  variable foreach_optional_arg ')' { zend_check_writable_variable(&$6); zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); }
37  foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); }
38| 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); }
39';'  /* empty statement */
40| T_TRY { zend_do_try(&$1 TSRMLS_CC); } '{' inner_statement_list '}'
41  T_CATCH '(' { zend_initialize_try_catch_element(&$1 TSRMLS_CC); }
42  fully_qualified_class_name { zend_do_first_catch(&$7 TSRMLS_CC); }
43  T_VARIABLE ')' { zend_do_begin_catch(&$1, &$9, &$11, &$7 TSRMLS_CC); }
44  '{' inner_statement_list '}' { zend_do_end_catch(&$1 TSRMLS_CC); }
45  additional_catches { zend_do_mark_last_catch(&$7, &$18 TSRMLS_CC); }
46| T_THROW expr ';' { zend_do_throw(&$2 TSRMLS_CC); }
47| T_GOTO T_STRING ';' { zend_do_goto(&$2 TSRMLS_CC); }
48;

根据上面的定义,我们知道,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操作等等。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 11个月的宝宝大便干燥怎么办 1岁宝宝又拉又吐怎么办 怀孕八个月了不想要了怎么办 奶水不够宝宝又不喝奶粉怎么办 手机恢复出厂设置密码忘了怎么办 5s锁屏密码忘了怎么办 深圳房子卖了户口没地方迁怎么办 宝马1系130i烧机怎么办 小孩流清鼻涕怎么办最简单方法 孕3个月胎盘低置怎么办 孩子判逆不听家长的话该怎么办 香港购物超5000被海关扣怎么办 浅色衣服被深色衣服染色了怎么办 金立手机微信不能发语音怎么办 吃鸡买的账号密码邮箱忘记了怎么办 氩弧焊枪管带里进水了怎么办 绝地求生穿头盔的时候连衣帽怎么办 开车不小心把光缆线给挂断了怎么办 脚刺到了生锈钢钉没打针怎么办 一加3t背壳螺丝掉了怎么办 30万美金美金中国被扣怎么办 电脑使用迅雷变的很卡怎么办 优盘拷贝过程中失去优盘路径怎么办 用百度云上传视频文件太慢了怎么办 网易云音乐云盘电脑上传很慢怎么办 路由器的宽带账号密码忘记了怎么办 蚂蚁邦路由器管理密码忘记了怎么办 红米2a刷机失败怎么办 小米手机开机图案锁忘记了怎么办 小米6进水无限闪屏开机重启怎么办 红米手机一直卡在开机画面怎么办 红米4卡在开机画面怎么办 红米手机一直在开机画面怎么办 红米手机一直跳开机画面怎么办 红米note3锁屏密码忘记怎么办 红米手机忘记锁屏密码怎么办 红米4锁屏密码忘了怎么办 红米note忘记锁屏密码怎么办 红米note2锁屏密码忘了怎么办 机打发票抬头名字少写一个字怎么办 卷式发票名字写错了怎么办