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