Skip to content

Commit

Permalink
AllowListParser allows empty list by default + always appends null type
Browse files Browse the repository at this point in the history
  • Loading branch information
matt committed Jan 27, 2024
1 parent 12bac9a commit 9e823d1
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/.phpcs-cache
/.phpunit.cache
/.phpunit.result.cache
/coverage/
/phpunit.xml
/vendor/
2 changes: 1 addition & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
failOnRisky="true"
failOnWarning="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutCoverageMetadata="true">
beStrictAboutCoverageMetadata="false">
<testsuites>
<testsuite name="default">
<directory suffix="Test.php">test</directory>
Expand Down
22 changes: 16 additions & 6 deletions src/ArrayShape/Filter/AllowListParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
{
public const DEFAULT_MAX_LITERALS = 10;

public function __construct(private int $maxLiterals = self::DEFAULT_MAX_LITERALS)
public function __construct(private bool $allowEmptyList = true, private int $maxLiterals = self::DEFAULT_MAX_LITERALS)
{
}

Expand All @@ -36,15 +36,22 @@ public function getTypes(FilterInterface $filter, array $existing): array
return $existing;
}

if (count($filter->getList()) > $this->maxLiterals) {
$list = $filter->getList();
if ($list === []) {
return $this->allowEmptyList ? $existing : [PsalmType::Null];
}

$existing[] = PsalmType::Null;

if (count($list) > $this->maxLiterals) {
return $filter->getStrict() === true
? $this->getStrictTypes($filter->getList(), $existing)
: $this->getLaxTypes($filter->getList(), $existing);
? $this->getStrictTypes($list, $existing)
: $this->getLaxTypes($list, $existing);
}

$types = $filter->getStrict() === true
? $this->getStrictLiteral($filter->getList(), $existing)
: $this->getLaxLiteral($filter->getList(), $existing);
? $this->getStrictLiteral($list, $existing)
: $this->getLaxLiteral($list, $existing);
return $this->appendUnique(PsalmType::Null, $types, $existing);
}

Expand Down Expand Up @@ -93,6 +100,8 @@ private function getStrictLiteral(array $list, array $existing): array
}
}

$types = $this->appendUnique(PsalmType::Null, $types, $existing);

if ($literals !== []) {
$types[] = new Literal($literals);
}
Expand Down Expand Up @@ -125,6 +134,7 @@ private function getLaxLiteral(array $list, array $existing): array
if ($types !== []) {
$types = $this->appendUnique(PsalmType::String, $types, $existing);
}
$types = $this->appendUnique(PsalmType::Null, $types, $existing);

if ($literals !== []) {
$types[] = new Literal($literals);
Expand Down
3 changes: 2 additions & 1 deletion src/ArrayShape/Filter/AllowListParserFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ public function __invoke(ContainerInterface $container): AllowListParser
/** @var ConfigProviderArray $config */
$config = $container->get('config');
// phpcs:disable Generic.Files.LineLength.TooLong
$allowEmpty = (bool)($config['laminas-form-cli']['array-shape']['filter']['allow-list']['allow-empty-list'] ?? true);
$maxLiterals = (int) ($config['laminas-form-cli']['array-shape']['filter']['allow-list']['max-literals'] ?? AllowListParser::DEFAULT_MAX_LITERALS);
// phpcs:enable

return new AllowListParser($maxLiterals);
return new AllowListParser($allowEmpty, $maxLiterals);
}
}
51 changes: 35 additions & 16 deletions test/ArrayShape/Filter/AllowListParserFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
use Kynx\Laminas\FormCli\ArrayShape\Type\Literal;
use Kynx\Laminas\FormCli\ArrayShape\Type\PsalmType;
use Laminas\Filter\AllowList;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;

/**
* @covers \Kynx\Laminas\FormCli\ArrayShape\Filter\AllowListParserFactory
*/
#[CoversClass(AllowListParserFactory::class)]
final class AllowListParserFactoryTest extends TestCase
{
public function testInvokeReturnsDefaultInstance(): void
Expand All @@ -25,26 +24,33 @@ public function testInvokeReturnsDefaultInstance(): void
$factory = new AllowListParserFactory();
$instance = $factory($container);

$expected = [new Literal(["'a'"])];
$expected = [PsalmType::Null, new Literal(["'a'"])];
$filter = new AllowList(['list' => ['a'], 'strict' => true]);
$actual = $instance->getTypes($filter, [PsalmType::String]);

self::assertEquals($expected, $actual);
}

public function testInvokeReturnsConfiguredInstance(): void
public function testInvokeConfiguresAllowEmptyList(): void
{
$config = [
'laminas-form-cli' => [
'array-shape' => [
'filter' => [
'allow-list' => [
'max-literals' => 0,
],
],
],
],
];
$config = $this->getConfig(['allow-empty-list' => false]);
$container = $this->createStub(ContainerInterface::class);
$container->method('get')
->willReturnMap([['config', $config]]);

$factory = new AllowListParserFactory();
$instance = $factory($container);

$expected = [PsalmType::Null];
$filter = new AllowList(['list' => []]);
$actual = $instance->getTypes($filter, [PsalmType::String]);

self::assertSame($expected, $actual);
}

public function testInvokeConfiguresMaxLiteral(): void
{
$config = $this->getConfig(['max-literals' => 0]);
$container = $this->createStub(ContainerInterface::class);
$container->method('get')
->willReturnMap([['config', $config]]);
Expand All @@ -58,4 +64,17 @@ public function testInvokeReturnsConfiguredInstance(): void

self::assertSame($expected, $actual);
}

private function getConfig(array $config): array
{
return [
'laminas-form-cli' => [
'array-shape' => [
'filter' => [
'allow-list' => $config,
],
],
],
];
}
}
25 changes: 19 additions & 6 deletions test/ArrayShape/Filter/AllowListParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
use Laminas\Filter\AllowList;
use Laminas\Filter\Boolean;
use Laminas\Filter\FilterInterface;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

/**
* @covers \Kynx\Laminas\FormCli\ArrayShape\Filter\AllowListParser
* @psalm-import-type ParsedArray from AbstractParsedType
*/
#[CoversClass(AllowListParser::class)]
final class AllowListParserTest extends TestCase
{
/**
Expand All @@ -36,10 +37,13 @@ public static function getTypeLiteralProvider(): array
// phpcs:disable Generic.Files.LineLength.TooLong
return [
'invalid' => [new Boolean(), [PsalmType::Int], [PsalmType::Int]],
'strict list' => [new AllowList(['list' => ['foo', 123], 'strict' => true]), [PsalmType::Int, PsalmType::String, PsalmType::Null], [new Literal(["'foo'", 123]), PsalmType::Null]],
'lax list' => [new AllowList(['list' => ['foo', 123], 'strict' => false]), [PsalmType::Int, PsalmType::String, PsalmType::Null], [new Literal(["'foo'", 123, "'123'"]), PsalmType::Null]],
'strict existing' => [new AllowList(['list' => ['foo', 123], 'strict' => true]), [PsalmType::Int], [new Literal([123])]],
'lax existing' => [new AllowList(['list' => ['foo', 123], 'strict' => false]), [PsalmType::String], [new Literal(["'foo'", "'123'"])]],
'empty list' => [new AllowList(['list' => []]), [PsalmType::Bool], [PsalmType::Bool]],
'strict list' => [new AllowList(['list' => ['foo', 123], 'strict' => true]), [PsalmType::Int, PsalmType::String], [PsalmType::Null, new Literal(["'foo'", 123])]],
'strict not literal' => [new AllowList(['list' => [1.23], 'strict' => true]), [PsalmType::Float], [PsalmType::Float, PsalmType::Null]],
'lax list' => [new AllowList(['list' => ['foo', 123], 'strict' => false]), [PsalmType::Int, PsalmType::String], [PsalmType::Null, new Literal(["'foo'", 123, "'123'"])]],
'lax not literal' => [new AllowList(['list' => [1.23], 'strict' => false]), [PsalmType::Float], [PsalmType::Float, PsalmType::Null]],
'strict existing' => [new AllowList(['list' => ['foo', 123], 'strict' => true]), [PsalmType::Int], [PsalmType::Null, new Literal([123])]],
'lax existing' => [new AllowList(['list' => ['foo', 123], 'strict' => false]), [PsalmType::String], [PsalmType::Null, new Literal(["'foo'", "'123'"])]],
];
// phpcs:enable
}
Expand All @@ -50,7 +54,7 @@ public static function getTypeLiteralProvider(): array
#[DataProvider('getTypeTypeProvider')]
public function testGetTypesReturnsTypes(FilterInterface $filter, array $existing, array $expected): void
{
$parser = new AllowListParser(1);
$parser = new AllowListParser(true, 1);
$actual = $parser->getTypes($filter, $existing);
self::assertSame($expected, $actual);
}
Expand All @@ -66,4 +70,13 @@ public static function getTypeTypeProvider(): array
];
// phpcs:enable
}

public function testGetTypesDisallowsEmptyList(): void
{
$expected = [PsalmType::Null];
$parser = new AllowListParser(false);
$filter = new AllowList(['list' => []]);
$actual = $parser->getTypes($filter, [PsalmType::String]);
self::assertSame($expected, $actual);
}
}

0 comments on commit 9e823d1

Please sign in to comment.