search
HomePHP FrameworkLaravelDetailed explanation of Laravel examples: containers, inversion of control and dependency injection

This article brings you relevant knowledge about Laravel, which mainly introduces related issues about containers, control inversion and dependency injection. Let’s take a look at the related content. ,I hope everyone has to help.

Detailed explanation of Laravel examples: containers, inversion of control and dependency injection

Recommended learning: Getting started with Laravel

As the scale of current applications becomes larger and larger, the dependencies between objects also It becomes more and more complex, the degree of coupling is getting higher and higher, and there are often multiple dependencies between objects. For such a large and complex application, any modification may affect the whole body, which causes a lot of trouble for the later maintenance of the application.

 In order to solve the problem of high coupling between objects, the idea of ​​Inversion of Control (IoC) was also born. The so-called inversion of control is a design principle in object-oriented programming. Its purpose is to reduce the degree of coupling between codes. In Laravel, inversion of control is implemented through dependency injection (DI).

The basic idea of ​​inversion of control is to use the IoC container to decouple the dependencies between objects. After the introduction of the IoC container, control of all objects is handed over to the IoC container. The IoC container becomes the core of the entire system, gluing all objects together to function. Containers in Laravel serve this purpose.

⒈ Container

The so-called container in Laravel refers to the \Illuminate\Foundation\Application object, which is created when the Laravel framework starts.

# public/index.php
$app = require_once __DIR__.'/../bootstrap/app.php';
# bootstrap/app.php
$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

 During the process of creating a container, Laravel will also perform some basic binding and service registration for the container. Laravel will first bind the container instance to the app and Illuminate\Container\Container; then, Laravel will register basic service providers to the container instance, including event, log, and routing service providers; finally, Laravel will bind the framework The core class is registered with the container instance together with its corresponding alias.

// namespace Illuminate\Foundation\Application
public function __construct($basePath = null)
{
    if ($basePath) {
        $this->setBasePath($basePath);
    }
    $this->registerBaseBindings();
    $this->registerBaseServiceProviders();
    $this->registerCoreContainerAliases();
}
    
protected function registerBaseBindings()
{
    static::setInstance($this);
    $this->instance('app', $this);
    $this->instance(Container::class, $this);
    /* ... ... */
}
protected function registerBaseServiceProviders()
{
    $this->register(new EventServiceProvider($this));
    $this->register(new LogServiceProvider($this));
    $this->register(new RoutingServiceProvider($this));
}
public function registerCoreContainerAliases()
{
    foreach ([
        'app' => [self::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
        /* ... ...*/
        'db' => [\Illuminate\Database\DatabaseManager::class, \Illuminate\Database\ConnectionResolverInterface::class],
        'db.connection' => [\Illuminate\Database\Connection::class, \Illuminate\Database\ConnectionInterface::class],
        /* ... ... */
        'request' => [\Illuminate\Http\Request::class, \Symfony\Component\HttpFoundation\Request::class],
        'router' => [\Illuminate\Routing\Router::class, \Illuminate\Contracts\Routing\Registrar::class, \Illuminate\Contracts\Routing\BindingRegistrar::class],
        /* ... ... */
    ] as $key => $aliases) {
        foreach ($aliases as $alias) {
            $this->alias($key, $alias);
        }
    }
}
// namespace Illuminate\Container\Container
public function alias($abstract, $alias)
{
    if ($alias === $abstract) {
        throw new LogicException("[{$abstract}] is aliased to itself.");
    }
    $this->aliases[$alias] = $abstract;
    $this->abstractAliases[$abstract][] = $alias;
}

  After completing these three basic steps of registration, we can easily access the object instances that have been registered in the container. For example, the container itself can be accessed directly via $app['app'] or $app['Illuminate\Container\Container'] , and the database connection can be accessed directly via $app['db'] .

⒉ Registration of service providers and access to services

Registering service providers

 In the process of container creation, basic service providers will be registered , the registration process is completed by calling the register() method.

// namespace Illuminate\Foundation\Application
public function register($provider, $force = false)
{
    if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }
    if (is_string($provider)) {
        $provider = $this->resolveProvider($provider);
    }
    $provider->register();
    if (property_exists($provider, 'bindings')) {
        foreach ($provider->bindings as $key => $value) {
            $this->bind($key, $value);
        }
    }
    if (property_exists($provider, 'singletons')) {
        foreach ($provider->singletons as $key => $value) {
            $this->singleton($key, $value);
        }
    }
    $this->markAsRegistered($provider);
    if ($this->isBooted()) {
        $this->bootProvider($provider);
    }
    return $provider;
}

 Laravel will first determine whether the specified service provider has been registered in the container (implemented by calling the getProvider() method). If the specified service provider has been registered in the container, and this registration operation is not If forced execution, the registered service provider will be returned directly.

  If the above conditions are not met, Laravel will start to register the service provider. At this time, if the parameter is a string, Laravel will default to the class name of the service provider and instantiate it (through the resolveProvider() method). After that, the register() method defined by the service provider is called to register. Taking the log service provider as an example, the method body of its register() method is as follows

// namespace Illuminate\Log\LogServiceProvider
public function register()
{
    $this->app->singleton('log', function ($app) {
        return new LogManager($app);
    });
}

The function of the register() method is to register the Illuminate\Log\LogManager object into the container in singleton mode. The registration is completed. After that, an item will be added to the $bindings property of the container.

$app->bindings['log'] = [
    'concrete' => 'Illuminate\Log\LogManager {#162}',
    'shared' => true,
];

If the service provider itself also defines the $bindings property and the $singletons property, Laravel will also call the corresponding bind() method and singleton() Method to complete the registration of custom bindings from these service providers.

After this, Laravel will mark the service provider as registered, and then call the boot() method defined by the service provider to start the service provider (provided that the application has been started).

 When registering a binding in a container, there are two methods, bind() and singleton(). The only difference lies in whether the registered binding is in singleton mode, that is, whether the shared attribute is true.

// namespace Illuminate\Container\Container
public function singleton($abstract, $concrete = null)
{
    $this->bind($abstract, $concrete, true);
}
public function bind($abstract, $concrete = null, $shared = false)
{
    // 删除旧的绑定
    $this->dropStaleInstances($abstract);
    if (is_null($concrete)) {
        $concrete = $abstract;
    }
    if (! $concrete instanceof Closure) {
        if (! is_string($concrete)) {
            throw new TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');
        }
        $concrete = $this->getClosure($abstract, $concrete);
    }
    $this->bindings[$abstract] = compact('concrete', 'shared');
    if ($this->resolved($abstract)) {
        $this->rebound($abstract);
    }
}
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
        );
    };
}

  Still taking the log service provider as an example, the log service provider is registered in singleton mode when registering, and the $concrete parameter is a closure. Before binding begins, Laravel first removes the old binding. Since $concrete is a closure at this time, Laravel will not perform any operations, but only stores the binding information in the $bindings property.

Accessing the service

After the service provider registration is completed, we can access the service in the same way as accessing the database connection mentioned above. Still taking the log service as an example, we can access the log service through $app['log']. In addition, in Laravel, we can also use facade to access services. For example, we can call Illuminate\Support\Facades\Log::info() to record logs.

// namespace Illuminate\Support\Facades\Log
class Log extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'log';
    }
}
// namespace Illuminate\Support\Facades\Facade
public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();
    /* ... ... */
    return $instance->$method(...$args);
}
public static function getFacadeRoot()
{
    return static::resolveFacadeInstance(static::getFacadeAccessor());
}
protected static function resolveFacadeInstance($name)
{
    if (is_object($name)) {
        return $name;
    }
    if (isset(static::$resolvedInstance[$name])) {
        return static::$resolvedInstance[$name];
    }
    if (static::$app) {
        return static::$resolvedInstance[$name] = static::$app[$name];
    }
}

  在通过静态调用的方式进行日志记录时,首先会访问 Facade 中的魔术方法 __callStatic() ,该方法的首先进行的就是解析出 facade 对应的服务实例,然后调用该服务实例下的方法来执行相应的功能。每个 facade 中都会定义一个 getFacadeAccessor() 方法,这个方法会返回一个 tag,在日志服务中,这个 tag 就是日志服务提供者的闭包在容器的 $bindings 属性中的 key。也就是说,通过 facade 方式最终得到的是 $app['log']。

  那么为什么可以通过关联数组的方式访问容器中注册的对象/服务?Illuminate\Container\Container 实现了 ArrayAccess 并且定义了 OffsetGet() 方法,而 Illuminate\Foundation\Application 继承了 Container ,$app 为 Application 实例化的对象,所以通过关联数组的方式访问容器中注册的对象时会访问 Container 的 OffsetGet() 方法。在 OffsetGet() 方法中会调用 Container 的 make() 方法,而 make() 方法中又会调用 resolve() 方法。resolve() 方法最终会解析并返回相应的对象。

// namespace Illuminate\Container
public function offsetGet($key)
{
    return $this->make($key);
}
public function make($abstract, array $parameters = [])
{
    return $this->resolve($abstract, $parameters);
}
protected function resolve($abstract, $parameters = [], $raiseEvents = true)
{
    /* ... ... */
    $this->with[] = $parameters;
    if (is_null($concrete)) {
        $concrete = $this->getConcrete($abstract);
    }
    if ($this->isBuildable($concrete, $abstract)) {
        $object = $this->build($concrete);
    } else {
        $object = $this->make($concrete);
    }
    /* ... ... */
    $this->resolved[$abstract] = true;
    array_pop($this->with);
    return $object;
}
protected function getConcrete($abstract)
{
    if (isset($this->bindings[$abstract])) {
        return $this->bindings[$abstract]['concrete'];
    }
    return $abstract;
}
protected function isBuildable($concrete, $abstract)
{
    return $concrete === $abstract || $concrete instanceof Closure;
}
public function build($concrete)
{
    if ($concrete instanceof Closure) {
        return $concrete($this, $this->getLastParameterOverride());
    }
    /* ... ... */
}
protected function getLastParameterOverride()
{
    return count($this->with) ? end($this->with) : [];
}

  这里需要说明,在通过 $app['log'] 的方式解析日志服务实例时,resolve() 方法中的 $concrete 解析得到的是一个闭包,导致 isBuildable() 方法返回结果为 true,所以 Laravel 会直接调用 build() 方法。而由于此时 $concrete 是一个闭包,所以在 build() 方法中会直接执行这个闭包函数,最终返回 LogManager 实例。

⒊ 请求处理

  在基础的绑定和服务注册完成之后,容器创建成功并返回 $app 。之后 Laravel 会将内核(包括 Http 内核和 Console 内核)和异常处理注册到容器当中。然后 Laravel 开始处理请求。

// namespace bootstrap/app.php
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);
// public/index.php
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Request::capture()
)->send();
$kernel->terminate($request, $response);

  在开始处理请求之前,Laravel 首先会解析出 Http 内核对象 $kernel,即 App\Http\Kernel 实例化的对象。而 App\Http\Kernel 继承了 Illuminate\Foundation\Kernel,所以 $kernel 实际调用的是 Illuminate\Foundation\Kernel 中的 handle() 方法。

namespace Illuminate\Foundation\Http
use Illuminate\Contracts\Debug\ExceptionHandler
public function handle($request)
{
    try {
        $request->enableHttpMethodParameterOverride();
        $response = $this->sendRequestThroughRouter($request);
    } catch (Throwable $e) {
        $this->reportException($e);
        $response = $this->renderException($request, $e);
    }
    $this->app['events']->dispatch(
        new RequestHandled($request, $response)
    );
    return $response;
}
// 上报错误
protected function reportException(Throwable $e)
{
    $this->app[ExceptionHandler::class]->report($e);
}
// 渲染错误信息    
protected function renderException($request, Throwable $e)
{
    return $this->app[ExceptionHandler::class]->render($request, $e);
}

  handle() 方法在处理请求的过程中如果出现任何异常或错误,Laravel 都会调用容器中已经注册好的异常处理对象来上报异常并且渲染返回信息。

  在容器创建成功以后,Laravel 会将 Illuminate\Contracts\Debug\ExceptionHandler 和 App\Exceptions\Handler 之间的绑定注册到容器当中,所以 Laravel 处理异常实际调用的都是 App\Exceptions\Handler 中的方法。在实际开发过程中,开发者可以根据自身需要在 App\Exceptions\Handler 中自定义 report() 和 render() 方法。

在 PHP 7 中,`Exception` 和 `Error` 是两种不同的类型,但它们同时都继承了 `Throwable` ,所以 `handler()` 方法中捕获的是 `Throwable` 对象。

  在正式开始处理请求之前,Laravel 会进行一些引导启动,包括加载环境变量、配置信息等,这些引导启动在 Laravel 运行过程中起到了非常重要的作用。

// namespace Illuminate\Foundation\Http\Kernel
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,
];
protected function sendRequestThroughRouter($request)
{
    /* ... ... */
    $this->bootstrap();
    /* ... ... */
}
public function bootstrap()
{
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers());
    }
}
// namespace Illuminate\Foundation\Application
public function bootstrapWith(array $bootstrappers)
{
    $this->hasBeenBootstrapped = true;
    foreach ($bootstrappers as $bootstrapper) {
        $this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]);
        $this->make($bootstrapper)->bootstrap($this);
        $this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]);
    }
}

  从代码中可以看出,引导启动的过程实际就是调用各个 class 中的 bootstrap() 方法。其中:

LoadEnvironmentVariables 用来加载环境变量

LoadConfiguration 用来加载 config 目录下的配置文件

HandleExceptions 用来设置 PHP 的错误报告级别以及相应的异常和错误处理函数,另外还会设置 PHP 的程序终止执行函数

// namespace Illuminate\Foundation\Bootstrap\HandleExceptions
public function bootstrap(Application $app)
{
    /* ... ... */
    $this->app = $app;
    error_reporting(-1);
    set_error_handler([$this, 'handleError']);
    set_exception_handler([$this, 'handleException']);
    register_shutdown_function([$this, 'handleShutdown']);
    /* ... ... */
}
public function handleError($level, $message, $file = '', $line = 0, $context = [])
{
    if (error_reporting() & $level) {
        /* ... ... */
        throw new ErrorException($message, 0, $level, $file, $line);
    }
}
public function handleException(Throwable $e)
{
    /* ... ... */
        $this->getExceptionHandler()->report($e);
    /* ... ... */
}
public function handleShutdown()
{
    if (! is_null($error = error_get_last()) && $this->isFatal($error['type'])) {
        $this->handleException($this->fatalErrorFromPhpError($error, 0));
    }
}
protected function getExceptionHandler()
{
    return $this->app->make(\Illuminate\Contracts\Debug\ExceptionHandler::class);
}

  从以上代码中可以看出,虽然 HandleExceptions 中定义了异常、错误、程序终止的处理函数,但无论是哪种情况,最终还是调用 App\Exceptions\Handler 中的方法来处理异常或错误。

RegisterFacades 的作用一个是注册配置文件以及第三方包中自定义的 alias 类,还有一个非常重要的作用就是为 Illuminate\Support\Facades\Facade 类设置 $app 属性。

// namespace Illuminate\Foundation\Bootstrap\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();
}

&emsp 我们在通过 facade 方式反问容器中注册的服务时,Facade 在解析容器中的服务实例时用到的 static::$app 即是在这个时候设置的。

RegisterProviders 的作用是注册配置文件以及第三方包中定义的服务提供者

// namespace Illuminate\Foundation\Bootstrap\RegisterProviders
public function bootstrap(Application $app)
{
    $app->registerConfiguredProviders();
}
public function registerConfiguredProviders()
{
    $providers = Collection::make($this->make('config')->get('app.providers'))
                    ->partition(function ($provider) {
                        return strpos($provider, 'Illuminate\\') === 0;
                    });
    $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
    (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                ->load($providers->collapse()->toArray());
}

  在实际注册的过程中,Laravel 会按照 Laravel 框架的服务提供者 > 第三方包的服务提供者 > 开发者自定义的服务提供者 的顺序进行注册

BootProviders 则是按顺序调用已经注册到容器中的服务提供者的 boot() 方法(前提是服务提供者定义的 boot() 方法)

  在引导启动完成之后,Laravel 开始处理请求,首先要做的就是将全局的中间件应用于 request 。这之后 Laravel 会将请求分发到相应的路由进行处理,处理之前需要先根据 request 找到相应的路由对象 Illuminate\Routing\Route。在 Laravel 中,除了全局中间件,还有一些中间件只作用于特定的路由或路由分组,此时这些中间件就会被作用于 request 。这些工作都完成之后,路由对象开始执行代码,完成请求。

// namespace Illuminate\Foundation\Http\Kernel
protected function sendRequestThroughRouter($request)
{
    /* ... ... */
    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
}
protected function dispatchToRouter()
{
    return function ($request) {
        $this->app->instance('request', $request);
        
        return $this->router->dispatch($request);
    };
}
// namespace Illuminate\Routing\Router
public function dispatch(Request $request)
{
    $this->currentRequest = $request;
    return $this->dispatchToRoute($request);
}
public function dispatchToRoute(Request $request)
{
    return $this->runRoute($request, $this->findRoute($request));
}
protected function runRoute(Request $request, Route $route)
{
    /* ... ... */
    return $this->prepareResponse($request,
        $this->runRouteWithinStack($route, $request)
    );
}
protected function runRouteWithinStack(Route $route, Request $request)
{
    /* ... ... */   
    return (new Pipeline($this->container))
                    ->send($request)
                    ->through($middleware)
                    ->then(function ($request) use ($route) {
                        return $this->prepareResponse(
                            $request, $route->run()
                        );
                    });
}

⒋ 依赖注入

  Laravel 中的路由在注册时,action 可以是控制器方法,也可以是闭包。但无论是那种形式,都需要传参,而传参就会遇到需要依赖注入的情况。

  Route 对象在执行 run() 方法时会根据 action 的类型分别进行控制器方法调用或闭包函数的调用。但两种方法最终都需要解析参数,而如果参数中用到了 class ,就需要进行依赖注入。

// namespace Illuminate\Routing\Router
public function run()
{
    $this->container = $this->container ?: new Container;
    try {
        if ($this->isControllerAction()) {
            return $this->runController();
        }
        return $this->runCallable();
    } catch (HttpResponseException $e) {
        return $e->getResponse();
    }
}
protected function runController()
{
    return $this->controllerDispatcher()->dispatch(
        $this, $this->getController(), $this->getControllerMethod()
    );
}
protected function runCallable()
{
    /* ... ... */
    return $callable(...array_values($this->resolveMethodDependencies(
        $this->parametersWithoutNulls(), new ReflectionFunction($callable)
    )));
}
// namespace Illuminate\Routing\ControllerDispatcher
public function dispatch(Route $route, $controller, $method)
{
    $parameters = $this->resolveClassMethodDependencies(
        $route->parametersWithoutNulls(), $controller, $method
    );
    /* ... ... */
}
// namespace Illuminate\Routing\RouteDependencyResolverTrait
protected function resolveClassMethodDependencies(array $parameters, $instance, $method)
{
    /* ... ... */
    return $this->resolveMethodDependencies(
        $parameters, new ReflectionMethod($instance, $method)
    );
}
public function resolveMethodDependencies(array $parameters, ReflectionFunctionAbstract $reflector)
{
    /* ... ... */
    foreach ($reflector->getParameters() as $key => $parameter) {
        $instance = $this->transformDependency($parameter, $parameters, $skippableValue);
        /* ... ... */
    }
    return $parameters;
}
protected function transformDependency(ReflectionParameter $parameter, $parameters, $skippableValue)
{
    $className = Reflector::getParameterClassName($parameter);
    if ($className && ! $this->alreadyInParameters($className, $parameters)) {
        return $parameter->isDefaultValueAvailable() ? null : $this->container->make($className);
    }
    return $skippableValue;
}

  在执行过程中,Laravel 首先通过反射取得参数列表(对于控制器方法,使用 ReflectionMethod ,对于闭包函数,则使用 ReflectionFunction )。在得到参数列表后,Laravel 仍然是利用反射,逐个判断参数类型。如果参数类型为 PHP 的内置类型,那么不需要什么特殊处理;但如果参数不是 PHP 内置类型,则需要利用反射解析出参数的具体类型。在解析出参数的具体类型之后,紧接着会判断该类型的对象是不是已经存在于参数列表中,如果不存在并且该类型也没有设置默认值,那么就需要通过容器创建出该类型的实例。

  要通过容器创建指定 class 的实例,仍然需要用到 resolve() 方法。前文已经叙述过使用 resolve() 方法解析闭包函数的情况,所以这里值叙述实例化 class 的情况。

// namespace Illuminate\Container\Container
public function build($concrete)
{
    /* ... ... */
    try {
        $reflector = new ReflectionClass($concrete);
    } catch (ReflectionException $e) {
        throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
    }
    if (! $reflector->isInstantiable()) {
        return $this->notInstantiable($concrete);
    }
    $this->buildStack[] = $concrete;
    $constructor = $reflector->getConstructor();
    if (is_null($constructor)) {
        array_pop($this->buildStack);
        return new $concrete;
    }
    $dependencies = $constructor->getParameters();
    try {
        $instances = $this->resolveDependencies($dependencies);
    } catch (BindingResolutionException $e) {
        array_pop($this->buildStack);
        throw $e;
    }
    array_pop($this->buildStack);
    return $reflector->newInstanceArgs($instances);
}
protected function resolveDependencies(array $dependencies)
{
    $results = [];
    foreach ($dependencies as $dependency) {
        if ($this->hasParameterOverride($dependency)) {
            $results[] = $this->getParameterOverride($dependency);
            continue;
        }
        $result = is_null(Util::getParameterClassName($dependency))
                        ? $this->resolvePrimitive($dependency)
                        : $this->resolveClass($dependency);
        if ($dependency->isVariadic()) {
            $results = array_merge($results, $result);
        } else {
            $results[] = $result;
        }
    }
    return $results;
}

  容器在实例化 class 的时候,仍然是通过反射获取 class 基本信息。对于一些无法进行实例化的 class (例如 interface 、abstract class ),Laravel 会抛出异常;否则 Laravel 会继续获取 class 的构造函数的信息。对于不存在构造函数的 class ,意味着这些 class 在实例化的时候不需要额外的依赖,可以直接通过 new 来实例化;否则仍然是通过反射解析出构造函数的参数列表信息,然后逐个实例化这些参数列表中用到的 class 。在这些参数列表中的 class 都实例化完成之后,通过容器创建 class 的准备工作也已经完成,此时容器可以顺利创建出指定 class 的实例,然后注入到控制器方法或闭包中。

推荐学习:Laravel入门

The above is the detailed content of Detailed explanation of Laravel examples: containers, inversion of control and dependency injection. For more information, please follow other related articles on the PHP Chinese website!

Statement
This article is reproduced at:掘金. If there is any infringement, please contact admin@php.cn delete
laravel单点登录方法详解laravel单点登录方法详解Jun 15, 2022 am 11:45 AM

本篇文章给大家带来了关于laravel的相关知识,其中主要介绍了关于单点登录的相关问题,单点登录是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统,下面一起来看一下,希望对大家有帮助。

一起来聊聊Laravel的生命周期一起来聊聊Laravel的生命周期Apr 25, 2022 pm 12:04 PM

本篇文章给大家带来了关于laravel的相关知识,其中主要介绍了关于Laravel的生命周期相关问题,Laravel 的生命周期从public\index.php开始,从public\index.php结束,希望对大家有帮助。

laravel中guard是什么laravel中guard是什么Jun 02, 2022 pm 05:54 PM

在laravel中,guard是一个用于用户认证的插件;guard的作用就是处理认证判断每一个请求,从数据库中读取数据和用户输入的对比,调用是否登录过或者允许通过的,并且Guard能非常灵活的构建一套自己的认证体系。

laravel中asset()方法怎么用laravel中asset()方法怎么用Jun 02, 2022 pm 04:55 PM

laravel中asset()方法的用法:1、用于引入静态文件,语法为“src="{{asset(‘需要引入的文件路径’)}}"”;2、用于给当前请求的scheme前端资源生成一个url,语法为“$url = asset('前端资源')”。

实例详解laravel使用中间件记录用户请求日志实例详解laravel使用中间件记录用户请求日志Apr 26, 2022 am 11:53 AM

本篇文章给大家带来了关于laravel的相关知识,其中主要介绍了关于使用中间件记录用户请求日志的相关问题,包括了创建中间件、注册中间件、记录用户访问等等内容,下面一起来看一下,希望对大家有帮助。

laravel中间件基础详解laravel中间件基础详解May 18, 2022 am 11:46 AM

本篇文章给大家带来了关于laravel的相关知识,其中主要介绍了关于中间件的相关问题,包括了什么是中间件、自定义中间件等等,中间件为过滤进入应用的 HTTP 请求提供了一套便利的机制,下面一起来看一下,希望对大家有帮助。

laravel的fill方法怎么用laravel的fill方法怎么用Jun 06, 2022 pm 03:33 PM

在laravel中,fill方法是一个给Eloquent实例赋值属性的方法,该方法可以理解为用于过滤前端传输过来的与模型中对应的多余字段;当调用该方法时,会先去检测当前Model的状态,根据fillable数组的设置,Model会处于不同的状态。

laravel路由文件在哪个目录里laravel路由文件在哪个目录里Apr 28, 2022 pm 01:07 PM

laravel路由文件在“routes”目录里。Laravel中所有的路由文件定义在routes目录下,它里面的内容会自动被框架加载;该目录下默认有四个路由文件用于给不同的入口使用:web.php、api.php、console.php等。

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Tools

mPDF

mPDF

mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

MantisBT

MantisBT

Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

Integrate Eclipse with SAP NetWeaver application server.

Atom editor mac version download

Atom editor mac version download

The most popular open source editor

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.