From 3bdbfc8fded7017c1550dd5ca2188526e2f5b74b Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 8 Sep 2026 21:36:03 +0700 Subject: [PATCH] fix: Make nameStartsWith()/nameEndsWith() case-insensitive in NameQueryTrait --- src/Analyser/NameQueryTrait.php | 11 +++++++++-- tests/Analyser/ClassNodeTest.php | 22 ++++++++++++++++++++++ tests/Analyser/FunctionNodeTest.php | 17 +++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Analyser/NameQueryTrait.php b/src/Analyser/NameQueryTrait.php index 76d26dd5..212709ab 100644 --- a/src/Analyser/NameQueryTrait.php +++ b/src/Analyser/NameQueryTrait.php @@ -6,6 +6,7 @@ use function str_ends_with; use function str_starts_with; +use function strtolower; /** * Short-name query helpers shared by {@see ClassNode} and {@see FunctionNode}. @@ -18,11 +19,17 @@ abstract public function shortName(): string; public function nameEndsWith(string $suffix): bool { - return str_ends_with($this->shortName(), $suffix); + $lowerSuffix = strtolower($suffix); + $lowerShortName = strtolower($this->shortName()); + + return str_ends_with($lowerShortName, $lowerSuffix); } public function nameStartsWith(string $prefix): bool { - return str_starts_with($this->shortName(), $prefix); + $lowerPrefix = strtolower($prefix); + $lowerShortName = strtolower($this->shortName()); + + return str_starts_with($lowerShortName, $lowerPrefix); } } diff --git a/tests/Analyser/ClassNodeTest.php b/tests/Analyser/ClassNodeTest.php index 8363469b..0921c8dc 100644 --- a/tests/Analyser/ClassNodeTest.php +++ b/tests/Analyser/ClassNodeTest.php @@ -40,6 +40,28 @@ className: 'App\\Domain\\CreateOrderHandler', $this->assertTrue($classNode->nameMatches('/^App\\\\Domain\\\\CreateOrderHandler$/', isFullName: true)); } + public function testNameStartsWithAndEndsWithAreCaseInsensitive(): void + { + $classNode = new ClassNode( + className: 'App\\Domain\\CreateOrderHandler', + file: '/src/CreateOrderHandler.php', + line: 12, + layer: 'Application', + extends: null, + isAbstract: false, + isFinal: true, + isInterface: false, + isReadonly: false, + ); + + $this->assertTrue($classNode->nameStartsWith('create')); + $this->assertTrue($classNode->nameStartsWith('CREATE')); + $this->assertTrue($classNode->nameEndsWith('handler')); + $this->assertTrue($classNode->nameEndsWith('HANDLER')); + $this->assertFalse($classNode->nameStartsWith('handler')); + $this->assertFalse($classNode->nameEndsWith('create')); + } + public function testShortNameReturnsFullNameForGlobalNamespaceClass(): void { $classNode = new ClassNode( diff --git a/tests/Analyser/FunctionNodeTest.php b/tests/Analyser/FunctionNodeTest.php index ce61b0fa..edfcf0c4 100644 --- a/tests/Analyser/FunctionNodeTest.php +++ b/tests/Analyser/FunctionNodeTest.php @@ -31,6 +31,23 @@ functionName: 'App\\Support\\format_money', $this->assertTrue($functionNode->nameMatches('/^App\\\\Support\\\\format_money$/', isFullName: true)); } + public function testNameStartsWithAndEndsWithAreCaseInsensitive(): void + { + $functionNode = new FunctionNode( + functionName: 'App\\Support\\formatMoney', + file: '/src/helpers.php', + line: 12, + layer: 'Support', + ); + + $this->assertTrue($functionNode->nameStartsWith('format')); + $this->assertTrue($functionNode->nameStartsWith('FORMAT')); + $this->assertTrue($functionNode->nameEndsWith('money')); + $this->assertTrue($functionNode->nameEndsWith('MONEY')); + $this->assertFalse($functionNode->nameStartsWith('money')); + $this->assertFalse($functionNode->nameEndsWith('format')); + } + public function testGlobalFunctionShortNameIsItsName(): void { $functionNode = new FunctionNode(functionName: 'helper', file: '/src/helpers.php', line: 1, layer: null);