Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[11.x] Fix: Custom Exceptions with Multiple Arguments does not properly rein… #54705

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Illuminate\Concurrency\Console;

use Illuminate\Console\Command;
use ReflectionClass;
use Symfony\Component\Console\Attribute\AsCommand;
use Throwable;

Expand Down Expand Up @@ -51,12 +52,30 @@ public function handle()
} catch (Throwable $e) {
report($e);

$reflection = new ReflectionClass($e);
$constructor = $reflection->getConstructor();
$params = [];

if ($constructor) {
$declaringClass = $constructor->getDeclaringClass()->getName();

// Ensure we're only getting parameters from the current class, not inherited ones
if ($declaringClass === $reflection->getName()) {
$args = $constructor->getParameters();
foreach ($args as $param) {
$property = $param->name;
$params[$property] = $e->{$property} ?? null;
}
}
}

$this->output->write(json_encode([
'successful' => false,
'exception' => get_class($e),
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'params' => $params,
]));
}
}
Expand Down
29 changes: 26 additions & 3 deletions src/Illuminate/Concurrency/ProcessDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Defer\DeferredCallback;
use Laravel\SerializableClosure\SerializableClosure;
use ReflectionClass;

use function Illuminate\Support\defer;

Expand Down Expand Up @@ -47,9 +48,31 @@ public function run(Closure|array $tasks): array
$result = json_decode($result->output(), true);

if (! $result['successful']) {
throw new $result['exception'](
$result['message']
);
$exceptionClass = $result['exception'];
$reflection = new ReflectionClass($exceptionClass);

$args = $result['params'] ?? [];

if (empty($args)) {
throw new $result['exception'](
$result['message']
);
}

$constructor = $reflection->getConstructor();
if ($constructor) {
$constructorParams = $constructor->getParameters();
$finalArgs = [];

foreach ($constructorParams as $param) {
$paramName = $param->getName();
$finalArgs[] = $args[$paramName] ?? ($param->isOptional() ? $param->getDefaultValue() : null);
}
} else {
$finalArgs = [];
}

throw $reflection->newInstanceArgs($finalArgs);
}

return unserialize($result['result']);
Expand Down
41 changes: 41 additions & 0 deletions tests/Integration/Concurrency/ConcurrencyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace Illuminate\Tests\Integration\Concurrency;

use Exception;
use Illuminate\Concurrency\ProcessDriver;
use Illuminate\Foundation\Application;
use Illuminate\Process\Factory as ProcessFactory;
use Illuminate\Support\Facades\Concurrency;
use Orchestra\Testbench\TestCase;
use PHPUnit\Framework\Attributes\RequiresOperatingSystem;

Expand Down Expand Up @@ -49,4 +51,43 @@ public function testRunHandlerProcessErrorCode()
fn () => exit(1),
]);
}

public function testRunHandlerProcessErrorWithCustomExceptionWithoutParam()
{
$this->expectException(ExceptionWithoutParam::class);
$this->expectExceptionMessage('Test');
Concurrency::run([
fn () => throw new ExceptionWithoutParam('Test'),
]);
}

public function testRunHandlerProcessErrorWithCustomExceptionWithParam()
{
$this->expectException(ExceptionWithParam::class);
$this->expectExceptionMessage('API request to https://api.example.com failed with status 400 Bad Request');
Concurrency::run([
fn () => throw new ExceptionWithParam(
'https://api.example.com',
400,
'Bad Request',
'Invalid payload'
),
]);
}
}

class ExceptionWithoutParam extends Exception
{
}

class ExceptionWithParam extends Exception
{
public function __construct(
public string $uri,
public int $statusCode,
public string $reason,
public string|array $responseBody = '',
) {
parent::__construct("API request to {$uri} failed with status $statusCode $reason");
}
}
Loading