# 附录\*PHP的ticks机制

PHP提供declare关键字和ticks关键字来声明ticks机制。如：declare(ticks = N); 这表示：在当前scope内，每执行N句internal statements（opcodes），就会中断当前的业务语句，去执行通过register\_tick\_function注册的函数（如果存在的话），然后再继续之前的代码。需要注意的是这里的N是指的PHP的一些OPCODE，而OPCODE与我们见到的PHP语句却不是一一对应的。

实例1：

```php
    $name = "phppan";
    echo $name;
    class Tipi {
        public function test() {
            echo "test";
        }
    }
    function f_tipi() {
    }
```

如上代码包括了我们常见的几种语句，赋值，输出，定义类，定义函数。通常我们用VLD查看PHP生成的中间代码，上面的代码通过**php -dvld.active=1 t.php**我们会看到 ECHO、ASSIGN、NOP等中间代码，如下所示：

```
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  (null)
number of ops:  7
compiled vars:  !0 = $name
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   EXT_STMT                                                 
         1        ASSIGN                                                   !0, 'phppan'
   3     2        EXT_STMT                                                 
         3        ECHO                                                     !0
   4     4        EXT_STMT                                                 
   9     5        EXT_STMT                                                 
  10     6      > RETURN                                                   1

branch: #  0; line:     2-   10; sop:     0; eop:     6; out1:  -2
path #1: 0, 
Function f_tipi:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  f_tipi
number of ops:  3
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   9     0  E >   EXT_NOP                                                  
  10     1        EXT_STMT                                                 
         2      > RETURN                                                   null

branch: #  0; line:     9-   10; sop:     0; eop:     2; out1:  -2
path #1: 0, 
End of function f_tipi

Class Tipi:
Function test:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  test
number of ops:  5
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   5     0  E >   EXT_NOP                                                  
   6     1        EXT_STMT                                                 
         2        ECHO                                                     'test'
   7     3        EXT_STMT                                                 
         4      > RETURN                                                   null

branch: #  0; line:     5-    7; sop:     0; eop:     4; out1:  -2
path #1: 0, 
End of function test

End of class Tipi.

phppan
```

现在我们在示例1的代码上添加上ticks机制。如PHP代码示例2：

```php
    declare(ticks=1);
    $name = "phppan";
    echo $name;
    class Tipi {
        public function test() {
            echo "test";
        }
    }
    function f_tipi() {
    }
```

示例2与示例1相比也就是多了第一条语句： declare(ticks=1); 如果我们此时再次通过VLD查看中间代码，会发现在每个中间代码的后面都多了一句中间代码：**TICKS**。

```
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  (null)
number of ops:  13
compiled vars:  !0 = $name
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   EXT_STMT                                                 
         1        TICKS                                                    
   3     2        EXT_STMT                                                 
         3        ASSIGN                                                   !0, 'phppan'
         4        TICKS                                                    
   4     5        EXT_STMT                                                 
         6        ECHO                                                     !0
         7        TICKS                                                    
   5     8        EXT_STMT                                                 
         9        TICKS                                                    
  10    10        EXT_STMT                                                 
  11    11        TICKS                                                    
        12      > RETURN                                                   1

branch: #  0; line:     2-   11; sop:     0; eop:    12; out1:  -2
path #1: 0, 
Function f_tipi:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  f_tipi
number of ops:  3
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  10     0  E >   EXT_NOP                                                  
  11     1        EXT_STMT                                                 
         2      > RETURN                                                   null

branch: #  0; line:    10-   11; sop:     0; eop:     2; out1:  -2
path #1: 0, 
End of function f_tipi

Class Tipi:
Function test:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  test
number of ops:  6
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   6     0  E >   EXT_NOP                                                  
   7     1        EXT_STMT                                                 
         2        ECHO                                                     'test'
         3        TICKS                                                    
   8     4        EXT_STMT                                                 
         5      > RETURN                                                   null

branch: #  0; line:     6-    8; sop:     0; eop:     5; out1:  -2
path #1: 0, 
End of function test

End of class Tipi.

phppan
```

是否因为ticks=1的原因而在每个中间代码的后面添加了TICKS？将declare(ticks=1);换成declare(ticks=100);，再次VLD，结果没有变化。从以上的结果可以看出，PHP内核在语法分析过程中实现了ticks机制。

## 声明ticks机制过程

声明的过程就是调用declare(ticks = N); 在语法分析时根据declare关键字和参数中的ticks关键字来声明ticks机制。通过zend\_compile.c文件中的zend\_do\_declare\_begin、declare\_statement、zend\_do\_declare\_end三个函数来编译声明ticks机制。在declare\_statement函数中我们可以看到：declare除了可以声明ticks之外，还可以声明encoding，这在代码里面就是一个if else的判断。

ticks机制的声明仅在编译过程有用，它为后面的声明控制语句服务。其编译过程中的全局变量为：CG(declarables)。这是一个结构体，它仅有一个成员：ticks。当然后面应该还会有更多的成员出现。

## 声明控制语句

示例1和示例2已经充分说明在每条语句的语法分析时，会根据是否声明了ticks机制来添加TICKS中间代码，其实现在于每条语句在语法解析时都会添加一条函数调用：zend\_do\_ticks。从zend\_language\_parser.y文件中可以看出：zend\_do\_ticks函数添加在类定义语句，函数定义语句和常规语句的后面。 zend\_compile.c文件中的zend\_do\_ticks函数会根据前面提到的 CG(declarables).ticks 来判断是否生成 ZEND\_TICKS 中间代码（在VLD中看到的中间代码都是没有ZEND开头）。

除了声明ticks机制，还有执行。执行过程中关键的变量是在声明时的ticks=N。其实这里的N可以换个角度去理解：ticks指定的数字是指执行了多少次TICKS语句。在TICKS中间代码的执行函数ZEND\_TICKS\_SPEC\_CONST\_HANDLER中，会统计执行当前函数的次数，存储变量为EG(ticks\_count)。当达到当初声明的界限，就会调用一次所有通过register\_tick\_function注册的函数，并计数清零。

与当初自己设想的实现相比，PHP内核对ticks机制的实现满足了功能单一原则和松耦合原则。将ticks机制作为一个中间代码添加到整个中间代码的执行体系中，包括状态的转移，函数的切换这些都是直接使用原有的机制。

## ticks机制的应用场景

手册上说：Ticks 很适合用来做调试，以及实现简单的多任务，后台 I/O 和很多其它任务。

在调试过程中，对于定位一段特定代码中速度慢的语句比较有用，我们可以每执行两条低级语句就记录一次时间。虽然这个过程也可以用其它方法完成，但用 tick 更方便也更容易实现。

PCNTL也使用ticks机制来作为信号处理机制（signal handle callback mechanism），可以最小程度地降低处理异步事件时的负载。这里的关键在于PCNTL扩展的模块初始化函数（PHP\_MINIT\_FUNCTION(pcntl)）。在此模块做模块初始化时，它会调用： php\_add\_tick\_function(pcntl\_signal\_dispatch);将pcntl的分发执行函数添加到ticks机制的调用函数中去，从而当ticks触发时就会调用PCNTL扩展函数中指定的所有方法。

## 资料

[PHP的ticks机制](http://www.codesky.net/article/201306/181874.html)

[php手册里，pcntl\_signal函数解释，很多例子前面declare(ticks=1)这句话的意义](http://blog.chinaunix.net/uid-26363964-id-3938401.html)

[你知道PHP信号处理的正确打开方式吗？](http://www.jianshu.com/p/c3fb535ecd8b)

[PHP官方的pcntl\_signal性能极差](http://rango.swoole.com/archives/364)
