Skip to content

Commit a34c475

Browse files
authored
Merge pull request #255 from spatie/fix/stderr-pollution-and-silent-exception-swallowing
Fix STDERR pollution corrupting exception serialization + silent exception swallowing
2 parents f9e9d49 + 34d2eaa commit a34c475

File tree

5 files changed

+45
-3
lines changed

5 files changed

+45
-3
lines changed

src/Process/ParallelProcess.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ public function getErrorOutput()
8787
if (! $this->errorOutput) {
8888
$processOutput = $this->process->getErrorOutput();
8989

90+
$marker = '___SPATIE_ASYNC_CHILD___';
91+
$markerPosition = strrpos($processOutput, $marker);
92+
93+
if ($markerPosition !== false) {
94+
$processOutput = substr($processOutput, $markerPosition + strlen($marker));
95+
}
96+
9097
$childResult = @unserialize(base64_decode($processOutput));
9198

9299
if ($childResult === false || ! array_key_exists('output', $childResult)) {

src/Process/ProcessCallbacks.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ public function triggerError()
6464

6565
call_user_func_array($callback, [$exception]);
6666

67-
break;
67+
return;
6868
}
69+
70+
throw $exception;
6971
}
7072

7173
abstract protected function resolveErrorOutput(): Throwable;

src/Runtime/ChildRuntime.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151

5252
$output = new \Spatie\Async\Output\SerializableException($exception);
5353

54-
fwrite(STDERR, base64_encode(serialize(['output' => $output])));
54+
fwrite(STDERR, '___SPATIE_ASYNC_CHILD___'.base64_encode(serialize(['output' => $output])));
5555

5656
exit(1);
5757
}

tests/Feature/ErrorHandlingTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,37 @@
185185
expect($caughtError)->toBeInstanceOf(ParseError::class);
186186
});
187187

188+
it('preserves exception type when stderr contains noise', function () {
189+
$pool = Pool::create();
190+
191+
$caughtException = null;
192+
193+
$pool->add(childTask(function () {
194+
trigger_error('some deprecation notice', E_USER_WARNING);
195+
196+
throw new MyException('test');
197+
}))->catch(function (MyException $e) use (&$caughtException) {
198+
$caughtException = $e;
199+
});
200+
201+
$pool->wait();
202+
203+
expect($caughtException)->toBeInstanceOf(MyException::class);
204+
expect($caughtException->getMessage())->toContain('test');
205+
});
206+
207+
it('throws exception when no catch handler matches', function () {
208+
$pool = Pool::create();
209+
210+
$pool->add(childTask(function () {
211+
throw new MyException('test');
212+
}))->catch(function (OtherException $e) {
213+
// This handler should not match
214+
});
215+
216+
$pool->wait();
217+
})->throws(MyException::class);
218+
188219
it('can handle synchronous exception', function () {
189220
Pool::$forceSynchronous = true;
190221

tests/Feature/PoolTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,9 @@
336336
$cntTasks = 30;
337337

338338
foreach (range(1, $cntTasks) as $i) {
339-
$pool->add(childTask(function () { return 1; }));
339+
$pool->add(childTask(function () {
340+
return 1;
341+
}));
340342
}
341343

342344
$pool->wait();

0 commit comments

Comments
 (0)