laravel Hashファサード 文字列ハッシュ化までの道

# 概要

- laravelのHashファサードで文字列がハッシュ化されるまでの道のりを追ってみる。

# 準備

- ハッシュドライバーの確認
    - どうやらlaravelでは3種類のハッシュドライバーをサポートしているらしい。
    - これから確認するlaravelのコードがどのハッシュドライバーを用いているかをチェックする。（デフォルトはBcrypt）
    - 下記のファイルを開く。
        - config/hashing.php
    - `driver`のキーに紐づく値を確認してハッシュドライバーをチェックする。デフォルトは下記のようにBcryptが設定されているはず。

        ```config/hashing.php
        <?php
        
        return [
        
            /*
            |--------------------------------------------------------------------------
            | Default Hash Driver
            |--------------------------------------------------------------------------
            |
            | This option controls the default hash driver that will be used to hash
            | passwords for your application. By default, the bcrypt algorithm is
            | used; however, you remain free to modify this option if you wish.
            |
            | Supported: "bcrypt", "argon", "argon2id"
            |
            */
        
            'driver' => 'bcrypt',
        ];
        ```

# ハッシュ化までの確認

1. 下記のようなコードをどこかのクラスに記載して何かしらの文字列をハッシュ化するところから話は始まる。

    ```php
    use Illuminate\Support\Facades\Hash;

    Hash::make('ハッシュ化される文字列');
    ```

1. まずは当該のファサードクラスを見てみる。処理そのものは記載されておらずあくまでファサードが定義されているだけ。

    ```vendor/laravel/framework/src/Illuminate/Support/Facades/Hash.php
    <?php
    
    namespace Illuminate\Support\Facades;
    
    /**
     * @method static array info(string $hashedValue)
     * @method static bool check(string $value, string $hashedValue, array $options = [])
     * @method static bool needsRehash(string $hashedValue, array $options = [])
     * @method static string make(string $value, array $options = [])
     * @method static \Illuminate\Hashing\HashManager extend($driver, \Closure $callback)
     *
     * @see \Illuminate\Hashing\HashManager
     */
    class Hash extends Facade
    {
        /**
         * Get the registered name of the component.
         *
         * @return string
         */
        protected static function getFacadeAccessor()
        {
            return 'hash';
        }
    }
    ```

1. ファサードクラスにアノテーションで`@see \Illuminate\Hashing\HashManager`って書かれていたので当該のクラスを見に行ってみる。HashManagerクラスではManagerクラスを継承し、Hasherインターフェースが実装され、いくつかの関数が記載されている。その中に文字列をハッシュ化するmake関数を発見した。どうやら継承元クラスManagerのdriver関数を実行して、その後にHasherインターフェースのmake関数を実行している模様

    ```vendor/laravel/framework/src/Illuminate/Hashing/HashManager.php
    <?php
    
    namespace Illuminate\Hashing;
    
    use Illuminate\Contracts\Hashing\Hasher;
    use Illuminate\Support\Manager;
    
    class HashManager extends Manager implements Hasher
    {
        /**
         * Create an instance of the Bcrypt hash Driver.
         *
         * @return \Illuminate\Hashing\BcryptHasher
         */
        public function createBcryptDriver()
        {
            return new BcryptHasher($this->config->get('hashing.bcrypt') ?? []);
        }

        /**
         * Hash the given value.
         *
         * @param  string  $value
         * @param  array  $options
         * @return string
         */
        public function make($value, array $options = [])
        {
            return $this->driver()->make($value, $options);
        }
    }
    ```

1. driver関数ではHashManagerクラスのgetDefaultDriver関数を実行し、configからデフォルトドライバーを取得し、取得ドライバーに応じてHashManagerクラスのcreateXXXDriver関数を実行してBcryptHasherクラスをインスタンス化して返している。（XXXは今回はBcrypt） だから`$this->driver()->make($value, $options);`の処理は結果として`BcryptHasherインスタンス->make($value, $options);`になっている。

    ```vendor/laravel/framework/src/Illuminate/Support/Manager.php
    <?php
    
    namespace Illuminate\Support;
    
    use Closure;
    use Illuminate\Contracts\Container\Container;
    use InvalidArgumentException;
    
    abstract class Manager
    {
        /**
         * Get a driver instance.
         *
         * @param  string|null  $driver
         * @return mixed
         *
         * @throws \InvalidArgumentException
         */
        public function driver($driver = null)
        {
            $driver = $driver ?: $this->getDefaultDriver();
        
            if (is_null($driver)) {
                throw new InvalidArgumentException(sprintf(
                    'Unable to resolve NULL driver for [%s].', static::class
                ));
            }
        
            // If the given driver has not been created before, we will create the instances
            // here and cache it so we can return it next time very quickly. If there is
            // already a driver created by this name, we'll just return that instance.
            if (! isset($this->drivers[$driver])) {
                $this->drivers[$driver] = $this->createDriver($driver);
            }
        
            return $this->drivers[$driver];
        }
    }
    ```

1. `BvryptHasherインスタンス->make($value, $options);`になっているのでBcryptHasherクラスを見に行く。ここでPHPのpassword_hash関数を用いて与えられた文字列をハッシュ化して返している。

    ```vendor/laravel/framework/src/Illuminate/Hassing/BcryptHasher.php
    <?php
    
    namespace Illuminate\Hashing;
    
    use Illuminate\Contracts\Hashing\Hasher as HasherContract;
    use RuntimeException;
    
    class BcryptHasher extends AbstractHasher implements HasherContract
    {
        /**
         * Hash the given value.
         *
         * @param  string  $value
         * @param  array  $options
         * @return string
         *
         * @throws \RuntimeException
         */
        public function make($value, array $options = [])
        {
            $hash = password_hash($value, PASSWORD_BCRYPT, [
                'cost' => $this->cost($options),
            ]);
        
            if ($hash === false) {
                throw new RuntimeException('Bcrypt hashing not supported.');
            }
        
            return $hash;
        }
    }
    ```