[Series Chọc Ngoáy Laravel] Tìm hiểu về Manager Support của Laravel

Giới thiệu chung

Trong Laravel có bao giờ bạn thử sử dụng Hash để mã hóa mật khẩu chưa? Nếu có rồi thì đã bao giờ bạn thử đổi các kiểu mã hóa khác nhau (driver) bằng cách như sau chưa:

Hash::driver('bcrypt')->make('secret'); // Mã hóa sử dụng thuật toán bcrypt
Hash::driver('argon')->make('secret'); // Mã hóa sử dụng thuật toán argon
Hash::driver('argon2Id')->make('secret'); // Mã hóa sử dụng thuật toán argon2

Vậy thì tại sao khi bạn sử dụng hàm driver('bcrypt') nó lại có thể đổi kiểu mã hóa? Chúng ta cùng thử tìm hiểu xem nhé.

Chọc ngoáy nào

Chọc ngoáy vào Hash Class

Như đã nói ở trên thì bạn có thể sử dụng class Hash để mã hóa dữ liệu. Vậy chúng ta hãy vào class này để xem nó như thế nào nhé. Nhưng trước tiên, Hash ở đâu?

Bạn hãy tìm vào trong file config/app.php nhé. Trong file này sẽ có đoạn như sau:

'aliases' => [
    ...
    'Hash' => Illuminate\Support\Facades\Hash::class,
    ...
],

Thực ra không có class nào là Hash cả nó chỉ là 1 …bề mặt (facade) thôi, việc bạn thao tác được với Hash giống như class vì đó là cơ chế của Laravel (các bạn có thể tìm hiểu kỹ thêm tại đây: Laravel Facades).

Cuối cùng thì class mà bạn đang thao tác đến chính là class có tên là Illuminate\Hashing\HashManager. Nó nằm tại đường dẫn sau:

Your-Laravel-Project/vendor/laravel/framework/src/Illuminate/Hashing/HashManager.php

Ta có thể thấy hàm make() mà chúng ta dùng để mã hóa:

/**
 * Hash the given value.
 *
 * @param  string  $value
 * @param  array   $options
 * @return string
 */
public function make($value, array $options = [])
{
    return $this->driver()->make($value, $options);
}

Hàm make() này cũng được gọi thông qua hàm driver() mà hàm này được HashManager extends từ class có tên là Illuminate\Support\Manager.

Tìm hiểu về Manager

Chọc ngoáy driver() method

Ở trong Manager này chúng ta có thể thấy method có tên là driver(). Nó có nội dung như sau:

    /**
     * Get a driver instance.
     *
     * @param  string  $driver
     * @return mixed
     *
     * @throws \InvalidArgumentException
     */
    public function driver($driver = null)
    {
        $driver = $driver ?: $this->getDefaultDriver();

        if (is_null($driver)) {
            throw new InvalidArgumentException(sprintf(
                'Unable to resolve NULL driver for [%s].', static::class
            ));
        }

        // If the given driver has not been created before, we will create the instances
        // here and cache it so we can return it next time very quickly. If there is
        // already a driver created by this name, we'll just return that instance.
        if (! isset($this->drivers[$driver])) {
            $this->drivers[$driver] = $this->createDriver($driver);
        }

        return $this->drivers[$driver];
    }

Hiểu một cách đơn giản thì method này có tác dụng:

  1. Kiểm tra xem $driver mà bạn truyền vào đã được khai báo hay chưa:
if (! isset($this->drivers[$driver])) {
    ...
}
  1. Nếu chưa được khai báo thì sẽ tạo thông qua createDriver():
$this->drivers[$driver] = $this->createDriver($driver);
  1. Trả về driver mà bạn cần:
return $this->drivers[$driver];

Chọc ngoáy createDriver() method

Lại 1 điểm nữa cần chú ý đến chính là method createDriver():

    /**
     * Create a new driver instance.
     *
     * @param  string  $driver
     * @return mixed
     *
     * @throws \InvalidArgumentException
     */
    protected function createDriver($driver)
    {
        // First, we will determine if a custom driver creator exists for the given driver and
        // if it does not we will check for a creator method for the driver. Custom creator
        // callbacks allow developers to build their own "drivers" easily using Closures.
        if (isset($this->customCreators[$driver])) {
            return $this->callCustomCreator($driver);
        } else {
            $method = 'create'.Str::studly($driver).'Driver';

            if (method_exists($this, $method)) {
                return $this->$method();
            }
        }
        throw new InvalidArgumentException("Driver [$driver] not supported.");
    }

Method này có tác dụng là lấy ra custom driver hoặc nếu không thì sẽ trả về tự trả về 1 method nếu có:

$method = 'create'.Str::studly($driver).'Driver';

if (method_exists($this, $method)) {
    return $this->$method();
}

Ví dụ: Nếu driver là bcrypt thì nó sẽ tìm thử xem có method nào là createBcryptDriver() hay không. Đó là lý do bạn thấy trong HashManager có 1 method là:

public function createBcryptDriver()
{
    return new BcryptHasher(...);
}

Đây rồi, bạn hiểu vấn đề rồi chứ? Khi bạn gọi đến Hash::driver('bcrypt') thì tức là bạn đang thao tác với instance của class có tên là BcryptHasher đó. Tương tự như với các driver khác:

Đó là lý do mà bạn có thể dùng các thuật toán mã hóa như đã nói ở phần trên:

Hash::driver('argon'); // Sử dụng class ArgonHasher
Hash::driver('argon2Id'); // Sử dụng class Argon2IdHasher

Kết luận lại

Clas Manager có nhiệm vụ cung cấp cho bạn những driver mà bạn cần sử dụng. Nó sẽ giống như một nhà máy (factory) vậy, bạn bảo rằng bạn cần xe máy thì nó sẽ chuyển xe máy cho bạn, bạn cần máy tính, điện thoại, người yêu,… nó sẽ chuyển cho bạn với điều kiện là nó có thể tạo và hỗ trợ (mỗi nhà máy thì có 1 mục đích riêng chứ ko thể đưa cho bạn mọi thứ được đúng ko).

Việc cung cấp cho bạn những class cần thiết để sử dụng như vậy gọi là Abstract Factory. Về Abstract Factory mình sẽ tìm hiểu và viết ở bài khác sau.

Bạn có thể xem trực tiếp ví dụ về cách thực hiện trong PHP tại đây: Abstract Factory

Nguồn: Vantien.net