Skip to content

Commit

Permalink
Add Image validation rule (#670)
Browse files Browse the repository at this point in the history
Co-authored-by: Alexey Rogachev <arogachev90@gmail.com>
Co-authored-by: Alexander Makarov <sam@rmcreative.ru>
  • Loading branch information
3 people committed Mar 28, 2024
1 parent d9a7bf7 commit 66cfeae
Show file tree
Hide file tree
Showing 19 changed files with 761 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Expand Up @@ -26,7 +26,7 @@ jobs:
phpunit:
uses: yiisoft/actions/.github/workflows/phpunit.yml@master
with:
extensions: intl
extensions: intl,fileinfo
coverage: xdebug
os: >-
['ubuntu-latest', 'windows-latest']
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

- New #665: Add methods `addErrorWithFormatOnly()` and `addErrorWithoutPostProcessing()` to `Result` object (@vjik)
- Enh #668: Clarify psalm types in `Result` (@vjik)
- New #670: Add `Image` validation rule (@vjik, @arogachev)

## 1.2.0 February 21, 2024

Expand Down
1 change: 1 addition & 0 deletions composer-require-checker.json
Expand Up @@ -15,6 +15,7 @@
"Reflection",
"SPL",
"standard",
"fileinfo",
"intl"
],
"scan-files": []
Expand Down
6 changes: 4 additions & 2 deletions composer.json
Expand Up @@ -30,11 +30,12 @@
"php": "^8.0",
"ext-mbstring": "*",
"psr/container": "^1.0|^2.0",
"psr/http-message": "^1.0|^2.0",
"yiisoft/arrays": "^2.1|^3.0",
"yiisoft/translator": "^2.1|^3.0",
"yiisoft/friendly-exception": "^1.0",
"yiisoft/network-utilities": "^1.0",
"yiisoft/strings": "^2.1"
"yiisoft/strings": "^2.1",
"yiisoft/translator": "^2.1|^3.0"
},
"require-dev": {
"jetbrains/phpstorm-attributes": "^1.0",
Expand All @@ -52,6 +53,7 @@
},
"suggest": {
"ext-intl": "Allows using IDN validation for emails",
"ext-fileinfo": "To use image rule",
"yiisoft/di": "To create rule handlers via Yii DI"
},
"autoload": {
Expand Down
4 changes: 4 additions & 0 deletions docs/guide/en/built-in-rules.md
Expand Up @@ -45,6 +45,10 @@ Here is a list of all available built-in rules, divided by category.
- [Count](../../../src/Rule/Count.php)
- [OneOf](../../../src/Rule/OneOf.php)

### File rules

- [Image](../../../src/Rule/Image/Image.php)

### General purpose rules

- [Callback](../../../src/Rule/Callback.php)
Expand Down
30 changes: 30 additions & 0 deletions src/Rule/Image/CompositeImageInfoProvider.php
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Validator\Rule\Image;

final class CompositeImageInfoProvider implements ImageInfoProviderInterface
{
/**
* @var ImageInfoProviderInterface[]
*/
private array $providers;

public function __construct(
ImageInfoProviderInterface ...$providers
) {
$this->providers = $providers;
}

public function get(string $path): ?ImageInfo
{
foreach ($this->providers as $provider) {
$info = $provider->get($path);
if ($info !== null) {
return $info;
}
}
return null;
}
}
243 changes: 243 additions & 0 deletions src/Rule/Image/Image.php
@@ -0,0 +1,243 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Validator\Rule\Image;

use Attribute;
use Closure;
use Yiisoft\Validator\Rule\Trait\SkipOnEmptyTrait;
use Yiisoft\Validator\Rule\Trait\SkipOnErrorTrait;
use Yiisoft\Validator\Rule\Trait\WhenTrait;
use Yiisoft\Validator\RuleWithOptionsInterface;
use Yiisoft\Validator\SkipOnEmptyInterface;
use Yiisoft\Validator\SkipOnErrorInterface;
use Yiisoft\Validator\WhenInterface;

/**
* Defines validation options to check that a value is an image and optionally check its dimensions.
*
* @see ImageHandler
*
* @psalm-import-type SkipOnEmptyValue from SkipOnEmptyInterface
* @psalm-import-type WhenType from WhenInterface
*/
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
final class Image implements RuleWithOptionsInterface, SkipOnErrorInterface, WhenInterface, SkipOnEmptyInterface
{
use SkipOnEmptyTrait;
use SkipOnErrorTrait;
use WhenTrait;

/**
* @param int|null $width Expected exact width of validated image file.
* @param int|null $height Expected exact height of validated image file.
* @param int|null $minWidth Expected minimum width of validated image file.
* @param int|null $minHeight Expected minimum height of validated image file.
* @param int|null $maxWidth Expected maximum width of validated image file.
* @param int|null $maxHeight Expected maximum height of validated image file.
* @param string $notImageMessage A message used when the validated value is not valid image file.
*
* You may use the following placeholders in the message:
*
* - `{attribute}`: the translated label of the attribute being validated.
*
* @param string $notExactWidthMessage A message used when the width of validated image file doesn't exactly equal
* to {@see $width}.
*
* You may use the following placeholders in the message:
*
* - `{attribute}`: the translated label of the attribute being validated.
* - `{exactly}`: expected exact width of validated image file.
*
* @param string $notExactHeightMessage A message used when the height of validated image file doesn't exactly equal
* to {@see $height}.
*
* You may use the following placeholders in the message:
*
* - `{attribute}`: the translated label of the attribute being validated.
* - `{exactly}`: expected exact height of validated image file.
*
* @param string $tooSmallWidthMessage A message used when the width of validated image file is less than
* {@see $minWidth}.
*
* You may use the following placeholders in the message:
*
* - `{attribute}`: the translated label of the attribute being validated.
* - `{limit}`: expected minimum width of validated image file.
*
* @param string $tooSmallHeightMessage A message used when the height of validated image file is less than
* {@see $minHeight}.
*
* You may use the following placeholders in the message:
*
* - `{attribute}`: the translated label of the attribute being validated.
* - `{limit}`: expected minimum height of validated image file.
*
* @param string $tooLargeWidthMessage A message used when the width of validated image file is more than
* {@see $maxWidth}.
*
* You may use the following placeholders in the message:
*
* - `{attribute}`: the translated label of the attribute being validated.
* - `{limit}`: expected maximum width of validated image file.
*
* @param string $tooLargeHeightMessage A message used when the height of validated image file is more than
* {@see $maxHeight}.
*
* You may use the following placeholders in the message:
*
* - `{attribute}`: the translated label of the attribute being validated.
* - `{limit}`: expected maximum height of validated image file.
* @param bool|callable|null $skipOnEmpty Whether to skip this rule if the value validated is empty.
* See {@see SkipOnEmptyInterface}.
* @param bool $skipOnError Whether to skip this rule if any of the previous rules gave an error.
* See {@see SkipOnErrorInterface}.
* @param Closure|null $when A callable to define a condition for applying the rule. See {@see WhenInterface}.
*
* @psalm-param SkipOnEmptyValue $skipOnEmpty
* @psalm-param WhenType $when
*/
public function __construct(
private ?int $width = null,
private ?int $height = null,
private ?int $minWidth = null,
private ?int $minHeight = null,
private ?int $maxWidth = null,
private ?int $maxHeight = null,
private string $notImageMessage = 'The value must be an image.',
private string $notExactWidthMessage = 'The width of image "{attribute}" must be exactly {exactly, number} {exactly, plural, one{pixel} other{pixels}}.',
private string $notExactHeightMessage = 'The height of image "{attribute}" must be exactly {exactly, number} {exactly, plural, one{pixel} other{pixels}}.',
private string $tooSmallWidthMessage = 'The width of image "{attribute}" cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.',
private string $tooSmallHeightMessage = 'The height of image "{attribute}" cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.',
private string $tooLargeWidthMessage = 'The width of image "{attribute}" cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.',
private string $tooLargeHeightMessage = 'The height of image "{attribute}" cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.',
private mixed $skipOnEmpty = null,
private bool $skipOnError = false,
private Closure|null $when = null,
) {
}

public function getWidth(): ?int
{
return $this->width;
}

public function getHeight(): ?int
{
return $this->height;
}

public function getMinWidth(): ?int
{
return $this->minWidth;
}

public function getMinHeight(): ?int
{
return $this->minHeight;
}

public function getMaxWidth(): ?int
{
return $this->maxWidth;
}

public function getMaxHeight(): ?int
{
return $this->maxHeight;
}

public function getNotImageMessage(): string
{
return $this->notImageMessage;
}

public function getNotExactWidthMessage(): string
{
return $this->notExactWidthMessage;
}

public function getNotExactHeightMessage(): string
{
return $this->notExactHeightMessage;
}

public function getTooSmallWidthMessage(): string
{
return $this->tooSmallWidthMessage;
}

public function getTooSmallHeightMessage(): string
{
return $this->tooSmallHeightMessage;
}

public function getTooLargeWidthMessage(): string
{
return $this->tooLargeWidthMessage;
}

public function getTooLargeHeightMessage(): string
{
return $this->tooLargeHeightMessage;
}

public function getName(): string
{
return 'image';
}

public function getHandler(): string
{
return ImageHandler::class;
}

public function getOptions(): array
{
return [
'notExactWidthMessage' => [
'template' => $this->notExactWidthMessage,
'parameters' => [
'exactly' => $this->width,
],
],
'notExactHeightMessage' => [
'template' => $this->notExactHeightMessage,
'parameters' => [
'exactly' => $this->height,
],
],
'tooSmallWidthMessage' => [
'template' => $this->tooSmallWidthMessage,
'parameters' => [
'limit' => $this->minWidth,
],
],
'tooSmallHeightMessage' => [
'template' => $this->tooSmallHeightMessage,
'parameters' => [
'limit' => $this->minHeight,
],
],
'tooLargeWidthMessage' => [
'template' => $this->tooLargeWidthMessage,
'parameters' => [
'limit' => $this->maxWidth,
],
],
'tooLargeHeightMessage' => [
'template' => $this->tooLargeHeightMessage,
'parameters' => [
'limit' => $this->maxHeight,
],
],
'notImageMessage' => [
'template' => $this->notImageMessage,
'parameters' => [],
],
'skipOnEmpty' => $this->getSkipOnEmptyOption(),
'skipOnError' => $this->skipOnError,
];
}
}

0 comments on commit 66cfeae

Please sign in to comment.