Resolve function imports before dispatch and policy checks (#108)
* fix(compiler): resolve function imports before dispatch and policy checks * test: close PHP section in function import PHPT * fix(compiler): resolve first-class function aliasesmaster
parent
df6c0dfd99
commit
ce4c712b0c
5 changed files with 124 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||||||
|
<?php |
||||||
|
use function exec as SAFE; |
||||||
|
function importedPolicyCall(): void { safe('not-executed'); } |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
<?php |
||||||
|
namespace { |
||||||
|
function route(): string { return 'global'; } |
||||||
|
function size(string $value): int { return 99; } |
||||||
|
} |
||||||
|
namespace AliasLibrary { |
||||||
|
function route(): string { return 'import'; } |
||||||
|
function callback_target(): string { return 'import-callback'; } |
||||||
|
} |
||||||
|
namespace AliasConsumer { |
||||||
|
use function AliasLibrary\route as ROUTE; |
||||||
|
use function AliasLibrary\route as GET_CALLED_CLASS; |
||||||
|
use function strlen as SIZE; |
||||||
|
use function AliasLibrary\route as extract; |
||||||
|
use function AliasLibrary\{route as GROUPED}; |
||||||
|
use function AliasLibrary\callback_target as CALLBACK_TARGET; |
||||||
|
|
||||||
|
function exercise(): void { |
||||||
|
echo route(), ':', RoUtE(), ':', get_called_class(), ':', sIzE('abc'), ':', grouped(), ':', extract(), ':', \route(), "\n"; |
||||||
|
$size = sIzE(...); |
||||||
|
$callback = cAlLbAcK_tArGeT(...); |
||||||
|
$route = rOuTe(...); |
||||||
|
$dynamic = $size; |
||||||
|
echo $size('abcd'), ':', $callback(), ':', $route(), ':', $dynamic('abc'), "\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
namespace { function main(): void { \AliasConsumer\exercise(); } } |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use TypePhp\CompilerTest; |
||||||
|
|
||||||
|
final class FunctionImportResolutionTest extends BaseTest |
||||||
|
{ |
||||||
|
public function testImportedFunctionsTakePrecedenceRegardlessOfAliasCase(): void |
||||||
|
{ |
||||||
|
global $translator; |
||||||
|
$compiler = CompilerTest::create(TYPEPHP_ROOT_PATH); |
||||||
|
$translator = $compiler; |
||||||
|
$source = TYPEPHP_ROOT_PATH . '/phpunit/code/function-import-resolution.php'; |
||||||
|
$compiler->addFiles([$source]); |
||||||
|
$compiler->prepareFile($source); |
||||||
|
$code = file_get_contents($compiler->convertFile($source)); |
||||||
|
preg_match('/void php_aliasconsumer__exercise\(\) \{(.*?)\n\}/s', $code, $match); |
||||||
|
self::assertCount(2, $match); |
||||||
|
self::assertSame(5, substr_count($match[1], 'php_aliaslibrary__route(')); |
||||||
|
self::assertSame(1, substr_count($match[1], 'php_route(')); |
||||||
|
// The three first-class callables are materialized through |
||||||
|
// Closure::fromCallable(); ordinary aliases above must stay direct. |
||||||
|
self::assertSame(3, substr_count($match[1], 'php::call(')); |
||||||
|
self::assertStringNotContainsString('ZEND_STRL("sIzE")', $match[1]); |
||||||
|
self::assertStringNotContainsString('ZEND_STRL("cAlLbAcK_tArGeT")', $match[1]); |
||||||
|
|
||||||
|
$literalStrings = (new \ReflectionProperty($compiler, 'literalStrings'))->getValue($compiler); |
||||||
|
self::assertArrayHasKey('strlen', $literalStrings); |
||||||
|
self::assertArrayHasKey('AliasLibrary\\callback_target', $literalStrings); |
||||||
|
self::assertArrayNotHasKey('sIzE', $literalStrings); |
||||||
|
self::assertArrayNotHasKey('cAlLbAcK_tArGeT', $literalStrings); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
--TEST-- |
||||||
|
Function imports are case-insensitive and take precedence over global functions |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
namespace { |
||||||
|
function route(): string { return 'global'; } |
||||||
|
function size(string $value): int { return 99; } |
||||||
|
} |
||||||
|
namespace AliasLibrary { |
||||||
|
function route(): string { return 'import'; } |
||||||
|
function callback_target(): string { return 'import-callback'; } |
||||||
|
} |
||||||
|
namespace AliasConsumer { |
||||||
|
use function AliasLibrary\route as ROUTE; |
||||||
|
use function AliasLibrary\route as GET_CALLED_CLASS; |
||||||
|
use function strlen as SIZE; |
||||||
|
use function AliasLibrary\route as extract; |
||||||
|
use function AliasLibrary\{route as GROUPED}; |
||||||
|
use function AliasLibrary\callback_target as CALLBACK_TARGET; |
||||||
|
|
||||||
|
function exercise(): void { |
||||||
|
echo route(), ':', RoUtE(), ':', get_called_class(), ':', sIzE('abc'), ':', grouped(), ':', extract(), ':', \route(), "\n"; |
||||||
|
$size = sIzE(...); |
||||||
|
$callback = cAlLbAcK_tArGeT(...); |
||||||
|
$route = rOuTe(...); |
||||||
|
$dynamic = $size; |
||||||
|
echo $size('abcd'), ':', $callback(), ':', $route(), ':', $dynamic('abc'), "\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
namespace { function main(): void { \AliasConsumer\exercise(); } } |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
import:import:import:3:import:import:global |
||||||
|
4:import-callback:import:3 |
||||||
Loading…
Reference in new issue