From 41ad31e7499ae304fc61d928ebddfd4162e55d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Bia=C5=82czak?= Date: Wed, 11 Jun 2025 13:47:39 +0200 Subject: [PATCH 1/3] IBX-8697: Added validation for Keyword field type values in content and tests --- phpstan-baseline.neon | 12 +++++ src/lib/FieldType/Keyword/Type.php | 41 +++++++++++++++++ tests/lib/FieldType/KeywordTest.php | 71 +++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a68bcbeb51..daa504a674 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -53442,6 +53442,18 @@ parameters: count: 1 path: tests/lib/FieldType/KeywordTest.php + - + message: '#^Method Ibexa\\Tests\\Core\\FieldType\\KeywordTest\:\:provideValidDataForValidate\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/lib/FieldType/KeywordTest.php + + - + message: '#^Method Ibexa\\Tests\\Core\\FieldType\\KeywordTest\:\:provideInvalidDataForValidate\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/lib/FieldType/KeywordTest.php + - message: '#^Method Ibexa\\Tests\\Core\\FieldType\\MapLocationTest\:\:getSettingsSchemaExpectation\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue diff --git a/src/lib/FieldType/Keyword/Type.php b/src/lib/FieldType/Keyword/Type.php index 6ef4f2e24a..caf546e96d 100644 --- a/src/lib/FieldType/Keyword/Type.php +++ b/src/lib/FieldType/Keyword/Type.php @@ -11,6 +11,7 @@ use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; use Ibexa\Core\Base\Exceptions\InvalidArgumentType; use Ibexa\Core\FieldType\FieldType; +use Ibexa\Core\FieldType\ValidationError; use Ibexa\Core\FieldType\Value as BaseValue; use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Translation\TranslationContainerInterface; @@ -22,6 +23,8 @@ */ class Type extends FieldType implements TranslationContainerInterface { + public const MAX_KEYWORD_LENGTH = 255; + /** * Returns the field type identifier for this field type. * @@ -87,6 +90,44 @@ protected function checkValueStructure(BaseValue $value) $value->values ); } + + foreach ($value->values as $keyword) { + if (!is_string($keyword) || mb_strlen($keyword) > self::MAX_KEYWORD_LENGTH) { + throw new InvalidArgumentType( + '$value->values[]', + 'string up to ' . self::MAX_KEYWORD_LENGTH . ' characters', + $keyword + ); + } + } + } + + /** + * @param \Ibexa\Core\FieldType\Keyword\Value $fieldValue + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue): array + { + $validationErrors = []; + + foreach ($fieldValue->values as $keyword) { + if (!is_string($keyword)) { + $validationErrors[] = new ValidationError( + 'Each keyword must be a string.', + null, + [], + 'values' + ); + } elseif (mb_strlen($keyword) > self::MAX_KEYWORD_LENGTH) { + $validationErrors[] = new ValidationError( + 'Each keyword must be at most ' . self::MAX_KEYWORD_LENGTH . ' characters long.', + null, + [], + 'values' + ); + } + } + + return $validationErrors; } /** diff --git a/tests/lib/FieldType/KeywordTest.php b/tests/lib/FieldType/KeywordTest.php index 61a1646137..a02adf4260 100644 --- a/tests/lib/FieldType/KeywordTest.php +++ b/tests/lib/FieldType/KeywordTest.php @@ -9,6 +9,7 @@ use Ibexa\Core\Base\Exceptions\InvalidArgumentException; use Ibexa\Core\FieldType\Keyword\Type as KeywordType; use Ibexa\Core\FieldType\Keyword\Value as KeywordValue; +use Ibexa\Core\FieldType\ValidationError; /** * @group fieldType @@ -238,6 +239,76 @@ public function provideDataForGetName(): array [new KeywordValue(['foo', 'bar']), 'foo, bar', [], 'en_GB'], ]; } + + /** + * @return iterable + */ + public function provideValidDataForValidate(): iterable + { + yield 'multiple keywords' => [ + [], + new KeywordValue(['foo', 'bar']), + ]; + + yield 'empty string keyword' => [ + [], + new KeywordValue(['']), + ]; + + yield 'empty keyword list' => [ + [], + new KeywordValue([]), + ]; + } + + /** + * @return iterable}> + */ + public function provideInvalidDataForValidate(): iterable + { + $maxLen = KeywordType::MAX_KEYWORD_LENGTH; + + yield 'non-string keyword (int)' => [ + [], + // @phpstan-ignore-next-line + new KeywordValue(['valid', 123]), + [ + new ValidationError( + 'Each keyword must be a string.', + null, + [], + 'values' + ), + ], + ]; + + yield 'non-string keyword (null)' => [ + [], + // @phpstan-ignore-next-line + new KeywordValue(['valid', null]), + [ + new ValidationError( + 'Each keyword must be a string.', + null, + [], + 'values' + ), + ], + ]; + + yield 'too long keyword' => [ + [], + new KeywordValue(['valid', str_repeat('a', $maxLen + 1)]), + [ + new ValidationError( + 'Each keyword must be at most ' . $maxLen . ' characters long.', + null, + [], + 'values' + ), + ], + ]; + } } class_alias(KeywordTest::class, 'eZ\Publish\Core\FieldType\Tests\KeywordTest'); From 8ccbdc47a6e7a2723add6309f993ab5873442006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Bia=C5=82czak?= Date: Mon, 16 Jun 2025 08:41:15 +0200 Subject: [PATCH 2/3] IBX-8697: Updated return type annotations in KeywordTest for better type safety and removed obsolete PHPStan baseline rules --- phpstan-baseline.neon | 12 ------------ tests/lib/FieldType/KeywordTest.php | 8 ++++++-- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index daa504a674..a68bcbeb51 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -53442,18 +53442,6 @@ parameters: count: 1 path: tests/lib/FieldType/KeywordTest.php - - - message: '#^Method Ibexa\\Tests\\Core\\FieldType\\KeywordTest\:\:provideValidDataForValidate\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/lib/FieldType/KeywordTest.php - - - - message: '#^Method Ibexa\\Tests\\Core\\FieldType\\KeywordTest\:\:provideInvalidDataForValidate\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/lib/FieldType/KeywordTest.php - - message: '#^Method Ibexa\\Tests\\Core\\FieldType\\MapLocationTest\:\:getSettingsSchemaExpectation\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue diff --git a/tests/lib/FieldType/KeywordTest.php b/tests/lib/FieldType/KeywordTest.php index a02adf4260..341cdf266b 100644 --- a/tests/lib/FieldType/KeywordTest.php +++ b/tests/lib/FieldType/KeywordTest.php @@ -241,7 +241,7 @@ public function provideDataForGetName(): array } /** - * @return iterable + * @return iterable, 1: \Ibexa\Core\FieldType\Keyword\Value}> */ public function provideValidDataForValidate(): iterable { @@ -262,7 +262,11 @@ public function provideValidDataForValidate(): iterable } /** - * @return iterable}> + * @return iterable, + * 1: \Ibexa\Core\FieldType\Keyword\Value, + * 2: array<\Ibexa\Contracts\Core\FieldType\ValidationError> + * }> */ public function provideInvalidDataForValidate(): iterable { From 6120f255ac7ab8c4215cbeb8d4bd25178e66d5cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Bia=C5=82czak?= Date: Wed, 25 Jun 2025 09:41:30 +0200 Subject: [PATCH 3/3] IBX-8697: Revised validation error message for Keyword field type to improve clarity --- src/lib/FieldType/Keyword/Type.php | 2 +- tests/lib/FieldType/KeywordTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/FieldType/Keyword/Type.php b/src/lib/FieldType/Keyword/Type.php index caf546e96d..1d51bf0f84 100644 --- a/src/lib/FieldType/Keyword/Type.php +++ b/src/lib/FieldType/Keyword/Type.php @@ -119,7 +119,7 @@ public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) ); } elseif (mb_strlen($keyword) > self::MAX_KEYWORD_LENGTH) { $validationErrors[] = new ValidationError( - 'Each keyword must be at most ' . self::MAX_KEYWORD_LENGTH . ' characters long.', + 'Keyword value must be less than or equal to ' . self::MAX_KEYWORD_LENGTH . ' characters.', null, [], 'values' diff --git a/tests/lib/FieldType/KeywordTest.php b/tests/lib/FieldType/KeywordTest.php index 341cdf266b..203964d6b3 100644 --- a/tests/lib/FieldType/KeywordTest.php +++ b/tests/lib/FieldType/KeywordTest.php @@ -305,7 +305,7 @@ public function provideInvalidDataForValidate(): iterable new KeywordValue(['valid', str_repeat('a', $maxLen + 1)]), [ new ValidationError( - 'Each keyword must be at most ' . $maxLen . ' characters long.', + 'Keyword value must be less than or equal to ' . $maxLen . ' characters.', null, [], 'values'