Skip to content

Static Analysis

The package includes PHPStan rules that catch common mistakes at analysis time.

Lifecycle Method Parameters

When a lifecycle method declares parameters, they are resolved from the arguments you passed to execute(). The rule catches mismatches between the two.

Type Match, Name Mismatch

When a lifecycle method declares a parameter with the right type but a different name, the container can't match it to the dispatched argument. For Eloquent models this means a new empty instance instead of the one you queued:

php
class ProcessOrder
{
    use QueueableExecutable;

    public function execute(Order $order): void
    {
        /* ... */
    }

    // BUG: $o instead of $order
    public function displayName(Order $o): string
    {
        return "Process order #{$o->reference}"; // $o->reference is null!
    }
}

PHPStan will flag this with: Parameter $o on method displayName() has type Order matching execute() parameter $order — did you mean $order?

Other Cases

php
class ProcessOrder
{
    use QueueableExecutable;

    public function execute(Order $order): void
    {
        /* ... */
    }

    // Name matches, type differs. Likely a typo
    public function tags(string $order): array
    {
        /* ... */
    }
}

PHPStan will flag this with: Parameter $order on method tags() has type string but execute() declares $order as Order

php
class ProcessOrder
{
    use QueueableExecutable;

    public function execute(Order $order): void
    {
        /* ... */
    }

    // Parameter not on execute() at all. Dead parameter
    public function uniqueId(string $tenant): string
    {
        /* ... */
    }
}

PHPStan will flag this with: Parameter $tenant on method uniqueId() is not declared on execute()

php
class ProcessOrder
{
    use QueueableExecutable;

    public function execute(Order $order): void
    {
        /* ... */
    }

    // failed() must have Throwable as first parameter
    public function failed(Order $order): void
    {
        /* ... */
    }
}

PHPStan will flag this with: Method failed() must declare Throwable as its first parameter, found Order $order

php
class ProcessOrder
{
    use QueueableExecutable;

    public function execute(Order $order): void
    {
        /* ... */
    }

    // configure() must have QueueableConfig as first parameter
    public function configure(Order $order): void
    {
        /* ... */
    }
}

PHPStan will flag this with: Method configure() must declare QueueableConfig as its first parameter, found Order $order

Checked Lifecycle Methods

The rule checks all lifecycle methods: configure, backoff, displayName, retryUntil, tries, uniqueFor, uniqueId, tags, middleware, failed, uniqueVia.

Suppressing the Rule

php
// @phpstan-ignore executable.paramMismatch
public function displayName(Order $o): string

Or globally in phpstan.neon:

yaml
parameters:
    ignoreErrors:
        - identifier: executable.paramMismatch

Direct execute() Calls

Calling ->execute() directly on an executable instance bypasses the execution pipeline. Transactions, conditional execution, and after-response dispatch won't work.

php
$action = app(ProcessOrder::class);
$action->execute($order); 

PHPStan will flag this with: Calling execute() directly bypasses the execution pipeline. Use $action->sync()->execute() instead.

Suppressing the Rule

php
// @phpstan-ignore executable.directExecuteCall
$action->execute($order);

Or globally in phpstan.neon:

yaml
parameters:
    ignoreErrors:
        - identifier: executable.directExecuteCall

Queue Features on Sync Executables

Classes that use the Executable trait run synchronously. Queue-related properties, lifecycle methods, interfaces, and attributes only take effect with the QueueableExecutable trait. The rule flags these dead declarations so you can remove them or switch to QueueableExecutable.

Queue Properties

php
class ProcessOrder
{
    use Executable;

    public int $tries = 3; 
    public int $timeout = 30; 

    public function execute(Order $order): void
    {
        /* ... */
    }
}

PHPStan will flag this with: Class ProcessOrder uses Executable (sync-only) but declares queue property $tries. This has no effect without the QueueableExecutable trait.

Lifecycle Methods

php
class ProcessOrder
{
    use Executable;

    public function execute(Order $order): void
    {
        /* ... */
    }

    public function tags(): array
    {
        return ["order"];
    }
}

PHPStan will flag this with: Class ProcessOrder uses Executable (sync-only) but declares queue lifecycle method tags(). This has no effect without the QueueableExecutable trait.

Queue Interfaces

php
use Illuminate\Contracts\Queue\ShouldBeEncrypted;

class ProcessOrder implements ShouldBeEncrypted
{
    use Executable;

    public function execute(Order $order): void
    {
        /* ... */
    }
}

PHPStan will flag this with: Class ProcessOrder uses Executable (sync-only) but implements ShouldBeEncrypted. This has no effect without the QueueableExecutable trait.

Queue Attributes

php
use Illuminate\Queue\Attributes\WithoutRelations; 

#[WithoutRelations]
class ProcessOrder
{
    use Executable;

    public function execute(Order $order): void
    {
        /* ... */
    }
}

PHPStan will flag this with: Class ProcessOrder uses Executable (sync-only) but has attribute #[WithoutRelations]. This has no effect without the QueueableExecutable trait.

Suppressing the Rule

php
// @phpstan-ignore executable.syncWithQueueFeatures
public int $tries = 3;

Or globally in phpstan.neon:

yaml
parameters:
    ignoreErrors:
        - identifier: executable.syncWithQueueFeatures