laravelのコード読んでみた Fasade編②

# はじめに
https://qiita.com/arinco_/items/3e247b39dd0021281865

上記記事の続きです。
ファサードがどうやって目的のインスタンスを取得するか確認していきたいと思います。

# 実際に読んでいく　
## 前回のおさらい
軽くおさらいすると、Logファサードに対して、`Log::info()`で呼び出すと、
Facadeクラスの`__callStatic()`が呼ばれ、下記のように処理しているところまで読みました。

```php:Illuminate\Support\Facades\Facade.php
<?php

namespace Illuminate\Support\Facades;

abstract class Facade
{
    /**
     * Handle dynamic, static calls to the object.
     *
     * @param  string  $method
     * @param  array  $args
     * @return mixed
     *
     * @throws \RuntimeException
     */
    public static function __callStatic($method, $args)// $method = 'info', $args = ['ログです']
    {
        // $instance を取得
        $instance = static::getFacadeRoot();

        // $instanceがなければ例外を投げる
        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        // 取得したインスタンスに対して、メソッドを呼び出す。
        // 今回で言うと、 $instance->info('ログです');
        return $instance->$method(...$args);
    }
}

```

今回は下記の行の部分のインスタンスを取得している部分の処理に関して読み進めていきたいと思います。
## 本題

```php:Illuminate\Support\Facades\Facade.php
$instance = static::getFacadeRoot();
```
ここからstaticキーワードを使ったメソッド呼び出しがよく出てきます。
ここでのstaticは基本的にはLogクラスを指すのですが、
Logクラスには、`getFacadeAccessor()`しか定義されていないので、
基本的に親クラスであるFasadeクラスのstaticメソッドが呼ばれると思って大丈夫です。(違う場合は都度補足します、、、多分、、、)

ファサードクラスの`getFacadeRoot()`を見に行きます。

### static::getFacadeRoot()

```php:Illuminate\Support\Facades\Facade.php
    /**
     * Get the root object behind the facade. 
     * // ファサードの裏にあるルート(根源的な)オブジェクトを取得します
     * @return mixed
     */
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }
```

ここでは2つメソッドが呼ばれています。
先に引数の`static::getFacadeAccessor()`から見ていきます。
いきなり例外ですが、前述した通り、Logクラスには`static::getFacadeAccessor()`だけは定義されているので、Logクラスのメソッドが呼ばれます。

### static::getFacadeAccessor()

```php:Illuminate\Support\Facades\Log.php
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'log';
    }

```
log という文字列を返すだけです。

ちなみに親クラスであるFacadeクラスにも同名メソッドがあります

```php:Illuminate\Support\Facades\Facade.php
    /**
     * Get the registered name of the component.
     *
     * @return string
     *
     * @throws \RuntimeException
     */
    protected static function getFacadeAccessor()
    {
        throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
    }
```
つまり、Facadeクラスを継承したクラスは必ず`getFacadeAccessor()`を定義する必要があるということがわかります。

次は、`getFacadeRoot()`で呼ばれているもう一つのメソッド、`static::resolveFacadeInstance()`を見ていきます。

## static::resolveFacadeInstance()
メソッドからもファサードのインスタンスを解決するんだなというのがわかってきて、
少しずつ核心に近づいている感があります。

```php:Illuminate\Support\Facades\Facade.php
    /**
     * The application instance being facaded.
     *
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected static $app;

    /**
     * The resolved object instances.
     *
     * @var array
     */
    protected static $resolvedInstance;

    /**
     * Resolve the facade root instance from the container.
     * // コンテナからルートインスタンスを解決する
     * @param  object|string  $name
     * @return mixed
     */
    protected static function resolveFacadeInstance($name)// $name = 'log'
    {
        // $name がオブジェクトならそのまま返却
        if (is_object($name)) {
            return $name;
        }
        // Facadeクラスの静的プロパティ $resolvedInstance 配列の中に、
        // $name(ここでは'log') というキーで値が格納されていればその値を返却(名前的に取得したインスタンス)
        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }
        // 静的プロパティ $app が存在すれば、 $resolvedInstances 配列に
        // static::$app[$name] を格納し、その値を返却する
        // つまり インスタンスの取得 => 配列への格納 =>返却を行なっている
        if (static::$app) {
            return static::$resolvedInstance[$name] = static::$app[$name];
        }
    }
```

ここでは、`$resolvedInstance`については深く触れずに、最後のif文の処理に関して見ていきたいと思います。

### static::$appはどこからきた？
```php:Illuminate\Support\Facades\Facade.php
        if (static::$app) {
            return static::$resolvedInstance[$name] = static::$app[$name];
        }
```
上記if文で気軽に`static::$app`と気軽に呼び出しているが、これはどこからきたのでしょう。
プロパティを確認してみます。

```php:Illuminate\Support\Facades\Facade.php
    /**
     * The application instance being facaded.
     * 
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected static $app;
```
コンストラクタで初期化されるわけでもないので、どこかでセットされているはず、、、
`$app`で検索してみると、

```php:Illuminate\Support\Facades\Facade.php
    /**
     * Set the application instance.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public static function setFacadeApplication($app)
    {
        static::$app = $app;
    }
```
ここでセットされている！
一件落着である。
ちなみにこのセットをどこで呼び出しているかまでは自分は追えていないので、そのうち読んでいきたいです()

### static::$app[$name] とは
if文の条件は理解できたので、処理に移っていきます。

```php:Illuminate\Support\Facades\Facade.php
 return static::$resolvedInstance[$name] = static::$app[$name];
```

`static::$app[$name]`、、、、`$app`の中には何が入っているか確認してみましょう。

```php:Illuminate\Support\Facades\Facade.php
     /**
     * The application instance being facaded.
     * 
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected static $app;
```

`$app`はインスタンス、インスタンスを配列として扱っている、、、
`$app`の中身を見ていきましょう。

```php
<?php

namespace Illuminate\Contracts\Foundation;

use Illuminate\Contracts\Container\Container;

interface Application extends Container
{
//省略
}
```
はい、インターフェースです。
インターフェースがタイプヒントされているので、ここには`\Illuminate\Contracts\Foundation\Application`を実装したクラスがセットされます。
この部分では、`Illuminate\Foundation\Application.php`がセットされます。(自分もPHPDebugやディレクトリからの想像で確認したので、どこでセットされているかまでは確認できていません。機会があれば確認してみたいです。)

```php:Illuminate\Foundation\Application.php
<?php

namespace Illuminate\Foundation;

class Application extends Container implements ApplicationContract, HttpKernelInterface
{
//省略
}

```

Applicationクラスを見た感じ普通のクラスなので、親クラスのContainerクラスを見に行きます。

```php:Illuminate\Foundation\Container.php
<?php

namespace Illuminate\Container;

use ArrayAccess;

class Container implements ArrayAccess, ContainerContract
{
//　省略
}

```

`ArrayAccess`
怪しい名前のインターフェースをimplementsしてます。
ArrayAccessインターフェースに関しては下記参照ください！

https://www.php.net/manual/ja/class.arrayaccess.php


```php
interface ArrayAccess {
/* メソッド */
public offsetExists(mixed $offset): bool
public offsetGet(mixed $offset): mixed
public offsetSet(mixed $offset, mixed $value): void
public offsetUnset(mixed $offset): void
}
```

つまり、ArrayAccessインターフェースが強制する4つのメソッドを実装すれば、配列みたいに操作できるということがわかります。

つまり `static::$app[$name]` は、 `ArrayAccess::offsetGet`を実装した部分を確認すれば良いので、Applicationクラスを確認すると定義がないのでContainerクラスを参照します。

```php:Illuminate\Foundation\Container.php

    /**
     * Get the value at a given offset.
     * 与えられたoffset(配列のキー)の値を取得
     * @param  string  $key
     * @return mixed
     */
    public function offsetGet($key)// 元は static::$app[$name] で呼ばれているので、$name = 'log';
    {
        return $this->make($key);
    }

```

`$this->make('log')`です。
ちなみにここの`make()`はサービスコンテナの`make()`と同じもののはずなので、実質ここで終わりにしても良いかもしれません、、、()

https://readouble.com/laravel/7.x/ja/container.html


```php:Illuminate\Foundation\Container.php

    /**
     * Resolve the given type from the container.
     * コンテナから与えられたタイプを解決する
     * @param  string  $abstract
     * @param  array  $parameters
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function make($abstract, array $parameters = [])// $abstract = 'log'
    {
        return $this->resolve($abstract, $parameters);
    }

```

次は、`$this->resolve('log', [])`ですが、ここからの処理がまた少し長いので今回はここまでにしようと思います。
ついに`resolve()`というメソッドが出てきたので、いよいよ大詰め感があります、、、！

# まとめ
- Facadeクラス内での処理を追うことができた。
- FacadeクラスにApplicationクラスがセットされ、そのApplicationクラスのメソッドでインスタンスが解決されているぽいことがわかった。

# 次回
- resolve()を読んでいきたいと思います、、、！