PHP處理WEB請求的流程

PHP做爲世界上最好的編程語音,被普遍的運用到Web開發中。由於其語法和C相似,有着很是平緩的學習曲線,愈來愈多的人使用PHP進行Web產品的快速開發。PHP世界裏也涌現了不少開發框架,好比Laravel、ThinkPHP等,但不論何總框架,他們在處理Web請求時的模式都是同樣的,本文首先闡述PHP開發Web應用的基本架構,而後分別分析Laravel和ThinkPHP在處理Web請求時的處理流程。php

PHP開發Web應用的基本架構


PHP開發Web應用時因此的請求須要指向具體的入口文件。WebServer是一個內容分發者,他接受用戶的請求後,若是是請求的是css、js等靜態文件,WebServer會找到這個文件,而後發送給瀏覽器;若是請求的是/index.php,根據配置文件,WebServer知道這個不是靜態文件,須要去找PHP解析器來處理,那麼他會把這個請求簡單處理後交給PHP解析器。
PHP%E5%A4%84%E7%90%86web%E8%AF%B7%E6%B1%82%E6%A8%A1%E5%9E%8B2.pngcss

WebServer會依據CGI協議,將請求的Url、數據、Http Header等信息發送給PHP解析器,接下來PHP解析器會解析php.ini文件,初始化執行環境,而後處理請求,再以CGI規定的格式返回處理後的結果,退出進程。web server再把結果返回給瀏覽器。整個處理過程如上圖所示。laravel

FastCGI


這裏的PHP解析器就是實現了CGI協議的程序,每次請求到來時他會解析php.ini文件,初始化執行環境,這就致使PHP解析器性能低下,因而就出現了CGI的改良升級版FastCGI。FastCGI是一種語言無關的協議,用來溝通程序(如PHP, Python, Java)和Web服務器(Apache2, Nginx), 理論上任何語言編寫的程序均可以經過FastCGI來提供Web服務。它的特色是會在動態分配處理進程給請求,以達到提升效率的目的,大多數FastCGI實現都會維護一個進程池。FastCGI會先啓一個master進程,解析配置文件,初始化執行環境,而後再啓動多個worker進程。當請求過來時,master進程會這個請求傳遞給一個worker進程,而後當即接受下一個請求。並且當worker進程不夠用時,master能夠根據配置預先啓動幾個worker進程等待;固然空閒worker進程太多時,也會自動關閉,這樣就提升了性能,節約了系統資源。整個過程FastCGI扮演着對CGI進程進行管理的角色。web

PHP-FPM


PHP-FPM是一個專門針對PHP實現了FastCGI協議的程序,它實際上就是一個PHP FastCGI進程管理器,負責管理一個進程池,調用PHP解析器來處理來自Web服務器的請求。PHP-FPM可以對php.ini文件的修改進行平滑過分。ajax

新建一個helloworld.php文件,寫入下列代碼數據庫

<?php
   echo "helloworld,";    
   echo "this is my first php script.";
   echo phpinfo();
?>

配置好WebServer和PHP-FPM等php運行環境後,在瀏覽器中訪問該文件就能夠直接獲得輸出。編程

基於PHP的Web框架


PHP Web框架是

基於某模式將PHP開發經常使用功能封裝實現使開發者快速開發的工具bootstrap

它主要的任務包括:

  • 代碼重用:定義包、類、函數的放置和加載規則,建議直接整合Composer及其AutoLoad特性。瀏覽器

  • 請求的分發管理:這個就是路由,Rest風的框架喜歡Rewrite,簡單的一點的框架主要經過參數來定位模塊和方法所在。服務器

  • 配置文件管理:加載和動態加載配置數據

  • 錯誤和異常管理:異常捕捉、錯誤日誌記錄以及錯誤碼規範。

  • Layout和模板引擎:如何規劃頁面佈局、widget如何重用、ajax頁面如何結合、過時- session如何重定向;數據和模板怎麼渲染成HTML,是否壓縮和設置過時頭。

  • 數據庫:如何融入控制器;支持什麼樣的driver;考慮主從分離的擴展性;以及是否使用ORM

ThinkPHP3.2框架處理流程分析

TP的設計邏輯就是簡單粗暴,面對問題解決問題,因此他的處理流程是基於面向過程的思想,而沒有采用面向對象的依賴注入、控制反轉等思路。他的自動加載、錯誤處理經過php原生函數的回調來實現。TP處理每次請求要通過四個步驟以下圖所示:
ThinkPHP%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B.png

調用應用路口index.php

index.php是TP的入口文件,全部的請求都由該文件接管,它的工做也很簡單主要是引入ThinkPHP入口文件

<?php


// 應用入口文件


 


// 檢測PHP環境


if(version_compare(PHP_VERSION,'5.3.0','<'))  die('require
 PHP > 5.3.0 !');


 


// 開啓調試模式 建議開發階段開啓 部署階段註釋或者設爲false


define('APP_DEBUG',False);


 


// 定義應用目錄


define('APP_PATH','./Application/');


 


// 引入ThinkPHP入口文件


require
'./ThinkPHP/ThinkPHP.php';

載入框架入口文件ThinkPHP.php

在ThinkPHP.php中主要記錄初始運行時間和內存開銷,而後完成系統常量判斷及定義,最後載入框架引導類(ThinkThink)並執行Think::start方法進行應用初始化。

應用初始化ThinkThink:start()

應用初始化首先設置錯誤處理機制和自動加載機制

static public function start() {
      // 註冊AUTOLOAD方法
      spl_autoload_register('Think\Think::autoload');      
      // 設定錯誤和異常處理
      register_shutdown_function('Think\Think::fatalError');
      set_error_handler('Think\Think::appError');
      set_exception_handler('Think\Think::appException');

而後加載相關配置文件和運行模式定義文件,最後調用ThinkApp類的run方法啓動應用

運行應用App::run()

此後TP進入請求處理管道,TP爲管道中定義了14個事件,每一個事件均可以綁定回調函數,請求到達管道後依次觸發這些事件,事件觸發後就會調用綁定到事件的回調函數,整個管道的生命週期由app_init開始,由app_end結束。具體實現上,TP將這些事件命名爲標籤(位),也能夠稱之爲鉤子,將回調函數命名爲行爲,當應用程序運行到標籤的時候,就會被攔截下來,統一執行相關的行爲。

Laravel框架處理流程分析

統一入口

Laravel框架使用了統一入口,入口文件:/public/index.php

<?php
//自動加載文件設置
require __DIR__.'/../bootstrap/autoload.php';
 
//初始化服務容器(能夠查看一下關於‘服務容器’的相關文檔)
$app = require_once __DIR__.'/../bootstrap/app.php';
 
//經過服務容器生成一個kernel類的實例(Illuminate\Contracts\Http\Kernel實際上只是一個接口,真正生成的實例是App\Http\Kernel類,至於怎麼把接口和類關聯起來,請查看Contracts相關文檔)
$kernel = $app->make('Illuminate\Contracts\Http\Kernel');
 
//運行Kernel類的handle方法,主要動做是運行middleware和啓動URL相關的Contrller
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
 
//控制器返回結果以後的操做,暫時還沒看,之後補上
$response->send();
 
$kernel->terminate($request, $response);

自動加載文件

laravel的自動加載,其實也就是Composer的自動加載
Composer根據聲明的依賴關係,從相關庫的源下載代碼文件,並根據依賴關係在 Composer 目錄下生成供類自動加載的 PHP 腳本,使用的時候,項目開始處引入 「/vendor/autoload.php」 文件,就能夠直接實例化這些第三方類庫中的類了。

服務容器——Laravel真正的核心

服務容器,也叫IoC容器,其實包含了依賴注入(DI)和控制反轉(IoC)兩部分,是Laravel的真正核心。其餘的各類功能模塊好比 Route(路由)、Eloquent ORM(數據庫 ORM 組件)、Request and Response(請求和響應)等等等等,實際上都是與核心無關的類模塊提供的,這些類從註冊到實例化,最終被使用,其實都是 Laravel 的服務容器負責的。

啓動Kernel代碼

Kernel實例調用handle方法,意味着Laravel的核心和公用代碼已經準備完畢,此項目正式開始運行

代碼清單/app/Http/Kernel.php

<?php
namespace
App\Http;


use
Illuminate\Foundation\Http\Kernel
as
HttpKernel;


class
Kernel
extends
HttpKernel
{


    //這是在調用路由以前須要啓動的中間件,通常都是核心文件,不要修改


    protected
$middleware
=
[


        'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',


        'Illuminate\Cookie\Middleware\EncryptCookies',


        'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',


        'Illuminate\Session\Middleware\StartSession',


        'Illuminate\View\Middleware\ShareErrorsFromSession',


        'App\Http\Middleware\VerifyCsrfToken',


    ];


    //這是咱們在router.php文件裏面或者Controller文件裏面,可使用的Middleware元素,能夠自定義加入不少


    protected
$routeMiddleware
=
[


        'auth'
=>
'App\Http\Middleware\Authenticate',


        'auth.basic'
=>
'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',


        'guest'
=>
'App\Http\Middleware\RedirectIfAuthenticated',


        'test'
=>
'App\Http\Middleware\testMiddleWare',


    ];


}

能夠看到,其實這個文件裏面沒有handle方法,只有一些屬性定義,因此真正的handle方法,實在父類裏面實現的

代碼清單…/Illuminate/Foundation/Http/Kernel.php

//這個很重要,是項目的一些啓動引導項,Kernel的重要步驟中,首先就是啓動這些文件的bootstrap方法
protected $bootstrappers = [
        //檢測環境變量文件是否正常
        'Illuminate\Foundation\Bootstrap\DetectEnvironment',
        //取得配置文件,即把/config/下的全部配置文件讀取到容器(app()->make('config')能夠查看全部配置信息)
        'Illuminate\Foundation\Bootstrap\LoadConfiguration',
        //綁定一個名字爲log的實例到容器,怎麼訪問??(app()->make('log'))
        'Illuminate\Foundation\Bootstrap\ConfigureLogging',
        //設置異常抓取信息,這個還沒仔細看,但大概就是這個意思
        'Illuminate\Foundation\Bootstrap\HandleExceptions',
        //把/config/app.php裏面的aliases項利用PHP庫函數class_alias建立別名,今後,咱們可使用App::make('app')方式取得實例
        'Illuminate\Foundation\Bootstrap\RegisterFacades',
        //把/config/app.php裏面的providers項,註冊到容器
        'Illuminate\Foundation\Bootstrap\RegisterProviders',
        //運行容器中註冊的全部的ServiceProvider中得boot方法
        'Illuminate\Foundation\Bootstrap\BootProviders',
    ];
 
  //真正的handle方法
  public function handle($request)
    {
        try
        {
            //主要是這行,調度了須要運行的方法
            return $this->sendRequestThroughRouter($request);
        }
        catch (Exception $e)
        {
            $this->reportException($e);
            return $this->renderException($request, $e);
        }
    }
 
    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);
        Facade::clearResolvedInstance('request');
        //運行上述$bootstrappers裏面包含的文件的bootstrap方法,運行的做用,上面已經註釋
        $this->bootstrap();
        //這是在對URL進行調度以前,也就是運行Route以前,進行的一些準備工做
        return (new Pipeline($this->app))    
                    ->send($request)        
                    //須要運行$this->middleware裏包含的中間件
                    ->through($this->middleware)
                    //運行完上述中間件以後,調度dispatchToRouter方法,進行Route的操做
                    ->then($this->dispatchToRouter());
    }
 
    //前奏執行完畢以後,進行Route操做
    protected function dispatchToRouter()
    {
        return function($request)
        {
            $this->app->instance('request', $request);
            //跳轉到Router類的dispatch方法
            return $this->router->dispatch($request);
        };
    }

下面就須要根據URL和/app/Http/routes.php文件,進行Route操做

文件清單…/Illuminate/Routing/Router.php

public
function
dispatch(Request
$request)


{


$this->currentRequest
=
$request;


//在4.2版本里面,Route有一個篩選屬性;5.0以後的版本,被Middleware代替


$response
=
$this->callFilter('before',
$request);


if
(is_null($response))


{    


//繼續調度


$response
=
$this->dispatchToRoute($request);


}


$response
=
$this->prepareResponse($request,
$response);


//在4.2版本里面,Route有一個篩選屬性;5.0以後的版本,被Middleware代替


$this->callFilter('after',
$request,
$response);


return
$response;


}


 


public
function
dispatchToRoute(Request
$request)


{


$route
=
$this->findRoute($request);


$request->setRouteResolver(function()
use
($route)


{


return
$route;


});


$this->events->fire('router.matched',
[$route,
$request]);


$response
=
$this->callRouteBefore($route,
$request);


if
(is_null($response))


{


//
 只看這一行,仍是調度文件


$response
=
$this->runRouteWithinStack(


$route,
$request


);


}


$response
=
$this->prepareResponse($request,
$response);


$this->callRouteAfter($route,
$request,
$response);


return
$response;


}


 


protected
function
runRouteWithinStack(Route
$route,
Request
$request)


{


//
 取得routes.php裏面的Middleware節點


$middleware
=
$this->gatherRouteMiddlewares($route);


//這個有點眼熟


return
(new
Pipeline($this->container))


->send($request)


//執行上述的中間件


->through($middleware)


->then(function($request)
use
($route)


{    


//到Controller類了


return
$this->prepareResponse(


$request,


//run控制器


$route->run($request)


);


});


}


 


public
function
run(Request
$request)


{


$this->container
=
$this->container
?:
new
Container;


try


{


if
(
!
is_string($this->action['uses']))


return
$this->runCallable($request);


if
($this->customDispatcherIsBound())


//其實是運行了這行


return
$this->runWithCustomDispatcher($request);


 


//其實我是直接想運行這行


return
$this->runController($request);


}


catch
(HttpResponseException
$e)


{


return
$e->getResponse();


}


}


 


//繼續調度,最終調度到.../Illuminate/Routing/ControllerDispatcher.php文件的dispatch方法


protected
function
runWithCustomDispatcher(Request
$request)


{


list($class,
$method)
=
explode('@',
$this->action['uses']);


 


$dispatcher
=
$this->container->make('illuminate.route.dispatcher');


return
$dispatcher->dispatch($this,
$request,
$class,
$method);
}

文件清單…/Illuminate/Routing/ControllerDispatcher.php

public
function
dispatch(Route
$route,
Request
$request,
$controller,
$method)


    {


        $instance
=
$this->makeController($controller);


        $this->assignAfter($instance,
$route,
$request,
$method);


        $response
=
$this->before($instance,
$route,
$request,
$method);


        if
(is_null($response))


        {


            //還要調度


            $response
=
$this->callWithinStack(


                $instance,
$route,
$request,
$method


            );


        }


        return
$response;


    }


 


    protected
function
callWithinStack($instance,
$route,
$request,
$method)


    {


        //又是Middleware......有沒有忘記,官方文檔裏面Middleware能夠加在控制器的構造函數中!!沒錯,這個Middleware就是在控制器裏面申明的


        $middleware
=
$this->getMiddleware($instance,
$method);


        //又是這個,眼熟吧


        return
(new
Pipeline($this->container))


                    ->send($request)


                    //再次運行Middleware


                    ->through($middleware)


                    ->then(function($request)
use
($instance,
$route,
$method)


                    {    


                        運行控制器,返回結果


                        return
$this->call($instance,
$route,
$method);


                    });


    }

終於到達控制器

轉自:http://www.eurekao.com/PHP-pr...

相關文章
相關標籤/搜索