Laravel 標準ヘルパ関数を自作ヘルパ関数でオーバーライドする

## 概要

前回 [Laravel 自作のヘルパ関数を追加する](https://qiita.com/ucan-lab/items/afae48ee5328b77b6c10) 記事を書きましたが、標準のヘルパ関数を上書きして使いたいというのが一定の需要がありそうなので記事にしました。

デフォルトのヘルパ関数を上書きすると新参者が困るので極力は止めた方がいいです。
ヘルパ関数自体も乱用は避けて、適度に利用するのが望ましいです。

## 環境

- PHP 7.4.5
- Laravel 7.9.2

## Laravelのヘルパ関数

https://github.com/laravel/framework/blob/7.x/src/Illuminate/Foundation/helpers.php

```php
if (! function_exists('now')) {
    /**
     * Create a new Carbon instance for the current time.
     *
     * @param  \DateTimeZone|string|null  $tz
     * @return \Illuminate\Support\Carbon
     */
    function now($tz = null)
    {
        return Date::now($tz);
    }
}
```

`now` 関数だけ抜粋ですが、このように記述されています。
[function_exists]
(https://www.php.net/manual/ja/function.function-exists.php) 関数が定義済みかチェックしてますので、自前で作ったヘルパ関数を先に読み込ませてあげれば良さそうです。

```shell-session
$ php artisan tinker
>>> now()
=> Illuminate\Support\Carbon @1588607807 {#3012
     date: 2020-05-04 15:56:47.513338 UTC (+00:00),
     timezone: "UTC",
   }
```

こんな感じの挙動です。

## やり方

CLI経由、WEB経由の時も `vendor/autoload.php` は必ず読み込まれるので一番最初に自作のヘルパ関数ファイルを読み込ませる形にします。
(こうしないとLaravelのヘルパファイルが先に読み込まれてしまいます)

### 自作のヘルパ関数を作る

`now()` ヘルパ関数を自作します。

```php:app/helpers.php
<?php declare(strict_types=1);

if (! function_exists('now')) {
    /**
     * @return string
     */
    function now(): string
    {
        return date('Y-m-d H:i:s');
    }
}
```

`Carbon` を返してたのを `string` で返す関数にしてみました！

### オートローダーを書き換えるコマンドを作成する

オートローダーを書き換える方法ですが、コマンドファイルを作ってあげるのが分かりやすいかなと思いました。

```shell-session
$ php artisan make:command BootstrapAutoloadCommand
```

作成された `app/Console/Commands/BootstrapAutoloadCommand.php` を下記の内容に書き換えます。

```php:app/Console/Commands/BootstrapAutoloadCommand.php
<?php declare(strict_types=1);

namespace App\Console\Commands;

use Illuminate\Console\Command;

class BootstrapAutoloadCommand extends Command
{
    protected $signature = 'bootstrap:autoload';
    protected $description = 'app/helpers.php を先読みさせる';
    public function handle(): void
    {
        $autoloadPath = base_path('vendor/autoload.php');
        $helperPath = app_path('helpers.php');

        // オートローダーのファイル内容を読み込む
        $before = file_get_contents($autoloadPath);

        // 先に自作ヘルパファイルを一番最初に読み込むようにする
        $after = str_replace('<?php' . PHP_EOL, "<?php require_once '$helperPath';" . PHP_EOL, $before);

        // オートローダーを上書きする
        file_put_contents($autoloadPath, $after);
    }
}
```

やってることは `vendor/autoload.php` を読み込んで自作した `app/helpers.php` ファイルを読み込んでます。

```json:composer.json
    "scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi",
            "@php artisan bootstrap:autoload"
        ],
        // ...
    }
```

`dump-autoload` 実行後のイベントフックに `php artisan bootstrap:autoload` を追加してあげます。

```shell-session
$ composer dump-autoload
```

オートローダーを更新します。
`vendor/autoload.php` 下記のように `app/helpers.php` を読み込むようになってたらokです。

```php:/laravel/vendor/autoload.php
<?php require_once '/laravel/app/helpers.php';

// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInitf2b4f3f391cdb9fc40ee0b8001495415::getLoader();
```

## お試し

```shell-session
$ php artisan tinker
>>> now()
=> "2020-05-04 15:57:29"
```

Laravel標準のヘルパ関数を自作のヘルパ関数でオーバーライドできました！

## 参考

- https://github.com/laravel/framework/blob/7.x/composer.json#L98
- https://github.com/laravel/framework/blob/7.x/src/Illuminate/Foundation/helpers.php