## 第一級callableを生成する記法

第一級callableを生成する記法 (First class callable syntax) とは、PHP 8.1.0 で追加された、 callable から 無名関数 を生成する記法です。 この記法は、 文字列や配列を使って callable を生成するやり方を置き換えるものです。 この記法の利点は、callable の静的解析を行いやすくなることに加え、 そのスコープが、callable を生成した時点のスコープになることです。

`CallableExpr(...)` という記法で、 callable から Closure を生成します。 `CallableExpr` には、 PHP の文法上直接コールできるあらゆる式が使えます:

例1 簡単な例

    <?php
    class Foo {
       public function method() {}
       public static function staticmethod() {}
       public function __invoke() {}
    }

    $obj = new Foo();
    $classStr = 'Foo';
    $methodStr = 'method';
    $staticmethodStr = 'staticmethod';

    $f1 = strlen(...);
    $f2 = $obj(...);  // 呼び出し可能オブジェクト
    $f3 = $obj->method(...);
    $f4 = $obj->$methodStr(...);
    $f5 = Foo::staticmethod(...);
    $f6 = $classStr::$staticmethodStr(...);

    // 文字列や配列を使った、古い記法
    $f7 = 'strlen'(...);
    $f8 = [$obj, 'method'](...);
    $f9 = [Foo::class, 'staticmethod'](...);

> 注意:

> `...` は文法の一部であり、省略形ではありません。

この記法の動作は、 Closure::fromCallable() の仕様に従います。 つまり、文字列や配列から callable を作るやり方とは異なり、 `CallableExpr(...)` のスコープは、 それを生成した時点でのスコープになります:

例2 古い callable の生成方法と、`CallableExpr(...)` の比較

    <?php
    class Foo {
        public function getPrivateMethod() {
            return [$this, 'privateMethod'];
        }

        private function privateMethod() {
            echo __METHOD__, "\n";
        }
    }

    $foo = new Foo;
    $privateMethod = $foo->getPrivateMethod();
    $privateMethod();
    // Fatal error: Call to private method Foo::privateMethod() from global scope
    // 上記がエラーになるのは、呼び出しが Foo の外部から行われ、
    // アクセス権のチェックもこの時点から行われるためです。

    <?php
    class Foo1 {
        public function getPrivateMethod() {
            // 下記は、callable を生成した時点のスコープになります。
            return $this->privateMethod(...); // Closure::fromCallable([$this, 'privateMethod']); と同等です。
        }

        private function privateMethod() {
            echo __METHOD__, "\n";
        }
    }

    $foo1 = new Foo1;
    $privateMethod = $foo1->getPrivateMethod();
    $privateMethod();  // Foo1::privateMethod

> 注意:

> この記法を使ってオブジェクトを生成する操作 (例: `new Foo(...)`) はサポートされていません。 なぜなら、`new Foo()` は、呼び出しとは見なされないからです。

> 注意:

> この記法は、nullsafe 演算子 と組み合わせて使うことはできません。 以下のコードは、いずれもコンパイルエラーになります:

>     <?php
>     $obj?->method(...);
>     $obj?->prop->method(...);

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open