Merge branch 'feat/validation' of https://github.com/chunhei2008/hyperf into feat/validation

This commit is contained in:
wangyi 2019-09-06 16:28:04 +08:00
commit a2ec86b54c
115 changed files with 2508 additions and 2075 deletions

View File

@ -1,4 +1,34 @@
# v1.0.13 - TBD # v1.0.14 - TBD
## Changed
- [#482](https://github.com/hyperf-cloud/hyperf/pull/482) Re-generate the `fillable` argument of Model when use `refresh-fillable` option, at the same time, the command will keep the `fillable` argument as default behaviours.
- [#501](https://github.com/hyperf-cloud/hyperf/pull/501) When the path argument of Mapping annotation is an empty string, then the path is equal to prefix of Controller annotation.
- [#513](https://github.com/hyperf-cloud/hyperf/pull/513) Rewrite process name with `app_name`.
## Fixed
- [#479](https://github.com/hyperf-cloud/hyperf/pull/479) Fixed typehint error when host of Elasticsearch client does not reached.
- [#508](https://github.com/hyperf-cloud/hyperf/pull/508) Fixed return type error of `Hyperf\Utils\Coroutine::parentId()` when running in non-coroutine environment.
- [#514](https://github.com/hyperf-cloud/hyperf/pull/514) Fixed redis auth failed when the password is an empty string.
# v1.0.13 - 2019-08-28
## Added
- [#428](https://github.com/hyperf-cloud/hyperf/pull/428) Added an independent component [hyperf/translation](https://github.com/hyperf-cloud/translation), forked by illuminate/translation.
- [#449](https://github.com/hyperf-cloud/hyperf/pull/449) Added standard error code for grpc-server.
- [#450](https://github.com/hyperf-cloud/hyperf/pull/450) Added comments of static methods for `Hyperf\Database\Schema\Schema`.
## Changed
- [#451](https://github.com/hyperf-cloud/hyperf/pull/451) Removed routes of magic methods from `AuthController`.
- [#468](https://github.com/hyperf-cloud/hyperf/pull/468) Default exception handlers catch all exceptions.
## Fixed
- [#466](https://github.com/hyperf-cloud/hyperf/pull/466) Fixed error when the number of data is not enough to paginate.
- [#466](https://github.com/hyperf-cloud/hyperf/pull/470) Optimized `vendor:publish` command, if the destination folder exists, then will not repeatedly create the folder.
# v1.0.12 - 2019-08-21 # v1.0.12 - 2019-08-21

View File

@ -38,8 +38,8 @@
"squizlabs/php_codesniffer": "^3.4", "squizlabs/php_codesniffer": "^3.4",
"symfony/console": "^4.2", "symfony/console": "^4.2",
"symfony/finder": "^4.1", "symfony/finder": "^4.1",
"symfony/http-foundation": "^4.3", "vlucas/phpdotenv": "^3.1",
"vlucas/phpdotenv": "^3.1" "egulias/email-validator": "^2.1"
}, },
"require-dev": { "require-dev": {
"doctrine/common": "@stable", "doctrine/common": "@stable",
@ -107,6 +107,7 @@
"files": [ "files": [
"src/config/src/Functions.php", "src/config/src/Functions.php",
"src/di/src/Functions.php", "src/di/src/Functions.php",
"src/translation/src/Functions.php",
"src/utils/src/Functions.php" "src/utils/src/Functions.php"
], ],
"psr-4": { "psr-4": {
@ -191,6 +192,7 @@
"HyperfTest\\Etcd\\": "src/etcd/tests/", "HyperfTest\\Etcd\\": "src/etcd/tests/",
"HyperfTest\\Event\\": "src/event/tests/", "HyperfTest\\Event\\": "src/event/tests/",
"HyperfTest\\GrpcClient\\": "src/grpc-client/tests/", "HyperfTest\\GrpcClient\\": "src/grpc-client/tests/",
"HyperfTest\\GrpcServer\\": "src/grpc-server/tests/",
"HyperfTest\\Guzzle\\": "src/guzzle/tests/", "HyperfTest\\Guzzle\\": "src/guzzle/tests/",
"HyperfTest\\HttpMessage\\": "src/http-message/tests/", "HyperfTest\\HttpMessage\\": "src/http-message/tests/",
"HyperfTest\\HttpServer\\": "src/http-server/tests/", "HyperfTest\\HttpServer\\": "src/http-server/tests/",

103
doc/zh/snowflake.md Normal file
View File

@ -0,0 +1,103 @@
# Snowflake
## 安装
```
composer require hyperf/snowflake
```
## 使用
框架提供了 `MetaGeneratorInterface``IdGeneratorInterface``MetaGeneratorInterface` 会生成 `ID``Meta` 文件,`IdGeneratorInterface` 则会根据对应的 `Meta` 文件生成 `分布式ID`
框架默认使用的 `MetaGeneratorInterface` 是基于 `Redis` 实现的 `毫秒级别生成器`。配置如下:
```php
<?php
declare(strict_types=1);
use Hyperf\Snowflake\MetaGenerator\RedisMilliSecondMetaGenerator;
use Hyperf\Snowflake\MetaGeneratorInterface;
return [
'begin_second' => MetaGeneratorInterface::DEFAULT_BEGIN_SECOND,
RedisMilliSecondMetaGenerator::class => [
'pool' => 'default',
],
];
```
框架中使用 `Snowfalke` 十分简单,只需要从 `DI` 中取出 `IdGeneratorInterface` 对象即可。
```php
<?php
use Hyperf\Snowflake\IdGeneratorInterface;
use Hyperf\Utils\ApplicationContext;
$container = ApplicationContext::getContainer();
$generator = $container->get(IdGeneratorInterface::class);
$id = $generator->generate();
```
当知道 `ID` 需要反推对应的 `Meta` 时,只需要调用 `degenerate` 即可。
```php
<?php
use Hyperf\Snowflake\IdGeneratorInterface;
use Hyperf\Utils\ApplicationContext;
$container = ApplicationContext::getContainer();
$generator = $container->get(IdGeneratorInterface::class);
$meta = $generator->degenerate($id);
```
## 重写 `Meta` 生成器
`分布式ID` 的实现方式多种多样,虽然都是 `Snowflake` 算法,但也不尽相同。比如有人可能会根据 `UserId` 生成 `Meta`,而非 `WorkerId`。接下来,让我们实现一个简单的 `MetaGenerator`
简单的来讲,`UserId` 绝对会超过 `10 bit`,所以默认的 `DataCenterId``WorkerId` 肯定是装不过来的,所以就需要对 `UserId` 取模。
```php
<?php
declare(strict_types=1);
use Hyperf\Snowflake\IdGenerator;
class UserDefinedIdGenerator
{
/**
* @var IdGenerator\SnowflakeIdGenerator
*/
protected $idGenerator;
public function __construct(IdGenerator\SnowflakeIdGenerator $idGenerator)
{
$this->idGenerator = $idGenerator;
}
public function generate(int $userId)
{
$meta = $this->idGenerator->getMetaGenerator()->generate();
return $this->idGenerator->generate($meta->setWorkerId($userId % 31));
}
public function degenerate(int $id)
{
return $this->idGenerator->degenerate($id);
}
}
use Hyperf\Utils\ApplicationContext;
$container = ApplicationContext::getContainer();
$generator = $container->get(UserDefinedIdGenerator::class);
$userId = 20190620;
$id = $generator->generate($userId);
```

138
doc/zh/translation.md Normal file
View File

@ -0,0 +1,138 @@
# 国际化
Hyperf 对国际化的支持是非常友好的,允许让您的项目支持多种语言。
# 安装
```bash
composer require hyperf/translation
```
> 该组件为一个独立组件,无框架相关依赖,可独立复用于其它项目或框架。
# 语言文件
Hyperf 的语言文件默认都放在 `storage/languages` 下面,您也可以在 `config/autoload/translation.php` 内更改语言文件的文件夹,每种语言对应其中的一个子文件夹,例如 `en` 指英文语言文件,`zh-CN` 指中文简体的语言文件,你可以按照实际需要创建新的语言文件夹和里面的语言文件。示例如下:
```
/storage
/languages
/en
messages.php
/zh-CN
messages.php
```
所有的语言文件都是返回一个数组,数组的键是字符串类型的:
```php
<?php
// storage/languages/en/messages.php
return [
'welcome' => 'Welcome to our application',
];
```
## 配置语言环境
关于国际化组件的相关配置都是在 `config/autoload/translation.php` 配置文件里设定的,你可以按照实际需要修改它。
```php
<?php
// config/autoload/translation.php
return [
// 默认语言
'locale' => 'zh_CN',
// 回退语言,当默认语言的语言文本没有提供时,就会使用回退语言的对应语言文本
'fallback_locale' => 'en',
// 语言文件存放的文件夹
'path' => BASE_PATH . '/storage/languages',
];
```
# 翻译字符串
## 通过 TranslatorInterface 翻译
可直接通过注入 `Hyperf\Contact\TranslatorInterface` 并调用实例的 `trans` 方法实现对字符串的翻译:
```php
<?php
use Hyperf\Di\Annotation\Inject;
use Hyperf\Contract\TranslatorInterface;
class FooController
{
/**
* @Inject
* @var TranslatorInterface
*/
private $translator;
public function index()
{
return $this->translator->trans('messages.welcome', [], 'zh-CN');
}
}
```
## 通过全局函数翻译
您也可以通过全局函数 `__()``trans()` 来对字符串进行翻译。
函数的第一个参数使用 `键`(指使用翻译字符串作为键的键) 或者是 `文件.键` 的形式。
```php
echo __('messages.welcome');
echo trans('messages.welcome');
```
# 翻译字符串中定义占位符
您也可以在语言字符串中定义占位符,所有的占位符使用 `:` 作为前缀。例如,把用户名作为占位符:
```php
<?php
// storage/languages/en/messages.php
return [
'welcome' => 'Welcome :name',
];
```
替换占位符使用函数的第二个参数:
```php
echo __('messages.welcome', ['name' => 'Hyperf']);
```
如果占位符全部是大写字母,或者是首字母大写。那么翻译过来的字符串也会是相应的大写形式:
```php
'welcome' => 'Welcome, :NAME', // Welcome, HYPERF
'goodbye' => 'Goodbye, :Name', // Goodbye, HYPERF
```
# 处理复数
不同语言的复数规则是不同的,在中文中可能不太关注这一点,但在翻译其它语言时我们需要处理复数形式的用词。我们可以使用 `「管道」` 字符,可以用来区分字符串的单数和复数形式:
```php
'apples' => 'There is one apple|There are many apples',
```
也可以指定数字范围,创建更加复杂的复数规则:
```php
'apples' => '{0} There are none|[1,19] There are some|[20,*] There are many',
```
使用 `「管道」` 字符,定义好复数规则后,就可以使用全局函数 `trans_choice` 来获得给定 `「数量」` 的字符串文本。在下面的例子中,因为数量大于 `1`,所以就会返回翻译字符串的复数形式:
```php
echo trans_choice('messages.apples', 10);
```
当然除了全局函数 `trans_choice()`,您也可以使用 `Hyperf\Contract\TranslatorInterface``transChoice` 方法。

View File

@ -23,6 +23,7 @@
<directory suffix="Test.php">./src/elasticsearch/tests</directory> <directory suffix="Test.php">./src/elasticsearch/tests</directory>
<directory suffix="Test.php">./src/event/tests</directory> <directory suffix="Test.php">./src/event/tests</directory>
<directory suffix="Test.php">./src/grpc-client/tests</directory> <directory suffix="Test.php">./src/grpc-client/tests</directory>
<directory suffix="Test.php">./src/grpc-server/tests</directory>
<directory suffix="Test.php">./src/guzzle/tests</directory> <directory suffix="Test.php">./src/guzzle/tests</directory>
<directory suffix="Test.php">./src/http-message/tests</directory> <directory suffix="Test.php">./src/http-message/tests</directory>
<directory suffix="Test.php">./src/http-server/tests</directory> <directory suffix="Test.php">./src/http-server/tests</directory>

View File

@ -10,42 +10,29 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace Hyperf\Translation\Contracts; namespace Hyperf\Contract;
interface Translator interface TranslatorInterface
{ {
/** /**
* Get the translation for a given key. * Get the translation for a given key.
*
* @param string $key
* @param array $replace
* @param null|string $locale
* @return mixed
*/ */
public function trans(string $key, array $replace = [], $locale = null); public function trans(string $key, array $replace = [], ?string $locale = null);
/** /**
* Get a translation according to an integer value. * Get a translation according to an integer value.
* *
* @param string $key
* @param array|\Countable|int $number * @param array|\Countable|int $number
* @param array $replace
* @param null|string $locale
* @return string
*/ */
public function transChoice(string $key, $number, array $replace = [], $locale = null): string; public function transChoice(string $key, $number, array $replace = [], ?string $locale = null): string;
/** /**
* Get the default locale being used. * Get the default locale being used.
*
* @return string
*/ */
public function getLocale(): string; public function getLocale(): string;
/** /**
* Set the default locale. * Set the default locale.
*
* @param string $locale
*/ */
public function setLocale(string $locale); public function setLocale(string $locale);
} }

View File

@ -10,39 +10,27 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace Hyperf\Translation\Contracts; namespace Hyperf\Contract;
interface Loader interface TranslatorLoaderInterface
{ {
/** /**
* Load the messages for the given locale. * Load the messages for the given locale.
*
* @param string $locale
* @param string $group
* @param null|string $namespace
* @return array
*/ */
public function load(string $locale, string $group, $namespace = null): array; public function load(string $locale, string $group, ?string $namespace = null): array;
/** /**
* Add a new namespace to the loader. * Add a new namespace to the loader.
*
* @param string $namespace
* @param string $hint
*/ */
public function addNamespace(string $namespace, string $hint); public function addNamespace(string $namespace, string $hint);
/** /**
* Add a new JSON path to the loader. * Add a new JSON path to the loader.
*
* @param string $path
*/ */
public function addJsonPath(string $path); public function addJsonPath(string $path);
/** /**
* Get an array of all the registered namespaces. * Get an array of all the registered namespaces.
*
* @return array
*/ */
public function namespaces(): array; public function namespaces(): array;
} }

View File

@ -1,47 +1,30 @@
<?php <?php
declare(strict_types=1); namespace Hyperf\Contract;
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Validation\Contracts\Validation; use Hyperf\Utils\Contracts\MessageProvider;
use Hyperf\Utils\Contracts\MessageBag;
use Hyperf\Validation\Contracts\Support\MessageProvider; interface ValidatorInterface extends MessageProvider
use Hyperf\Validation\Support\MessageBag;
interface Validator extends MessageProvider
{ {
/** /**
* Run the validator's rules against its data. * Run the validator's rules against its data.
*
* @return array
*/ */
public function validate(): array; public function validate(): array;
/** /**
* Get the attributes and values that were validated. * Get the attributes and values that were validated.
*
* @return array
*/ */
public function validated(): array; public function validated(): array;
/** /**
* Determine if the data fails the validation rules. * Determine if the data fails the validation rules.
*
* @return bool
*/ */
public function fails(): bool; public function fails(): bool;
/** /**
* Get the failed validation rules. * Get the failed validation rules.
*
* @return array
*/ */
public function failed(): array; public function failed(): array;
@ -50,7 +33,6 @@ interface Validator extends MessageProvider
* *
* @param array|string $attribute * @param array|string $attribute
* @param array|string $rules * @param array|string $rules
* @param callable $callback
* @return $this * @return $this
*/ */
public function sometimes($attribute, $rules, callable $callback); public function sometimes($attribute, $rules, callable $callback);
@ -65,8 +47,7 @@ interface Validator extends MessageProvider
/** /**
* Get all of the validation error messages. * Get all of the validation error messages.
*
* @return MessageBag
*/ */
public function errors(); public function errors(): MessageBag;
}
}

View File

@ -12,24 +12,34 @@ declare(strict_types=1);
namespace Hyperf\Database\Commands\Ast; namespace Hyperf\Database\Commands\Ast;
use Hyperf\Database\Commands\ModelOption;
use PhpParser\Comment\Doc; use PhpParser\Comment\Doc;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\NodeVisitorAbstract; use PhpParser\NodeVisitorAbstract;
class ModelUpdateVisitor extends NodeVisitorAbstract class ModelUpdateVisitor extends NodeVisitorAbstract
{ {
/**
* @var array
*/
protected $columns = []; protected $columns = [];
public function __construct($columns = []) /**
* @var ModelOption
*/
protected $option;
public function __construct($columns = [], ModelOption $option)
{ {
$this->columns = $columns; $this->columns = $columns;
$this->option = $option;
} }
public function leaveNode(Node $node) public function leaveNode(Node $node)
{ {
switch ($node) { switch ($node) {
case $node instanceof Node\Stmt\PropertyProperty: case $node instanceof Node\Stmt\PropertyProperty:
if ($node->name == 'fillable') { if ($node->name == 'fillable' && $this->option->isRefreshFillable()) {
$node = $this->rewriteFillable($node); $node = $this->rewriteFillable($node);
} elseif ($node->name == 'casts') { } elseif ($node->name == 'casts') {
$node = $this->rewriteCasts($node); $node = $this->rewriteCasts($node);

View File

@ -93,7 +93,8 @@ class ModelCommand extends Command
->setPrefix($this->getOption('prefix', 'prefix', $pool, '')) ->setPrefix($this->getOption('prefix', 'prefix', $pool, ''))
->setInheritance($this->getOption('inheritance', 'commands.db:model.inheritance', $pool, 'Model')) ->setInheritance($this->getOption('inheritance', 'commands.db:model.inheritance', $pool, 'Model'))
->setUses($this->getOption('uses', 'commands.db:model.uses', $pool, 'Hyperf\DbConnection\Model\Model')) ->setUses($this->getOption('uses', 'commands.db:model.uses', $pool, 'Hyperf\DbConnection\Model\Model'))
->setForceCasts($this->getOption('force-casts', 'commands.db:model.force_casts', $pool, false)); ->setForceCasts($this->getOption('force-casts', 'commands.db:model.force_casts', $pool, false))
->setRefreshFillable($this->getOption('refresh-fillable', 'commands.db:model.refresh_fillable', $pool, false));
if ($table) { if ($table) {
$this->createModel($table, $option); $this->createModel($table, $option);
@ -112,6 +113,7 @@ class ModelCommand extends Command
$this->addOption('prefix', 'P', InputOption::VALUE_OPTIONAL, 'What prefix that you want the Model set.'); $this->addOption('prefix', 'P', InputOption::VALUE_OPTIONAL, 'What prefix that you want the Model set.');
$this->addOption('inheritance', 'i', InputOption::VALUE_OPTIONAL, 'The inheritance that you want the Model extends.'); $this->addOption('inheritance', 'i', InputOption::VALUE_OPTIONAL, 'The inheritance that you want the Model extends.');
$this->addOption('uses', 'U', InputOption::VALUE_OPTIONAL, 'The default class uses of the Model.'); $this->addOption('uses', 'U', InputOption::VALUE_OPTIONAL, 'The default class uses of the Model.');
$this->addOption('refresh-fillable', null, InputOption::VALUE_NONE, 'Whether generate fillable argement for model.');
} }
protected function getSchemaBuilder(string $poolName): MySqlBuilder protected function getSchemaBuilder(string $poolName): MySqlBuilder
@ -158,7 +160,10 @@ class ModelCommand extends Command
$stms = $this->astParser->parse(file_get_contents($path)); $stms = $this->astParser->parse(file_get_contents($path));
$traverser = new NodeTraverser(); $traverser = new NodeTraverser();
$visitor = make(ModelUpdateVisitor::class, ['columns' => $columns]); $visitor = make(ModelUpdateVisitor::class, [
'columns' => $columns,
'option' => $option,
]);
$traverser->addVisitor($visitor); $traverser->addVisitor($visitor);
$stms = $traverser->traverse($stms); $stms = $traverser->traverse($stms);
$code = $this->printer->prettyPrintFile($stms); $code = $this->printer->prettyPrintFile($stms);
@ -203,7 +208,7 @@ class ModelCommand extends Command
protected function getOption(string $name, string $key, string $pool = 'default', $default = null) protected function getOption(string $name, string $key, string $pool = 'default', $default = null)
{ {
$result = $this->input->getOption($name); $result = $this->input->getOption($name);
$nonInput = $name === 'force-casts' ? false : null; $nonInput = in_array($name, ['force-casts', 'refresh-fillable']) ? false : null;
if ($result === $nonInput) { if ($result === $nonInput) {
$result = $this->config->get("databases.{$pool}.{$key}", $default); $result = $this->config->get("databases.{$pool}.{$key}", $default);
} }

View File

@ -44,6 +44,11 @@ class ModelOption
*/ */
protected $uses; protected $uses;
/**
* @var bool
*/
protected $refreshFillable;
public function getPool(): string public function getPool(): string
{ {
return $this->pool; return $this->pool;
@ -99,21 +104,25 @@ class ModelOption
return $this; return $this;
} }
/**
* @return string
*/
public function getUses(): string public function getUses(): string
{ {
return $this->uses; return $this->uses;
} }
/**
* @param string $uses
* @return ModelOption
*/
public function setUses(string $uses): ModelOption public function setUses(string $uses): ModelOption
{ {
$this->uses = $uses; $this->uses = $uses;
return $this; return $this;
} }
public function isRefreshFillable(): bool
{
return $this->refreshFillable;
}
public function setRefreshFillable(bool $refreshFillable): ModelOption
{
$this->refreshFillable = $refreshFillable;
return $this;
}
} }

View File

@ -16,6 +16,28 @@ use Hyperf\Database\ConnectionInterface;
use Hyperf\Database\ConnectionResolverInterface; use Hyperf\Database\ConnectionResolverInterface;
use Hyperf\Utils\ApplicationContext; use Hyperf\Utils\ApplicationContext;
/**
* @method static bool hasTable(string $table)
* @method static array getColumnListing(string $table)
* @method static array getColumnTypeListing(string $table)
* @method static void dropAllTables()
* @method static void dropAllViews()
* @method static array getAllTables()
* @method static array getAllViews()
* @method static bool hasColumn(string $table, string $column)
* @method static bool hasColumns(string $table, array $columns)
* @method static string getColumnType(string $table, string $column)
* @method static void table(string $table, \Closure $callback)
* @method static void create(string $table, \Closure $callback))
* @method static void drop(string $table)
* @method static void dropIfExists(string $table)
* @method static void rename(string $from, string $to)
* @method static bool enableForeignKeyConstraints()
* @method static bool disableForeignKeyConstraints()
* @method static \Hyperf\Database\Connection getConnection()
* @method static Builder setConnection(\Hyperf\Database\Connection $connection)
* @method static void blueprintResolver(\Closure $resolver)
*/
class Schema class Schema
{ {
public static function __callStatic($name, $arguments) public static function __callStatic($name, $arguments)

View File

@ -33,12 +33,12 @@ use Psr\Container\ContainerInterface;
* @method static int affectingStatement(string $query, array $bindings = []) * @method static int affectingStatement(string $query, array $bindings = [])
* @method static bool unprepared(string $query) * @method static bool unprepared(string $query)
* @method static array prepareBindings(array $bindings) * @method static array prepareBindings(array $bindings)
* @method static transaction(Closure $callback, int $attempts = 1) * @method static transaction(\Closure $callback, int $attempts = 1)
* @method static beginTransaction() * @method static beginTransaction()
* @method static rollBack() * @method static rollBack()
* @method static commit() * @method static commit()
* @method static int transactionLevel() * @method static int transactionLevel()
* @method static array pretend(Closure $callback) * @method static array pretend(\Closure $callback)
* @method static ConnectionInterface connection(string $pool) * @method static ConnectionInterface connection(string $pool)
*/ */
class Db class Db

View File

@ -38,8 +38,11 @@ return [
'middleware' => [ 'middleware' => [
'namespace' => 'App\\Middleware', 'namespace' => 'App\\Middleware',
], ],
'Process' => [ 'process' => [
'namespace' => 'App\\Process', 'namespace' => 'App\\Process',
], ],
'request' => [
'namespace' => 'App\\Request',
],
], ],
]; ];

View File

@ -10,13 +10,11 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace Hyperf\Validation\Request; namespace Hyperf\Devtool\Generator;
use Hyperf\Command\Annotation\Command; use Hyperf\Command\Annotation\Command;
use Hyperf\Devtool\Generator\GeneratorCommand;
/** /**
* Class RequestCommand.
* @Command * @Command
*/ */
class RequestCommand extends GeneratorCommand class RequestCommand extends GeneratorCommand
@ -29,11 +27,11 @@ class RequestCommand extends GeneratorCommand
protected function getStub(): string protected function getStub(): string
{ {
return $this->getConfig()['stub'] ?? __DIR__ . '/stubs/request.stub'; return $this->getConfig()['stub'] ?? __DIR__ . '/stubs/validation-request.stub';
} }
protected function getDefaultNamespace(): string protected function getDefaultNamespace(): string
{ {
return $this->getConfig()['namespace'] ?? 'App\\Requests'; return $this->getConfig()['namespace'] ?? 'App\\Request';
} }
} }

View File

@ -113,7 +113,9 @@ class VendorPublishCommand extends SymfonyCommand
continue; continue;
} }
mkdir(dirname($destination), 0755, true); if (! file_exists(dirname($destination))) {
mkdir(dirname($destination), 0755, true);
}
copy($source, $destination); copy($source, $destination);
$this->output->writeln(sprintf('<fg=green>[%s] publish [%s] success.</>', $package, $id)); $this->output->writeln(sprintf('<fg=green>[%s] publish [%s] success.</>', $package, $id));

View File

@ -13,6 +13,7 @@ declare(strict_types=1);
namespace HyperfTest\Elasticsearch; namespace HyperfTest\Elasticsearch;
use Elasticsearch\ClientBuilder; use Elasticsearch\ClientBuilder;
use Elasticsearch\Common\Exceptions\NoNodesAvailableException;
use Hyperf\Elasticsearch\ClientBuilderFactory; use Hyperf\Elasticsearch\ClientBuilderFactory;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -30,4 +31,15 @@ class ClientFactoryTest extends TestCase
$this->assertInstanceOf(ClientBuilder::class, $client); $this->assertInstanceOf(ClientBuilder::class, $client);
} }
public function testHostNotReached()
{
$this->expectException(NoNodesAvailableException::class);
$clientFactory = new ClientBuilderFactory();
$client = $clientFactory->create()->setHosts(['http://127.0.0.1:9201'])->build();
$client->info();
}
} }

View File

@ -56,6 +56,9 @@ class AnnotationReader
*/ */
private $mode; private $mode;
/**
* @var array
*/
private $methodAnnotationCache = []; private $methodAnnotationCache = [];
/** /**
@ -101,25 +104,21 @@ class AnnotationReader
public function getRequestAnnotation(ReflectionMethod $refMethod, string $annotationName): ?AbstractRequest public function getRequestAnnotation(ReflectionMethod $refMethod, string $annotationName): ?AbstractRequest
{ {
/* @var null|AbstractRequest $queryAnnotation */
return $this->getMethodAnnotation($refMethod, $annotationName); return $this->getMethodAnnotation($refMethod, $annotationName);
} }
public function getLoggedAnnotation(ReflectionMethod $refMethod): ?Logged public function getLoggedAnnotation(ReflectionMethod $refMethod): ?Logged
{ {
/* @var null|Logged $loggedAnnotation */
return $this->getMethodAnnotation($refMethod, Logged::class); return $this->getMethodAnnotation($refMethod, Logged::class);
} }
public function getRightAnnotation(ReflectionMethod $refMethod): ?Right public function getRightAnnotation(ReflectionMethod $refMethod): ?Right
{ {
/* @var null|Right $rightAnnotation */
return $this->getMethodAnnotation($refMethod, Right::class); return $this->getMethodAnnotation($refMethod, Right::class);
} }
public function getFailWithAnnotation(ReflectionMethod $refMethod): ?FailWith public function getFailWithAnnotation(ReflectionMethod $refMethod): ?FailWith
{ {
/* @var null|FailWith $failWithAnnotation */
return $this->getMethodAnnotation($refMethod, FailWith::class); return $this->getMethodAnnotation($refMethod, FailWith::class);
} }
@ -128,13 +127,11 @@ class AnnotationReader
*/ */
public function getSourceFields(ReflectionClass $refClass): array public function getSourceFields(ReflectionClass $refClass): array
{ {
/* @var SourceField[] $sourceFields */
return $this->getClassAnnotations($refClass, SourceField::class); return $this->getClassAnnotations($refClass, SourceField::class);
} }
public function getFactoryAnnotation(ReflectionMethod $refMethod): ?Factory public function getFactoryAnnotation(ReflectionMethod $refMethod): ?Factory
{ {
/* @var null|Factory $factoryAnnotation */
return $this->getMethodAnnotation($refMethod, Factory::class); return $this->getMethodAnnotation($refMethod, Factory::class);
} }

View File

@ -34,6 +34,7 @@
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"HyperfTest\\GrpcServer\\": "tests/"
} }
}, },
"config": { "config": {

View File

@ -10,13 +10,10 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace HyperfTest\Translation\Cases; namespace Hyperf\GrpcServer\Exception;
use PHPUnit\Framework\TestCase; use Hyperf\Server\Exception\ServerException;
/** class GrpcException extends ServerException
* Class AbstractTestCase.
*/
abstract class AbstractTestCase extends TestCase
{ {
} }

View File

@ -15,6 +15,8 @@ namespace Hyperf\GrpcServer\Exception\Handler;
use Hyperf\Contract\StdoutLoggerInterface; use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\ExceptionHandler; use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\ExceptionHandler\Formatter\FormatterInterface; use Hyperf\ExceptionHandler\Formatter\FormatterInterface;
use Hyperf\Grpc\StatusCode;
use Hyperf\GrpcServer\Exception\GrpcException;
use Hyperf\Server\Exception\ServerException; use Hyperf\Server\Exception\ServerException;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Throwable; use Throwable;
@ -39,27 +41,28 @@ class GrpcExceptionHandler extends ExceptionHandler
public function handle(Throwable $throwable, ResponseInterface $response) public function handle(Throwable $throwable, ResponseInterface $response)
{ {
$this->logger->warning($this->formatter->format($throwable)); if ($throwable instanceof GrpcException) {
$this->logger->debug($this->formatter->format($throwable));
} else {
$this->logger->warning($this->formatter->format($throwable));
}
return $this->transferToResponse($throwable->getCode(), $throwable->getMessage(), $response); return $this->transferToResponse($throwable->getCode(), $throwable->getMessage(), $response);
} }
public function isValid(Throwable $throwable): bool public function isValid(Throwable $throwable): bool
{ {
return $throwable instanceof ServerException; return true;
} }
/** /**
* Transfer the non-standard response content to a standard response object. * Transfer the non-standard response content to a standard response object.
*
* @param int|string $code
* @param string $message
* @param ResponseInterface $response
*/ */
protected function transferToResponse($code, $message, ResponseInterface $response): ResponseInterface protected function transferToResponse(int $code, string $message, ResponseInterface $response): ResponseInterface
{ {
$response = $response->withAddedHeader('Content-Type', 'application/grpc') $response = $response->withAddedHeader('Content-Type', 'application/grpc')
->withAddedHeader('trailer', 'grpc-status, grpc-message'); ->withAddedHeader('trailer', 'grpc-status, grpc-message')
->withStatus(StatusCode::HTTP_CODE_MAPPING[$code] ?? 500);
$response->getSwooleResponse()->trailer('grpc-status', (string) $code); $response->getSwooleResponse()->trailer('grpc-status', (string) $code);
$response->getSwooleResponse()->trailer('grpc-message', (string) $message); $response->getSwooleResponse()->trailer('grpc-message', (string) $message);

View File

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\GrpcServer;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\Formatter\FormatterInterface;
use Hyperf\Grpc\StatusCode;
use Hyperf\HttpMessage\Server\Response;
use HyperfTest\GrpcServer\Stub\GrpcExceptionHandlerStub;
use Mockery;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
/**
* @internal
* @coversNothing
*/
class GrpcExceptionHandlerTest extends TestCase
{
public function testTransferToResponse200()
{
$container = $this->getContainer();
$logger = $container->get(StdoutLoggerInterface::class);
$formatter = $container->get(FormatterInterface::class);
$swooleResponse = Mockery::mock(\Swoole\Http\Response::class);
$data = [];
$swooleResponse->shouldReceive('trailer')->andReturnUsing(function (...$args) use (&$data) {
$data[] = $args;
});
$response = new Response($swooleResponse);
$handler = new GrpcExceptionHandlerStub($logger, $formatter);
$response = $handler->transferToResponse(StatusCode::OK, 'OK', $response);
$this->assertSame([['grpc-status', '0'], ['grpc-message', 'OK']], $data);
$this->assertSame(200, $response->getStatusCode());
}
public function testTransferToResponse499()
{
$container = $this->getContainer();
$logger = $container->get(StdoutLoggerInterface::class);
$formatter = $container->get(FormatterInterface::class);
$swooleResponse = Mockery::mock(\Swoole\Http\Response::class);
$data = [];
$swooleResponse->shouldReceive('trailer')->andReturnUsing(function (...$args) use (&$data) {
$data[] = $args;
});
$response = new Response($swooleResponse);
$handler = new GrpcExceptionHandlerStub($logger, $formatter);
$response = $handler->transferToResponse(StatusCode::CANCELLED, 'The operation was cancelled', $response);
$this->assertSame([['grpc-status', '1'], ['grpc-message', 'The operation was cancelled']], $data);
$this->assertSame(499, $response->getStatusCode());
}
public function testTransferToResponseUnKnown()
{
$container = $this->getContainer();
$logger = $container->get(StdoutLoggerInterface::class);
$formatter = $container->get(FormatterInterface::class);
$swooleResponse = Mockery::mock(\Swoole\Http\Response::class);
$data = [];
$swooleResponse->shouldReceive('trailer')->andReturnUsing(function (...$args) use (&$data) {
$data[] = $args;
});
$response = new Response($swooleResponse);
$handler = new GrpcExceptionHandlerStub($logger, $formatter);
$response = $handler->transferToResponse(123, 'UNKNOWN', $response);
$this->assertSame([['grpc-status', '123'], ['grpc-message', 'UNKNOWN']], $data);
$this->assertSame(500, $response->getStatusCode());
}
protected function getContainer()
{
$container = Mockery::mock(ContainerInterface::class);
$logger = Mockery::mock(StdoutLoggerInterface::class);
$logger->shouldReceive(Mockery::any())->with(Mockery::any())->andReturn(null);
$container->shouldReceive('get')->with(StdoutLoggerInterface::class)->andReturn($logger);
$formatter = Mockery::mock(FormatterInterface::class);
$formatter->shouldReceive(Mockery::any())->with(Mockery::any())->andReturn('');
$container->shouldReceive('get')->with(FormatterInterface::class)->andReturn($formatter);
return $container;
}
}

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\GrpcServer\Stub;
use Hyperf\GrpcServer\Exception\Handler\GrpcExceptionHandler;
use Psr\Http\Message\ResponseInterface;
class GrpcExceptionHandlerStub extends GrpcExceptionHandler
{
public function transferToResponse($code, $message, ResponseInterface $response): ResponseInterface
{
return parent::transferToResponse($code, $message, $response);
}
}

186
src/grpc/src/StatusCode.php Normal file
View File

@ -0,0 +1,186 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Grpc;
/**
* the const inside this class is copied from this address.
* @see https://github.com/grpc/grpc-java/blob/b363f80764bcc8452ef54511b6c6d6b596f5f177/api/src/main/java/io/grpc/Status.java
* @see https://grpc.github.io/grpc/core/md_doc_statuscodes.html
*/
class StatusCode
{
/**
* The operation completed successfully.
*/
const OK = 0;
/**
* The operation was cancelled (typically by the caller).
*/
const CANCELLED = 1;
/**
* Unknown error. An example of where this error may be returned is
* if a Status value received from another address space belongs to
* an error-space that is not known in this address space. Also
* errors raised by APIs that do not return enough error information
* may be converted to this error.
*/
const UNKNOWN = 2;
/**
* Client specified an invalid argument. Note that this differs
* from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments
* that are problematic regardless of the state of the system
* (e.g., a malformed file name).
*/
const INVALID_ARGUMENT = 3;
/**
* Deadline expired before operation could complete. For operations
* that change the state of the system, this error may be returned
* even if the operation has completed successfully. For example, a
* successful response from a server could have been delayed long
* enough for the deadline to expire.
*/
const DEADLINE_EXCEEDED = 4;
/**
* Some requested entity (e.g., file or directory) was not found.
*/
const NOT_FOUND = 5;
/**
* Some entity that we attempted to create (e.g., file or directory) already exists.
*/
const ALREADY_EXISTS = 6;
/**
* The caller does not have permission to execute the specified
* operation. PERMISSION_DENIED must not be used for rejections
* caused by exhausting some resource (use RESOURCE_EXHAUSTED
* instead for those errors). PERMISSION_DENIED must not be
* used if the caller cannot be identified (use UNAUTHENTICATED
* instead for those errors).
*/
const PERMISSION_DENIED = 7;
/**
* Some resource has been exhausted, perhaps a per-user quota, or
* perhaps the entire file system is out of space.
*/
const RESOURCE_EXHAUSTED = 8;
/**
* Operation was rejected because the system is not in a state
* required for the operation's execution. For example, directory
* to be deleted may be non-empty, an rmdir operation is applied to
* a non-directory, etc.
*
* <p>A litmus test that may help a service implementor in deciding
* between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
* (a) Use UNAVAILABLE if the client can retry just the failing call.
* (b) Use ABORTED if the client should retry at a higher-level
* (e.g., restarting a read-modify-write sequence).
* (c) Use FAILED_PRECONDITION if the client should not retry until
* the system state has been explicitly fixed. E.g., if an "rmdir"
* fails because the directory is non-empty, FAILED_PRECONDITION
* should be returned since the client should not retry unless
* they have first fixed up the directory by deleting files from it.
*/
const FAILED_PRECONDITION = 9;
/**
* The operation was aborted, typically due to a concurrency issue
* like sequencer check failures, transaction aborts, etc.
*
* <p>See litmus test above for deciding between FAILED_PRECONDITION,
* ABORTED, and UNAVAILABLE.
*/
const ABORTED = 10;
/**
* Operation was attempted past the valid range. E.g., seeking or
* reading past end of file.
*
* <p>Unlike INVALID_ARGUMENT, this error indicates a problem that may
* be fixed if the system state changes. For example, a 32-bit file
* system will generate INVALID_ARGUMENT if asked to read at an
* offset that is not in the range [0,2^32-1], but it will generate
* OUT_OF_RANGE if asked to read from an offset past the current
* file size.
*
* <p>There is a fair bit of overlap between FAILED_PRECONDITION and OUT_OF_RANGE.
* We recommend using OUT_OF_RANGE (the more specific error) when it applies
* so that callers who are iterating through
* a space can easily look for an OUT_OF_RANGE error to detect when they are done.
*/
const OUT_OF_RANGE = 11;
/**
* Operation is not implemented or not supported/enabled in this service.
*/
const UNIMPLEMENTED = 12;
/**
* Internal errors. Means some invariants expected by underlying
* system has been broken. If you see one of these errors,
* something is very broken.
*/
const INTERNAL = 13;
/**
* The service is currently unavailable. This is a most likely a
* transient condition and may be corrected by retrying with
* a backoff. Note that it is not always safe to retry
* non-idempotent operations.
*
* <p>See litmus test above for deciding between FAILED_PRECONDITION,
* ABORTED, and UNAVAILABLE.
*/
const UNAVAILABLE = 14;
/**
* Unrecoverable data loss or corruption.
*/
const DATA_LOSS = 15;
/**
* The request does not have valid authentication credentials for the
* operation.
*/
const UNAUTHENTICATED = 16;
/**
* @see https://grpc.github.io/grpc/core/md_doc_statuscodes.html
*/
const HTTP_CODE_MAPPING = [
self::OK => 200,
self::CANCELLED => 499,
self::UNKNOWN => 500,
self::INVALID_ARGUMENT => 400,
self::DEADLINE_EXCEEDED => 504,
self::NOT_FOUND => 404,
self::ALREADY_EXISTS => 409,
self::PERMISSION_DENIED => 403,
self::RESOURCE_EXHAUSTED => 429,
self::FAILED_PRECONDITION => 400,
self::ABORTED => 409,
self::OUT_OF_RANGE => 400,
self::UNIMPLEMENTED => 501,
self::INTERNAL => 500,
self::UNAVAILABLE => 503,
self::DATA_LOSS => 500,
self::UNAUTHENTICATED => 401,
];
}

View File

@ -67,12 +67,7 @@ class CoroutineHandler
$ex = $this->checkStatusCode($client, $request); $ex = $this->checkStatusCode($client, $request);
if ($ex !== true) { if ($ex !== true) {
return [ return $this->getErrorResponse($ex, $btime, $effectiveUrl);
'status' => null,
'reason' => null,
'headers' => [],
'error' => $ex,
];
} }
return $this->getResponse($client, $btime, $effectiveUrl); return $this->getResponse($client, $btime, $effectiveUrl);
@ -116,6 +111,24 @@ class CoroutineHandler
$client->setHeaders($headers); $client->setHeaders($headers);
} }
protected function getErrorResponse(\Throwable $throwable, $btime, $effectiveUrl)
{
return new CompletedFutureArray([
'curl' => [
'errno' => 0,
],
'transfer_stats' => [
'total_time' => microtime(true) - $btime,
],
'effective_url' => $effectiveUrl,
'body' => '',
'status' => null,
'reason' => null,
'headers' => [],
'error' => $throwable,
]);
}
protected function getResponse(Client $client, $btime, $effectiveUrl) protected function getResponse(Client $client, $btime, $effectiveUrl)
{ {
return new CompletedFutureArray([ return new CompletedFutureArray([

View File

@ -74,12 +74,7 @@ class PoolHandler extends CoroutineHandler
if ($ex !== true) { if ($ex !== true) {
$connection->close(); $connection->close();
$connection->release(); $connection->release();
return [ return $this->getErrorResponse($ex, $btime, $effectiveUrl);
'status' => null,
'reason' => null,
'headers' => [],
'error' => $ex,
];
} }
$response = $this->getResponse($client, $btime, $effectiveUrl); $response = $this->getResponse($client, $btime, $effectiveUrl);

View File

@ -61,6 +61,11 @@ class UploadedFile extends \SplFileInfo implements UploadedFileInterface
*/ */
private $size; private $size;
/**
* @var string
*/
private $mimeType;
/** /**
* @param string $tmpFile * @param string $tmpFile
* @param null|int $size * @param null|int $size
@ -98,6 +103,14 @@ class UploadedFile extends \SplFileInfo implements UploadedFileInterface
return end($segments) ?? null; return end($segments) ?? null;
} }
public function getMimeType(): string
{
if (is_string($this->mimeType)) {
return $this->mimeType;
}
return $this->mimeType = mime_content_type($this->tmpFile);
}
/** /**
* Returns whether the file was uploaded successfully. * Returns whether the file was uploaded successfully.
* *

View File

@ -51,6 +51,6 @@ class HttpExceptionHandler extends ExceptionHandler
*/ */
public function isValid(Throwable $throwable): bool public function isValid(Throwable $throwable): bool
{ {
return $throwable instanceof ServerException; return true;
} }
} }

View File

@ -118,6 +118,9 @@ class DispatcherFactory
foreach ($methods as $method) { foreach ($methods as $method) {
$path = $this->parsePath($prefix, $method); $path = $this->parsePath($prefix, $method);
$methodName = $method->getName(); $methodName = $method->getName();
if (substr($methodName, 0, 2) === '__') {
continue;
}
$router->addRoute($autoMethods, $path, [$className, $methodName, $annotation->server]); $router->addRoute($autoMethods, $path, [$className, $methodName, $annotation->server]);
$methodMiddlewares = $middlewares; $methodMiddlewares = $middlewares;
@ -177,7 +180,9 @@ class DispatcherFactory
} }
$path = $mapping->path; $path = $mapping->path;
if ($path[0] !== '/') { if ($path === '') {
$path = $prefix;
} elseif ($path[0] !== '/') {
$path = $prefix . '/' . $path; $path = $prefix . '/' . $path;
} }
$router->addRoute($mapping->methods, $path, [ $router->addRoute($mapping->methods, $path, [

View File

@ -12,6 +12,8 @@ declare(strict_types=1);
namespace HyperfTest\HttpServer\Router; namespace HyperfTest\HttpServer\Router;
use Hyperf\HttpServer\Annotation\AutoController;
use HyperfTest\HttpServer\Stub\DemoController;
use HyperfTest\HttpServer\Stub\DispatcherFactory; use HyperfTest\HttpServer\Stub\DispatcherFactory;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -31,4 +33,21 @@ class DispatcherFactoryTest extends TestCase
$res = $factory->getPrefix('App\\Controller\\Admin\\UserAuthController', ''); $res = $factory->getPrefix('App\\Controller\\Admin\\UserAuthController', '');
$this->assertSame('/admin/user_auth', $res); $this->assertSame('/admin/user_auth', $res);
} }
public function testRemoveMagicMethods()
{
$factory = new DispatcherFactory();
$annotation = new AutoController(['prefix' => 'test']);
$factory->handleAutoController(DemoController::class, $annotation);
$router = $factory->getRouter('http');
[$routers] = $router->getData();
$this->assertSame(['GET', 'POST', 'HEAD'], array_keys($routers));
foreach ($routers as $method => $items) {
$this->assertFalse(in_array('/test/__construct', array_keys($items)));
$this->assertFalse(in_array('/test/__return', array_keys($items)));
}
}
} }

View File

@ -14,8 +14,17 @@ namespace HyperfTest\HttpServer\Stub;
class DemoController class DemoController
{ {
public function __construct()
{
}
public function index(int $id, string $name = 'Hyperf', array $params = []) public function index(int $id, string $name = 'Hyperf', array $params = [])
{ {
return [$id, $name, $params]; return $this->__return($id, $name, $params);
}
public function __return(...$args)
{
return $args;
} }
} }

View File

@ -12,10 +12,23 @@ declare(strict_types=1);
namespace HyperfTest\HttpServer\Stub; namespace HyperfTest\HttpServer\Stub;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Router\RouteCollector;
class DispatcherFactory extends \Hyperf\HttpServer\Router\DispatcherFactory class DispatcherFactory extends \Hyperf\HttpServer\Router\DispatcherFactory
{ {
public function getPrefix(string $className, string $prefix): string public function getPrefix(string $className, string $prefix): string
{ {
return parent::getPrefix($className, $prefix); // TODO: Change the autogenerated stub return parent::getPrefix($className, $prefix);
}
public function handleAutoController(string $className, AutoController $annotation, array $middlewares = [], array $methodMetadata = []): void
{
parent::handleAutoController($className, $annotation, $middlewares, $methodMetadata);
}
public function getRouter(string $serverName): RouteCollector
{
return parent::getRouter($serverName);
} }
} }

View File

@ -61,6 +61,7 @@ class Paginator extends AbstractPaginator implements Arrayable, ArrayAccess, Cou
if ($this->hasMorePages()) { if ($this->hasMorePages()) {
return $this->url($this->currentPage() + 1); return $this->url($this->currentPage() + 1);
} }
return null;
} }
/** /**

View File

@ -81,7 +81,7 @@ class RedisConnection extends BaseConnection implements ConnectionInterface
throw new ConnectionException('Connection reconnect failed.'); throw new ConnectionException('Connection reconnect failed.');
} }
if (isset($auth)) { if (isset($auth) && $auth !== '') {
$redis->auth($auth); $redis->auth($auth);
} }

1
src/server/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
/tests export-ignore

View File

@ -12,14 +12,35 @@ declare(strict_types=1);
namespace Hyperf\Server\Listener; namespace Hyperf\Server\Listener;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Event\Contract\ListenerInterface; use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Framework\Event\AfterWorkerStart; use Hyperf\Framework\Event\AfterWorkerStart;
use Hyperf\Framework\Event\OnManagerStart; use Hyperf\Framework\Event\OnManagerStart;
use Hyperf\Framework\Event\OnStart; use Hyperf\Framework\Event\OnStart;
use Hyperf\Process\Event\BeforeProcessHandle; use Hyperf\Process\Event\BeforeProcessHandle;
use Psr\Container\ContainerInterface;
class InitProcessTitleListener implements ListenerInterface class InitProcessTitleListener implements ListenerInterface
{ {
/**
* @var string
*/
protected $name = '';
/**
* @var string
*/
protected $dot = '.';
public function __construct(ContainerInterface $container)
{
if ($container->has(ConfigInterface::class)) {
if ($name = $container->get(ConfigInterface::class)->get('app_name')) {
$this->name = $name;
}
}
}
public function listen(): array public function listen(): array
{ {
return [ return [
@ -32,18 +53,35 @@ class InitProcessTitleListener implements ListenerInterface
public function process(object $event) public function process(object $event)
{ {
$array = [];
if ($this->name !== '') {
$array[] = $this->name;
}
if ($event instanceof OnStart) { if ($event instanceof OnStart) {
@cli_set_process_title('Master'); $array[] = 'Master';
} elseif ($event instanceof OnManagerStart) { } elseif ($event instanceof OnManagerStart) {
@cli_set_process_title('Manager'); $array[] = 'Manager';
} elseif ($event instanceof AfterWorkerStart) { } elseif ($event instanceof AfterWorkerStart) {
if ($event->server->taskworker) { if ($event->server->taskworker) {
@cli_set_process_title('TaskWorker.' . $event->workerId); $array[] = 'TaskWorker';
$array[] = $event->workerId;
} else { } else {
@cli_set_process_title('Worker.' . $event->workerId); $array[] = 'Worker';
$array[] = $event->workerId;
} }
} elseif ($event instanceof BeforeProcessHandle) { } elseif ($event instanceof BeforeProcessHandle) {
@cli_set_process_title($event->process->name . '.' . $event->index); $array[] = $event->process->name;
$array[] = $event->index;
}
if ($title = implode($this->dot, $array)) {
$this->setTitle($title);
} }
} }
protected function setTitle(string $title)
{
@cli_set_process_title($title);
}
} }

View File

@ -12,12 +12,21 @@ declare(strict_types=1);
namespace HyperfTest\Server\Listener; namespace HyperfTest\Server\Listener;
use Hyperf\Config\Config;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Framework\Event\AfterWorkerStart; use Hyperf\Framework\Event\AfterWorkerStart;
use Hyperf\Framework\Event\OnManagerStart; use Hyperf\Framework\Event\OnManagerStart;
use Hyperf\Framework\Event\OnStart; use Hyperf\Framework\Event\OnStart;
use Hyperf\Process\Event\BeforeProcessHandle; use Hyperf\Process\Event\BeforeProcessHandle;
use Hyperf\Server\Listener\InitProcessTitleListener; use Hyperf\Server\Listener\InitProcessTitleListener;
use Hyperf\Utils\Context;
use HyperfTest\Server\Stub\DemoProcess;
use HyperfTest\Server\Stub\InitProcessTitleListenerStub;
use HyperfTest\Server\Stub\InitProcessTitleListenerStub2;
use Mockery;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
/** /**
* @internal * @internal
@ -25,9 +34,17 @@ use PHPUnit\Framework\TestCase;
*/ */
class InitProcessTitleListenerTest extends TestCase class InitProcessTitleListenerTest extends TestCase
{ {
protected function tearDown()
{
Mockery::close();
}
public function testInitProcessTitleListenerListen() public function testInitProcessTitleListenerListen()
{ {
$listener = new InitProcessTitleListener(); $container = Mockery::mock(ContainerInterface::class);
$container->shouldReceive('has')->with(Mockery::any())->andReturn(false);
$listener = new InitProcessTitleListener($container);
$this->assertSame([ $this->assertSame([
OnStart::class, OnStart::class,
@ -36,4 +53,53 @@ class InitProcessTitleListenerTest extends TestCase
BeforeProcessHandle::class, BeforeProcessHandle::class,
], $listener->listen()); ], $listener->listen());
} }
public function testProcessDefaultName()
{
$container = Mockery::mock(ContainerInterface::class);
$container->shouldReceive('has')->with(Mockery::any())->andReturn(false);
$listener = new InitProcessTitleListenerStub($container);
$process = new DemoProcess($container);
$listener->process(new BeforeProcessHandle($process, 1));
$this->assertSame('test.demo.1', Context::get('test.server.process.title'));
}
public function testProcessName()
{
$name = 'hyperf-skeleton.' . uniqid();
$container = Mockery::mock(ContainerInterface::class);
$container->shouldReceive('has')->with(ConfigInterface::class)->andReturn(true);
$container->shouldReceive('has')->with(EventDispatcherInterface::class)->andReturn(false);
$container->shouldReceive('get')->with(ConfigInterface::class)->andReturn(new Config([
'app_name' => $name,
]));
$listener = new InitProcessTitleListenerStub($container);
$process = new DemoProcess($container);
$listener->process(new BeforeProcessHandle($process, 0));
$this->assertSame($name . '.test.demo.0', Context::get('test.server.process.title'));
}
public function testUserDefinedDot()
{
$name = 'hyperf-skeleton.' . uniqid();
$container = Mockery::mock(ContainerInterface::class);
$container->shouldReceive('has')->with(ConfigInterface::class)->andReturn(true);
$container->shouldReceive('has')->with(EventDispatcherInterface::class)->andReturn(false);
$container->shouldReceive('get')->with(ConfigInterface::class)->andReturn(new Config([
'app_name' => $name,
]));
$listener = new InitProcessTitleListenerStub2($container);
$process = new DemoProcess($container);
$listener->process(new BeforeProcessHandle($process, 0));
$this->assertSame($name . '#test.demo#0', Context::get('test.server.process.title'));
}
} }

View File

@ -10,14 +10,15 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace Hyperf\Translation\Contracts; namespace HyperfTest\Server\Stub;
interface HasLocalePreference use Hyperf\Process\AbstractProcess;
class DemoProcess extends AbstractProcess
{ {
/** public $name = 'test.demo';
* Get the preferred locale of the entity.
* public function handle(): void
* @return null|string {
*/ }
public function preferredLocale();
} }

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Server\Stub;
use Hyperf\Server\Listener\InitProcessTitleListener;
use Hyperf\Utils\Context;
class InitProcessTitleListenerStub extends InitProcessTitleListener
{
public function setTitle(string $title)
{
Context::set('test.server.process.title', $title);
}
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Server\Stub;
use Hyperf\Server\Listener\InitProcessTitleListener;
use Hyperf\Utils\Context;
class InitProcessTitleListenerStub2 extends InitProcessTitleListener
{
protected $dot = '#';
public function setTitle(string $title)
{
Context::set('test.server.process.title', $title);
}
}

View File

@ -18,8 +18,9 @@
"phpunit/phpunit": "^7.0" "phpunit/phpunit": "^7.0"
}, },
"suggest": { "suggest": {
"psr/container": "Required to use SnowflakeFactory.", "psr/container": "Required to use MetaGeneratorFactory.",
"hyperf/config": "Required to read snowflake config." "hyperf/config": "Required to read snowflake config.",
"hyperf/redis": "Required to use RedisMilliSecondMetaGenerator or RedisSecondMetaGenerator."
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View File

@ -10,9 +10,16 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
use Hyperf\Snowflake\IdGeneratorInterface; use Hyperf\Snowflake\MetaGenerator\RedisMilliSecondMetaGenerator;
use Hyperf\Snowflake\MetaGenerator\RedisSecondMetaGenerator;
use Hyperf\Snowflake\MetaGeneratorInterface;
return [ return [
'level' => IdGeneratorInterface::LEVEL_MILLISECOND, 'begin_second' => MetaGeneratorInterface::DEFAULT_BEGIN_SECOND,
'begin_second' => IdGeneratorInterface::DEFAULT_SECOND, RedisMilliSecondMetaGenerator::class => [
'pool' => 'default',
],
RedisSecondMetaGenerator::class => [
'pool' => 'default',
],
]; ];

View File

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Snowflake;
class Config implements ConfigInterface
{
const MILLISECOND_BITS = 41;
const DATA_CENTER_ID_BITS = 5;
const WORKER_ID_BITS = 5;
const SEQUENCE_BITS = 12;
public function maxWorkerId(): int
{
return -1 ^ (-1 << self::WORKER_ID_BITS);
}
public function maxDataCenterId(): int
{
return -1 ^ (-1 << self::DATA_CENTER_ID_BITS);
}
public function maxSequence(): int
{
return -1 ^ (-1 << self::SEQUENCE_BITS);
}
public function getTimeStampShift(): int
{
return self::SEQUENCE_BITS + self::WORKER_ID_BITS + self::DATA_CENTER_ID_BITS;
}
public function getDataCenterShift(): int
{
return self::SEQUENCE_BITS + self::WORKER_ID_BITS;
}
public function getWorkerIdShift(): int
{
return self::SEQUENCE_BITS;
}
public function getTimeStampBits(): int
{
return self::MILLISECOND_BITS;
}
public function getDataCenterBits(): int
{
return self::DATA_CENTER_ID_BITS;
}
public function getWorkerBits(): int
{
return self::WORKER_ID_BITS;
}
public function getSequenceBits(): int
{
return self::SEQUENCE_BITS;
}
}

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Snowflake;
interface ConfigInterface
{
public function maxWorkerId(): int;
public function maxDataCenterId(): int;
public function maxSequence(): int;
public function getTimeStampShift(): int;
public function getDataCenterShift(): int;
public function getWorkerIdShift(): int;
public function getTimeStampBits(): int;
public function getDataCenterBits(): int;
public function getWorkerBits(): int;
public function getSequenceBits(): int;
}

View File

@ -12,14 +12,17 @@ declare(strict_types=1);
namespace Hyperf\Snowflake; namespace Hyperf\Snowflake;
use Hyperf\Snowflake\IdGenerator\SnowflakeIdGenerator;
class ConfigProvider class ConfigProvider
{ {
public function __invoke(): array public function __invoke(): array
{ {
return [ return [
'dependencies' => [ 'dependencies' => [
IdGeneratorInterface::class => SnowflakeFactory::class, IdGeneratorInterface::class => SnowflakeIdGenerator::class,
MetaGeneratorInterface::class => RandomMetaGenerator::class, MetaGeneratorInterface::class => MetaGeneratorFactory::class,
ConfigInterface::class => Config::class,
], ],
'commands' => [ 'commands' => [
], ],

View File

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Snowflake;
abstract class IdGenerator implements IdGeneratorInterface
{
/**
* @var MetaGeneratorInterface
*/
protected $metaGenerator;
/**
* @var ConfigInterface
*/
protected $config;
public function __construct(MetaGeneratorInterface $metaGenerator)
{
$this->metaGenerator = $metaGenerator;
$this->config = $metaGenerator->getConfig();
}
public function generate(?Meta $meta = null): int
{
$meta = $this->meta($meta);
$interval = $meta->getTimeInterval() << $this->config->getTimeStampShift();
$dataCenterId = $meta->getDataCenterId() << $this->config->getDataCenterShift();
$workerId = $meta->getWorkerId() << $this->config->getWorkerIdShift();
return $interval | $dataCenterId | $workerId | $meta->getSequence();
}
public function degenerate(int $id): Meta
{
$interval = $id >> $this->config->getTimeStampShift();
$dataCenterId = $id >> $this->config->getDataCenterShift();
$workerId = $id >> $this->config->getWorkerIdShift();
return new Meta(
$interval << $this->config->getDataCenterBits() ^ $dataCenterId,
$dataCenterId << $this->config->getWorkerBits() ^ $workerId,
$workerId << $this->config->getSequenceBits() ^ $id,
$interval + $this->metaGenerator->getBeginTimeStamp(),
$this->metaGenerator->getBeginTimeStamp()
);
}
/**
* @return MetaGeneratorInterface
*/
public function getMetaGenerator(): MetaGeneratorInterface
{
return $this->metaGenerator;
}
protected function meta(?Meta $meta = null): Meta
{
if (is_null($meta)) {
return $this->metaGenerator->generate();
}
return $meta;
}
}

View File

@ -10,4 +10,10 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
require_once dirname(dirname(__FILE__)) . '/vendor/autoload.php'; namespace Hyperf\Snowflake\IdGenerator;
use Hyperf\Snowflake\IdGenerator;
class SnowflakeIdGenerator extends IdGenerator
{
}

View File

@ -14,12 +14,6 @@ namespace Hyperf\Snowflake;
interface IdGeneratorInterface extends \Hyperf\Contract\IdGeneratorInterface interface IdGeneratorInterface extends \Hyperf\Contract\IdGeneratorInterface
{ {
const LEVEL_SECOND = 1;
const LEVEL_MILLISECOND = 2;
const DEFAULT_SECOND = 1565712000;
public function generate(?Meta $meta = null): int; public function generate(?Meta $meta = null): int;
public function degenerate(int $id): Meta; public function degenerate(int $id): Meta;

View File

@ -12,89 +12,122 @@ declare(strict_types=1);
namespace Hyperf\Snowflake; namespace Hyperf\Snowflake;
use Hyperf\Snowflake\Exception\SnowflakeException;
class Meta class Meta
{ {
const MILLISECOND_BITS = 41;
const DATA_CENTER_ID_BITS = 5;
const MACHINE_ID_BITS = 5;
const SEQUENCE_BITS = 12; const SEQUENCE_BITS = 12;
const MILLISECOND_BITS = 39; /**
* @var int [0, 31]
const BUSINESS_ID_BITS = 4; */
protected $dataCenterId;
const DATA_CENTER_ID_BITS = 2;
const MACHINE_ID_BITS = 7;
/** /**
* @var int [0, 15] * @var int [0, 31]
*/ */
public $businessId; protected $workerId;
/**
* @var int [0, 3]
*/
public $dataCenterId;
/**
* @var int [0, 127]
*/
public $machineId;
/** /**
* @var int [0, 4095] * @var int [0, 4095]
*/ */
public $sequence; protected $sequence;
/** /**
* @var int seconds or millisecond * @var int seconds or milliseconds
*/ */
public $timestamp = 0; protected $timestamp = 0;
public function __construct(int $businessId, int $dataCenterId, int $machineId, int $sequence) /**
* @var int seconds or milliseconds
*/
protected $beginTimeStamp = 0;
public function __construct(int $dataCenterId, int $workerId, int $sequence, int $timestamp, int $beginTimeStamp = 1560960000)
{ {
if ($businessId < 0 || $businessId > $this->maxBusinessId()) {
throw new SnowflakeException('Business Id can\'t be greater than 15 or less than 0');
}
if ($dataCenterId < 0 || $dataCenterId > $this->maxDataCenterId()) {
throw new SnowflakeException('DataCenter Id can\'t be greater than 4 or less than 0');
}
if ($machineId < 0 || $machineId > $this->maxMachineId()) {
throw new SnowflakeException('Machine Id can\'t be greater than 128 or less than 0');
}
if ($sequence < 0 || $sequence > $this->maxSequence()) {
throw new SnowflakeException('Sequence can\'t be greater than 4096 or less than 0');
}
$this->businessId = $businessId;
$this->dataCenterId = $dataCenterId; $this->dataCenterId = $dataCenterId;
$this->machineId = $machineId; $this->workerId = $workerId;
$this->sequence = $sequence; $this->sequence = $sequence;
$this->timestamp = $timestamp;
$this->beginTimeStamp = $beginTimeStamp;
} }
public function setTimestamp(int $timestamp): self public function getTimeInterval(): int
{ {
$this->timestamp = $timestamp; return $this->timestamp - $this->beginTimeStamp;
}
/**
* @return int
*/
public function getDataCenterId(): int
{
return $this->dataCenterId;
}
/**
* @return int
*/
public function getWorkerId(): int
{
return $this->workerId;
}
/**
* @return int
*/
public function getSequence(): int
{
return $this->sequence;
}
/**
* @param int $dataCenterId
* @return Meta
*/
public function setDataCenterId(int $dataCenterId): self
{
$this->dataCenterId = $dataCenterId;
return $this; return $this;
} }
protected function maxMachineId(): int /**
* @param int $workerId
* @return Meta
*/
public function setWorkerId(int $workerId): self
{ {
return -1 ^ (-1 << self::MACHINE_ID_BITS); $this->workerId = $workerId;
return $this;
} }
protected function maxDataCenterId(): int /**
* @param int $sequence
* @return Meta
*/
public function setSequence(int $sequence): self
{ {
return -1 ^ (-1 << self::DATA_CENTER_ID_BITS); $this->sequence = $sequence;
return $this;
} }
protected function maxBusinessId(): int /**
* @return int
*/
public function getTimestamp(): int
{ {
return -1 ^ (-1 << self::BUSINESS_ID_BITS); return $this->timestamp;
} }
protected function maxSequence(): int /**
* @return int
*/
public function getBeginTimeStamp(): int
{ {
return -1 ^ (-1 << self::SEQUENCE_BITS); return $this->beginTimeStamp;
} }
} }

View File

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Snowflake;
use Hyperf\Snowflake\Exception\SnowflakeException;
abstract class MetaGenerator implements MetaGeneratorInterface
{
/**
* @var ConfigInterface
*/
protected $config;
protected $sequence = 0;
protected $lastTimeStamp = 0;
protected $beginTimeStamp = 0;
public function __construct(ConfigInterface $config, int $beginTimeStamp)
{
$this->config = $config;
$this->lastTimeStamp = $this->getTimeStamp();
$this->beginTimeStamp = $beginTimeStamp;
}
public function generate(): Meta
{
$timestamp = $this->getTimeStamp();
if ($timestamp < $this->lastTimeStamp) {
$this->clockMovedBackwards($timestamp, $this->lastTimeStamp);
}
if ($timestamp == $this->lastTimeStamp) {
$this->sequence = ($this->sequence + 1) % $this->config->maxSequence();
if ($this->sequence == 0) {
$timestamp = $this->getNextTimeStamp();
}
} else {
$this->sequence = 0;
}
if ($timestamp < $this->beginTimeStamp) {
throw new SnowflakeException(sprintf('The beginTimeStamp %d is invalid, because it smaller than timestamp %d.', $this->beginTimeStamp, $timestamp));
}
$this->lastTimeStamp = $timestamp;
return new Meta($this->getDataCenterId(), $this->getWorkerId(), $this->sequence, $timestamp, $this->beginTimeStamp);
}
public function getBeginTimeStamp(): int
{
return $this->beginTimeStamp;
}
public function getConfig(): ConfigInterface
{
return $this->config;
}
abstract public function getDataCenterId(): int;
abstract public function getWorkerId(): int;
abstract public function getTimeStamp(): int;
abstract public function getNextTimeStamp(): int;
protected function clockMovedBackwards($timestamp, $lastTimeStamp)
{
throw new SnowflakeException(sprintf('Clock moved backwards. Refusing to generate id for %d milliseconds.', $lastTimeStamp - $timestamp));
}
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Snowflake\MetaGenerator;
use Hyperf\Snowflake\ConfigInterface;
use Hyperf\Snowflake\MetaGenerator;
class RandomMilliSecondMetaGenerator extends MetaGenerator
{
public function __construct(ConfigInterface $config, int $beginTimeStamp)
{
parent::__construct($config, $beginTimeStamp * 1000);
}
public function getDataCenterId(): int
{
return rand(0, 31);
}
public function getWorkerId(): int
{
return rand(0, 31);
}
public function getTimeStamp(): int
{
return intval(microtime(true) * 1000);
}
public function getNextTimeStamp(): int
{
$timestamp = $this->getTimeStamp();
while ($timestamp <= $this->lastTimeStamp) {
$timestamp = $this->getTimeStamp();
}
return $timestamp;
}
}

View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Snowflake\MetaGenerator;
use Hyperf\Contract\ConfigInterface as HyperfConfig;
use Hyperf\Redis\RedisProxy;
use Hyperf\Snowflake\ConfigInterface;
use Hyperf\Snowflake\MetaGenerator;
class RedisMilliSecondMetaGenerator extends MetaGenerator
{
const REDIS_KEY = 'hyperf:snowflake:worker';
protected $workerId;
protected $dataCenterId;
public function __construct(HyperfConfig $hConfig, ConfigInterface $config, int $beginTimeStamp = self::DEFAULT_BEGIN_SECOND)
{
parent::__construct($config, $beginTimeStamp * 1000);
$pool = $hConfig->get('snowflake.' . static::class . '.pool', 'default');
/** @var \Redis $redis */
$redis = make(RedisProxy::class, [
'pool' => $pool,
]);
$id = $redis->incr(static::REDIS_KEY);
$this->workerId = $id % $config->maxWorkerId();
$this->dataCenterId = intval($id / $config->maxWorkerId()) % $config->maxDataCenterId();
}
public function getDataCenterId(): int
{
return $this->dataCenterId;
}
public function getWorkerId(): int
{
return $this->workerId;
}
public function getTimeStamp(): int
{
return intval(microtime(true) * 1000);
}
public function getNextTimeStamp(): int
{
$timestamp = $this->getTimeStamp();
while ($timestamp <= $this->lastTimeStamp) {
$timestamp = $this->getTimeStamp();
}
return $timestamp;
}
}

View File

@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Snowflake\MetaGenerator;
use Hyperf\Contract\ConfigInterface as HyperfConfig;
use Hyperf\Redis\RedisProxy;
use Hyperf\Snowflake\ConfigInterface;
use Hyperf\Snowflake\MetaGenerator;
class RedisSecondMetaGenerator extends MetaGenerator
{
const REDIS_KEY = 'hyperf:snowflake:worker';
protected $workerId;
protected $dataCenterId;
public function __construct(HyperfConfig $hConfig, ConfigInterface $config, int $beginTimeStamp = self::DEFAULT_BEGIN_SECOND)
{
parent::__construct($config, $beginTimeStamp);
$pool = $hConfig->get('snowflake.' . static::class . '.pool', 'default');
/** @var \Redis $redis */
$redis = make(RedisProxy::class, [
'pool' => $pool,
]);
$id = $redis->incr(static::REDIS_KEY);
$this->workerId = $id % $config->maxWorkerId();
$this->dataCenterId = intval($id / $config->maxWorkerId()) % $config->maxDataCenterId();
}
public function getDataCenterId(): int
{
return $this->dataCenterId;
}
public function getWorkerId(): int
{
return $this->workerId;
}
public function getTimeStamp(): int
{
return time();
}
public function getNextTimeStamp(): int
{
return $this->lastTimeStamp + 1;
}
protected function clockMovedBackwards($timestamp, $lastTimeStamp)
{
}
}

View File

@ -13,19 +13,21 @@ declare(strict_types=1);
namespace Hyperf\Snowflake; namespace Hyperf\Snowflake;
use Hyperf\Contract\ConfigInterface; use Hyperf\Contract\ConfigInterface;
use Hyperf\Snowflake\ConfigInterface as SnowflakeConfigInterface;
use Hyperf\Snowflake\MetaGenerator\RedisMilliSecondMetaGenerator;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
class SnowflakeFactory class MetaGeneratorFactory
{ {
public function __invoke(ContainerInterface $container) public function __invoke(ContainerInterface $container)
{ {
$config = $container->get(ConfigInterface::class); $config = $container->get(ConfigInterface::class);
$level = $config->get('snowflake.level', IdGeneratorInterface::LEVEL_MILLISECOND); $beginSecond = $config->get('snowflake.begin_second', MetaGeneratorInterface::DEFAULT_BEGIN_SECOND);
$beginSecond = $config->get('snowflake.begin_second', IdGeneratorInterface::DEFAULT_SECOND);
return make(Snowflake::class, [ return make(RedisMilliSecondMetaGenerator::class, [
'level' => $level, $config,
'beginSecond' => $beginSecond, $container->get(SnowflakeConfigInterface::class),
$beginSecond,
]); ]);
} }
} }

View File

@ -14,5 +14,11 @@ namespace Hyperf\Snowflake;
interface MetaGeneratorInterface interface MetaGeneratorInterface
{ {
const DEFAULT_BEGIN_SECOND = 1560960000;
public function generate(): Meta; public function generate(): Meta;
public function getBeginTimeStamp(): int;
public function getConfig(): ConfigInterface;
} }

View File

@ -1,28 +0,0 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Snowflake;
class RandomMetaGenerator implements MetaGeneratorInterface
{
protected $sequence = 0;
public function generate(): Meta
{
$businessId = rand(0, 15);
$dataCenterId = rand(0, 3);
$machineId = rand(0, 127);
$sequence = ($this->sequence++) % 4096;
return new Meta($businessId, $dataCenterId, $machineId, $sequence);
}
}

View File

@ -1,104 +0,0 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Snowflake;
class Snowflake implements IdGeneratorInterface
{
/**
* @var MetaGeneratorInterface
*/
protected $metaGenerator;
/**
* @var int
*/
protected $level;
/**
* @var int
*/
protected $beginSecond;
public function __construct(MetaGeneratorInterface $metaGenerator, int $level = self::LEVEL_MILLISECOND, int $beginSecond = self::DEFAULT_SECOND)
{
$this->metaGenerator = $metaGenerator;
$this->level = $level;
$this->beginSecond = $level == self::LEVEL_SECOND ? $beginSecond : $beginSecond * 1000;
}
public function generate(?Meta $meta = null): int
{
$meta = $this->meta($meta);
$timestamp = $this->getTimestamp();
$timestamp = ($timestamp - $this->beginSecond) << $this->getTimestampShift();
$businessId = $meta->businessId << $this->getBusinessIdShift();
$dataCenterId = $meta->dataCenterId << $this->getDataCenterShift();
$machineId = $meta->machineId << $this->getMachineIdShift();
return $timestamp | $businessId | $dataCenterId | $machineId | $meta->sequence;
}
public function degenerate(int $id): Meta
{
$timestamp = $id >> $this->getTimestampShift();
$businessId = $id >> $this->getBusinessIdShift();
$dataCenterId = $id >> $this->getDataCenterShift();
$machineId = $id >> $this->getMachineIdShift();
return (new Meta(
$timestamp << Meta::BUSINESS_ID_BITS ^ $businessId,
$businessId << Meta::DATA_CENTER_ID_BITS ^ $dataCenterId,
$dataCenterId << Meta::MACHINE_ID_BITS ^ $machineId,
$machineId << Meta::SEQUENCE_BITS ^ $id
))->setTimestamp($timestamp + $this->beginSecond);
}
protected function getTimestampShift(): int
{
return Meta::SEQUENCE_BITS + Meta::MACHINE_ID_BITS + Meta::DATA_CENTER_ID_BITS + Meta::BUSINESS_ID_BITS;
}
protected function getBusinessIdShift(): int
{
return Meta::SEQUENCE_BITS + Meta::MACHINE_ID_BITS + Meta::DATA_CENTER_ID_BITS;
}
protected function getDataCenterShift(): int
{
return Meta::SEQUENCE_BITS + Meta::MACHINE_ID_BITS;
}
protected function getMachineIdShift(): int
{
return Meta::SEQUENCE_BITS;
}
protected function getTimestamp(): int
{
if ($this->level == self::LEVEL_SECOND) {
return time();
}
return intval(microtime(true) * 1000);
}
protected function meta(?Meta $meta = null): Meta
{
if (is_null($meta)) {
return $this->metaGenerator->generate();
}
return $meta;
}
}

View File

@ -1,66 +0,0 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Snowflake;
use Hyperf\Snowflake\Meta;
use Hyperf\Snowflake\RandomMetaGenerator;
use Hyperf\Snowflake\Snowflake;
use PHPUnit\Framework\TestCase;
/**
* @internal
* @coversNothing
*/
class GeneratorTest extends TestCase
{
public function testGenerateReturnInt()
{
$generator = new Snowflake(new RandomMetaGenerator());
$this->assertTrue(is_int($generator->generate()));
}
public function testDegenerateInstanceofMeta()
{
$generator = new Snowflake(new RandomMetaGenerator());
$id = $generator->generate();
$this->assertInstanceOf(Meta::class, $generator->degenerate($id));
}
public function testGenerateAndDegenerate()
{
$metaGenerator = new RandomMetaGenerator();
$generator = new Snowflake($metaGenerator);
$meta = $metaGenerator->generate();
$id = $generator->generate($meta);
$this->assertEquals($meta, $generator->degenerate($id)->setTimestamp(0));
$id = $generator->generate();
$this->assertEquals($meta->sequence + 1, $generator->degenerate($id)->sequence);
}
public function testDegenerateMaxId()
{
$generator = new Snowflake(new RandomMetaGenerator(), Snowflake::LEVEL_SECOND, 0);
$meta = $generator->degenerate(PHP_INT_MAX);
$days = intval(($meta->timestamp) / (3600 * 24 * 1000));
$this->assertSame(3181, $days);
$generator = new Snowflake(new RandomMetaGenerator(), Snowflake::LEVEL_SECOND, 0);
$meta = $generator->degenerate(PHP_INT_MAX);
$years = intval($meta->timestamp / (3600 * 24 * 365));
$this->assertSame(8716, $years);
}
}

View File

@ -0,0 +1,192 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Snowflake;
use Hyperf\Config\Config;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Di\Container;
use Hyperf\Pool\Channel;
use Hyperf\Pool\PoolOption;
use Hyperf\Redis\Pool\PoolFactory;
use Hyperf\Redis\Pool\RedisPool;
use Hyperf\Redis\RedisProxy;
use Hyperf\Snowflake\Config as SnowflakeConfig;
use Hyperf\Snowflake\IdGenerator\SnowflakeIdGenerator;
use Hyperf\Snowflake\Meta;
use Hyperf\Snowflake\MetaGenerator\RedisMilliSecondMetaGenerator;
use Hyperf\Snowflake\MetaGenerator\RedisSecondMetaGenerator;
use Hyperf\Snowflake\MetaGeneratorInterface;
use Hyperf\Utils\ApplicationContext;
use HyperfTest\Snowflake\Stub\UserDefinedIdGenerator;
use Mockery;
use PHPUnit\Framework\TestCase;
/**
* @internal
* @coversNothing
*/
class RedisMetaGeneratorTest extends TestCase
{
protected function tearDown()
{
$container = $this->getContainer();
$redis = $container->make(RedisProxy::class, ['pool' => 'snowflake']);
$redis->del(RedisMilliSecondMetaGenerator::REDIS_KEY);
}
public function testGenerateMeta()
{
$container = $this->getContainer();
$config = $container->get(ConfigInterface::class);
$metaGenerator = new RedisMilliSecondMetaGenerator($config, new SnowflakeConfig());
$meta = $metaGenerator->generate();
$this->assertInstanceOf(Meta::class, $meta);
$this->assertSame(0, $meta->getDataCenterId());
$this->assertSame(1, $meta->getWorkerId());
}
public function testGenerateId()
{
$container = $this->getContainer();
$hConfig = $container->get(ConfigInterface::class);
$config = new SnowflakeConfig();
$metaGenerator = new RedisMilliSecondMetaGenerator($hConfig, $config);
$generator = new SnowflakeIdGenerator($metaGenerator);
$id = $generator->generate();
$this->assertTrue(is_int($id));
$meta = $generator->degenerate($id);
$this->assertInstanceOf(Meta::class, $meta);
$this->assertSame(0, $meta->getDataCenterId());
$this->assertSame(1, $meta->getWorkerId());
}
public function testGenerateMetaSeconds()
{
$container = $this->getContainer();
$hConfig = $container->get(ConfigInterface::class);
$config = new SnowflakeConfig();
$metaGenerator = new RedisSecondMetaGenerator($hConfig, $config);
$meta = $metaGenerator->generate();
$this->assertInstanceOf(Meta::class, $meta);
$this->assertSame(0, $meta->getDataCenterId());
$this->assertSame(1, $meta->getWorkerId());
}
public function testGenerateAndDeGenerateSeconds()
{
$container = $this->getContainer();
$hConfig = $container->get(ConfigInterface::class);
$config = new SnowflakeConfig();
$metaGenerator = new RedisSecondMetaGenerator($hConfig, $config);
$generator = new SnowflakeIdGenerator($metaGenerator);
$id = $generator->generate();
$this->assertTrue(is_int($id));
$meta = $generator->degenerate($id);
$this->assertInstanceOf(Meta::class, $meta);
$this->assertSame(0, $meta->getDataCenterId());
$this->assertSame(1, $meta->getWorkerId());
}
public function testDeGenerateMaxId()
{
$container = $this->getContainer();
$hConfig = $container->get(ConfigInterface::class);
$config = new SnowflakeConfig();
$metaGenerator = new RedisSecondMetaGenerator($hConfig, $config);
$generator = new SnowflakeIdGenerator($metaGenerator);
$meta = $generator->degenerate(PHP_INT_MAX);
$interval = $meta->getTimeInterval();
$this->assertSame(31, $meta->getDataCenterId());
$this->assertSame(31, $meta->getWorkerId());
$this->assertSame(4095, $meta->getSequence());
$this->assertSame(69730, intval($interval / (3600 * 24 * 365))); // 7W years
}
public function testUserDefinedIdGenerator()
{
$container = $this->getContainer();
$hConfig = $container->get(ConfigInterface::class);
$config = new SnowflakeConfig();
$metaGenerator = new RedisSecondMetaGenerator($hConfig, $config);
$generator = new SnowflakeIdGenerator($metaGenerator);
$generator = new UserDefinedIdGenerator($generator);
$userId = 20190620;
$id = $generator->generate($userId);
$meta = $generator->degenerate($id);
$this->assertSame($meta->getWorkerId(), $userId % 31);
}
protected function getContainer()
{
$config = new Config([
'redis' => [
'snowflake' => [
'host' => 'localhost',
'auth' => '910123',
'port' => 6379,
'db' => 0,
'timeout' => 0.0,
'reserved' => null,
'retry_interval' => 0,
'pool' => [
'min_connections' => 1,
'max_connections' => 10,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => -1,
'max_idle_time' => 60,
],
],
],
'snowflake' => [
'begin_second' => MetaGeneratorInterface::DEFAULT_BEGIN_SECOND,
RedisMilliSecondMetaGenerator::class => [
'pool' => 'snowflake',
],
RedisSecondMetaGenerator::class => [
'pool' => 'snowflake',
],
],
]);
$container = Mockery::mock(Container::class);
$container->shouldReceive('get')->with(ConfigInterface::class)->andReturn($config);
$container->shouldReceive('make')->with(Channel::class, Mockery::any())->andReturnUsing(function ($class, $args) {
return new Channel($args['size']);
});
$container->shouldReceive('make')->with(PoolOption::class, Mockery::any())->andReturnUsing(function ($class, $args) {
return new PoolOption(...array_values($args));
});
$container->shouldReceive('make')->with(RedisPool::class, Mockery::any())->andReturnUsing(function ($class, $args) use ($container) {
return new RedisPool($container, $args['name']);
});
$factory = new PoolFactory($container);
$container->shouldReceive('make')->with(RedisProxy::class, Mockery::any())->andReturnUsing(function ($class, $args) use ($factory) {
return new RedisProxy($factory, $args['pool']);
});
ApplicationContext::setContainer($container);
return $container;
}
}

View File

@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Snowflake;
use Hyperf\Snowflake\Config;
use Hyperf\Snowflake\IdGenerator\SnowflakeIdGenerator;
use Hyperf\Snowflake\Meta;
use Hyperf\Snowflake\MetaGenerator\RandomMilliSecondMetaGenerator;
use Hyperf\Snowflake\MetaGeneratorInterface;
use PHPUnit\Framework\TestCase;
/**
* @internal
* @coversNothing
*/
class SnowflakeGeneratorTest extends TestCase
{
public function testGenerateReturnInt()
{
$config = new Config();
$generator = new SnowflakeIdGenerator(new RandomMilliSecondMetaGenerator($config, MetaGeneratorInterface::DEFAULT_BEGIN_SECOND));
$this->assertTrue(is_int($generator->generate()));
}
public function testDegenerateInstanceofMeta()
{
$config = new Config();
$generator = new SnowflakeIdGenerator(new RandomMilliSecondMetaGenerator($config, MetaGeneratorInterface::DEFAULT_BEGIN_SECOND));
$id = $generator->generate();
$this->assertInstanceOf(Meta::class, $generator->degenerate($id));
}
public function testGenerateAndDegenerate()
{
$config = new Config();
$metaGenerator = new RandomMilliSecondMetaGenerator($config, MetaGeneratorInterface::DEFAULT_BEGIN_SECOND);
$generator = new SnowflakeIdGenerator($metaGenerator);
$meta = $metaGenerator->generate();
$id = $generator->generate($meta);
$this->assertEquals($meta, $generator->degenerate($id));
}
public function testDegenerateMaxId()
{
$config = new Config();
$metaGenerator = new RandomMilliSecondMetaGenerator($config, MetaGeneratorInterface::DEFAULT_BEGIN_SECOND);
$generator = new SnowflakeIdGenerator($metaGenerator);
$meta = $generator->degenerate(PHP_INT_MAX);
$days = intval(($meta->getTimeInterval()) / (3600 * 24 * 1000));
$this->assertSame(25451, $days); // 70 years.
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Snowflake\Stub;
use Hyperf\Snowflake\IdGenerator;
class UserDefinedIdGenerator
{
/**
* @var IdGenerator\SnowflakeIdGenerator
*/
protected $idGenerator;
public function __construct(IdGenerator\SnowflakeIdGenerator $idGenerator)
{
$this->idGenerator = $idGenerator;
}
public function generate(int $userId)
{
$meta = $this->idGenerator->getMetaGenerator()->generate();
return $this->idGenerator->generate($meta->setWorkerId($userId % 31));
}
public function degenerate(int $id)
{
return $this->idGenerator->degenerate($id);
}
}

View File

@ -1,64 +1,43 @@
# Hyperf Translation # Hyperf Translation
## About [hyperf/translation](https://github.com/hyperf-cloud/translation) 组件衍生于 `Laravel Translation` 组件的,我们对它进行了一些改造,大部分功能保持了相同。在这里感谢一下 Laravel 开发组,实现了如此强大好用的 Translation 组件。
hyperf/translation 是对Laravel Translation的移植不包含门面和快捷函数部分具体使用方法可以参考Laravel Lang的使用。 ## Installation
## Install ```bash
```
composer require hyperf/translation composer require hyperf/translation
``` ```
## Config ## Configuration
### Publish config
### publish config ```bash
``` php bin/hyperf.php vendor:publish hyperf/translation
php bin/hyperf.php vendor:publish hyperf/translation
``` ```
### config path Config files:
``` ```
your/config/path/autoload/translation.php + ./config/autoload/translation.php
``` ```
### config content ### Configuration
``` ```php
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
return [ return [
'locale' => 'en', 'locale' => 'en',
'fallback_locale' => '', 'fallback_locale' => '',
'lang' => BASE_PATH . '/resources/lang', 'path' => BASE_PATH . '/storage/languages',
]; ];
``` ```
## Usage ## Usage
```php
``` $container = ApplicationContext::getContainer();
$translator = $container->get(\Hyperf\Contract\TranslatorInterface::class);
$translator = $this->container->get(\Hyperf\Translation\Contracts\Translator::class); $translator->trans('validation.accepted');
$translator->trans('validation.accepted'),
``` ```

View File

@ -1,13 +1,16 @@
{ {
"name": "hyperf/translation", "name": "hyperf/translation",
"type": "library", "type": "library",
"description": "An independent translation component, forked by illuminate/translation.",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
"translation", "translation",
"hyperf" "hyperf"
], ],
"description": "",
"autoload": { "autoload": {
"files": [
"src/Functions.php"
],
"psr-4": { "psr-4": {
"Hyperf\\Translation\\": "src/" "Hyperf\\Translation\\": "src/"
} }
@ -19,18 +22,15 @@
}, },
"require": { "require": {
"php": ">=7.2", "php": ">=7.2",
"ext-swoole": ">=4.3", "hyperf/contract": "1.0.*",
"hyperf/utils": "1.0.*", "hyperf/utils": "1.0.*",
"hyperf/framework": "1.0.*",
"hyperf/di": "1.0.*",
"psr/container": "^1.0" "psr/container": "^1.0"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^2.14", "friendsofphp/php-cs-fixer": "^2.14",
"hyperf/testing": "1.0.*", "hyperf/testing": "1.0.*",
"mockery/mockery": "^1.2", "mockery/mockery": "^1.2",
"phpstan/phpstan": "^0.10.5", "phpstan/phpstan": "^0.10.5"
"swoft/swoole-ide-helper": "^4.4.0"
}, },
"config": { "config": {
"sort-packages": true "sort-packages": true

View File

@ -13,5 +13,5 @@ declare(strict_types=1);
return [ return [
'locale' => 'zh_CN', 'locale' => 'zh_CN',
'fallback_locale' => 'en', 'fallback_locale' => 'en',
'lang' => BASE_PATH . '/resources/lang', 'path' => BASE_PATH . '/storage/languages',
]; ];

View File

@ -12,9 +12,9 @@ declare(strict_types=1);
namespace Hyperf\Translation; namespace Hyperf\Translation;
use Hyperf\Translation\Contracts\Loader; use Hyperf\Contract\TranslatorLoaderInterface;
class ArrayLoader implements Loader class ArrayLoader implements TranslatorLoaderInterface
{ {
/** /**
* All of the translation messages. * All of the translation messages.
@ -25,13 +25,8 @@ class ArrayLoader implements Loader
/** /**
* Load the messages for the given locale. * Load the messages for the given locale.
*
* @param string $locale
* @param string $group
* @param null|string $namespace
* @return array
*/ */
public function load(string $locale, string $group, $namespace = null): array public function load(string $locale, string $group, ?string $namespace = null): array
{ {
$namespace = $namespace ?: '*'; $namespace = $namespace ?: '*';
@ -40,9 +35,6 @@ class ArrayLoader implements Loader
/** /**
* Add a new namespace to the loader. * Add a new namespace to the loader.
*
* @param string $namespace
* @param string $hint
*/ */
public function addNamespace(string $namespace, string $hint) public function addNamespace(string $namespace, string $hint)
{ {
@ -50,8 +42,6 @@ class ArrayLoader implements Loader
/** /**
* Add a new JSON path to the loader. * Add a new JSON path to the loader.
*
* @param string $path
*/ */
public function addJsonPath(string $path) public function addJsonPath(string $path)
{ {
@ -59,14 +49,8 @@ class ArrayLoader implements Loader
/** /**
* Add messages to the loader. * Add messages to the loader.
*
* @param string $locale
* @param string $group
* @param array $messages
* @param null|string $namespace
* @return $this
*/ */
public function addMessages(string $locale, string $group, array $messages, $namespace = null) public function addMessages(string $locale, string $group, array $messages, ?string $namespace = null): self
{ {
$namespace = $namespace ?: '*'; $namespace = $namespace ?: '*';
@ -77,8 +61,6 @@ class ArrayLoader implements Loader
/** /**
* Get an array of all the registered namespaces. * Get an array of all the registered namespaces.
*
* @return array
*/ */
public function namespaces(): array public function namespaces(): array
{ {

View File

@ -12,8 +12,8 @@ declare(strict_types=1);
namespace Hyperf\Translation; namespace Hyperf\Translation;
use Hyperf\Translation\Contracts\Loader; use Hyperf\Contract\TranslatorInterface;
use Hyperf\Translation\Contracts\Translator; use Hyperf\Contract\TranslatorLoaderInterface;
class ConfigProvider class ConfigProvider
{ {
@ -21,8 +21,8 @@ class ConfigProvider
{ {
return [ return [
'dependencies' => [ 'dependencies' => [
Loader::class => FileLoaderFactory::class, TranslatorLoaderInterface::class => FileLoaderFactory::class,
Translator::class => TranslatorFactory::class, TranslatorInterface::class => TranslatorFactory::class,
], ],
'scan' => [ 'scan' => [
'paths' => [ 'paths' => [

View File

@ -12,11 +12,11 @@ declare(strict_types=1);
namespace Hyperf\Translation; namespace Hyperf\Translation;
use Hyperf\Translation\Contracts\Loader; use Hyperf\Contract\TranslatorLoaderInterface;
use Hyperf\Utils\Filesystem\Filesystem; use Hyperf\Utils\Filesystem\Filesystem;
use RuntimeException; use RuntimeException;
class FileLoader implements Loader class FileLoader implements TranslatorLoaderInterface
{ {
/** /**
* The filesystem instance. * The filesystem instance.
@ -60,13 +60,8 @@ class FileLoader implements Loader
/** /**
* Load the messages for the given locale. * Load the messages for the given locale.
*
* @param string $locale
* @param string $group
* @param null|string $namespace
* @return array
*/ */
public function load(string $locale, string $group, $namespace = null): array public function load(string $locale, string $group, ?string $namespace = null): array
{ {
if ($group === '*' && $namespace === '*') { if ($group === '*' && $namespace === '*') {
return $this->loadJsonPaths($locale); return $this->loadJsonPaths($locale);
@ -81,9 +76,6 @@ class FileLoader implements Loader
/** /**
* Add a new namespace to the loader. * Add a new namespace to the loader.
*
* @param string $namespace
* @param string $hint
*/ */
public function addNamespace(string $namespace, string $hint) public function addNamespace(string $namespace, string $hint)
{ {
@ -92,8 +84,6 @@ class FileLoader implements Loader
/** /**
* Add a new JSON path to the loader. * Add a new JSON path to the loader.
*
* @param string $path
*/ */
public function addJsonPath(string $path) public function addJsonPath(string $path)
{ {
@ -102,8 +92,6 @@ class FileLoader implements Loader
/** /**
* Get an array of all the registered namespaces. * Get an array of all the registered namespaces.
*
* @return array
*/ */
public function namespaces(): array public function namespaces(): array
{ {
@ -112,11 +100,6 @@ class FileLoader implements Loader
/** /**
* Load a namespaced translation group. * Load a namespaced translation group.
*
* @param string $locale
* @param string $group
* @param string $namespace
* @return array
*/ */
protected function loadNamespaced(string $locale, string $group, string $namespace): array protected function loadNamespaced(string $locale, string $group, string $namespace): array
{ {
@ -131,12 +114,6 @@ class FileLoader implements Loader
/** /**
* Load a local namespaced translation group for overrides. * Load a local namespaced translation group for overrides.
*
* @param array $lines
* @param string $locale
* @param string $group
* @param string $namespace
* @return array
*/ */
protected function loadNamespaceOverrides(array $lines, string $locale, string $group, string $namespace): array protected function loadNamespaceOverrides(array $lines, string $locale, string $group, string $namespace): array
{ {
@ -151,11 +128,6 @@ class FileLoader implements Loader
/** /**
* Load a locale from a given path. * Load a locale from a given path.
*
* @param string $path
* @param string $locale
* @param string $group
* @return array
*/ */
protected function loadPath(string $path, string $locale, string $group): array protected function loadPath(string $path, string $locale, string $group): array
{ {
@ -169,11 +141,9 @@ class FileLoader implements Loader
/** /**
* Load a locale from the given JSON file path. * Load a locale from the given JSON file path.
* *
* @param string $locale
* @throws \RuntimeException * @throws \RuntimeException
* @return array
*/ */
protected function loadJsonPaths(string $locale) protected function loadJsonPaths(string $locale): iterable
{ {
return collect(array_merge($this->jsonPaths, [$this->path])) return collect(array_merge($this->jsonPaths, [$this->path]))
->reduce(function ($output, $path) use ($locale) { ->reduce(function ($output, $path) use ($locale) {

View File

@ -22,7 +22,7 @@ class FileLoaderFactory
{ {
$config = $container->get(ConfigInterface::class); $config = $container->get(ConfigInterface::class);
$files = $container->get(Filesystem::class); $files = $container->get(Filesystem::class);
$path = $config->get('translation.lang'); $path = $config->get('translation.path');
return make(FileLoader::class, compact('files', 'path')); return make(FileLoader::class, compact('files', 'path'));
} }

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
use Hyperf\Contract\TranslatorInterface;
use Hyperf\Utils\ApplicationContext;
if (! function_exists('__')) {
function __(string $key, array $replace = [], ?string $locale = null)
{
$translator = ApplicationContext::getContainer()->get(TranslatorInterface::class);
return $translator->trans($key, $replace, $locale);
}
}
if (! function_exists('trans')) {
function trans(string $key, array $replace = [], ?string $locale = null)
{
return __($key, $replace, $locale);
}
}
if (! function_exists('trans_choice')) {
function trans_choice(string $key, $number, array $replace = [], ?string $locale = null): string
{
$translator = ApplicationContext::getContainer()->get(TranslatorInterface::class);
return $translator->transChoice($key, $number, $replace, $locale);
}
}

View File

@ -1,112 +0,0 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Translation\Support;
class NamespacedItemResolver
{
/**
* A cache of the parsed items.
*
* @var array
*/
protected $parsed = [];
/**
* Parse a key into namespace, group, and item.
*
* @param string $key
* @return array
*/
public function parseKey(string $key): array
{
// If we've already parsed the given key, we'll return the cached version we
// already have, as this will save us some processing. We cache off every
// key we parse so we can quickly return it on all subsequent requests.
if (isset($this->parsed[$key])) {
return $this->parsed[$key];
}
// If the key does not contain a double colon, it means the key is not in a
// namespace, and is just a regular configuration item. Namespaces are a
// tool for organizing configuration items for things such as modules.
if (strpos($key, '::') === false) {
$segments = explode('.', $key);
$parsed = $this->parseBasicSegments($segments);
} else {
$parsed = $this->parseNamespacedSegments($key);
}
// Once we have the parsed array of this key's elements, such as its groups
// and namespace, we will cache each array inside a simple list that has
// the key and the parsed array for quick look-ups for later requests.
return $this->parsed[$key] = $parsed;
}
/**
* Set the parsed value of a key.
*
* @param string $key
* @param array $parsed
*/
public function setParsedKey(string $key, array $parsed)
{
$this->parsed[$key] = $parsed;
}
/**
* Parse an array of basic segments.
*
* @param array $segments
* @return array
*/
protected function parseBasicSegments(array $segments)
{
// The first segment in a basic array will always be the group, so we can go
// ahead and grab that segment. If there is only one total segment we are
// just pulling an entire group out of the array and not a single item.
$group = $segments[0];
// If there is more than one segment in this group, it means we are pulling
// a specific item out of a group and will need to return this item name
// as well as the group so we know which item to pull from the arrays.
$item = count($segments) === 1
? null
: implode('.', array_slice($segments, 1));
return [null, $group, $item];
}
/**
* Parse an array of namespaced segments.
*
* @param string $key
* @return array
*/
protected function parseNamespacedSegments(string $key): array
{
[$namespace, $item] = explode('::', $key);
// First we'll just explode the first segment to get the namespace and group
// since the item should be in the remaining segments. Once we have these
// two pieces of data we can proceed with parsing out the item's value.
$itemSegments = explode('.', $item);
$groupAndItem = array_slice(
$this->parseBasicSegments($itemSegments),
1
);
return array_merge([$namespace], $groupAndItem);
}
}

View File

@ -13,22 +13,21 @@ declare(strict_types=1);
namespace Hyperf\Translation; namespace Hyperf\Translation;
use Countable; use Countable;
use Hyperf\Translation\Contracts\Loader; use Hyperf\Contract\TranslatorInterface;
use Hyperf\Translation\Contracts\Translator as TranslatorContract; use Hyperf\Contract\TranslatorLoaderInterface;
use Hyperf\Translation\Support\NamespacedItemResolver;
use Hyperf\Utils\Arr; use Hyperf\Utils\Arr;
use Hyperf\Utils\Collection; use Hyperf\Utils\Collection;
use Hyperf\Utils\Str; use Hyperf\Utils\Str;
use Hyperf\Utils\Traits\Macroable; use Hyperf\Utils\Traits\Macroable;
class Translator extends NamespacedItemResolver implements TranslatorContract class Translator implements TranslatorInterface
{ {
use Macroable; use Macroable;
/** /**
* The loader implementation. * The loader implementation.
* *
* @var \Hyperf\Translation\Contracts\Loader * @var TranslatorLoaderInterface
*/ */
protected $loader; protected $loader;
@ -61,12 +60,13 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
protected $selector; protected $selector;
/** /**
* Create a new translator instance. * A cache of the parsed items.
* *
* @param \Hyperf\Translation\Contracts\Loader $loader * @var array
* @param string $locale
*/ */
public function __construct(Loader $loader, string $locale) protected $parsed = [];
public function __construct(TranslatorLoaderInterface $loader, string $locale)
{ {
$this->loader = $loader; $this->loader = $loader;
$this->locale = $locale; $this->locale = $locale;
@ -74,25 +74,16 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Determine if a translation exists for a given locale. * Determine if a translation exists for a given locale.
*
* @param string $key
* @param null|string $locale
* @return bool
*/ */
public function hasForLocale(string $key, $locale = null): bool public function hasForLocale(string $key, ?string $locale = null): bool
{ {
return $this->has($key, $locale, false); return $this->has($key, $locale, false);
} }
/** /**
* Determine if a translation exists. * Determine if a translation exists.
*
* @param string $key
* @param null|string $locale
* @param bool $fallback
* @return bool
*/ */
public function has(string $key, $locale = null, bool $fallback = true): bool public function has(string $key, ?string $locale = null, bool $fallback = true): bool
{ {
return $this->get($key, [], $locale, $fallback) !== $key; return $this->get($key, [], $locale, $fallback) !== $key;
} }
@ -100,12 +91,9 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Get the translation for a given key. * Get the translation for a given key.
* *
* @param string $key
* @param array $replace
* @param null|string $locale
* @return array|string * @return array|string
*/ */
public function trans(string $key, array $replace = [], $locale = null) public function trans(string $key, array $replace = [], ?string $locale = null)
{ {
return $this->get($key, $replace, $locale); return $this->get($key, $replace, $locale);
} }
@ -113,13 +101,9 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Get the translation for the given key. * Get the translation for the given key.
* *
* @param string $key
* @param array $replace
* @param null|string $locale
* @param bool $fallback
* @return array|string * @return array|string
*/ */
public function get(string $key, array $replace = [], $locale = null, $fallback = true) public function get(string $key, array $replace = [], ?string $locale = null, bool $fallback = true)
{ {
[$namespace, $group, $item] = $this->parseKey($key); [$namespace, $group, $item] = $this->parseKey($key);
@ -150,12 +134,9 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Get the translation for a given key from the JSON translation files. * Get the translation for a given key from the JSON translation files.
* *
* @param string $key
* @param array $replace
* @param null|string $locale
* @return array|string * @return array|string
*/ */
public function getFromJson(string $key, array $replace = [], $locale = null) public function getFromJson(string $key, array $replace = [], ?string $locale = null)
{ {
$locale = $locale ?: $this->locale; $locale = $locale ?: $this->locale;
@ -183,13 +164,9 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Get a translation according to an integer value. * Get a translation according to an integer value.
* *
* @param string $key
* @param array|\Countable|int $number * @param array|\Countable|int $number
* @param array $replace
* @param null|string $locale
* @return string
*/ */
public function transChoice(string $key, $number, array $replace = [], $locale = null): string public function transChoice(string $key, $number, array $replace = [], ?string $locale = null): string
{ {
return $this->choice($key, $number, $replace, $locale); return $this->choice($key, $number, $replace, $locale);
} }
@ -197,13 +174,9 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Get a translation according to an integer value. * Get a translation according to an integer value.
* *
* @param string $key
* @param array|\Countable|int $number * @param array|\Countable|int $number
* @param array $replace
* @param null|string $locale
* @return string
*/ */
public function choice(string $key, $number, array $replace = [], $locale = null): string public function choice(string $key, $number, array $replace = [], ?string $locale = null): string
{ {
$line = $this->get( $line = $this->get(
$key, $key,
@ -228,10 +201,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Add translation lines to the given locale. * Add translation lines to the given locale.
*
* @param array $lines
* @param string $locale
* @param string $namespace
*/ */
public function addLines(array $lines, string $locale, string $namespace = '*') public function addLines(array $lines, string $locale, string $namespace = '*')
{ {
@ -244,10 +213,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Load the specified language group. * Load the specified language group.
*
* @param string $namespace
* @param string $group
* @param string $locale
*/ */
public function load(string $namespace, string $group, string $locale) public function load(string $namespace, string $group, string $locale)
{ {
@ -265,9 +230,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Add a new namespace to the loader. * Add a new namespace to the loader.
*
* @param string $namespace
* @param string $hint
*/ */
public function addNamespace(string $namespace, string $hint) public function addNamespace(string $namespace, string $hint)
{ {
@ -276,8 +238,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Add a new JSON path to the loader. * Add a new JSON path to the loader.
*
* @param string $path
*/ */
public function addJsonPath(string $path) public function addJsonPath(string $path)
{ {
@ -286,27 +246,41 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Parse a key into namespace, group, and item. * Parse a key into namespace, group, and item.
*
* @param string $key
* @return array
*/ */
public function parseKey(string $key): array public function parseKey(string $key): array
{ {
$segments = parent::parseKey($key); // If we've already parsed the given key, we'll return the cached version we
// already have, as this will save us some processing. We cache off every
if (is_null($segments[0])) { // key we parse so we can quickly return it on all subsequent requests.
$segments[0] = '*'; if (isset($this->parsed[$key])) {
return $this->parsed[$key];
} }
return $segments; // If the key does not contain a double colon, it means the key is not in a
// namespace, and is just a regular configuration item. Namespaces are a
// tool for organizing configuration items for things such as modules.
if (strpos($key, '::') === false) {
$segments = explode('.', $key);
$parsed = $this->parseBasicSegments($segments);
} else {
$parsed = $this->parseNamespacedSegments($key);
}
if (is_null($parsed[0])) {
$parsed[0] = '*';
}
// Once we have the parsed array of this key's elements, such as its groups
// and namespace, we will cache each array inside a simple list that has
// the key and the parsed array for quick look-ups for later requests.
return $this->parsed[$key] = $parsed;
} }
/** /**
* Get the message selector instance. * Get the message selector instance.
*
* @return \Hyperf\Translation\MessageSelector
*/ */
public function getSelector() public function getSelector(): MessageSelector
{ {
if (! isset($this->selector)) { if (! isset($this->selector)) {
$this->selector = new MessageSelector(); $this->selector = new MessageSelector();
@ -317,8 +291,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Set the message selector instance. * Set the message selector instance.
*
* @param \Hyperf\Translation\MessageSelector $selector
*/ */
public function setSelector(MessageSelector $selector) public function setSelector(MessageSelector $selector)
{ {
@ -328,7 +300,7 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Get the language line loader implementation. * Get the language line loader implementation.
* *
* @return \Hyperf\Translation\Contracts\Loader * @return TranslatorLoaderInterface
*/ */
public function getLoader() public function getLoader()
{ {
@ -337,8 +309,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Get the default locale being used. * Get the default locale being used.
*
* @return string
*/ */
public function locale(): string public function locale(): string
{ {
@ -347,8 +317,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Get the default locale being used. * Get the default locale being used.
*
* @return string
*/ */
public function getLocale(): string public function getLocale(): string
{ {
@ -357,8 +325,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Set the default locale. * Set the default locale.
*
* @param string $locale
*/ */
public function setLocale(string $locale) public function setLocale(string $locale)
{ {
@ -367,8 +333,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Get the fallback locale being used. * Get the fallback locale being used.
*
* @return string
*/ */
public function getFallback(): string public function getFallback(): string
{ {
@ -377,8 +341,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Set the fallback locale being used. * Set the fallback locale being used.
*
* @param string $fallback
*/ */
public function setFallback(string $fallback) public function setFallback(string $fallback)
{ {
@ -387,8 +349,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Set the loaded translation groups. * Set the loaded translation groups.
*
* @param array $loaded
*/ */
public function setLoaded(array $loaded) public function setLoaded(array $loaded)
{ {
@ -396,12 +356,20 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
} }
/** /**
* Get the proper locale for a choice operation. * Set the parsed value of a key.
* *
* @param null|string $locale * @param string $key
* @return string * @param array $parsed
*/ */
protected function localeForChoice($locale): string public function setParsedKey(string $key, array $parsed)
{
$this->parsed[$key] = $parsed;
}
/**
* Get the proper locale for a choice operation.
*/
protected function localeForChoice(?string $locale): string
{ {
return $locale ?: $this->locale ?: $this->fallback; return $locale ?: $this->locale ?: $this->fallback;
} }
@ -409,11 +377,7 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Retrieve a language line out the loaded array. * Retrieve a language line out the loaded array.
* *
* @param string $namespace
* @param string $group
* @param string $locale
* @param mixed $item * @param mixed $item
* @param array $replace
* @return null|array|string * @return null|array|string
*/ */
protected function getLine(string $namespace, string $group, string $locale, $item, array $replace) protected function getLine(string $namespace, string $group, string $locale, $item, array $replace)
@ -429,6 +393,7 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
if (is_string($line)) { if (is_string($line)) {
return $this->makeReplacements($line, $replace); return $this->makeReplacements($line, $replace);
} }
if (is_array($line) && count($line) > 0) { if (is_array($line) && count($line) > 0) {
foreach ($line as $key => $value) { foreach ($line as $key => $value) {
$line[$key] = $this->makeReplacements($value, $replace); $line[$key] = $this->makeReplacements($value, $replace);
@ -441,9 +406,8 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Make the place-holder replacements on a line. * Make the place-holder replacements on a line.
* *
* @param string $line * @param array|string $line
* @param array $replace * @return array|string
* @return string
*/ */
protected function makeReplacements($line, array $replace) protected function makeReplacements($line, array $replace)
{ {
@ -468,9 +432,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Sort the replacements array. * Sort the replacements array.
*
* @param array $replace
* @return array
*/ */
protected function sortReplacements(array $replace): array protected function sortReplacements(array $replace): array
{ {
@ -481,25 +442,63 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
/** /**
* Determine if the given group has been loaded. * Determine if the given group has been loaded.
*
* @param string $namespace
* @param string $group
* @param string $locale
* @return bool
*/ */
protected function isLoaded(string $namespace, string $group, string $locale) protected function isLoaded(string $namespace, string $group, string $locale): bool
{ {
return isset($this->loaded[$namespace][$group][$locale]); return isset($this->loaded[$namespace][$group][$locale]);
} }
/** /**
* Get the array of locales to be checked. * Get the array of locales to be checked.
*
* @param null|string $locale
* @return array
*/ */
protected function localeArray($locale): array protected function localeArray(?string $locale): array
{ {
return array_filter([$locale ?: $this->locale, $this->fallback]); return array_filter([$locale ?: $this->locale, $this->fallback]);
} }
/**
* Parse an array of basic segments.
*
* @param array $segments
* @return array
*/
protected function parseBasicSegments(array $segments)
{
// The first segment in a basic array will always be the group, so we can go
// ahead and grab that segment. If there is only one total segment we are
// just pulling an entire group out of the array and not a single item.
$group = $segments[0];
// If there is more than one segment in this group, it means we are pulling
// a specific item out of a group and will need to return this item name
// as well as the group so we know which item to pull from the arrays.
$item = count($segments) === 1
? null
: implode('.', array_slice($segments, 1));
return [null, $group, $item];
}
/**
* Parse an array of namespaced segments.
*
* @param string $key
* @return array
*/
protected function parseNamespacedSegments(string $key): array
{
[$namespace, $item] = explode('::', $key);
// First we'll just explode the first segment to get the namespace and group
// since the item should be in the remaining segments. Once we have these
// two pieces of data we can proceed with parsing out the item's value.
$itemSegments = explode('.', $item);
$groupAndItem = array_slice(
$this->parseBasicSegments($itemSegments),
1
);
return array_merge([$namespace], $groupAndItem);
}
} }

View File

@ -13,7 +13,7 @@ declare(strict_types=1);
namespace Hyperf\Translation; namespace Hyperf\Translation;
use Hyperf\Contract\ConfigInterface; use Hyperf\Contract\ConfigInterface;
use Hyperf\Translation\Contracts\Loader; use Hyperf\Contract\TranslatorLoaderInterface;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
class TranslatorFactory class TranslatorFactory
@ -28,11 +28,11 @@ class TranslatorFactory
$locale = $config->get('translation.locale'); $locale = $config->get('translation.locale');
$fallbackLocale = $config->get('translation.fallback_locale'); $fallbackLocale = $config->get('translation.fallback_locale');
$loader = $container->get(Loader::class); $loader = $container->get(TranslatorLoaderInterface::class);
$trans = make(Translator::class, compact('loader', 'locale')); $translator = make(Translator::class, compact('loader', 'locale'));
$trans->setFallback((string) $fallbackLocale); $translator->setFallback((string) $fallbackLocale);
return $trans; return $translator;
} }
} }

View File

@ -10,26 +10,26 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace HyperfTest\Translation\Cases; namespace HyperfTest\Translation;
use Hyperf\Translation\FileLoader; use Hyperf\Translation\FileLoader;
use Hyperf\Utils\Filesystem\Filesystem; use Hyperf\Utils\Filesystem\Filesystem;
use Mockery as m; use Mockery;
use PHPUnit\Framework\TestCase;
/** /**
* @internal * @internal
* @coversNothing
*/ */
class TranslationFileLoaderTest extends AbstractTestCase class FileLoaderTest extends TestCase
{ {
protected function tearDown(): void protected function tearDown(): void
{ {
m::close(); Mockery::close();
} }
public function testLoadMethodWithoutNamespacesProperlyCallsLoader() public function testLoadMethodWithoutNamespacesProperlyCallsLoader()
{ {
$loader = new FileLoader($files = m::mock(Filesystem::class), __DIR__); $loader = new FileLoader($files = Mockery::mock(Filesystem::class), __DIR__);
$files->shouldReceive('exists')->once()->with(__DIR__ . '/en/foo.php')->andReturn(true); $files->shouldReceive('exists')->once()->with(__DIR__ . '/en/foo.php')->andReturn(true);
$files->shouldReceive('getRequire')->once()->with(__DIR__ . '/en/foo.php')->andReturn(['messages']); $files->shouldReceive('getRequire')->once()->with(__DIR__ . '/en/foo.php')->andReturn(['messages']);
@ -38,7 +38,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
public function testLoadMethodWithNamespacesProperlyCallsLoader() public function testLoadMethodWithNamespacesProperlyCallsLoader()
{ {
$loader = new FileLoader($files = m::mock(Filesystem::class), __DIR__); $loader = new FileLoader($files = Mockery::mock(Filesystem::class), __DIR__);
$files->shouldReceive('exists')->once()->with('bar/en/foo.php')->andReturn(true); $files->shouldReceive('exists')->once()->with('bar/en/foo.php')->andReturn(true);
$files->shouldReceive('exists')->once()->with(__DIR__ . '/vendor/namespace/en/foo.php')->andReturn(false); $files->shouldReceive('exists')->once()->with(__DIR__ . '/vendor/namespace/en/foo.php')->andReturn(false);
$files->shouldReceive('getRequire')->once()->with('bar/en/foo.php')->andReturn(['foo' => 'bar']); $files->shouldReceive('getRequire')->once()->with('bar/en/foo.php')->andReturn(['foo' => 'bar']);
@ -49,7 +49,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
public function testLoadMethodWithNamespacesProperlyCallsLoaderAndLoadsLocalOverrides() public function testLoadMethodWithNamespacesProperlyCallsLoaderAndLoadsLocalOverrides()
{ {
$loader = new FileLoader($files = m::mock(Filesystem::class), __DIR__); $loader = new FileLoader($files = Mockery::mock(Filesystem::class), __DIR__);
$files->shouldReceive('exists')->once()->with('bar/en/foo.php')->andReturn(true); $files->shouldReceive('exists')->once()->with('bar/en/foo.php')->andReturn(true);
$files->shouldReceive('exists')->once()->with(__DIR__ . '/vendor/namespace/en/foo.php')->andReturn(true); $files->shouldReceive('exists')->once()->with(__DIR__ . '/vendor/namespace/en/foo.php')->andReturn(true);
$files->shouldReceive('getRequire')->once()->with('bar/en/foo.php')->andReturn(['foo' => 'bar']); $files->shouldReceive('getRequire')->once()->with('bar/en/foo.php')->andReturn(['foo' => 'bar']);
@ -61,7 +61,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
public function testEmptyArraysReturnedWhenFilesDontExist() public function testEmptyArraysReturnedWhenFilesDontExist()
{ {
$loader = new FileLoader($files = m::mock(Filesystem::class), __DIR__); $loader = new FileLoader($files = Mockery::mock(Filesystem::class), __DIR__);
$files->shouldReceive('exists')->once()->with(__DIR__ . '/en/foo.php')->andReturn(false); $files->shouldReceive('exists')->once()->with(__DIR__ . '/en/foo.php')->andReturn(false);
$files->shouldReceive('getRequire')->never(); $files->shouldReceive('getRequire')->never();
@ -70,7 +70,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
public function testEmptyArraysReturnedWhenFilesDontExistForNamespacedItems() public function testEmptyArraysReturnedWhenFilesDontExistForNamespacedItems()
{ {
$loader = new FileLoader($files = m::mock(Filesystem::class), __DIR__); $loader = new FileLoader($files = Mockery::mock(Filesystem::class), __DIR__);
$files->shouldReceive('getRequire')->never(); $files->shouldReceive('getRequire')->never();
$this->assertEquals([], $loader->load('en', 'foo', 'bar')); $this->assertEquals([], $loader->load('en', 'foo', 'bar'));
@ -78,7 +78,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
public function testLoadMethodForJSONProperlyCallsLoader() public function testLoadMethodForJSONProperlyCallsLoader()
{ {
$loader = new FileLoader($files = m::mock(Filesystem::class), __DIR__); $loader = new FileLoader($files = Mockery::mock(Filesystem::class), __DIR__);
$files->shouldReceive('exists')->once()->with(__DIR__ . '/en.json')->andReturn(true); $files->shouldReceive('exists')->once()->with(__DIR__ . '/en.json')->andReturn(true);
$files->shouldReceive('get')->once()->with(__DIR__ . '/en.json')->andReturn('{"foo":"bar"}'); $files->shouldReceive('get')->once()->with(__DIR__ . '/en.json')->andReturn('{"foo":"bar"}');
@ -87,7 +87,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
public function testLoadMethodForJSONProperlyCallsLoaderForMultiplePaths() public function testLoadMethodForJSONProperlyCallsLoaderForMultiplePaths()
{ {
$loader = new FileLoader($files = m::mock(Filesystem::class), __DIR__); $loader = new FileLoader($files = Mockery::mock(Filesystem::class), __DIR__);
$loader->addJsonPath(__DIR__ . '/another'); $loader->addJsonPath(__DIR__ . '/another');
$files->shouldReceive('exists')->once()->with(__DIR__ . '/en.json')->andReturn(true); $files->shouldReceive('exists')->once()->with(__DIR__ . '/en.json')->andReturn(true);

View File

@ -10,15 +10,15 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace HyperfTest\Translation\Cases; namespace HyperfTest\Translation;
use Hyperf\Translation\MessageSelector; use Hyperf\Translation\MessageSelector;
use PHPUnit\Framework\TestCase;
/** /**
* @internal * @internal
* @coversNothing
*/ */
class TranslationMessageSelectorTest extends AbstractTestCase class MessageSelectorTest extends TestCase
{ {
/** /**
* @dataProvider chooseTestData * @dataProvider chooseTestData

View File

@ -10,58 +10,105 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace HyperfTest\Translation\Cases; namespace HyperfTest\Translation;
use Hyperf\Translation\Contracts\Loader; use Hyperf\Contract\TranslatorLoaderInterface;
use Hyperf\Translation\MessageSelector; use Hyperf\Translation\MessageSelector;
use Hyperf\Translation\Translator; use Hyperf\Translation\Translator;
use Hyperf\Utils\Collection; use Hyperf\Utils\Collection;
use Mockery as m; use Mockery;
use PHPUnit\Framework\TestCase;
/** /**
* @internal * @internal
* @coversNothing
*/ */
class TranslationTranslatorTest extends AbstractTestCase class TranslatorTest extends TestCase
{ {
protected function tearDown(): void protected function tearDown(): void
{ {
m::close(); Mockery::close();
} }
public function testHasMethodReturnsFalseWhenReturnedTranslationIsNull() public function testHasMethodReturnsFalseWhenReturnedTranslationIsNull()
{ {
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); $t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
$t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'))->will($this->returnValue('foo')); $this->getLoader(),
'en',
])->getMock();
$t->expects($this->once())
->method('get')
->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'))
->will($this->returnValue('foo'));
$this->assertFalse($t->has('foo', 'bar')); $this->assertFalse($t->has('foo', 'bar'));
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en', 'sp'])->getMock(); $t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
$t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'))->will($this->returnValue('bar')); $this->getLoader(),
'en',
'sp',
])->getMock();
$t->expects($this->once())
->method('get')
->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'))
->will($this->returnValue('bar'));
$this->assertTrue($t->has('foo', 'bar')); $this->assertTrue($t->has('foo', 'bar'));
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); $t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
$t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'), false)->will($this->returnValue('bar')); $this->getLoader(),
'en',
])->getMock();
$t->expects($this->once())
->method('get')
->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'), false)
->will($this->returnValue('bar'));
$this->assertTrue($t->hasForLocale('foo', 'bar')); $this->assertTrue($t->hasForLocale('foo', 'bar'));
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); $t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
$t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'), false)->will($this->returnValue('foo')); $this->getLoader(),
'en',
])->getMock();
$t->expects($this->once())
->method('get')
->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'), false)
->will($this->returnValue('foo'));
$this->assertFalse($t->hasForLocale('foo', 'bar')); $this->assertFalse($t->hasForLocale('foo', 'bar'));
$t = $this->getMockBuilder(Translator::class)->setMethods(['load', 'getLine'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); $t = $this->getMockBuilder(Translator::class)
$t->expects($this->any())->method('load')->with($this->equalTo('*'), $this->equalTo('foo'), $this->equalTo('en'))->will($this->returnValue(null)); ->setMethods(['load', 'getLine'])
$t->expects($this->once())->method('getLine')->with($this->equalTo('*'), $this->equalTo('foo'), $this->equalTo('en'), null, $this->equalTo([]))->will($this->returnValue('bar')); ->setConstructorArgs([$this->getLoader(), 'en'])
->getMock();
$t->expects($this->any())
->method('load')
->with($this->equalTo('*'), $this->equalTo('foo'), $this->equalTo('en'))
->will($this->returnValue(null));
$t->expects($this->once())
->method('getLine')
->with($this->equalTo('*'), $this->equalTo('foo'), $this->equalTo('en'), null, $this->equalTo([]))
->will($this->returnValue('bar'));
$this->assertTrue($t->hasForLocale('foo')); $this->assertTrue($t->hasForLocale('foo'));
$t = $this->getMockBuilder(Translator::class)->setMethods(['load', 'getLine'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); $t = $this->getMockBuilder(Translator::class)
$t->expects($this->any())->method('load')->with($this->equalTo('*'), $this->equalTo('foo'), $this->equalTo('en'))->will($this->returnValue(null)); ->setMethods(['load', 'getLine'])
$t->expects($this->once())->method('getLine')->with($this->equalTo('*'), $this->equalTo('foo'), $this->equalTo('en'), null, $this->equalTo([]))->will($this->returnValue('foo')); ->setConstructorArgs([$this->getLoader(), 'en'])
->getMock();
$t->expects($this->any())
->method('load')
->with($this->equalTo('*'), $this->equalTo('foo'), $this->equalTo('en'))
->will($this->returnValue(null));
$t->expects($this->once())
->method('getLine')
->with($this->equalTo('*'), $this->equalTo('foo'), $this->equalTo('en'), null, $this->equalTo([]))
->will($this->returnValue('foo'));
$this->assertFalse($t->hasForLocale('foo')); $this->assertFalse($t->hasForLocale('foo'));
} }
public function testGetMethodProperlyLoadsAndRetrievesItem() public function testGetMethodProperlyLoadsAndRetrievesItem()
{ {
$t = new Translator($this->getLoader(), 'en'); $t = new Translator($this->getLoader(), 'en');
$t->getLoader()->shouldReceive('load')->once()->with('en', 'bar', 'foo')->andReturn(['foo' => 'foo', 'baz' => 'breeze :foo', 'qux' => ['tree :foo', 'breeze :foo']]); $t->getLoader()->shouldReceive('load')->once()->with('en', 'bar', 'foo')->andReturn([
'foo' => 'foo',
'baz' => 'breeze :foo',
'qux' => ['tree :foo', 'breeze :foo'],
]);
$this->assertEquals(['tree bar', 'breeze bar'], $t->get('foo::bar.qux', ['foo' => 'bar'], 'en')); $this->assertEquals(['tree bar', 'breeze bar'], $t->get('foo::bar.qux', ['foo' => 'bar'], 'en'));
$this->assertEquals('breeze bar', $t->get('foo::bar.baz', ['foo' => 'bar'], 'en')); $this->assertEquals('breeze bar', $t->get('foo::bar.baz', ['foo' => 'bar'], 'en'));
$this->assertEquals('foo', $t->get('foo::bar.foo')); $this->assertEquals('foo', $t->get('foo::bar.foo'));
@ -70,14 +117,24 @@ class TranslationTranslatorTest extends AbstractTestCase
public function testTransMethodProperlyLoadsAndRetrievesItemWithHTMLInTheMessage() public function testTransMethodProperlyLoadsAndRetrievesItemWithHTMLInTheMessage()
{ {
$t = new Translator($this->getLoader(), 'en'); $t = new Translator($this->getLoader(), 'en');
$t->getLoader()->shouldReceive('load')->once()->with('en', 'foo', '*')->andReturn(['bar' => 'breeze <p>test</p>']); $t->getLoader()
->shouldReceive('load')
->once()
->with('en', 'foo', '*')
->andReturn(['bar' => 'breeze <p>test</p>']);
$this->assertSame('breeze <p>test</p>', $t->trans('foo.bar', [], 'en')); $this->assertSame('breeze <p>test</p>', $t->trans('foo.bar', [], 'en'));
} }
public function testGetMethodProperlyLoadsAndRetrievesItemWithCapitalization() public function testGetMethodProperlyLoadsAndRetrievesItemWithCapitalization()
{ {
$t = $this->getMockBuilder(Translator::class)->setMethods(null)->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); $t = $this->getMockBuilder(Translator::class)
$t->getLoader()->shouldReceive('load')->once()->with('en', 'bar', 'foo')->andReturn(['foo' => 'foo', 'baz' => 'breeze :Foo :BAR']); ->setMethods(null)
->setConstructorArgs([$this->getLoader(), 'en'])
->getMock();
$t->getLoader()->shouldReceive('load')->once()->with('en', 'bar', 'foo')->andReturn([
'foo' => 'foo',
'baz' => 'breeze :Foo :BAR',
]);
$this->assertEquals('breeze Bar FOO', $t->get('foo::bar.baz', ['foo' => 'bar', 'bar' => 'foo'], 'en')); $this->assertEquals('breeze Bar FOO', $t->get('foo::bar.baz', ['foo' => 'bar', 'bar' => 'foo'], 'en'));
$this->assertEquals('foo', $t->get('foo::bar.foo')); $this->assertEquals('foo', $t->get('foo::bar.foo'));
} }
@ -85,9 +142,15 @@ class TranslationTranslatorTest extends AbstractTestCase
public function testGetMethodProperlyLoadsAndRetrievesItemWithLongestReplacementsFirst() public function testGetMethodProperlyLoadsAndRetrievesItemWithLongestReplacementsFirst()
{ {
$t = new Translator($this->getLoader(), 'en'); $t = new Translator($this->getLoader(), 'en');
$t->getLoader()->shouldReceive('load')->once()->with('en', 'bar', 'foo')->andReturn(['foo' => 'foo', 'baz' => 'breeze :foo :foobar']); $t->getLoader()->shouldReceive('load')->once()->with('en', 'bar', 'foo')->andReturn([
'foo' => 'foo',
'baz' => 'breeze :foo :foobar',
]);
$this->assertEquals('breeze bar taylor', $t->get('foo::bar.baz', ['foo' => 'bar', 'foobar' => 'taylor'], 'en')); $this->assertEquals('breeze bar taylor', $t->get('foo::bar.baz', ['foo' => 'bar', 'foobar' => 'taylor'], 'en'));
$this->assertEquals('breeze foo bar baz taylor', $t->get('foo::bar.baz', ['foo' => 'foo bar baz', 'foobar' => 'taylor'], 'en')); $this->assertEquals('breeze foo bar baz taylor', $t->get('foo::bar.baz', [
'foo' => 'foo bar baz',
'foobar' => 'taylor',
], 'en'));
$this->assertEquals('foo', $t->get('foo::bar.foo')); $this->assertEquals('foo', $t->get('foo::bar.foo'));
} }
@ -96,7 +159,10 @@ class TranslationTranslatorTest extends AbstractTestCase
$t = new Translator($this->getLoader(), 'en'); $t = new Translator($this->getLoader(), 'en');
$t->setFallback('lv'); $t->setFallback('lv');
$t->getLoader()->shouldReceive('load')->once()->with('en', 'bar', 'foo')->andReturn([]); $t->getLoader()->shouldReceive('load')->once()->with('en', 'bar', 'foo')->andReturn([]);
$t->getLoader()->shouldReceive('load')->once()->with('lv', 'bar', 'foo')->andReturn(['foo' => 'foo', 'baz' => 'breeze :foo']); $t->getLoader()->shouldReceive('load')->once()->with('lv', 'bar', 'foo')->andReturn([
'foo' => 'foo',
'baz' => 'breeze :foo',
]);
$this->assertEquals('breeze bar', $t->get('foo::bar.baz', ['foo' => 'bar'], 'en')); $this->assertEquals('breeze bar', $t->get('foo::bar.baz', ['foo' => 'bar'], 'en'));
$this->assertEquals('foo', $t->get('foo::bar.foo')); $this->assertEquals('foo', $t->get('foo::bar.foo'));
} }
@ -110,9 +176,15 @@ class TranslationTranslatorTest extends AbstractTestCase
public function testChoiceMethodProperlyLoadsAndRetrievesItem() public function testChoiceMethodProperlyLoadsAndRetrievesItem()
{ {
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); $t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
$t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo(['replace']), $this->equalTo('en'))->will($this->returnValue('line')); $this->getLoader(),
$t->setSelector($selector = m::mock(MessageSelector::class)); 'en',
])->getMock();
$t->expects($this->once())
->method('get')
->with($this->equalTo('foo'), $this->equalTo(['replace']), $this->equalTo('en'))
->will($this->returnValue('line'));
$t->setSelector($selector = Mockery::mock(MessageSelector::class));
$selector->shouldReceive('choose')->once()->with('line', 10, 'en')->andReturn('choiced'); $selector->shouldReceive('choose')->once()->with('line', 10, 'en')->andReturn('choiced');
$t->choice('foo', 10, ['replace']); $t->choice('foo', 10, ['replace']);
@ -120,9 +192,15 @@ class TranslationTranslatorTest extends AbstractTestCase
public function testChoiceMethodProperlyCountsCollectionsAndLoadsAndRetrievesItem() public function testChoiceMethodProperlyCountsCollectionsAndLoadsAndRetrievesItem()
{ {
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); $t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
$t->expects($this->exactly(2))->method('get')->with($this->equalTo('foo'), $this->equalTo(['replace']), $this->equalTo('en'))->will($this->returnValue('line')); $this->getLoader(),
$t->setSelector($selector = m::mock(MessageSelector::class)); 'en',
])->getMock();
$t->expects($this->exactly(2))
->method('get')
->with($this->equalTo('foo'), $this->equalTo(['replace']), $this->equalTo('en'))
->will($this->returnValue('line'));
$t->setSelector($selector = Mockery::mock(MessageSelector::class));
$selector->shouldReceive('choose')->twice()->with('line', 3, 'en')->andReturn('choiced'); $selector->shouldReceive('choose')->twice()->with('line', 3, 'en')->andReturn('choiced');
$values = ['foo', 'bar', 'baz']; $values = ['foo', 'bar', 'baz'];
@ -142,8 +220,16 @@ class TranslationTranslatorTest extends AbstractTestCase
public function testGetJsonReplaces() public function testGetJsonReplaces()
{ {
$t = new Translator($this->getLoader(), 'en'); $t = new Translator($this->getLoader(), 'en');
$t->getLoader()->shouldReceive('load')->once()->with('en', '*', '*')->andReturn(['foo :i:c :u' => 'bar :i:c :u']); $t->getLoader()
$this->assertEquals('bar onetwo three', $t->getFromJson('foo :i:c :u', ['i' => 'one', 'c' => 'two', 'u' => 'three'])); ->shouldReceive('load')
->once()
->with('en', '*', '*')
->andReturn(['foo :i:c :u' => 'bar :i:c :u']);
$this->assertEquals('bar onetwo three', $t->getFromJson('foo :i:c :u', [
'i' => 'one',
'c' => 'two',
'u' => 'three',
]));
} }
public function testGetJsonReplacesForAssociativeInput() public function testGetJsonReplacesForAssociativeInput()
@ -156,8 +242,15 @@ class TranslationTranslatorTest extends AbstractTestCase
public function testGetJsonPreservesOrder() public function testGetJsonPreservesOrder()
{ {
$t = new Translator($this->getLoader(), 'en'); $t = new Translator($this->getLoader(), 'en');
$t->getLoader()->shouldReceive('load')->once()->with('en', '*', '*')->andReturn(['to :name I give :greeting' => ':greeting :name']); $t->getLoader()
$this->assertEquals('Greetings David', $t->getFromJson('to :name I give :greeting', ['name' => 'David', 'greeting' => 'Greetings'])); ->shouldReceive('load')
->once()
->with('en', '*', '*')
->andReturn(['to :name I give :greeting' => ':greeting :name']);
$this->assertEquals('Greetings David', $t->getFromJson('to :name I give :greeting', [
'name' => 'David',
'greeting' => 'Greetings',
]));
} }
public function testGetJsonForNonExistingJsonKeyLooksForRegularKeys() public function testGetJsonForNonExistingJsonKeyLooksForRegularKeys()
@ -194,6 +287,6 @@ class TranslationTranslatorTest extends AbstractTestCase
protected function getLoader() protected function getLoader()
{ {
return m::mock(Loader::class); return Mockery::mock(TranslatorLoaderInterface::class);
} }
} }

View File

@ -1,8 +0,0 @@
[opcache]
opcache.enable_cli=1
[redis]
extension = "redis.so"
[swoole]
extension = "swoole.so"

View File

@ -1,10 +0,0 @@
#!/usr/bin/env bash
wget https://github.com/swoole/swoole-src/archive/v${SW_VERSION}.tar.gz -O swoole.tar.gz
mkdir -p swoole
tar -xf swoole.tar.gz -C swoole --strip-components=1
rm swoole.tar.gz
cd swoole
phpize
./configure --enable-openssl --enable-mysqlnd
make -j$(nproc)
make install

View File

@ -10,27 +10,21 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace Hyperf\Validation\Contracts\Support; namespace Hyperf\Utils\Contracts;
use Hyperf\Utils\Contracts\Arrayable; use Hyperf\Validation\Contracts\Support\MessageProvider;
interface MessageBag extends Arrayable interface MessageBag
{ {
/** /**
* Get the keys present in the message bag. * Get the keys present in the message bag.
*
* @return array
*/ */
public function keys(): array; public function keys(): array;
/** /**
* Add a message to the bag. * Add a message to the bag.
*
* @param string $key
* @param string $message
* @return $this
*/ */
public function add(string $key, string $message); public function add(string $key, string $message): MessageBag;
/** /**
* Merge a new array of messages into the bag. * Merge a new array of messages into the bag.
@ -44,7 +38,6 @@ interface MessageBag extends Arrayable
* Determine if messages exist for a given key. * Determine if messages exist for a given key.
* *
* @param array|string $key * @param array|string $key
* @return bool
*/ */
public function has($key): bool; public function has($key): bool;
@ -53,67 +46,48 @@ interface MessageBag extends Arrayable
* *
* @param null|string $key * @param null|string $key
* @param null|string $format * @param null|string $format
* @return string
*/ */
public function first($key = null, $format = null): string; public function first(?string $key = null, ?string $format = null): string;
/** /**
* Get all of the messages from the bag for a given key. * Get all of the messages from the bag for a given key.
*
* @param string $key
* @param null|string $format
* @return array
*/ */
public function get(string $key, $format = null): array; public function get(string $key, ?string $format = null): array;
/** /**
* Get all of the messages for every key in the bag. * Get all of the messages for every key in the bag.
*
* @param null|string $format
* @return array
*/ */
public function all($format = null): array; public function all(?string $format = null): array;
/** /**
* Get the raw messages in the container. * Get the raw messages in the container.
*
* @return array
*/ */
public function getMessages(): array; public function getMessages(): array;
/** /**
* Get the default message format. * Get the default message format.
*
* @return string
*/ */
public function getFormat(): string; public function getFormat(): string;
/** /**
* Set the default message format. * Set the default message format.
* *
* @param string $format
* @return $this * @return $this
*/ */
public function setFormat(string $format = ':message'); public function setFormat(string $format = ':message');
/** /**
* Determine if the message bag has any messages. * Determine if the message bag has any messages.
*
* @return bool
*/ */
public function isEmpty(): bool; public function isEmpty(): bool;
/** /**
* Determine if the message bag has any messages. * Determine if the message bag has any messages.
*
* @return bool
*/ */
public function isNotEmpty(): bool; public function isNotEmpty(): bool;
/** /**
* Get the number of messages in the container. * Get the number of messages in the container.
*
* @return int
*/ */
public function count(): int; public function count(): int;
} }

View File

@ -10,14 +10,12 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace Hyperf\Validation\Contracts\Support; namespace Hyperf\Utils\Contracts;
interface MessageProvider interface MessageProvider
{ {
/** /**
* Get the messages for the instance. * Get the messages for the instance.
*
* @return MessageBag
*/ */
public function getMessageBag(); public function getMessageBag(): MessageBag;
} }

View File

@ -43,10 +43,17 @@ class Coroutine
/** /**
* Returns the parent coroutine ID. * Returns the parent coroutine ID.
* Returns -1 when running in non-coroutine context. * Returns -1 when running in non-coroutine context.
*
* @see https://github.com/swoole/swoole-src/pull/2669/files#diff-3bdf726b0ac53be7e274b60d59e6ec80R940
*/ */
public static function parentId(): int public static function parentId(): int
{ {
return SwooleCoroutine::getPcid(); $cid = SwooleCoroutine::getPcid();
if ($cid === false) {
return -1;
}
return $cid;
} }
/** /**

View File

@ -10,15 +10,13 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE * @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/ */
namespace Hyperf\Validation\Support; namespace Hyperf\Utils;
use Countable; use Countable;
use Hyperf\Utils\Arr;
use Hyperf\Utils\Contracts\Arrayable; use Hyperf\Utils\Contracts\Arrayable;
use Hyperf\Utils\Contracts\Jsonable; use Hyperf\Utils\Contracts\Jsonable;
use Hyperf\Utils\Str; use Hyperf\Utils\Contracts\MessageBag as MessageBagContract;
use Hyperf\Validation\Contracts\Support\MessageBag as MessageBagContract; use Hyperf\Utils\Contracts\MessageProvider;
use Hyperf\Validation\Contracts\Support\MessageProvider;
use JsonSerializable; use JsonSerializable;
class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, MessageBagContract, MessageProvider class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, MessageBagContract, MessageProvider
@ -39,8 +37,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Create a new message bag instance. * Create a new message bag instance.
*
* @param array $messages
*/ */
public function __construct(array $messages = []) public function __construct(array $messages = [])
{ {
@ -53,8 +49,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Convert the message bag to its string representation. * Convert the message bag to its string representation.
*
* @return string
*/ */
public function __toString(): string public function __toString(): string
{ {
@ -63,8 +57,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Get the keys present in the message bag. * Get the keys present in the message bag.
*
* @return array
*/ */
public function keys(): array public function keys(): array
{ {
@ -73,12 +65,8 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Add a message to the message bag. * Add a message to the message bag.
*
* @param string $key
* @param string $message
* @return $this
*/ */
public function add(string $key, string $message) public function add(string $key, string $message): MessageBagContract
{ {
if ($this->isUnique($key, $message)) { if ($this->isUnique($key, $message)) {
$this->messages[$key][] = $message; $this->messages[$key][] = $message;
@ -108,7 +96,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
* Determine if messages exist for all of the given keys. * Determine if messages exist for all of the given keys.
* *
* @param array|string $key * @param array|string $key
* @return bool
*/ */
public function has($key): bool public function has($key): bool
{ {
@ -135,7 +122,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
* Determine if messages exist for any of the given keys. * Determine if messages exist for any of the given keys.
* *
* @param array|string $keys * @param array|string $keys
* @return bool
*/ */
public function hasAny($keys = []): bool public function hasAny($keys = []): bool
{ {
@ -159,7 +145,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
* *
* @param string $key * @param string $key
* @param string $format * @param string $format
* @return string
*/ */
public function first($key = null, $format = null): string public function first($key = null, $format = null): string
{ {
@ -172,12 +157,8 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Get all of the messages from the message bag for a given key. * Get all of the messages from the message bag for a given key.
*
* @param string $key
* @param string $format
* @return array
*/ */
public function get(string $key, $format = null): array public function get(string $key, ?string $format = null): array
{ {
// If the message exists in the message bag, we will transform it and return // If the message exists in the message bag, we will transform it and return
// the message. Otherwise, we will check if the key is implicit & collect // the message. Otherwise, we will check if the key is implicit & collect
@ -199,11 +180,8 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Get all of the messages for every key in the message bag. * Get all of the messages for every key in the message bag.
*
* @param string $format
* @return array
*/ */
public function all($format = null): array public function all(?string $format = null): array
{ {
$format = $this->checkFormat($format); $format = $this->checkFormat($format);
@ -218,19 +196,14 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Get all of the unique messages for every key in the message bag. * Get all of the unique messages for every key in the message bag.
*
* @param string $format
* @return array
*/ */
public function unique($format = null): array public function unique(?string $format = null): array
{ {
return array_unique($this->all($format)); return array_unique($this->all($format));
} }
/** /**
* Get the raw messages in the message bag. * Get the raw messages in the message bag.
*
* @return array
*/ */
public function messages(): array public function messages(): array
{ {
@ -239,8 +212,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Get the raw messages in the message bag. * Get the raw messages in the message bag.
*
* @return array
*/ */
public function getMessages(): array public function getMessages(): array
{ {
@ -249,18 +220,14 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Get the messages for the instance. * Get the messages for the instance.
*
* @return MessageBag
*/ */
public function getMessageBag() public function getMessageBag(): MessageBagContract
{ {
return $this; return $this;
} }
/** /**
* Get the default message format. * Get the default message format.
*
* @return string
*/ */
public function getFormat(): string public function getFormat(): string
{ {
@ -269,11 +236,8 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Set the default message format. * Set the default message format.
*
* @param string $format
* @return MessageBag
*/ */
public function setFormat(string $format = ':message') public function setFormat(string $format = ':message'): self
{ {
$this->format = $format; $this->format = $format;
@ -282,8 +246,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Determine if the message bag has any messages. * Determine if the message bag has any messages.
*
* @return bool
*/ */
public function isEmpty(): bool public function isEmpty(): bool
{ {
@ -292,8 +254,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Determine if the message bag has any messages. * Determine if the message bag has any messages.
*
* @return bool
*/ */
public function isNotEmpty(): bool public function isNotEmpty(): bool
{ {
@ -302,8 +262,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Determine if the message bag has any messages. * Determine if the message bag has any messages.
*
* @return bool
*/ */
public function any(): bool public function any(): bool
{ {
@ -312,8 +270,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Get the number of messages in the message bag. * Get the number of messages in the message bag.
*
* @return int
*/ */
public function count(): int public function count(): int
{ {
@ -322,8 +278,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Get the instance as an array. * Get the instance as an array.
*
* @return array
*/ */
public function toArray(): array public function toArray(): array
{ {
@ -332,8 +286,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Convert the object into something JSON serializable. * Convert the object into something JSON serializable.
*
* @return array
*/ */
public function jsonSerialize(): array public function jsonSerialize(): array
{ {
@ -342,9 +294,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Convert the object to its JSON representation. * Convert the object to its JSON representation.
*
* @param int $options
* @return string
*/ */
public function toJson(int $options = 0): string public function toJson(int $options = 0): string
{ {
@ -353,10 +302,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Determine if a key and message combination already exists. * Determine if a key and message combination already exists.
*
* @param string $key
* @param string $message
* @return bool
*/ */
protected function isUnique(string $key, string $message): bool protected function isUnique(string $key, string $message): bool
{ {
@ -367,12 +312,8 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Get the messages for a wildcard key. * Get the messages for a wildcard key.
*
* @param string $key
* @param null|string $format
* @return array
*/ */
protected function getMessagesForWildcardKey(string $key, $format): array protected function getMessagesForWildcardKey(string $key, ?string $format): array
{ {
return collect($this->messages) return collect($this->messages)
->filter(function ($messages, $messageKey) use ($key) { ->filter(function ($messages, $messageKey) use ($key) {
@ -389,11 +330,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Format an array of messages. * Format an array of messages.
*
* @param array $messages
* @param string $format
* @param string $messageKey
* @return array
*/ */
protected function transform(array $messages, string $format, string $messageKey): array protected function transform(array $messages, string $format, string $messageKey): array
{ {
@ -408,11 +344,8 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
/** /**
* Get the appropriate format based on the given format. * Get the appropriate format based on the given format.
*
* @param string $format
* @return string
*/ */
protected function checkFormat($format) protected function checkFormat(?string $format): string
{ {
return $format ?: $this->format; return $format ?: $this->format;
} }

View File

@ -3,7 +3,7 @@
## About ## About
hyperf/validation 是对Laravel Validation的移植不包含门面部分具体使用方法可以参考Laravel Validation 的使用 [hyperf/validation](https://github.com/hyperf-cloud/validation) 组件衍生于 `Laravel Validation` 组件的,我们对它进行了一些改造,大部分功能保持了相同。在这里感谢一下 Laravel 开发组,实现了如此强大好用的 Validation 组件
## Install ## Install
@ -17,7 +17,7 @@ composer require hyperf/validation
### publish config ### publish config
``` ```
php bin/hyperf.php vendor:publish hyperf/translation php bin/hyperf.php vendor:publish hyperf/validation
``` ```

View File

@ -20,19 +20,18 @@
"require": { "require": {
"php": ">=7.2", "php": ">=7.2",
"ext-swoole": ">=4.3", "ext-swoole": ">=4.3",
"hyperf/translation": "^1.0",
"egulias/email-validator": "^2.1", "egulias/email-validator": "^2.1",
"hyperf/command": "^1.0", "hyperf/command": "~1.0.0",
"hyperf/database": "^1.0", "hyperf/database": "~1.0.0",
"hyperf/devtool": "^1.0", "hyperf/devtool": "~1.0.0",
"hyperf/di": "1.0.*", "hyperf/di": "~1.0.0",
"hyperf/framework": "1.0.*", "hyperf/framework": "~1.0.0",
"hyperf/http-server": "~1.0.0", "hyperf/http-server": "~1.0.0",
"hyperf/utils": "1.0.*", "hyperf/utils": "~1.0.0",
"hyperf/translation": "~1.0.0",
"nesbot/carbon": "^2.21", "nesbot/carbon": "^2.21",
"psr/container": "^1.0", "psr/container": "^1.0",
"psr/http-message": "^1.0", "psr/http-message": "^1.0"
"symfony/http-foundation": "^4.3"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^2.14", "friendsofphp/php-cs-fixer": "^2.14",

View File

@ -69,10 +69,8 @@ class ClosureValidationRule implements RuleContract
/** /**
* Get the validation error message. * Get the validation error message.
*
* @return string
*/ */
public function message() public function message(): string
{ {
return $this->message; return $this->message;
} }

View File

@ -13,9 +13,10 @@ declare(strict_types=1);
namespace Hyperf\Validation\Concerns; namespace Hyperf\Validation\Concerns;
use Closure; use Closure;
use Hyperf\HttpMessage\Upload\UploadedFile;
use Hyperf\Utils\Arr; use Hyperf\Utils\Arr;
use Hyperf\Utils\Str; use Hyperf\Utils\Str;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Hyperf\Validation\Validator;
trait FormatsMessages trait FormatsMessages
{ {
@ -23,12 +24,6 @@ trait FormatsMessages
/** /**
* Replace all error message place-holders with actual values. * Replace all error message place-holders with actual values.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
public function makeReplacements(string $message, string $attribute, string $rule, array $parameters): string public function makeReplacements(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -51,9 +46,6 @@ trait FormatsMessages
/** /**
* Get the displayable name of the attribute. * Get the displayable name of the attribute.
*
* @param string $attribute
* @return string
*/ */
public function getDisplayableAttribute(string $attribute): string public function getDisplayableAttribute(string $attribute): string
{ {
@ -112,10 +104,6 @@ trait FormatsMessages
/** /**
* Get the validation message for an attribute and rule. * Get the validation message for an attribute and rule.
*
* @param string $attribute
* @param string $rule
* @return string
*/ */
protected function getMessage(string $attribute, string $rule): string protected function getMessage(string $attribute, string $rule): string
{ {
@ -167,8 +155,6 @@ trait FormatsMessages
/** /**
* Get the proper inline error message for standard and size rules. * Get the proper inline error message for standard and size rules.
* *
* @param string $attribute
* @param string $rule
* @return null|string * @return null|string
*/ */
protected function getInlineMessage(string $attribute, string $rule) protected function getInlineMessage(string $attribute, string $rule)
@ -183,8 +169,6 @@ trait FormatsMessages
/** /**
* Get the inline message for a rule if it exists. * Get the inline message for a rule if it exists.
* *
* @param string $attribute
* @param string $lowerRule
* @param null|array $source * @param null|array $source
* @return null|string * @return null|string
*/ */
@ -208,9 +192,6 @@ trait FormatsMessages
/** /**
* Get the custom error message from translator. * Get the custom error message from translator.
*
* @param string $key
* @return string
*/ */
protected function getCustomMessageFromTranslator(string $key): string protected function getCustomMessageFromTranslator(string $key): string
{ {
@ -234,11 +215,6 @@ trait FormatsMessages
/** /**
* Check the given messages for a wildcard key. * Check the given messages for a wildcard key.
*
* @param array $messages
* @param string $search
* @param string $default
* @return string
*/ */
protected function getWildcardCustomMessages(array $messages, string $search, string $default): string protected function getWildcardCustomMessages(array $messages, string $search, string $default): string
{ {
@ -253,10 +229,6 @@ trait FormatsMessages
/** /**
* Get the proper error message for an attribute and size rule. * Get the proper error message for an attribute and size rule.
*
* @param string $attribute
* @param string $rule
* @return string
*/ */
protected function getSizeMessage(string $attribute, string $rule): string protected function getSizeMessage(string $attribute, string $rule): string
{ {
@ -274,9 +246,6 @@ trait FormatsMessages
/** /**
* Get the data type of the given attribute. * Get the data type of the given attribute.
*
* @param string $attribute
* @return string
*/ */
protected function getAttributeType(string $attribute): string protected function getAttributeType(string $attribute): string
{ {
@ -298,9 +267,6 @@ trait FormatsMessages
/** /**
* Get the given attribute from the attribute translations. * Get the given attribute from the attribute translations.
*
* @param string $name
* @return string
*/ */
protected function getAttributeFromTranslations(string $name): string protected function getAttributeFromTranslations(string $name): string
{ {
@ -309,10 +275,6 @@ trait FormatsMessages
/** /**
* Replace the :attribute placeholder in the given message. * Replace the :attribute placeholder in the given message.
*
* @param string $message
* @param string $value
* @return string
*/ */
protected function replaceAttributePlaceholder(string $message, string $value): string protected function replaceAttributePlaceholder(string $message, string $value): string
{ {
@ -325,10 +287,6 @@ trait FormatsMessages
/** /**
* Replace the :input placeholder in the given message. * Replace the :input placeholder in the given message.
*
* @param string $message
* @param string $attribute
* @return string
*/ */
protected function replaceInputPlaceholder(string $message, string $attribute): string protected function replaceInputPlaceholder(string $message, string $attribute): string
{ {
@ -343,9 +301,6 @@ trait FormatsMessages
/** /**
* Transform an array of attributes to their displayable form. * Transform an array of attributes to their displayable form.
*
* @param array $values
* @return array
*/ */
protected function getAttributeList(array $values): array protected function getAttributeList(array $values): array
{ {
@ -363,15 +318,8 @@ trait FormatsMessages
/** /**
* Call a custom validator message replacer. * Call a custom validator message replacer.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @param \Hyperf\Validation\Validator $validator
* @return null|string
*/ */
protected function callReplacer(string $message, string $attribute, string $rule, array $parameters, $validator) protected function callReplacer(string $message, string $attribute, string $rule, array $parameters, Validator $validator): ?string
{ {
$callback = $this->replacers[$rule]; $callback = $this->replacers[$rule];
@ -386,18 +334,12 @@ trait FormatsMessages
/** /**
* Call a class based validator message replacer. * Call a class based validator message replacer.
* *
* @param string $callback
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @param \Hyperf\Validation\Validator $validator * @param \Hyperf\Validation\Validator $validator
* @return string
*/ */
protected function callClassBasedReplacer(string $callback, string $message, string $attribute, string $rule, array $parameters, $validator): string protected function callClassBasedReplacer(string $callback, string $message, string $attribute, string $rule, array $parameters, $validator): string
{ {
[$class, $method] = Str::parseCallback($callback, 'replace'); [$class, $method] = Str::parseCallback($callback, 'replace');
return call_user_func_array([$this->container->make($class), $method], array_slice(func_get_args(), 1)); return call_user_func_array([make($class), $method], array_slice(func_get_args(), 1));
} }
} }

View File

@ -18,12 +18,6 @@ trait ReplacesAttributes
{ {
/** /**
* Replace all place-holders for the between rule. * Replace all place-holders for the between rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceBetween(string $message, string $attribute, string $rule, array $parameters): string protected function replaceBetween(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -32,12 +26,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the date_format rule. * Replace all place-holders for the date_format rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceDateFormat(string $message, string $attribute, string $rule, array $parameters): string protected function replaceDateFormat(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -46,12 +34,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the different rule. * Replace all place-holders for the different rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceDifferent(string $message, string $attribute, string $rule, array $parameters): string protected function replaceDifferent(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -60,12 +42,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the digits rule. * Replace all place-holders for the digits rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceDigits(string $message, string $attribute, string $rule, array $parameters): string protected function replaceDigits(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -74,12 +50,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the digits (between) rule. * Replace all place-holders for the digits (between) rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceDigitsBetween(string $message, string $attribute, string $rule, array $parameters): string protected function replaceDigitsBetween(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -88,12 +58,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the min rule. * Replace all place-holders for the min rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceMin(string $message, string $attribute, string $rule, array $parameters): string protected function replaceMin(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -102,26 +66,14 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the max rule. * Replace all place-holders for the max rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceMax(string $message, string $attribute, string $rule, array $parameters) protected function replaceMax(string $message, string $attribute, string $rule, array $parameters): string
{ {
return str_replace(':max', $parameters[0], $message); return str_replace(':max', $parameters[0], $message);
} }
/** /**
* Replace all place-holders for the in rule. * Replace all place-holders for the in rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceIn(string $message, string $attribute, string $rule, array $parameters): string protected function replaceIn(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -134,12 +86,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the not_in rule. * Replace all place-holders for the not_in rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceNotIn(string $message, string $attribute, string $rule, array $parameters): string protected function replaceNotIn(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -148,12 +94,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the in_array rule. * Replace all place-holders for the in_array rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceInArray(string $message, string $attribute, string $rule, array $parameters): string protected function replaceInArray(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -162,12 +102,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the mimetypes rule. * Replace all place-holders for the mimetypes rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceMimetypes(string $message, string $attribute, string $rule, array $parameters): string protected function replaceMimetypes(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -176,12 +110,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the mimes rule. * Replace all place-holders for the mimes rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceMimes(string $message, string $attribute, string $rule, array $parameters): string protected function replaceMimes(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -190,12 +118,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the required_with rule. * Replace all place-holders for the required_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceRequiredWith(string $message, string $attribute, string $rule, array $parameters): string protected function replaceRequiredWith(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -204,12 +126,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the required_with_all rule. * Replace all place-holders for the required_with_all rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceRequiredWithAll(string $message, string $attribute, string $rule, array $parameters): string protected function replaceRequiredWithAll(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -218,12 +134,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the required_without rule. * Replace all place-holders for the required_without rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceRequiredWithout(string $message, string $attribute, string $rule, array $parameters): string protected function replaceRequiredWithout(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -232,12 +142,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the required_without_all rule. * Replace all place-holders for the required_without_all rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceRequiredWithoutAll(string $message, string $attribute, string $rule, array $parameters): string protected function replaceRequiredWithoutAll(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -246,12 +150,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the size rule. * Replace all place-holders for the size rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceSize(string $message, string $attribute, string $rule, array $parameters): string protected function replaceSize(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -260,12 +158,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the gt rule. * Replace all place-holders for the gt rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceGt(string $message, string $attribute, string $rule, array $parameters): string protected function replaceGt(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -278,12 +170,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the lt rule. * Replace all place-holders for the lt rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceLt(string $message, string $attribute, string $rule, array $parameters): string protected function replaceLt(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -296,12 +182,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the gte rule. * Replace all place-holders for the gte rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceGte(string $message, string $attribute, string $rule, array $parameters): string protected function replaceGte(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -314,12 +194,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the lte rule. * Replace all place-holders for the lte rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceLte(string $message, string $attribute, string $rule, array $parameters): string protected function replaceLte(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -332,12 +206,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the required_if rule. * Replace all place-holders for the required_if rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceRequiredIf(string $message, string $attribute, string $rule, array $parameters): string protected function replaceRequiredIf(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -350,12 +218,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the required_unless rule. * Replace all place-holders for the required_unless rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceRequiredUnless(string $message, string $attribute, string $rule, array $parameters): string protected function replaceRequiredUnless(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -372,12 +234,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the same rule. * Replace all place-holders for the same rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceSame(string $message, string $attribute, string $rule, array $parameters): string protected function replaceSame(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -386,12 +242,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the before rule. * Replace all place-holders for the before rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceBefore(string $message, string $attribute, string $rule, array $parameters): string protected function replaceBefore(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -404,12 +254,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the before_or_equal rule. * Replace all place-holders for the before_or_equal rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceBeforeOrEqual(string $message, string $attribute, string $rule, array $parameters): string protected function replaceBeforeOrEqual(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -418,12 +262,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the after rule. * Replace all place-holders for the after rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceAfter(string $message, string $attribute, string $rule, array $parameters): string protected function replaceAfter(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -432,12 +270,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the after_or_equal rule. * Replace all place-holders for the after_or_equal rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceAfterOrEqual(string $message, string $attribute, string $rule, array $parameters): string protected function replaceAfterOrEqual(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -446,12 +278,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the date_equals rule. * Replace all place-holders for the date_equals rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceDateEquals(string $message, string $attribute, string $rule, array $parameters): string protected function replaceDateEquals(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -460,12 +286,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the dimensions rule. * Replace all place-holders for the dimensions rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceDimensions(string $message, string $attribute, string $rule, array $parameters): string protected function replaceDimensions(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -482,12 +302,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the ends_with rule. * Replace all place-holders for the ends_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceEndsWith(string $message, string $attribute, string $rule, array $parameters): string protected function replaceEndsWith(string $message, string $attribute, string $rule, array $parameters): string
{ {
@ -500,12 +314,6 @@ trait ReplacesAttributes
/** /**
* Replace all place-holders for the starts_with rule. * Replace all place-holders for the starts_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/ */
protected function replaceStartsWith(string $message, string $attribute, string $rule, array $parameters): string protected function replaceStartsWith(string $message, string $attribute, string $rule, array $parameters): string
{ {

View File

@ -21,14 +21,14 @@ use DateTimeZone;
use Egulias\EmailValidator\EmailValidator; use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation; use Egulias\EmailValidator\Validation\RFCValidation;
use Exception; use Exception;
use Hyperf\HttpMessage\Upload\UploadedFile;
use Hyperf\Utils\Arr; use Hyperf\Utils\Arr;
use Hyperf\Utils\Str; use Hyperf\Utils\Str;
use Hyperf\Validation\Rules\Exists; use Hyperf\Validation\Rules\Exists;
use Hyperf\Validation\Rules\Unique; use Hyperf\Validation\Rules\Unique;
use Hyperf\Validation\ValidationData; use Hyperf\Validation\ValidationData;
use InvalidArgumentException; use InvalidArgumentException;
use Symfony\Component\HttpFoundation\File\File; use SplFileInfo;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Throwable; use Throwable;
trait ValidatesAttributes trait ValidatesAttributes
@ -38,9 +38,7 @@ trait ValidatesAttributes
* *
* This validation rule implies the attribute is "required". * This validation rule implies the attribute is "required".
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateAccepted(string $attribute, $value): bool public function validateAccepted(string $attribute, $value): bool
{ {
@ -52,9 +50,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is an active URL. * Validate that an attribute is an active URL.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateActiveUrl(string $attribute, $value): bool public function validateActiveUrl(string $attribute, $value): bool
{ {
@ -77,8 +73,6 @@ trait ValidatesAttributes
* "Break" on first validation fail. * "Break" on first validation fail.
* *
* Always returns true, just lets us put "bail" in rules. * Always returns true, just lets us put "bail" in rules.
*
* @return bool
*/ */
public function validateBail(): bool public function validateBail(): bool
{ {
@ -88,10 +82,7 @@ trait ValidatesAttributes
/** /**
* Validate the date is before a given date. * Validate the date is before a given date.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateBefore(string $attribute, $value, array $parameters): bool public function validateBefore(string $attribute, $value, array $parameters): bool
{ {
@ -103,10 +94,7 @@ trait ValidatesAttributes
/** /**
* Validate the date is before or equal a given date. * Validate the date is before or equal a given date.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateBeforeOrEqual(string $attribute, $value, array $parameters): bool public function validateBeforeOrEqual(string $attribute, $value, array $parameters): bool
{ {
@ -118,10 +106,7 @@ trait ValidatesAttributes
/** /**
* Validate the date is after a given date. * Validate the date is after a given date.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateAfter(string $attribute, $value, array $parameters): bool public function validateAfter(string $attribute, $value, array $parameters): bool
{ {
@ -133,10 +118,7 @@ trait ValidatesAttributes
/** /**
* Validate the date is equal or after a given date. * Validate the date is equal or after a given date.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateAfterOrEqual(string $attribute, $value, array $parameters): bool public function validateAfterOrEqual(string $attribute, $value, array $parameters): bool
{ {
@ -148,9 +130,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute contains only alphabetic characters. * Validate that an attribute contains only alphabetic characters.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateAlpha(string $attribute, $value): bool public function validateAlpha(string $attribute, $value): bool
{ {
@ -160,9 +140,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute contains only alpha-numeric characters, dashes, and underscores. * Validate that an attribute contains only alpha-numeric characters, dashes, and underscores.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateAlphaDash(string $attribute, $value): bool public function validateAlphaDash(string $attribute, $value): bool
{ {
@ -176,9 +154,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute contains only alpha-numeric characters. * Validate that an attribute contains only alpha-numeric characters.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateAlphaNum(string $attribute, $value): bool public function validateAlphaNum(string $attribute, $value): bool
{ {
@ -192,9 +168,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is an array. * Validate that an attribute is an array.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateArray(string $attribute, $value): bool public function validateArray(string $attribute, $value): bool
{ {
@ -204,10 +178,7 @@ trait ValidatesAttributes
/** /**
* Validate the size of an attribute is between a set of values. * Validate the size of an attribute is between a set of values.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateBetween(string $attribute, $value, array $parameters): bool public function validateBetween(string $attribute, $value, array $parameters): bool
{ {
@ -221,9 +192,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is a boolean. * Validate that an attribute is a boolean.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateBoolean(string $attribute, $value): bool public function validateBoolean(string $attribute, $value): bool
{ {
@ -235,9 +204,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute has a matching confirmation. * Validate that an attribute has a matching confirmation.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateConfirmed(string $attribute, $value): bool public function validateConfirmed(string $attribute, $value): bool
{ {
@ -247,9 +214,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is a valid date. * Validate that an attribute is a valid date.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateDate(string $attribute, $value): bool public function validateDate(string $attribute, $value): bool
{ {
@ -276,10 +241,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute matches a date format. * Validate that an attribute matches a date format.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateDateFormat(string $attribute, $value, array $parameters): bool public function validateDateFormat(string $attribute, $value, array $parameters): bool
{ {
@ -299,10 +261,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is equal to another date. * Validate that an attribute is equal to another date.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateDateEquals(string $attribute, $value, array $parameters): bool public function validateDateEquals(string $attribute, $value, array $parameters): bool
{ {
@ -314,10 +273,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is different from another attribute. * Validate that an attribute is different from another attribute.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateDifferent(string $attribute, $value, array $parameters): bool public function validateDifferent(string $attribute, $value, array $parameters): bool
{ {
@ -341,10 +297,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute has a given number of digits. * Validate that an attribute has a given number of digits.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateDigits(string $attribute, $value, array $parameters): bool public function validateDigits(string $attribute, $value, array $parameters): bool
{ {
@ -357,10 +310,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is between a given number of digits. * Validate that an attribute is between a given number of digits.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateDigitsBetween(string $attribute, $value, array $parameters): bool public function validateDigitsBetween(string $attribute, $value, array $parameters): bool
{ {
@ -375,17 +325,10 @@ trait ValidatesAttributes
/** /**
* Validate the dimensions of an image matches the given values. * Validate the dimensions of an image matches the given values.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateDimensions(string $attribute, $value, array $parameters): bool public function validateDimensions(string $attribute, $value, array $parameters): bool
{ {
if ($this->isValidFileInstance($value) && $value->getClientMimeType() === 'image/svg+xml') {
return true;
}
if (! $this->isValidFileInstance($value) || ! $sizeDetails = @getimagesize($value->getRealPath())) { if (! $this->isValidFileInstance($value) || ! $sizeDetails = @getimagesize($value->getRealPath())) {
return false; return false;
} }
@ -407,10 +350,7 @@ trait ValidatesAttributes
/** /**
* Validate an attribute is unique among other values. * Validate an attribute is unique among other values.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateDistinct(string $attribute, $value, array $parameters): bool public function validateDistinct(string $attribute, $value, array $parameters): bool
{ {
@ -426,9 +366,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is a valid e-mail address. * Validate that an attribute is a valid e-mail address.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateEmail(string $attribute, $value): bool public function validateEmail(string $attribute, $value): bool
{ {
@ -442,10 +380,7 @@ trait ValidatesAttributes
/** /**
* Validate the existence of an attribute value in a database table. * Validate the existence of an attribute value in a database table.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateExists(string $attribute, $value, array $parameters): bool public function validateExists(string $attribute, $value, array $parameters): bool
{ {
@ -466,7 +401,7 @@ trait ValidatesAttributes
$column, $column,
$value, $value,
$parameters $parameters
) >= $expected; ) >= $expected;
} }
/** /**
@ -474,10 +409,7 @@ trait ValidatesAttributes
* *
* If a database column is not specified, the attribute will be used. * If a database column is not specified, the attribute will be used.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateUnique(string $attribute, $value, array $parameters): bool public function validateUnique(string $attribute, $value, array $parameters): bool
{ {
@ -518,28 +450,21 @@ trait ValidatesAttributes
$id, $id,
$idColumn, $idColumn,
$extra $extra
) == 0; ) == 0;
} }
/** /**
* Parse the connection / table for the unique / exists rules. * Parse the connection / table for the unique / exists rules.
*
* @param string $table
* @return array
*/ */
public function parseTable($table): array public function parseTable(string $table): array
{ {
return Str::contains($table, '.') ? explode('.', $table, 2) : [null, $table]; return Str::contains($table, '.') ? explode('.', $table, 2) : [null, $table];
} }
/** /**
* Get the column name for an exists / unique query. * Get the column name for an exists / unique query.
*
* @param array $parameters
* @param string $attribute
* @return bool
*/ */
public function getQueryColumn($parameters, $attribute): string public function getQueryColumn(array $parameters, string $attribute): string
{ {
return isset($parameters[1]) && $parameters[1] !== 'NULL' return isset($parameters[1]) && $parameters[1] !== 'NULL'
? $parameters[1] : $this->guessColumnForQuery($attribute); ? $parameters[1] : $this->guessColumnForQuery($attribute);
@ -547,9 +472,6 @@ trait ValidatesAttributes
/** /**
* Guess the database column from the given attribute name. * Guess the database column from the given attribute name.
*
* @param string $attribute
* @return string
*/ */
public function guessColumnForQuery(string $attribute): string public function guessColumnForQuery(string $attribute): string
{ {
@ -564,9 +486,7 @@ trait ValidatesAttributes
/** /**
* Validate the given value is a valid file. * Validate the given value is a valid file.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateFile(string $attribute, $value): bool public function validateFile(string $attribute, $value): bool
{ {
@ -576,9 +496,7 @@ trait ValidatesAttributes
/** /**
* Validate the given attribute is filled if it is present. * Validate the given attribute is filled if it is present.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateFilled(string $attribute, $value): bool public function validateFilled(string $attribute, $value): bool
{ {
@ -592,10 +510,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is greater than another attribute. * Validate that an attribute is greater than another attribute.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateGt(string $attribute, $value, array $parameters): bool public function validateGt(string $attribute, $value, array $parameters): bool
{ {
@ -619,10 +534,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is less than another attribute. * Validate that an attribute is less than another attribute.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateLt(string $attribute, $value, array $parameters): bool public function validateLt(string $attribute, $value, array $parameters): bool
{ {
@ -646,10 +558,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is greater than or equal another attribute. * Validate that an attribute is greater than or equal another attribute.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateGte(string $attribute, $value, array $parameters): bool public function validateGte(string $attribute, $value, array $parameters): bool
{ {
@ -673,10 +582,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is less than or equal another attribute. * Validate that an attribute is less than or equal another attribute.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateLte(string $attribute, $value, array $parameters): bool public function validateLte(string $attribute, $value, array $parameters): bool
{ {
@ -700,9 +606,7 @@ trait ValidatesAttributes
/** /**
* Validate the MIME type of a file is an image MIME type. * Validate the MIME type of a file is an image MIME type.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateImage(string $attribute, $value): bool public function validateImage(string $attribute, $value): bool
{ {
@ -712,10 +616,7 @@ trait ValidatesAttributes
/** /**
* Validate an attribute is contained within a list of values. * Validate an attribute is contained within a list of values.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateIn(string $attribute, $value, array $parameters): bool public function validateIn(string $attribute, $value, array $parameters): bool
{ {
@ -735,10 +636,7 @@ trait ValidatesAttributes
/** /**
* Validate that the values of an attribute is in another attribute. * Validate that the values of an attribute is in another attribute.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateInArray(string $attribute, $value, array $parameters): bool public function validateInArray(string $attribute, $value, array $parameters): bool
{ {
@ -758,9 +656,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is an integer. * Validate that an attribute is an integer.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateInteger(string $attribute, $value): bool public function validateInteger(string $attribute, $value): bool
{ {
@ -770,9 +666,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is a valid IP. * Validate that an attribute is a valid IP.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateIp(string $attribute, $value): bool public function validateIp(string $attribute, $value): bool
{ {
@ -782,9 +676,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is a valid IPv4. * Validate that an attribute is a valid IPv4.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateIpv4(string $attribute, $value): bool public function validateIpv4(string $attribute, $value): bool
{ {
@ -794,9 +686,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is a valid IPv6. * Validate that an attribute is a valid IPv6.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateIpv6(string $attribute, $value): bool public function validateIpv6(string $attribute, $value): bool
{ {
@ -806,9 +696,7 @@ trait ValidatesAttributes
/** /**
* Validate the attribute is a valid JSON string. * Validate the attribute is a valid JSON string.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateJson(string $attribute, $value): bool public function validateJson(string $attribute, $value): bool
{ {
@ -824,10 +712,7 @@ trait ValidatesAttributes
/** /**
* Validate the size of an attribute is less than a maximum value. * Validate the size of an attribute is less than a maximum value.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateMax(string $attribute, $value, array $parameters): bool public function validateMax(string $attribute, $value, array $parameters): bool
{ {
@ -843,10 +728,7 @@ trait ValidatesAttributes
/** /**
* Validate the guessed extension of a file upload is in a set of file extensions. * Validate the guessed extension of a file upload is in a set of file extensions.
* *
* @param string $attribute * @param SplFileInfo $value
* @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateMimes(string $attribute, $value, array $parameters): bool public function validateMimes(string $attribute, $value, array $parameters): bool
{ {
@ -858,16 +740,25 @@ trait ValidatesAttributes
return false; return false;
} }
return $value->getPath() !== '' && in_array($value->guessExtension(), $parameters); if (empty($value->getPath())) {
return false;
}
if (in_array($value->getExtension(), $parameters)) {
return true;
}
if ($value instanceof UploadedFile) {
return in_array($value->getMimeType(), $parameters);
}
return false;
} }
/** /**
* Validate the MIME type of a file upload attribute is in a set of MIME types. * Validate the MIME type of a file upload attribute is in a set of MIME types.
* *
* @param string $attribute * @param SplFileInfo $value
* @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateMimetypes(string $attribute, $value, array $parameters): bool public function validateMimetypes(string $attribute, $value, array $parameters): bool
{ {
@ -887,10 +778,7 @@ trait ValidatesAttributes
/** /**
* Validate the size of an attribute is greater than a minimum value. * Validate the size of an attribute is greater than a minimum value.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateMin(string $attribute, $value, array $parameters): bool public function validateMin(string $attribute, $value, array $parameters): bool
{ {
@ -903,8 +791,6 @@ trait ValidatesAttributes
* "Indicate" validation should pass if value is null. * "Indicate" validation should pass if value is null.
* *
* Always returns true, just lets us put "nullable" in rules. * Always returns true, just lets us put "nullable" in rules.
*
* @return bool
*/ */
public function validateNullable(): bool public function validateNullable(): bool
{ {
@ -914,10 +800,7 @@ trait ValidatesAttributes
/** /**
* Validate an attribute is not contained within a list of values. * Validate an attribute is not contained within a list of values.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateNotIn(string $attribute, $value, array $parameters): bool public function validateNotIn(string $attribute, $value, array $parameters): bool
{ {
@ -927,9 +810,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is numeric. * Validate that an attribute is numeric.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateNumeric(string $attribute, $value): bool public function validateNumeric(string $attribute, $value): bool
{ {
@ -939,9 +820,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute exists even if not filled. * Validate that an attribute exists even if not filled.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validatePresent(string $attribute, $value): bool public function validatePresent(string $attribute, $value): bool
{ {
@ -951,10 +830,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute passes a regular expression check. * Validate that an attribute passes a regular expression check.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateRegex(string $attribute, $value, array $parameters): bool public function validateRegex(string $attribute, $value, array $parameters): bool
{ {
@ -970,10 +846,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute does not pass a regular expression check. * Validate that an attribute does not pass a regular expression check.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateNotRegex(string $attribute, $value, array $parameters): bool public function validateNotRegex(string $attribute, $value, array $parameters): bool
{ {
@ -989,9 +862,7 @@ trait ValidatesAttributes
/** /**
* Validate that a required attribute exists. * Validate that a required attribute exists.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateRequired(string $attribute, $value): bool public function validateRequired(string $attribute, $value): bool
{ {
@ -1004,7 +875,7 @@ trait ValidatesAttributes
if ((is_array($value) || $value instanceof Countable) && count($value) < 1) { if ((is_array($value) || $value instanceof Countable) && count($value) < 1) {
return false; return false;
} }
if ($value instanceof File) { if ($value instanceof SplFileInfo) {
return (string) $value->getPath() !== ''; return (string) $value->getPath() !== '';
} }
@ -1014,10 +885,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute exists when another attribute has a given value. * Validate that an attribute exists when another attribute has a given value.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param mixed $parameters
* @return bool
*/ */
public function validateRequiredIf(string $attribute, $value, array $parameters): bool public function validateRequiredIf(string $attribute, $value, array $parameters): bool
{ {
@ -1041,10 +909,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute exists when another attribute does not have a given value. * Validate that an attribute exists when another attribute does not have a given value.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param mixed $parameters
* @return bool
*/ */
public function validateRequiredUnless(string $attribute, $value, array $parameters): bool public function validateRequiredUnless(string $attribute, $value, array $parameters): bool
{ {
@ -1064,10 +929,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute exists when any other attribute exists. * Validate that an attribute exists when any other attribute exists.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param mixed $parameters
* @return bool
*/ */
public function validateRequiredWith(string $attribute, $value, array $parameters): bool public function validateRequiredWith(string $attribute, $value, array $parameters): bool
{ {
@ -1081,10 +943,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute exists when all other attributes exists. * Validate that an attribute exists when all other attributes exists.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param mixed $parameters
* @return bool
*/ */
public function validateRequiredWithAll(string $attribute, $value, array $parameters): bool public function validateRequiredWithAll(string $attribute, $value, array $parameters): bool
{ {
@ -1098,10 +957,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute exists when another attribute does not. * Validate that an attribute exists when another attribute does not.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param mixed $parameters
* @return bool
*/ */
public function validateRequiredWithout(string $attribute, $value, array $parameters): bool public function validateRequiredWithout(string $attribute, $value, array $parameters): bool
{ {
@ -1115,10 +971,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute exists when all other attributes do not. * Validate that an attribute exists when all other attributes do not.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param mixed $parameters
* @return bool
*/ */
public function validateRequiredWithoutAll(string $attribute, $value, array $parameters): bool public function validateRequiredWithoutAll(string $attribute, $value, array $parameters): bool
{ {
@ -1132,10 +985,7 @@ trait ValidatesAttributes
/** /**
* Validate that two attributes match. * Validate that two attributes match.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateSame(string $attribute, $value, array $parameters): bool public function validateSame(string $attribute, $value, array $parameters): bool
{ {
@ -1149,10 +999,7 @@ trait ValidatesAttributes
/** /**
* Validate the size of an attribute. * Validate the size of an attribute.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateSize(string $attribute, $value, array $parameters): bool public function validateSize(string $attribute, $value, array $parameters): bool
{ {
@ -1165,8 +1012,6 @@ trait ValidatesAttributes
* "Validate" optional attributes. * "Validate" optional attributes.
* *
* Always returns true, just lets us put sometimes in rules. * Always returns true, just lets us put sometimes in rules.
*
* @return bool
*/ */
public function validateSometimes() public function validateSometimes()
{ {
@ -1176,10 +1021,7 @@ trait ValidatesAttributes
/** /**
* Validate the attribute starts with a given substring. * Validate the attribute starts with a given substring.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateStartsWith(string $attribute, $value, array $parameters): bool public function validateStartsWith(string $attribute, $value, array $parameters): bool
{ {
@ -1189,10 +1031,7 @@ trait ValidatesAttributes
/** /**
* Validate the attribute ends with a given substring. * Validate the attribute ends with a given substring.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return bool
*/ */
public function validateEndsWith(string $attribute, $value, array $parameters): bool public function validateEndsWith(string $attribute, $value, array $parameters): bool
{ {
@ -1202,9 +1041,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is a string. * Validate that an attribute is a string.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateString(string $attribute, $value): bool public function validateString(string $attribute, $value): bool
{ {
@ -1214,9 +1051,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is a valid timezone. * Validate that an attribute is a valid timezone.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateTimezone(string $attribute, $value): bool public function validateTimezone(string $attribute, $value): bool
{ {
@ -1234,9 +1069,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is a valid URL. * Validate that an attribute is a valid URL.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateUrl(string $attribute, $value): bool public function validateUrl(string $attribute, $value): bool
{ {
@ -1271,9 +1104,7 @@ trait ValidatesAttributes
/** /**
* Validate that an attribute is a valid UUID. * Validate that an attribute is a valid UUID.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function validateUuid(string $attribute, $value): bool public function validateUuid(string $attribute, $value): bool
{ {
@ -1288,7 +1119,6 @@ trait ValidatesAttributes
* Check that the given value is a valid file instance. * Check that the given value is a valid file instance.
* *
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function isValidFileInstance($value): bool public function isValidFileInstance($value): bool
{ {
@ -1296,16 +1126,12 @@ trait ValidatesAttributes
return false; return false;
} }
return $value instanceof File; return $value instanceof SplFileInfo;
} }
/** /**
* Require a certain number of parameters to be present. * Require a certain number of parameters to be present.
* *
* @param int $count
* @param array $parameters
* @param string $rule
*
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function requireParameterCount(int $count, array $parameters, string $rule) public function requireParameterCount(int $count, array $parameters, string $rule)
@ -1318,11 +1144,7 @@ trait ValidatesAttributes
/** /**
* Compare a given date against another using an operator. * Compare a given date against another using an operator.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @param array $parameters
* @param string $operator
* @return bool
*/ */
protected function compareDates(string $attribute, $value, array $parameters, string $operator): bool protected function compareDates(string $attribute, $value, array $parameters, string $operator): bool
{ {
@ -1344,7 +1166,6 @@ trait ValidatesAttributes
/** /**
* Get the date format for an attribute if it has one. * Get the date format for an attribute if it has one.
* *
* @param string $attribute
* @return null|string * @return null|string
*/ */
protected function getDateFormat(string $attribute) protected function getDateFormat(string $attribute)
@ -1358,7 +1179,7 @@ trait ValidatesAttributes
* Get the date timestamp. * Get the date timestamp.
* *
* @param mixed $value * @param mixed $value
* @return int * @return bool|int
*/ */
protected function getDateTimestamp($value) protected function getDateTimestamp($value)
{ {
@ -1379,12 +1200,6 @@ trait ValidatesAttributes
/** /**
* Given two date/time strings, check that one is after the other. * Given two date/time strings, check that one is after the other.
*
* @param string $format
* @param string $first
* @param string $second
* @param string $operator
* @return bool
*/ */
protected function checkDateTimeOrder(string $format, string $first, string $second, string $operator): bool protected function checkDateTimeOrder(string $format, string $first, string $second, string $operator): bool
{ {
@ -1400,8 +1215,6 @@ trait ValidatesAttributes
/** /**
* Get a DateTime instance from a string. * Get a DateTime instance from a string.
* *
* @param string $format
* @param string $value
* @return null|\DateTime * @return null|\DateTime
*/ */
protected function getDateTimeWithOptionalFormat(string $format, string $value) protected function getDateTimeWithOptionalFormat(string $format, string $value)
@ -1416,7 +1229,6 @@ trait ValidatesAttributes
/** /**
* Get a DateTime instance from a string with no format. * Get a DateTime instance from a string with no format.
* *
* @param string $value
* @return null|\DateTime * @return null|\DateTime
*/ */
protected function getDateTime(string $value) protected function getDateTime(string $value)
@ -1435,22 +1247,16 @@ trait ValidatesAttributes
* Check if the given value should be adjusted to Carbon::getTestNow(). * Check if the given value should be adjusted to Carbon::getTestNow().
* *
* @param mixed $value * @param mixed $value
* @return bool
*/ */
protected function isTestingRelativeDateTime($value): bool protected function isTestingRelativeDateTime($value): bool
{ {
return Carbon::hasTestNow() && is_string($value) && ( return Carbon::hasTestNow() && is_string($value) && (
$value === 'now' || Carbon::hasRelativeKeywords($value) $value === 'now' || Carbon::hasRelativeKeywords($value)
); );
} }
/** /**
* Test if the given width and height fail any conditions. * Test if the given width and height fail any conditions.
*
* @param array $parameters
* @param int $width
* @param int $height
* @return bool
*/ */
protected function failsBasicDimensionChecks(array $parameters, int $width, int $height): bool protected function failsBasicDimensionChecks(array $parameters, int $width, int $height): bool
{ {
@ -1464,11 +1270,6 @@ trait ValidatesAttributes
/** /**
* Determine if the given parameters fail a dimension ratio check. * Determine if the given parameters fail a dimension ratio check.
*
* @param array $parameters
* @param int $width
* @param int $height
* @return bool
*/ */
protected function failsRatioCheck(array $parameters, int $width, int $height): bool protected function failsRatioCheck(array $parameters, int $width, int $height): bool
{ {
@ -1488,9 +1289,6 @@ trait ValidatesAttributes
/** /**
* Get the values to distinct between. * Get the values to distinct between.
*
* @param string $attribute
* @return array
*/ */
protected function getDistinctValues(string $attribute): array protected function getDistinctValues(string $attribute): array
{ {
@ -1509,9 +1307,6 @@ trait ValidatesAttributes
/** /**
* Extract the distinct values from the data. * Extract the distinct values from the data.
*
* @param string $attribute
* @return array
*/ */
protected function extractDistinctValues(string $attribute): array protected function extractDistinctValues(string $attribute): array
{ {
@ -1531,11 +1326,7 @@ trait ValidatesAttributes
* Get the number of records that exist in storage. * Get the number of records that exist in storage.
* *
* @param mixed $connection * @param mixed $connection
* @param string $table
* @param string $column
* @param mixed $value * @param mixed $value
* @param array $parameters
* @return int
*/ */
protected function getExistCount($connection, string $table, string $column, $value, array $parameters): int protected function getExistCount($connection, string $table, string $column, $value, array $parameters): int
{ {
@ -1556,9 +1347,6 @@ trait ValidatesAttributes
/** /**
* Get the excluded ID column and value for the unique rule. * Get the excluded ID column and value for the unique rule.
*
* @param array $parameters
* @return array
*/ */
protected function getUniqueIds(array $parameters): array protected function getUniqueIds(array $parameters): array
{ {
@ -1592,9 +1380,6 @@ trait ValidatesAttributes
/** /**
* Get the extra conditions for a unique rule. * Get the extra conditions for a unique rule.
*
* @param array $parameters
* @return array
*/ */
protected function getUniqueExtra(array $parameters): array protected function getUniqueExtra(array $parameters): array
{ {
@ -1607,9 +1392,6 @@ trait ValidatesAttributes
/** /**
* Get the extra conditions for a unique / exists rule. * Get the extra conditions for a unique / exists rule.
*
* @param array $segments
* @return array
*/ */
protected function getExtraConditions(array $segments): array protected function getExtraConditions(array $segments): array
{ {
@ -1627,9 +1409,7 @@ trait ValidatesAttributes
/** /**
* Check if PHP uploads are explicitly allowed. * Check if PHP uploads are explicitly allowed.
* *
* @param mixed $value * @param SplFileInfo $value
* @param array $parameters
* @return bool
*/ */
protected function shouldBlockPhpUpload($value, array $parameters): bool protected function shouldBlockPhpUpload($value, array $parameters): bool
{ {
@ -1641,16 +1421,11 @@ trait ValidatesAttributes
'php', 'php3', 'php4', 'php5', 'phtml', 'php', 'php3', 'php4', 'php5', 'phtml',
]; ];
return ($value instanceof UploadedFile) return in_array(trim(strtolower($value->getExtension())), $phpExtensions);
? in_array(trim(strtolower($value->getClientOriginalExtension())), $phpExtensions)
: in_array(trim(strtolower($value->getExtension())), $phpExtensions);
} }
/** /**
* Convert the given values to boolean if they are string "true" / "false". * Convert the given values to boolean if they are string "true" / "false".
*
* @param array $values
* @return array
*/ */
protected function convertValuesToBoolean(array $values): array protected function convertValuesToBoolean(array $values): array
{ {
@ -1668,9 +1443,6 @@ trait ValidatesAttributes
/** /**
* Determine if any of the given attributes fail the required test. * Determine if any of the given attributes fail the required test.
*
* @param array $attributes
* @return bool
*/ */
protected function anyFailingRequired(array $attributes): bool protected function anyFailingRequired(array $attributes): bool
{ {
@ -1685,9 +1457,6 @@ trait ValidatesAttributes
/** /**
* Determine if all of the given attributes fail the required test. * Determine if all of the given attributes fail the required test.
*
* @param array $attributes
* @return bool
*/ */
protected function allFailingRequired(array $attributes): bool protected function allFailingRequired(array $attributes): bool
{ {
@ -1703,7 +1472,6 @@ trait ValidatesAttributes
/** /**
* Get the size of an attribute. * Get the size of an attribute.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return mixed * @return mixed
*/ */
@ -1721,7 +1489,7 @@ trait ValidatesAttributes
if (is_array($value)) { if (is_array($value)) {
return count($value); return count($value);
} }
if ($value instanceof File) { if ($value instanceof SplFileInfo) {
return $value->getSize() / 1024; return $value->getSize() / 1024;
} }
@ -1733,7 +1501,6 @@ trait ValidatesAttributes
* *
* @param mixed $first * @param mixed $first
* @param mixed $second * @param mixed $second
* @param string $operator
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
* @return bool * @return bool
*/ */
@ -1757,11 +1524,8 @@ trait ValidatesAttributes
/** /**
* Parse named parameters to $key => $value items. * Parse named parameters to $key => $value items.
*
* @param array $parameters
* @return array
*/ */
protected function parseNamedParameters(array $parameters) protected function parseNamedParameters(array $parameters): ?array
{ {
return array_reduce($parameters, function ($result, $item) { return array_reduce($parameters, function ($result, $item) {
[$key, $value] = array_pad(explode('=', $item, 2), 2, null); [$key, $value] = array_pad(explode('=', $item, 2), 2, null);
@ -1777,7 +1541,6 @@ trait ValidatesAttributes
* *
* @param mixed $first * @param mixed $first
* @param mixed $second * @param mixed $second
* @return bool
*/ */
protected function isSameType($first, $second): bool protected function isSameType($first, $second): bool
{ {
@ -1786,9 +1549,6 @@ trait ValidatesAttributes
/** /**
* Adds the existing rule to the numericRules array if the attribute's value is numeric. * Adds the existing rule to the numericRules array if the attribute's value is numeric.
*
* @param string $attribute
* @param string $rule
*/ */
protected function shouldBeNumeric(string $attribute, string $rule) protected function shouldBeNumeric(string $attribute, string $rule)
{ {

View File

@ -12,15 +12,18 @@ declare(strict_types=1);
namespace Hyperf\Validation; namespace Hyperf\Validation;
use Hyperf\Validation\Contracts\Validation\Factory as FactoryInterface;
use Hyperf\Contract\ValidatorInterface;
class ConfigProvider class ConfigProvider
{ {
public function __invoke(): array public function __invoke(): array
{ {
return [ return [
'dependencies' => [ 'dependencies' => [
\Hyperf\Validation\Contracts\Validation\Validator::class => \Hyperf\Validation\ValidatorFactory::class, ValidatorInterface::class => ValidatorFactory::class,
\Hyperf\Validation\PresenceVerifierInterface::class => \Hyperf\Validation\DatabasePresenceVerifierFactory::class, PresenceVerifierInterface::class => DatabasePresenceVerifierFactory::class,
\Hyperf\Validation\Contracts\Validation\Factory::class => \Hyperf\Validation\ValidatorFactory::class, FactoryInterface::class => Factory::class,
], ],
'scan' => [ 'scan' => [
'paths' => [ 'paths' => [

View File

@ -17,18 +17,13 @@ interface Factory
/** /**
* Create a new Validator instance. * Create a new Validator instance.
* *
* @param array $data * @return \Hyperf\Contract\ValidatorInterface
* @param array $rules
* @param array $messages
* @param array $customAttributes
* @return \Hyperf\Validation\Contracts\Validation\Validator
*/ */
public function make(array $data, array $rules, array $messages = [], array $customAttributes = []); public function make(array $data, array $rules, array $messages = [], array $customAttributes = []);
/** /**
* Register a custom validator extension. * Register a custom validator extension.
* *
* @param string $rule
* @param \Closure|string $extension * @param \Closure|string $extension
* @param null|string $message * @param null|string $message
*/ */
@ -37,7 +32,6 @@ interface Factory
/** /**
* Register a custom implicit validator extension. * Register a custom implicit validator extension.
* *
* @param string $rule
* @param \Closure|string $extension * @param \Closure|string $extension
* @param null|string $message * @param null|string $message
*/ */
@ -46,7 +40,6 @@ interface Factory
/** /**
* Register a custom implicit validator message replacer. * Register a custom implicit validator message replacer.
* *
* @param string $rule
* @param \Closure|string $replacer * @param \Closure|string $replacer
*/ */
public function replacer(string $rule, $replacer); public function replacer(string $rule, $replacer);

View File

@ -17,9 +17,7 @@ interface Rule
/** /**
* Determine if the validation rule passes. * Determine if the validation rule passes.
* *
* @param string $attribute
* @param mixed $value * @param mixed $value
* @return bool
*/ */
public function passes(string $attribute, $value): bool; public function passes(string $attribute, $value): bool;

View File

@ -45,13 +45,9 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
/** /**
* Count the number of objects in a collection having the given value. * Count the number of objects in a collection having the given value.
* *
* @param string $collection
* @param string $column
* @param string $value * @param string $value
* @param null|int $excludeId * @param null|int $excludeId
* @param null|string $idColumn * @param null|string $idColumn
* @param array $extra
* @return int
*/ */
public function getCount(string $collection, string $column, $value, $excludeId = null, $idColumn = null, array $extra = []): int public function getCount(string $collection, string $column, $value, $excludeId = null, $idColumn = null, array $extra = []): int
{ {
@ -66,12 +62,6 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
/** /**
* Count the number of objects in a collection with the given values. * Count the number of objects in a collection with the given values.
*
* @param string $collection
* @param string $column
* @param array $values
* @param array $extra
* @return int
*/ */
public function getMultiCount(string $collection, string $column, array $values, array $extra = []): int public function getMultiCount(string $collection, string $column, array $values, array $extra = []): int
{ {
@ -83,7 +73,6 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
/** /**
* Get a query builder for the given table. * Get a query builder for the given table.
* *
* @param string $table
* @return \Hyperf\Database\Query\Builder * @return \Hyperf\Database\Query\Builder
*/ */
public function table(string $table) public function table(string $table)
@ -93,10 +82,8 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
/** /**
* Set the connection to be used. * Set the connection to be used.
*
* @param string $connection
*/ */
public function setConnection($connection) public function setConnection(?string $connection)
{ {
$this->connection = $connection; $this->connection = $connection;
} }
@ -105,7 +92,6 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
* Add the given conditions to the query. * Add the given conditions to the query.
* *
* @param \Hyperf\Database\Query\Builder $query * @param \Hyperf\Database\Query\Builder $query
* @param array $conditions
* @return \Hyperf\Database\Query\Builder * @return \Hyperf\Database\Query\Builder
*/ */
protected function addConditions($query, array $conditions) protected function addConditions($query, array $conditions)
@ -127,7 +113,6 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
* Add a "where" clause to the given query. * Add a "where" clause to the given query.
* *
* @param \Hyperf\Database\Query\Builder $query * @param \Hyperf\Database\Query\Builder $query
* @param string $key
* @param string $extraValue * @param string $extraValue
*/ */
protected function addWhere($query, string $key, $extraValue) protected function addWhere($query, string $key, $extraValue)

View File

@ -13,17 +13,17 @@ declare(strict_types=1);
namespace Hyperf\Validation; namespace Hyperf\Validation;
use Closure; use Closure;
use Hyperf\Di\Container; use Hyperf\Contract\TranslatorInterface;
use Hyperf\Translation\Contracts\Translator;
use Hyperf\Utils\Str; use Hyperf\Utils\Str;
use Hyperf\Validation\Contracts\Validation\Factory as FactoryContract; use Hyperf\Validation\Contracts\Validation\Factory as FactoryContract;
use Psr\Container\ContainerInterface;
class Factory implements FactoryContract class Factory implements FactoryContract
{ {
/** /**
* The Translator implementation. * The Translator implementation.
* *
* @var \Hyperf\Translation\Contracts\Translator * @var TranslatorInterface
*/ */
protected $translator; protected $translator;
@ -37,7 +37,7 @@ class Factory implements FactoryContract
/** /**
* The IoC container instance. * The IoC container instance.
* *
* @var Container * @var ContainerInterface
*/ */
protected $container; protected $container;
@ -85,11 +85,8 @@ class Factory implements FactoryContract
/** /**
* Create a new Validator factory instance. * Create a new Validator factory instance.
*
* @param null|\Hyperf\Translation\Contracts\Translator $translator
* @param Container
*/ */
public function __construct(Translator $translator, Container $container = null) public function __construct(TranslatorInterface $translator, ContainerInterface $container = null)
{ {
$this->container = $container; $this->container = $container;
$this->translator = $translator; $this->translator = $translator;
@ -98,10 +95,6 @@ class Factory implements FactoryContract
/** /**
* Create a new Validator instance. * Create a new Validator instance.
* *
* @param array $data
* @param array $rules
* @param array $messages
* @param array $customAttributes
* @return \Hyperf\Validation\Validator * @return \Hyperf\Validation\Validator
*/ */
public function make(array $data, array $rules, array $messages = [], array $customAttributes = []) public function make(array $data, array $rules, array $messages = [], array $customAttributes = [])
@ -135,12 +128,7 @@ class Factory implements FactoryContract
/** /**
* Validate the given data against the provided rules. * Validate the given data against the provided rules.
* *
* @param array $data
* @param array $rules
* @param array $messages
* @param array $customAttributes
* @throws \Hyperf\Validation\ValidationException * @throws \Hyperf\Validation\ValidationException
* @return array
*/ */
public function validate(array $data, array $rules, array $messages = [], array $customAttributes = []): array public function validate(array $data, array $rules, array $messages = [], array $customAttributes = []): array
{ {
@ -150,7 +138,6 @@ class Factory implements FactoryContract
/** /**
* Register a custom validator extension. * Register a custom validator extension.
* *
* @param string $rule
* @param \Closure|string $extension * @param \Closure|string $extension
* @param null|string $message * @param null|string $message
*/ */
@ -166,7 +153,6 @@ class Factory implements FactoryContract
/** /**
* Register a custom implicit validator extension. * Register a custom implicit validator extension.
* *
* @param string $rule
* @param \Closure|string $extension * @param \Closure|string $extension
* @param null|string $message * @param null|string $message
*/ */
@ -182,7 +168,6 @@ class Factory implements FactoryContract
/** /**
* Register a custom dependent validator extension. * Register a custom dependent validator extension.
* *
* @param string $rule
* @param \Closure|string $extension * @param \Closure|string $extension
* @param null|string $message * @param null|string $message
*/ */
@ -198,7 +183,6 @@ class Factory implements FactoryContract
/** /**
* Register a custom validator message replacer. * Register a custom validator message replacer.
* *
* @param string $rule
* @param \Closure|string $replacer * @param \Closure|string $replacer
*/ */
public function replacer(string $rule, $replacer) public function replacer(string $rule, $replacer)
@ -219,7 +203,7 @@ class Factory implements FactoryContract
/** /**
* Get the Translator implementation. * Get the Translator implementation.
* *
* @return \Hyperf\Translation\Contracts\Translator * @return TranslatorInterface
*/ */
public function getTranslator() public function getTranslator()
{ {
@ -238,8 +222,6 @@ class Factory implements FactoryContract
/** /**
* Set the Presence Verifier implementation. * Set the Presence Verifier implementation.
*
* @param \Hyperf\Validation\PresenceVerifierInterface $presenceVerifier
*/ */
public function setPresenceVerifier(PresenceVerifierInterface $presenceVerifier) public function setPresenceVerifier(PresenceVerifierInterface $presenceVerifier)
{ {
@ -249,10 +231,6 @@ class Factory implements FactoryContract
/** /**
* Resolve a new Validator instance. * Resolve a new Validator instance.
* *
* @param array $data
* @param array $rules
* @param array $messages
* @param array $customAttributes
* @return \Hyperf\Validation\Validator * @return \Hyperf\Validation\Validator
*/ */
protected function resolve(array $data, array $rules, array $messages, array $customAttributes) protected function resolve(array $data, array $rules, array $messages, array $customAttributes)
@ -266,8 +244,6 @@ class Factory implements FactoryContract
/** /**
* Add the extensions to a validator instance. * Add the extensions to a validator instance.
*
* @param \Hyperf\Validation\Validator $validator
*/ */
protected function addExtensions(Validator $validator) protected function addExtensions(Validator $validator)
{ {

View File

@ -17,24 +17,13 @@ interface PresenceVerifierInterface
/** /**
* Count the number of objects in a collection having the given value. * Count the number of objects in a collection having the given value.
* *
* @param string $collection
* @param string $column
* @param string $value
* @param null|int $excludeId * @param null|int $excludeId
* @param null|string $idColumn * @param null|string $idColumn
* @param array $extra
* @return int
*/ */
public function getCount(string $collection, string $column, string $value, $excludeId = null, $idColumn = null, array $extra = []): int; public function getCount(string $collection, string $column, string $value, $excludeId = null, $idColumn = null, array $extra = []): int;
/** /**
* Count the number of objects in a collection with the given values. * Count the number of objects in a collection with the given values.
*
* @param string $collection
* @param string $column
* @param array $values
* @param array $extra
* @return int
*/ */
public function getMultiCount(string $collection, string $column, array $values, array $extra = []): int; public function getMultiCount(string $collection, string $column, array $values, array $extra = []): int;
} }

View File

@ -13,14 +13,14 @@ declare(strict_types=1);
namespace Hyperf\Validation\Request; namespace Hyperf\Validation\Request;
use Hyperf\HttpServer\Request; use Hyperf\HttpServer\Request;
use Hyperf\Validation\Contracts\Validation\Factory; use Hyperf\Utils\Context;
use Hyperf\Validation\Contracts\Validation\Factory as ValidationFactory; use Hyperf\Validation\Contracts\Validation\Factory as ValidationFactory;
use Hyperf\Validation\Contracts\Validation\ValidatesWhenResolved; use Hyperf\Validation\Contracts\Validation\ValidatesWhenResolved;
use Hyperf\Validation\Contracts\Validation\Validator; use Hyperf\Contract\ValidatorInterface;
use Hyperf\Validation\ValidatesWhenResolvedTrait; use Hyperf\Validation\ValidatesWhenResolvedTrait;
use Hyperf\Validation\ValidationException; use Hyperf\Validation\ValidationException;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse; use Psr\Http\Message\ResponseInterface;
class FormRequest extends Request implements ValidatesWhenResolved class FormRequest extends Request implements ValidatesWhenResolved
{ {
@ -33,34 +33,6 @@ class FormRequest extends Request implements ValidatesWhenResolved
*/ */
protected $container; protected $container;
// /**
// * The redirector instance.
// *
// * @var \Illuminate\Routing\Redirector
// */
// protected $redirector;
//
// /**
// * The URI to redirect to if validation fails.
// *
// * @var string
// */
// protected $redirect;
//
// /**
// * The route to redirect to if validation fails.
// *
// * @var string
// */
// protected $redirectRoute;
//
// /**
// * The controller action to redirect to if validation fails.
// *
// * @var string
// */
// protected $redirectAction;
/** /**
* The key to be used for the view error bag. * The key to be used for the view error bag.
* *
@ -83,58 +55,35 @@ class FormRequest extends Request implements ValidatesWhenResolved
/** /**
* Get the proper failed validation response for the request. * Get the proper failed validation response for the request.
* *
* @param array $errors * @return ResponseInterface
* @return \Symfony\Component\HttpFoundation\Response
*/ */
public function response(array $errors) public function response()
{ {
// if ($this->expectsJson()) { /** @var ResponseInterface $response */
// return new JsonResponse($errors, 422); $response = Context::get(ResponseInterface::class);
// }
// return $this->redirector->to($this->getRedirectUrl()) return $response->withStatus(422);
// ->withInput($this->except($this->dontFlash))
// ->withErrors($errors, $this->errorBag);
return new JsonResponse($errors, 422);
} }
/** /**
* Get custom messages for validator errors. * Get custom messages for validator errors.
*
* @return array
*/ */
public function messages() public function messages(): array
{ {
return []; return [];
} }
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes() public function attributes(): array
{ {
return []; return [];
} }
// /**
// * Set the Redirector instance.
// *
// * @param \Illuminate\Routing\Redirector $redirector
// * @return $this
// */
// public function setRedirector(Redirector $redirector)
// {
// $this->redirector = $redirector;
//
// return $this;
// }
/** /**
* Set the container implementation. * Set the container implementation.
* *
* @param ContainerInterface $container
* @return $this * @return $this
*/ */
public function setContainer(ContainerInterface $container) public function setContainer(ContainerInterface $container)
@ -147,7 +96,7 @@ class FormRequest extends Request implements ValidatesWhenResolved
/** /**
* Get the validator instance for the request. * Get the validator instance for the request.
* *
* @return \Hyperf\Validation\Contracts\Validation\Validator * @return ValidatorInterface
*/ */
protected function getValidatorInstance() protected function getValidatorInstance()
{ {
@ -169,8 +118,7 @@ class FormRequest extends Request implements ValidatesWhenResolved
/** /**
* Create the default validator instance. * Create the default validator instance.
* *
* @param Factory $factory * @return ValidatorInterface
* @return \Hyperf\Validation\Contracts\Validation\Validator
*/ */
protected function createDefaultValidator(ValidationFactory $factory) protected function createDefaultValidator(ValidationFactory $factory)
{ {
@ -195,48 +143,21 @@ class FormRequest extends Request implements ValidatesWhenResolved
/** /**
* Handle a failed validation attempt. * Handle a failed validation attempt.
* *
* @param Validator $validator
*
* @throws ValidationException * @throws ValidationException
*/ */
protected function failedValidation(Validator $validator) protected function failedValidation(ValidatorInterface $validator)
{ {
throw new ValidationException($validator, $this->response( throw new ValidationException($validator, $this->response());
$this->formatErrors($validator)
));
} }
/** /**
* Format the errors from the given Validator instance. * Format the errors from the given Validator instance.
*
* @param Validator $validator
* @return array
*/ */
protected function formatErrors(Validator $validator) protected function formatErrors(ValidatorInterface $validator): array
{ {
return $validator->getMessageBag()->toArray(); return $validator->getMessageBag()->toArray();
} }
// /**
// * Get the URL to redirect to on a validation error.
// *
// * @return string
// */
// protected function getRedirectUrl()
// {
// $url = $this->redirector->getUrlGenerator();
//
// if ($this->redirect) {
// return $url->to($this->redirect);
// } elseif ($this->redirectRoute) {
// return $url->route($this->redirectRoute);
// } elseif ($this->redirectAction) {
// return $url->action($this->redirectAction);
// }
//
// return $url->previous();
// }
/** /**
* Determine if the request passes the authorization check. * Determine if the request passes the authorization check.
* *
@ -253,8 +174,6 @@ class FormRequest extends Request implements ValidatesWhenResolved
/** /**
* Handle a failed authorization attempt. * Handle a failed authorization attempt.
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/ */
protected function failedAuthorization() protected function failedAuthorization()
{ {

View File

@ -22,7 +22,6 @@ class Rule
/** /**
* Get a dimensions constraint builder instance. * Get a dimensions constraint builder instance.
* *
* @param array $constraints
* @return \Hyperf\Validation\Rules\Dimensions * @return \Hyperf\Validation\Rules\Dimensions
*/ */
public static function dimensions(array $constraints = []) public static function dimensions(array $constraints = [])
@ -33,8 +32,6 @@ class Rule
/** /**
* Get a exists constraint builder instance. * Get a exists constraint builder instance.
* *
* @param string $table
* @param string $column
* @return \Hyperf\Validation\Rules\Exists * @return \Hyperf\Validation\Rules\Exists
*/ */
public static function exists(string $table, string $column = 'NULL') public static function exists(string $table, string $column = 'NULL')

View File

@ -46,9 +46,6 @@ trait DatabaseRule
/** /**
* Create a new rule instance. * Create a new rule instance.
*
* @param string $table
* @param string $column
*/ */
public function __construct(string $table, string $column = 'NULL') public function __construct(string $table, string $column = 'NULL')
{ {
@ -81,7 +78,6 @@ trait DatabaseRule
/** /**
* Set a "where not" constraint on the query. * Set a "where not" constraint on the query.
* *
* @param string $column
* @param array|string $value * @param array|string $value
* @return $this * @return $this
*/ */
@ -97,7 +93,6 @@ trait DatabaseRule
/** /**
* Set a "where null" constraint on the query. * Set a "where null" constraint on the query.
* *
* @param string $column
* @return $this * @return $this
*/ */
public function whereNull(string $column) public function whereNull(string $column)
@ -108,7 +103,6 @@ trait DatabaseRule
/** /**
* Set a "where not null" constraint on the query. * Set a "where not null" constraint on the query.
* *
* @param string $column
* @return $this * @return $this
*/ */
public function whereNotNull(string $column) public function whereNotNull(string $column)
@ -119,8 +113,6 @@ trait DatabaseRule
/** /**
* Set a "where in" constraint on the query. * Set a "where in" constraint on the query.
* *
* @param string $column
* @param array $values
* @return $this * @return $this
*/ */
public function whereIn(string $column, array $values) public function whereIn(string $column, array $values)
@ -133,8 +125,6 @@ trait DatabaseRule
/** /**
* Set a "where not in" constraint on the query. * Set a "where not in" constraint on the query.
* *
* @param string $column
* @param array $values
* @return $this * @return $this
*/ */
public function whereNotIn(string $column, array $values) public function whereNotIn(string $column, array $values)
@ -147,7 +137,6 @@ trait DatabaseRule
/** /**
* Register a custom query callback. * Register a custom query callback.
* *
* @param \Closure $callback
* @return $this * @return $this
*/ */
public function using(Closure $callback) public function using(Closure $callback)
@ -159,8 +148,6 @@ trait DatabaseRule
/** /**
* Get the custom query callbacks for the rule. * Get the custom query callbacks for the rule.
*
* @return array
*/ */
public function queryCallbacks(): array public function queryCallbacks(): array
{ {
@ -169,8 +156,6 @@ trait DatabaseRule
/** /**
* Format the where clauses. * Format the where clauses.
*
* @return string
*/ */
protected function formatWheres(): string protected function formatWheres(): string
{ {

View File

@ -23,8 +23,6 @@ class Dimensions
/** /**
* Create a new dimensions rule instance. * Create a new dimensions rule instance.
*
* @param array $constraints;
*/ */
public function __construct(array $constraints = []) public function __construct(array $constraints = [])
{ {
@ -33,8 +31,6 @@ class Dimensions
/** /**
* Convert the rule to a validation string. * Convert the rule to a validation string.
*
* @return string
*/ */
public function __toString(): string public function __toString(): string
{ {
@ -50,7 +46,6 @@ class Dimensions
/** /**
* Set the "width" constraint. * Set the "width" constraint.
* *
* @param int $value
* @return $this * @return $this
*/ */
public function width(int $value) public function width(int $value)
@ -63,7 +58,6 @@ class Dimensions
/** /**
* Set the "height" constraint. * Set the "height" constraint.
* *
* @param int $value
* @return $this * @return $this
*/ */
public function height(int $value) public function height(int $value)
@ -76,7 +70,6 @@ class Dimensions
/** /**
* Set the "min width" constraint. * Set the "min width" constraint.
* *
* @param int $value
* @return $this * @return $this
*/ */
public function minWidth(int $value) public function minWidth(int $value)
@ -89,7 +82,6 @@ class Dimensions
/** /**
* Set the "min height" constraint. * Set the "min height" constraint.
* *
* @param int $value
* @return $this * @return $this
*/ */
public function minHeight(int $value) public function minHeight(int $value)
@ -102,7 +94,6 @@ class Dimensions
/** /**
* Set the "max width" constraint. * Set the "max width" constraint.
* *
* @param int $value
* @return $this * @return $this
*/ */
public function maxWidth(int $value) public function maxWidth(int $value)
@ -115,7 +106,6 @@ class Dimensions
/** /**
* Set the "max height" constraint. * Set the "max height" constraint.
* *
* @param int $value
* @return $this * @return $this
*/ */
public function maxHeight(int $value) public function maxHeight(int $value)
@ -128,7 +118,6 @@ class Dimensions
/** /**
* Set the "ratio" constraint. * Set the "ratio" constraint.
* *
* @param float $value
* @return $this * @return $this
*/ */
public function ratio(float $value) public function ratio(float $value)

View File

@ -18,8 +18,6 @@ class Exists
/** /**
* Convert the rule to a validation string. * Convert the rule to a validation string.
*
* @return string
*/ */
public function __toString(): string public function __toString(): string
{ {

View File

@ -28,8 +28,6 @@ class In
/** /**
* Create a new in rule instance. * Create a new in rule instance.
*
* @param array $values
*/ */
public function __construct(array $values) public function __construct(array $values)
{ {
@ -39,8 +37,6 @@ class In
/** /**
* Convert the rule to a validation string. * Convert the rule to a validation string.
* *
* @return string
*
* @see \Hyperf\Validation\ValidationRuleParser::parseParameters * @see \Hyperf\Validation\ValidationRuleParser::parseParameters
*/ */
public function __toString(): string public function __toString(): string

Some files were not shown because too many files have changed in this diff Show More