👌

tickにまつわるエトセトラ

に公開

はじめに

普通に PHP を使っている中では、まずお目にかかることが無いであろう機能の一つに declare(ticks=N); があります (というか最近初めて知りました)。
あんまり使う機会は無いですが、調査したことを書いておきます。

tick とは

PHP マニュアルに以下の記述があります。

tickとはdeclareブロックの実行中にパーサが N個の低レベル tick 可能な文を実行するごとに 発生するイベントのことです。Nの値は declareブロックのディレクティブの箇所で ticks=Nのように 指定します。

https://www.php.net/manual/ja/control-structures.declare.php#control-structures.declare.ticks

実際に使ってみると declare(ticks=N); と記述すると、tick 可能な文がN回実行される毎に register_tick_function が呼び出される、という挙動になります。

<?php

// ハンドラを定義
function my_tick_function() {
    echo 'A tick has occurred.' . PHP_EOL;
}

// register_tick_function でハンドラを登録
register_tick_function('my_tick_function');

declare(ticks=1);

for ($i = 0; $i < 5; $i++) {
    echo 'In the loop: $i' . PHP_EOL;;
}
% php ticks.php
A tick has occurred.
In the loop: 0
A tick has occurred.
In the loop: 1
A tick has occurred.
In the loop: 2
A tick has occurred.
In the loop: 3
A tick has occurred.
In the loop: 4
A tick has occurred.
A tick has occurred.

何に使うの?

文毎にログを出したり、文と文の間の microtime を出力して性能検証をしたりする、という使い方もあるようです。あるいは、シグナルハンドラをディスパッチするために利用するというのが多いんじゃないでしょうか。

<?php

declare(ticks=1);

$receivedSigterm = false;

pcntl_signal(SIGTERM, function() use (&$receivedSigterm) {
    echo 'SIGTERM received.' . PHP_EOL;
    $receivedSigterm = true;
});

pcntl_signal(SIGINT, function() use (&$receivedSigterm) {
    echo 'SIGINT received.' . PHP_EOL;
    $receivedSigterm = true;
});

echo 'PID: ' . getmypid() . PHP_EOL;

while (!$receivedSigterm) {
    echo 'Working...' . PHP_EOL;
    sleep(1);
}

% php signals-dispatch.php
PID: 67626
Working...
Working...
Working...
^CSIGINT received. # control + C
% php signals-dispatch.php
PID: 67601
Working...
Working...
Working...
Working...
SIGTERM received. # kill 67601

例えば cli でバッチ処理などの比較的長時間生存するプロセスがあったとして、kill されたときに安全に終了するために利用することが考えられます。PHP でバッチ処理を実現する是非については置いておくとして。

何で見かけないの?

ただし、当然ながら tick 可能な文が無いとディスパッチされません。そして tick には少なくないオーバーヘッドがあります。
PHP 7.1からは tick のシグナルハンドラのディスパッチ機能の代替として pcntl_async_signals(true) が実装されました。
https://wiki.php.net/rfc/async_signals
https://www.php.net/manual/ja/function.pcntl-async-signals.php

<?php

pcntl_async_signals(true);

pcntl_signal(SIGTERM, function() use (&$receivedSigterm) {
    echo 'SIGTERM received.' . PHP_EOL;
    exit;
});

pcntl_signal(SIGINT, function() use (&$receivedSigterm) {
    echo 'SIGINT received.' . PHP_EOL;
});

echo 'PID: ' . getmypid() . PHP_EOL;

while (true) {
    // tick 可能な文が無い => declare(ticks=1); だと、ハンドラは実行されない
}

また、tick にまつわる PHP 5系のバグが PHP 7で修正されました。
https://bugs.php.net/bug.php?id=71448
これにより、このバグに依存したライブラリやプラグインがもろに影響を受けて淘汰されてしまった、ということも見かけない理由の一つかと思われます。

今後は削除される?

一応、非推奨化/削除の RFC が過去に出されたことがあります。
https://wiki.php.net/rfc/deprecate_ticks

しかしながら、それなりのインストール数のある WordPress プラグインでプロファイリングとしての利用があることなどから、後に撤回されています。
https://externals.io/message/114368
https://github.com/php/php-src/pull/6967
https://wordpress.org/plugins/p3-profiler/

確かに、WordPress 環境では Xdebug や外形的なプロファイラを用意することができないこともあるので、機能ごと切り捨てるのは難しそうですね。
また、議論の中で名前のあがっていた WordPress プラグインは先の PHP 5系でのバグ依存だった実装を修正して生き残ったという歴史があります。
https://github.com/wpmudev/p3-profiler/blob/master/classes/class.streamwrapper.php

ただ、数年前からメンテが止まっているようで、公式ディレクトリからも2022年からダウンロードができなくなっているようです。

おわりに

特にオチはありません。

Discussion