Skip to content

Commit

Permalink
Return original value for any invalid input to filter
Browse files Browse the repository at this point in the history
Signed-off-by: ramchale <[email protected]>
  • Loading branch information
ramchale committed Dec 7, 2024
1 parent bcfbd21 commit f9f1f77
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 41 deletions.
66 changes: 35 additions & 31 deletions src/MonthSelect.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@

namespace Laminas\Filter;

use function array_reduce;
use function count;
use function filter_var;
use function is_array;
use function ksort;
use function vsprintf;
use function is_numeric;
use function sprintf;

use const FILTER_VALIDATE_INT;

/**
* @psalm-type Options = array{
* null_on_empty?: bool,
* null_on_all_empty?: bool,
* }
* @psalm-type InputArray = array{
* year: numeric,
* month: numeric,
* year: int,
* month: int,
* }
* @template TInput of array<array-key, numeric>
* @implements FilterInterface<InputArray|string>
* @implements FilterInterface<string>
*/
final class MonthSelect implements FilterInterface
{
private readonly bool $returnNullIfAnyFieldEmpty;
private readonly bool $returnNullIfAllFieldsEmpty;
private const EXPECTED_INPUTS = 2;

/** Options $options */
/** @param Options $options */
public function __construct(array $options = [])
{
$this->returnNullIfAnyFieldEmpty = $options['null_on_empty'] ?? false;
Expand All @@ -40,44 +40,48 @@ public function __invoke(mixed $value): mixed
return $this->filter($value);
}

/**
* @psalm-return ($value is InputArray ? string : mixed|null)
*/
/** @psalm-suppress InvalidReturnType */
public function filter(mixed $value): mixed
{
if (! is_array($value)) {
return $value;
}

if (count($value) !== self::EXPECTED_INPUTS) {
return $value;
}
/** @var mixed $month */
$month = $value['month'] ?? null;
/** @var mixed $year */
$year = $value['year'] ?? null;

// Convert the date to a specific format
if (
$this->returnNullIfAnyFieldEmpty
&& array_reduce($value, self::reduce(...), false)
) {
if ($this->returnNullIfAnyFieldEmpty && ($month === null || $year === null)) {
return null;
}

if (
$this->returnNullIfAllFieldsEmpty
&& array_reduce($value, self::reduce(...), true)
) {
if ($this->returnNullIfAllFieldsEmpty && $month === null && $year === null) {
return null;
}

ksort($value);


/** @psalm-var array<array-key, string> $value Forcing the type here because it has already been asserted */
if (! $this->isParsableAsDateValue($month, 1, 12) || ! $this->isParsableAsDateValue($year, 0, 9999)) {
/** @psalm-suppress InvalidReturnStatement */
return $value;
}

return vsprintf('%2$s-%1$s', $value);
/** @psalm-suppress MixedArgument */
return sprintf('%s-%s', $year, $month);
}

private static function reduce(bool $soFar, string|null $value): bool
private function isParsableAsDateValue(mixed $value, int $lowestValue, int $highestValue): bool
{
return $soFar || ($value === null || $value === '');
if (
! is_numeric($value)
|| filter_var(
$value,
FILTER_VALIDATE_INT,
['options' => ['min_range' => $lowestValue, 'max_range' => $highestValue]]
) === false
) {
return false;
}

return true;
}
}
30 changes: 20 additions & 10 deletions test/MonthSelectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class MonthSelectTest extends TestCase
#[DataProvider('provideFilter')]
public function testFilter(array $options, array $input, ?string $expected): void
{
/** @psalm-suppress MixedArgumentTypeCoercion */
$sut = new MonthSelectFilter($options);
self::assertSame($expected, $sut->filter($input));
}
Expand All @@ -22,11 +23,12 @@ public static function provideFilter(): array
{
return [
[[], ['year' => '2014', 'month' => '10'], '2014-10'],
[[], ['year' => '2014', 'month' => '48'], '2014-48'],
[[], ['year' => 'not a year', 'month' => '10'], 'not a year-10'],
[[], ['year' => '2023', 'month' => 'not a month'], '2023-not a month'],
[[], ['year' => 2014, 'month' => 10], '2014-10'],
[['null_on_empty' => true], ['year' => null, 'month' => '10'], null],
[['null_on_empty' => true], ['month' => null], null],
[['null_on_empty' => true], ['year' => null], null],
[['null_on_all_empty' => true], ['year' => null, 'month' => null], null],
[['null_on_all_empty' => true], [], null],
];
}

Expand All @@ -41,13 +43,21 @@ public function testInvalidInput(mixed $value): void
public static function provideInvalidFilterValues(): array
{
return [
[[]],
[['month' => '10']],
[['year' => '2023']],
[true],
['string'],
[10],
[10.5],
'empty array' => [[]],
'missing year' => [['month' => '10']],
'missing month' => [['year' => '2023']],
'passed bool' => [true],
'passed string' => ['string'],
'passed int' => [10],
'passed float' => [10.5],
'invalid keys' => [['should be year' => '2014', 'should be month' => '10']],
'year is invalid type' => [['year' => true, 'month' => '09']],
'year out of bounds' => [['year' => '-1', 'month' => '09']],
'month is too high' => [['year' => '2014', 'month' => '13']],
'month is low' => [['year' => '2014', 'month' => '0']],
'month is invalid type' => [['year' => '2014', 'month' => true]],
'invalid year' => [['year' => 'not a year', 'month' => '10']],
'invalid month' => [['year' => '2023', 'month' => 'not a month']],
];
}
}

0 comments on commit f9f1f77

Please sign in to comment.