- Updated resolveAnonClassTypeNames to resolveAnonClassNames with improved name resolution - Added proper handling of imported names in anonymous class method bodies - Ensured fully qualified names are embedded when anonymous classes are evaluated - Added test case for anonymous class method bodies preserving namespace imports - Fixed function call trait to check global name instead of local name for unsupported functions - Added tests for destructor exception handling boundaries - Improved include error handling and shutdown exception handler behavior - Added unsupported function detection for extract function with proper error messages - Updated swoole/phpx dependency from ~2.5.3 to ~2.5.5pull/48/head
parent
5fb3fdf490
commit
a79610fb2c
12 changed files with 228 additions and 10 deletions
@ -0,0 +1,8 @@ |
||||
<?php |
||||
|
||||
namespace App; |
||||
|
||||
function importVariables(array $values): void |
||||
{ |
||||
\extract($values); |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<?php |
||||
|
||||
function importVariables(array $values): void |
||||
{ |
||||
extract($values); |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
class UnsupportedFunctionTest extends BaseTest |
||||
{ |
||||
public function testExtractIsRejectedAtCompileTime(): void |
||||
{ |
||||
$this->exec('Unsupported function: `extract`', 'unsupported-function-extract.php'); |
||||
} |
||||
|
||||
public function testFullyQualifiedExtractIsRejectedAtCompileTime(): void |
||||
{ |
||||
$this->exec('Unsupported function: `extract`', 'unsupported-function-extract-qualified.php'); |
||||
} |
||||
} |
||||
@ -0,0 +1,49 @@ |
||||
--TEST-- |
||||
Anonymous class method bodies preserve namespace imports |
||||
--FILE-- |
||||
<?php |
||||
namespace AnonymousClassSupport { |
||||
const FLAG = 'imported'; |
||||
|
||||
class Subject {} |
||||
|
||||
class Marker { |
||||
public const string VALUE = 'resolved'; |
||||
} |
||||
|
||||
function accepts(Subject $value): bool { |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
namespace AnonymousClassConsumer { |
||||
use AnonymousClassSupport\Marker as ImportedMarker; |
||||
use AnonymousClassSupport\Subject as ImportedSubject; |
||||
use const AnonymousClassSupport\FLAG as IMPORTED_FLAG; |
||||
use function AnonymousClassSupport\accepts as imported_accepts; |
||||
|
||||
function main(): void { |
||||
$visitor = new class('ready') { |
||||
public function __construct(private readonly string $state) {} |
||||
|
||||
public function accepts(object $value): bool { |
||||
return $value instanceof ImportedSubject |
||||
&& ImportedMarker::VALUE === 'resolved' |
||||
&& IMPORTED_FLAG === 'imported' |
||||
&& imported_accepts($value) |
||||
&& $this->state === 'ready'; |
||||
} |
||||
}; |
||||
|
||||
var_dump($visitor->accepts(new ImportedSubject())); |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
function main(): void { |
||||
AnonymousClassConsumer\main(); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
bool(true) |
||||
@ -0,0 +1,28 @@ |
||||
--TEST-- |
||||
TypePHP destructor exceptions remain inside the Zend wrapper boundary |
||||
--FILE-- |
||||
<?php |
||||
|
||||
final class ThrowingDestructor |
||||
{ |
||||
public function __destruct() |
||||
{ |
||||
throw new RuntimeException('destructor'); |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
try { |
||||
$value = new ThrowingDestructor(); |
||||
unset($value); |
||||
} catch (RuntimeException $exception) { |
||||
echo $exception->getMessage(), "\n"; |
||||
} |
||||
|
||||
echo "continued\n"; |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
destructor |
||||
continued |
||||
@ -0,0 +1,38 @@ |
||||
--TEST-- |
||||
An exception handler may include a PHP file during shutdown |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function handleShutdownException(Throwable $exception): void |
||||
{ |
||||
global $shutdownState; |
||||
|
||||
echo 'state:', $shutdownState, "\n"; |
||||
echo 'handled:', $exception->getMessage(), "\n"; |
||||
|
||||
$file = tempnam(sys_get_temp_dir(), 'typephp-shutdown-'); |
||||
file_put_contents($file, '<?php echo "included during shutdown\\n";'); |
||||
include $file; |
||||
unlink($file); |
||||
} |
||||
|
||||
function throwDuringShutdown(): void |
||||
{ |
||||
throw new RuntimeException('shutdown failure'); |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
global $shutdownState; |
||||
|
||||
$shutdownState = 'request alive'; |
||||
set_exception_handler('handleShutdownException'); |
||||
register_shutdown_function('throwDuringShutdown'); |
||||
echo "main completed\n"; |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
main completed |
||||
state:request alive |
||||
handled:shutdown failure |
||||
included during shutdown |
||||
@ -0,0 +1,28 @@ |
||||
--TEST-- |
||||
An exception from an error handler unwinds an included PHP frame safely |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function throwIncludeWarning(int $severity, string $message, string $file, int $line): never |
||||
{ |
||||
throw new ErrorException($message, 0, $severity, $file, $line); |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$file = tempnam(sys_get_temp_dir(), 'typephp-include-unwind-'); |
||||
file_put_contents($file, '<?php echo $undefinedIncludeVariable;'); |
||||
set_error_handler('throwIncludeWarning'); |
||||
|
||||
try { |
||||
include $file; |
||||
} catch (ErrorException $exception) { |
||||
echo str_contains($exception->getMessage(), 'undefinedIncludeVariable') ? "caught\n" : "wrong exception\n"; |
||||
} finally { |
||||
restore_error_handler(); |
||||
unlink($file); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
caught |
||||
Loading…
Reference in new issue