- 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