ツイート
シェア
LINEで送る
B! はてぶでブックマーク
Pocketでブックマーク
RSSフィード

PHP関数ってなんで'test'()でも実行できるの? いらんもの付けても動く何じゃそりゃの仕様。

php
イラストダウンロードサイト【イラストAC】
の画像をもとに加工しています。

ふつうプログラミングの関数は、test() のように記述して実行します。

しかしPHPでは、'test'(), "test"() でもまったく同じことができる。

クォーテーションがなくてもいいならいらないじゃん、ていう仕様なんですが、これの正体ははっきりしています。

一般的な関数コールとはちがう仕様があるPHP

PHPの関数コールは、ふつうではありえない仕様があります。

まずはサンプルコードを実行してみましょう。

function test($val1, $val2)
{
    return $val1 + $val2;
}

$result1 = test(3, 5);
$result2 = 'test'(3, 5);
$result3 = "test"(3, 5);

var_dump($result1);
var_dump($result2);
var_dump($result3);
実行結果
int(8)
int(8)
int(8)

PHPの標準関数でもできます。

$result1 = strlen('sample');
$result2 = 'strlen'('sample');
$result3 = "strlen"('sample');

var_dump($result1);
var_dump($result2);
var_dump($result3);
実行結果
int(6)
int(6)
int(6)

また、クラスのメソッドでもできます。

class Test
{
    public static function add($val1, $val2)
    {
        return $val1 + $val2;
    }

    public function add2($val1, $val2)
    {
        return $val1 + $val2;
    }
}

$result1 = Test::add(3, 5);
$result2 = 'Test::add'(3, 5);
$result3 = "Test::add"(3, 5);

var_dump($result1);
var_dump($result2);
var_dump($result3);
実行結果
int(8)
int(8)
int(8)

staticメソッドだけでやってみました。理由は、インスタンスメソッドでは、なぜ関数名をクォーテーション(', "")でくくって文字列でも実行できるのか? が分かってしまうから。

さっそく答え合わせしましょう。インスタンスメソッドではこうなります。

$test = new Test();
$result1 = $test->add2(3, 5);
$result2 = [$test, 'add'](3, 5);
$result3 = [$test, "add"](3, 5);

var_dump($result1);
var_dump($result2);
var_dump($result3);
実行結果
int(8)
int(8)
int(8)

文字列ではなく配列になりました。これでピンときた人もいるんじゃないかと思います。

関数名文字列はコールバック

クォーテーションを使う関数と使わないふつうの関数のコールはまったくの別物です。

ふつうの関数はそれこそ関数。それ以外に何もありません。

一方、関数名の文字列・配列はコールバックです。文字列や配列の後ろに '()' を付けると関数が実行できる。

その動きは、Closure::fromCallable() と似ています。

Closureオブジェクトは無名関数(クロージャ)やアロー関数の戻り値の型として使うもので、new演算子でインスタンスが作れないほど、作成方法が限定されてます。

ようは、無名関数(アロー関数も無名関数の一種)を定義したときしか作れねーよってこと。

無名関数の実行は Closureオブジェクト + '()' なので、関数名文字列(コールバック)+ '()' と類似性が高い。

コールバックは文字列や配列だが、型はstring, arrayでなくcallable型になる。

callable型に '()' を付けると関数実行になる。

コールバック + () は初めて見ると意味不明

この記法はよっぽどプログラミングに慣れてないとさっぱり意味が分かりません。

でもPHPを最初に学ぶときに、意味もわからず関数コールでは関数名をクォーテーションでくくってもいいよ? と教えられます。

教えられても使うことはないから余計に意味を考えなくなる。

その答えをもう1回いいますが、Closure::fromCallable()と同じ考えの記法で、クォーテーションでくくった文字列はコールバックです。

コールバック + () の仕様とClosure::fromCallable() はどちらが先に考えられたのか分からない。

卵が先か、ニワトリが先かの話になるのでこれ以上は突っ込まない。

PHPプログラミングの学び始めにこのレベルを説明しても分からないから教えないんでしょうね?

クォーテーションでくくった関数名の文字列はコールバック。

コールバックの正体はcallable型、もしくは変換された無名関数のオブジェクトでふつうの関数ではない。

コールバック + '()' で関数の実行になる。

コールバックを変数に入れたらどうなるか?

最後に、コールバックの文字列、配列を変数に入れて、その後ろに '()' を付けたらどうなるのか見てみましょう。

$function = 'test';
$result = $function(3, 5);
var_dump($result);

$method = 'Test::add';
$result = $method(3, 5);
var_dump($result);

$test = new Test();
$method = [$test, 'add'];
$result = $method(3, 5);
var_dump($result);
実行結果
int(8)
int(8)
int(8)

変数に代入してもできました。

ただこのコードを見て、なんで関数が実行できるんだろう? と思う人はけっこういると思います。

おそらく、『PHPの関数名はクォーテーションでくくっても実行できるよ?』だけの知識だけでは理解できないでしょう。

かといって、『文字列や配列でも型はcallable型で、Closure::fromCallable()と同じようなものだからだよ?』と言っても分かりそうにないし。

これを理解するためには、無名関数、クラスの考え方、コールバックの実行方法など、勉強することがたくさんです。

よっぽど意図がないかぎり、クォーテーションなしの関数コールを使ったほうがいい。ましてや意味も分からず使うものではない。

ふつうではありえない仕様と言ってきたが、C言語にも関数名を変数に代入して、変数から関数を実行する方法がある。(あった気がする。)

C言語をがっつりやってたのは、もう20年近くも前なのでくわしくは忘れた。ごめんなさい。

ただ基本構文ではなかった。1990年代のプログラマが書いたものを見て、そんなこともできるんだ、とびっくりした記憶だけは鮮明にある。

昔のプログラマが書いたものは、教科書的なものをはるかに超えるコーディングがいっぱいあってすごく勉強になった。

こういう人たちはプログラム言語を開発できるレベルで、文法がどうやって作られているかまで知っていたので、『その文法ならこう書いてもいいよね?』とオリジナリティのある文法で処理を書く人がけっこういた。

(っぽい。コードを読んだだけで会ったことはないから。)

今では考えられないが。

でも、こういう人がプログラマって言うんでしょうね?

正直、そういう人のコードを見た手前、いまだに『プログラマです』と自称するのを恥ずかしいと思ってしまう。

前の投稿
PHPは '...' のことをスプレッド演算子とは言っていない。
PHPの無名関数とクロージャは同じ扱い。元々そう思ってる人は多いけど。
次の投稿

コメントを残す