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

本文详细解析PHP中的ticks指令与低级语句概念,通过代码实例揭示ticks事件触发机制及其在PHP编译过程中的应用。深入探讨ticks事件如何在每次执行特定数量的低级语句后触发,并通过register_tick_function()函数自定义事件处理函数。文章进一步解释了低级语句的定义及其在PHP语法结构中的分布,以帮助开发者理解ticks事件的触发规则与实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看看手册是怎么说的:

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

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

运算结果:

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

产生三个疑问:


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

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

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

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

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

01 1: <?php
02         0  NOP                
03 2:
04 3: function doTicks ()
05 4: {
06 5:     echo 'Ticks';
07         0  ECHO                'Ticks'
08 6: }
09         1  RETURN              null
10 7: register_tick_function('doTicks');
11         1  SEND_VAL            'doTicks'
12         2  DO_FCALL            'register_tick_function' [extval:1]
13 8: declare(ticks = 1) {
14 9:     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
20 10:         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[]       
25 11:     }
26        12  TICKS               1 =>RES[]       
27        13  JMP                 ->6
28        14  TICKS               1 =>RES[]       
29 12: }
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的三条语法规则:

01 statement:
02   unticked_statement { zend_do_ticks(TSRMLS_C); }
03 | ...
04 ;
05 function_declaration_statement:
06   unticked_function_declaration_statement { zend_do_ticks(TSRMLS_C); }
07 ;
08 class_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的语法定义:

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(&$1TSRMLS_CC); }
4 ;

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

    同样从unticked_class_declaration_statement的语法定义:

01 unticked_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,它的核心是下面的定义:

01 unticked_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操作等等。

标题基于SpringBoot+Vue的学生交流互助平台研究AI更换标题第1章引言介绍学生交流互助平台的研究背景、意义、现状、方法与创新点。1.1研究背景与意义分析学生交流互助平台在当前教育环境下的需求及其重要性。1.2国内外研究现状综述国内外在学生交流互助平台方面的研究进展与实践应用。1.3研究方法与创新点概述本研究采用的方法论、技术路线及预期的创新成果。第2章相关理论阐述SpringBoot与Vue框架的理论基础及在学生交流互助平台中的应用。2.1SpringBoot框架概述介绍SpringBoot框架的核心思想、特点及优势。2.2Vue框架概述阐述Vue框架的基本原理、组件化开发思想及与前端的交互机制。2.3SpringBoot与Vue的整合应用探讨SpringBoot与Vue在学生交流互助平台中的整合方式及优势。第3章平台需求分析深入分析学生交流互助平台的功能需求、非功能需求及用户体验要求。3.1功能需求分析详细阐述平台的各项功能需求,如用户管理、信息交流、互助学习等。3.2非功能需求分析对平台的性能、安全性、可扩展性等非功能需求进行分析。3.3用户体验要求从用户角度出发,提出平台在易用性、美观性等方面的要求。第4章平台设计与实现具体描述学生交流互助平台的架构设计、功能实现及前后端交互细节。4.1平台架构设计给出平台的整体架构设计,包括前后端分离、微服务架构等思想的应用。4.2功能模块实现详细阐述各个功能模块的实现过程,如用户登录注册、信息发布与查看、在线交流等。4.3前后端交互细节介绍前后端数据交互的方式、接口设计及数据传输过程中的安全问题。第5章平台测试与优化对平台进行全面的测试,发现并解决潜在问题,同时进行优化以提高性能。5.1测试环境与方案介绍测试环境的搭建及所采用的测试方案,包括单元测试、集成测试等。5.2测试结果分析对测试结果进行详细分析,找出问题的根源并
内容概要:本文详细介绍了一个基于灰狼优化算法(GWO)优化的卷积双向长短期记忆神经网络(CNN-BiLSTM)融合注意力机制的多变量多步时间序列预测项目。该项目旨在解决传统时序预测方法难以捕捉非线性、复杂时序依赖关系的问题,通过融合CNN的空间特征提取、BiLSTM的时序建模能力及注意力机制的动态权重调节能力,实现对多变量多步时间序列的精准预测。项目不仅涵盖了数据预处理、模型构建与训练、性能评估,还包括了GUI界面的设计与实现。此外,文章还讨论了模型的部署、应用领域及其未来改进方向。 适合人群:具备一定编程基础,特别是对深度学习、时间序列预测及优化算法有一定了解的研发人员和数据科学家。 使用场景及目标:①用于智能电网负荷预测、金融市场多资产价格预测、环境气象多参数预报、智能制造设备状态监测与预测维护、交通流量预测与智慧交通管理、医疗健康多指标预测等领域;②提升多变量多步时间序列预测精度,优化资源调度和风险管控;③实现自动化超参数优化,降低人工调参成本,提高模型训练效率;④增强模型对复杂时序数据特征的学习能力,促进智能决策支持应用。 阅读建议:此资源不仅提供了详细的代码实现和模型架构解析,还深入探讨了模型优化和实际应用中的挑战与解决方案。因此,在学习过程中,建议结合理论与实践,逐步理解各个模块的功能和实现细节,并尝试在自己的项目中应用这些技术和方法。同时,注意数据预处理的重要性,合理设置模型参数与网络结构,控制多步预测误差传播,防范过拟合,规划计算资源与训练时间,关注模型的可解释性和透明度,以及持续更新与迭代模型,以适应数据分布的变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值