LaravelでおてがるFacade作成

## ◆ファサードを作成してみる
・サービスコンテナにサービスプロバイダを利用してバインド(登録)する。
・サービスコンテナにバインドしたクラスを静的な形で使うためにそれ用のFacadeを継承したクラスを作成する。

### ・サービスコンテナにサービスプロバイダを利用してバインド(登録)する。
```
<?php

namespace App\Http\Services;

class A
{
    public function echoA(){
        return 'a';
    }
}
```

```
<?php

namespace App\Providers;

use App\Http\Services\A;
use Illuminate\Support\ServiceProvider;

class AServiceProvider extends ServiceProvider
{
    public function register()
    {
        //
    }

    public function boot()
    {
        $this->app->bind('a', function (){
            return new A();
        });
    }
}
```

```:config\app.php
providersに App\Providers\AServiceProvider::class
をプラスして、サービスコンテナに登録するよう仕向ける
```

### ・サービスコンテナにバインドしたクラスを静的な形で使うためにそれ用のFacadeを継承したクラスを作成する。

```
<?php
namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class A extends Facade {
    protected static function getFacadeAccessor() {
        return 'a';
    }
}
```

この親のFacadeクラスを見る

```
public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();

    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }

    return $instance->$method(...$args);
}
```

マジックメソッドの__callStaticは呼ばれた静的メソッドがなければ、このメソッドが自動発動する。
<b>$instance = static::getFacadeRoot();</b>　
少し省略するが、この中の部分でAServiceProviderで登録した(今回なら'a'でバインドしている)名前を呼ぶため、getFacadeAccessor()をよんで、'a'文字列をもってきて、さらにgetFacadeRootの中で、その名前でバインドしたクラスをよんでいる。(つまり、getFacadeAccessor()はオーバーライド必須となっている)

<b>return \$instance->\$method(...$args);</b>
でバインドしたクラスの引数メソッドをよんでいる。

今回なら、A::echoA();を呼ぶつもりなので、$methodに入るのは、echoA

```
Controllerの上でuse App\Facades\A;

メソッド内でdd(A::echoA());とすれば、'a'と出力される。
```

これで一応Facadeの作成は完了したが、既存のものはuse DB;とかで使えるようになっている。<b>今回のやつを、use A;で使えるようにする。</b>

<b>結論としては、config\app.phpのaliasに'A' => App\Facades\A::classを加えるだけ</b>

その理由を見ていく
## ◆facadeのalias
既存のFacadeがどうやって、aliasを登録しているのかを見る

```:index.php
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
```

このhandleメソッドで登録されているのでKernelクラスを見る。

```
use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{中身省略}
```

この親クラスのIlluminate\Foundation\Http\Kernelにhandleメソッドがあるので、親クラスを見る。

```
public function handle($request)
{
    try {
        $request->enableHttpMethodParameterOverride();

        $response = $this->sendRequestThroughRouter($request);
    } catch (Exception $e) {
        $this->reportException($e);

        $response = $this->renderException($request, $e);
    } catch (Throwable $e) {
        $this->reportException($e = new FatalThrowableError($e));

        $response = $this->renderException($request, $e);
    }

    $this->app['events']->dispatch(
        new Events\RequestHandled($request, $response)
    );

    return $response;
}
```

handleメソッド発見。この中の<b>\$response = \$this->sendRequestThroughRouter($request);</b>が重要なので、詳しく見る

```
protected function sendRequestThroughRouter($request)
{
    $this->app->instance('request', $request);

    Facade::clearResolvedInstance('request');

    $this->bootstrap();

    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
}
```

リクエストをミドルウェアを通して、ルーターに投げるんだけど、その途中で<b>$this->bootstrap();</b>でFacadeに使うやつの登録をしている。

```
public function bootstrap()
{
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers());
    }
}
```

ファサードがすでにサービスコンテナにboot(登録)されてるか調べて、されてなかったら登録
引数に渡してるのは、これ

```
protected $bootstrappers = [
    \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
    \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
    \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
    \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
    \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
    \Illuminate\Foundation\Bootstrap\BootProviders::class,
];
```

先ほどのbootstarapWith()を見る。

```
public function bootstrapWith(array $bootstrappers)
{
	//登録するからtrueに変える
    $this->hasBeenBootstrapped = true;

    foreach ($bootstrappers as $bootstrapper) {
        $this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]);

        $this->make($bootstrapper)->bootstrap($this);

        $this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]);
    }
}
```

このなかの<b>\$this->make(\$bootstrapper)->bootstrap(\$this);</b>
\$bootstrapperには、各クラスのnamespace + クラス
(例:\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class)が入っている。

app()->make()メソッドを呼ぶと、サービスコンテナにはバインド(登録)されていないが、各クラスの場所を入れているので、インスタンス化したものは取得でき、そのあとに各クラスメソッドは使えるので->bootstrap()をよんでいる。

bootstrappers配列の中の<b>\Illuminate\Foundation\Bootstrap\LoadConfiguration::class</b>のbootstrapメソッドを見る。

```
public function bootstrap(Application $app)
{
    $items = [];

    if (file_exists($cached = $app->getCachedConfigPath())) {
        $items = require $cached;

        $loadedFromCache = true;
    }

    $app->instance('config', $config = new Repository($items));

    if (! isset($loadedFromCache)) {
        $this->loadConfigurationFiles($app, $config);
    }

    $app->detectEnvironment(function () use ($config) {
        return $config->get('app.env', 'production');
    });

    date_default_timezone_set($config->get('app.timezone', 'UTC'));

    mb_internal_encoding('UTF-8');
}
```

<b>\$app->instance('config', \$config = new Repository(\$items));</b>で名前を'config'で、中身($items)は、bootstrap\cache\config.php内の設定を記述している配列をインスタンス結合している。これで今度から取り出しが楽。

<b>\$items</b>
<img width="139" alt="Screenshot_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/325600/e8263584-3308-a87c-86e5-15eb83fe6d18.png">


次に$bootstrappers配列の中の\Illuminate\Foundation\Bootstrap\RegisterFacades::classの中にFacade登録に関する記述があるので見ていく。

```
class RegisterFacades
{
    public function bootstrap(Application $app)
    {
        Facade::clearResolvedInstances();

        Facade::setFacadeApplication($app);
        
        AliasLoader::getInstance(array_merge(
            $app->make('config')->get('app.aliases', []),
            $app->make(PackageManifest::class)->aliases()
        ))->register();
    }
}
```

#### AliasLoader::getInstance()
AliasLoaderをインスタンス化して、引数の配列を自身の<b>$aliases</b>変数に格納する。
今回の場合だと、
<b>・先ほどインスタンス結合したので使える$app->make('config')の->get('app.aliases', [])で設定に登録されているalias内の配列</b>
<b>・$app->make(PackageManifest::class)->aliases()こっちはFormとHtmlファサード用(viewで使うもの)</b>
この2配列をマージして渡している。

<b>$app->make('config')の->get('app.aliases', [])</b>
<img width="261" alt="Screenshot_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/325600/467516db-1450-a1a9-c916-efc08d52dfe8.png">

```:そのあと、AliasLoaderクラスのregister()で登録している。
public function register()
{
    if (! $this->registered) {
        $this->prependToLoaderStack();

        $this->registered = true;
    }
}
```

```:prependToLoaderStack()を見る。
protected function prependToLoaderStack()
{
    spl_autoload_register([$this, 'load'], true, true);
}
```

spl_autoload_register関数は、第一引数のクラスがautoloadされていない場合に呼ばれる便利関数
http://akisi.tabiyaku.net/?p=545

今回の場合だと第一引数が配列なので、オートロードされていないクラスが呼ばれた瞬間、$this(自身)のloadメソッドを呼ぶようになっている。(ロードメソッドには、呼ばれた際の)
https://ts0818.hatenablog.com/entry/2015/09/25/213400

```:loadメソッドを見ると、
public function load($alias)
{
	//あればそのまま読み込む
    if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {
        $this->loadFacade($alias);

        return true;
    }

    if (isset($this->aliases[$alias])) {
        return class_alias($this->aliases[$alias], $alias);
    }
}
```

class_alias関数を使うと、第二引数の名前で第一引数のファサードごとの指定されている名前空間+クラスが呼ばれていることになる、
例：第一引数　Illuminate\Support\Facades\Auth::class 第二引数　Auth
https://php.plus-server.net/function.class-alias.html

なので、use Auth;としても使える。(今回の場合use A;)