master

laravel/framework

Last updated at: 29/12/2023 09:24

RateLimited.php

TLDR

This file contains the implementation of the RateLimited middleware class, which is responsible for rate limiting jobs in the Laravel queue system.

Methods

handle($job, $next)

This method is responsible for handling the job and applying rate limiting logic to it.

  • Parameters:
    • $job: The job to be processed.
    • $next: The next middleware or closure to be executed.
  • Returns: The result of executing the next middleware or closure.

dontRelease()

This method is used to prevent the job from being released back to the queue if the rate limit is exceeded.

  • Returns: It returns the middleware instance itself for method chaining.

__sleep()

This method is called to prepare the object for serialization.

  • Returns: An array containing the names of the properties to be serialized.

__wakeup()

This method is called after unserialization and is used to re-instantiate the RateLimiter instance.

Classes

RateLimited

This class represents a middleware that applies rate limiting to jobs in the Laravel queue system.

  • Properties:
    • $limiter: An instance of the RateLimiter class used for rate limiting.
    • $limiterName: The name of the rate limiter.
    • $shouldRelease: Indicates whether the job should be released if the rate limit is exceeded.
  • Methods:
    • __construct($limiterName): Constructs a new instance of the RateLimited class.
    • handle($job, $next): Handles the job and applies rate limiting logic.
    • handleJob($job, $next, $limits): Handles a rate-limited job and checks if the rate limit is exceeded.
    • dontRelease(): Prevents the job from being released back to the queue if the rate limit is exceeded.
    • getTimeUntilNextRetry($key): Retrieves the number of seconds until the next retry for a job.
    • __sleep(): Prepares the object for serialization.
    • __wakeup(): Prepares the object after unserialization.
<?php

namespace Illuminate\Queue\Middleware;

use Illuminate\Cache\RateLimiter;
use Illuminate\Cache\RateLimiting\Unlimited;
use Illuminate\Container\Container;
use Illuminate\Support\Arr;

class RateLimited
{
    /**
     * The rate limiter instance.
     *
     * @var \Illuminate\Cache\RateLimiter
     */
    protected $limiter;

    /**
     * The name of the rate limiter.
     *
     * @var string
     */
    protected $limiterName;

    /**
     * Indicates if the job should be released if the limit is exceeded.
     *
     * @var bool
     */
    public $shouldRelease = true;

    /**
     * Create a new middleware instance.
     *
     * @param  string  $limiterName
     * @return void
     */
    public function __construct($limiterName)
    {
        $this->limiter = Container::getInstance()->make(RateLimiter::class);

        $this->limiterName = $limiterName;
    }

    /**
     * Process the job.
     *
     * @param  mixed  $job
     * @param  callable  $next
     * @return mixed
     */
    public function handle($job, $next)
    {
        if (is_null($limiter = $this->limiter->limiter($this->limiterName))) {
            return $next($job);
        }

        $limiterResponse = $limiter($job);

        if ($limiterResponse instanceof Unlimited) {
            return $next($job);
        }

        return $this->handleJob(
            $job,
            $next,
            collect(Arr::wrap($limiterResponse))->map(function ($limit) {
                return (object) [
                    'key' => md5($this->limiterName.$limit->key),
                    'maxAttempts' => $limit->maxAttempts,
                    'decaySeconds' => $limit->decaySeconds,
                ];
            })->all()
        );
    }

    /**
     * Handle a rate limited job.
     *
     * @param  mixed  $job
     * @param  callable  $next
     * @param  array  $limits
     * @return mixed
     */
    protected function handleJob($job, $next, array $limits)
    {
        foreach ($limits as $limit) {
            if ($this->limiter->tooManyAttempts($limit->key, $limit->maxAttempts)) {
                return $this->shouldRelease
                        ? $job->release($this->getTimeUntilNextRetry($limit->key))
                        : false;
            }

            $this->limiter->hit($limit->key, $limit->decaySeconds);
        }

        return $next($job);
    }

    /**
     * Do not release the job back to the queue if the limit is exceeded.
     *
     * @return $this
     */
    public function dontRelease()
    {
        $this->shouldRelease = false;

        return $this;
    }

    /**
     * Get the number of seconds that should elapse before the job is retried.
     *
     * @param  string  $key
     * @return int
     */
    protected function getTimeUntilNextRetry($key)
    {
        return $this->limiter->availableIn($key) + 3;
    }

    /**
     * Prepare the object for serialization.
     *
     * @return array
     */
    public function __sleep()
    {
        return [
            'limiterName',
            'shouldRelease',
        ];
    }

    /**
     * Prepare the object after unserialization.
     *
     * @return void
     */
    public function __wakeup()
    {
        $this->limiter = Container::getInstance()->make(RateLimiter::class);
    }
}