Laravel 5.5 source notes (3. facade)

Last time I said provider, this time I said facade

The first is the source of startup. From the $bootstrappers array in the kernel class of laravel, we can see some of its system boot methods. The registerfacies is used to register the facade class.

    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,
    ];

 

There is also a register class to register aliases

namespace Illuminate\Foundation\Bootstrap;

use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Facades\Facade;
use Illuminate\Foundation\PackageManifest;
use Illuminate\Contracts\Foundation\Application;

class RegisterFacades
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        //Eliminate facade All bound instances
        Facade::clearResolvedInstances();
        //take app Deposit object
        Facade::setFacadeApplication($app);
        //Store these alias arrays in the object through the constructor
        AliasLoader::getInstance(array_merge(
            //adopt Illuminate\Config\Repository Obtain config/app.php Medium aliases array
            $app->make('config')->get('app.aliases', []),
            //Do you remember the list of packages mentioned in the initial blog post, from which we also get its aliases
            $app->make(PackageManifest::class)->aliases()
        //adopt spl_autoload_register Function register another auto load function
        ))->register();
    }
}
    public static function getInstance(array $aliases = [])
    {
        //When registering, the instance is empty, and the alias array just passed in will be stored into the object through the construction method
        if (is_null(static::$instance)) {
            return static::$instance = new static($aliases);
        }

        $aliases = array_merge(static::$instance->getAliases(), $aliases);

        static::$instance->setAliases($aliases);

        return static::$instance;
    }

    private function __construct($aliases)
    {
        $this->aliases = $aliases;
    }
    public function register()
    {
        if (! $this->registered) {
            $this->prependToLoaderStack();

            $this->registered = true;
        }
    }

    /**
     * Prepend the load method to the auto-loader stack.
     *
     * @return void
     */
    protected function prependToLoaderStack()
    {
        //Finally, the automatic loading function is used to load the load Method is put into the front end of the automatic load queue. When we call a method through the class name, the automatic load function queue will be triggered. This function will be triggered first. Find the path of the corresponding file and load the corresponding file
        spl_autoload_register([$this, 'load'], true, true);
    }
    public function load($alias)
    {
        //If the namespace passed in is not current facade The array will be loaded through this method
        if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {
            $this->loadFacade($alias);

            return true;
        }
        //Otherwise, return its alias directly
        if (isset($this->aliases[$alias])) {
            return class_alias($this->aliases[$alias], $alias);
        }
    }

    /**
     * Load a real-time facade for the given alias.
     *
     * @param  string  $alias
     * @return void
     */
    protected function loadFacade($alias)
    {
        require $this->ensureFacadeExists($alias);
    }

    /**
     * Ensure that the given alias has an existing real-time facade class.
     *
     * @param  string  $alias
     * @return string
     */
    protected function ensureFacadeExists($alias)
    {
        //Return path from cache
        if (file_exists($path = storage_path('framework/cache/facade-'.sha1($alias).'.php'))) {
            return $path;
        }
        
        file_put_contents($path, $this->formatFacadeStub(
            $alias, file_get_contents(__DIR__.'/stubs/facade.stub')
        ));

        return $path;
    }

Because the implementation process of composer in laravel is too tedious, we won't go deep here. The principle is the same. Find the class name of the namespace through the alias, and then map the class name of composer with the file path. The auto load function finds the corresponding file to load in.

 

So let's try to define a facade class

1. Create a new facade class. The returned test is the object defined in the previous blog

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class Test extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'App\Contracts\Test';
    }
}

2. Add it to aliases array of config/app.php

'aliases' => [

        'App' => Illuminate\Support\Facades\App::class,
        'Artisan' => Illuminate\Support\Facades\Artisan::class,
        'Auth' => Illuminate\Support\Facades\Auth::class,
        'Blade' => Illuminate\Support\Facades\Blade::class,
        'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
        'Bus' => Illuminate\Support\Facades\Bus::class,
        'Cache' => Illuminate\Support\Facades\Cache::class,
        'Config' => Illuminate\Support\Facades\Config::class,
        'Cookie' => Illuminate\Support\Facades\Cookie::class,
        'Crypt' => Illuminate\Support\Facades\Crypt::class,
        'DB' => Illuminate\Support\Facades\DB::class,
        'Eloquent' => Illuminate\Database\Eloquent\Model::class,
        'Event' => Illuminate\Support\Facades\Event::class,
        'File' => Illuminate\Support\Facades\File::class,
        'Gate' => Illuminate\Support\Facades\Gate::class,
        'Hash' => Illuminate\Support\Facades\Hash::class,
        'Lang' => Illuminate\Support\Facades\Lang::class,
        'Log' => Illuminate\Support\Facades\Log::class,
        'Mail' => Illuminate\Support\Facades\Mail::class,
        'Notification' => Illuminate\Support\Facades\Notification::class,
        'Password' => Illuminate\Support\Facades\Password::class,
        'Queue' => Illuminate\Support\Facades\Queue::class,
        'Redirect' => Illuminate\Support\Facades\Redirect::class,
        'Redis' => Illuminate\Support\Facades\Redis::class,
        'Request' => Illuminate\Support\Facades\Request::class,
        'Response' => Illuminate\Support\Facades\Response::class,
        'Route' => Illuminate\Support\Facades\Route::class,
        'Schema' => Illuminate\Support\Facades\Schema::class,
        'Session' => Illuminate\Support\Facades\Session::class,
        'Storage' => Illuminate\Support\Facades\Storage::class,
        'URL' => Illuminate\Support\Facades\URL::class,
        'Validator' => Illuminate\Support\Facades\Validator::class,
        'View' => Illuminate\Support\Facades\View::class,
        //Just created facades
        'Test' => \App\Facades\Test::class,
    ],

3. Call it in the routing file.

Route::any('/index', function (){
    Test::doing();
});

We will find that even if we don't make through app objects, there is no introduction of any namespace, so it is called.

If it is called in a class, it will be a little more troublesome. It needs the alias of ues.

namespace App\Http\Controllers;

use Illuminate\Http\Request;
//use App\Contracts\Test;
//Note that only aliases are introduced here, because of namespace conflicts, so I annotated other parts
use Test;

class IndexController extends Controller
{
//    public function __construct(Test $test)
//    {
//        $this->test = $test;
//    }

    public function index(Test $test)
    {
        Test::doing();
//        app()->make('App\Contracts\Test')->doing();
//        echo '<br>';
//
//        //The when binding of a contract can only be triggered by automatically loading dependencies through construction methods
//        $this->test->doing();
//
//        echo '<br>';
//        //Because the context binding in laravel can only be specific to classes, the $test instance here is still a normal binding
//        $test->doing();

    }
}

Through the demo practice, the facade is relatively simple.

Tags: PHP Laravel Redis Session

Posted on Tue, 10 Dec 2019 10:40:46 -0800 by jonoc33