mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-11-29 18:27:44 +08:00
Added some validation rules such as ExcludeIf
and ProhibitedIf
. (#6094)
This commit is contained in:
parent
b3db5cd692
commit
54a98c678c
@ -8,6 +8,7 @@
|
||||
## Added
|
||||
|
||||
- [#6096](https://github.com/hyperf/hyperf/pull/6096) Added `getThrowable` method to request events and crontab event.
|
||||
- [#6094](https://github.com/hyperf/hyperf/pull/6094) Added some validation rules such as `ExcludeIf` `File` `ImageFile` and `ProhibitedIf`.
|
||||
|
||||
## Optimized
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
"php": ">=8.0",
|
||||
"egulias/email-validator": "^3.0",
|
||||
"hyperf/collection": "~3.0.0",
|
||||
"hyperf/conditionable": "~3.0.0",
|
||||
"hyperf/contract": "~3.0.0",
|
||||
"hyperf/database": "~3.0.0",
|
||||
"hyperf/di": "~3.0.0",
|
||||
@ -29,6 +30,7 @@
|
||||
"hyperf/macroable": "~3.0.0",
|
||||
"hyperf/tappable": "~3.0.0",
|
||||
"hyperf/translation": "~3.0.0",
|
||||
"hyperf/stringable": "~3.0.0",
|
||||
"hyperf/support": "~3.0.0",
|
||||
"hyperf/utils": "~3.0.0",
|
||||
"nesbot/carbon": "^2.21",
|
||||
|
@ -342,13 +342,25 @@ trait ValidatesAttributes
|
||||
*/
|
||||
public function validateDimensions(string $attribute, $value, array $parameters): bool
|
||||
{
|
||||
if (! $this->isValidFileInstance($value) || ! $sizeDetails = @getimagesize($value->getRealPath())) {
|
||||
if ($this->isValidFileInstance($value) && in_array($value->getMimeType(), ['image/svg+xml', 'image/svg'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $this->isValidFileInstance($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dimensions = method_exists($value, 'dimensions')
|
||||
? $value->dimensions()
|
||||
: @getimagesize($value->getRealPath());
|
||||
|
||||
if (! $dimensions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->requireParameterCount(1, $parameters, 'dimensions');
|
||||
|
||||
[$width, $height] = $sizeDetails;
|
||||
[$width, $height] = $dimensions;
|
||||
|
||||
$parameters = $this->parseNamedParameters($parameters);
|
||||
|
||||
|
22
src/validation/src/Contract/DataAwareRule.php
Normal file
22
src/validation/src/Contract/DataAwareRule.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Validation\Contract;
|
||||
|
||||
interface DataAwareRule
|
||||
{
|
||||
/**
|
||||
* Set the data under validation.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setData(array $data): static;
|
||||
}
|
24
src/validation/src/Contract/ValidatorAwareRule.php
Normal file
24
src/validation/src/Contract/ValidatorAwareRule.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://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Validation\Contract;
|
||||
|
||||
use Hyperf\Validation\Validator;
|
||||
|
||||
interface ValidatorAwareRule
|
||||
{
|
||||
/**
|
||||
* Set the current validator.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValidator(Validator $validator): static;
|
||||
}
|
49
src/validation/src/Rules/ExcludeIf.php
Normal file
49
src/validation/src/Rules/ExcludeIf.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Validation\Rules;
|
||||
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use Stringable;
|
||||
|
||||
class ExcludeIf implements Stringable
|
||||
{
|
||||
public Closure|bool $condition;
|
||||
|
||||
/**
|
||||
* Create a new exclude validation rule based on a condition.
|
||||
* @param bool|Closure $condition the condition that validates the attribute
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct($condition)
|
||||
{
|
||||
if ($condition instanceof Closure || is_bool($condition)) {
|
||||
$this->condition = $condition;
|
||||
} else {
|
||||
throw new InvalidArgumentException('The provided condition must be a callable or boolean.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the rule to a validation string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
if (is_callable($this->condition)) {
|
||||
return call_user_func($this->condition) ? 'exclude' : '';
|
||||
}
|
||||
|
||||
return $this->condition ? 'exclude' : '';
|
||||
}
|
||||
}
|
334
src/validation/src/Rules/File.php
Normal file
334
src/validation/src/Rules/File.php
Normal file
@ -0,0 +1,334 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Validation\Rules;
|
||||
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Conditionable\Conditionable;
|
||||
use Hyperf\Context\ApplicationContext;
|
||||
use Hyperf\Macroable\Macroable;
|
||||
use Hyperf\Stringable\Str;
|
||||
use Hyperf\Validation\Contract\DataAwareRule;
|
||||
use Hyperf\Validation\Contract\Rule;
|
||||
use Hyperf\Validation\Contract\ValidatorAwareRule;
|
||||
use Hyperf\Validation\Validator;
|
||||
use Hyperf\Validation\ValidatorFactory;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class File implements Rule, DataAwareRule, ValidatorAwareRule
|
||||
{
|
||||
use Conditionable;
|
||||
use Macroable;
|
||||
|
||||
/**
|
||||
* The callback that will generate the "default" version of the file rule.
|
||||
*
|
||||
* @var null|array|callable|string
|
||||
*/
|
||||
public static $defaultCallback;
|
||||
|
||||
/**
|
||||
* The MIME types that the given file should match. This array may also contain file extensions.
|
||||
*/
|
||||
protected array $allowedMimetypes = [];
|
||||
|
||||
/**
|
||||
* The minimum size in kilobytes that the file can be.
|
||||
*/
|
||||
protected ?int $minimumFileSize = null;
|
||||
|
||||
/**
|
||||
* The maximum size in kilobytes that the file can be.
|
||||
*/
|
||||
protected ?int $maximumFileSize = null;
|
||||
|
||||
/**
|
||||
* An array of custom rules that will be merged into the validation rules.
|
||||
*/
|
||||
protected array $customRules = [];
|
||||
|
||||
/**
|
||||
* The error message after validation, if any.
|
||||
*/
|
||||
protected array $messages = [];
|
||||
|
||||
/**
|
||||
* The data under validation.
|
||||
*/
|
||||
protected array $data = [];
|
||||
|
||||
/**
|
||||
* The validator performing the validation.
|
||||
*/
|
||||
protected ?Validator $validator = null;
|
||||
|
||||
/**
|
||||
* Set the default callback to be used for determining the file default rules.
|
||||
*
|
||||
* If no arguments are passed, the default file rule configuration will be returned.
|
||||
*
|
||||
* @param null|callable|static $callback
|
||||
* @return null|static
|
||||
*/
|
||||
public static function defaults(File|callable $callback = null)
|
||||
{
|
||||
if (is_null($callback)) {
|
||||
return static::default();
|
||||
}
|
||||
|
||||
if (! is_callable($callback) && ! $callback instanceof static) {
|
||||
throw new InvalidArgumentException('The given callback should be callable or an instance of ' . static::class);
|
||||
}
|
||||
|
||||
static::$defaultCallback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default configuration of the file rule.
|
||||
*/
|
||||
public static function default()
|
||||
{
|
||||
$file = is_callable(static::$defaultCallback)
|
||||
? call_user_func(static::$defaultCallback)
|
||||
: static::$defaultCallback;
|
||||
|
||||
return $file instanceof Rule ? $file : new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the uploaded file to only image types.
|
||||
*/
|
||||
public static function image(): ImageFile
|
||||
{
|
||||
return new ImageFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the uploaded file to the given MIME types or file extensions.
|
||||
*
|
||||
* @param array<int, string>|string $mimetypes
|
||||
*/
|
||||
public static function types(array|string $mimetypes): static
|
||||
{
|
||||
return \Hyperf\Tappable\tap(new static(), fn ($file) => $file->allowedMimetypes = (array) $mimetypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the uploaded file should be exactly a certain size in kilobytes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function size(int|string $size): static
|
||||
{
|
||||
$this->minimumFileSize = $this->toKilobytes($size);
|
||||
$this->maximumFileSize = $this->minimumFileSize;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the uploaded file should be between a minimum and maximum size in kilobytes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function between(int|string $minSize, int|string $maxSize): static
|
||||
{
|
||||
$this->minimumFileSize = $this->toKilobytes($minSize);
|
||||
$this->maximumFileSize = $this->toKilobytes($maxSize);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the uploaded file should be no less than the given number of kilobytes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function min(int|string $size): static
|
||||
{
|
||||
$this->minimumFileSize = (int) $this->toKilobytes($size);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the uploaded file should be no more than the given number of kilobytes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function max(int|string $size): static
|
||||
{
|
||||
$this->maximumFileSize = (int) $this->toKilobytes($size);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify additional validation rules that should be merged with the default rules during validation.
|
||||
*
|
||||
* @param mixed $rules
|
||||
* @return $this
|
||||
*/
|
||||
public function rules($rules): static
|
||||
{
|
||||
$this->customRules = array_merge($this->customRules, Arr::wrap($rules));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the validation rule passes.
|
||||
*/
|
||||
public function passes(string $attribute, mixed $value): bool
|
||||
{
|
||||
$this->messages = [];
|
||||
|
||||
$test = $this->buildValidationRules();
|
||||
|
||||
$validator = ApplicationContext::getContainer()->get(ValidatorFactory::class)->make(
|
||||
$this->data,
|
||||
[$attribute => $test],
|
||||
$this->validator->customMessages,
|
||||
$this->validator->customAttributes
|
||||
);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return $this->fail($validator->messages()->all());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation error message.
|
||||
*/
|
||||
public function message(): array|string
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current validator.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValidator(Validator $validator): static
|
||||
{
|
||||
$this->validator = $validator;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current data under validation.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setData(array $data): static
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a potentially human-friendly file size to kilobytes.
|
||||
*
|
||||
* @param int|string $size
|
||||
* @return mixed
|
||||
*/
|
||||
protected function toKilobytes($size)
|
||||
{
|
||||
if (! is_string($size)) {
|
||||
return $size;
|
||||
}
|
||||
|
||||
$value = floatval($size);
|
||||
|
||||
return round(match (true) {
|
||||
Str::endsWith($size, 'kb') => $value * 1,
|
||||
Str::endsWith($size, 'mb') => $value * 1000,
|
||||
Str::endsWith($size, 'gb') => $value * 1000000,
|
||||
Str::endsWith($size, 'tb') => $value * 1000000000,
|
||||
default => throw new InvalidArgumentException('Invalid file size suffix.'),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the array of underlying validation rules based on the current state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function buildValidationRules()
|
||||
{
|
||||
$rules = ['file'];
|
||||
|
||||
$rules = array_merge($rules, $this->buildMimetypes());
|
||||
|
||||
$rules[] = match (true) {
|
||||
is_null($this->minimumFileSize) && is_null($this->maximumFileSize) => null,
|
||||
is_null($this->maximumFileSize) => "min:{$this->minimumFileSize}",
|
||||
is_null($this->minimumFileSize) => "max:{$this->maximumFileSize}",
|
||||
$this->minimumFileSize !== $this->maximumFileSize => "between:{$this->minimumFileSize},{$this->maximumFileSize}",
|
||||
default => "size:{$this->minimumFileSize}",
|
||||
};
|
||||
|
||||
return array_merge(array_filter($rules), $this->customRules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Separate the given mimetypes from extensions and return an array of correct rules to validate against.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function buildMimetypes()
|
||||
{
|
||||
if (count($this->allowedMimetypes) === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rules = [];
|
||||
|
||||
$mimetypes = array_filter(
|
||||
$this->allowedMimetypes,
|
||||
fn ($type) => str_contains($type, '/')
|
||||
);
|
||||
|
||||
$mimes = array_diff($this->allowedMimetypes, $mimetypes);
|
||||
|
||||
if (count($mimetypes) > 0) {
|
||||
$rules[] = 'mimetypes:' . implode(',', $mimetypes);
|
||||
}
|
||||
|
||||
if (count($mimes) > 0) {
|
||||
$rules[] = 'mimes:' . implode(',', $mimes);
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given failures, and return false.
|
||||
*
|
||||
* @param array|string $messages
|
||||
* @return bool
|
||||
*/
|
||||
protected function fail($messages)
|
||||
{
|
||||
$messages = collect(Arr::wrap($messages))->map(function ($message) {
|
||||
return $this->validator->getTranslator()->get($message);
|
||||
})->all();
|
||||
|
||||
$this->messages = array_merge($this->messages, $messages);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
33
src/validation/src/Rules/ImageFile.php
Normal file
33
src/validation/src/Rules/ImageFile.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Validation\Rules;
|
||||
|
||||
class ImageFile extends File
|
||||
{
|
||||
/**
|
||||
* Create a new image file rule instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->rules('image');
|
||||
}
|
||||
|
||||
/**
|
||||
* The dimension constraints for the uploaded file.
|
||||
*/
|
||||
public function dimensions(Dimensions $dimensions): static
|
||||
{
|
||||
$this->rules($dimensions);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
51
src/validation/src/Rules/ProhibitedIf.php
Normal file
51
src/validation/src/Rules/ProhibitedIf.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Validation\Rules;
|
||||
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use Stringable;
|
||||
|
||||
class ProhibitedIf implements Stringable
|
||||
{
|
||||
public Closure|bool $condition;
|
||||
|
||||
/**
|
||||
* Create a new prohibited validation rule based on a condition.
|
||||
*
|
||||
* @param bool|Closure $condition the condition that validates the attribute
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct($condition)
|
||||
{
|
||||
if ($condition instanceof Closure || is_bool($condition)) {
|
||||
$this->condition = $condition;
|
||||
} else {
|
||||
throw new InvalidArgumentException('The provided condition must be a callable or boolean.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the rule to a validation string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
if (is_callable($this->condition)) {
|
||||
return call_user_func($this->condition) ? 'prohibited' : '';
|
||||
}
|
||||
|
||||
return $this->condition ? 'prohibited' : '';
|
||||
}
|
||||
}
|
@ -21,9 +21,11 @@ use Hyperf\HttpMessage\Upload\UploadedFile;
|
||||
use Hyperf\Stringable\Str;
|
||||
use Hyperf\Support\Fluent;
|
||||
use Hyperf\Support\MessageBag;
|
||||
use Hyperf\Validation\Contract\DataAwareRule;
|
||||
use Hyperf\Validation\Contract\ImplicitRule;
|
||||
use Hyperf\Validation\Contract\PresenceVerifierInterface;
|
||||
use Hyperf\Validation\Contract\Rule as RuleContract;
|
||||
use Hyperf\Validation\Contract\ValidatorAwareRule;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use RuntimeException;
|
||||
use Stringable;
|
||||
@ -888,6 +890,14 @@ class Validator implements ValidatorContract
|
||||
*/
|
||||
protected function validateUsingCustomRule(string $attribute, $value, RuleContract $rule)
|
||||
{
|
||||
if ($rule instanceof ValidatorAwareRule) {
|
||||
$rule->setValidator($this);
|
||||
}
|
||||
|
||||
if ($rule instanceof DataAwareRule) {
|
||||
$rule->setData($this->data);
|
||||
}
|
||||
|
||||
if (! $rule->passes($attribute, $value)) {
|
||||
$this->failedRules[$attribute][$rule::class] = [];
|
||||
|
||||
|
73
src/validation/tests/Cases/ValidationExcludeIfTest.php
Normal file
73
src/validation/tests/Cases/ValidationExcludeIfTest.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace HyperfTest\Validation\Cases;
|
||||
|
||||
use Exception;
|
||||
use Hyperf\Validation\Rules\ExcludeIf;
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class ValidationExcludeIfTest extends TestCase
|
||||
{
|
||||
public function testItReturnsStringVersionOfRuleWhenCast()
|
||||
{
|
||||
$rule = new ExcludeIf(function () {
|
||||
return true;
|
||||
});
|
||||
|
||||
$this->assertSame('exclude', (string) $rule);
|
||||
|
||||
$rule = new ExcludeIf(function () {
|
||||
return false;
|
||||
});
|
||||
|
||||
$this->assertSame('', (string) $rule);
|
||||
|
||||
$rule = new ExcludeIf(true);
|
||||
|
||||
$this->assertSame('exclude', (string) $rule);
|
||||
|
||||
$rule = new ExcludeIf(false);
|
||||
|
||||
$this->assertSame('', (string) $rule);
|
||||
}
|
||||
|
||||
public function testItValidatesCallableAndBooleanAreAcceptableArguments()
|
||||
{
|
||||
$this->assertInstanceOf(ExcludeIf::class, new ExcludeIf(false));
|
||||
$this->assertInstanceOf(ExcludeIf::class, new ExcludeIf(true));
|
||||
$this->assertInstanceOf(ExcludeIf::class, new ExcludeIf(fn () => true));
|
||||
|
||||
foreach ([1, 1.1, 'phpinfo', new stdClass()] as $condition) {
|
||||
try {
|
||||
$this->assertInstanceOf(ExcludeIf::class, new ExcludeIf($condition));
|
||||
$this->fail('The ExcludeIf constructor must not accept ' . gettype($condition));
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$this->assertEquals('The provided condition must be a callable or boolean.', $exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testItThrowsExceptionIfRuleIsNotSerializable()
|
||||
{
|
||||
$this->expectException(Exception::class);
|
||||
|
||||
serialize(new ExcludeIf(function () {
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
}
|
361
src/validation/tests/Cases/ValidationFileRuleTest.php
Normal file
361
src/validation/tests/Cases/ValidationFileRuleTest.php
Normal file
@ -0,0 +1,361 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace HyperfTest\Validation\Cases;
|
||||
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Context\ApplicationContext;
|
||||
use Hyperf\Di\Container;
|
||||
use Hyperf\Translation\ArrayLoader;
|
||||
use Hyperf\Translation\Translator;
|
||||
use Hyperf\Validation\Rules\File;
|
||||
use Hyperf\Validation\Validator;
|
||||
use Hyperf\Validation\ValidatorFactory;
|
||||
use HyperfTest\Validation\File\FileFactory;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class ValidationFileRuleTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
$container = Mockery::mock(Container::class);
|
||||
ApplicationContext::setContainer($container);
|
||||
|
||||
$container->shouldReceive('get')->with(ValidatorFactory::class)->andReturn(
|
||||
new ValidatorFactory($this->getIlluminateArrayTranslator())
|
||||
);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testBasic()
|
||||
{
|
||||
$this->fails(
|
||||
File::default(),
|
||||
'foo',
|
||||
['validation.file'],
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::default(),
|
||||
(new FileFactory())->create('foo.bar'),
|
||||
);
|
||||
|
||||
$this->passes(File::default(), null);
|
||||
}
|
||||
|
||||
public function testSingleMimetype()
|
||||
{
|
||||
$this->fails(
|
||||
File::types('text/plain'),
|
||||
(new FileFactory())->createWithContent('foo.png', file_get_contents(__DIR__ . '/fixtures/image.png')),
|
||||
['validation.mimetypes']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::types('image/png'),
|
||||
(new FileFactory())->createWithContent('foo.png', file_get_contents(__DIR__ . '/fixtures/image.png')),
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleMimeTypes()
|
||||
{
|
||||
$this->fails(
|
||||
File::types(['text/plain', 'image/jpeg']),
|
||||
(new FileFactory())->createWithContent('foo.png', file_get_contents(__DIR__ . '/fixtures/image.png')),
|
||||
['validation.mimetypes']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::types(['text/plain', 'image/png']),
|
||||
(new FileFactory())->createWithContent('foo.png', file_get_contents(__DIR__ . '/fixtures/image.png')),
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleMime()
|
||||
{
|
||||
$this->fails(
|
||||
File::types('txt'),
|
||||
(new FileFactory())->createWithContent('foo.png', file_get_contents(__DIR__ . '/fixtures/image.png')),
|
||||
['validation.mimes']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::types('png'),
|
||||
(new FileFactory())->createWithContent('foo.png', file_get_contents(__DIR__ . '/fixtures/image.png')),
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleMimes()
|
||||
{
|
||||
$this->fails(
|
||||
File::types(['png', 'jpg', 'jpeg', 'svg']),
|
||||
(new FileFactory())->createWithContent('foo.txt', 'Hello World!'),
|
||||
['validation.mimes']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::types(['png', 'jpg', 'jpeg', 'svg']),
|
||||
[
|
||||
(new FileFactory())->createWithContent('foo.png', file_get_contents(__DIR__ . '/fixtures/image.png')),
|
||||
(new FileFactory())->createWithContent('foo.svg', file_get_contents(__DIR__ . '/fixtures/image.svg')),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testMixOfMimetypesAndMimes()
|
||||
{
|
||||
$this->fails(
|
||||
File::types(['png', 'image/png']),
|
||||
(new FileFactory())->createWithContent('foo.txt', 'Hello World!'),
|
||||
['validation.mimetypes', 'validation.mimes']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::types(['png', 'image/png']),
|
||||
(new FileFactory())->createWithContent('foo.png', file_get_contents(__DIR__ . '/fixtures/image.png')),
|
||||
);
|
||||
}
|
||||
|
||||
public function testImage()
|
||||
{
|
||||
$this->fails(
|
||||
File::image(),
|
||||
(new FileFactory())->createWithContent('foo.txt', 'Hello World!'),
|
||||
['validation.image']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::image(),
|
||||
(new FileFactory())->image('foo.png'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testSize()
|
||||
{
|
||||
$this->fails(
|
||||
File::default()->size(1024),
|
||||
[
|
||||
(new FileFactory())->create('foo.txt', 1025),
|
||||
(new FileFactory())->create('foo.txt', 1023),
|
||||
],
|
||||
['validation.size.file']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::default()->size(1024),
|
||||
(new FileFactory())->create('foo.txt', 1024),
|
||||
);
|
||||
}
|
||||
|
||||
public function testBetween()
|
||||
{
|
||||
$this->fails(
|
||||
File::default()->between(1024, 2048),
|
||||
[
|
||||
(new FileFactory())->create('foo.txt', 1023),
|
||||
(new FileFactory())->create('foo.txt', 2049),
|
||||
],
|
||||
['validation.between.file']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::default()->between(1024, 2048),
|
||||
[
|
||||
(new FileFactory())->create('foo.txt', 1024),
|
||||
(new FileFactory())->create('foo.txt', 2048),
|
||||
(new FileFactory())->create('foo.txt', 1025),
|
||||
(new FileFactory())->create('foo.txt', 2047),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testMin()
|
||||
{
|
||||
$this->fails(
|
||||
File::default()->min(1024),
|
||||
(new FileFactory())->create('foo.txt', 1023),
|
||||
['validation.min.file']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::default()->min(1024),
|
||||
[
|
||||
(new FileFactory())->create('foo.txt', 1024),
|
||||
(new FileFactory())->create('foo.txt', 1025),
|
||||
(new FileFactory())->create('foo.txt', 2048),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testMinWithHumanReadableSize()
|
||||
{
|
||||
$this->fails(
|
||||
File::default()->min('1024kb'),
|
||||
(new FileFactory())->create('foo.txt', 1023),
|
||||
['validation.min.file']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::default()->min('1024kb'),
|
||||
[
|
||||
(new FileFactory())->create('foo.txt', 1024),
|
||||
(new FileFactory())->create('foo.txt', 1025),
|
||||
(new FileFactory())->create('foo.txt', 2048),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testMax()
|
||||
{
|
||||
$this->fails(
|
||||
File::default()->max(1024),
|
||||
(new FileFactory())->create('foo.txt', 1025),
|
||||
['validation.max.file']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::default()->max(1024),
|
||||
[
|
||||
(new FileFactory())->create('foo.txt', 1024),
|
||||
(new FileFactory())->create('foo.txt', 1023),
|
||||
(new FileFactory())->create('foo.txt', 512),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testMaxWithHumanReadableSize()
|
||||
{
|
||||
$this->fails(
|
||||
File::default()->max('1024kb'),
|
||||
(new FileFactory())->create('foo.txt', 1025),
|
||||
['validation.max.file']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::default()->max('1024kb'),
|
||||
[
|
||||
(new FileFactory())->create('foo.txt', 1024),
|
||||
(new FileFactory())->create('foo.txt', 1023),
|
||||
(new FileFactory())->create('foo.txt', 512),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testMaxWithHumanReadableSizeAndMultipleValue()
|
||||
{
|
||||
$this->fails(
|
||||
File::default()->max('1mb'),
|
||||
(new FileFactory())->create('foo.txt', 1025),
|
||||
['validation.max.file']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::default()->max('1mb'),
|
||||
[
|
||||
(new FileFactory())->create('foo.txt', 1000),
|
||||
(new FileFactory())->create('foo.txt', 999),
|
||||
(new FileFactory())->create('foo.txt', 512),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testMacro()
|
||||
{
|
||||
File::macro('toDocument', function () {
|
||||
return static::default()->rules('mimes:txt,csv');
|
||||
});
|
||||
|
||||
$this->fails(
|
||||
File::toDocument(),
|
||||
(new FileFactory())->create('foo.png'),
|
||||
['validation.mimes']
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
File::toDocument(),
|
||||
[
|
||||
(new FileFactory())->create('foo.txt'),
|
||||
(new FileFactory())->create('foo.csv'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testItCanSetDefaultUsing()
|
||||
{
|
||||
$this->assertInstanceOf(File::class, File::default());
|
||||
|
||||
File::defaults(function () {
|
||||
return File::types('txt')->max(12 * 1024);
|
||||
});
|
||||
|
||||
$this->fails(
|
||||
File::default(),
|
||||
(new FileFactory())->create('foo.png', 13 * 1024),
|
||||
[
|
||||
'validation.mimes',
|
||||
'validation.max.file',
|
||||
]
|
||||
);
|
||||
|
||||
File::defaults(File::image()->between(1024, 2048));
|
||||
|
||||
$this->passes(
|
||||
File::default(),
|
||||
(new FileFactory())->create('foo.png', (int) (1.5 * 1024)),
|
||||
);
|
||||
}
|
||||
|
||||
public function getIlluminateArrayTranslator(): Translator
|
||||
{
|
||||
return new Translator(
|
||||
new ArrayLoader(),
|
||||
'en'
|
||||
);
|
||||
}
|
||||
|
||||
protected function fails($rule, $values, $messages)
|
||||
{
|
||||
$this->assertValidationRules($rule, $values, false, $messages);
|
||||
}
|
||||
|
||||
protected function assertValidationRules($rule, $values, $result, $messages)
|
||||
{
|
||||
$values = Arr::wrap($values);
|
||||
|
||||
foreach ($values as $value) {
|
||||
$v = new Validator(
|
||||
$this->getIlluminateArrayTranslator(),
|
||||
['my_file' => $value],
|
||||
['my_file' => is_object($rule) ? clone $rule : $rule]
|
||||
);
|
||||
|
||||
$this->assertSame($result, $v->passes());
|
||||
|
||||
$this->assertSame(
|
||||
$result ? [] : ['my_file' => $messages],
|
||||
$v->messages()->toArray()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function passes($rule, $values)
|
||||
{
|
||||
$this->assertValidationRules($rule, $values, true, []);
|
||||
}
|
||||
}
|
131
src/validation/tests/Cases/ValidationImageFileRuleTest.php
Normal file
131
src/validation/tests/Cases/ValidationImageFileRuleTest.php
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace HyperfTest\Validation\Cases;
|
||||
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Context\ApplicationContext;
|
||||
use Hyperf\Di\Container;
|
||||
use Hyperf\Testing\HttpMessage\Upload\UploadedFile;
|
||||
use Hyperf\Translation\ArrayLoader;
|
||||
use Hyperf\Translation\Translator;
|
||||
use Hyperf\Validation\Rule;
|
||||
use Hyperf\Validation\Rules\ImageFile;
|
||||
use Hyperf\Validation\Validator;
|
||||
use Hyperf\Validation\ValidatorFactory;
|
||||
use HyperfTest\Validation\File\FileFactory;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class ValidationImageFileRuleTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
$container = Mockery::mock(Container::class);
|
||||
ApplicationContext::setContainer($container);
|
||||
|
||||
$container->shouldReceive('get')->with(ValidatorFactory::class)->andReturn(
|
||||
new ValidatorFactory($this->getIlluminateArrayTranslator())
|
||||
);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testDimensions()
|
||||
{
|
||||
$this->fails(
|
||||
( new ImageFile())->dimensions(Rule::dimensions()->width(100)->height(100)),
|
||||
(new FileFactory())->image('foo.png', 101, 101),
|
||||
['validation.dimensions'],
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
( new ImageFile())->dimensions(Rule::dimensions()->width(100)->height(100)),
|
||||
(new FileFactory())->image('foo.png', 100, 100),
|
||||
);
|
||||
}
|
||||
|
||||
public function testDimensionsWithCustomImageSizeMethod()
|
||||
{
|
||||
$this->fails(
|
||||
(new ImageFile())->dimensions(Rule::dimensions()->width(100)->height(100)),
|
||||
new UploadedFileWithCustomImageSizeMethod(stream_get_meta_data($tmpFile = tmpfile())['uri'], 0, 0, 'foo.png'),
|
||||
['validation.dimensions'],
|
||||
);
|
||||
|
||||
$this->passes(
|
||||
(new ImageFile())->dimensions(Rule::dimensions()->width(200)->height(200)),
|
||||
new UploadedFileWithCustomImageSizeMethod(stream_get_meta_data($tmpFile = tmpfile())['uri'], 0, 0, 'foo.png'),
|
||||
);
|
||||
}
|
||||
|
||||
public function getIlluminateArrayTranslator(): Translator
|
||||
{
|
||||
return new Translator(
|
||||
new ArrayLoader(),
|
||||
'en'
|
||||
);
|
||||
}
|
||||
|
||||
protected function fails($rule, $values, $messages): void
|
||||
{
|
||||
$this->assertValidationRules($rule, $values, false, $messages);
|
||||
}
|
||||
|
||||
protected function assertValidationRules($rule, $values, $result, $messages): void
|
||||
{
|
||||
$values = Arr::wrap($values);
|
||||
|
||||
foreach ($values as $value) {
|
||||
$v = new Validator(
|
||||
$this->getIlluminateArrayTranslator(),
|
||||
['my_file' => $value],
|
||||
['my_file' => is_object($rule) ? clone $rule : $rule]
|
||||
);
|
||||
|
||||
$this->assertSame($result, $v->passes());
|
||||
|
||||
$this->assertSame(
|
||||
$result ? [] : ['my_file' => $messages],
|
||||
$v->messages()->toArray()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function passes($rule, $values): void
|
||||
{
|
||||
$this->assertValidationRules($rule, $values, true, []);
|
||||
}
|
||||
}
|
||||
|
||||
class UploadedFileWithCustomImageSizeMethod extends UploadedFile
|
||||
{
|
||||
public function isValid(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function guessExtension(): string
|
||||
{
|
||||
return 'png';
|
||||
}
|
||||
|
||||
public function dimensions(): array
|
||||
{
|
||||
return [200, 200];
|
||||
}
|
||||
}
|
73
src/validation/tests/Cases/ValidationProhibitedIfTest.php
Normal file
73
src/validation/tests/Cases/ValidationProhibitedIfTest.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace HyperfTest\Validation\Cases;
|
||||
|
||||
use Exception;
|
||||
use Hyperf\Validation\Rules\ProhibitedIf;
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class ValidationProhibitedIfTest extends TestCase
|
||||
{
|
||||
public function testItReturnsStringVersionOfRuleWhenCast()
|
||||
{
|
||||
$rule = new ProhibitedIf(function () {
|
||||
return true;
|
||||
});
|
||||
|
||||
$this->assertSame('prohibited', (string) $rule);
|
||||
|
||||
$rule = new ProhibitedIf(function () {
|
||||
return false;
|
||||
});
|
||||
|
||||
$this->assertSame('', (string) $rule);
|
||||
|
||||
$rule = new ProhibitedIf(true);
|
||||
|
||||
$this->assertSame('prohibited', (string) $rule);
|
||||
|
||||
$rule = new ProhibitedIf(false);
|
||||
|
||||
$this->assertSame('', (string) $rule);
|
||||
}
|
||||
|
||||
public function testItValidatesCallableAndBooleanAreAcceptableArguments()
|
||||
{
|
||||
$this->assertInstanceOf(ProhibitedIf::class, new ProhibitedIf(false));
|
||||
$this->assertInstanceOf(ProhibitedIf::class, new ProhibitedIf(true));
|
||||
$this->assertInstanceOf(ProhibitedIf::class, new ProhibitedIf(fn () => true));
|
||||
|
||||
foreach ([1, 1.1, 'phpinfo', new stdClass()] as $condition) {
|
||||
try {
|
||||
$this->assertInstanceOf(ProhibitedIf::class, new ProhibitedIf($condition));
|
||||
$this->fail('The ProhibitedIf constructor must not accept ' . gettype($condition));
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$this->assertEquals('The provided condition must be a callable or boolean.', $exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testItThrowsExceptionIfRuleIsNotSerializable()
|
||||
{
|
||||
$this->expectException(Exception::class);
|
||||
|
||||
serialize(new ProhibitedIf(function () {
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
}
|
1
src/validation/tests/Cases/fixtures/txt.txt
Normal file
1
src/validation/tests/Cases/fixtures/txt.txt
Normal file
@ -0,0 +1 @@
|
||||
Hello World!
|
125
src/validation/tests/File/File.php
Normal file
125
src/validation/tests/File/File.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace HyperfTest\Validation\File;
|
||||
|
||||
use Hyperf\Testing\HttpMessage\Upload\UploadedFile;
|
||||
|
||||
class File extends UploadedFile
|
||||
{
|
||||
/**
|
||||
* The temporary file resource.
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
public $tempFile;
|
||||
|
||||
/**
|
||||
* The "size" to report.
|
||||
*/
|
||||
public int $sizeToReport = 0;
|
||||
|
||||
/**
|
||||
* Create a new file instance.
|
||||
*
|
||||
* @param resource $tempFile
|
||||
*/
|
||||
public function __construct(
|
||||
public string $name,
|
||||
$tempFile,
|
||||
private int $error = 0,
|
||||
private ?string $mimeType = null
|
||||
) {
|
||||
$this->tempFile = $tempFile;
|
||||
|
||||
parent::__construct(
|
||||
$this->tempFilePath(),
|
||||
$this->sizeToReport,
|
||||
$this->error,
|
||||
$this->name,
|
||||
$this->mimeType
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new fake file.
|
||||
* @param null|mixed $clientFilename
|
||||
* @param null|mixed $clientMediaType
|
||||
*/
|
||||
public static function create(string $name, int|string $kilobytes = 0, int $error = 0, $clientFilename = null, $clientMediaType = null): File
|
||||
{
|
||||
return (new FileFactory())->create($name, $kilobytes, $error, $clientFilename, $clientMediaType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new fake file with content.
|
||||
*/
|
||||
public static function createWithContent(string $name, string $content): File
|
||||
{
|
||||
return (new FileFactory())->createWithContent($name, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new fake image.
|
||||
*/
|
||||
public static function image(string $name, int $width = 10, int $height = 10): File
|
||||
{
|
||||
return (new FileFactory())->image($name, $width, $height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "size" of the file in kilobytes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function size(int $kilobytes): static
|
||||
{
|
||||
$this->sizeToReport = $kilobytes * 1024;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the file.
|
||||
*/
|
||||
public function getSize(): int
|
||||
{
|
||||
return $this->sizeToReport ?: parent::getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "MIME type" for the file.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function mimeType(string $mimeType): static
|
||||
{
|
||||
$this->mimeType = $mimeType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MIME type of the file.
|
||||
*/
|
||||
public function getMimeType(): string
|
||||
{
|
||||
return $this->mimeType ?: MimeType::from($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the temporary file.
|
||||
*/
|
||||
protected function tempFilePath(): string
|
||||
{
|
||||
return stream_get_meta_data($this->tempFile)['uri'];
|
||||
}
|
||||
}
|
98
src/validation/tests/File/FileFactory.php
Normal file
98
src/validation/tests/File/FileFactory.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://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace HyperfTest\Validation\File;
|
||||
|
||||
use LogicException;
|
||||
|
||||
use function Hyperf\Tappable\tap;
|
||||
|
||||
class FileFactory
|
||||
{
|
||||
/**
|
||||
* Create a new fake file.
|
||||
* @param null|mixed $clientFilename
|
||||
* @param null|mixed $clientMediaType
|
||||
*/
|
||||
public function create(string $name, int|string $kilobytes = 0, int $error = 0, $clientFilename = null, $clientMediaType = null): File
|
||||
{
|
||||
if (is_string($kilobytes)) {
|
||||
return $this->createWithContent($name, $kilobytes);
|
||||
}
|
||||
|
||||
return tap(new File($name, tmpfile(), $error, $clientMediaType), function ($file) use ($kilobytes, $clientMediaType) {
|
||||
$file->sizeToReport = $kilobytes * 1024;
|
||||
$file->mimeTypeToReport = $clientMediaType;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new fake file with content.
|
||||
*/
|
||||
public function createWithContent(string $name, string $content): File
|
||||
{
|
||||
$tmpFile = tmpfile();
|
||||
|
||||
fwrite($tmpFile, $content);
|
||||
|
||||
return tap(new File($name, $tmpFile), function ($file) use ($tmpFile) {
|
||||
$file->sizeToReport = fstat($tmpFile)['size'];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new fake image.
|
||||
*
|
||||
* @throws LogicException
|
||||
*/
|
||||
public function image(string $name, int $width = 10, int $height = 10): File
|
||||
{
|
||||
return new File($name, $this->generateImage(
|
||||
$width,
|
||||
$height,
|
||||
pathinfo($name, PATHINFO_EXTENSION)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a dummy image of the given width and height.
|
||||
*
|
||||
* @return resource
|
||||
*
|
||||
* @throws LogicException
|
||||
*/
|
||||
protected function generateImage(int $width, int $height, string $extension)
|
||||
{
|
||||
if (! function_exists('imagecreatetruecolor')) {
|
||||
throw new LogicException('GD extension is not installed.');
|
||||
}
|
||||
|
||||
return tap(tmpfile(), function ($temp) use ($width, $height, $extension) {
|
||||
ob_start();
|
||||
|
||||
$extension = in_array($extension, ['jpeg', 'png', 'gif', 'webp', 'wbmp', 'bmp'])
|
||||
? strtolower($extension)
|
||||
: 'jpeg';
|
||||
|
||||
$image = imagecreatetruecolor($width, $height);
|
||||
|
||||
if (! function_exists($functionName = "image{$extension}")) {
|
||||
ob_get_clean();
|
||||
|
||||
throw new LogicException("{$functionName} function is not defined and image cannot be generated.");
|
||||
}
|
||||
|
||||
call_user_func($functionName, $image);
|
||||
|
||||
fwrite($temp, ob_get_clean());
|
||||
});
|
||||
}
|
||||
}
|
60
src/validation/tests/File/MimeType.php
Normal file
60
src/validation/tests/File/MimeType.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace HyperfTest\Validation\File;
|
||||
|
||||
use Hyperf\Support\MimeTypeExtensionGuesser;
|
||||
|
||||
class MimeType
|
||||
{
|
||||
/**
|
||||
* The mime types instance.
|
||||
*/
|
||||
private static ?MimeTypeExtensionGuesser $mime = null;
|
||||
|
||||
/**
|
||||
* Get the mime types instance.
|
||||
*/
|
||||
public static function getMimeTypes(): MimeTypeExtensionGuesser
|
||||
{
|
||||
if (self::$mime === null) {
|
||||
self::$mime = new MimeTypeExtensionGuesser();
|
||||
}
|
||||
|
||||
return self::$mime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MIME type for a file based on the file's extension.
|
||||
*/
|
||||
public static function from(string $filename): string
|
||||
{
|
||||
$extension = pathinfo($filename, PATHINFO_EXTENSION);
|
||||
|
||||
return self::get($extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MIME type for a given extension or return all mimes.
|
||||
*/
|
||||
public static function get(string $extension): string
|
||||
{
|
||||
return self::getMimeTypes()->guessMimeType($extension) ?? 'application/octet-stream';
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the extension of a given MIME type.
|
||||
*/
|
||||
public static function search(string $mimeType): ?string
|
||||
{
|
||||
return self::getMimeTypes()->guessExtension($mimeType);
|
||||
}
|
||||
}
|
62
src/validation/tests/FileTest.php
Normal file
62
src/validation/tests/FileTest.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace HyperfTest\Validation;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing0
|
||||
* @coversNothing
|
||||
*/
|
||||
class FileTest extends TestCase
|
||||
{
|
||||
public function testFile()
|
||||
{
|
||||
$file = \HyperfTest\Validation\File\File::create('foo.txt', 1024);
|
||||
$this->assertSame('text/plain', $file->getMimeType());
|
||||
$this->assertSame(1024 * 1024, $file->getSize());
|
||||
$this->assertSame(0, $file->getError());
|
||||
|
||||
$file = \HyperfTest\Validation\File\File::createWithContent('foo.txt', 'bar');
|
||||
$this->assertSame('text/plain', $file->getMimeType());
|
||||
$this->assertSame(3, $file->getSize());
|
||||
$this->assertSame(0, $file->getError());
|
||||
}
|
||||
|
||||
public function testImage()
|
||||
{
|
||||
$file = \HyperfTest\Validation\File\File::image('foo.png', 1024, 1024);
|
||||
$this->assertSame('image/png', $file->getMimeType());
|
||||
// 读取图片尺寸
|
||||
$imageSize = getimagesize($file->getPathname());
|
||||
$this->assertSame([1024, 1024], [$imageSize[0], $imageSize[1]]);
|
||||
$this->assertSame(0, $file->getError());
|
||||
$this->assertSame('png', $file->getExtension());
|
||||
|
||||
$file = \HyperfTest\Validation\File\File::image('foo.jpg', 1024, 1024);
|
||||
$this->assertSame('image/jpeg', $file->getMimeType());
|
||||
// 读取图片尺寸
|
||||
$imageSize = getimagesize($file->getPathname());
|
||||
$this->assertSame([1024, 1024], [$imageSize[0], $imageSize[1]]);
|
||||
$this->assertSame(0, $file->getError());
|
||||
$this->assertSame('jpg', $file->getExtension());
|
||||
|
||||
$file = \HyperfTest\Validation\File\File::image('foo.gif', 1024, 1024);
|
||||
$this->assertSame('image/gif', $file->getMimeType());
|
||||
// 读取图片尺寸
|
||||
$imageSize = getimagesize($file->getPathname());
|
||||
$this->assertSame([1024, 1024], [$imageSize[0], $imageSize[1]]);
|
||||
$this->assertSame(0, $file->getError());
|
||||
$this->assertSame('gif', $file->getExtension());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user