PHPの無名関数って何？

# 無名関数は、関数名のない関数のことです。
> 無名関数はクロージャとも呼ばれ、 関数名を指定せずに関数を作成できるようにするものです。 コールバック パラメータとして使う際に便利ですが、用途はそれにとどまりません。
> [PHP: 無名関数 - Manual](http://php.net/manual/ja/functions.anonymous.php)

PHPでは、「無名関数」「ラムダ関数」「匿名関数」が同じものを指す・・・らしい。

- [匿名関数とラムダの違いについて｜teratail](https://teratail.com/questions/26142)
- [PHPでラムダ式で関数を書く方法【初心者向け】 | TechAcademyマガジン](https://techacademy.jp/magazine/19139)

# 無名関数をコールバックで使うパターン

```php:WhatMumeiKansu.php
<?php
namespace App;

/**
 * PHPの無名関数って何？.
 */
class WhatMumeiKansu
{
    /** @var string スネークケースの区切り文字. */
    const SNAKE_DELIMITER = '_';

    /*
     * パスカルケース文字列を大文字のスネークケース文字列へ変換する.
     * @param  array $pascals パスカルケース文字列.
     * @return array         大文字のスネークケース文字列.
     */
    public function convertStringToUpperSnakeCase(array $pascals): array
    {
        // 無名関数をコールバックで使うパターン.
        return array_map(function ($pascal) {
            $snake = preg_replace_callback('([A-Z])', function($matches) {
                return self::SNAKE_DELIMITER.$matches[0];
            }, $pascal);

            if (mb_substr($snake, 0, 1) === self::SNAKE_DELIMITER) {
                $snake = ltrim($snake, self::SNAKE_DELIMITER);
            }
            return strtoupper($snake);
        }, $pascals);
    }
}

$pascals = [
    'PonsukeTarou',
    'ponsukeTarou',
    'Ponsuketarou'
];
$what = new WhatMumeiKansu();
var_dump($what->convertStringToUpperSnakeCase($pascals));
```

```bash
$ php src/WhatMumeiKansu.php 
array(3) {
  [0]=>
  string(13) "PONSUKE_TAROU"
  [1]=>
  string(13) "PONSUKE_TAROU"
  [2]=>
  string(12) "PONSUKETAROU"
```

# 無名関数を変数に代入するパターン

```php:WhatMumeiKansu.php
<?php
// 無名関数を変数に代入する.
$toPascalCase = function($snake)
{
    $lowerSnakeCase = strtolower($snake);
    $upperCamelCase = ucwords($lowerSnakeCase, '_');
    return str_replace('_', '', $upperCamelCase);
};

var_dump($toPascalCase('ponsuke_tarou'));

$snakeStrings = [
    'ponsuke_tarou',
    'PONSUKE_TAROU',
    'poNSUKE_TARou'
];

// 無名関数を代入した変数をコールバックで使う.
var_dump(array_map($toPascalCase, $snakeStrings));
```

```bash
$ php WhatMumeiKansu.php 
string(12) "PonsukeTarou"
array(3) {
  [0]=>
  string(12) "PonsukeTarou"
  [1]=>
  string(12) "PonsukeTarou"
  [2]=>
  string(12) "PonsukeTarou"
}
```

## class内で無名関数をプロパティに代入すると`Fatal error: Constant expression contains invalid operations`になります。
```php:WhatMumeiKansu.php
<?php
/**
 * PHPの無名関数って何？.
 */
class WhatMumeiKansu
{
    /** @var string スネークケースの区切り文字. */
    private const SNAKE_DELIMITER = '_';

    /** @var Closure $toPacalCas スネークケース文字列をパスカルケース文字列にする. */
    $toPascalCase = function($snake)
    {
        // 無名関数を変数に代入するパターン
        $lowerSnakeCase = strtolower($snake);
        $upperCamelCase = ucwords($lowerSnakeCase, self::SNAKE_DELIMITER);
        return str_replace(self::SNAKE_DELIMITER, '', $upperCamelCase);
    };
}

$what = new WhatMumeiKansu();
var_dump($what->toPascalCase('ponsuke_tarou'));
```

```bash
$ php WhatMumeiKansu.php 
PHP Fatal error: Constant expression contains invalid operations in /path/to/WhatMumeiKansu.php on line 11

Fatal error: Constant expression contains invalid operations in /path/to/WhatMumeiKansu.php on line 11
```

### 原因は、クラスのプロパティとメソッドは別々の名前空間にいるからです。
> プロパティとメソッド
> クラスの**プロパティとメソッドは、それぞれ別の名前空間に存在**するので、 同じ名前のプロパティとメソッドを共存させることもできます。 プロパティを参照する場合もメソッドを参照する場合も書きかたは同じです。 それがプロパティへのアクセスなのかメソッドの呼び出しなのかは、そのコンテキストによって決まります。 つまり、変数にアクセスしようとしているのか関数を呼び出そうとしているのかの違いです。
> これはつまり、**プロパティに 無名関数 を代入した場合に、その関数は直接呼び出せない**ということです。 その場合は、たとえば事前にプロパティを変数に代入しておく必要があります。
> [PHP: クラスの基礎 - Manual](http://php.net/manual/ja/language.oop5.basic.php)

## class内で無名関数をプロパティに代入する場合はコンストラクタで代入します。
```php:WhatMumeiKansu.php
<?php
namespace App;

/**
 * PHPの無名関数って何？.
 */
class WhatMumeiKansu
{
    /** @var string スネークケースの区切り文字. */
    private const SNAKE_DELIMITER = '_';

    /** @var Closure $toPacalCas スネークケース文字列をパスカルケース文字列にする. */
    public $toPascalCase;

    /**
     * コンストラクタ.
     */
    public function __construct()
    {
        // コンストラクタでプロパティに無名関数を代入する.
        $this->toPascalCase = function($snake)
        {
            // 無名関数を変数に代入するパターン
            $lowerSnakeCase = strtolower($snake);
            $upperCamelCase = ucwords($lowerSnakeCase, self::SNAKE_DELIMITER);
            return str_replace(self::SNAKE_DELIMITER, '', $upperCamelCase);
        };
    }

    /**
     * スネークケース文字列の配列をパスカルケース文字列の配列へ変換する.
     * @param  array  $snakes スネークケース文字列の配列.
     * @return array          パスカルケース文字列の配列.
     */
    function convertStringsToPascalCase(array $snakes): array
    {
        // 無名関数を代入した変数をコールバックで使うパターン.
        return array_map($this->toPascalCase, $snakes);
    }

    /*
     * パスカルケース文字列を大文字のスネークケース文字列へ変換する.
     * @param  array $pascals パスカルケース文字列.
     * @return array         大文字のスネークケース文字列.
     */
    public function convertStringToUpperSnakeCase(array $pascals): array
    {
        // 無名関数をコールバックで使うパターン.
        return array_map(function ($pascal) {
            $snake = preg_replace_callback('([A-Z])', function($matches) {
                return self::SNAKE_DELIMITER.$matches[0];
            }, $pascal);

            if (mb_substr($snake, 0, 1) === self::SNAKE_DELIMITER) {
                $snake = ltrim($snake, self::SNAKE_DELIMITER);
            }
            return strtoupper($snake);
        }, $pascals);
    }
}

$what = new WhatMumeiKansu();

var_dump(($what->toPascalCase)('ponsuke_tarou'));

$pascals = [
    'PonsukeTarou',
    'ponsukeTarou',
    'Ponsuketarou'
];

var_dump(
    $what->convertStringsToPascalCase(
        $what->convertStringToUpperSnakeCase($pascals)
    )
);
```

```bash
$ php WhatMumeiKansu.php 
string(12) "PonsukeTarou"
array(3) {
  [0]=>
  string(12) "PonsukeTarou"
  [1]=>
  string(12) "PonsukeTarou"
  [2]=>
  string(12) "Ponsuketarou"
}
```