Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add handling for enum as haystack #168

Open
wants to merge 3 commits into
base: 2.30.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 48 additions & 4 deletions src/InArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@

namespace Laminas\Validator;

use BackedEnum;
use RecursiveArrayIterator;
use RecursiveIteratorIterator;
use UnitEnum;

use function array_map;
use function in_array;
use function interface_exists;
use function is_array;
use function is_bool;
use function is_float;
use function is_int;
use function is_string;
use function is_subclass_of;

class InArray extends AbstractValidator
{
Expand Down Expand Up @@ -42,7 +48,7 @@ class InArray extends AbstractValidator
/**
* Haystack of possible values
*
* @var array
* @var array|class-string<UnitEnum>
*/
protected $haystack;

Expand All @@ -66,7 +72,7 @@ class InArray extends AbstractValidator
/**
* Returns the haystack option
*
* @return mixed
* @return array|class-string<UnitEnum>
* @throws Exception\RuntimeException If haystack option is not set.
*/
public function getHaystack()
Expand All @@ -80,11 +86,21 @@ public function getHaystack()
/**
* Sets the haystack option
*
* @param mixed $haystack
* @param array|class-string<UnitEnum> $haystack
* @return $this Provides a fluent interface
*/
public function setHaystack(array $haystack)
public function setHaystack($haystack)
{
if (! is_array($haystack)) {
if (! is_string($haystack) || ! interface_exists(UnitEnum::class)) {
throw new Exception\RuntimeException('haystack can only be an array');
}

if (! is_subclass_of($haystack, UnitEnum::class)) {
throw new Exception\RuntimeException('haystack has invalid type');
}
}

$this->haystack = $haystack;
return $this;
}
Expand Down Expand Up @@ -172,6 +188,34 @@ public function isValid($value)
// we create a copy of the haystack in case we need to modify it
$haystack = $this->getHaystack();

if (! is_array($haystack)) {
if (is_subclass_of($haystack, 'BackedEnum')) {
$enumHaystack = array_map(
static fn (BackedEnum $case) => $case->value,
$haystack::cases()
);

if (in_array($value, $enumHaystack, (bool) $this->strict)) {
return true;
}

$this->error(self::NOT_IN_ARRAY);
return false;
}

$enumHaystack = array_map(
static fn (UnitEnum $case): string => $case->name,
$haystack::cases()
);

if (in_array($value, $enumHaystack, (bool) $this->strict)) {
return true;
}

$this->error(self::NOT_IN_ARRAY);
return false;
}

// if the input is a string or float, and vulnerability protection is on
// we type cast the input to a string
if (
Expand Down
38 changes: 38 additions & 0 deletions test/InArrayTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

use Laminas\Validator\Exception\RuntimeException;
use Laminas\Validator\InArray;
use LaminasTest\Validator\TestAsset\Enum\TestBackedIntEnum;
use LaminasTest\Validator\TestAsset\Enum\TestBackedStringEnum;
use LaminasTest\Validator\TestAsset\Enum\TestUnitEnum;
use PHPUnit\Framework\TestCase;

use function array_keys;
Expand Down Expand Up @@ -472,4 +475,39 @@ public function testBooleanNotStrictEnforcesNonStrictMode(array $haystack, mixed
self::assertTrue($validator->isValid($valid));
self::assertFalse($validator->isValid($invalid));
}

/**
* @requires PHP 8.1
*/
public function testEnumValidation(): void
{
$validator = new InArray([
'haystack' => TestUnitEnum::class,
]);

self::assertTrue($validator->isValid('foo'));
self::assertFalse($validator->isValid('baz'));

$validator = new InArray([
'haystack' => TestBackedStringEnum::class,
]);

self::assertTrue($validator->isValid('foo'));
self::assertFalse($validator->isValid('baz'));

$validator = new InArray([
'haystack' => TestBackedIntEnum::class,
]);

self::assertTrue($validator->isValid(1));
self::assertFalse($validator->isValid(3));

$validator = new InArray([
'haystack' => TestBackedIntEnum::class,
'strict' => true,
]);

self::assertTrue($validator->isValid(1));
self::assertFalse($validator->isValid('2'));
}
}
11 changes: 11 additions & 0 deletions test/TestAsset/Enum/TestBackedIntEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace LaminasTest\Validator\TestAsset\Enum;

enum TestBackedIntEnum: int
{
case Foo = 1;
case Bar = 2;
}
11 changes: 11 additions & 0 deletions test/TestAsset/Enum/TestBackedStringEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace LaminasTest\Validator\TestAsset\Enum;

enum TestBackedStringEnum: string
{
case Foo = 'foo';
case Bar = 'bar';
}
11 changes: 11 additions & 0 deletions test/TestAsset/Enum/TestUnitEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace LaminasTest\Validator\TestAsset\Enum;

enum TestUnitEnum
{
case foo;
case bar;
}