routes/web.phpが何をしているのかを読み解く

# まえがき
##追記
輪読会にてバシバシに間違っている点指摘もらったので、5/7訂正、追記を行いました。
## 前提
https://qiita.com/carrotRakko/items/3e6d19a7bd9427ab6479
上記の記事を読んであることを前提に話を進めます

また、筆者はプログラミング初心者であり、前回、前々回ではsingletonやfacade,kernel等が出てきて概念というよりは用語自体に**？？？**となったレベルです
##背景
上記の記事にもありますがLarevel輪読会のレジュメです
前回`Illuminate\Container\Container`の`resolve()`メソッドが呼び出されている所までたどり着きました。
#今回の目標
今回はその後を追っていき、実際に`web.php`で

```php:web.php
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});
```
**実際にこの処理で結果的に何が行われるか**まで追えれば最高です~~(全く自信ありませんが)~~

##resolve()は何をやっているのか
 `resolve()`メソッドは以下です

```php:vendor/laravel/framework/src/Illuminate/Container/Container.php
/**
     * Resolve the given type from the container.
     *
     * @param  string  $abstract
     * @param  array  $parameters
     * @param  bool  $raiseEvents
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    protected function resolve($abstract, $parameters = [], $raiseEvents = true)
    {
        $abstract = $this->getAlias($abstract);

        $concrete = $this->getContextualConcrete($abstract);

        $needsContextualBuild = ! empty($parameters) || ! is_null($concrete);

        // If an instance of the type is currently being managed as a singleton we'll
        // just return an existing instance instead of instantiating new instances
        // so the developer can keep using the same objects instance every time.
        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
            return $this->instances[$abstract];
        }

        $this->with[] = $parameters;

        if (is_null($concrete)) {
            $concrete = $this->getConcrete($abstract);
        }

        // We're ready to instantiate an instance of the concrete type registered for
        // the binding. This will instantiate the types, as well as resolve any of
        // its "nested" dependencies recursively until all have gotten resolved.
        if ($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete);
        } else {
            $object = $this->make($concrete);
        }

        // If we defined any extenders for this type, we'll need to spin through them
        // and apply them to the object being built. This allows for the extension
        // of services, such as changing configuration or decorating the object.
        foreach ($this->getExtenders($abstract) as $extender) {
            $object = $extender($object, $this);
        }

        // If the requested type is registered as a singleton we'll want to cache off
        // the instances in "memory" so we can return it later without creating an
        // entirely new instance of an object on each subsequent request for it.
        if ($this->isShared($abstract) && ! $needsContextualBuild) {
            $this->instances[$abstract] = $object;
        }

        if ($raiseEvents) {
            $this->fireResolvingCallbacks($abstract, $object);
        }

        // Before returning, we will also set the resolved flag to "true" and pop off
        // the parameter overrides for this build. After those two things are done
        // we will be ready to return back the fully constructed class instance.
        $this->resolved[$abstract] = true;

        array_pop($this->with);

        return $object;
    }
```

よくわからないので上から追っていくと
`$abstract = $this->getAlias($abstract);`
は

```php:vendor/laravel/framework/src/Illuminate/Container/Container.php
/**
     * Get the alias for an abstract if available.
     *
     * @param  string  $abstract
     * @return string
     */
    public function getAlias($abstract)
    {
        if (! isset($this->aliases[$abstract])) {
            return $abstract;
        }

        return $this->getAlias($this->aliases[$abstract]);
    }
```

~~より`aliases`は空配列なので~~`aliases`のキーに`router`はないのでそのまま`$abstract`を返します

```php:vendor/laravel/framework/src/Illuminate/Container/Container.phpから抜粋
/**
     * Get the contextual concrete binding for the given abstract.
     *
     * @param  string  $abstract
     * @return \Closure|string|array|null
     */
    protected function getContextualConcrete($abstract)
    {
        if (! is_null($binding = $this->findInContextualBindings($abstract))) {
            return $binding;
        }

        // Next we need to see if a contextual binding might be bound under an alias of the
        // given abstract type. So, we will need to check if any aliases exist with this
        // type and then spin through them and check for contextual bindings on these.
        if (empty($this->abstractAliases[$abstract])) {
            return;
        }

        foreach ($this->abstractAliases[$abstract] as $alias) {
            if (! is_null($binding = $this->findInContextualBindings($alias))) {
                return $binding;
            }
        }
    }

```

今回はどこにも該当しないので`null`が帰ってきます
ここで`resolve()`に戻ると`$needsContextualBuild`は`false`となります
次のif文はすっ飛ばして`$this->with[] = $parameters;`に行きますがこれは
`$parameters`が空配列なので特に関係ないです
よって次の

```php:
if (is_null($concrete)) {
            $concrete = $this->getConcrete($abstract);
        }
```
が何をしてるのかを追います。`getConcrete`は

```php:vendor/laravel/framework/src/Illuminate/Container/Container.php
/**
     * Get the concrete type for a given abstract.
     *
     * @param  string  $abstract
     * @return mixed
     */
    protected function getConcrete($abstract)
    {
        // If we don't have a registered resolver or concrete for the type, we'll just
        // assume each type is a concrete name and will attempt to resolve it as is
        // since the container should be able to resolve concretes automatically.
        if (isset($this->bindings[$abstract])) {
            return $this->bindings[$abstract]['concrete'];
        }

        return $abstract;
    }
```

ここで`bindings`???何それってなります
なので`bindings`に何かを入れているとこを探します
##`bindings`とはなんぞや
前回の内容を踏まえて最初から追っていくと

```php:bootstrap/app.php

$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
```
ここっぽいなと思い追ってみることにしました。`new`してるので`__construct`を追います

```php:Illuminate\Foundation\Application
/**
     * Create a new Illuminate application instance.
     *
     * @param  string|null  $basePath
     * @return void
     */
    public function __construct($basePath = null)
    {
        if ($basePath) {
            $this->setBasePath($basePath);
        }

        $this->registerBaseBindings();
        $this->registerBaseServiceProviders();
        $this->registerCoreContainerAliases();
    }
```
ここから全部をみるべきなんですが、今回`router`登録しているのはこれです
`$this->registerBaseServiceProviders();`
そして同クラス内にあります

```php
    /**
     * Register all of the base service providers.
     *
     * @return void
     */
    protected function registerBaseServiceProviders()
    {
        $this->register(new EventServiceProvider($this));
        $this->register(new LogServiceProvider($this));
        $this->register(new RoutingServiceProvider($this));
    }
```
というわけでまず、`new`されている方を追いましょうか。`router`関係のものがコンストラクトされているのはこれです`new RoutingServiceProvider($this)`
`use Illuminate\Routing\RoutingServiceProvider;`とあるので該当クラスに飛びます
`register`を追いましょう。これも同クラス内にあって

```php:Illuminate\Routing\RoutingServiceProvider.php
/**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerRouter();
        $this->registerUrlGenerator();
        $this->registerRedirector();
        $this->registerPsrRequest();
        $this->registerPsrResponse();
        $this->registerResponseFactory();
        $this->registerControllerDispatcher();
    }
```
今回`router`を登録しているのは
`$this->registerRouter();`これです。同クラス内にあって

```php
    /**
     * Register the router instance.
     *
     * @return void
     */
    protected function registerRouter()
    {
        $this->app->singleton('router', function ($app) {
            return new Router($app['events'], $app);
        });
    }
```

(一部ですが)`bindings`の中身がわかったとこで
`getConcrete`に戻ると

```php
if (isset($this->bindings[$abstract])) {
            return $this->bindings[$abstract]['concrete'];
        }

```
とあるので先ほど作成した連想配列の `$absteact`に`['concrete']`を追加したものを返します次は

```php
// We're ready to instantiate an instance of the concrete type registered for
        // the binding. This will instantiate the types, as well as resolve any of
        // its "nested" dependencies recursively until all have gotten resolved.
        if ($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete);
        } else {
            $object = $this->make($concrete);
        }
```
ここを追います。同クラスにありました

```php
/**
     * Determine if the given concrete is buildable.
     *
     * @param  mixed  $concrete
     * @param  string  $abstract
     * @return bool
     */
    protected function isBuildable($concrete, $abstract)
    {
        return $concrete === $abstract || $concrete instanceof Closure;
    }
```
以上のように`singleton`は
ここで`$concrete`)は`Closure`のインスタンスなので
`$object = $this->build($concrete);`となります
よって`build`を追います
これまた、同クラス内にあって一部抜粋すると

```php:vendor/laravel/framework/src/Illuminate/Container/Container.phpのbuild()
 // If the concrete type is actually a Closure, we will just execute it and
        // hand back the results of the functions, which allows functions to be
        // used as resolvers for more fine-tuned resolution of these objects.
        if ($concrete instanceof Closure) {
           return $concrete($this, $this->getLastParameterOverride());
        }
```
とあるので
`$object = $concrete($this, $this->getLastParameterOverride())`となります
よって今回の`$concrete`には`router`という文字列が入っているので
先ほど登録したクロージャが呼ばれて
`$object`には`new Router`が返されます。
以上より`web.php`にて`Route`が呼ばれたときは
`Router`のインスタンスが返されることが無事追い切れました。
次回以降は

```
・Illuminate\Routing\RouteServiceProvider クラスの registerRouter() メソッドで $this->app->singleton() の第2引数に渡している Closure が実行されたときの処理
・routes/web.php で Route::get(‘/’, function () { … }) が実行されたとき、すなわち Illuminate\Routing\Router クラスのシングルトンの get(‘/’, function () { … }) メソッドが実行されたときの処理
```
を追っていきます。

##`singleton`って何してるの？
少し深掘りになりますが`singleton`について追ってみました
`Illuminate\Foundation\Application`を追ってみても`singleton`は無かったので
継承元の`Illuminate\Container\Container`に戻ってみました。

```php:vendor/laravel/framework/src/Illuminate/Container/Container.php
/**
     * Register a shared binding in the container.
     *
     * @param  string  $abstract
     * @param  \Closure|string|null  $concrete
     * @return void
     */
    public function singleton($abstract, $concrete = null)
    {
        $this->bind($abstract, $concrete, true);
    }
```
ありました。
というわけで`bind`を追います

```php
/**
     * Register a binding with the container.
     *
     * @param  string  $abstract
     * @param  \Closure|string|null  $concrete
     * @param  bool  $shared
     * @return void
     */
    public function bind($abstract, $concrete = null, $shared = false)
    {
        $this->dropStaleInstances($abstract);

        // If no concrete type was given, we will simply set the concrete type to the
        // abstract type. After that, the concrete type to be registered as shared
        // without being forced to state their classes in both of the parameters.
        if (is_null($concrete)) {
            $concrete = $abstract;
        }

        // If the factory is not a Closure, it means it is just a class name which is
        // bound into this container to the abstract type and we will just wrap it
        // up inside its own Closure to give us more convenience when extending.
        if (! $concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }

        $this->bindings[$abstract] = compact('concrete', 'shared');

        // If the abstract type was already resolved in this container we'll fire the
        // rebound listener so that any objects which have already gotten resolved
        // can have their copy of the object updated via the listener callbacks.
        if ($this->resolved($abstract)) {
            $this->rebound($abstract);
        }
    }
```
と、同クラス上にありました。とりあえずここです

```php
// If the factory is not a Closure, it means it is just a class name which is
        // bound into this container to the abstract type and we will just wrap it
        // up inside its own Closure to give us more convenience when extending.
        if (! $concrete instanceof Closure) {
            
            $concrete = $this->getClosure($abstract, $concrete);
        }
```
これまた同クラス上にありました

```php
    /**
     * Get the Closure to be used when building a type.
     *
     * @param  string  $abstract
     * @param  string  $concrete
     * @return \Closure
     */
    protected function getClosure($abstract, $concrete)
    {
        return function ($container, $parameters = []) use ($abstract, $concrete) {
            if ($abstract == $concrete) {
                return $container->build($concrete);
            }

            return $container->resolve(
                $concrete, $parameters, $raiseEvents = false
            );
        };
    }
```



` $this->bindings[$abstract] = compact('concrete', 'shared');`です。
これにより `$this->bindings[$abstract]`は`'concrete'`と`'shared'`をキーと変数に持った連想配列が入っています。

#後書き
PHPの知見が浅い筆者の当日の発表はボロボロでしたが、エンジニアの先輩方に教えてもらい
結果的には一通り追い切ることができました。感謝感謝です。
ここが輪読会のメリットであり面白いところなのではと感じました。
精力的にやっていきたいですね。