diff --git a/src/Process/ParallelProcess.php b/src/Process/ParallelProcess.php index c27155c..99f03cd 100644 --- a/src/Process/ParallelProcess.php +++ b/src/Process/ParallelProcess.php @@ -87,6 +87,13 @@ public function getErrorOutput() if (! $this->errorOutput) { $processOutput = $this->process->getErrorOutput(); + $marker = '___SPATIE_ASYNC_CHILD___'; + $markerPosition = strrpos($processOutput, $marker); + + if ($markerPosition !== false) { + $processOutput = substr($processOutput, $markerPosition + strlen($marker)); + } + $childResult = @unserialize(base64_decode($processOutput)); if ($childResult === false || ! array_key_exists('output', $childResult)) { diff --git a/src/Process/ProcessCallbacks.php b/src/Process/ProcessCallbacks.php index 52f9945..7780e70 100644 --- a/src/Process/ProcessCallbacks.php +++ b/src/Process/ProcessCallbacks.php @@ -64,8 +64,10 @@ public function triggerError() call_user_func_array($callback, [$exception]); - break; + return; } + + throw $exception; } abstract protected function resolveErrorOutput(): Throwable; diff --git a/src/Runtime/ChildRuntime.php b/src/Runtime/ChildRuntime.php index e0cbc4a..fa36db9 100644 --- a/src/Runtime/ChildRuntime.php +++ b/src/Runtime/ChildRuntime.php @@ -51,7 +51,7 @@ $output = new \Spatie\Async\Output\SerializableException($exception); - fwrite(STDERR, base64_encode(serialize(['output' => $output]))); + fwrite(STDERR, '___SPATIE_ASYNC_CHILD___'.base64_encode(serialize(['output' => $output]))); exit(1); } diff --git a/tests/Feature/ErrorHandlingTest.php b/tests/Feature/ErrorHandlingTest.php index b7ba7ad..a304c12 100644 --- a/tests/Feature/ErrorHandlingTest.php +++ b/tests/Feature/ErrorHandlingTest.php @@ -185,6 +185,37 @@ expect($caughtError)->toBeInstanceOf(ParseError::class); }); +it('preserves exception type when stderr contains noise', function () { + $pool = Pool::create(); + + $caughtException = null; + + $pool->add(childTask(function () { + trigger_error('some deprecation notice', E_USER_WARNING); + + throw new MyException('test'); + }))->catch(function (MyException $e) use (&$caughtException) { + $caughtException = $e; + }); + + $pool->wait(); + + expect($caughtException)->toBeInstanceOf(MyException::class); + expect($caughtException->getMessage())->toContain('test'); +}); + +it('throws exception when no catch handler matches', function () { + $pool = Pool::create(); + + $pool->add(childTask(function () { + throw new MyException('test'); + }))->catch(function (OtherException $e) { + // This handler should not match + }); + + $pool->wait(); +})->throws(MyException::class); + it('can handle synchronous exception', function () { Pool::$forceSynchronous = true; diff --git a/tests/Feature/PoolTest.php b/tests/Feature/PoolTest.php index 93ef1ab..dc98f99 100644 --- a/tests/Feature/PoolTest.php +++ b/tests/Feature/PoolTest.php @@ -336,7 +336,9 @@ $cntTasks = 30; foreach (range(1, $cntTasks) as $i) { - $pool->add(childTask(function () { return 1; })); + $pool->add(childTask(function () { + return 1; + })); } $pool->wait();