プログラムの構築をしていると、時折不可思議な動きをすることがあります。
今回、クラスの関数を1回しか呼び出ししていないのに、2回呼び出されるという現象が起こりました。
しかも、テスト環境では1回しか呼び出されないのに、実機で2回呼び出されました。
異なる環境で現象が変わると、怪現象のようにすら感じます。
しかし、システムである以上どこかしらに原因があります。
ということで、なぜ実機で関数が2回コールされてしまったのか調べました。
現象はこのような感じです。
次のソースを実行したところ、関数が2回コールされました。
ソース
以下のソースを実行したところ、sample()関数が2回コールされました。
<?php
$class = new sample();
$class->sample();
class sample
{
function sample()
{
echo "sample!<br>";
}
}
?>
処理結果
原因はクラス名と関数名が同じであること
なぜ2回コールされているのか最初は理屈がさっぱりでした。
あちこちにログ出力を仕込んで動きを調べてみたところ、次のことがわかりました。
- 1回目のコール:sample()関数コール前に呼ばれていました。
- 2回目のコール:sample()関数をコールしたタイミングで呼ばれていました。
これでピンときました。
もしかして、クラス生成時に呼ばれているのではないか?
予測はあたりでした。
クラス名が「sample」で、呼び出し関数名も「sample」と、クラス名=関数名であった場合、クラス生成時に自動でコールされていました。
正確にに言うと、クラス名と同じ名称の関数は「コントクラスタ」と同じ動作をすることがわかりました。
だから、2回呼ばれていました。
何とかするには3つの方法があります。
クラス関数のコントクラスタ化が原因であるならば、対処方法は次の3通りの方法があります。
- 関数名を変更する
- コントクラスタ関数をつける
- PHP8にする
(1)関数名を変更する
1番無難な方法です。
次のように関数名を変更したところ、うまく動作しました。
ソース
$class = new sample();
$class->disp();
class sample
{
function disp()
{
echo "sample!<br>";
}
}
?>
処理結果
(2)コントクラスタ関数をつける
クラス生成時に動作するコントクラスタ関数を指定しておけば、クラス名と関数名が同じでも、コントクラスタ関数が優先して呼ばれるため、2回呼ばれるということがなくなります。
ソース
<?php
$class = new sample();
$class->sample();
class sample
{
function __construct()
{
}
function sample()
{
echo "sample!<br>";
}
}
?>
処理結果
PHP8に切り替える
今回現象が発生した実機のPHPバージョンは、PHP7.4でした。
PHP8からは、クラス名と同じ関数名はコントクラスタとならないため、2回呼ばれる現象がなくなります。
ソース(PHP7.4で現象が発生するソース)
<?php
$class = new sample();
$class->sample();
class sample
{
function sample()
{
echo "sample!<br>";
}
}
?>
処理結果
この問題はいずれ発生しなくなります。
クラス名=関数名の場合、コントクラスタになるという仕様はPHP7までで、PHP8からはコントクラスタになりません。
今回テスト環境はPHP8.2で、実機がPHP7.4でした。
だから異なる動作をしていました。
逆を言えば、クラス名と同じ名称でコントクラスタ代わりにしている場合、PHP8からはそうならなくなるため、注意が必要とも言えます。
私は、コントクラスタ関数は知っていましたが、クラス名と関数名が同じ場合コントクラスタになるという仕様を知らなかったため、思ったより四苦八苦いたしました。
PHPのバージョン改変期は、仕様の変更によりこういった怪現象が多く発生します。
ただ、プログラムの場合怪現象には必ず原因があります。
原因を突き止めるのもプログラマーの重要な資質なのだと思った現象でした。