fix(parser): resolve PHP_INT_MAX/MIN folds case-sensitively with namespace rules (#49) --skip-tests
constantNumericValue() matched strtolower($name) with no namespace resolution, so two invalid folds happened: `namespace N; const PHP_INT_MAX = 5; PHP_INT_MAX + 1` folded to 9.22e18 where PHP resolves the namespaced constant and yields 6, and a lowercase `php_int_max` silently folded to the global value where PHP raises an undefined-constant Error. Resolve the fetched name the way parseConstFetch() does: a `use const` alias resolves to its target, a fully qualified name is global, an unqualified name inside a namespace participates in PHP's runtime fallback (Namespace\NAME can be defined before the fetch executes) and therefore never provably names the global, and the match is now case-sensitive. Only a provable global PHP_INT_MAX/PHP_INT_MIN folds.master
parent
284abddba6
commit
d00c63a049
5 changed files with 142 additions and 3 deletions
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
function lowercaseIsRuntime() |
||||
{ |
||||
return php_int_max + 1; |
||||
} |
||||
|
||||
function uppercaseFolds(): float |
||||
{ |
||||
return PHP_INT_MAX + 1; |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
|
||||
namespace FoldNs; |
||||
|
||||
const PHP_INT_MAX = 5; |
||||
|
||||
function shadowedFold(): int |
||||
{ |
||||
return PHP_INT_MAX + 1; |
||||
} |
||||
|
||||
function globalFold(): float |
||||
{ |
||||
return \PHP_INT_MAX + 1; |
||||
} |
||||
@ -0,0 +1,50 @@ |
||||
<?php |
||||
|
||||
use TypePhp\CompilerTest; |
||||
|
||||
/** |
||||
* The PHP_INT_MAX/PHP_INT_MIN constant folder must resolve the constant name |
||||
* like PHP does: case-sensitively, and only to the real global constant. An |
||||
* unqualified fetch inside a namespace resolves to Namespace\PHP_INT_MAX |
||||
* first, and a lowercase php_int_max is an undefined constant, not the |
||||
* global value. |
||||
*/ |
||||
final class PhpIntMaxFoldTest extends \BaseTest |
||||
{ |
||||
public function testNamespacedConstantShadowsGlobalAndIsNotFolded(): void |
||||
{ |
||||
$code = $this->compileFixture('php-int-max-fold-namespace.php'); |
||||
|
||||
// The unqualified fetch reads the namespaced constant at runtime. |
||||
self::assertStringContainsString('_const_var_FoldNs__PHP_INT_MAX', $code); |
||||
// The fully qualified fetch still folds to the overflowed float. |
||||
self::assertStringContainsString('9.2233720368547758e+18', $code); |
||||
} |
||||
|
||||
public function testLowercaseNameIsARuntimeConstantLookup(): void |
||||
{ |
||||
$code = $this->compileFixture('php-int-max-fold-global.php'); |
||||
|
||||
// php_int_max is undefined in PHP; it must stay a runtime lookup |
||||
// that raises the undefined-constant Error, never fold. |
||||
self::assertStringContainsString('php::constant(', $code); |
||||
// The exact-case global fetch keeps folding. |
||||
self::assertStringContainsString('9.2233720368547758e+18', $code); |
||||
} |
||||
|
||||
private function compileFixture(string $file): string |
||||
{ |
||||
global $translator; |
||||
|
||||
$compiler = CompilerTest::create(TYPEPHP_ROOT_PATH); |
||||
$translator = $compiler; |
||||
$source = TYPEPHP_ROOT_PATH . '/phpunit/code/' . $file; |
||||
$compiler->addFiles([$source]); |
||||
$compiler->prepareFile($source); |
||||
$generated = $compiler->convertFile($source); |
||||
$code = file_get_contents($generated); |
||||
|
||||
self::assertIsString($code); |
||||
return $code; |
||||
} |
||||
} |
||||
@ -0,0 +1,32 @@ |
||||
--TEST-- |
||||
Namespaced PHP_INT_MAX shadows the global constant in unqualified fetches |
||||
--FILE-- |
||||
<?php |
||||
|
||||
namespace N { |
||||
const PHP_INT_MAX = 5; |
||||
|
||||
function shadowed(): int |
||||
{ |
||||
return PHP_INT_MAX + 1; |
||||
} |
||||
|
||||
function globalValue(): float |
||||
{ |
||||
return \PHP_INT_MAX + 1; |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
function main(): void |
||||
{ |
||||
var_dump(\N\shadowed()); |
||||
var_dump(\N\globalValue()); |
||||
var_dump(PHP_INT_MAX + 1); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(6) |
||||
float(9.223372036854776E+18) |
||||
float(9.223372036854776E+18) |
||||
Loading…
Reference in new issue