欢迎光临散文网 会员登陆 & 注册

第二部分 第1章 项目访问解析

2023-09-06 17:33 作者:纳西妲爱编程  | 我要投稿

第1章 项目访问解析

1.1 PHP 项目访问解析

PHP 交互的前提条件 ?

PHP 是一门动态的脚本编程语言,PHP可以通过 SAPI 来实现不同 PHP 脚本的执行方式,以满足在不同环境下的 PHP 代码执行,其中 Web 执行模式就属 FastCGI 的工作模式。FastCGI 由 FPM 实现,那 FPM 是什么呢 ?

FPM 是 PHP 的进程管理器,即 PHP 软件程序启动后,就会在后台运行 PHP 相关的进程。那如果进程意外挂掉、接受指令要完成什么工作。这时要怎么办呢? 所以就需要 FPM 来统一的进行进程管理。

在 linux 下,通过 ps -ef | grep php 可查看所有启动的 PHP 进程,但你好像发现了,php-fpm 有很多啊。这个是咋回事呢?

这就不得不说下 PHP 的进程架构,它并不是一个人,因为你一个人做事太慢,所以 PHP 就做了个分工,进程由 Master+Worker 模式构成,也就是进程有多个,但不同进程负责的工作是不一样,也就是 2 种。

PHP 在启动程序后就会先启动 Master ,然后 fork 出多个 Worker 进程,最后由 Worker 直接与客户端建立请求连接。也就是 Master 做进程管理的工作,一个 worker 做请求的连接与数据的处理。

有了进程咱们是不是就可以执行工作啦 ?

并不是,因为你进程要执行工作,是不需要让别人把原材料(数据)给你,也就数据。 但这个数据不可能无缘无故过来,一般都是通过 Nginx 传输过来的。但 Nginx 和 PHP 是 2 个程序呀,彼此都互不认识。就像你和快递小哥一样,他要把包裹交到你手上,但你们彼此根本都不认识。但你们会通过

手机号码+包裹签收

来进行识别呢 !

所以 FPM 也一样,它也需要实现一种规则,用来接受原材料,也就是进行数据交互。这也就是为啥每个 PHP 进程都支持 fast-cgi 协议的原因。有了它,咱们就可以让 Nginx 和 PHP 打交道啦。

上面谈到了,PHP 的工作模式与处理,那么在 PHP 的项目中,完整 PHP 请求的处理模式是怎么实现的 ?

用户访问域名,域名进行 DNS 解析 -> 请求到对应 IP 服务器和端口。

根据 IP 地址与端口发送请求到 IP 地址服务器上的 Nginx 软件监听的对应端口中。

Nginx 对请求 url 进行 location 匹配,执行匹配 location 下的规则,然后由 Nginx 转发请求给 php 。

php-fpm 的 master 进程监听到 nginx 请求,然后 Master 进程将请求分配给其中一个闲置的 worker进程。

最后由 worker 进程执行请求,并返回执行结果给 nginx 。

Nginx 返回结果给用户。

注意:这里的 Nginx 就是 web 服务器,类似的还有 Apache、IIS 等。

你可能会觉得,这个是PHP访问的方式,那项目如何访问呢 ?

你的项目基于 PHP 开发,一般 PHP 部署在线上的环境都为 LNMP 架构,就是基于 Linux操作系统来搭建的 PHP 开发环境,那这时候,你 HTTP 请求访问到服务器后,就自然把请求交给 Nginx,在由 Nginx 把请求交给项目的执行文件,最后在进行执行处理的。

1.2 框架基准请求响应解析

MVC

谈框架的基准请求都不能离开 MVC 模式处理,一切请求都会先进入到框架的 index.php 的入口文件中,然后在引入相关文件类库,做框架初始化、请求验证、路由分析,最后实例化控制器并调用方法去查找数据库的数据,最后在响应给客户端。

如上图,为 MVC 原理图,分别代表什么意思 ?

Model(模型)

- 模型代表一个存取数据的对象。它也可以带有逻辑,在数据变化时更新控制器。

View(视图)

- 视图代表模型包含的数据的可视化

Controller(控制器)

- 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开 。

优点 :

视图控制模型分离, 提高代码重用性。

提高开发效率。

便于后期维护, 降低维护成本。

方便多开发人员间的分工。

缺点 :

方法越来越大,运行效率相对较低 。

控制层和表现层有时会过于紧密,导致没有真正分离和重用 。类本身也是越来越大,职责也会越来越多。

框架的请求执行处理。

有了框架通用的 MVC 模式,接下来就可以看下框架的请求响应流程。这里就以 laravel 请求周期来进行举例,其它一些框架与 Laravel 框架是类型的。如图所示 :

注意:以下涉及代码都为laravel框架核心代码解析。

1 . 用户的 HTTP 请求发送到 index.php 入口处。

2 . index.php 开始引入 app 应用对象、异常处理机制、HTTP 等相关核心类库,并进行进行相关初始化。

//laraveldemo\public\index.php

$app = require_once __DIR__.'/../bootstrap/app.php';

-----------------------------------------------------

//laraveldemo\bootstrap\app.php

$app = new Illuminate\Foundation\Application(

  $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)

);

$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

);

3 . 在 app 应用内部,注册容器对象和文件类、路由、事件、日志、相关别名与类的关系等等到容器中。从而来完成框架的基础初始化。这样框架需要的基本服务功能就都有了。

文件位置:laraveldemo\vendor\laravel\framework\src\Illuminate\Foundation\Application.php

 public function __construct($basePath = null)

 {

if ($basePath) {

  $this->setBasePath($basePath);

}

$this->registerBaseBindings();

$this->registerBaseServiceProviders();

$this->registerCoreContainerAliases();

 }

  //$this->registerBaseBindings(); 将容器别名、文件类等基础信息注入到容器

  protected function registerBaseBindings()

 {

static::setInstance($this);

$this->instance('app', $this);

$this->instance(Container::class, $this);

$this->singleton(Mix::class);

$this->singleton(PackageManifest::class, function () {

  return new PackageManifest(

new Filesystem, $this->basePath(), $this->getCachedPackagesPath()

 );

});

 }

-----------------------------------------------------

  // $this->registerBaseServiceProviders(); 注册相关基础服务

 protected function registerBaseServiceProviders()

 {

$this->register(new EventServiceProvider($this));

$this->register(new LogServiceProvider($this));

$this->register(new RoutingServiceProvider($this));

 }

-----------------------------------------------------

//$this->registerCoreContainerAliases();注册别名和类的关系

public function registerCoreContainerAliases()

 {

foreach ([

  'app' => [self::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],

  'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],

  'auth.driver' =>

//.......省略些核心代码

  'view' => [\Illuminate\View\Factory::class, \Illuminate\Contracts\View\Factory::class],

] as $key => $aliases) {

  foreach ($aliases as $alias) {

$this->alias($key, $alias);

 }

}

 }

   

}

4 . 完成了框架组件服务组装,咱们就需要开始来解析 HTTP 请求啦。现在可基于容器获取到 HTTP 对象,在这里会通过 make 方法直接反射拿取 HTTP 实例。并调用 HTTP 类下面的 capture 方法获取 HTTP 请求中的参数,并用服务提供者把相关的服务类绑定到容器中,然后对请求通过中间件进行过滤,通过后在进行路由的匹配与代码执行操作。执行完最后返回给客户端。

//laraveldemo\public\index.php

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle( 

  $request = Illuminate\Http\Request::capture());

//文件位置:laraveldemo\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php

-----------------------------------------------------

//应用服务提供者,用来注册其它相关的服务组件  

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 $middleware = [    

\App\Http\Middleware\TrustHosts::class,    

\App\Http\Middleware\TrustProxies::class,    

\Fruitcake\Cors\HandleCors::class,    

\App\Http\Middleware\CheckForMaintenanceMode::class,  

\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,    

\App\Http\Middleware\TrimStrings::class,    

\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, 

];  

public function handle($request)

try {      

$request->enableHttpMethodParameterOverride();  

//1、开始分发请求的路由      

$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;  

}

//2、开启路由分发之前,先获取请求对象,然后开始注册服务提供者。并在管道模式下面进行中间件对HTTP请求的过滤检查

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());  

}  

public function bootstrap()

{   

if (! $this->app->hasBeenBootstrapped()) {   

$this->app->bootstrapWith($this->bootstrappers()); 

}  

}

protected function bootstrappers() 

{    

return $this->bootstrappers;  

}

5 . 请求首先通过中间件进行请求的过滤,如果不满足相关中间件的条件,就直接结束请求。

6 . 如果中间件处理成功,就会获取请求中的 URL 地址与注册的路由地址进行比较,如果匹配到,就根据路由拿到控制器和方法名,然后进行控制器实例化并调用注册方法,在处理当前的 HTTP 请求

 public function gatherRouteMiddleware(Route $route)

 {

$excluded = collect($route->excludedMiddleware())->map(function ($name) {

return (array) MiddlewareNameResolver::resolve(

$name, $this->middleware, $this->middlewareGroups);

})->flatten()->values()->all();

$middleware = collect($route->gatherMiddleware())->map(function ($name) {

return (array) MiddlewareNameResolver::resolve(

   $name, $this->middleware, $this->middlewareGroups

);

  })->flatten()->reject(function ($name) use ($excluded) {

   return in_array($name, $excluded, true);

})->values();

return $this->sortMiddleware($middleware);

}

////获取 路由中 和 控制器中 定义的中间件单词标识

public function gatherMiddleware()

 {

if (! is_null($this->computedMiddleware)) {

   return $this->computedMiddleware;

}

$this->computedMiddleware = [];

return $this->computedMiddleware = Router::uniqueMiddleware(array_merge(

   $this->middleware(), $this->controllerMiddleware()

));

 }

public function controllerMiddleware()

{

if (! $this->isControllerAction()) {

   return [];

}

// 调用 `controllerDispatcher` 获取 控制器调度器 对象

// 调用 控制器调度器 对象中的 getMiddleware 方法,以 控制器对象 和 方法名 为参数

return $this->controllerDispatcher()->getMiddleware(

// 调用 getController 方法,获取 控制器对象

   $this->getController(), $this->getControllerMethod()

);

 }

protected function dispatchToRouter()

{

return function ($request) {

   $this->app->instance('request', $request);

   return $this->router->dispatch($request);

};

 }

 public function dispatch(Request $request)

 {

$this->currentRequest = $request;

return $this->dispatchToRoute($request);

 }

//查找路由并开始运行路由对应的控制器下的方法

public function dispatchToRoute(Request $request)

 {

  return $this->runRoute($request, $this->findRoute($request));

 }

7 . 请求处理完成后,通过 Responce 直接响应业务数据到客户端。

第二部分 第1章 项目访问解析的评论 (共 条)

分享到微博请遵守国家法律