mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-12-03 20:27:59 +08:00
Merge branch 'feat/validation' of https://github.com/chunhei2008/hyperf into feat/validation
This commit is contained in:
commit
a2ec86b54c
32
CHANGELOG.md
32
CHANGELOG.md
@ -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
|
||||
|
||||
|
@ -38,8 +38,8 @@
|
||||
"squizlabs/php_codesniffer": "^3.4",
|
||||
"symfony/console": "^4.2",
|
||||
"symfony/finder": "^4.1",
|
||||
"symfony/http-foundation": "^4.3",
|
||||
"vlucas/phpdotenv": "^3.1"
|
||||
"vlucas/phpdotenv": "^3.1",
|
||||
"egulias/email-validator": "^2.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/common": "@stable",
|
||||
@ -107,6 +107,7 @@
|
||||
"files": [
|
||||
"src/config/src/Functions.php",
|
||||
"src/di/src/Functions.php",
|
||||
"src/translation/src/Functions.php",
|
||||
"src/utils/src/Functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
@ -191,6 +192,7 @@
|
||||
"HyperfTest\\Etcd\\": "src/etcd/tests/",
|
||||
"HyperfTest\\Event\\": "src/event/tests/",
|
||||
"HyperfTest\\GrpcClient\\": "src/grpc-client/tests/",
|
||||
"HyperfTest\\GrpcServer\\": "src/grpc-server/tests/",
|
||||
"HyperfTest\\Guzzle\\": "src/guzzle/tests/",
|
||||
"HyperfTest\\HttpMessage\\": "src/http-message/tests/",
|
||||
"HyperfTest\\HttpServer\\": "src/http-server/tests/",
|
||||
|
103
doc/zh/snowflake.md
Normal file
103
doc/zh/snowflake.md
Normal 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
138
doc/zh/translation.md
Normal 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` 方法。
|
@ -23,6 +23,7 @@
|
||||
<directory suffix="Test.php">./src/elasticsearch/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-server/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-server/tests</directory>
|
||||
|
@ -10,42 +10,29 @@ declare(strict_types=1);
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @param string $key
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocale(): string;
|
||||
|
||||
/**
|
||||
* Set the default locale.
|
||||
*
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setLocale(string $locale);
|
||||
}
|
18
src/translation/src/Contracts/Loader.php → src/contract/src/TranslatorLoaderInterface.php
Executable file → Normal file
18
src/translation/src/Contracts/Loader.php → src/contract/src/TranslatorLoaderInterface.php
Executable file → Normal file
@ -10,39 +10,27 @@ declare(strict_types=1);
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param string $hint
|
||||
*/
|
||||
public function addNamespace(string $namespace, string $hint);
|
||||
|
||||
/**
|
||||
* Add a new JSON path to the loader.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function addJsonPath(string $path);
|
||||
|
||||
/**
|
||||
* Get an array of all the registered namespaces.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function namespaces(): array;
|
||||
}
|
35
src/validation/src/Contracts/Validation/Validator.php → src/contract/src/ValidatorInterface.php
Executable file → Normal file
35
src/validation/src/Contracts/Validation/Validator.php → src/contract/src/ValidatorInterface.php
Executable file → Normal file
@ -1,47 +1,30 @@
|
||||
<?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\Contract;
|
||||
|
||||
namespace Hyperf\Validation\Contracts\Validation;
|
||||
use Hyperf\Utils\Contracts\MessageProvider;
|
||||
use Hyperf\Utils\Contracts\MessageBag;
|
||||
|
||||
use Hyperf\Validation\Contracts\Support\MessageProvider;
|
||||
use Hyperf\Validation\Support\MessageBag;
|
||||
|
||||
interface Validator extends MessageProvider
|
||||
interface ValidatorInterface extends MessageProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the validator's rules against its data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function validate(): array;
|
||||
|
||||
/**
|
||||
* Get the attributes and values that were validated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function validated(): array;
|
||||
|
||||
/**
|
||||
* Determine if the data fails the validation rules.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function fails(): bool;
|
||||
|
||||
/**
|
||||
* Get the failed validation rules.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function failed(): array;
|
||||
|
||||
@ -50,7 +33,6 @@ interface Validator extends MessageProvider
|
||||
*
|
||||
* @param array|string $attribute
|
||||
* @param array|string $rules
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function sometimes($attribute, $rules, callable $callback);
|
||||
@ -65,8 +47,7 @@ interface Validator extends MessageProvider
|
||||
|
||||
/**
|
||||
* Get all of the validation error messages.
|
||||
*
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function errors();
|
||||
}
|
||||
public function errors(): MessageBag;
|
||||
|
||||
}
|
@ -12,24 +12,34 @@ declare(strict_types=1);
|
||||
|
||||
namespace Hyperf\Database\Commands\Ast;
|
||||
|
||||
use Hyperf\Database\Commands\ModelOption;
|
||||
use PhpParser\Comment\Doc;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
class ModelUpdateVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $columns = [];
|
||||
|
||||
public function __construct($columns = [])
|
||||
/**
|
||||
* @var ModelOption
|
||||
*/
|
||||
protected $option;
|
||||
|
||||
public function __construct($columns = [], ModelOption $option)
|
||||
{
|
||||
$this->columns = $columns;
|
||||
$this->option = $option;
|
||||
}
|
||||
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
switch ($node) {
|
||||
case $node instanceof Node\Stmt\PropertyProperty:
|
||||
if ($node->name == 'fillable') {
|
||||
if ($node->name == 'fillable' && $this->option->isRefreshFillable()) {
|
||||
$node = $this->rewriteFillable($node);
|
||||
} elseif ($node->name == 'casts') {
|
||||
$node = $this->rewriteCasts($node);
|
||||
|
@ -93,7 +93,8 @@ class ModelCommand extends Command
|
||||
->setPrefix($this->getOption('prefix', 'prefix', $pool, ''))
|
||||
->setInheritance($this->getOption('inheritance', 'commands.db:model.inheritance', $pool, '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) {
|
||||
$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('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('refresh-fillable', null, InputOption::VALUE_NONE, 'Whether generate fillable argement for model.');
|
||||
}
|
||||
|
||||
protected function getSchemaBuilder(string $poolName): MySqlBuilder
|
||||
@ -158,7 +160,10 @@ class ModelCommand extends Command
|
||||
|
||||
$stms = $this->astParser->parse(file_get_contents($path));
|
||||
$traverser = new NodeTraverser();
|
||||
$visitor = make(ModelUpdateVisitor::class, ['columns' => $columns]);
|
||||
$visitor = make(ModelUpdateVisitor::class, [
|
||||
'columns' => $columns,
|
||||
'option' => $option,
|
||||
]);
|
||||
$traverser->addVisitor($visitor);
|
||||
$stms = $traverser->traverse($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)
|
||||
{
|
||||
$result = $this->input->getOption($name);
|
||||
$nonInput = $name === 'force-casts' ? false : null;
|
||||
$nonInput = in_array($name, ['force-casts', 'refresh-fillable']) ? false : null;
|
||||
if ($result === $nonInput) {
|
||||
$result = $this->config->get("databases.{$pool}.{$key}", $default);
|
||||
}
|
||||
|
@ -44,6 +44,11 @@ class ModelOption
|
||||
*/
|
||||
protected $uses;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $refreshFillable;
|
||||
|
||||
public function getPool(): string
|
||||
{
|
||||
return $this->pool;
|
||||
@ -99,21 +104,25 @@ class ModelOption
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUses(): string
|
||||
{
|
||||
return $this->uses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uses
|
||||
* @return ModelOption
|
||||
*/
|
||||
public function setUses(string $uses): ModelOption
|
||||
{
|
||||
$this->uses = $uses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isRefreshFillable(): bool
|
||||
{
|
||||
return $this->refreshFillable;
|
||||
}
|
||||
|
||||
public function setRefreshFillable(bool $refreshFillable): ModelOption
|
||||
{
|
||||
$this->refreshFillable = $refreshFillable;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,28 @@ use Hyperf\Database\ConnectionInterface;
|
||||
use Hyperf\Database\ConnectionResolverInterface;
|
||||
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
|
||||
{
|
||||
public static function __callStatic($name, $arguments)
|
||||
|
@ -33,12 +33,12 @@ use Psr\Container\ContainerInterface;
|
||||
* @method static int affectingStatement(string $query, array $bindings = [])
|
||||
* @method static bool unprepared(string $query)
|
||||
* @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 rollBack()
|
||||
* @method static commit()
|
||||
* @method static int transactionLevel()
|
||||
* @method static array pretend(Closure $callback)
|
||||
* @method static array pretend(\Closure $callback)
|
||||
* @method static ConnectionInterface connection(string $pool)
|
||||
*/
|
||||
class Db
|
||||
|
@ -38,8 +38,11 @@ return [
|
||||
'middleware' => [
|
||||
'namespace' => 'App\\Middleware',
|
||||
],
|
||||
'Process' => [
|
||||
'process' => [
|
||||
'namespace' => 'App\\Process',
|
||||
],
|
||||
'request' => [
|
||||
'namespace' => 'App\\Request',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
@ -10,13 +10,11 @@ declare(strict_types=1);
|
||||
* @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\Devtool\Generator\GeneratorCommand;
|
||||
|
||||
/**
|
||||
* Class RequestCommand.
|
||||
* @Command
|
||||
*/
|
||||
class RequestCommand extends GeneratorCommand
|
||||
@ -29,11 +27,11 @@ class RequestCommand extends GeneratorCommand
|
||||
|
||||
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
|
||||
{
|
||||
return $this->getConfig()['namespace'] ?? 'App\\Requests';
|
||||
return $this->getConfig()['namespace'] ?? 'App\\Request';
|
||||
}
|
||||
}
|
@ -113,7 +113,9 @@ class VendorPublishCommand extends SymfonyCommand
|
||||
continue;
|
||||
}
|
||||
|
||||
mkdir(dirname($destination), 0755, true);
|
||||
if (! file_exists(dirname($destination))) {
|
||||
mkdir(dirname($destination), 0755, true);
|
||||
}
|
||||
copy($source, $destination);
|
||||
|
||||
$this->output->writeln(sprintf('<fg=green>[%s] publish [%s] success.</>', $package, $id));
|
||||
|
@ -13,6 +13,7 @@ declare(strict_types=1);
|
||||
namespace HyperfTest\Elasticsearch;
|
||||
|
||||
use Elasticsearch\ClientBuilder;
|
||||
use Elasticsearch\Common\Exceptions\NoNodesAvailableException;
|
||||
use Hyperf\Elasticsearch\ClientBuilderFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
@ -30,4 +31,15 @@ class ClientFactoryTest extends TestCase
|
||||
|
||||
$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();
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,9 @@ class AnnotationReader
|
||||
*/
|
||||
private $mode;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $methodAnnotationCache = [];
|
||||
|
||||
/**
|
||||
@ -101,25 +104,21 @@ class AnnotationReader
|
||||
|
||||
public function getRequestAnnotation(ReflectionMethod $refMethod, string $annotationName): ?AbstractRequest
|
||||
{
|
||||
/* @var null|AbstractRequest $queryAnnotation */
|
||||
return $this->getMethodAnnotation($refMethod, $annotationName);
|
||||
}
|
||||
|
||||
public function getLoggedAnnotation(ReflectionMethod $refMethod): ?Logged
|
||||
{
|
||||
/* @var null|Logged $loggedAnnotation */
|
||||
return $this->getMethodAnnotation($refMethod, Logged::class);
|
||||
}
|
||||
|
||||
public function getRightAnnotation(ReflectionMethod $refMethod): ?Right
|
||||
{
|
||||
/* @var null|Right $rightAnnotation */
|
||||
return $this->getMethodAnnotation($refMethod, Right::class);
|
||||
}
|
||||
|
||||
public function getFailWithAnnotation(ReflectionMethod $refMethod): ?FailWith
|
||||
{
|
||||
/* @var null|FailWith $failWithAnnotation */
|
||||
return $this->getMethodAnnotation($refMethod, FailWith::class);
|
||||
}
|
||||
|
||||
@ -128,13 +127,11 @@ class AnnotationReader
|
||||
*/
|
||||
public function getSourceFields(ReflectionClass $refClass): array
|
||||
{
|
||||
/* @var SourceField[] $sourceFields */
|
||||
return $this->getClassAnnotations($refClass, SourceField::class);
|
||||
}
|
||||
|
||||
public function getFactoryAnnotation(ReflectionMethod $refMethod): ?Factory
|
||||
{
|
||||
/* @var null|Factory $factoryAnnotation */
|
||||
return $this->getMethodAnnotation($refMethod, Factory::class);
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"HyperfTest\\GrpcServer\\": "tests/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
|
9
src/translation/tests/Cases/AbstractTestCase.php → src/grpc-server/src/Exception/GrpcException.php
Executable file → Normal file
9
src/translation/tests/Cases/AbstractTestCase.php → src/grpc-server/src/Exception/GrpcException.php
Executable file → Normal file
@ -10,13 +10,10 @@ declare(strict_types=1);
|
||||
* @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 AbstractTestCase.
|
||||
*/
|
||||
abstract class AbstractTestCase extends TestCase
|
||||
class GrpcException extends ServerException
|
||||
{
|
||||
}
|
@ -15,6 +15,8 @@ namespace Hyperf\GrpcServer\Exception\Handler;
|
||||
use Hyperf\Contract\StdoutLoggerInterface;
|
||||
use Hyperf\ExceptionHandler\ExceptionHandler;
|
||||
use Hyperf\ExceptionHandler\Formatter\FormatterInterface;
|
||||
use Hyperf\Grpc\StatusCode;
|
||||
use Hyperf\GrpcServer\Exception\GrpcException;
|
||||
use Hyperf\Server\Exception\ServerException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
@ -39,27 +41,28 @@ class GrpcExceptionHandler extends ExceptionHandler
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public function isValid(Throwable $throwable): bool
|
||||
{
|
||||
return $throwable instanceof ServerException;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')
|
||||
->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-message', (string) $message);
|
||||
|
98
src/grpc-server/tests/GrpcExceptionHandlerTest.php
Normal file
98
src/grpc-server/tests/GrpcExceptionHandlerTest.php
Normal 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;
|
||||
}
|
||||
}
|
24
src/grpc-server/tests/Stub/GrpcExceptionHandlerStub.php
Normal file
24
src/grpc-server/tests/Stub/GrpcExceptionHandlerStub.php
Normal 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
186
src/grpc/src/StatusCode.php
Normal 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,
|
||||
];
|
||||
}
|
@ -67,12 +67,7 @@ class CoroutineHandler
|
||||
|
||||
$ex = $this->checkStatusCode($client, $request);
|
||||
if ($ex !== true) {
|
||||
return [
|
||||
'status' => null,
|
||||
'reason' => null,
|
||||
'headers' => [],
|
||||
'error' => $ex,
|
||||
];
|
||||
return $this->getErrorResponse($ex, $btime, $effectiveUrl);
|
||||
}
|
||||
|
||||
return $this->getResponse($client, $btime, $effectiveUrl);
|
||||
@ -116,6 +111,24 @@ class CoroutineHandler
|
||||
$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)
|
||||
{
|
||||
return new CompletedFutureArray([
|
||||
|
@ -74,12 +74,7 @@ class PoolHandler extends CoroutineHandler
|
||||
if ($ex !== true) {
|
||||
$connection->close();
|
||||
$connection->release();
|
||||
return [
|
||||
'status' => null,
|
||||
'reason' => null,
|
||||
'headers' => [],
|
||||
'error' => $ex,
|
||||
];
|
||||
return $this->getErrorResponse($ex, $btime, $effectiveUrl);
|
||||
}
|
||||
|
||||
$response = $this->getResponse($client, $btime, $effectiveUrl);
|
||||
|
@ -61,6 +61,11 @@ class UploadedFile extends \SplFileInfo implements UploadedFileInterface
|
||||
*/
|
||||
private $size;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $mimeType;
|
||||
|
||||
/**
|
||||
* @param string $tmpFile
|
||||
* @param null|int $size
|
||||
@ -98,6 +103,14 @@ class UploadedFile extends \SplFileInfo implements UploadedFileInterface
|
||||
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.
|
||||
*
|
||||
|
@ -51,6 +51,6 @@ class HttpExceptionHandler extends ExceptionHandler
|
||||
*/
|
||||
public function isValid(Throwable $throwable): bool
|
||||
{
|
||||
return $throwable instanceof ServerException;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -118,6 +118,9 @@ class DispatcherFactory
|
||||
foreach ($methods as $method) {
|
||||
$path = $this->parsePath($prefix, $method);
|
||||
$methodName = $method->getName();
|
||||
if (substr($methodName, 0, 2) === '__') {
|
||||
continue;
|
||||
}
|
||||
$router->addRoute($autoMethods, $path, [$className, $methodName, $annotation->server]);
|
||||
|
||||
$methodMiddlewares = $middlewares;
|
||||
@ -177,7 +180,9 @@ class DispatcherFactory
|
||||
}
|
||||
$path = $mapping->path;
|
||||
|
||||
if ($path[0] !== '/') {
|
||||
if ($path === '') {
|
||||
$path = $prefix;
|
||||
} elseif ($path[0] !== '/') {
|
||||
$path = $prefix . '/' . $path;
|
||||
}
|
||||
$router->addRoute($mapping->methods, $path, [
|
||||
|
@ -12,6 +12,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace HyperfTest\HttpServer\Router;
|
||||
|
||||
use Hyperf\HttpServer\Annotation\AutoController;
|
||||
use HyperfTest\HttpServer\Stub\DemoController;
|
||||
use HyperfTest\HttpServer\Stub\DispatcherFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
@ -31,4 +33,21 @@ class DispatcherFactoryTest extends TestCase
|
||||
$res = $factory->getPrefix('App\\Controller\\Admin\\UserAuthController', '');
|
||||
$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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,17 @@ namespace HyperfTest\HttpServer\Stub;
|
||||
|
||||
class DemoController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -12,10 +12,23 @@ declare(strict_types=1);
|
||||
|
||||
namespace HyperfTest\HttpServer\Stub;
|
||||
|
||||
use Hyperf\HttpServer\Annotation\AutoController;
|
||||
use Hyperf\HttpServer\Router\RouteCollector;
|
||||
|
||||
class DispatcherFactory extends \Hyperf\HttpServer\Router\DispatcherFactory
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ class Paginator extends AbstractPaginator implements Arrayable, ArrayAccess, Cou
|
||||
if ($this->hasMorePages()) {
|
||||
return $this->url($this->currentPage() + 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,7 +81,7 @@ class RedisConnection extends BaseConnection implements ConnectionInterface
|
||||
throw new ConnectionException('Connection reconnect failed.');
|
||||
}
|
||||
|
||||
if (isset($auth)) {
|
||||
if (isset($auth) && $auth !== '') {
|
||||
$redis->auth($auth);
|
||||
}
|
||||
|
||||
|
1
src/server/.gitattributes
vendored
Normal file
1
src/server/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
/tests export-ignore
|
@ -12,14 +12,35 @@ declare(strict_types=1);
|
||||
|
||||
namespace Hyperf\Server\Listener;
|
||||
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Event\Contract\ListenerInterface;
|
||||
use Hyperf\Framework\Event\AfterWorkerStart;
|
||||
use Hyperf\Framework\Event\OnManagerStart;
|
||||
use Hyperf\Framework\Event\OnStart;
|
||||
use Hyperf\Process\Event\BeforeProcessHandle;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
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
|
||||
{
|
||||
return [
|
||||
@ -32,18 +53,35 @@ class InitProcessTitleListener implements ListenerInterface
|
||||
|
||||
public function process(object $event)
|
||||
{
|
||||
$array = [];
|
||||
if ($this->name !== '') {
|
||||
$array[] = $this->name;
|
||||
}
|
||||
|
||||
if ($event instanceof OnStart) {
|
||||
@cli_set_process_title('Master');
|
||||
$array[] = 'Master';
|
||||
} elseif ($event instanceof OnManagerStart) {
|
||||
@cli_set_process_title('Manager');
|
||||
$array[] = 'Manager';
|
||||
} elseif ($event instanceof AfterWorkerStart) {
|
||||
if ($event->server->taskworker) {
|
||||
@cli_set_process_title('TaskWorker.' . $event->workerId);
|
||||
$array[] = 'TaskWorker';
|
||||
$array[] = $event->workerId;
|
||||
} else {
|
||||
@cli_set_process_title('Worker.' . $event->workerId);
|
||||
$array[] = 'Worker';
|
||||
$array[] = $event->workerId;
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,21 @@ declare(strict_types=1);
|
||||
|
||||
namespace HyperfTest\Server\Listener;
|
||||
|
||||
use Hyperf\Config\Config;
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Framework\Event\AfterWorkerStart;
|
||||
use Hyperf\Framework\Event\OnManagerStart;
|
||||
use Hyperf\Framework\Event\OnStart;
|
||||
use Hyperf\Process\Event\BeforeProcessHandle;
|
||||
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 Psr\Container\ContainerInterface;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -25,9 +34,17 @@ use PHPUnit\Framework\TestCase;
|
||||
*/
|
||||
class InitProcessTitleListenerTest extends TestCase
|
||||
{
|
||||
protected function tearDown()
|
||||
{
|
||||
Mockery::close();
|
||||
}
|
||||
|
||||
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([
|
||||
OnStart::class,
|
||||
@ -36,4 +53,53 @@ class InitProcessTitleListenerTest extends TestCase
|
||||
BeforeProcessHandle::class,
|
||||
], $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'));
|
||||
}
|
||||
}
|
||||
|
@ -10,14 +10,15 @@ declare(strict_types=1);
|
||||
* @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
|
||||
{
|
||||
/**
|
||||
* Get the preferred locale of the entity.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function preferredLocale();
|
||||
public $name = 'test.demo';
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
}
|
||||
}
|
24
src/server/tests/Stub/InitProcessTitleListenerStub.php
Normal file
24
src/server/tests/Stub/InitProcessTitleListenerStub.php
Normal 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);
|
||||
}
|
||||
}
|
26
src/server/tests/Stub/InitProcessTitleListenerStub2.php
Normal file
26
src/server/tests/Stub/InitProcessTitleListenerStub2.php
Normal 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);
|
||||
}
|
||||
}
|
@ -18,8 +18,9 @@
|
||||
"phpunit/phpunit": "^7.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/container": "Required to use SnowflakeFactory.",
|
||||
"hyperf/config": "Required to read snowflake config."
|
||||
"psr/container": "Required to use MetaGeneratorFactory.",
|
||||
"hyperf/config": "Required to read snowflake config.",
|
||||
"hyperf/redis": "Required to use RedisMilliSecondMetaGenerator or RedisSecondMetaGenerator."
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -10,9 +10,16 @@ declare(strict_types=1);
|
||||
* @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 [
|
||||
'level' => IdGeneratorInterface::LEVEL_MILLISECOND,
|
||||
'begin_second' => IdGeneratorInterface::DEFAULT_SECOND,
|
||||
'begin_second' => MetaGeneratorInterface::DEFAULT_BEGIN_SECOND,
|
||||
RedisMilliSecondMetaGenerator::class => [
|
||||
'pool' => 'default',
|
||||
],
|
||||
RedisSecondMetaGenerator::class => [
|
||||
'pool' => 'default',
|
||||
],
|
||||
];
|
||||
|
74
src/snowflake/src/Config.php
Normal file
74
src/snowflake/src/Config.php
Normal 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;
|
||||
}
|
||||
}
|
36
src/snowflake/src/ConfigInterface.php
Normal file
36
src/snowflake/src/ConfigInterface.php
Normal 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;
|
||||
}
|
@ -12,14 +12,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace Hyperf\Snowflake;
|
||||
|
||||
use Hyperf\Snowflake\IdGenerator\SnowflakeIdGenerator;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
public function __invoke(): array
|
||||
{
|
||||
return [
|
||||
'dependencies' => [
|
||||
IdGeneratorInterface::class => SnowflakeFactory::class,
|
||||
MetaGeneratorInterface::class => RandomMetaGenerator::class,
|
||||
IdGeneratorInterface::class => SnowflakeIdGenerator::class,
|
||||
MetaGeneratorInterface::class => MetaGeneratorFactory::class,
|
||||
ConfigInterface::class => Config::class,
|
||||
],
|
||||
'commands' => [
|
||||
],
|
||||
|
75
src/snowflake/src/IdGenerator.php
Normal file
75
src/snowflake/src/IdGenerator.php
Normal 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;
|
||||
}
|
||||
}
|
8
src/translation/tests/bootstrap.php → src/snowflake/src/IdGenerator/SnowflakeIdGenerator.php
Executable file → Normal file
8
src/translation/tests/bootstrap.php → src/snowflake/src/IdGenerator/SnowflakeIdGenerator.php
Executable file → Normal file
@ -10,4 +10,10 @@ declare(strict_types=1);
|
||||
* @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
|
||||
{
|
||||
}
|
@ -14,12 +14,6 @@ namespace Hyperf\Snowflake;
|
||||
|
||||
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 degenerate(int $id): Meta;
|
||||
|
@ -12,89 +12,122 @@ declare(strict_types=1);
|
||||
|
||||
namespace Hyperf\Snowflake;
|
||||
|
||||
use Hyperf\Snowflake\Exception\SnowflakeException;
|
||||
|
||||
class Meta
|
||||
{
|
||||
const MILLISECOND_BITS = 41;
|
||||
|
||||
const DATA_CENTER_ID_BITS = 5;
|
||||
|
||||
const MACHINE_ID_BITS = 5;
|
||||
|
||||
const SEQUENCE_BITS = 12;
|
||||
|
||||
const MILLISECOND_BITS = 39;
|
||||
|
||||
const BUSINESS_ID_BITS = 4;
|
||||
|
||||
const DATA_CENTER_ID_BITS = 2;
|
||||
|
||||
const MACHINE_ID_BITS = 7;
|
||||
/**
|
||||
* @var int [0, 31]
|
||||
*/
|
||||
protected $dataCenterId;
|
||||
|
||||
/**
|
||||
* @var int [0, 15]
|
||||
* @var int [0, 31]
|
||||
*/
|
||||
public $businessId;
|
||||
|
||||
/**
|
||||
* @var int [0, 3]
|
||||
*/
|
||||
public $dataCenterId;
|
||||
|
||||
/**
|
||||
* @var int [0, 127]
|
||||
*/
|
||||
public $machineId;
|
||||
protected $workerId;
|
||||
|
||||
/**
|
||||
* @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->machineId = $machineId;
|
||||
$this->workerId = $workerId;
|
||||
$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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
85
src/snowflake/src/MetaGenerator.php
Normal file
85
src/snowflake/src/MetaGenerator.php
Normal 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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
68
src/snowflake/src/MetaGenerator/RedisSecondMetaGenerator.php
Normal file
68
src/snowflake/src/MetaGenerator/RedisSecondMetaGenerator.php
Normal 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)
|
||||
{
|
||||
}
|
||||
}
|
@ -13,19 +13,21 @@ declare(strict_types=1);
|
||||
namespace Hyperf\Snowflake;
|
||||
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Snowflake\ConfigInterface as SnowflakeConfigInterface;
|
||||
use Hyperf\Snowflake\MetaGenerator\RedisMilliSecondMetaGenerator;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class SnowflakeFactory
|
||||
class MetaGeneratorFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$config = $container->get(ConfigInterface::class);
|
||||
$level = $config->get('snowflake.level', IdGeneratorInterface::LEVEL_MILLISECOND);
|
||||
$beginSecond = $config->get('snowflake.begin_second', IdGeneratorInterface::DEFAULT_SECOND);
|
||||
$beginSecond = $config->get('snowflake.begin_second', MetaGeneratorInterface::DEFAULT_BEGIN_SECOND);
|
||||
|
||||
return make(Snowflake::class, [
|
||||
'level' => $level,
|
||||
'beginSecond' => $beginSecond,
|
||||
return make(RedisMilliSecondMetaGenerator::class, [
|
||||
$config,
|
||||
$container->get(SnowflakeConfigInterface::class),
|
||||
$beginSecond,
|
||||
]);
|
||||
}
|
||||
}
|
@ -14,5 +14,11 @@ namespace Hyperf\Snowflake;
|
||||
|
||||
interface MetaGeneratorInterface
|
||||
{
|
||||
const DEFAULT_BEGIN_SECOND = 1560960000;
|
||||
|
||||
public function generate(): Meta;
|
||||
|
||||
public function getBeginTimeStamp(): int;
|
||||
|
||||
public function getConfig(): ConfigInterface;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
192
src/snowflake/tests/RedisMetaGeneratorTest.php
Normal file
192
src/snowflake/tests/RedisMetaGeneratorTest.php
Normal 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;
|
||||
}
|
||||
}
|
66
src/snowflake/tests/SnowflakeGeneratorTest.php
Normal file
66
src/snowflake/tests/SnowflakeGeneratorTest.php
Normal 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.
|
||||
}
|
||||
}
|
40
src/snowflake/tests/Stub/UserDefinedIdGenerator.php
Normal file
40
src/snowflake/tests/Stub/UserDefinedIdGenerator.php
Normal 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);
|
||||
}
|
||||
}
|
@ -1,64 +1,43 @@
|
||||
# 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
|
||||
|
||||
```
|
||||
|
||||
## Config
|
||||
## Configuration
|
||||
|
||||
### Publish config
|
||||
|
||||
### publish config
|
||||
```
|
||||
php bin/hyperf.php vendor:publish hyperf/translation
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
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 [
|
||||
'locale' => 'en',
|
||||
'locale' => 'en',
|
||||
'fallback_locale' => '',
|
||||
'lang' => BASE_PATH . '/resources/lang',
|
||||
'path' => BASE_PATH . '/storage/languages',
|
||||
];
|
||||
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
```
|
||||
|
||||
$translator = $this->container->get(\Hyperf\Translation\Contracts\Translator::class);
|
||||
|
||||
$translator->trans('validation.accepted'),
|
||||
|
||||
|
||||
```php
|
||||
$container = ApplicationContext::getContainer();
|
||||
$translator = $container->get(\Hyperf\Contract\TranslatorInterface::class);
|
||||
$translator->trans('validation.accepted');
|
||||
```
|
@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "hyperf/translation",
|
||||
"type": "library",
|
||||
"description": "An independent translation component, forked by illuminate/translation.",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"translation",
|
||||
"hyperf"
|
||||
],
|
||||
"description": "",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/Functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Hyperf\\Translation\\": "src/"
|
||||
}
|
||||
@ -19,18 +22,15 @@
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"ext-swoole": ">=4.3",
|
||||
"hyperf/contract": "1.0.*",
|
||||
"hyperf/utils": "1.0.*",
|
||||
"hyperf/framework": "1.0.*",
|
||||
"hyperf/di": "1.0.*",
|
||||
"psr/container": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.14",
|
||||
"hyperf/testing": "1.0.*",
|
||||
"mockery/mockery": "^1.2",
|
||||
"phpstan/phpstan": "^0.10.5",
|
||||
"swoft/swoole-ide-helper": "^4.4.0"
|
||||
"phpstan/phpstan": "^0.10.5"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
|
@ -13,5 +13,5 @@ declare(strict_types=1);
|
||||
return [
|
||||
'locale' => 'zh_CN',
|
||||
'fallback_locale' => 'en',
|
||||
'lang' => BASE_PATH . '/resources/lang',
|
||||
'path' => BASE_PATH . '/storage/languages',
|
||||
];
|
||||
|
@ -12,9 +12,9 @@ declare(strict_types=1);
|
||||
|
||||
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.
|
||||
@ -25,13 +25,8 @@ class ArrayLoader implements Loader
|
||||
|
||||
/**
|
||||
* 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 ?: '*';
|
||||
|
||||
@ -40,9 +35,6 @@ class ArrayLoader implements Loader
|
||||
|
||||
/**
|
||||
* Add a new namespace to the loader.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param 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.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function addJsonPath(string $path)
|
||||
{
|
||||
@ -59,14 +49,8 @@ class ArrayLoader implements 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 ?: '*';
|
||||
|
||||
@ -77,8 +61,6 @@ class ArrayLoader implements Loader
|
||||
|
||||
/**
|
||||
* Get an array of all the registered namespaces.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function namespaces(): array
|
||||
{
|
||||
|
@ -12,8 +12,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Hyperf\Translation;
|
||||
|
||||
use Hyperf\Translation\Contracts\Loader;
|
||||
use Hyperf\Translation\Contracts\Translator;
|
||||
use Hyperf\Contract\TranslatorInterface;
|
||||
use Hyperf\Contract\TranslatorLoaderInterface;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
@ -21,8 +21,8 @@ class ConfigProvider
|
||||
{
|
||||
return [
|
||||
'dependencies' => [
|
||||
Loader::class => FileLoaderFactory::class,
|
||||
Translator::class => TranslatorFactory::class,
|
||||
TranslatorLoaderInterface::class => FileLoaderFactory::class,
|
||||
TranslatorInterface::class => TranslatorFactory::class,
|
||||
],
|
||||
'scan' => [
|
||||
'paths' => [
|
||||
|
@ -12,11 +12,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Hyperf\Translation;
|
||||
|
||||
use Hyperf\Translation\Contracts\Loader;
|
||||
use Hyperf\Contract\TranslatorLoaderInterface;
|
||||
use Hyperf\Utils\Filesystem\Filesystem;
|
||||
use RuntimeException;
|
||||
|
||||
class FileLoader implements Loader
|
||||
class FileLoader implements TranslatorLoaderInterface
|
||||
{
|
||||
/**
|
||||
* The filesystem instance.
|
||||
@ -60,13 +60,8 @@ class FileLoader implements Loader
|
||||
|
||||
/**
|
||||
* 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 === '*') {
|
||||
return $this->loadJsonPaths($locale);
|
||||
@ -81,9 +76,6 @@ class FileLoader implements Loader
|
||||
|
||||
/**
|
||||
* Add a new namespace to the loader.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param 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.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function addJsonPath(string $path)
|
||||
{
|
||||
@ -102,8 +92,6 @@ class FileLoader implements Loader
|
||||
|
||||
/**
|
||||
* Get an array of all the registered namespaces.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function namespaces(): array
|
||||
{
|
||||
@ -112,11 +100,6 @@ class FileLoader implements Loader
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -131,12 +114,6 @@ class FileLoader implements Loader
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -151,11 +128,6 @@ class FileLoader implements Loader
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -169,11 +141,9 @@ class FileLoader implements Loader
|
||||
/**
|
||||
* Load a locale from the given JSON file path.
|
||||
*
|
||||
* @param string $locale
|
||||
* @throws \RuntimeException
|
||||
* @return array
|
||||
*/
|
||||
protected function loadJsonPaths(string $locale)
|
||||
protected function loadJsonPaths(string $locale): iterable
|
||||
{
|
||||
return collect(array_merge($this->jsonPaths, [$this->path]))
|
||||
->reduce(function ($output, $path) use ($locale) {
|
||||
|
@ -22,7 +22,7 @@ class FileLoaderFactory
|
||||
{
|
||||
$config = $container->get(ConfigInterface::class);
|
||||
$files = $container->get(Filesystem::class);
|
||||
$path = $config->get('translation.lang');
|
||||
$path = $config->get('translation.path');
|
||||
|
||||
return make(FileLoader::class, compact('files', 'path'));
|
||||
}
|
||||
|
37
src/translation/src/Functions.php
Normal file
37
src/translation/src/Functions.php
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -13,22 +13,21 @@ declare(strict_types=1);
|
||||
namespace Hyperf\Translation;
|
||||
|
||||
use Countable;
|
||||
use Hyperf\Translation\Contracts\Loader;
|
||||
use Hyperf\Translation\Contracts\Translator as TranslatorContract;
|
||||
use Hyperf\Translation\Support\NamespacedItemResolver;
|
||||
use Hyperf\Contract\TranslatorInterface;
|
||||
use Hyperf\Contract\TranslatorLoaderInterface;
|
||||
use Hyperf\Utils\Arr;
|
||||
use Hyperf\Utils\Collection;
|
||||
use Hyperf\Utils\Str;
|
||||
use Hyperf\Utils\Traits\Macroable;
|
||||
|
||||
class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
class Translator implements TranslatorInterface
|
||||
{
|
||||
use Macroable;
|
||||
|
||||
/**
|
||||
* The loader implementation.
|
||||
*
|
||||
* @var \Hyperf\Translation\Contracts\Loader
|
||||
* @var TranslatorLoaderInterface
|
||||
*/
|
||||
protected $loader;
|
||||
|
||||
@ -61,12 +60,13 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
protected $selector;
|
||||
|
||||
/**
|
||||
* Create a new translator instance.
|
||||
* A cache of the parsed items.
|
||||
*
|
||||
* @param \Hyperf\Translation\Contracts\Loader $loader
|
||||
* @param string $locale
|
||||
* @var array
|
||||
*/
|
||||
public function __construct(Loader $loader, string $locale)
|
||||
protected $parsed = [];
|
||||
|
||||
public function __construct(TranslatorLoaderInterface $loader, string $locale)
|
||||
{
|
||||
$this->loader = $loader;
|
||||
$this->locale = $locale;
|
||||
@ -74,25 +74,16 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@ -100,12 +91,9 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
/**
|
||||
* Get the translation for a given key.
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $replace
|
||||
* @param null|string $locale
|
||||
* @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);
|
||||
}
|
||||
@ -113,13 +101,9 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
/**
|
||||
* Get the translation for the given key.
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $replace
|
||||
* @param null|string $locale
|
||||
* @param bool $fallback
|
||||
* @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);
|
||||
|
||||
@ -150,12 +134,9 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function getFromJson(string $key, array $replace = [], $locale = null)
|
||||
public function getFromJson(string $key, array $replace = [], ?string $locale = null)
|
||||
{
|
||||
$locale = $locale ?: $this->locale;
|
||||
|
||||
@ -183,13 +164,9 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
/**
|
||||
* Get a translation according to an integer value.
|
||||
*
|
||||
* @param string $key
|
||||
* @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);
|
||||
}
|
||||
@ -197,13 +174,9 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
/**
|
||||
* Get a translation according to an integer value.
|
||||
*
|
||||
* @param string $key
|
||||
* @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(
|
||||
$key,
|
||||
@ -228,10 +201,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* 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 = '*')
|
||||
{
|
||||
@ -244,10 +213,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* Load the specified language group.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param string $group
|
||||
* @param 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.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param 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.
|
||||
*
|
||||
* @param 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.
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public function parseKey(string $key): array
|
||||
{
|
||||
$segments = parent::parseKey($key);
|
||||
|
||||
if (is_null($segments[0])) {
|
||||
$segments[0] = '*';
|
||||
// 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];
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @return \Hyperf\Translation\MessageSelector
|
||||
*/
|
||||
public function getSelector()
|
||||
public function getSelector(): MessageSelector
|
||||
{
|
||||
if (! isset($this->selector)) {
|
||||
$this->selector = new MessageSelector();
|
||||
@ -317,8 +291,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* Set the message selector instance.
|
||||
*
|
||||
* @param \Hyperf\Translation\MessageSelector $selector
|
||||
*/
|
||||
public function setSelector(MessageSelector $selector)
|
||||
{
|
||||
@ -328,7 +300,7 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
/**
|
||||
* Get the language line loader implementation.
|
||||
*
|
||||
* @return \Hyperf\Translation\Contracts\Loader
|
||||
* @return TranslatorLoaderInterface
|
||||
*/
|
||||
public function getLoader()
|
||||
{
|
||||
@ -337,8 +309,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* Get the default locale being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function locale(): string
|
||||
{
|
||||
@ -347,8 +317,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* Get the default locale being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocale(): string
|
||||
{
|
||||
@ -357,8 +325,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* Set the default locale.
|
||||
*
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setLocale(string $locale)
|
||||
{
|
||||
@ -367,8 +333,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* Get the fallback locale being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFallback(): string
|
||||
{
|
||||
@ -377,8 +341,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* Set the fallback locale being used.
|
||||
*
|
||||
* @param string $fallback
|
||||
*/
|
||||
public function setFallback(string $fallback)
|
||||
{
|
||||
@ -387,8 +349,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* Set the loaded translation groups.
|
||||
*
|
||||
* @param 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
|
||||
* @return string
|
||||
* @param string $key
|
||||
* @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;
|
||||
}
|
||||
@ -409,11 +377,7 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
/**
|
||||
* Retrieve a language line out the loaded array.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param string $group
|
||||
* @param string $locale
|
||||
* @param mixed $item
|
||||
* @param array $replace
|
||||
* @return null|array|string
|
||||
*/
|
||||
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)) {
|
||||
return $this->makeReplacements($line, $replace);
|
||||
}
|
||||
|
||||
if (is_array($line) && count($line) > 0) {
|
||||
foreach ($line as $key => $value) {
|
||||
$line[$key] = $this->makeReplacements($value, $replace);
|
||||
@ -441,9 +406,8 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
/**
|
||||
* Make the place-holder replacements on a line.
|
||||
*
|
||||
* @param string $line
|
||||
* @param array $replace
|
||||
* @return string
|
||||
* @param array|string $line
|
||||
* @return array|string
|
||||
*/
|
||||
protected function makeReplacements($line, array $replace)
|
||||
{
|
||||
@ -468,9 +432,6 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
|
||||
|
||||
/**
|
||||
* Sort the replacements array.
|
||||
*
|
||||
* @param array $replace
|
||||
* @return 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.
|
||||
*
|
||||
* @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]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ declare(strict_types=1);
|
||||
namespace Hyperf\Translation;
|
||||
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Translation\Contracts\Loader;
|
||||
use Hyperf\Contract\TranslatorLoaderInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class TranslatorFactory
|
||||
@ -28,11 +28,11 @@ class TranslatorFactory
|
||||
$locale = $config->get('translation.locale');
|
||||
$fallbackLocale = $config->get('translation.fallback_locale');
|
||||
|
||||
$loader = $container->get(Loader::class);
|
||||
$loader = $container->get(TranslatorLoaderInterface::class);
|
||||
|
||||
$trans = make(Translator::class, compact('loader', 'locale'));
|
||||
$trans->setFallback((string) $fallbackLocale);
|
||||
$translator = make(Translator::class, compact('loader', 'locale'));
|
||||
$translator->setFallback((string) $fallbackLocale);
|
||||
|
||||
return $trans;
|
||||
return $translator;
|
||||
}
|
||||
}
|
||||
|
@ -10,26 +10,26 @@ declare(strict_types=1);
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace HyperfTest\Translation\Cases;
|
||||
namespace HyperfTest\Translation;
|
||||
|
||||
use Hyperf\Translation\FileLoader;
|
||||
use Hyperf\Utils\Filesystem\Filesystem;
|
||||
use Mockery as m;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class TranslationFileLoaderTest extends AbstractTestCase
|
||||
class FileLoaderTest extends TestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
m::close();
|
||||
Mockery::close();
|
||||
}
|
||||
|
||||
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('getRequire')->once()->with(__DIR__ . '/en/foo.php')->andReturn(['messages']);
|
||||
|
||||
@ -38,7 +38,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
|
||||
|
||||
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(__DIR__ . '/vendor/namespace/en/foo.php')->andReturn(false);
|
||||
$files->shouldReceive('getRequire')->once()->with('bar/en/foo.php')->andReturn(['foo' => 'bar']);
|
||||
@ -49,7 +49,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
|
||||
|
||||
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(__DIR__ . '/vendor/namespace/en/foo.php')->andReturn(true);
|
||||
$files->shouldReceive('getRequire')->once()->with('bar/en/foo.php')->andReturn(['foo' => 'bar']);
|
||||
@ -61,7 +61,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
|
||||
|
||||
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('getRequire')->never();
|
||||
|
||||
@ -70,7 +70,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
|
||||
|
||||
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();
|
||||
|
||||
$this->assertEquals([], $loader->load('en', 'foo', 'bar'));
|
||||
@ -78,7 +78,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
|
||||
|
||||
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('get')->once()->with(__DIR__ . '/en.json')->andReturn('{"foo":"bar"}');
|
||||
|
||||
@ -87,7 +87,7 @@ class TranslationFileLoaderTest extends AbstractTestCase
|
||||
|
||||
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');
|
||||
|
||||
$files->shouldReceive('exists')->once()->with(__DIR__ . '/en.json')->andReturn(true);
|
@ -10,15 +10,15 @@ declare(strict_types=1);
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace HyperfTest\Translation\Cases;
|
||||
namespace HyperfTest\Translation;
|
||||
|
||||
use Hyperf\Translation\MessageSelector;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class TranslationMessageSelectorTest extends AbstractTestCase
|
||||
class MessageSelectorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider chooseTestData
|
@ -10,58 +10,105 @@ declare(strict_types=1);
|
||||
* @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\Translator;
|
||||
use Hyperf\Utils\Collection;
|
||||
use Mockery as m;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class TranslationTranslatorTest extends AbstractTestCase
|
||||
class TranslatorTest extends TestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
m::close();
|
||||
Mockery::close();
|
||||
}
|
||||
|
||||
public function testHasMethodReturnsFalseWhenReturnedTranslationIsNull()
|
||||
{
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock();
|
||||
$t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'))->will($this->returnValue('foo'));
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
|
||||
$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'));
|
||||
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en', 'sp'])->getMock();
|
||||
$t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'))->will($this->returnValue('bar'));
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
|
||||
$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'));
|
||||
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock();
|
||||
$t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'), false)->will($this->returnValue('bar'));
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
|
||||
$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'));
|
||||
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock();
|
||||
$t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('bar'), false)->will($this->returnValue('foo'));
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
|
||||
$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'));
|
||||
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['load', 'getLine'])->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'));
|
||||
$t = $this->getMockBuilder(Translator::class)
|
||||
->setMethods(['load', 'getLine'])
|
||||
->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'));
|
||||
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['load', 'getLine'])->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'));
|
||||
$t = $this->getMockBuilder(Translator::class)
|
||||
->setMethods(['load', 'getLine'])
|
||||
->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'));
|
||||
}
|
||||
|
||||
public function testGetMethodProperlyLoadsAndRetrievesItem()
|
||||
{
|
||||
$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('breeze bar', $t->get('foo::bar.baz', ['foo' => 'bar'], 'en'));
|
||||
$this->assertEquals('foo', $t->get('foo::bar.foo'));
|
||||
@ -70,14 +117,24 @@ class TranslationTranslatorTest extends AbstractTestCase
|
||||
public function testTransMethodProperlyLoadsAndRetrievesItemWithHTMLInTheMessage()
|
||||
{
|
||||
$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'));
|
||||
}
|
||||
|
||||
public function testGetMethodProperlyLoadsAndRetrievesItemWithCapitalization()
|
||||
{
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(null)->setConstructorArgs([$this->getLoader(), 'en'])->getMock();
|
||||
$t->getLoader()->shouldReceive('load')->once()->with('en', 'bar', 'foo')->andReturn(['foo' => 'foo', 'baz' => 'breeze :Foo :BAR']);
|
||||
$t = $this->getMockBuilder(Translator::class)
|
||||
->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('foo', $t->get('foo::bar.foo'));
|
||||
}
|
||||
@ -85,9 +142,15 @@ class TranslationTranslatorTest extends AbstractTestCase
|
||||
public function testGetMethodProperlyLoadsAndRetrievesItemWithLongestReplacementsFirst()
|
||||
{
|
||||
$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 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'));
|
||||
}
|
||||
|
||||
@ -96,7 +159,10 @@ class TranslationTranslatorTest extends AbstractTestCase
|
||||
$t = new Translator($this->getLoader(), 'en');
|
||||
$t->setFallback('lv');
|
||||
$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('foo', $t->get('foo::bar.foo'));
|
||||
}
|
||||
@ -110,9 +176,15 @@ class TranslationTranslatorTest extends AbstractTestCase
|
||||
|
||||
public function testChoiceMethodProperlyLoadsAndRetrievesItem()
|
||||
{
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), '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 = m::mock(MessageSelector::class));
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
|
||||
$this->getLoader(),
|
||||
'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');
|
||||
|
||||
$t->choice('foo', 10, ['replace']);
|
||||
@ -120,9 +192,15 @@ class TranslationTranslatorTest extends AbstractTestCase
|
||||
|
||||
public function testChoiceMethodProperlyCountsCollectionsAndLoadsAndRetrievesItem()
|
||||
{
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([$this->getLoader(), '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 = m::mock(MessageSelector::class));
|
||||
$t = $this->getMockBuilder(Translator::class)->setMethods(['get'])->setConstructorArgs([
|
||||
$this->getLoader(),
|
||||
'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');
|
||||
|
||||
$values = ['foo', 'bar', 'baz'];
|
||||
@ -142,8 +220,16 @@ class TranslationTranslatorTest extends AbstractTestCase
|
||||
public function testGetJsonReplaces()
|
||||
{
|
||||
$t = new Translator($this->getLoader(), 'en');
|
||||
$t->getLoader()->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']));
|
||||
$t->getLoader()
|
||||
->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()
|
||||
@ -156,8 +242,15 @@ class TranslationTranslatorTest extends AbstractTestCase
|
||||
public function testGetJsonPreservesOrder()
|
||||
{
|
||||
$t = new Translator($this->getLoader(), 'en');
|
||||
$t->getLoader()->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']));
|
||||
$t->getLoader()
|
||||
->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()
|
||||
@ -194,6 +287,6 @@ class TranslationTranslatorTest extends AbstractTestCase
|
||||
|
||||
protected function getLoader()
|
||||
{
|
||||
return m::mock(Loader::class);
|
||||
return Mockery::mock(TranslatorLoaderInterface::class);
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
[opcache]
|
||||
opcache.enable_cli=1
|
||||
|
||||
[redis]
|
||||
extension = "redis.so"
|
||||
|
||||
[swoole]
|
||||
extension = "swoole.so"
|
@ -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
|
40
src/validation/src/Contracts/Support/MessageBag.php → src/utils/src/Contracts/MessageBag.php
Executable file → Normal file
40
src/validation/src/Contracts/Support/MessageBag.php → src/utils/src/Contracts/MessageBag.php
Executable file → Normal file
@ -10,27 +10,21 @@ declare(strict_types=1);
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function keys(): array;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -44,7 +38,6 @@ interface MessageBag extends Arrayable
|
||||
* Determine if messages exist for a given key.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key): bool;
|
||||
|
||||
@ -53,67 +46,48 @@ interface MessageBag extends Arrayable
|
||||
*
|
||||
* @param null|string $key
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMessages(): array;
|
||||
|
||||
/**
|
||||
* Get the default message format.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFormat(): string;
|
||||
|
||||
/**
|
||||
* Set the default message format.
|
||||
*
|
||||
* @param string $format
|
||||
* @return $this
|
||||
*/
|
||||
public function setFormat(string $format = ':message');
|
||||
|
||||
/**
|
||||
* Determine if the message bag has any messages.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool;
|
||||
|
||||
/**
|
||||
* Determine if the message bag has any messages.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool;
|
||||
|
||||
/**
|
||||
* Get the number of messages in the container.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int;
|
||||
}
|
6
src/validation/src/Contracts/Support/MessageProvider.php → src/utils/src/Contracts/MessageProvider.php
Executable file → Normal file
6
src/validation/src/Contracts/Support/MessageProvider.php → src/utils/src/Contracts/MessageProvider.php
Executable file → Normal file
@ -10,14 +10,12 @@ declare(strict_types=1);
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Validation\Contracts\Support;
|
||||
namespace Hyperf\Utils\Contracts;
|
||||
|
||||
interface MessageProvider
|
||||
{
|
||||
/**
|
||||
* Get the messages for the instance.
|
||||
*
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getMessageBag();
|
||||
public function getMessageBag(): MessageBag;
|
||||
}
|
@ -43,10 +43,17 @@ class Coroutine
|
||||
/**
|
||||
* Returns the parent coroutine ID.
|
||||
* 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
|
||||
{
|
||||
return SwooleCoroutine::getPcid();
|
||||
$cid = SwooleCoroutine::getPcid();
|
||||
if ($cid === false) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return $cid;
|
||||
}
|
||||
|
||||
/**
|
||||
|
89
src/validation/src/Support/MessageBag.php → src/utils/src/MessageBag.php
Executable file → Normal file
89
src/validation/src/Support/MessageBag.php → src/utils/src/MessageBag.php
Executable file → Normal file
@ -10,15 +10,13 @@ declare(strict_types=1);
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Validation\Support;
|
||||
namespace Hyperf\Utils;
|
||||
|
||||
use Countable;
|
||||
use Hyperf\Utils\Arr;
|
||||
use Hyperf\Utils\Contracts\Arrayable;
|
||||
use Hyperf\Utils\Contracts\Jsonable;
|
||||
use Hyperf\Utils\Str;
|
||||
use Hyperf\Validation\Contracts\Support\MessageBag as MessageBagContract;
|
||||
use Hyperf\Validation\Contracts\Support\MessageProvider;
|
||||
use Hyperf\Utils\Contracts\MessageBag as MessageBagContract;
|
||||
use Hyperf\Utils\Contracts\MessageProvider;
|
||||
use JsonSerializable;
|
||||
|
||||
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.
|
||||
*
|
||||
* @param 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.
|
||||
*
|
||||
* @return 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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function keys(): array
|
||||
{
|
||||
@ -73,12 +65,8 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
|
||||
|
||||
/**
|
||||
* 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)) {
|
||||
$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.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @return 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.
|
||||
*
|
||||
* @param array|string $keys
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAny($keys = []): bool
|
||||
{
|
||||
@ -159,7 +145,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $format
|
||||
* @return 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.
|
||||
*
|
||||
* @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
|
||||
// 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.
|
||||
*
|
||||
* @param string $format
|
||||
* @return array
|
||||
*/
|
||||
public function all($format = null): array
|
||||
public function all(?string $format = null): array
|
||||
{
|
||||
$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.
|
||||
*
|
||||
* @param string $format
|
||||
* @return array
|
||||
*/
|
||||
public function unique($format = null): array
|
||||
public function unique(?string $format = null): array
|
||||
{
|
||||
return array_unique($this->all($format));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw messages in the message bag.
|
||||
*
|
||||
* @return 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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMessages(): array
|
||||
{
|
||||
@ -249,18 +220,14 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
|
||||
|
||||
/**
|
||||
* Get the messages for the instance.
|
||||
*
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getMessageBag()
|
||||
public function getMessageBag(): MessageBagContract
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default message format.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFormat(): string
|
||||
{
|
||||
@ -269,11 +236,8 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@ -282,8 +246,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
|
||||
|
||||
/**
|
||||
* Determine if the message bag has any messages.
|
||||
*
|
||||
* @return 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.
|
||||
*
|
||||
* @return 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.
|
||||
*
|
||||
* @return 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.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
@ -322,8 +278,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
|
||||
|
||||
/**
|
||||
* Get the instance as an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
@ -332,8 +286,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
|
||||
|
||||
/**
|
||||
* Convert the object into something JSON serializable.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
@ -342,9 +294,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
|
||||
|
||||
/**
|
||||
* Convert the object to its JSON representation.
|
||||
*
|
||||
* @param int $options
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $message
|
||||
* @return 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.
|
||||
*
|
||||
* @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)
|
||||
->filter(function ($messages, $messageKey) use ($key) {
|
||||
@ -389,11 +330,6 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -408,11 +344,8 @@ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, Me
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
|
||||
## About
|
||||
|
||||
hyperf/validation 是对Laravel Validation的移植(不包含门面部分),具体使用方法可以参考Laravel Validation 的使用。
|
||||
[hyperf/validation](https://github.com/hyperf-cloud/validation) 组件衍生于 `Laravel Validation` 组件的,我们对它进行了一些改造,大部分功能保持了相同。在这里感谢一下 Laravel 开发组,实现了如此强大好用的 Validation 组件。
|
||||
|
||||
## Install
|
||||
|
||||
@ -17,7 +17,7 @@ composer require hyperf/validation
|
||||
|
||||
### publish config
|
||||
```
|
||||
php bin/hyperf.php vendor:publish hyperf/translation
|
||||
php bin/hyperf.php vendor:publish hyperf/validation
|
||||
|
||||
```
|
||||
|
||||
|
@ -20,19 +20,18 @@
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"ext-swoole": ">=4.3",
|
||||
"hyperf/translation": "^1.0",
|
||||
"egulias/email-validator": "^2.1",
|
||||
"hyperf/command": "^1.0",
|
||||
"hyperf/database": "^1.0",
|
||||
"hyperf/devtool": "^1.0",
|
||||
"hyperf/di": "1.0.*",
|
||||
"hyperf/framework": "1.0.*",
|
||||
"hyperf/command": "~1.0.0",
|
||||
"hyperf/database": "~1.0.0",
|
||||
"hyperf/devtool": "~1.0.0",
|
||||
"hyperf/di": "~1.0.0",
|
||||
"hyperf/framework": "~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",
|
||||
"psr/container": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"symfony/http-foundation": "^4.3"
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.14",
|
||||
|
@ -69,10 +69,8 @@ class ClosureValidationRule implements RuleContract
|
||||
|
||||
/**
|
||||
* Get the validation error message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
public function message(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
@ -13,9 +13,10 @@ declare(strict_types=1);
|
||||
namespace Hyperf\Validation\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Hyperf\HttpMessage\Upload\UploadedFile;
|
||||
use Hyperf\Utils\Arr;
|
||||
use Hyperf\Utils\Str;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Hyperf\Validation\Validator;
|
||||
|
||||
trait FormatsMessages
|
||||
{
|
||||
@ -23,12 +24,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -51,9 +46,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* Get the displayable name of the attribute.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayableAttribute(string $attribute): string
|
||||
{
|
||||
@ -112,10 +104,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -167,8 +155,6 @@ trait FormatsMessages
|
||||
/**
|
||||
* Get the proper inline error message for standard and size rules.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param string $rule
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getInlineMessage(string $attribute, string $rule)
|
||||
@ -183,8 +169,6 @@ trait FormatsMessages
|
||||
/**
|
||||
* Get the inline message for a rule if it exists.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param string $lowerRule
|
||||
* @param null|array $source
|
||||
* @return null|string
|
||||
*/
|
||||
@ -208,9 +192,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* Get the custom error message from translator.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function getCustomMessageFromTranslator(string $key): string
|
||||
{
|
||||
@ -234,11 +215,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -253,10 +229,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -274,9 +246,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* Get the data type of the given attribute.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return string
|
||||
*/
|
||||
protected function getAttributeType(string $attribute): string
|
||||
{
|
||||
@ -298,9 +267,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* Get the given attribute from the attribute translations.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function getAttributeFromTranslations(string $name): string
|
||||
{
|
||||
@ -309,10 +275,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* Replace the :attribute placeholder in the given message.
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function replaceAttributePlaceholder(string $message, string $value): string
|
||||
{
|
||||
@ -325,10 +287,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* Replace the :input placeholder in the given message.
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $attribute
|
||||
* @return string
|
||||
*/
|
||||
protected function replaceInputPlaceholder(string $message, string $attribute): string
|
||||
{
|
||||
@ -343,9 +301,6 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* Transform an array of attributes to their displayable form.
|
||||
*
|
||||
* @param array $values
|
||||
* @return array
|
||||
*/
|
||||
protected function getAttributeList(array $values): array
|
||||
{
|
||||
@ -363,15 +318,8 @@ trait FormatsMessages
|
||||
|
||||
/**
|
||||
* 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];
|
||||
|
||||
@ -386,18 +334,12 @@ trait FormatsMessages
|
||||
/**
|
||||
* 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
|
||||
* @return string
|
||||
*/
|
||||
protected function callClassBasedReplacer(string $callback, string $message, string $attribute, string $rule, array $parameters, $validator): string
|
||||
{
|
||||
[$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));
|
||||
}
|
||||
}
|
||||
|
@ -18,12 +18,6 @@ trait ReplacesAttributes
|
||||
{
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -32,12 +26,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -46,12 +34,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -60,12 +42,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -74,12 +50,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -88,12 +58,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -102,26 +66,14 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -134,12 +86,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -148,12 +94,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -162,12 +102,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -176,12 +110,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -190,12 +118,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -204,12 +126,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -218,12 +134,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -232,12 +142,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -246,12 +150,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -260,12 +158,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -278,12 +170,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -296,12 +182,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -314,12 +194,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -332,12 +206,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -350,12 +218,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -372,12 +234,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -386,12 +242,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -404,12 +254,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -418,12 +262,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -432,12 +270,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -446,12 +278,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -460,12 +286,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -482,12 +302,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -500,12 +314,6 @@ trait ReplacesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
|
@ -21,14 +21,14 @@ use DateTimeZone;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\Validation\RFCValidation;
|
||||
use Exception;
|
||||
use Hyperf\HttpMessage\Upload\UploadedFile;
|
||||
use Hyperf\Utils\Arr;
|
||||
use Hyperf\Utils\Str;
|
||||
use Hyperf\Validation\Rules\Exists;
|
||||
use Hyperf\Validation\Rules\Unique;
|
||||
use Hyperf\Validation\ValidationData;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use SplFileInfo;
|
||||
use Throwable;
|
||||
|
||||
trait ValidatesAttributes
|
||||
@ -38,9 +38,7 @@ trait ValidatesAttributes
|
||||
*
|
||||
* This validation rule implies the attribute is "required".
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateAccepted(string $attribute, $value): bool
|
||||
{
|
||||
@ -52,9 +50,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is an active URL.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateActiveUrl(string $attribute, $value): bool
|
||||
{
|
||||
@ -77,8 +73,6 @@ trait ValidatesAttributes
|
||||
* "Break" on first validation fail.
|
||||
*
|
||||
* Always returns true, just lets us put "bail" in rules.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateBail(): bool
|
||||
{
|
||||
@ -88,10 +82,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate the date is before a given date.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateBeforeOrEqual(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -118,10 +106,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate the date is after a given date.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateAfterOrEqual(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -148,9 +130,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute contains only alphabetic characters.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateAlphaDash(string $attribute, $value): bool
|
||||
{
|
||||
@ -176,9 +154,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute contains only alpha-numeric characters.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateAlphaNum(string $attribute, $value): bool
|
||||
{
|
||||
@ -192,9 +168,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is an array.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateBetween(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -221,9 +192,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is a boolean.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateBoolean(string $attribute, $value): bool
|
||||
{
|
||||
@ -235,9 +204,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute has a matching confirmation.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateConfirmed(string $attribute, $value): bool
|
||||
{
|
||||
@ -247,9 +214,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is a valid date.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateDate(string $attribute, $value): bool
|
||||
{
|
||||
@ -276,10 +241,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute matches a date format.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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())) {
|
||||
return false;
|
||||
}
|
||||
@ -407,10 +350,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate an attribute is unique among other values.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateExists(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -466,7 +401,7 @@ trait ValidatesAttributes
|
||||
$column,
|
||||
$value,
|
||||
$parameters
|
||||
) >= $expected;
|
||||
) >= $expected;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -474,10 +409,7 @@ trait ValidatesAttributes
|
||||
*
|
||||
* If a database column is not specified, the attribute will be used.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateUnique(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -518,28 +450,21 @@ trait ValidatesAttributes
|
||||
$id,
|
||||
$idColumn,
|
||||
$extra
|
||||
) == 0;
|
||||
) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'
|
||||
? $parameters[1] : $this->guessColumnForQuery($attribute);
|
||||
@ -547,9 +472,6 @@ trait ValidatesAttributes
|
||||
|
||||
/**
|
||||
* Guess the database column from the given attribute name.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return string
|
||||
*/
|
||||
public function guessColumnForQuery(string $attribute): string
|
||||
{
|
||||
@ -564,9 +486,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate the given value is a valid file.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateFile(string $attribute, $value): bool
|
||||
{
|
||||
@ -576,9 +496,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate the given attribute is filled if it is present.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateFilled(string $attribute, $value): bool
|
||||
{
|
||||
@ -592,10 +510,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is greater than another attribute.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateImage(string $attribute, $value): bool
|
||||
{
|
||||
@ -712,10 +616,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate an attribute is contained within a list of values.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateInArray(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -758,9 +656,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is an integer.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateInteger(string $attribute, $value): bool
|
||||
{
|
||||
@ -770,9 +666,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is a valid IP.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateIp(string $attribute, $value): bool
|
||||
{
|
||||
@ -782,9 +676,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is a valid IPv4.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateIpv4(string $attribute, $value): bool
|
||||
{
|
||||
@ -794,9 +686,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is a valid IPv6.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateIpv6(string $attribute, $value): bool
|
||||
{
|
||||
@ -806,9 +696,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate the attribute is a valid JSON string.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
* @param SplFileInfo $value
|
||||
*/
|
||||
public function validateMimes(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -858,16 +740,25 @@ trait ValidatesAttributes
|
||||
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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
* @param SplFileInfo $value
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateMin(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -903,8 +791,6 @@ trait ValidatesAttributes
|
||||
* "Indicate" validation should pass if value is null.
|
||||
*
|
||||
* Always returns true, just lets us put "nullable" in rules.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateNullable(): bool
|
||||
{
|
||||
@ -914,10 +800,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate an attribute is not contained within a list of values.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateNotIn(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -927,9 +810,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is numeric.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateNumeric(string $attribute, $value): bool
|
||||
{
|
||||
@ -939,9 +820,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute exists even if not filled.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validatePresent(string $attribute, $value): bool
|
||||
{
|
||||
@ -951,10 +830,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute passes a regular expression check.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateNotRegex(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -989,9 +862,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that a required attribute exists.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateRequired(string $attribute, $value): bool
|
||||
{
|
||||
@ -1004,7 +875,7 @@ trait ValidatesAttributes
|
||||
if ((is_array($value) || $value instanceof Countable) && count($value) < 1) {
|
||||
return false;
|
||||
}
|
||||
if ($value instanceof File) {
|
||||
if ($value instanceof SplFileInfo) {
|
||||
return (string) $value->getPath() !== '';
|
||||
}
|
||||
|
||||
@ -1014,10 +885,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute exists when another attribute has a given value.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateRequiredWithoutAll(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -1132,10 +985,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that two attributes match.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateSame(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -1149,10 +999,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate the size of an attribute.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateSize(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -1165,8 +1012,6 @@ trait ValidatesAttributes
|
||||
* "Validate" optional attributes.
|
||||
*
|
||||
* Always returns true, just lets us put sometimes in rules.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateSometimes()
|
||||
{
|
||||
@ -1176,10 +1021,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate the attribute starts with a given substring.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateStartsWith(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -1189,10 +1031,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate the attribute ends with a given substring.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateEndsWith(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
@ -1202,9 +1041,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is a string.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateString(string $attribute, $value): bool
|
||||
{
|
||||
@ -1214,9 +1051,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is a valid timezone.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateTimezone(string $attribute, $value): bool
|
||||
{
|
||||
@ -1234,9 +1069,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is a valid URL.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateUrl(string $attribute, $value): bool
|
||||
{
|
||||
@ -1271,9 +1104,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Validate that an attribute is a valid UUID.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function validateUuid(string $attribute, $value): bool
|
||||
{
|
||||
@ -1288,7 +1119,6 @@ trait ValidatesAttributes
|
||||
* Check that the given value is a valid file instance.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValidFileInstance($value): bool
|
||||
{
|
||||
@ -1296,16 +1126,12 @@ trait ValidatesAttributes
|
||||
return false;
|
||||
}
|
||||
|
||||
return $value instanceof File;
|
||||
return $value instanceof SplFileInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require a certain number of parameters to be present.
|
||||
*
|
||||
* @param int $count
|
||||
* @param array $parameters
|
||||
* @param string $rule
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function requireParameterCount(int $count, array $parameters, string $rule)
|
||||
@ -1318,11 +1144,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Compare a given date against another using an operator.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @param string $operator
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getDateFormat(string $attribute)
|
||||
@ -1358,7 +1179,7 @@ trait ValidatesAttributes
|
||||
* Get the date timestamp.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return int
|
||||
* @return bool|int
|
||||
*/
|
||||
protected function getDateTimestamp($value)
|
||||
{
|
||||
@ -1379,12 +1200,6 @@ trait ValidatesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -1400,8 +1215,6 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Get a DateTime instance from a string.
|
||||
*
|
||||
* @param string $format
|
||||
* @param string $value
|
||||
* @return null|\DateTime
|
||||
*/
|
||||
protected function getDateTimeWithOptionalFormat(string $format, string $value)
|
||||
@ -1416,7 +1229,6 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Get a DateTime instance from a string with no format.
|
||||
*
|
||||
* @param string $value
|
||||
* @return null|\DateTime
|
||||
*/
|
||||
protected function getDateTime(string $value)
|
||||
@ -1435,22 +1247,16 @@ trait ValidatesAttributes
|
||||
* Check if the given value should be adjusted to Carbon::getTestNow().
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
protected function isTestingRelativeDateTime($value): bool
|
||||
{
|
||||
return Carbon::hasTestNow() && is_string($value) && (
|
||||
$value === 'now' || Carbon::hasRelativeKeywords($value)
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -1464,11 +1270,6 @@ trait ValidatesAttributes
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -1488,9 +1289,6 @@ trait ValidatesAttributes
|
||||
|
||||
/**
|
||||
* Get the values to distinct between.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return array
|
||||
*/
|
||||
protected function getDistinctValues(string $attribute): array
|
||||
{
|
||||
@ -1509,9 +1307,6 @@ trait ValidatesAttributes
|
||||
|
||||
/**
|
||||
* Extract the distinct values from the data.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return array
|
||||
*/
|
||||
protected function extractDistinctValues(string $attribute): array
|
||||
{
|
||||
@ -1531,11 +1326,7 @@ trait ValidatesAttributes
|
||||
* Get the number of records that exist in storage.
|
||||
*
|
||||
* @param mixed $connection
|
||||
* @param string $table
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return 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.
|
||||
*
|
||||
* @param array $parameters
|
||||
* @return array
|
||||
*/
|
||||
protected function getUniqueIds(array $parameters): array
|
||||
{
|
||||
@ -1592,9 +1380,6 @@ trait ValidatesAttributes
|
||||
|
||||
/**
|
||||
* Get the extra conditions for a unique rule.
|
||||
*
|
||||
* @param array $parameters
|
||||
* @return array
|
||||
*/
|
||||
protected function getUniqueExtra(array $parameters): array
|
||||
{
|
||||
@ -1607,9 +1392,6 @@ trait ValidatesAttributes
|
||||
|
||||
/**
|
||||
* Get the extra conditions for a unique / exists rule.
|
||||
*
|
||||
* @param array $segments
|
||||
* @return array
|
||||
*/
|
||||
protected function getExtraConditions(array $segments): array
|
||||
{
|
||||
@ -1627,9 +1409,7 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Check if PHP uploads are explicitly allowed.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return bool
|
||||
* @param SplFileInfo $value
|
||||
*/
|
||||
protected function shouldBlockPhpUpload($value, array $parameters): bool
|
||||
{
|
||||
@ -1641,16 +1421,11 @@ trait ValidatesAttributes
|
||||
'php', 'php3', 'php4', 'php5', 'phtml',
|
||||
];
|
||||
|
||||
return ($value instanceof UploadedFile)
|
||||
? in_array(trim(strtolower($value->getClientOriginalExtension())), $phpExtensions)
|
||||
: in_array(trim(strtolower($value->getExtension())), $phpExtensions);
|
||||
return in_array(trim(strtolower($value->getExtension())), $phpExtensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given values to boolean if they are string "true" / "false".
|
||||
*
|
||||
* @param array $values
|
||||
* @return array
|
||||
*/
|
||||
protected function convertValuesToBoolean(array $values): array
|
||||
{
|
||||
@ -1668,9 +1443,6 @@ trait ValidatesAttributes
|
||||
|
||||
/**
|
||||
* Determine if any of the given attributes fail the required test.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @return bool
|
||||
*/
|
||||
protected function anyFailingRequired(array $attributes): bool
|
||||
{
|
||||
@ -1685,9 +1457,6 @@ trait ValidatesAttributes
|
||||
|
||||
/**
|
||||
* Determine if all of the given attributes fail the required test.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @return bool
|
||||
*/
|
||||
protected function allFailingRequired(array $attributes): bool
|
||||
{
|
||||
@ -1703,7 +1472,6 @@ trait ValidatesAttributes
|
||||
/**
|
||||
* Get the size of an attribute.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
@ -1721,7 +1489,7 @@ trait ValidatesAttributes
|
||||
if (is_array($value)) {
|
||||
return count($value);
|
||||
}
|
||||
if ($value instanceof File) {
|
||||
if ($value instanceof SplFileInfo) {
|
||||
return $value->getSize() / 1024;
|
||||
}
|
||||
|
||||
@ -1733,7 +1501,6 @@ trait ValidatesAttributes
|
||||
*
|
||||
* @param mixed $first
|
||||
* @param mixed $second
|
||||
* @param string $operator
|
||||
* @throws \InvalidArgumentException
|
||||
* @return bool
|
||||
*/
|
||||
@ -1757,11 +1524,8 @@ trait ValidatesAttributes
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
[$key, $value] = array_pad(explode('=', $item, 2), 2, null);
|
||||
@ -1777,7 +1541,6 @@ trait ValidatesAttributes
|
||||
*
|
||||
* @param mixed $first
|
||||
* @param mixed $second
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param string $rule
|
||||
*/
|
||||
protected function shouldBeNumeric(string $attribute, string $rule)
|
||||
{
|
||||
|
@ -12,15 +12,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace Hyperf\Validation;
|
||||
|
||||
use Hyperf\Validation\Contracts\Validation\Factory as FactoryInterface;
|
||||
use Hyperf\Contract\ValidatorInterface;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
public function __invoke(): array
|
||||
{
|
||||
return [
|
||||
'dependencies' => [
|
||||
\Hyperf\Validation\Contracts\Validation\Validator::class => \Hyperf\Validation\ValidatorFactory::class,
|
||||
\Hyperf\Validation\PresenceVerifierInterface::class => \Hyperf\Validation\DatabasePresenceVerifierFactory::class,
|
||||
\Hyperf\Validation\Contracts\Validation\Factory::class => \Hyperf\Validation\ValidatorFactory::class,
|
||||
ValidatorInterface::class => ValidatorFactory::class,
|
||||
PresenceVerifierInterface::class => DatabasePresenceVerifierFactory::class,
|
||||
FactoryInterface::class => Factory::class,
|
||||
],
|
||||
'scan' => [
|
||||
'paths' => [
|
||||
|
@ -17,18 +17,13 @@ interface Factory
|
||||
/**
|
||||
* Create a new Validator instance.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
* @param array $messages
|
||||
* @param array $customAttributes
|
||||
* @return \Hyperf\Validation\Contracts\Validation\Validator
|
||||
* @return \Hyperf\Contract\ValidatorInterface
|
||||
*/
|
||||
public function make(array $data, array $rules, array $messages = [], array $customAttributes = []);
|
||||
|
||||
/**
|
||||
* Register a custom validator extension.
|
||||
*
|
||||
* @param string $rule
|
||||
* @param \Closure|string $extension
|
||||
* @param null|string $message
|
||||
*/
|
||||
@ -37,7 +32,6 @@ interface Factory
|
||||
/**
|
||||
* Register a custom implicit validator extension.
|
||||
*
|
||||
* @param string $rule
|
||||
* @param \Closure|string $extension
|
||||
* @param null|string $message
|
||||
*/
|
||||
@ -46,7 +40,6 @@ interface Factory
|
||||
/**
|
||||
* Register a custom implicit validator message replacer.
|
||||
*
|
||||
* @param string $rule
|
||||
* @param \Closure|string $replacer
|
||||
*/
|
||||
public function replacer(string $rule, $replacer);
|
||||
|
@ -17,9 +17,7 @@ interface Rule
|
||||
/**
|
||||
* Determine if the validation rule passes.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function passes(string $attribute, $value): bool;
|
||||
|
||||
|
@ -45,13 +45,9 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
|
||||
/**
|
||||
* 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|string $idColumn
|
||||
* @param array $extra
|
||||
* @return 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.
|
||||
*
|
||||
* @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
|
||||
{
|
||||
@ -83,7 +73,6 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
|
||||
/**
|
||||
* Get a query builder for the given table.
|
||||
*
|
||||
* @param string $table
|
||||
* @return \Hyperf\Database\Query\Builder
|
||||
*/
|
||||
public function table(string $table)
|
||||
@ -93,10 +82,8 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
|
||||
|
||||
/**
|
||||
* Set the connection to be used.
|
||||
*
|
||||
* @param string $connection
|
||||
*/
|
||||
public function setConnection($connection)
|
||||
public function setConnection(?string $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
@ -105,7 +92,6 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
|
||||
* Add the given conditions to the query.
|
||||
*
|
||||
* @param \Hyperf\Database\Query\Builder $query
|
||||
* @param array $conditions
|
||||
* @return \Hyperf\Database\Query\Builder
|
||||
*/
|
||||
protected function addConditions($query, array $conditions)
|
||||
@ -127,7 +113,6 @@ class DatabasePresenceVerifier implements PresenceVerifierInterface
|
||||
* Add a "where" clause to the given query.
|
||||
*
|
||||
* @param \Hyperf\Database\Query\Builder $query
|
||||
* @param string $key
|
||||
* @param string $extraValue
|
||||
*/
|
||||
protected function addWhere($query, string $key, $extraValue)
|
||||
|
@ -13,17 +13,17 @@ declare(strict_types=1);
|
||||
namespace Hyperf\Validation;
|
||||
|
||||
use Closure;
|
||||
use Hyperf\Di\Container;
|
||||
use Hyperf\Translation\Contracts\Translator;
|
||||
use Hyperf\Contract\TranslatorInterface;
|
||||
use Hyperf\Utils\Str;
|
||||
use Hyperf\Validation\Contracts\Validation\Factory as FactoryContract;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class Factory implements FactoryContract
|
||||
{
|
||||
/**
|
||||
* The Translator implementation.
|
||||
*
|
||||
* @var \Hyperf\Translation\Contracts\Translator
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
@ -37,7 +37,7 @@ class Factory implements FactoryContract
|
||||
/**
|
||||
* The IoC container instance.
|
||||
*
|
||||
* @var Container
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
@ -85,11 +85,8 @@ class Factory implements FactoryContract
|
||||
|
||||
/**
|
||||
* 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->translator = $translator;
|
||||
@ -98,10 +95,6 @@ class Factory implements FactoryContract
|
||||
/**
|
||||
* Create a new Validator instance.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
* @param array $messages
|
||||
* @param array $customAttributes
|
||||
* @return \Hyperf\Validation\Validator
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
* @param array $messages
|
||||
* @param array $customAttributes
|
||||
* @throws \Hyperf\Validation\ValidationException
|
||||
* @return 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.
|
||||
*
|
||||
* @param string $rule
|
||||
* @param \Closure|string $extension
|
||||
* @param null|string $message
|
||||
*/
|
||||
@ -166,7 +153,6 @@ class Factory implements FactoryContract
|
||||
/**
|
||||
* Register a custom implicit validator extension.
|
||||
*
|
||||
* @param string $rule
|
||||
* @param \Closure|string $extension
|
||||
* @param null|string $message
|
||||
*/
|
||||
@ -182,7 +168,6 @@ class Factory implements FactoryContract
|
||||
/**
|
||||
* Register a custom dependent validator extension.
|
||||
*
|
||||
* @param string $rule
|
||||
* @param \Closure|string $extension
|
||||
* @param null|string $message
|
||||
*/
|
||||
@ -198,7 +183,6 @@ class Factory implements FactoryContract
|
||||
/**
|
||||
* Register a custom validator message replacer.
|
||||
*
|
||||
* @param string $rule
|
||||
* @param \Closure|string $replacer
|
||||
*/
|
||||
public function replacer(string $rule, $replacer)
|
||||
@ -219,7 +203,7 @@ class Factory implements FactoryContract
|
||||
/**
|
||||
* Get the Translator implementation.
|
||||
*
|
||||
* @return \Hyperf\Translation\Contracts\Translator
|
||||
* @return TranslatorInterface
|
||||
*/
|
||||
public function getTranslator()
|
||||
{
|
||||
@ -238,8 +222,6 @@ class Factory implements FactoryContract
|
||||
|
||||
/**
|
||||
* Set the Presence Verifier implementation.
|
||||
*
|
||||
* @param \Hyperf\Validation\PresenceVerifierInterface $presenceVerifier
|
||||
*/
|
||||
public function setPresenceVerifier(PresenceVerifierInterface $presenceVerifier)
|
||||
{
|
||||
@ -249,10 +231,6 @@ class Factory implements FactoryContract
|
||||
/**
|
||||
* Resolve a new Validator instance.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
* @param array $messages
|
||||
* @param array $customAttributes
|
||||
* @return \Hyperf\Validation\Validator
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param \Hyperf\Validation\Validator $validator
|
||||
*/
|
||||
protected function addExtensions(Validator $validator)
|
||||
{
|
||||
|
@ -17,24 +17,13 @@ interface PresenceVerifierInterface
|
||||
/**
|
||||
* 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|string $idColumn
|
||||
* @param array $extra
|
||||
* @return 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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
@ -13,14 +13,14 @@ declare(strict_types=1);
|
||||
namespace Hyperf\Validation\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\ValidatesWhenResolved;
|
||||
use Hyperf\Validation\Contracts\Validation\Validator;
|
||||
use Hyperf\Contract\ValidatorInterface;
|
||||
use Hyperf\Validation\ValidatesWhenResolvedTrait;
|
||||
use Hyperf\Validation\ValidationException;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class FormRequest extends Request implements ValidatesWhenResolved
|
||||
{
|
||||
@ -33,34 +33,6 @@ class FormRequest extends Request implements ValidatesWhenResolved
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -83,58 +55,35 @@ class FormRequest extends Request implements ValidatesWhenResolved
|
||||
/**
|
||||
* Get the proper failed validation response for the request.
|
||||
*
|
||||
* @param array $errors
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function response(array $errors)
|
||||
public function response()
|
||||
{
|
||||
// if ($this->expectsJson()) {
|
||||
// return new JsonResponse($errors, 422);
|
||||
// }
|
||||
/** @var ResponseInterface $response */
|
||||
$response = Context::get(ResponseInterface::class);
|
||||
|
||||
// return $this->redirector->to($this->getRedirectUrl())
|
||||
// ->withInput($this->except($this->dontFlash))
|
||||
// ->withErrors($errors, $this->errorBag);
|
||||
return new JsonResponse($errors, 422);
|
||||
return $response->withStatus(422);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom messages for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function messages()
|
||||
public function messages(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes()
|
||||
public function attributes(): array
|
||||
{
|
||||
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.
|
||||
*
|
||||
* @param ContainerInterface $container
|
||||
* @return $this
|
||||
*/
|
||||
public function setContainer(ContainerInterface $container)
|
||||
@ -147,7 +96,7 @@ class FormRequest extends Request implements ValidatesWhenResolved
|
||||
/**
|
||||
* Get the validator instance for the request.
|
||||
*
|
||||
* @return \Hyperf\Validation\Contracts\Validation\Validator
|
||||
* @return ValidatorInterface
|
||||
*/
|
||||
protected function getValidatorInstance()
|
||||
{
|
||||
@ -169,8 +118,7 @@ class FormRequest extends Request implements ValidatesWhenResolved
|
||||
/**
|
||||
* Create the default validator instance.
|
||||
*
|
||||
* @param Factory $factory
|
||||
* @return \Hyperf\Validation\Contracts\Validation\Validator
|
||||
* @return ValidatorInterface
|
||||
*/
|
||||
protected function createDefaultValidator(ValidationFactory $factory)
|
||||
{
|
||||
@ -195,48 +143,21 @@ class FormRequest extends Request implements ValidatesWhenResolved
|
||||
/**
|
||||
* Handle a failed validation attempt.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function failedValidation(Validator $validator)
|
||||
protected function failedValidation(ValidatorInterface $validator)
|
||||
{
|
||||
throw new ValidationException($validator, $this->response(
|
||||
$this->formatErrors($validator)
|
||||
));
|
||||
throw new ValidationException($validator, $this->response());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 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.
|
||||
*
|
||||
@ -253,8 +174,6 @@ class FormRequest extends Request implements ValidatesWhenResolved
|
||||
|
||||
/**
|
||||
* Handle a failed authorization attempt.
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
protected function failedAuthorization()
|
||||
{
|
||||
|
@ -22,7 +22,6 @@ class Rule
|
||||
/**
|
||||
* Get a dimensions constraint builder instance.
|
||||
*
|
||||
* @param array $constraints
|
||||
* @return \Hyperf\Validation\Rules\Dimensions
|
||||
*/
|
||||
public static function dimensions(array $constraints = [])
|
||||
@ -33,8 +32,6 @@ class Rule
|
||||
/**
|
||||
* Get a exists constraint builder instance.
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $column
|
||||
* @return \Hyperf\Validation\Rules\Exists
|
||||
*/
|
||||
public static function exists(string $table, string $column = 'NULL')
|
||||
|
@ -46,9 +46,6 @@ trait DatabaseRule
|
||||
|
||||
/**
|
||||
* Create a new rule instance.
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $column
|
||||
*/
|
||||
public function __construct(string $table, string $column = 'NULL')
|
||||
{
|
||||
@ -81,7 +78,6 @@ trait DatabaseRule
|
||||
/**
|
||||
* Set a "where not" constraint on the query.
|
||||
*
|
||||
* @param string $column
|
||||
* @param array|string $value
|
||||
* @return $this
|
||||
*/
|
||||
@ -97,7 +93,6 @@ trait DatabaseRule
|
||||
/**
|
||||
* Set a "where null" constraint on the query.
|
||||
*
|
||||
* @param string $column
|
||||
* @return $this
|
||||
*/
|
||||
public function whereNull(string $column)
|
||||
@ -108,7 +103,6 @@ trait DatabaseRule
|
||||
/**
|
||||
* Set a "where not null" constraint on the query.
|
||||
*
|
||||
* @param string $column
|
||||
* @return $this
|
||||
*/
|
||||
public function whereNotNull(string $column)
|
||||
@ -119,8 +113,6 @@ trait DatabaseRule
|
||||
/**
|
||||
* Set a "where in" constraint on the query.
|
||||
*
|
||||
* @param string $column
|
||||
* @param array $values
|
||||
* @return $this
|
||||
*/
|
||||
public function whereIn(string $column, array $values)
|
||||
@ -133,8 +125,6 @@ trait DatabaseRule
|
||||
/**
|
||||
* Set a "where not in" constraint on the query.
|
||||
*
|
||||
* @param string $column
|
||||
* @param array $values
|
||||
* @return $this
|
||||
*/
|
||||
public function whereNotIn(string $column, array $values)
|
||||
@ -147,7 +137,6 @@ trait DatabaseRule
|
||||
/**
|
||||
* Register a custom query callback.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function using(Closure $callback)
|
||||
@ -159,8 +148,6 @@ trait DatabaseRule
|
||||
|
||||
/**
|
||||
* Get the custom query callbacks for the rule.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function queryCallbacks(): array
|
||||
{
|
||||
@ -169,8 +156,6 @@ trait DatabaseRule
|
||||
|
||||
/**
|
||||
* Format the where clauses.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function formatWheres(): string
|
||||
{
|
||||
|
@ -23,8 +23,6 @@ class Dimensions
|
||||
|
||||
/**
|
||||
* Create a new dimensions rule instance.
|
||||
*
|
||||
* @param array $constraints;
|
||||
*/
|
||||
public function __construct(array $constraints = [])
|
||||
{
|
||||
@ -33,8 +31,6 @@ class Dimensions
|
||||
|
||||
/**
|
||||
* Convert the rule to a validation string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
@ -50,7 +46,6 @@ class Dimensions
|
||||
/**
|
||||
* Set the "width" constraint.
|
||||
*
|
||||
* @param int $value
|
||||
* @return $this
|
||||
*/
|
||||
public function width(int $value)
|
||||
@ -63,7 +58,6 @@ class Dimensions
|
||||
/**
|
||||
* Set the "height" constraint.
|
||||
*
|
||||
* @param int $value
|
||||
* @return $this
|
||||
*/
|
||||
public function height(int $value)
|
||||
@ -76,7 +70,6 @@ class Dimensions
|
||||
/**
|
||||
* Set the "min width" constraint.
|
||||
*
|
||||
* @param int $value
|
||||
* @return $this
|
||||
*/
|
||||
public function minWidth(int $value)
|
||||
@ -89,7 +82,6 @@ class Dimensions
|
||||
/**
|
||||
* Set the "min height" constraint.
|
||||
*
|
||||
* @param int $value
|
||||
* @return $this
|
||||
*/
|
||||
public function minHeight(int $value)
|
||||
@ -102,7 +94,6 @@ class Dimensions
|
||||
/**
|
||||
* Set the "max width" constraint.
|
||||
*
|
||||
* @param int $value
|
||||
* @return $this
|
||||
*/
|
||||
public function maxWidth(int $value)
|
||||
@ -115,7 +106,6 @@ class Dimensions
|
||||
/**
|
||||
* Set the "max height" constraint.
|
||||
*
|
||||
* @param int $value
|
||||
* @return $this
|
||||
*/
|
||||
public function maxHeight(int $value)
|
||||
@ -128,7 +118,6 @@ class Dimensions
|
||||
/**
|
||||
* Set the "ratio" constraint.
|
||||
*
|
||||
* @param float $value
|
||||
* @return $this
|
||||
*/
|
||||
public function ratio(float $value)
|
||||
|
@ -18,8 +18,6 @@ class Exists
|
||||
|
||||
/**
|
||||
* Convert the rule to a validation string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
|
@ -28,8 +28,6 @@ class In
|
||||
|
||||
/**
|
||||
* Create a new in rule instance.
|
||||
*
|
||||
* @param array $values
|
||||
*/
|
||||
public function __construct(array $values)
|
||||
{
|
||||
@ -39,8 +37,6 @@ class In
|
||||
/**
|
||||
* Convert the rule to a validation string.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @see \Hyperf\Validation\ValidationRuleParser::parseParameters
|
||||
*/
|
||||
public function __toString(): string
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user