- Add try-catch block around generated ZEND_FUNCTION/ZEND_METHOD wrappers - Convert C++ exceptions to normal Zend exception propagation at outermost wrapper - Prevent C++ exceptions from escaping through Zend's C frames during cleanup - Ensure EG(exception) is properly handled for frame cleanup in ZendVM - Add proper exception handling for native TypePHP code unwinding - Maintain callback boundary integrity for dynamic method callspull/48/head
parent
6e5fadcbc6
commit
8aa8cdb065
2 changed files with 78 additions and 1 deletions
@ -0,0 +1,67 @@ |
||||
--TEST-- |
||||
Zend callback frames are restored after caught exceptions |
||||
--FILE-- |
||||
<?php |
||||
|
||||
final class CallbackFrameProbe |
||||
{ |
||||
public function __clone() |
||||
{ |
||||
throw new DomainException('clone'); |
||||
} |
||||
|
||||
public function fail(): void |
||||
{ |
||||
throw new DomainException('reflection'); |
||||
} |
||||
} |
||||
|
||||
function callback_frame_is_stale(Throwable $exception, string $function): bool |
||||
{ |
||||
foreach ($exception->getTrace() as $frame) { |
||||
if (($frame['function'] ?? '') === $function) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
try { |
||||
$copy = clone new CallbackFrameProbe(); |
||||
} catch (DomainException $exception) { |
||||
} |
||||
try { |
||||
throw new RuntimeException('after clone'); |
||||
} catch (RuntimeException $exception) { |
||||
echo 'clone=', callback_frame_is_stale($exception, '__clone') ? 'stale' : 'clean', "\n"; |
||||
} |
||||
|
||||
try { |
||||
array_map(static function (int $value): int { |
||||
throw new DomainException('array_map'); |
||||
}, [1]); |
||||
} catch (DomainException $exception) { |
||||
} |
||||
try { |
||||
throw new RuntimeException('after array_map'); |
||||
} catch (RuntimeException $exception) { |
||||
echo 'array_map=', callback_frame_is_stale($exception, '{closure}') ? 'stale' : 'clean', "\n"; |
||||
} |
||||
|
||||
try { |
||||
(new ReflectionMethod(CallbackFrameProbe::class, 'fail'))->invoke(new CallbackFrameProbe()); |
||||
} catch (DomainException $exception) { |
||||
} |
||||
try { |
||||
throw new RuntimeException('after reflection'); |
||||
} catch (RuntimeException $exception) { |
||||
echo 'reflection=', callback_frame_is_stale($exception, 'fail') ? 'stale' : 'clean', "\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
clone=clean |
||||
array_map=clean |
||||
reflection=clean |
||||
Loading…
Reference in new issue