Source code analysis of laravel application layer loading process

Time:2022-6-22

Source code analysis of laravel application layer loading process

Source code analysis of laravel application layer execution process

Laravel is an elegant PHP web development framework. It is efficient, concise and expressive. MVC design is a full stack framework that advocates development efficiency. Is the most popular PHP framework.

Therefore, I think the design idea of the laravel framework itself is worth learning by each PHPer. Here, I know and understand the process of laravel in the application layer through a handwritten simple framework. The main contents include:Container, application framework, kernel, contract, entry file, service provider, routing, request, facade, controller and auxiliary functions. A picture of the implementation process is attached at the end of the article for your reference.

Container: a tool that performs dependency injection and manages dependencies

Two main things have been done:

① [binding] object, instance (shared instance), interface (interface to implementation) and closure are added to the container array
② [resolve] bound abstractions (resolve dependency injection in specific classes through reflection mechanism), that is, create instances

The whole framework provides dependencies around this container, so it is equivalent to a heart, and it is also a heart of laravel. The source code is posted here, but not later.

<?php
/**
 * [Description]
 *
 * @Author  leeprince:2020-03-11 19:28
 */
class Container
{
    //An array bound to a container, equivalent to bindings and $aliases
    protected $bind = [];

    //Current instance, single instance creation
    protected static $instance;

    //Array of shared instances bound to the container
    protected $instances = [];
    
    //Array of parameter overlay stacks
    protected $with = [];

    /**
     *[bind to container array]
     *
     * @Author  leeprince:2020-03-11 19:36
     * @param $abstract
     * @param $concrete
     */
    public function bind($abstract, $concrete = null)
    {
        //Bind itself
        if (is_null($concrete)) {
            $concrete = $abstract;
        }

        $this->bind[$abstract] = $concrete;
    }

    /**
     *[verify if bound to container]
     *
     * @Author  leeprince:2020-03-12 01:15
     */
    public function has($abstract)
    {
        return isset($this->bind[$abstract]);
    }

    /**
     *[bind to shared instance]
     *
     * @Author  leeprince:2020-03-12 01:17
     * @param $abstract
     * @param $instance
     */
    public function instance($abstract, $instance)
    {
        if (isset($this->bind[$abstract])) {
            unset($this->bind[$abstract]);
        }

        $this->instances[$abstract] = $instance;
    }

    /**
     *[get current instance]
     *
     * @Author  leeprince:2020-03-12 01:18
     * @return Container
     */
    public static function getInstance()
    {
        if (is_null(static::$instance)) {
            static::$instance = new static;
        }
        return static::$instance;
    }

    /**
     *[set current instance]
     *
     * @Author  leeprince:2020-03-12 01:19
     * @param $container
     * @return mixed
     */
    public static function setInstance($container)
    {
        return static::$instance = $container;
    }

    /**
     *[create instance]
     *
     *In laravel is the resolve method
     *
     * @Author  leeprince:2020-03-12 01:20
     * @param $abstract
     * @return mixed
     * @throws Exception
     */
    public function make($abstract, array $parameter = [])
    {
        //Determine whether there is a direct return in the shared instance
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }
        
        $this->with[] = $parameter;
    
        //Re optimized version: continue to recursively resolve the dependency injection in specific classes through the reflection mechanism: just refer to the IOC section!
        if (isset($this->bind[$abstract])) {
            $concrete = $this->bind[$abstract];
            
            //If the specific implementation is a closure, you can directly execute the closure without binding to the shared instance, because the closure function itself is not an instance.
            //There are three ways to determine whether a closure is required. Obviously the first is more professional
            if ($concrete instanceof Closure) {
            // if (is_object($concrete)) {
            // if (is_callable($concrete)) {
                return $concrete();
            }
    
            /**
             *There are two versions
             */
            
            /**Version 1: This is a simple version that does not consider class based dependency injection*/
            // return $this->instances[$abstract] = (empty($parameter))? new $concrete() : new $concrete(...$parameter);
    
            /**Version 2: consider the optimized version of class based dependency injection through reflection mechanism*/
            //All information of the concrete is reflected through the reflection class
            $reflection = new ReflectionClass($concrete);
            if (! $reflection->isInstantiable()) {
                Throw new exception ('judge that this class cannot be instantiated after reflection ');
            }
    
            //Get constructor
            $constructor = $reflection->getConstructor();
            if (is_null($constructor)) {
                $object = $reflection->newInstance();
    
            } else {
                //Get constructor参数
                $depenen = $constructor->getParameters();
                $instances = $this->getDependencies($depenen);
    
                $object = $reflection->newInstanceArgs($instances);
            }
            
            /**
             *Delete the last record of the array whose parameter overrides the stack.
             *Because this may be recursively resolved class based dependency injection,
             *If not deleted, the parameter array overwritten by the parameters in the current resolution will not be obtained in the previous step of recursion
             */
            array_pop($this->with);
            
            return $this->instances[$abstract] = $object;
            
        }
        
        Throw new exception ('instance'.$abstract not found);
    }
    
    /**
     *[continue to instantiate the object according to the type prompt parameter in the constructor (dependency injection: class based dependency injection or non class based original dependency injection)]
     *
     * @Author  leeprince:2020-03-15 13:12
     * @param array $depenen
     * @return array
     * @throws Exception
     */
    public function getDependencies(array $dependencies)
    {
        $results = [];
        foreach ($dependencies as $key => $dependency) {
            //Determines whether a given dependency has a parameter override
            if ($this->hasParameterOverride($dependency)) {
                $results[] = $this->getParameterOverride($dependency);
                continue;
            }
    
            //Consider whether the constructor parameters are class based dependency injection
            $results[] = is_null($dependency->getClass())
                ? $this->resolvePrimitive($dependency, $key)
                : $this->resolveClass($dependency);
        }
        
        return $results;
    }
    
    /**
     *[resolve non class based primitive dependencies]
     *
     * @Author  leeprince:2020-03-15 15:47
     * @param ReflectionParameter $parameter
     * @param $key
     * @return mixed
     * @throws ReflectionException
     */
    public function resolvePrimitive(ReflectionParameter $parameter, $key)
    {
        if ($parameter->isDefaultValueAvailable()) {
            return $parameter->getDefaultValue();
        }
        
        return null;
    }
    
    /**
     *[resolve class based dependencies in container]
     *
     * @Author  leeprince:2020-03-15 15:29
     * @param ReflectionParameter $parameter
     * @return mixed
     * @throws Exception
     */
    public function resolveClass(ReflectionParameter $parameter)
    {
        return $this->make($parameter->getClass()->name, []);
    }
    
    
    /**
     *[determines whether a given dependency has a parameter override.]
     *
     * @Author  leeprince:2020-03-15 16:17
     * @param $dependency
     * @return bool
     */
    protected function hasParameterOverride(ReflectionParameter $dependency)
    {
        return array_key_exists(
            $dependency->name, $this->getLastParameterOverride()
        );
    }
    
    /**
     *[get parameter overrides for dependencies.]
     *
     * @Author  leeprince:2020-03-15 16:18
     * @param ReflectionParameter $dependency
     * @return mixed
     */
    protected function getParameterOverride(ReflectionParameter $dependency)
    {
        return $this->getLastParameterOverride()[$dependency->name];
    }
    
    /**
     *[get last parameter override]
     *
     * @Author  leeprince:2020-03-15 16:23
     * @return array|mixed
     */
    protected function getLastParameterOverride()
    {
        return count($this->with) ? end($this->with) : [];
    }
}

application program

It mainly does two things: register the basic binding in the constructor and register the basic service

  1. Register basic binding in the constructor to the container shared instance array: bind itself to the app abstraction; Bind itself to abstract from a container class
  2. Register basic services in the constructor to the container binding array: including but not limited to: routing object, request object, log contract interface to file log implementation class, file log service object, MySQL log service object

kernel

  1. The kernel class injects the dependency (application framework (container)) through the constructor
  2. And resolve the routing object in the constructor to the attributes of the kernel
  3. Pass the request object into the request distribution method of the routing object (resolving the properties of the routing object to the kernel in the constructor)

Contract: a contract is a set of interfaces, and each contract has a corresponding implementation provided by the framework

Container contract
Kernel contract
Service provider contract
-Log contract

Entry file

I have mainly done five things, and the execution sequence is also in accordance with the following. Of course, it can be adjusted, but for better understanding, I just arranged it this way

  1. Instantiate application framework
  2. Perform routing configuration through the static methods of the routing facade class. Just check the corresponding contents of the facade for the specific implementation process
  3. Interface to implementation of binding kernel contract
  4. Resolve the kernel contract object interface (that is, obtain the instantiated object of the specific implementation class that implements the contract interface)
  5. Call the method that the request object sets the request address into the request object’s properties and returns the request object
  6. Pass the request object into the request processing method of the kernel

Service provider

Here we mainly introduce the concept of service. The design idea is different from that of laravel on the implementation of service providers. Friends who are interested in this can read the source code or leave a message for discussion.

File log service class: constraint method for implementing contract interface
MySQL log service class: constraint method for implementing contract interface

Routing object

Three main things have been done

  1. Add the routing configuration item to the routing table of the routing object
  2. The request distribution method performs the method of finding the routing table
  3. Execute the route matching the request, that is, execute the method under the controller or execute the closure immediately. The example introduces the method flow of the execution controller, so that you can better understand the concepts of service providers and auxiliary functions

Request object

Set the request address into the properties of the request object and return the request object

Facade: the underlying classes available in the service container of the “static proxy” application by defining the abstract classes of the facade

Abstract class of facade

  1. Call specific objects (routes, requests…) Facade class of, execute__ Callstatic() magic method dynamically calls static objects
  2. Get the facade object name that inherits the abstract facade class
  3. By obtaining the name of the facade, resolve the base class of the facade to be statically proxied from the bound class library of the container, and return
  4. Pass in variable parameters and dynamically execute the methods of the specific object

controller

  1. Execute the corresponding service of the controller’s method.
  2. The example here provides a service with database connection, which can be used together with the application service container auxiliary function and call the service method

auxiliary function

Application service container auxiliary function: directly return to the service container or parse the corresponding abstract instance according to the parameter judgment

Execution process

The order of execution shall be in Arabic numerals As a sequential reading

Source code analysis of laravel application layer loading process

Source address

https://github.com/leeprince/my_pattern/tree/master/my_minilaravel

This framework is just for everyone to understand and familiarize themselves with the loading process of laravel in the application layer. Of course, the laravel framework itself has many core architectures, such as service providers, pipelines, and middleware. I will add them when I have time

Welcome to leave a message and discuss with us ~

Source code analysis of laravel application layer loading process

Recommended Today

P3120 [USACO15FEB]Cow Hopscotch G

portal ideas A simple idea is a\(O(n^2m^2)\)transfer of: \[f_{i,j}=\sum_{x=1}^{i-1}\sum_{y=1}^{j-1}f_{x,y}*[a_{i,j}!=a_{x,y}] \] There are so many constraints, thinking about using cdq divide and conquer to optimize we consider theRowdivide and conquer\([l,mid]\) Then brute force enumerationList,use\([l,mid]\)to update\((mid,r]\) enumerate columns each time\(j\)After the transfer, the\(\sum_{i=l}^{mid}f_{i,j}\)add to\(sum\), and update\(s[a_{i,j}]\)(used to subtract the contribution of the same number) The equation for […]