Fusic Advent Calendar 2019 - Qiita 9日目の記事です。
今回はPHPでこんなメソッドの呼び出しあるんだ?としらないことがあったので、そのことについて書いてみました。
知らなかった呼び出し方
CakePHPのソースコードを読んでいた時に、ここどうなってんだ?となったのがこの部分になります。
https://github.com/cakephp/cakephp/blob/master/src/Datasource/ModelAwareTrait.php#L127
$factoryに入ってる値
/src/Datasource/ModelAwareTrait.php (line 128)
[
(int) 0 => object(Cake\ORM\Locator\TableLocator) {
[protected] locations => [
(int) 0 => 'Model/Table'
]
[protected] _config => []
[protected] _instances => []
[protected] _fallbacked => []
[protected] _options => []
},
(int) 1 => 'get'
]
配列の1つ目にTableLocatorのインスタンス
、2つめにgetという文字列
が入ってます。
呼び出し
$factory($modelClass, $options);
こうなってます。配列(引数1, 引数2)
です。
この部分の配列に対して、メソッド呼び出しみたいなことが出来ることを知りませんでした。
動き的には、 TableLocator::get
が実行されており、call_user_func
と同じような動作になってます。
調べてみました
ググってみましたが、結構情報が出ません。
というか検索しにくい。。。
php 配列 メソッド実行
とかでググると call_user_func
ばかり引っかかって目的に到達できません。。。
以下のURLを見つけたのですが、どうやらPHP5.4からすでに実装されてるみたいです(笑)
知らなったよ。。。
PHP5.4っていうと2011年
参考URL
https://blog.sarabande.jp/post/6303916255
https://wiki.php.net/rfc/indirect-method-call-by-array-var
この記事によると、PHP5.5でベンチをとっており、直接インスタンスからの呼び出しが一番早いみたいですが、call_user_funcより配列からのメソッド実行の方が速い
ようです。
せっかくPHP7.4が出たので、ベンチを取ってみました
ベンチ結果
基本的にはPHP5.5のときと変わらないですね。
参考URLでは call_user_func
と call_user_func_array
で多少差が出ているようですが、自分の環境ではまったく差が出なかったので省きました。
配列からの呼出しを2種類行ってますが、配列からの実行の場合でも書き方によって性能が出ないようです。
※参考URLより、ループの回数を増やしてます。(メソッド直接呼出しをおおよそ1秒に設定)
呼び出し方法 | 秒数 |
---|---|
メソッド直接呼出し | 0.99343309402466 |
call_user_func | 4.6852278709412 |
配列から呼び出し1 | 4.5191860198975 |
配列から呼び出し2 | 2.491909027099 |
文字列から呼び出し | 2.6240780353546 |
- PHPバージョン : PHP7.4.0
ベンチソースコード
class Example {
public function hoge()
{
return ;
}
}
$e = new Example();
$start = microtime(true);
for ($i = 0; $i < 30000000; ++$i) {
// この部分のコードのみを置き換えて計測しています。
$e->hoge();
}
$end = microtime(true);
call_user_func
for ($i = 0; $i < 30000000; ++$i) {
call_user_func([$e, 'hoge']);
}
配列から呼び出し1
変数に入れずに直接メソッド実行しています。
この方法だと、call_user_func
と性能が変わらないです。
for ($i = 0; $i < 30000000; ++$i) {
[$e, 'hoge']();
}
配列から呼び出し2
ループの外で変数に配列を入れてからメソッド実行しています。
$arr = [$e, 'hoge'];
for ($i = 0; $i < 1000000; ++$i) {
$arr();
}
文字列から呼び出し
$methodo = 'hoge';
for ($i = 0; $i < 30000000; ++$i) {
$e->{$methodo}();
}
まとめ
相当ループを回さないと大きな性能差は出ませんが、速度を気にするのであればcall_user_func
は使わずに以下がよさそうです。
- メソッドを直接実行
- 配列から実行(必ず変数に入れてから実行)
- 文字列から実行
今回はベンチ取ってないですが、以下のようにstaticなメソッドに関しても配列実行可能です。
class Example {
public static function fuga()
{
return ;
}
}
$arr = ['Example', 'fuga'];
for ($i = 0; $i < 30000000; ++$i) {
$arr();
}