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 new filter to filter to an enum #94

Open
wants to merge 10 commits into
base: 2.31.x
Choose a base branch
from
50 changes: 19 additions & 31 deletions src/ToEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

namespace Laminas\Filter;

use BackedEnum;
use Laminas\Filter\Exception\RuntimeException;
use Laminas\Stdlib\ArrayUtils;
use Traversable;
use UnitEnum;

use function array_column;
use function constant;
use function in_array;
use function is_array;
use function is_int;
use function is_string;
Expand All @@ -21,53 +25,39 @@
*/
final class ToEnum implements FilterInterface
{
/**
* @var class-string<UnitEnum>|null
*/
/** @var class-string<UnitEnum>|null */
private ?string $enumClass = null;

/**
* @param class-string<UnitEnum>|Traversable|Options $enumOrOptions
* @param Traversable|class-string<UnitEnum>|Options $enumOrOptions
*/
public function __construct($enumOrOptions)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love for this to only accept class-string<UnitEnum> :D

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor arg needs to be

Suggested change
public function __construct($enumOrOptions)
public function __construct($enumOrOptions = [])

or

Suggested change
public function __construct($enumOrOptions)
public function __construct($enumOrOptions = null)

(null would also need to be added to the @param in this case)

The FilterPluginManager will just new the filter without arguments unless a factory is created (which is pointless).

In order for the filter to be available in the plugin manager, FilterPluginManager must be updated with entries under factories and aliases

The problem is that at this point, PHPUnit will fail CI on PHP 8.0 (Along with Psalm)

I don't see an easy way around this at the moment unless dropping support for PHP 8.0 is an option.

Thoughts @Ocramius ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FilterPluginManager will just new the filter without arguments unless a factory is created…

Correct! 👍🏻

{
if ($enumOrOptions instanceof Traversable) {
/** @var Options $enumOrOptions */
$enumOrOptions = ArrayUtils::iteratorToArray($enumOrOptions);
}

if (
is_array($enumOrOptions) &&
isset($enumOrOptions['enum'])
) {
$this->setEnum($enumOrOptions['enum']);

return;
$this->enumClass = $enumOrOptions['enum'];
}

if (is_string($enumOrOptions)) {
$this->setEnum($enumOrOptions);
$this->enumClass = $enumOrOptions;
}
}

/**
* @param class-string<UnitEnum> $enum
*/
protected function setEnum(string $enum): self
{
$this->enumClass = $enum;

return $this;
}

/**
* Defined by Laminas\Filter\FilterInterface
*
* Returns an enum representation of $value or null
* Returns an enum representation of $value if matching.
*
* @param mixed $value
* @return UnitEnum|null
*/
reinfi marked this conversation as resolved.
Show resolved Hide resolved
public function filter($value): ?UnitEnum
public function filter($value): mixed
{
$enum = $this->enumClass;

Expand All @@ -78,23 +68,21 @@ public function filter($value): ?UnitEnum
}

if (! is_string($value) && ! is_int($value)) {
return null;
return $value;
}

if (is_subclass_of($enum, 'BackedEnum')) {
return $enum::tryFrom($value);
if (is_subclass_of($enum, BackedEnum::class)) {
return $enum::tryFrom($value) ?: $value;
}

if (! is_string($value) || ! is_subclass_of($enum, 'UnitEnum')) {
return null;
if (! is_subclass_of($enum, UnitEnum::class)) {
return $value;
}

foreach ($enum::cases() as $enumCase) {
if ($enumCase->name === $value) {
return $enumCase;
}
if (in_array($value, array_column($enum::cases(), 'name'), true)) {
return constant($enum . '::' . $value);
}

return null;
return $value;
}
}
15 changes: 12 additions & 3 deletions test/ToEnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace LaminasTest\Filter;

use BackedEnum;
use Laminas\Filter\Exception\RuntimeException;
use Laminas\Filter\ToEnum;
use LaminasTest\Filter\TestAsset\TestIntBackedEnum;
Expand All @@ -17,6 +18,7 @@
*/
class ToEnumTest extends TestCase
{
/** @return array<string, array{0: class-string<UnitEnum>, 1: string|int, 2: UnitEnum|BackedEnum}> */
public function filterableValuesProvider(): array
{
return [
Expand All @@ -41,13 +43,17 @@ public function testCanFilterToEnum(string $enumClass, string|int $value, UnitEn
* @dataProvider filterableValuesProvider
* @param class-string<UnitEnum> $enumClass
*/
public function testCanFilterToEnumWithOptions(string $enumClass, string|int $value, UnitEnum $expectedFilteredValue): void
{
public function testCanFilterToEnumWithOptions(
string $enumClass,
string|int $value,
UnitEnum $expectedFilteredValue
): void {
$filter = new ToEnum(['enum' => $enumClass]);

self::assertSame($expectedFilteredValue, $filter->filter($value));
}

/** @return array<string, array{0: class-string<UnitEnum>, 1: mixed}> */
public function unfilterableValuesProvider(): array
{
return [
Expand All @@ -68,14 +74,17 @@ public function testFiltersToNull(string $enumClass, mixed $value): void
{
$filter = new ToEnum($enumClass);

self::assertNull($filter->filter($value));
self::assertEquals($value, $filter->filter($value));
}

public function testThrowsExceptionIfEnumNotSet(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('enum class not set');

/**
* @psalm-suppress InvalidArgument
*/
$filter = new ToEnum([]);

$filter->filter('foo');
Expand Down