为什么Container :: getInstance()返回一个应用程序类

I am I'm wondering why the Container::getInstance() can return a application class.

For example:

I want to make a hash str, I want to know how they work:

app('hash')->make('password');

and I found the source code in laravel :

vendor/laravel/framework/src/Illuminate/Foundation/helpers.php

if (! function_exists('app')) {
    /**
     * Get the available container instance.
     *
     * @param  string  $make
     * @param  array   $parameters
     * @return mixed|\Illuminate\Foundation\Application
     */
    function app($make = null, $parameters = [])
    {
        if (is_null($make)) {
            return Container::getInstance();
        }

        return Container::getInstance()->make($make, $parameters);

    }
}

I dont know what the Container::getInstance() will return, then I dd(Container::getInstance()) and I know it will can return an application class, but I dont know how they work.

Maybe I'm a little bit late with my answer, but anyway.

Description is current as of Laravel framework version 5.3.24.

Why calling app(), that then calls Container::getInstance() returns object, instance of Application? For example, why

Route::get('/', function () {
    var_dump(app());
});

outputs:

object(Illuminate\Foundation\Application)
...

Because... And here we have to go step by step though it to understand everything.

  1. User initiates a web request. The request is processed by /public/index.php
  2. /public/index.php contains the following:

    $app = require_once __DIR__.'/../bootstrap/app.php';
    
  3. /bootstrap/app.php has the following lines:

    $app = new Illuminate\Foundation\Application(
        realpath(__DIR__.'/../')
    );
    
  4. When an $app object is instantiated, the Illuminate\Foundation\Application class constructor is called.

/vendor/laravel/framework/src/Illuminate/Foundation/Application.php

    class Application extends Container implements ...
    {
        // ...
        public function __construct($basePath = null)
        {
            // 5. constructor triggers the following method:
            $this->registerBaseBindings();
            // ...
        }
        // ...
        protected function registerBaseBindings()
        {
            // 6. which then triggers the following:
            static::setInstance($this);
            // 7. Important! $this points to class Application here
            // and is passed to Container
            // ...
        }
        // ...
    }
  1. static::setInstance($this); refers us to class Container, because class Application extends Container

/vendor/laravel/framework/src/Illuminate/Container/Container.php

    class Container implements ...
    {
        // ...
        // 11. $instance now contains an object,
        // which is an instance of Application class
        protected static $instance;
        // ...
        public static function setInstance(ContainerContract $container = null)
        {
            // 9. $container = Application here, because it has been passed
            // from class Application while calling static::setInstance($this);
            // 10. Thus, static::$instance is set to Application here
            return static::$instance = $container;
        }
        // ...
    }

  1. Now, suppose, we have written the following lines in our routes file. /routes/web.php

    Route::get('/', function () {
        dd(app()); // 13. We a calling an app() helper function
    });
    

14 Calling app() leads us to /vendor/laravel/framework/src/Illuminate/Foundation/helpers.php

    // ...
    /** @return mixed|\Illuminate\Foundation\Application */
    function app($make = null, $parameters = [])
    {
        // 15. $make is null, so this is the case
        if (is_null($make)) {
            // 16. The following static method is called:
            return Container::getInstance();
        }
        // ...
    }
    // ...
  1. Now we are back in our Container class /vendor/laravel/framework/src/Illuminate/Container/Container.php

    public static function getInstance()
    {
        // 18. Important!
        // To this point static::$instance is NOT null,
        // because it has already been set (up to "step 11").
        if (is_null(static::$instance)) {
            static::$instance = new static; // Thus, we skip this.
        }
        // 19. static::$instance is returned
        // that contains an object,
        // which is an instance of Application class
        return static::$instance;
    }
    

Some important notes for steps 16-19.

Important note 1!

Static Keyword

Declaring class properties or methods as static makes them accessible without needing an instantiation of the class.

Important note 2!

static::$instance = new static; is NOT related to calling our app() function in step 13. And was somewhat misleading for me at first...

But just to note, it makes use of Late Static Bindings.

The Application class (Illuminate\Foundation\Application) extends the Container class. This is the core of the framework and allows all the dependency injection magic, and it follows a Sigleton pattern, this means that when you request the Application object (with the app() helper function, or more internally Container::getInstance()) anywhere in your code you get the same global instance.

Without getting to complex: this let's you bind any class into that Container instance:

app()->bind('MyModule', new MyModuleInstace());

So then you can "resolve" that class out of the container with:

app()->make('MyModule);

But remember there are several methods to do this, with different pattern targets, this is just a basic demonstration of the concept.