Luồng xử lý của laravel

bài viết này mình xin giới thiệu về cách thức laravel chạy. để nếu bạn nào debug code laravel thì còn có sơ sở xem sai chỗ nào. Bài này không dành cho pro nhé

2021-01-04 1913 lượt xem

  1. Trang Chủ
  2. laravel
  3. Cơ chế xử lý request trong Laravel

tôi thích vậy đó

 😂😂😂 Mời các Pro cút khỏi bài viết để khỏi ném đá chủ thớt 😂😂😂

tôi thích vậy đó

hùng đẹp trai

ahihi đồ ngốc trương thanh hùng nè

Luồng xử lý của laravel

hùng đẹp trai

ahihi đồ ngốc trương thanh hùng nè

Javascript là 1 ngôn ngữ bị đồn là si-đa thì PHP lại là 1 ngôn ngữ không hề si-đa. Nó bị AIDS thôi. Đại loại là càng dễ tiếp cận càng dễ chơi cùng thì càng dễ si-đa. laravel không ngoại lệ, nó được viết trên nền php và dễ dùng, nhưng bạn đã thực sự hiểu laravel chạy như nào chưa? 

hùng ebudezain

tôi thích vậy đó

Laravel là một framework theo mô hình MVC (Model – View – Controller) và mọi request đều được phân tích từ 1 url của request. từ request đó chúng ta sẽ phân tách để biết được nên chạy controller nào, action nào. 

hùng ebudezain

hùng thông minh lắm

đọc dữ liệu hộ bố

0 - tổng quan

tôi thích vậy đó

hùng đẹp trai

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

hùng ebudezain

hùng đẹp trai

hùng ebudezain

hùng ebudezain

Biến global của nodejs người dùng A lưu lại giá trị thì người dùng B cũng có thể thấy giá trị đó. còn biến global của PHP thì khác ( khác nodejs thôi chứ giống java nhé ).

hùng ebudezain

hùng ebudezain

tôi thích vậy đó

ahihi đồ ngốc trương thanh hùng nè

tôi thích vậy đó

đọc dữ liệu hộ bố

đọc dữ liệu hộ bố

ahihi đồ ngốc trương thanh hùng nè

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

ahihi đồ ngốc trương thanh hùng nè

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

hùng thông minh lắm

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

ahihi đồ ngốc trương thanh hùng nè

Mọi request của người dùng vào laravel quy cho cùng đều chạy vào public/index.php. Blog này mình đang viết code bằng laravel và bạn thử bấm link này để url có thêm chữ index.php nhưng vẫn chạy cùng 1 controller cùng action nha.

tôi thích vậy đó

tôi thích vậy đó

hùng ebudezain

phân tích những thứ laravel làm

tôi thích vậy đó

Auto Loader

hùng thông minh lắm

hùng ebudezain

đọc dữ liệu hộ bố

tôi thích vậy đó

khi laravel khởi tạo project sẽ download rất nhiều package vào trong vendor. và nếu muốn sử dụng các package thì php cần require vào. bạn vào public/index.php sẽ thấy đoạn code require sau: 

hùng ebudezain

hùng đẹp trai

hùng thông minh lắm

require __DIR__.'/../vendor/autoload.php';

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

ahihi đồ ngốc trương thanh hùng nè

ahihi đồ ngốc trương thanh hùng nè

đọc dữ liệu hộ bố

hùng ebudezain

nếu bạn download 1 package khác từ  packagist.org thì bạn khỏi cần thêm autoload như các project khác. 

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

Load application

đọc dữ liệu hộ bố

hùng thông minh lắm

tôi thích vậy đó

tôi thích vậy đó

tôi thích vậy đó

tôi thích vậy đó

đọc dữ liệu hộ bố

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

đọc dữ liệu hộ bố

Sau khi load vendor thì ứng dụng laravel cần load 1 app trong folder bootstraps. bạn nhìn trong folder public/index.php thì bạn thấy đoạn require sau: 

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

hùng ebudezain

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

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

thì trong file bootstrap/app.php mục đích chính là khởi tạo 1 cái app, sau đó binding 1 và thứ cần thiết vào app bằng singleton. ví dụ khởi tạo những thứ cần thiết như path, biến global, đăng ký service provider,...

hùng ebudezain

bạn có thể xem kỹ hơn ở vendor\laravel\framework\src\Illuminate\Foundation\Application.php như sau: 

ahihi đồ ngốc trương thanh hùng nè

hùng ebudezain

hùng đẹp trai

/**
     * Create a new Illuminate application instance.
     *
     * @param  string|null  $basePath
     * @return void
     */
    public function __construct($basePath = null)
    {
        if ($basePath) {
            $this->setBasePath($basePath);
        }
        $this->registerBaseBindings(); /// tạo path và binding Service Container
        $this->registerBaseServiceProviders(); /// đăng ký  Service Providers cơ bản cho application
        $this->registerCoreContainerAliases(); /// nạp các alias
    }

hùng thông minh lắm

hùng đẹp trai

hùng ebudezain

hùng thông minh lắm

ahihi đồ ngốc trương thanh hùng nè

Run application laravel

hùng đẹp trai

hùng ebudezain

tôi thích vậy đó

hùng thông minh lắm

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

ahihi đồ ngốc trương thanh hùng nè

ahihi đồ ngốc trương thanh hùng nè

đọc dữ liệu hộ bố

hùng ebudezain

Nãy giờ không động chạm gì tới request, bây giờ mới là lúc chạy thật. 

hùng ebudezain

hùng ebudezain

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

tôi thích vậy đó

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

Luồng xử lý của laravel

hùng đẹp trai

hùng ebudezain

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

Tạo Http Kernel

hùng đẹp trai

ahihi đồ ngốc trương thanh hùng nè

hùng đẹp trai

hùng đẹp trai

ahihi đồ ngốc trương thanh hùng nè

đọc dữ liệu hộ bố

hùng thông minh lắm

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

hùng đẹp trai

Để xử lý được request thì laravel khởi tạo kernel trước. dựa trên kernel sẽ xử lý request thông qua đối tượng request đã được nạp từ trên. Bạn tạm hiểu kernel là hạt nhân, core, lõi hay đại loại cái gì đó trung tâm xử lý cũng đc. Nó nhận vào request, đem đi phân tích và xử lý sau đó trả ra 1 response tương ứng. 

tôi thích vậy đó

hùng ebudezain

hùng thông minh lắm

Xử lý request thông qua kernel

ahihi đồ ngốc trương thanh hùng nè

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

hùng thông minh lắm

Quay lại đoạn này 

ahihi đồ ngốc trương thanh hùng nè

hùng đẹp trai

hùng thông minh lắm

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

tôi thích vậy đó

ahihi đồ ngốc trương thanh hùng nè

hùng ebudezain

đọc dữ liệu hộ bố

ahihi đồ ngốc trương thanh hùng nè

tuy ngắn gọn nhưng kinh khủng khiếp lắm các bạn.

tôi thích vậy đó

tôi thích vậy đó

hùng ebudezain

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

đọc dữ liệu hộ bố

hùng ebudezain

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

ahihi đồ ngốc trương thanh hùng nè

ahihi đồ ngốc trương thanh hùng nè

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

Đầu tiên bạn mở app/Http/Kernel.php sẽ thấy nó extend từ HttpKernel ( tên định danh của class  Illuminate\Foundation\Http\Kernel ).

hùng thông minh lắm

hùng đẹp trai

hùng thông minh lắm

ahihi đồ ngốc trương thanh hùng nè

hùng ebudezain

hùng đẹp trai

hùng thông minh lắm

Lưu ý là laravel lấy nền từ Symfony nên không khó hiểu khi mà class Request extends SymfonyRequest

hùng ebudezain

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

hùng ebudezain

hùng thông minh lắm

ahihi đồ ngốc trương thanh hùng nè

tôi thích vậy đó

hùng thông minh lắm

hùng đẹp trai

Dòng $request = Illuminate\Http\Request::capture() quy cho cùng là tạo ra cái biến $request để trong HttpKernel sẽ có hàm public function handle($request) sẽ handle biến $request. 

đọc dữ liệu hộ bố

tôi thích vậy đó

hùng ebudezain

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

Trở lại app/Http/Kernel.php bạn xem thấy có 3 thuộc tính định nghĩa trong file này là: 

  • protected $middleware, là Global middleware, mảng middleware này được khai báo, và tất cả các request đến ứng dụng đều phải đi qua tưng cái 
  • protected $middlewareGroups, loại này để phân ra làm 2 loại web và api. ví dụ như web thì kệ mợ nó chứ api thì phải trả ra json, hay hạn chế đánh cắp dữ liệu thì phải sử dụng rating limit. 
  • protected $routeMiddleware.đây là middleware mà khi khai báo trong router thì mới chạy.

hùng đẹp trai

truyền request vào router

hùng thông minh lắm

ahihi đồ ngốc trương thanh hùng nè

hùng đẹp trai

ahihi đồ ngốc trương thanh hùng nè

ahihi đồ ngốc trương thanh hùng nè

hùng ebudezain

hùng thông minh lắm

hùng đẹp trai

hùng ebudezain

hùng thông minh lắm

Khi laravel chạy function handle của Illuminate\Foundation\Http\Kernel thì sẽ có cái hàm cần chú ý là $this->sendRequestThroughRouter($request);

ahihi đồ ngốc trương thanh hùng nè

tôi thích vậy đó

hùng thông minh lắm

tôi thích vậy đó

/**
     * Handle an incoming HTTP request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    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;
    }

hùng ebudezain

tôi thích vậy đó

đọc dữ liệu hộ bố

đọc dữ liệu hộ bố

hùng đẹp trai

đọc dữ liệu hộ bố

hùng thông minh lắm

ahihi đồ ngốc trương thanh hùng nè

hùng thông minh lắm

hùng đẹp trai

Nếu bạn muốn tìm hiểu kỹ hơn, mình hy vọng bạn sẽ bắt đầu từ hàm này. Vì gần như mọi thứ lõi core đều được xử lý trong hàm này(Service Container, Request, File, Cookie, biến môi trường, các cấu hình, handle exception, đăng ký các Facades, đăng ký các Service Providers, và khởi động Service Provider, xử lý middleware, ...

tôi thích vậy đó

hùng đẹp trai

hùng đẹp trai

hùng thông minh lắm

Dispatch request

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

ahihi đồ ngốc trương thanh hùng nè

đọc dữ liệu hộ bố

tôi thích vậy đó

ahihi đồ ngốc trương thanh hùng nè

hùng thông minh lắm

đối tượng request được tạo ra nhờ 1 loạt các biến global của PHP chạy vào Request của Symfony, đặc biệt biến $_SERVER để detect được url, bạn xem đoạn code này: 

hùng ebudezain

ahihi đồ ngốc trương thanh hùng nè

hùng ebudezain

/**
     * Creates a new request with values from PHP's super globals.
     *
     * @return static
     */
    public static function createFromGlobals()
    {
        $request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);

        if ($_POST) {
            $request->request = new InputBag($_POST);
        } elseif (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
            && \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
        ) {
            parse_str($request->getContent(), $data);
            $request->request = new InputBag($data);
        }

        return $request;
    }

hùng đẹp trai

ahihi đồ ngốc trương thanh hùng nè

tôi thích vậy đó

đọc dữ liệu hộ bố

hùng thông minh lắm

Cụ thể, hàm sendRequestThroughRouter ở phần truyền request vào router sẽ start các hàm con như dispatchToRoute, findRoute cuối cùng là hàm getRequestUri trong Symfony\Component\HttpFoundation\Request.

hùng đẹp trai

ahihi đồ ngốc trương thanh hùng nè

ahihi đồ ngốc trương thanh hùng nè

hùng đẹp trai

hùng đẹp trai

Vì khi request được khởi tạo có truyền nguyên biến $_SERVER vào nên trong hàm prepareRequestUri có thể lấy REQUEST_URI trong biến $_SERVER ra để tìm đúng router.

hùng ebudezain

Luồng xử lý của laravel

hùng thông minh lắm

tôi thích vậy đó

đọc dữ liệu hộ bố

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

hùng đẹp trai

Response được trả về và đóng thread.

đọc dữ liệu hộ bố

bài viết được viết bởi trương thanh hùng https://ebudezain.com/

ahihi đồ ngốc trương thanh hùng nè

hùng đẹp trai

hùng đẹp trai

hùng đẹp trai

đọc dữ liệu hộ bố

hùng đẹp trai

Sau khi có $response nó sẽ được trả về cho người dùng từ mục xử lý request thông qua kernel thì cũng đã đến lúc gửi về cho client và đóng ứng dụng. 

hùng ebudezain

hùng ebudezain

hùng ebudezain

$response->send(); // Gửi response đến người dùng
$kernel->terminate($request, $response); // Đóng Kernel và kết thúc vòng đời của một request

đọc dữ liệu hộ bố

hùng đẹp trai

đọc dữ liệu hộ bố

hùng thông minh lắm

Tổng kết

hùng ebudezain

hùng ebudezain

ahihi đồ ngốc trương thanh hùng nè

dài quá nên mình tổng kết quá trình tổng thể của laravel sinh ra 1 response là: 

  • Auto loader: Tải những gì cần thiết (packages, library, classmap…) đầu tiên.
  • Khởi tạo Laravel Application.
  • Đăng ký và tạo Http Kernel, Console Kernel, Exceptions Handle.
  • Đăng ký các middleware trong Illuminate\Foundation\Http\Kernel.
  • Đăng ký request hiện tại thay thế cho request mới nhất ứng dụng nhận được để xử lý.
  • Load bootstrappers: load biến môi trường, cấu hình, …
  • Đăng ký và xử lý Service Providers
  • Chuyển request qua các Middleware hoặc route và xử lý chúng theo ý dev ( có đôi khi bạn sẽ chạy affter middleware nên tùy lúc middleware sẽ chạy trước) và khi xử lý route, một chuỗi các hoạt động từ Router đến Controller đến Model và trả về một Response.
  • Gửi response về cho người dùng.
  • Kết thúc vòng đời của một request bằng việc đóng lại Http Kernel.