├── .gitattributes ├── .gitignore ├── .php-cs-fixer.php ├── .travis.yml ├── .travis ├── ci.ini └── swoole.install.sh ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml ├── publish └── hashing.php └── src ├── ConfigProvider.php ├── Contract ├── DriverInterface.php └── HashInterface.php ├── Driver ├── AbstractDriver.php ├── Argon2IDriver.php ├── Argon2IdDriver.php └── BcryptDriver.php ├── Hash.php └── HashManager.php /.gitattributes: -------------------------------------------------------------------------------- 1 | /tests export-ignore 2 | /.github export-ignore 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.lock 3 | *.cache 4 | *.log 5 | .idea/ 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | setRiskyAllowed(true) 13 | ->setRules([ 14 | '@PSR2' => true, 15 | '@Symfony' => true, 16 | '@DoctrineAnnotation' => true, 17 | '@PhpCsFixer' => true, 18 | 'header_comment' => [ 19 | 'comment_type' => 'PHPDoc', 20 | 'header' => $header, 21 | 'separate' => 'none', 22 | 'location' => 'after_declare_strict', 23 | ], 24 | 'array_syntax' => [ 25 | 'syntax' => 'short' 26 | ], 27 | 'list_syntax' => [ 28 | 'syntax' => 'short' 29 | ], 30 | 'concat_space' => [ 31 | 'spacing' => 'one' 32 | ], 33 | 'blank_line_before_statement' => [ 34 | 'statements' => [ 35 | 'declare', 36 | ], 37 | ], 38 | 'general_phpdoc_annotation_remove' => [ 39 | 'annotations' => [ 40 | 'author' 41 | ], 42 | ], 43 | 'ordered_imports' => [ 44 | 'imports_order' => [ 45 | 'class', 'function', 'const', 46 | ], 47 | 'sort_algorithm' => 'alpha', 48 | ], 49 | 'single_line_comment_style' => [ 50 | 'comment_types' => [ 51 | ], 52 | ], 53 | 'yoda_style' => [ 54 | 'always_move_variable' => false, 55 | 'equal' => false, 56 | 'identical' => false, 57 | ], 58 | 'phpdoc_align' => [ 59 | 'align' => 'left', 60 | ], 61 | 'multiline_whitespace_before_semicolons' => [ 62 | 'strategy' => 'no_multi_line', 63 | ], 64 | 'constant_case' => [ 65 | 'case' => 'lower', 66 | ], 67 | 'class_attributes_separation' => true, 68 | 'combine_consecutive_unsets' => true, 69 | 'declare_strict_types' => true, 70 | 'linebreak_after_opening_tag' => true, 71 | 'lowercase_static_reference' => true, 72 | 'no_useless_else' => true, 73 | 'no_unused_imports' => true, 74 | 'not_operator_with_successor_space' => true, 75 | 'not_operator_with_space' => false, 76 | 'ordered_class_elements' => true, 77 | 'php_unit_strict' => false, 78 | 'phpdoc_separation' => false, 79 | 'single_quote' => true, 80 | 'standardize_not_equals' => true, 81 | 'multiline_comment_opening_closing' => true, 82 | ]) 83 | ->setFinder( 84 | PhpCsFixer\Finder::create() 85 | ->exclude('bin') 86 | ->exclude('public') 87 | ->exclude('runtime') 88 | ->exclude('vendor') 89 | ->in(__DIR__) 90 | ) 91 | ->setUsingCache(false); 92 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: required 4 | 5 | matrix: 6 | include: 7 | - php: 7.2 8 | env: SW_VERSION="4.5.3RC1" 9 | - php: 7.3 10 | env: SW_VERSION="4.5.3RC1" 11 | - php: 7.4 12 | env: SW_VERSION="4.5.3RC1" 13 | 14 | allow_failures: 15 | - php: master 16 | 17 | services: 18 | - docker 19 | 20 | before_install: 21 | - export PHP_MAJOR="$(`phpenv which php` -r 'echo phpversion();' | cut -d '.' -f 1)" 22 | - export PHP_MINOR="$(`phpenv which php` -r 'echo phpversion();' | cut -d '.' -f 2)" 23 | - echo $PHP_MAJOR 24 | - echo $PHP_MINOR 25 | 26 | install: 27 | - cd $TRAVIS_BUILD_DIR 28 | - bash .travis/swoole.install.sh 29 | - phpenv config-rm xdebug.ini || echo "xdebug not available" 30 | - phpenv config-add .travis/ci.ini 31 | 32 | before_script: 33 | - cd $TRAVIS_BUILD_DIR 34 | - composer config -g process-timeout 900 && composer update 35 | 36 | script: 37 | - composer analyse 38 | - composer test -------------------------------------------------------------------------------- /.travis/ci.ini: -------------------------------------------------------------------------------- 1 | [opcache] 2 | opcache.enable_cli=1 3 | 4 | [swoole] 5 | extension = "swoole.so" 6 | -------------------------------------------------------------------------------- /.travis/swoole.install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | sudo apt-get update 3 | sudo apt-get install libcurl4-openssl-dev 4 | wget https://github.com/swoole/swoole-src/archive/${SW_VERSION}.tar.gz -O swoole.tar.gz 5 | mkdir -p swoole 6 | tar -xf swoole.tar.gz -C swoole --strip-components=1 7 | rm swoole.tar.gz 8 | cd swoole 9 | phpize 10 | ./configure --enable-openssl --enable-http2 --enable-swoole-curl --enable-swoole-json 11 | make -j$(nproc) 12 | sudo make install 13 | sudo sh -c "echo extension=swoole > /etc/php/${PHP_VERSION}/cli/conf.d/swoole.ini" 14 | sudo sh -c "echo swoole.use_shortname='Off' >> /etc/php/${PHP_VERSION}/cli/conf.d/swoole.ini" 15 | php --ri swoole 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Taylor Otwell 4 | Copyright (c) Eric Zhu 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hyperf 哈希组件 2 | 3 | 该组件为存储用户密码提供了安全的 Bcrypt 和 Argon2 哈希加密方式。 4 | 5 | > 移植自 [illuminate/hashing](https://github.com/illuminate/hashing )。 6 | 7 | ## 安装 8 | 9 | ```shell script 10 | composer require hyperf-ext/hashing 11 | ``` 12 | 13 | ## 发布配置 14 | 15 | ```shell script 16 | php bin/hyperf.php vendor:publish hyperf-ext/hashing 17 | ``` 18 | 19 | > 配置文件位于 `config/autoload/hashing.php`。 20 | 21 | ## 默认配置 22 | 23 | ```php 24 | 'bcrypt', 30 | 'driver' => [ 31 | 'bcrypt' => [ 32 | 'class' => \HyperfExt\Hashing\Driver\BcryptDriver::class, 33 | 'rounds' => env('BCRYPT_ROUNDS', 10), 34 | ], 35 | 'argon' => [ 36 | 'class' => \HyperfExt\Hashing\Driver\Argon2IDriver::class, 37 | 'memory' => 1024, 38 | 'threads' => 2, 39 | 'time' => 2, 40 | ], 41 | 'argon2id' => [ 42 | 'class' => \HyperfExt\Hashing\Driver\Argon2IdDriver::class, 43 | 'memory' => 1024, 44 | 'threads' => 2, 45 | 'time' => 2, 46 | ], 47 | ], 48 | ]; 49 | ``` 50 | 51 | 你可以在 `config/autoload/hashing.php` 配置文件中配置默认哈希驱动程序。目前支持三种驱动程序: Bcrypt 和 Argon2(Argon2i 和 Argon2id variants)。 52 | 53 | > 注意:Argon2i 驱动程序需要 PHP 7.2.0 或更高版本,而 Argon2id 驱动程序则需要 PHP 7.3.0 或更高版本。 54 | 55 | ## 使用 56 | 57 | 你可以通过 `\HyperfExt\Hashing\Hash` 类来加密你的密码: 58 | 59 | ```php 60 | fill([ 76 | 'password' => Hash::make($request->input('new_password')) 77 | ])->save(); 78 | } 79 | } 80 | ``` 81 | 82 | ### 调整 Bcrypt 加密系数 83 | 84 | 如果使用 Bcrypt 算法,你可以在 `make` 方法中使用 `rounds` 选项来配置该算法的加密系数。然而,对大多数应用程序来说,默认值就足够了: 85 | 86 | ```php 87 | $hashed = Hash::make('password', [ 88 | 'rounds' => 12 89 | ]); 90 | ``` 91 | 92 | ### 调整 Argon2 加密系数 93 | 94 | 如果使用 Argon2 算法,你可以在 `make` 方法中使用 `memory`,`time` 和 `threads` 选项来配置该算法的加密系数。然后,对大多数应用程序来说,默认值就足够了: 95 | 96 | ```php 97 | $hashed = Hash::make('password', [ 98 | 'memory' => 1024, 99 | 'time' => 2, 100 | 'threads' => 2, 101 | ]); 102 | ``` 103 | 104 | > 有关这些选项的更多信息,请查阅 [PHP 官方文档](https://secure.php.net/manual/en/function.password-hash.php )。 105 | 106 | ### 密码哈希验证 107 | 108 | `check` 方法能为您验证一段给定的未加密字符串与给定的哈希值是否一致: 109 | 110 | ```php 111 | if (Hash::check('plain-text', $hashedPassword)) { 112 | // 密码匹配… 113 | } 114 | ``` 115 | 116 | ### 检查密码是否需要重新哈希 117 | 118 | `needsRehash` 方法可以为您检查当哈希的加密系数改变时,您的密码是否被新的加密系数重新加密过: 119 | 120 | ```php 121 | if (Hash::needsRehash($hashed)) { 122 | $hashed = Hash::make('plain-text'); 123 | } 124 | ``` 125 | 126 | ### 使用指定驱动 127 | 128 | ```php 129 | $hasher = Hash::getDriver('argon2i'); 130 | $hasher->make('plain-text'); 131 | ``` 132 | 133 | ### 使用自定义哈希类 134 | 135 | 实现 `\HyperfExt\Hashing\Contract\DriverInterface` 接口,并参照配置文件中的其他算法进行配置。 136 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyperf-ext/hashing", 3 | "type": "library", 4 | "license": "MIT", 5 | "keywords": [ 6 | "php", 7 | "hyperf", 8 | "hashing" 9 | ], 10 | "description": "The Hyperf Hashing package.", 11 | "authors": [ 12 | { 13 | "name": "Eric Zhu", 14 | "email": "eric@zhu.email" 15 | }, 16 | { 17 | "name": "Taylor Otwell", 18 | "email": "taylor@laravel.com" 19 | } 20 | ], 21 | "autoload": { 22 | "psr-4": { 23 | "HyperfExt\\Hashing\\": "src/" 24 | } 25 | }, 26 | "autoload-dev": { 27 | "psr-4": { 28 | "HyperfTest\\": "tests" 29 | } 30 | }, 31 | "require": { 32 | "php": ">=7.3", 33 | "hyperf/config": "^2.1", 34 | "hyperf/di": "^2.1", 35 | "hyperf/framework": "^2.1" 36 | }, 37 | "require-dev": { 38 | "friendsofphp/php-cs-fixer": "^3.0", 39 | "hyperf/testing": "^2.1", 40 | "phpstan/phpstan": "^0.12", 41 | "swoole/ide-helper": "dev-master" 42 | }, 43 | "config": { 44 | "sort-packages": true 45 | }, 46 | "scripts": { 47 | "test": "phpunit --prepend tests/bootstrap.php -c phpunit.xml --colors=always", 48 | "analyse": "phpstan analyse --memory-limit 1024M -l 0 ./src", 49 | "cs-fix": "php-cs-fixer fix $1" 50 | }, 51 | "extra": { 52 | "hyperf": { 53 | "config": "HyperfExt\\Hashing\\ConfigProvider" 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | ./tests/ 14 | 15 | -------------------------------------------------------------------------------- /publish/hashing.php: -------------------------------------------------------------------------------- 1 | 'bcrypt', 24 | 25 | 'driver' => [ 26 | /* 27 | |-------------------------------------------------------------------------- 28 | | Bcrypt Options 29 | |-------------------------------------------------------------------------- 30 | | 31 | | Here you may specify the configuration options that should be used when 32 | | passwords are hashed using the Bcrypt algorithm. This will allow you 33 | | to control the amount of time it takes to hash the given password. 34 | | 35 | */ 36 | 37 | 'bcrypt' => [ 38 | 'class' => \HyperfExt\Hashing\Driver\BcryptDriver::class, 39 | 'options' => [ 40 | 'rounds' => env('BCRYPT_ROUNDS', 10), 41 | ], 42 | ], 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Argon Options 47 | |-------------------------------------------------------------------------- 48 | | 49 | | Here you may specify the configuration options that should be used when 50 | | passwords are hashed using the Argon algorithm. These will allow you 51 | | to control the amount of time it takes to hash the given password. 52 | | 53 | */ 54 | 55 | 'argon2i' => [ 56 | 'class' => \HyperfExt\Hashing\Driver\Argon2IDriver::class, 57 | 'options' => [ 58 | 'memory' => 1024, 59 | 'threads' => 2, 60 | 'time' => 2, 61 | ], 62 | ], 63 | 64 | 'argon2id' => [ 65 | 'class' => \HyperfExt\Hashing\Driver\Argon2IdDriver::class, 66 | 'options' => [ 67 | 'memory' => 1024, 68 | 'threads' => 2, 69 | 'time' => 2, 70 | ], 71 | ], 72 | ], 73 | ]; 74 | -------------------------------------------------------------------------------- /src/ConfigProvider.php: -------------------------------------------------------------------------------- 1 | [ 21 | HashInterface::class => HashManager::class, 22 | ], 23 | 'annotations' => [ 24 | 'scan' => [ 25 | 'paths' => [ 26 | __DIR__, 27 | ], 28 | ], 29 | ], 30 | 'publish' => [ 31 | [ 32 | 'id' => 'config', 33 | 'description' => 'The config for hyperf-ext/hashing.', 34 | 'source' => __DIR__ . '/../publish/hashing.php', 35 | 'destination' => BASE_PATH . '/config/autoload/hashing.php', 36 | ], 37 | ], 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Contract/DriverInterface.php: -------------------------------------------------------------------------------- 1 | time = $options['time'] ?? $this->time; 52 | $this->memory = $options['memory'] ?? $this->memory; 53 | $this->threads = $options['threads'] ?? $this->threads; 54 | $this->verifyAlgorithm = $options['verify'] ?? $this->verifyAlgorithm; 55 | } 56 | 57 | /** 58 | * Hash the given value. 59 | * 60 | * @throws \RuntimeException 61 | */ 62 | public function make(string $value, array $options = []): string 63 | { 64 | $hash = password_hash($value, $this->algorithm(), [ 65 | 'memory_cost' => $this->memory($options), 66 | 'time_cost' => $this->time($options), 67 | 'threads' => $this->threads($options), 68 | ]); 69 | 70 | if ($hash === false) { 71 | throw new RuntimeException('Argon2 hashing not supported.'); 72 | } 73 | 74 | return $hash; 75 | } 76 | 77 | /** 78 | * Check the given plain value against a hash. 79 | * 80 | * @throws \RuntimeException 81 | */ 82 | public function check(string $value, string $hashedValue, array $options = []): bool 83 | { 84 | if ($this->verifyAlgorithm && $this->info($hashedValue)['algoName'] !== 'argon2i') { 85 | throw new RuntimeException('This password does not use the Argon2i algorithm.'); 86 | } 87 | 88 | return parent::check($value, $hashedValue, $options); 89 | } 90 | 91 | /** 92 | * Check if the given hash has been hashed using the given options. 93 | */ 94 | public function needsRehash(string $hashedValue, array $options = []): bool 95 | { 96 | return password_needs_rehash($hashedValue, $this->algorithm(), [ 97 | 'memory_cost' => $this->memory($options), 98 | 'time_cost' => $this->time($options), 99 | 'threads' => $this->threads($options), 100 | ]); 101 | } 102 | 103 | /** 104 | * Set the default password memory factor. 105 | * 106 | * @return $this 107 | */ 108 | public function setMemory(int $memory): self 109 | { 110 | $this->memory = $memory; 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * Set the default password timing factor. 117 | * 118 | * @return $this 119 | */ 120 | public function setTime(int $time): self 121 | { 122 | $this->time = $time; 123 | 124 | return $this; 125 | } 126 | 127 | /** 128 | * Set the default password threads factor. 129 | * 130 | * @return $this 131 | */ 132 | public function setThreads(int $threads): self 133 | { 134 | $this->threads = $threads; 135 | 136 | return $this; 137 | } 138 | 139 | /** 140 | * Get the algorithm that should be used for hashing. 141 | * 142 | * @return int 143 | */ 144 | protected function algorithm() 145 | { 146 | return PASSWORD_ARGON2I; 147 | } 148 | 149 | /** 150 | * Extract the memory cost value from the options array. 151 | */ 152 | protected function memory(array $options): int 153 | { 154 | return $options['memory'] ?? $this->memory; 155 | } 156 | 157 | /** 158 | * Extract the time cost value from the options array. 159 | */ 160 | protected function time(array $options): int 161 | { 162 | return $options['time'] ?? $this->time; 163 | } 164 | 165 | /** 166 | * Extract the threads value from the options array. 167 | */ 168 | protected function threads(array $options): int 169 | { 170 | return $options['threads'] ?? $this->threads; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/Driver/Argon2IdDriver.php: -------------------------------------------------------------------------------- 1 | verifyAlgorithm && $this->info($hashedValue)['algoName'] !== 'argon2id') { 25 | throw new RuntimeException('This password does not use the Argon2id algorithm.'); 26 | } 27 | 28 | if (strlen($hashedValue) === 0) { 29 | return false; 30 | } 31 | 32 | return password_verify($value, $hashedValue); 33 | } 34 | 35 | /** 36 | * Get the algorithm that should be used for hashing. 37 | * 38 | * @return int 39 | */ 40 | protected function algorithm() 41 | { 42 | return PASSWORD_ARGON2ID; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Driver/BcryptDriver.php: -------------------------------------------------------------------------------- 1 | rounds = $options['rounds'] ?? $this->rounds; 38 | $this->verifyAlgorithm = $options['verify'] ?? $this->verifyAlgorithm; 39 | } 40 | 41 | /** 42 | * Hash the given value. 43 | * 44 | * @throws \RuntimeException 45 | */ 46 | public function make(string $value, array $options = []): string 47 | { 48 | $hash = password_hash($value, PASSWORD_BCRYPT, [ 49 | 'cost' => $this->cost($options), 50 | ]); 51 | 52 | if ($hash === false) { 53 | throw new RuntimeException('Bcrypt hashing not supported.'); 54 | } 55 | 56 | return $hash; 57 | } 58 | 59 | /** 60 | * Check the given plain value against a hash. 61 | * 62 | * @throws \RuntimeException 63 | */ 64 | public function check(string $value, string $hashedValue, array $options = []): bool 65 | { 66 | if ($this->verifyAlgorithm && $this->info($hashedValue)['algoName'] !== 'bcrypt') { 67 | throw new RuntimeException('This password does not use the Bcrypt algorithm.'); 68 | } 69 | 70 | return parent::check($value, $hashedValue, $options); 71 | } 72 | 73 | /** 74 | * Check if the given hash has been hashed using the given options. 75 | */ 76 | public function needsRehash(string $hashedValue, array $options = []): bool 77 | { 78 | return password_needs_rehash($hashedValue, PASSWORD_BCRYPT, [ 79 | 'cost' => $this->cost($options), 80 | ]); 81 | } 82 | 83 | /** 84 | * Set the default password work factor. 85 | * 86 | * @return $this 87 | */ 88 | public function setRounds(int $rounds): self 89 | { 90 | $this->rounds = $rounds; 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * Extract the cost value from the options array. 97 | */ 98 | protected function cost(array $options = []): int 99 | { 100 | return $options['rounds'] ?? $this->rounds; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Hash.php: -------------------------------------------------------------------------------- 1 | get(HashInterface::class)->getDriver($name); 22 | } 23 | 24 | public static function info(string $hashedValue, ?string $driverName = null): array 25 | { 26 | return static::getDriver($driverName)->info($hashedValue); 27 | } 28 | 29 | public static function make(string $value, array $options = [], ?string $driverName = null): string 30 | { 31 | return static::getDriver($driverName)->make($value, $options); 32 | } 33 | 34 | public static function check(string $value, string $hashedValue, array $options = [], ?string $driverName = null): bool 35 | { 36 | return static::getDriver($driverName)->check($value, $hashedValue, $options); 37 | } 38 | 39 | public static function needsRehash(string $hashedValue, array $options = [], ?string $driverName = null): bool 40 | { 41 | return static::getDriver($driverName)->needsRehash($hashedValue, $options); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/HashManager.php: -------------------------------------------------------------------------------- 1 | config = $config; 38 | } 39 | 40 | /** 41 | * Get information about the given hashed value. 42 | */ 43 | public function info(string $hashedValue): array 44 | { 45 | return $this->getDriver()->info($hashedValue); 46 | } 47 | 48 | /** 49 | * Hash the given value. 50 | */ 51 | public function make(string $value, array $options = []): string 52 | { 53 | return $this->getDriver()->make($value, $options); 54 | } 55 | 56 | /** 57 | * Check the given plain value against a hash. 58 | */ 59 | public function check(string $value, string $hashedValue, array $options = []): bool 60 | { 61 | return $this->getDriver()->check($value, $hashedValue, $options); 62 | } 63 | 64 | /** 65 | * Check if the given hash has been hashed using the given options. 66 | */ 67 | public function needsRehash(string $hashedValue, array $options = []): bool 68 | { 69 | return $this->getDriver()->needsRehash($hashedValue, $options); 70 | } 71 | 72 | /** 73 | * Get a driver instance. 74 | * 75 | * @throws \InvalidArgumentException 76 | */ 77 | public function getDriver(?string $name = null): DriverInterface 78 | { 79 | if (isset($this->drivers[$name]) && $this->drivers[$name] instanceof DriverInterface) { 80 | return $this->drivers[$name]; 81 | } 82 | 83 | $name = $name ?: $this->config->get('hashing.default', 'bcrypt'); 84 | 85 | $config = $this->config->get("hashing.driver.{$name}"); 86 | if (empty($config) or empty($config['class'])) { 87 | throw new InvalidArgumentException(sprintf('The hashing driver config %s is invalid.', $name)); 88 | } 89 | 90 | $driverClass = $config['class'] ?? BcryptDriver::class; 91 | 92 | $driver = make($driverClass, ['options' => $config['options'] ?? []]); 93 | 94 | return $this->drivers[$name] = $driver; 95 | } 96 | } 97 | --------------------------------------------------------------------------------