phpのini_set("max_execution_time",n)とset_time_limitの違いについて調べた

背景

phpスクリプトタイムアウト上限を伸ばすには2つの方法がある。

このうちset_time_limitのドキュメントにこのような記述がある。

デフォルトの制限値は 30 秒です。 なお、php.iniでmax_execution_timeの 値が定義されている場合にはそれを用います。

これを僕は「常にmax_execution_timeが優先される」ように解釈した。調べてみた。

結論

「常にmax_execution_timeが優先される」ようなことはない。
set_time_limitini_set('max_execution_time', n)のラッパーというだけな気がする。

調査

OSはMacphpのバージョンは7.0.14。

$ php -v
PHP 7.0.14 (cli) (built: Apr  1 2017 23:41:23) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies

まずは実際に挙動を確認

<?php
// 初期値を確認
var_dump(ini_get("max_execution_time"));

// ini_setで上限値を更新
ini_set("max_execution_time", 60);
var_dump(ini_get("max_execution_time"));

// set_time_limitで上限値を更新
set_time_limit(120);
var_dump(ini_get("max_execution_time"));

コマンドライン経由だとmax_execution_timeは0になるので、ビルトインサーバーを起動してリクエストを投げることで確認した。

-- ビルトインサーバー起動
$ php -S localhost:9000


-- 別コンソールからリクエスト実行
$ curl "http://localhost:9000/sample.php"
string(2) "30"
string(2) "60"
string(3) "120"

もし「max_execution_timeが優先される」なら最後の出力は30もしくは60になるはずだが、実際はちゃんと値が更新されている。
だとすると、ドキュメントの一文は何を意味するのか。

英語の原文を見る

英語のドキュメントだとこう書かれている。

The default limit is 30 seconds or, if it exists, the max_execution_time value defined in the php.ini.

「デフォルト値は30秒だが、もしphp.iniにmax_execution_timeの定義があればそれをデフォルト値とする」という意味に読める。
言葉の意味は分かるが、そもそもset_time_limitは引数が必須だ。このデフォルト値とは何のことなのか。一応php本体のコードも読むことにした。

PHP本体のソースを読む

ざっくりとしか読んでいないので間違いはあるかもしれない。

処理的には両者に違いはほとんどない。
デフォルト値がどう関係するかといえば次に書く通り。

どちらも設定値を上書きするたびにカウンタはリセットされる

set_time_limitのドキュメントより。

この関数がコールされた場合、 タイムアウトカウンタをゼロから再スタートします。 言いかえると、タイムアウトがデフォルトの 30 秒で スクリプト実行までに 25 秒かかる場合に、 set_time_limit(20) を実行すると、スクリプトは、 タイムアウトまでに全体で 45秒 の間実行されます。

つまり、以下のコードはタイムアウトにならずに無限ループになる。

<?php
while (true) {
    set_time_limit(1);
    // ini_setの場合も同じ
    // ini_set("max_execution_time", 1);
}

なお、これを実際に試すとプロセスを直接killしないとCPU100%でずっと走り続けるので注意。

本体のソースコードを読んで「これini_setも同様にゼロから再スタートするのでは」と思って試した。
この挙動はset_time_limitだけだと勝手に思っていたが、ini_set('max_execution_time', n)でも同様だった。
ドキュメント的にもmax_execution_timeの項にはset_time_limitを参照するように書かれているわけなので正しい。

ちなみに手元のphp.iniは最初からmax_execution_timeが30秒で設定されていたが、仮に設定がなくてもphp本体が30秒をデフォルト値にしている。

要は「タイムアウト値のデフォルトはmax_execution_timeに依存する」というだけの話だと思う。