<?php

namespace Illuminate\Queue;

use Closure;
use Illuminate\Contracts\Queue\Factory as FactoryContract;
use Illuminate\Contracts\Queue\Monitor as MonitorContract;
use Illuminate\Support\Queue\Concerns\ResolvesQueueRoutes;
use InvalidArgumentException;
use UnitEnum;

use function Illuminate\Support\enum_value;

/**
 * @mixin \Illuminate\Contracts\Queue\Queue
 */
class QueueManager implements FactoryContract, MonitorContract
{
    use ResolvesQueueRoutes;

    /**
     * The application instance.
     *
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected $app;

    /**
     * The array of resolved queue connections.
     *
     * @var array
     */
    protected $connections = [];

    /**
     * The array of resolved queue connectors.
     *
     * @var array
     */
    protected $connectors = [];

    /**
     * Create a new queue manager instance.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     */
    public function __construct($app)
    {
        $this->app = $app;
    }

    /**
     * Register an event listener for the before job event.
     *
     * @param  mixed  $callback
     * @return void
     */
    public function before($callback)
    {
        $this->app['events']->listen(Events\JobProcessing::class, $callback);
    }

    /**
     * Register an event listener for the after job event.
     *
     * @param  mixed  $callback
     * @return void
     */
    public function after($callback)
    {
        $this->app['events']->listen(Events\JobProcessed::class, $callback);
    }

    /**
     * Register an event listener for the exception occurred job event.
     *
     * @param  mixed  $callback
     * @return void
     */
    public function exceptionOccurred($callback)
    {
        $this->app['events']->listen(Events\JobExceptionOccurred::class, $callback);
    }

    /**
     * Register an event listener for the daemon queue loop.
     *
     * @param  mixed  $callback
     * @return void
     */
    public function looping($callback)
    {
        $this->app['events']->listen(Events\Looping::class, $callback);
    }

    /**
     * Register an event listener for the failed job event.
     *
     * @param  mixed  $callback
     * @return void
     */
    public function failing($callback)
    {
        $this->app['events']->listen(Events\JobFailed::class, $callback);
    }

    /**
     * Register an event listener for the daemon queue starting.
     *
     * @param  mixed  $callback
     * @return void
     */
    public function starting($callback)
    {
        $this->app['events']->listen(Events\WorkerStarting::class, $callback);
    }

    /**
     * Register an event listener for the daemon queue stopping.
     *
     * @param  mixed  $callback
     * @return void
     */
    public function stopping($callback)
    {
        $this->app['events']->listen(Events\WorkerStopping::class, $callback);
    }

    /**
     * Set the queue route for the given class.
     *
     * @param  array|class-string  $class
     * @param  \UnitEnum|string|null  $queue
     * @param  \UnitEnum|string|null  $connection
     * @return void
     */
    public function route(array|string $class, $queue = null, $connection = null)
    {
        $this->queueRoutes()->set($class, $queue, $connection);
    }

    /**
     * Forward the given queue to another queue and/or connection.
     *
     * @param  array<string, \UnitEnum|string>|\UnitEnum|string  $queue
     * @param  \UnitEnum|string|null  $to
     * @param  \UnitEnum|string|null  $connection
     * @return void
     */
    public function forward(array|string|UnitEnum $queue, $to = null, $connection = null)
    {
        $this->queueRoutes()->forward($queue, $to, $connection);
    }

    /**
     * Determine if the driver is connected.
     *
     * @param  \UnitEnum|string|null  $name
     * @return bool
     */
    public function connected($name = null)
    {
        return isset($this->connections[enum_value($name) ?: $this->getDefaultDriver()]);
    }

    /**
     * Resolve a queue connection instance.
     *
     * @param  \UnitEnum|string|null  $name
     * @return \Illuminate\Contracts\Queue\Queue
     */
    public function connection($name = null)
    {
        $name = enum_value($name) ?: $this->getDefaultDriver();

        // If the connection has not been resolved yet we will resolve it now as all
        // of the connections are resolved when they are actually needed so we do
        // not make any unnecessary connection to the various queue end-points.
        if (! isset($this->connections[$name])) {
            $this->connections[$name] = $this->resolve($name);

            $this->connections[$name]->setContainer($this->app);
        }

        return $this->connections[$name];
    }

    /**
     * Resolve a queue connection.
     *
     * @param  string  $name
     * @return \Illuminate\Contracts\Queue\Queue
     *
     * @throws \InvalidArgumentException
     */
    protected function resolve($name)
    {
        $config = $this->getConfig($name);

        if (is_null($config)) {
            throw new InvalidArgumentException("The [{$name}] queue connection has not been configured.");
        }

        $queue = $this->getConnector($config['driver'])
            ->connect($config)
            ->setConnectionName($name);

        if (method_exists($queue, 'setConfig')) {
            $queue->setConfig($config);
        }

        return $queue;
    }

    /**
     * Get the connector for a given driver.
     *
     * @param  string  $driver
     * @return \Illuminate\Queue\Connectors\ConnectorInterface
     *
     * @throws \InvalidArgumentException
     */
    protected function getConnector($driver)
    {
        if (! isset($this->connectors[$driver])) {
            throw new InvalidArgumentException("No connector for [$driver].");
        }

        return call_user_func($this->connectors[$driver]);
    }

    /**
     * Pause a queue by its connection and name.
     *
     * @param  string  $connection
     * @param  string  $queue
     * @return void
     */
    public function pause($connection, $queue)
    {
        $this->app['cache']
            ->store()
            ->forever("illuminate:queue:paused:{$connection}:{$queue}", true);

        $this->app['events']->dispatch(
            new Events\QueuePaused($connection, $queue)
        );
    }

    /**
     * Pause a queue by its connection and name for a given amount of time.
     *
     * @param  string  $connection
     * @param  string  $queue
     * @param  \DateTimeInterface|\DateInterval|int  $ttl
     * @return void
     */
    public function pauseFor($connection, $queue, $ttl)
    {
        $this->app['cache']
            ->store()
            ->put("illuminate:queue:paused:{$connection}:{$queue}", true, $ttl);

        $this->app['events']->dispatch(
            new Events\QueuePaused($connection, $queue, $ttl)
        );
    }

    /**
     * Pause job processing for all queues on all connections.
     *
     * @return void
     */
    public function pauseAll()
    {
        $this->app['cache']
            ->store()
            ->forever('illuminate:queues:paused', true);

        $this->app['events']->dispatch(
            new Events\QueuesPaused
        );
    }

    /**
     * Resume a paused queue by its connection and name.
     *
     * @param  string  $connection
     * @param  string  $queue
     * @return void
     */
    public function resume($connection, $queue)
    {
        $this->app['cache']
            ->store()
            ->forget("illuminate:queue:paused:{$connection}:{$queue}");

        $this->app['events']->dispatch(
            new Events\QueueResumed($connection, $queue)
        );
    }

    /**
     * Resume job processing for all queues on all connections.
     *
     * Queues paused individually are not affected.
     *
     * @return void
     */
    public function resumeAll()
    {
        $this->app['cache']
            ->store()
            ->forget('illuminate:queues:paused');

        $this->app['events']->dispatch(
            new Events\QueuesResumed
        );
    }

    /**
     * Determine if a queue is paused.
     *
     * @param  string  $connection
     * @param  string  $queue
     * @return bool
     */
    public function isPaused($connection, $queue)
    {
        $cache = $this->app['cache']->store();

        return (bool) ($cache->get('illuminate:queues:paused')
            ?: $cache->get("illuminate:queue:paused:{$connection}:{$queue}"));
    }

    /**
     * Determine which of the given queues are currently paused.
     *
     * @param  string  $connection
     * @param  array  $queues
     * @return array
     */
    public function getPausedQueues($connection, $queues)
    {
        $cache = $this->app['cache']->store();

        if ($cache->get('illuminate:queues:paused')) {
            return array_values($queues);
        }

        $states = $cache->many(
            array_map(fn ($queue) => "illuminate:queue:paused:{$connection}:{$queue}", $queues)
        );

        return array_values(array_filter(
            $queues, fn ($queue) => $states["illuminate:queue:paused:{$connection}:{$queue}"] ?? false
        ));
    }

    /**
     * Indicate that queue workers should not poll for restart or pause signals.
     *
     * This prevents the workers from hitting the application cache to determine if they need to pause or restart.
     *
     * @return void
     */
    public function withoutInterruptionPolling()
    {
        Worker::$restartable = false;
        Worker::$pausable = false;
    }

    /**
     * Add a queue connection resolver.
     *
     * @param  string  $driver
     * @param  \Closure  $resolver
     * @return void
     */
    public function extend($driver, Closure $resolver)
    {
        $this->addConnector($driver, $resolver);
    }

    /**
     * Add a queue connection resolver.
     *
     * @param  string  $driver
     * @param  \Closure  $resolver
     * @return void
     */
    public function addConnector($driver, Closure $resolver)
    {
        $this->connectors[$driver] = $resolver;
    }

    /**
     * Get the queue connection configuration.
     *
     * @param  string  $name
     * @return array|null
     */
    protected function getConfig($name)
    {
        if (! is_null($name) && $name !== 'null') {
            return $this->app['config']["queue.connections.{$name}"];
        }

        return ['driver' => 'null'];
    }

    /**
     * Get the name of the default queue connection.
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        return $this->app['config']['queue.default'];
    }

    /**
     * Set the name of the default queue connection.
     *
     * @param  \UnitEnum|string  $name
     * @return void
     */
    public function setDefaultDriver($name)
    {
        $this->app['config']['queue.default'] = enum_value($name);
    }

    /**
     * Get the full name for the given connection.
     *
     * @param  string|null  $connection
     * @return string
     */
    public function getName($connection = null)
    {
        return $connection ?: $this->getDefaultDriver();
    }

    /**
     * Get the application instance used by the manager.
     *
     * @return \Illuminate\Contracts\Foundation\Application
     */
    public function getApplication()
    {
        return $this->app;
    }

    /**
     * Set the application instance used by the manager.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return $this
     */
    public function setApplication($app)
    {
        $this->app = $app;

        foreach ($this->connections as $connection) {
            $connection->setContainer($app);
        }

        return $this;
    }

    /**
     * Dynamically pass calls to the default connection.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->connection()->$method(...$parameters);
    }
}
                                                                                                                                                        {
    "name": "laravel/framework",
    "description": "The Laravel Framework.",
    "license": "MIT",
    "keywords": [
        "framework",
        "laravel"
    ],
    "authors": [
        {
            "name": "Taylor Otwell",
            "email": "taylor@laravel.com"
        }
    ],
    "homepage": "https://laravel.com",
    "support": {
        "issues": "https://github.com/laravel/framework/issues",
        "source": "https://github.com/laravel/framework"
    },
    "require": {
        "php": "^8.3",
        "ext-ctype": "*",
        "ext-filter": "*",
        "ext-hash": "*",
        "ext-mbstring": "*",
        "ext-openssl": "*",
        "ext-session": "*",
        "ext-tokenizer": "*",
        "composer-runtime-api": "^2.2",
        "brick/math": "^0.14.2 || ^0.15 || ^0.16 || ^0.17 || ^0.18 || ^0.19",
        "doctrine/inflector": "^2.0.5",
        "dragonmantank/cron-expression": "^3.4",
        "egulias/email-validator": "^4.0",
        "fruitcake/php-cors": "^1.3",
        "guzzlehttp/guzzle": "^7.8.2 || ^8.0",
        "guzzlehttp/promises": "^2.0.3 || ^3.0",
        "guzzlehttp/psr7": "^2.9 || ^3.0",
        "guzzlehttp/uri-template": "^1.0 || ^2.0",
        "laravel/prompts": "^0.3.11",
        "laravel/serializable-closure": "^2.0.10",
        "league/commonmark": "^2.8.1",
        "league/flysystem": "^3.25.1",
        "league/flysystem-local": "^3.25.1",
        "league/uri": "^7.5.1",
        "monolog/monolog": "^3.10",
        "nesbot/carbon": "^3.8.4",
        "nunomaduro/termwind": "^2.0",
        "psr/container": "^1.1.1 || ^2.0.1",
        "psr/http-message": "^1.0 || ^2.0",
        "psr/log": "^1.0 || ^2.0 || ^3.0",
        "psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
        "ramsey/uuid": "^4.7",
        "symfony/console": "^7.4.0 || ^8.0.0",
        "symfony/error-handler": "^7.4.0 || ^8.0.0",
        "symfony/finder": "^7.4.0 || ^8.0.0",
        "symfony/http-foundation": "^7.4.0 || ^8.0.0",
        "symfony/http-kernel": "^7.4.0 || ^8.0.0",
        "symfony/mailer": "^7.4.0 || ^8.0.0",
        "symfony/mime": "^7.4.0 || ^8.0.0",
        "symfony/polyfill-php84": "^1.36",
        "symfony/polyfill-php85": "^1.36",
        "symfony/polyfill-php86": "^1.36",
        "symfony/process": "^7.4.5 || ^8.0.5",
        "symfony/routing": "^7.4.0 || ^8.0.0",
        "symfony/uid": "^7.4.0 || ^8.0.0",
        "symfony/var-dumper": "^7.4.0 || ^8.0.0",
        "tijsverkoyen/css-to-inline-styles": "^2.2.5",
        "vlucas/phpdotenv": "^5.6.1",
        "voku/portable-ascii": "^2.0.2"
    },
    "require-dev": {
        "ext-gmp": "*",
        "ably/ably-php": "^1.0",
        "aws/aws-sdk-php": "^3.322.9",
        "fakerphp/faker": "^1.24",
        "intervention/image": "^4.0",
        "laravel/pint": "^1.18",
        "league/flysystem-aws-s3-v3": "^3.25.1",
        "league/flysystem-ftp": "^3.25.1",
        "league/flysystem-path-prefixing": "^3.25.1",
        "league/flysystem-read-only": "^3.25.1",
        "league/flysystem-sftp-v3": "^3.25.1",
        "mockery/mockery": "^1.6.10",
        "opis/json-schema": "^2.4.1",
        "orchestra/testbench-core": "^11.0.0",
        "pda/pheanstalk": "^7.0.0 || ^8.0.0",
        "php-http/discovery": "^1.15",
        "phpstan/phpstan": "^2.0",
        "phpunit/phpunit": "^11.5.50 || ^12.5.8 || ^13.0.3",
        "predis/predis": "^2.3 || ^3.0",
        "rector/rector": "^2.3",
        "resend/resend-php": "^1.0",
        "symfony/cache": "^7.4.0 || ^8.0.0",
        "symfony/http-client": "^7.4.0 || ^8.0.0",
        "symfony/psr-http-message-bridge": "^7.4.0 || ^8.0.0",
        "symfony/translation": "^7.4.0 || ^8.0.0"
    },
    "replace": {
        "illuminate/auth": "self.version",
        "illuminate/broadcasting": "self.version",
        "illuminate/bus": "self.version",
        "illuminate/cache": "self.version",
        "illuminate/collections": "self.version",
        "illuminate/concurrency": "self.version",
        "illuminate/conditionable": "self.version",
        "illuminate/config": "self.version",
        "illuminate/console": "self.version",
        "illuminate/container": "self.version",
        "illuminate/contracts": "self.version",
        "illuminate/cookie": "self.version",
        "illuminate/database": "self.version",
        "illuminate/encryption": "self.version",
        "illuminate/events": "self.version",
        "illuminate/filesystem": "self.version",
        "illuminate/hashing": "self.version",
        "illuminate/http": "self.version",
        "illuminate/image": "self.version",
        "illuminate/json-schema": "self.version",
        "illuminate/log": "self.version",
        "illuminate/macroable": "self.version",
        "illuminate/mail": "self.version",
        "illuminate/notifications": "self.version",
        "illuminate/pagination": "self.version",
        "illuminate/pipeline": "self.version",
        "illuminate/process": "self.version",
        "illuminate/queue": "self.version",
        "illuminate/redis": "self.version",
        "illuminate/reflection": "self.version",
        "illuminate/routing": "self.version",
        "illuminate/session": "self.version",
        "illuminate/support": "self.version",
        "illuminate/testing": "self.version",
        "illuminate/translation": "self.version",
        "illuminate/validation": "self.version",
        "illuminate/view": "self.version",
        "spatie/once": "*"
    },
    "conflict": {
        "tightenco/collect": "<5.5.33"
    },
    "provide": {
        "psr/container-implementation": "1.1 || 2.0",
        "psr/log-implementation": "1.0 || 2.0 || 3.0",
        "psr/simple-cache-implementation": "1.0 || 2.0 || 3.0"
    },
    "suggest": {
        "ext-apcu": "Required to use the APC cache driver.",
        "ext-fileinfo": "Required to use the Filesystem class.",
        "ext-ftp": "Required to use the Flysystem FTP driver.",
        "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
        "ext-memcached": "Required to use the memcache cache driver.",
        "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.",
        "ext-pdo": "Required to use all database features.",
        "ext-posix": "Required to use all features of the queue worker.",
        "ext-redis": "Required to use the Redis cache and queue drivers (^4.0 || ^5.0 || ^6.0).",
        "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
        "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).",
        "brianium/paratest": "Required to run tests in parallel (^7.0 || ^8.0).",
        "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).",
        "filp/whoops": "Required for friendly error pages in development (^2.14.3).",
        "intervention/image": "Required to use the image processing features (^4.0).",
        "laravel/tinker": "Required to use the tinker console command (^2.0).",
        "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).",
        "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).",
        "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).",
        "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)",
        "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).",
        "mockery/mockery": "Required to use mocking (^1.6).",
        "pda/pheanstalk": "Required to use the beanstalk queue driver (^7.0 || ^8.0).",
        "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).",
        "phpunit/phpunit": "Required to use assertions and run tests (^11.5.50 || ^12.5.8 || ^13.0.3).",
        "predis/predis": "Required to use the predis connector (^2.3 || ^3.0).",
        "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0 || ^7.0).",
        "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0 || ^1.0).",
        "spatie/fork": "Required to use the 'fork' concurrency driver (^1.2).",
        "symfony/cache": "Required to PSR-6 cache bridge (^7.4 || ^8.0).",
        "symfony/filesystem": "Required to enable support for relative symbolic links (^7.4 || ^8.0).",
        "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.4 || ^8.0).",
        "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.4 || ^8.0).",
        "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.4 || ^8.0).",
        "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.4 || ^8.0)."
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "autoload": {
        "psr-4": {
            "Illuminate\\": "src/Illuminate/",
            "Illuminate\\Support\\": [
                "src/Illuminate/Macroable/",
                "src/Illuminate/Collections/",
                "src/Illuminate/Conditionable/",
                "src/Illuminate/Reflection/"
            ]
        },
        "files": [
            "src/Illuminate/Collections/functions.php",
            "src/Illuminate/Collections/helpers.php",
            "src/Illuminate/Events/functions.php",
            "src/Illuminate/Filesystem/functions.php",
            "src/Illuminate/Foundation/helpers.php",
            "src/Illuminate/Log/functions.php",
            "src/Illuminate/Reflection/helpers.php",
            "src/Illuminate/Support/functions.php",
            "src/Illuminate/Support/helpers.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Illuminate\\Tests\\": "tests/"
        },
        "files": [
            "tests/Database/stubs/MigrationCreatorFakeMigration.php"
        ]
    },
    "config": {
        "allow-plugins": {
            "composer/package-versions-deprecated": true,
            "php-http/discovery": false
        },
        "audit": {
            "ignore": {
                "GHSA-qrr6-mg7r-m243": "Ensure testing features are compatible with affected PHPUnit versions",
                "GHSA-vvj3-c3rp-c85p": "Ensure testing features are compatible with affected PHPUnit versions"
            }
        },
        "sort-packages": true
    },
    "extra": {
        "branch-alias": {
            "dev-master": "13.0.x-dev"
        }
    }
}
