From 73e1e662df17a322b6347f8bfc0d8f5c8e86bec4 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 22 Oct 2025 17:37:23 +0200 Subject: [PATCH 1/3] decopule RequiresAttributeFactory --- .../NodeFactory/RequiresAttributeFactory.php | 66 ++++++++++ ...esAnnotationWithValueToAttributeRector.php | 121 ++++-------------- 2 files changed, 93 insertions(+), 94 deletions(-) create mode 100644 rules/AnnotationsToAttributes/NodeFactory/RequiresAttributeFactory.php diff --git a/rules/AnnotationsToAttributes/NodeFactory/RequiresAttributeFactory.php b/rules/AnnotationsToAttributes/NodeFactory/RequiresAttributeFactory.php new file mode 100644 index 00000000..24579473 --- /dev/null +++ b/rules/AnnotationsToAttributes/NodeFactory/RequiresAttributeFactory.php @@ -0,0 +1,66 @@ +phpAttributeGroupFactory->createFromClassWithItems($attributeClass, [...$attributeValue]); + } +} diff --git a/rules/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector.php b/rules/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector.php index 9494c114..3712a322 100644 --- a/rules/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector.php +++ b/rules/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector.php @@ -14,6 +14,7 @@ use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory; +use Rector\PHPUnit\AnnotationsToAttributes\NodeFactory\RequiresAttributeFactory; use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; @@ -28,7 +29,7 @@ final class RequiresAnnotationWithValueToAttributeRector extends AbstractRector { public function __construct( private readonly PhpDocTagRemover $phpDocTagRemover, - private readonly PhpAttributeGroupFactory $phpAttributeGroupFactory, + private readonly RequiresAttributeFactory $requiresAttributeFactory, private readonly TestsNodeAnalyzer $testsNodeAnalyzer, private readonly DocBlockUpdater $docBlockUpdater, private readonly PhpDocInfoFactory $phpDocInfoFactory, @@ -45,26 +46,11 @@ public function getRuleDefinition(): RuleDefinition /** * @requires PHP > 8.4 * @requires PHPUnit >= 10 - * @requires OS Windows - * @requires OSFAMILY Darwin - * @requires function someFunction - * @requires function \some\className::someMethod - * @requires extension mysqli - * @requires extension mysqli >= 8.3.0 - * @requires setting date.timezone Europe/Berlin */ final class SomeTest extends TestCase { /** - * @requires PHP > 8.4 - * @requires PHPUnit >= 10 - * @requires OS Windows - * @requires OSFAMILY Darwin - * @requires function someFunction - * @requires function \some\className::someMethod - * @requires extension mysqli - * @requires extension mysqli >= 8.3.0 * @requires setting date.timezone Europe/Berlin */ public function test() @@ -79,24 +65,8 @@ public function test() #[\PHPUnit\Framework\Attributes\RequiresPhp('> 8.4')] #[\PHPUnit\Framework\Attributes\RequiresPhpunit('>= 10')] -#[\PHPUnit\Framework\Attributes\RequiresOperatingSystem('Windows')] -#[\PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily('Darwin')] -#[\PHPUnit\Framework\Attributes\RequiresFunction('someFunction')] -#[\PHPUnit\Framework\Attributes\RequiresMethod(\some\className::class, 'someMethod')] -#[\PHPUnit\Framework\Attributes\RequiresPhpExtension('mysqli')] -#[\PHPUnit\Framework\Attributes\RequiresPhpExtension('mysqli', '>= 8.3.0')] -#[\PHPUnit\Framework\Attributes\RequiresSetting('date.timezone', 'Europe/Berlin')] final class SomeTest extends TestCase { - - #[\PHPUnit\Framework\Attributes\RequiresPhp('> 8.4')] - #[\PHPUnit\Framework\Attributes\RequiresPhpunit('>= 10')] - #[\PHPUnit\Framework\Attributes\RequiresOperatingSystem('Windows')] - #[\PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily('Darwin')] - #[\PHPUnit\Framework\Attributes\RequiresFunction('someFunction')] - #[\PHPUnit\Framework\Attributes\RequiresMethod(\some\className::class, 'someMethod')] - #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('mysqli')] - #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('mysqli', '>= 8.3.0')] #[\PHPUnit\Framework\Attributes\RequiresSetting('date.timezone', 'Europe/Berlin')] public function test() { @@ -129,6 +99,10 @@ public function refactor(Node $node): ?Node return null; } + if ($node instanceof ClassMethod) { + return $this->refactorClassMethod($node); + } + $hasChanged = false; if ($node instanceof Class_) { @@ -142,72 +116,11 @@ public function refactor(Node $node): ?Node $hasChanged = true; } } - - foreach ($node->getMethods() as $classMethod) { - $phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); - if ($phpDocInfo instanceof PhpDocInfo) { - $requiresAttributeGroups = $this->handleRequires($phpDocInfo); - if ($requiresAttributeGroups !== []) { - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod); - $classMethod->attrGroups = array_merge($classMethod->attrGroups, $requiresAttributeGroups); - $this->removeMethodRequiresAnnotations($phpDocInfo); - $hasChanged = true; - } - } - } } return $hasChanged ? $node : null; } - private function createAttributeGroup(string $annotationValue): ?AttributeGroup - { - $annotationValues = explode(' ', $annotationValue, 2); - $type = array_shift($annotationValues); - $attributeValue = array_shift($annotationValues); - switch ($type) { - case 'PHP': - $attributeClass = 'PHPUnit\Framework\Attributes\RequiresPhp'; - $attributeValue = [$attributeValue]; - break; - case 'PHPUnit': - $attributeClass = 'PHPUnit\Framework\Attributes\RequiresPhpunit'; - $attributeValue = [$attributeValue]; - break; - case 'OS': - $attributeClass = 'PHPUnit\Framework\Attributes\RequiresOperatingSystem'; - $attributeValue = [$attributeValue]; - break; - case 'OSFAMILY': - $attributeClass = 'PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily'; - $attributeValue = [$attributeValue]; - break; - case 'function': - if (str_contains((string) $attributeValue, '::')) { - $attributeClass = 'PHPUnit\Framework\Attributes\RequiresMethod'; - $attributeValue = explode('::', (string) $attributeValue); - $attributeValue[0] .= '::class'; - } else { - $attributeClass = 'PHPUnit\Framework\Attributes\RequiresFunction'; - $attributeValue = [$attributeValue]; - } - - break; - case 'extension': - $attributeClass = 'PHPUnit\Framework\Attributes\RequiresPhpExtension'; - $attributeValue = explode(' ', (string) $attributeValue, 2); - break; - case 'setting': - $attributeClass = 'PHPUnit\Framework\Attributes\RequiresSetting'; - $attributeValue = explode(' ', (string) $attributeValue, 2); - break; - default: - return null; - } - - return $this->phpAttributeGroupFactory->createFromClassWithItems($attributeClass, [...$attributeValue]); - } - /** * @return array */ @@ -221,7 +134,8 @@ private function handleRequires(PhpDocInfo $phpDocInfo): array } $requires = $desiredTagValueNode->value->value; - $attributeGroups[$requires] = $this->createAttributeGroup($requires); + $attributeGroups[$requires] = $this->requiresAttributeFactory->create($requires); + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $desiredTagValueNode); } @@ -244,4 +158,23 @@ private function removeMethodRequiresAnnotations(PhpDocInfo $phpDocInfo): bool return $hasChanged; } + + private function refactorClassMethod(ClassMethod $node): ?ClassMethod + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (! $phpDocInfo instanceof PhpDocInfo) { + return null; + } + + $requiresAttributeGroups = $this->handleRequires($phpDocInfo); + if ($requiresAttributeGroups === []) { + return null; + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + $node->attrGroups = array_merge($node->attrGroups, $requiresAttributeGroups); + $this->removeMethodRequiresAnnotations($phpDocInfo); + + return $node; + } } From 8f1128567b80830cd058930b9b13e992e160be9f Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 22 Oct 2025 17:43:27 +0200 Subject: [PATCH 2/3] split test fixtures to be more granular and easier to extend --- .../Fixture/requires_extension.php.inc | 29 +++++++++ .../Fixture/requires_fixture.php.inc | 65 ------------------- .../Fixture/requires_function.php.inc | 35 ++++++++++ .../Fixture/requires_os.php.inc | 29 +++++++++ .../Fixture/requires_php.php.inc | 33 ++++++++++ .../Fixture/requires_phpunit.php.inc | 27 ++++++++ .../Fixture/requires_setting.php.inc | 33 ++++++++++ ...esAnnotationWithValueToAttributeRector.php | 11 ++-- 8 files changed, 191 insertions(+), 71 deletions(-) create mode 100644 rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_extension.php.inc delete mode 100644 rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_fixture.php.inc create mode 100644 rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_function.php.inc create mode 100644 rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_os.php.inc create mode 100644 rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_php.php.inc create mode 100644 rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_phpunit.php.inc create mode 100644 rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_setting.php.inc diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_extension.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_extension.php.inc new file mode 100644 index 00000000..67fc1c9c --- /dev/null +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_extension.php.inc @@ -0,0 +1,29 @@ += 8.3.0 + */ +final class BarController extends TestCase +{ +} + +?> +----- += 8.3.0')] +final class BarController extends TestCase +{ +} + +?> diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_fixture.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_fixture.php.inc deleted file mode 100644 index ff925a5d..00000000 --- a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_fixture.php.inc +++ /dev/null @@ -1,65 +0,0 @@ - 8.4 - * @requires PHPUnit >= 10 - * @requires OS Windows - * @requires OSFAMILY Darwin - * @requires function someFunction - * @requires function \some\className::someMethod - * @requires extension mysqli - * @requires extension mysqli >= 8.3.0 - * @requires setting date.timezone Europe/Berlin - */ -class BarController extends TestCase -{ - /** - * @requires PHP > 8.4 - * @requires PHPUnit >= 10 - * @requires OS Windows - * @requires OSFAMILY Darwin - * @requires function someFunction - * @requires function \some\className::someMethod - * @requires extension mysqli - * @requires extension mysqli >= 8.3.0 - * @requires setting date.timezone Europe/Berlin - */ - public function testWithRequires() - { - } -} - -?> ------ - 8.4')] -#[\PHPUnit\Framework\Attributes\RequiresPhpunit('>= 10')] -#[\PHPUnit\Framework\Attributes\RequiresOperatingSystem('Windows')] -#[\PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily('Darwin')] -#[\PHPUnit\Framework\Attributes\RequiresFunction('someFunction')] -#[\PHPUnit\Framework\Attributes\RequiresMethod(\some\className::class, 'someMethod')] -#[\PHPUnit\Framework\Attributes\RequiresPhpExtension('mysqli')] -#[\PHPUnit\Framework\Attributes\RequiresPhpExtension('mysqli', '>= 8.3.0')] -#[\PHPUnit\Framework\Attributes\RequiresSetting('date.timezone', 'Europe/Berlin')] -class BarController extends TestCase -{ - #[\PHPUnit\Framework\Attributes\RequiresPhp('> 8.4')] - #[\PHPUnit\Framework\Attributes\RequiresPhpunit('>= 10')] - #[\PHPUnit\Framework\Attributes\RequiresOperatingSystem('Windows')] - #[\PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily('Darwin')] - #[\PHPUnit\Framework\Attributes\RequiresFunction('someFunction')] - #[\PHPUnit\Framework\Attributes\RequiresMethod(\some\className::class, 'someMethod')] - #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('mysqli')] - #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('mysqli', '>= 8.3.0')] - #[\PHPUnit\Framework\Attributes\RequiresSetting('date.timezone', 'Europe/Berlin')] - public function testWithRequires() - { - } -} - -?> diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_function.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_function.php.inc new file mode 100644 index 00000000..df4e21c6 --- /dev/null +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_function.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_os.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_os.php.inc new file mode 100644 index 00000000..8556e4ce --- /dev/null +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_os.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_php.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_php.php.inc new file mode 100644 index 00000000..a6476305 --- /dev/null +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_php.php.inc @@ -0,0 +1,33 @@ + 8.4 + */ + public function testWithRequires() + { + } +} + +?> +----- + 8.4')] + public function testWithRequires() + { + } +} + +?> diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_phpunit.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_phpunit.php.inc new file mode 100644 index 00000000..a24ecde9 --- /dev/null +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_phpunit.php.inc @@ -0,0 +1,27 @@ += 10 + */ +final class RequiresPHPUnit extends TestCase +{ +} + +?> +----- += 10')] +final class RequiresPHPUnit extends TestCase +{ +} + +?> diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_setting.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_setting.php.inc new file mode 100644 index 00000000..5b870a30 --- /dev/null +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_setting.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector.php b/rules/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector.php index 3712a322..b7ba8d8a 100644 --- a/rules/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector.php +++ b/rules/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector.php @@ -13,7 +13,6 @@ use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; use Rector\Comments\NodeDocBlock\DocBlockUpdater; -use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory; use Rector\PHPUnit\AnnotationsToAttributes\NodeFactory\RequiresAttributeFactory; use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; use Rector\Rector\AbstractRector; @@ -159,9 +158,9 @@ private function removeMethodRequiresAnnotations(PhpDocInfo $phpDocInfo): bool return $hasChanged; } - private function refactorClassMethod(ClassMethod $node): ?ClassMethod + private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod { - $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); if (! $phpDocInfo instanceof PhpDocInfo) { return null; } @@ -171,10 +170,10 @@ private function refactorClassMethod(ClassMethod $node): ?ClassMethod return null; } - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); - $node->attrGroups = array_merge($node->attrGroups, $requiresAttributeGroups); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod); + $classMethod->attrGroups = array_merge($classMethod->attrGroups, $requiresAttributeGroups); $this->removeMethodRequiresAnnotations($phpDocInfo); - return $node; + return $classMethod; } } From f7619c1be6e66fd1be5d293787bd3efea0dca6ae Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 22 Oct 2025 17:49:15 +0200 Subject: [PATCH 3/3] prefix PHP and PHPUnit version with >= in RequiresAnnotationWithValueToAttributeRector --- .../Fixture/requires_php.php.inc | 4 ++++ .../Fixture/requires_phpunit.php.inc | 2 ++ .../NodeFactory/RequiresAttributeFactory.php | 12 ++++++++++++ 3 files changed, 18 insertions(+) diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_php.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_php.php.inc index a6476305..94314a0a 100644 --- a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_php.php.inc +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_php.php.inc @@ -4,6 +4,9 @@ namespace Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\RequiresAnn use PHPUnit\Framework\TestCase; +/** + * @requires PHP 8.0 + */ class RequiresPHP extends TestCase { /** @@ -22,6 +25,7 @@ namespace Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\RequiresAnn use PHPUnit\Framework\TestCase; +#[\PHPUnit\Framework\Attributes\RequiresPhp('>= 8.0')] class RequiresPHP extends TestCase { #[\PHPUnit\Framework\Attributes\RequiresPhp('> 8.4')] diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_phpunit.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_phpunit.php.inc index a24ecde9..ac00c96a 100644 --- a/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_phpunit.php.inc +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/RequiresAnnotationWithValueToAttributeRector/Fixture/requires_phpunit.php.inc @@ -6,6 +6,7 @@ use PHPUnit\Framework\TestCase; /** * @requires PHPUnit >= 10 + * @requires PHPUnit 5 */ final class RequiresPHPUnit extends TestCase { @@ -20,6 +21,7 @@ namespace Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\RequiresAnn use PHPUnit\Framework\TestCase; #[\PHPUnit\Framework\Attributes\RequiresPhpunit('>= 10')] +#[\PHPUnit\Framework\Attributes\RequiresPhpunit('>= 5')] final class RequiresPHPUnit extends TestCase { } diff --git a/rules/AnnotationsToAttributes/NodeFactory/RequiresAttributeFactory.php b/rules/AnnotationsToAttributes/NodeFactory/RequiresAttributeFactory.php index 24579473..fcf754d1 100644 --- a/rules/AnnotationsToAttributes/NodeFactory/RequiresAttributeFactory.php +++ b/rules/AnnotationsToAttributes/NodeFactory/RequiresAttributeFactory.php @@ -24,10 +24,22 @@ public function create(string $annotationValue): ?AttributeGroup switch ($type) { case 'PHP': $attributeClass = 'PHPUnit\Framework\Attributes\RequiresPhp'; + + // only version is used, we need to prefix with >= + if (is_string($attributeValue) && is_numeric($attributeValue[0])) { + $attributeValue = '>= ' . $attributeValue; + } + $attributeValue = [$attributeValue]; break; case 'PHPUnit': $attributeClass = 'PHPUnit\Framework\Attributes\RequiresPhpunit'; + + // only version is used, we need to prefix with >= + if (is_string($attributeValue) && is_numeric($attributeValue[0])) { + $attributeValue = '>= ' . $attributeValue; + } + $attributeValue = [$attributeValue]; break; case 'OS':