`
zhangchibang
  • 浏览: 346210 次
社区版块
存档分类
最新评论

PHP源代码分析- tick(s)

    博客分类:
  • PHP
阅读更多

By Altair, http://www.phpinternals.com 转载请注明本信息

昨天有位朋友在杭州PHPer群里面贴出了下面的一段代码并给出了运行结果:
程序
<?php
function doTicks ()
{
    echo 'Ticks';
}
register_tick_function('doTicks');
declare(ticks = 1) {
    for ($x = 1; $x < 10; ++ $x) {
        echo $x * $x . '<br />';
    }
}
?>运行结果:
  1. 1
  2. TicksTicks4
  3. TicksTicks9
  4. TicksTicks16
  5. TicksTicks25
  6. TicksTicks36
  7. TicksTicks49
  8. TicksTicks64
  9. TicksTicks81
  10. TicksTicksTicksTicks
复制代码
他对运行结果感到疑惑,问了三个问题:
(1) 为什么先输出1之后才输出“Ticks”? 
(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:
[php] 
    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]很明显,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的三条语法规则:
  1. statement:
  2.   unticked_statement { zend_do_ticks(TSRMLS_C); }
  3. | ...
  4. ;
  5. function_declaration_statement:
  6.   unticked_function_declaration_statement { zend_do_ticks(TSRMLS_C); }
  7. ;
  8. class_declaration_statement:
  9.   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的语法定义:
  1. unticked_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(&$1 TSRMLS_CC); }
  4. ;
复制代码
可以知道function_declaration_statement是完整的函数声明,也就是说,一个函数的定义包括完整的函数原形及函数体算一条function_declaration_statement。
同样从unticked_class_declaration_statement的语法定义:
  1. unticked_class_declaration_statement:
  2.   class_entry_type T_STRING extends_from
  3.    { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); }
  4.    implements_list
  5.    '{'
  6.     class_statement_list
  7.    '}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); }
  8. | interface_entry T_STRING
  9.    { 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,它的核心是下面的定义:
  1. unticked_statement:
  2.   '{' inner_statement_list '}'
  3. | 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); }
  4. | 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); }
  5. | 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); }
  6. | 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); }
  7. | T_FOR
  8.    '('
  9.     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, &$7 TSRMLS_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包括:
根据上面的定义,我们知道,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 
后面找到一些有趣的tick的应用

 

http://bbs.phpchina.com/viewthread.php?tid=94534

分享到:
评论

相关推荐

    C-tick_ATS01.pdf

    C-tick_ATS01pdf,C-tick_ATS01

    前端开源库-call-next-tick

    在前端开发中,异步处理是至关重要的,它使得我们可以执行一些非阻塞操作,比如网络请求、定时任务...通过阅读 `call-next-tick-master` 压缩包中的源代码和文档,你可以更深入地了解这个库的工作细节和更多使用技巧。

    linux内核源代码分析-定时器与时间管理.ppt

    【Linux内核源代码分析-定时器与时间管理】 在Linux内核中,时间管理和定时器扮演着至关重要的角色。时间管理涉及到系统如何跟踪和处理时间事件,而定时器则是这些事件的基础。以下是对相关知识点的详细说明: 1. ...

    Telemecanique certificate c-tick.pdf

    Telemecanique certificate c-tickpdf,Telemecanique certificate c-tick

    施耐德变频器C-Tick-ATV61/ATV71产品规格.pdf

    施耐德变频器C-Tick-ATV61/ATV71产品规格pdf,施耐德变频器C-Tick-ATV61/ATV71说明书

    system-tick-delay20230306-190137.7z

    "system-tick-delay20230306-190137.7z"这个压缩包文件很可能包含了一套关于如何使用SystemTick来实现精确延迟的方案,特别地,这是SYDTEK公司的一种改良版方法。SystemTick通常是指嵌入式系统中的一种硬件定时器,...

    C-Tick ATV21 for 400V H and W(英文).pdf

    C-Tick ATV21 for 400V H and W(英文)pdf,C-Tick ATV21 for 400V H and W(英文)

    node-tick-processor:易于安装的 v8 分析器日志处理器

    这只不过是对 v8 源代码附带的“tick 处理器”的重新打包。 安装 $ npm install -g node-tick-processor 用法 在启用分析的情况下运行脚本,以生成v8.log文件$ node --prof myscript.js 在同一个目录下,运行这个...

    tick指标 - MetaTrader 4脚本.zip

    总结来说,"tick指标 - MetaTrader 4脚本.zip"提供了记录和观察tick数据的工具,对于交易者理解和分析市场行为具有实际价值。通过深入理解并正确使用"okwhticks.mq4"脚本,交易者可以提升他们的市场洞察力和交易策略...

    devexpress c#源代码-增加-删除-修改-查询类集成-含图片

    - `timer1_Tick`事件处理程序虽然没有完整的代码,但通常会用作周期性任务,例如每隔1秒执行一次某些操作,例如更新UI或执行数据库查询。 5. **数据库连接管理**: - 在查询后,代码尝试关闭数据库连接,这是良好...

    SYD8811-SystemTick-Source Code

    【SYD8811-SystemTick-Source Code】是一个针对SYD8811微控制器的源代码库,主要用于实现System Tick功能。...通过理解和分析源代码,开发者可以更好地掌握SYD8811的中断系统以及如何利用它来实现复杂的实时任务。

    ucos源代码分析,有注释

    《UCOS源代码分析——单片机上的操作系统实践》 UCOS,全称为 μC/OS,是一款轻量级实时操作系统(RTOS),专为嵌入式系统设计,尤其适合资源有限的微控制器。其源代码公开,允许用户进行深度定制和移植,这也是它...

    Project---43-tick-tok-

    在"Project---43-tick-tok-"中,我们可以推测这是一个用p5.play构建的计时器或者定时器项目,"tick-tok"可能代表时间流逝的声音或概念。在JavaScript中,这样的功能通常涉及定时器(setTimeout或setInterval)和时间...

    mongodb-tick-test:MongoDB Tick 数据测试

    MongoDB-tick-test 模块就是为了评估 MongoDB 处理这种高频率、实时数据流的能力而设计的。 这个测试主要关注的是 MongoDB 在处理时间序列数据时的性能,尤其是当数据以“tick”的形式快速涌入时。时间序列数据是按...

    react-tick-tac-toe游戏:React-tick-tac-toe游戏

    React Tick-Tac-Toe游戏执照即将来临!描述一个简单的井字游戏先决条件设置您的壁虱游戏开发环境如果您尚未安装Node,请从自制软件或使用Node官方网站Nodejs.org下载版本10.16.2。 一旦安装了Node和NPM。 您应该通过...

    C43-Tick-Tock

    《C43-Tick-Tock:深入理解p5.play样板及其JavaScript实现》 在IT行业中,游戏开发是一项充满挑战和创新的工作,而JavaScript作为一种广泛应用于Web前端开发的编程语言,其在游戏领域的应用也越来越广泛。"C43-Tick...

    PRO-C43-TICK-TOCK

    在IT行业中,"PRO-C43-TICK-TOCK"这个标题可能代表一个特定的项目、代码库或者是一个编程概念的实例。从描述来看,它指的是"p5.play样板"和"p5.play的锅炉板"。这表明我们正在讨论的是使用p5.play库的一个示例或基础...

    C语言-源代码-C-界面编程小结.doc

    // "开始"中的初步处理代码 ... timeDisplay[0] = (int)timeSum; timeDisplay[1] = (int)(60 * (timeSum - timeDisplay[0])); timeDisplay[2] = (int)(60 * (60 * (timeSum - timeDisplay[0]) - timeDisplay[1])); ...

    TdxHqApi 行情测速-源代码.zip(代码+工具2个包)

    本文将深入探讨使用TdxHqApi.dll和TradeX.dll进行全市场股票行情获取的速度测试,并通过源代码分析,揭示其内在的工作机制。 首先,TdxHqApi的核心功能在于提供快速、稳定的数据接口,使得开发者能够便捷地获取沪深...

    上期CTP_API_C++可实盘的源代码(更新).rar

    上期CTP API C++ 源代码 单合约版 下载文件名AutoTrader_ctp_c++源代码.rar 填入经纪公司代码,实盘帐号,密码即可完成行情接收,指标计算,实盘下单连续开平仓。 功能简要介绍如下: 自动保存订阅合约TICK数据到\...

Global site tag (gtag.js) - Google Analytics