摘要:年月日阶段划分请求到响应的整个执行阶段归纳为个程序启动准备阶段文件自动加载服务容器实例化基础服务提供者的注册核心类的实例化请求实例化阶段实例化实例请求处理阶段准备请求处理的环境将请求实例通过中间件处理及通过路由和控制器的分发控制响应发送和
Last-Modified: 2019年5月10日16:19:07
阶段划分Laravel 5.5
请求到响应的整个执行阶段归纳为 4 个:
程序启动准备阶段
文件自动加载
服务容器实例化
基础服务提供者的注册
核心类的实例化
请求实例化阶段
实例化 Request 实例
请求处理阶段
准备请求处理的环境
将请求实例通过中间件处理 及 通过路由和控制器的分发控制
响应发送和程序终止阶段
将响应内容返回给客户端
记录与客户端有关的信息等
1. 程序启动准备程序入口在 index.php 中
require __DIR__."/../vendor/autoload.php"; $app = require_once __DIR__."/../bootstrap/app.php"; # 获取服务容器实例 $kernel = $app->make(IlluminateContractsHttpKernel::class); $response = $kernel->handle( $request = IlluminateHttpRequest::capture() ); $response->send(); $kernel->terminate($request, $response);
创建服务容器实例
服务容器的创建在 bootstrapapp.php 中进行.
$app = new IlluminateFoundationApplication( realpath(__DIR__."/../") );1.1 容器基础配置
容器 Application 的构造函数:
public function __construct($basePath = null) { if ($basePath) { $this->setBasePath($basePath); } $this->registerBaseBindings(); $this->registerBaseServiceProviders(); $this->registerCoreContainerAliases(); }
构造函数 主要完成以下基本配置:
目录路径(绑定到容器中, 并提供类方法获取子目录)
public function setBasePath($basePath) { $this->basePath = rtrim($basePath, "/"); $this->bindPathsInContainer(); return $this; } protected function bindPathsInContainer() { $this->instance("path", $this->path()); $this->instance("path.base", $this->basePath()); $this->instance("path.lang", $this->langPath()); $this->instance("path.config", $this->configPath()); $this->instance("path.public", $this->publicPath()); $this->instance("path.storage", $this->storagePath()); $this->instance("path.database", $this->databasePath()); $this->instance("path.resources", $this->resourcePath()); $this->instance("path.bootstrap", $this->bootstrapPath()); }
绑定容器自身
protected function registerBaseBindings() { static::setInstance($this); $this->instance("app", $this); $this->instance(Container::class, $this); $this->instance(PackageManifest::class, new PackageManifest( new Filesystem, $this->basePath(), $this->getCachedPackagesPath() )); }
基础服务注册( Event, Log, Route)
protected function registerBaseServiceProviders() { $this->register(new EventServiceProvider($this)); $this->register(new LogServiceProvider($this)); $this->register(new RoutingServiceProvider($this)); }
别名注册
多个接口名 对应一个简短别名, 后续在注册服务时只需绑定到别名上即可 (而不必绑定到具体接口名)
public function registerCoreContainerAliases() { foreach ([ "app" => [IlluminateFoundationApplication::class, IlluminateContractsContainerContainer::class, IlluminateContractsFoundationApplication::class, PsrContainerContainerInterface::class], "auth" => [IlluminateAuthAuthManager::class, IlluminateContractsAuthFactory::class], "auth.driver" => [IlluminateContractsAuthGuard::class], "blade.compiler" => [IlluminateViewCompilersBladeCompiler::class], "cache" => [IlluminateCacheCacheManager::class, IlluminateContractsCacheFactory::class], "cache.store" => [IlluminateCacheRepository::class, IlluminateContractsCacheRepository::class], "config" => [IlluminateConfigRepository::class, IlluminateContractsConfigRepository::class], "cookie" => [IlluminateCookieCookieJar::class, IlluminateContractsCookieFactory::class, IlluminateContractsCookieQueueingFactory::class], "encrypter" => [IlluminateEncryptionEncrypter::class, IlluminateContractsEncryptionEncrypter::class], "db" => [IlluminateDatabaseDatabaseManager::class], "db.connection" => [IlluminateDatabaseConnection::class, IlluminateDatabaseConnectionInterface::class], "events" => [IlluminateEventsDispatcher::class, IlluminateContractsEventsDispatcher::class], "files" => [IlluminateFilesystemFilesystem::class], "filesystem" => [IlluminateFilesystemFilesystemManager::class, IlluminateContractsFilesystemFactory::class], "filesystem.disk" => [IlluminateContractsFilesystemFilesystem::class], "filesystem.cloud" => [IlluminateContractsFilesystemCloud::class], "hash" => [IlluminateContractsHashingHasher::class], "translator" => [IlluminateTranslationTranslator::class, IlluminateContractsTranslationTranslator::class], "log" => [IlluminateLogWriter::class, IlluminateContractsLoggingLog::class, PsrLogLoggerInterface::class], "mailer" => [IlluminateMailMailer::class, IlluminateContractsMailMailer::class, IlluminateContractsMailMailQueue::class], "auth.password" => [IlluminateAuthPasswordsPasswordBrokerManager::class, IlluminateContractsAuthPasswordBrokerFactory::class], "auth.password.broker" => [IlluminateAuthPasswordsPasswordBroker::class, IlluminateContractsAuthPasswordBroker::class], "queue" => [IlluminateQueueQueueManager::class, IlluminateContractsQueueFactory::class, IlluminateContractsQueueMonitor::class], "queue.connection" => [IlluminateContractsQueueQueue::class], "queue.failer" => [IlluminateQueueFailedFailedJobProviderInterface::class], "redirect" => [IlluminateRoutingRedirector::class], "redis" => [IlluminateRedisRedisManager::class, IlluminateContractsRedisFactory::class], "request" => [IlluminateHttpRequest::class, SymfonyComponentHttpFoundationRequest::class], "router" => [IlluminateRoutingRouter::class, IlluminateContractsRoutingRegistrar::class, IlluminateContractsRoutingBindingRegistrar::class], "session" => [IlluminateSessionSessionManager::class], "session.store" => [IlluminateSessionStore::class, IlluminateContractsSessionSession::class], "url" => [IlluminateRoutingUrlGenerator::class, IlluminateContractsRoutingUrlGenerator::class], "validator" => [IlluminateValidationFactory::class, IlluminateContractsValidationFactory::class], "view" => [IlluminateViewFactory::class, IlluminateContractsViewFactory::class], ] as $key => $aliases) { foreach ($aliases as $alias) { $this->alias($key, $alias); } } }1.2 核心类绑定
$app->singleton( IlluminateContractsHttpKernel::class, AppHttpKernel::class ); $app->singleton( IlluminateContractsConsoleKernel::class, AppConsoleKernel::class ); $app->singleton( IlluminateContractsDebugExceptionHandler::class, AppExceptionsHandler::class );
绑定重要接口:
Http 核心类
命令行 核心类
异常处理类
1.3 实例化 Http 核心类$kernel = $app->make(IlluminateContractsHttpKernel::class);
Http 核心类的构造函数
public function __construct(Application $app, Router $router) { $this->app = $app; $this->router = $router; $router->middlewarePriority = $this->middlewarePriority; foreach ($this->middlewareGroups as $key => $middleware) { $router->middlewareGroup($key, $middleware); } foreach ($this->routeMiddleware as $key => $middleware) { $router->aliasMiddleware($key, $middleware); } }
上述过程主要做的事是将中间件赋值给路由
中间件顺序优先级列表
中间件组
中间件别名
核心类 app/Http/Kernel.php
[ // Cookie 加密解密 AppHttpMiddlewareEncryptCookies::class, // 将 Cookie 添加到响应中 IlluminateCookieMiddlewareAddQueuedCookiesToResponse::class, // 开启会话 IlluminateSessionMiddlewareStartSession::class, // 认证用户,此中间件以后 Auth 类才能生效 // 见:https://d.laravel-china.org/docs/5.5/authentication IlluminateSessionMiddlewareAuthenticateSession::class, // 将系统的错误数据注入到视图变量 $errors 中 IlluminateViewMiddlewareShareErrorsFromSession::class, // 检验 CSRF ,防止跨站请求伪造的安全威胁 // 见:https://d.laravel-china.org/docs/5.5/csrf AppHttpMiddlewareVerifyCsrfToken::class, // 处理路由绑定 // 见:https://d.laravel-china.org/docs/5.5/routing#route-model-binding IlluminateRoutingMiddlewareSubstituteBindings::class, ], // API 中间件组,应用于 routes/api.php 路由文件 "api" => [ // 使用别名来调用中间件 // 请见:https://d.laravel-china.org/docs/5.5/middleware#为路由分配中间件 "throttle:60,1", "bindings", ], ]; // 中间件别名设置,允许你使用别名调用中间件,例如上面的 api 中间件组调用 protected $routeMiddleware = [ // 只有登录用户才能访问,我们在控制器的构造方法中大量使用 "auth" => IlluminateAuthMiddlewareAuthenticate::class, // HTTP Basic Auth 认证 "auth.basic" => IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class, // 处理路由绑定 // 见:https://d.laravel-china.org/docs/5.5/routing#route-model-binding "bindings" => IlluminateRoutingMiddlewareSubstituteBindings::class, // 用户授权功能 "can" => IlluminateAuthMiddlewareAuthorize::class, // 只有游客才能访问,在 register 和 login 请求中使用,只有未登录用户才能访问这些页面 "guest" => AppHttpMiddlewareRedirectIfAuthenticated::class, // 访问节流,类似于 『1 分钟只能请求 10 次』的需求,一般在 API 中使用 "throttle" => IlluminateRoutingMiddlewareThrottleRequests::class, ]; }2. 请求实例化
以处理 Http 请求为例
index.php 入口文件
$response = $kernel->handle( $request = IlluminateHttpRequest::capture() );
请求是通过 IlluminateHttpRequest::capture() 实例化的, 主要是将请求信息以对象形式表现出来
3. 请求处理入口文件:
$response = $kernel->handle( $request = IlluminateHttpRequest::capture() );
$kernel->handle(...) 处理请求过程
IlluminateFoundationHttpKernel
public function handle($request) { try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); $response = $this->renderException($request, $e); } catch (Throwable $e) { $this->reportException($e = new FatalThrowableError($e)); $response = $this->renderException($request, $e); } $this->app["events"]->dispatch( new EventsRequestHandled($request, $response) ); return $response; } protected function sendRequestThroughRouter($request) { $this->app->instance("request", $request); Facade::clearResolvedInstance("request"); $this->bootstrap(); # 核心类初始化 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); }; }
实际处理请求逻辑主要在 sendRequestThroughRouter 方法中, 它主要做了:
核心类的初始化
经由中间件过滤后将请求最终交由 Router 处理
3.1 请求处理环境初始化对于 Http 请求处理, 中间件包括:
protected $middleware = [ IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class, IlluminateFoundationHttpMiddlewareValidatePostSize::class, AppHttpMiddlewareTrimStrings::class, IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::class, AppHttpMiddlewareTrustProxies::class, ];该中间件数组定义在 Http 核心类中, 同时在核心类的构造函数中传递给 Router 类
核心类的初始化 bootstrap()
protected $bootstrappers = [ IlluminateFoundationBootstrapLoadEnvironmentVariables::class, IlluminateFoundationBootstrapLoadConfiguration::class, IlluminateFoundationBootstrapHandleExceptions::class, IlluminateFoundationBootstrapRegisterFacades::class, IlluminateFoundationBootstrapRegisterProviders::class, IlluminateFoundationBootstrapBootProviders::class, ]; # 初始化 public function bootstrap() { if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); } } protected function bootstrappers() { return $this->bootstrappers; }
在服务容器 Application 类中
public function bootstrapWith(array $bootstrappers) { $this->hasBeenBootstrapped = true; foreach ($bootstrappers as $bootstrapper) { $this["events"]->fire("bootstrapping: ".$bootstrapper, [$this]); $this->make($bootstrapper)->bootstrap($this); $this["events"]->fire("bootstrapped: ".$bootstrapper, [$this]); } }
该步骤主要是主要是对核心类中定义的 $bootstrappers 数组元素(引导类)初始化.
bootstrap 过程具体是在服务容器来中进行, 由核心类调用并传入待初始化的类
Http 核心类默认包含以下 6 个启动服务:
1. 环境监测 IlluminateFoundationBootstrapLoadEnvironmentVariables::class从 .env 文件中解析环境变量到 getevn(), $_ENV, $_SERVER
依赖 vlucas/phpdotenv 扩展包2. 配置加载 IlluminateFoundationBootstrapLoadConfiguration::class
载入 config 目录下所有 php 配置文件, 并将生成的配置存储类绑定到服务容器 $app["config"]
同时配置时区及 多字节格式(utf8)
3. 常处理 IlluminateFoundationBootstrapHandleExceptions::class报告所有错误 error_report(E_ALL)
提供对未捕获的异常, 错误的全局处理 set_error_handler, set_exception_handler, register_shutdown_function
4. 外观注册 IlluminateFoundationBootstrapRegisterFacades::class从 app.aliases 中读取外观配置数组
"aliases" => [ "App" => IlluminateSupportFacadesApp::class, "Artisan" => IlluminateSupportFacadesArtisan::class, "Auth" => IlluminateSupportFacadesAuth::class, "Blade" => IlluminateSupportFacadesBlade::class, "Broadcast" => IlluminateSupportFacadesBroadcast::class, "Bus" => IlluminateSupportFacadesBus::class, "Cache" => IlluminateSupportFacadesCache::class, "Config" => IlluminateSupportFacadesConfig::class, "Cookie" => IlluminateSupportFacadesCookie::class, "Crypt" => IlluminateSupportFacadesCrypt::class, "DB" => IlluminateSupportFacadesDB::class, "Eloquent" => IlluminateDatabaseEloquentModel::class, "Event" => IlluminateSupportFacadesEvent::class, "File" => IlluminateSupportFacadesFile::class, "Gate" => IlluminateSupportFacadesGate::class, "Hash" => IlluminateSupportFacadesHash::class, "Lang" => IlluminateSupportFacadesLang::class, "Log" => IlluminateSupportFacadesLog::class, "Mail" => IlluminateSupportFacadesMail::class, "Notification" => IlluminateSupportFacadesNotification::class, "Password" => IlluminateSupportFacadesPassword::class, "Queue" => IlluminateSupportFacadesQueue::class, "Redirect" => IlluminateSupportFacadesRedirect::class, "Redis" => IlluminateSupportFacadesRedis::class, "Request" => IlluminateSupportFacadesRequest::class, "Response" => IlluminateSupportFacadesResponse::class, "Route" => IlluminateSupportFacadesRoute::class, "Schema" => IlluminateSupportFacadesSchema::class, "Session" => IlluminateSupportFacadesSession::class, "Storage" => IlluminateSupportFacadesStorage::class, "URL" => IlluminateSupportFacadesURL::class, "Validator" => IlluminateSupportFacadesValidator::class, "View" => IlluminateSupportFacadesView::class, ],
使用 spl_autoload_register(...) 处理类加载, 配合 class_alias() 提供类的别名调用
Facade 外观类基类依赖 __callStatic` 调用方法( 使用服务容器实例化对应类)
5. 服务提供者注册 IlluminateFoundationBootstrapRegisterProviders::class从 app.providers 中读取所有服务提供者
"providers" => [ /* * Laravel Framework Service Providers... */ IlluminateAuthAuthServiceProvider::class, IlluminateBroadcastingBroadcastServiceProvider::class, IlluminateBusBusServiceProvider::class, IlluminateCacheCacheServiceProvider::class, IlluminateFoundationProvidersConsoleSupportServiceProvider::class, IlluminateCookieCookieServiceProvider::class, IlluminateDatabaseDatabaseServiceProvider::class, IlluminateEncryptionEncryptionServiceProvider::class, IlluminateFilesystemFilesystemServiceProvider::class, IlluminateFoundationProvidersFoundationServiceProvider::class, IlluminateHashingHashServiceProvider::class, IlluminateMailMailServiceProvider::class, IlluminateNotificationsNotificationServiceProvider::class, IlluminatePaginationPaginationServiceProvider::class, IlluminatePipelinePipelineServiceProvider::class, IlluminateQueueQueueServiceProvider::class, IlluminateRedisRedisServiceProvider::class, IlluminateAuthPasswordsPasswordResetServiceProvider::class, IlluminateSessionSessionServiceProvider::class, IlluminateTranslationTranslationServiceProvider::class, IlluminateValidationValidationServiceProvider::class, IlluminateViewViewServiceProvider::class, /* * Package Service Providers... */ /* * Application Service Providers... */ AppProvidersAppServiceProvider::class, AppProvidersAuthServiceProvider::class, // AppProvidersBroadcastServiceProvider::class, AppProvidersEventServiceProvider::class, AppProvidersRouteServiceProvider::class, # 路由表生成 ],
服务提供者经过解析后分为 3 种类型的服务提供者:
eager 类型
马上调用 register 注册
deferred 类型
记录下来, 当服务容器解析对应服务时, 才注册对应的服务提供者
when 类型
记录下来, 当对应 event 触发时在注册对应服务提供者
6. 启动提供者 IlluminateFoundationBootstrapBootProviders::class调用服务容器的 boot() 方法, 依次调用在服务容器中 register 的所有服务提供者的 boot() 方法
3.2 路由处理请求在内核处理请求, 将请求实例通过中间件处理后, 将请求的处理交给路由 Router 进行控制器的分发.
Http Kernel
protected function dispatchToRouter() { return function ($request) { $this->app->instance("request", $request); return $this->router->dispatch($request); }; }
路由表存储结构说明
IlluminateRoutingRoute 存储单条路由IlluminateRoutingRouteCollection 保存所有 Route 实例, 形成路由表
IlluminateRoutingRouter 类实例持有 RouteCollection 路由表实例.
即, 一个 Router 持有一个 RouteCollection, 而 RouteCollection 拥有 N 个 Route
在 Router 中对请求的处理同样经过一系列的 路由中间件
# 路由处理请求的入库 public function dispatchToRoute(Request $request) { return $this->runRoute($request, $this->findRoute($request)); } # 根据请求的 url 和 method 查找对应的 route protected function findRoute($request) { $this->current = $route = $this->routes->match($request); $this->container->instance(Route::class, $route); return $route; } # 根据对应的请求和路由条目, 返回相应的 $response protected function runRoute(Request $request, Route $route) { $request->setRouteResolver(function () use ($route) { return $route; }); $this->events->dispatch(new EventsRouteMatched($route, $request)); return $this->prepareResponse($request, $this->runRouteWithinStack($route, $request) ); } # 请求经过路由中间件过滤后, 交由 route 的 run() 方法处理 protected function runRouteWithinStack(Route $route, Request $request) { $shouldSkipMiddleware = $this->container->bound("middleware.disable") && $this->container->make("middleware.disable") === true; $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route); return (new Pipeline($this->container)) ->send($request) ->through($middleware) ->then(function ($request) use ($route) { return $this->prepareResponse( $request, $route->run() ); }); }
route 的 run() 方法最终将请求转给 IlluminateRoutingControllerDispatcher::dispatch 处理
public function dispatch(Route $route, $controller, $method) { $parameters = $this->resolveClassMethodDependencies( $route->parametersWithoutNulls(), $controller, $method ); if (method_exists($controller, "callAction")) { return $controller->callAction($method, $parameters); } return $controller->{$method}(...array_values($parameters)); }
剩下的事情就是 Controller控制器 的事了.
3.3 处理返回的 Response在 Router 中有一个方法, 用于对返回的 $response 进行处理
public function prepareResponse($request, $response) { return static::toResponse($request, $response); } /** * @return IlluminateHttpResponse|IlluminateHttpJsonResponse */ public static function toResponse($request, $response) { if ($response instanceof Responsable) { $response = $response->toResponse($request); } if ($response instanceof PsrResponseInterface) { $response = (new HttpFoundationFactory)->createResponse($response); } elseif (! $response instanceof SymfonyResponse && ($response instanceof Arrayable || $response instanceof Jsonable || $response instanceof ArrayObject || $response instanceof JsonSerializable || is_array($response))) { $response = new JsonResponse($response); } elseif (! $response instanceof SymfonyResponse) { $response = new Response($response); } if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) { $response->setNotModified(); } return $response->prepare($request); # 最后的处理 }
上述过程中, 在返回 $response 之前进行了最后的处理 $response->prepare($request)
该过程是在 SymfonyComponentHttpFoundationResponse::prepare() 中进行
对响应的封装是通过 IlluminateHttpResponse 类完成, 该类底层是 Symfony 框架的 Response 类即, SymfonyComponentHttpFoundationResponse
public function prepare(Request $request) { $headers = $this->headers; if ($this->isInformational() || $this->isEmpty()) { $this->setContent(null); $headers->remove("Content-Type"); $headers->remove("Content-Length"); } else { // Content-type based on the Request if (!$headers->has("Content-Type")) { $format = $request->getRequestFormat(); if (null !== $format && $mimeType = $request->getMimeType($format)) { $headers->set("Content-Type", $mimeType); } } // Fix Content-Type $charset = $this->charset ?: "UTF-8"; if (!$headers->has("Content-Type")) { $headers->set("Content-Type", "text/html; charset=".$charset); } elseif (0 === stripos($headers->get("Content-Type"), "text/") && false === stripos($headers->get("Content-Type"), "charset")) { // add the charset $headers->set("Content-Type", $headers->get("Content-Type")."; charset=".$charset); } // Fix Content-Length if ($headers->has("Transfer-Encoding")) { $headers->remove("Content-Length"); } if ($request->isMethod("HEAD")) { // cf. RFC2616 14.13 $length = $headers->get("Content-Length"); $this->setContent(null); if ($length) { $headers->set("Content-Length", $length); } } } // Fix protocol if ("HTTP/1.0" != $request->server->get("SERVER_PROTOCOL")) { $this->setProtocolVersion("1.1"); } // Check if we need to send extra expire info headers if ("1.0" == $this->getProtocolVersion() && false !== strpos($this->headers->get("Cache-Control"), "no-cache")) { $this->headers->set("pragma", "no-cache"); $this->headers->set("expires", -1); } $this->ensureIEOverSSLCompatibility($request); return $this; }4. 响应发送和程序终止 4.1 响应的发送
在 index.php 入口文件的最后是将响应返回给客户端
$response->send();
SymfonyComponentHttpFoundationResponse
public function send() { $this->sendHeaders(); $this->sendContent(); if (function_exists("fastcgi_finish_request")) { fastcgi_finish_request(); } elseif (!in_array(PHP_SAPI, array("cli", "phpdbg"), true)) { static::closeOutputBuffers(0, true); } return $this; } public function sendHeaders() { // headers have already been sent by the developer if (headers_sent()) { return $this; } // headers foreach ($this->headers->allPreserveCase() as $name => $values) { foreach ($values as $value) { header($name.": ".$value, false, $this->statusCode); } } // status header(sprintf("HTTP/%s %s %s", $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); return $this; } public function sendContent() { echo $this->content; return $this; }4.2 请求中止
在 index.php 入口文件的最后:
$kernel->terminate($request, $response);
依旧以 Http Kernel 为例:
public function terminate($request, $response) { $this->terminateMiddleware($request, $response); # 中间件中止处理 $this->app->terminate(); # 服务容器的中止处理函数 } protected function terminateMiddleware($request, $response) { $middlewares = $this->app->shouldSkipMiddleware() ? [] : array_merge( $this->gatherRouteMiddleware($request), $this->middleware ); foreach ($middlewares as $middleware) { if (! is_string($middleware)) { continue; } list($name) = $this->parseMiddleware($middleware); $instance = $this->app->make($name); if (method_exists($instance, "terminate")) { $instance->terminate($request, $response); } } }
此处的中间件指的是定义在 Kernel 中的 $middleware 中间件数组列表, 不包含 路由中间件.
Laravel 5.1 注: 默认只有会话中间件包含 terminate() 函数
Application 服务容器的中止处理函数
public function terminate() { foreach ($this->terminatingCallbacks as $terminating) { $this->call($terminating); } }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/31436.html
摘要:但是不用担心,我们后续的教程会陆续讲解相关的内容。所以上面的路由注册其实就是负责解决访问的时候的响应。 原文来自:https://jellybool.com/post/programming-with-laravel-5-routers-views-controllers-workflow 免费视频教程地址 https://laravist.com/series/laravel-5-b...
摘要:直到所有中间件都执行完毕,最后在执行最后的即上述的方法如果上述有地方难懂的,可以参考这边文章内置函数在中的使用以上是在通过全局中间件时的大致流程,通过中间件和路由中间件也是一样的,都是采用管道流操作,详情可翻阅源码 简介 Laravel 中间件提供了一种方便的机制来过滤进入应用的 HTTP 请求, 如ValidatePostSize用来验证POST请求体大小、ThrottleReque...
摘要:最佳实践良好的编码规范单元测试持续集成文档,从一开始就形成良好的编码习惯。真实的电商业务所有的业务需求来自真实的客户,并且线上良好运营中。 重要通知: Laravel + 小程序的开源电商版本源码已经在 github 上拉,欢迎提交 issue 和 star :) 开源电商 Server 端: Laravel API源码 开源电商 client 端:小程序源码 iBrand 简介...
摘要:至于其他的算一般我习惯说树形模型,这里说的概率模型可能是差不多的意思。 要点 不同工具之间版本匹配很重要由点及面,先实践起来再学细节的原理和使用 技术栈 laravel5.5框架+scout组件+elasticsearch6.3.0搜索引擎 辅助 elasticsearch-head 查看集群数据可视化 中文分词插件Ik 介绍 laravel是一款现代化的php框架es是搜索引擎e...
阅读 3868·2021-09-23 11:51
阅读 3059·2021-09-22 15:59
阅读 857·2021-09-09 11:37
阅读 2066·2021-09-08 09:45
阅读 1263·2019-08-30 15:54
阅读 2057·2019-08-30 15:53
阅读 488·2019-08-29 12:12
阅读 3285·2019-08-29 11:15