> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Classe Lottery

> Découvrez comment implémenter élégamment des opérations probabilistes avec Illuminate\Support\Lottery. Cas d'utilisation lors du développement de packages et fixation des résultats pour les tests inclus.

## Qu'est-ce que la classe Lottery

`Illuminate\Support\Lottery` est une classe utilitaire qui permet d'exprimer des opérations basées sur des probabilités via une API fluide. Elle permet d'écrire simplement des schémas tels que « exécuter un traitement une fois toutes les 100 requêtes » ou « ne journaliser en détail qu'une partie des requêtes ».

<Info>
  L'implémentation se trouve dans `src/Illuminate/Support/Lottery.php`. Laravel l'utilise également en interne, par exemple pour le garbage collector de session ou l'élagage des verrous de cache.
</Info>

```mermaid theme={null}
flowchart LR
  A["Lottery::odds(1, 100)"] --> B{"tirage aléatoire"}
  B -->|"gagné (1 %)"| C["callback winner exécuté"]
  B -->|"perdu (99 %)"| D["callback loser exécuté"]
```

## Utilisation de base

### Spécifier la probabilité avec un ratio entier

`Lottery::odds($chances, $outOf)` définit la probabilité « $chances gains sur $outOf ».

```php theme={null}
use Illuminate\Support\Lottery;

Lottery::odds(1, 100)           // 1 fois sur 100
    ->winner(fn () => $this->runMaintenance())
    ->loser(fn () => null)
    ->choose();
```

### Spécifier la probabilité avec un décimal

Si `$outOf` est omis et qu'un décimal entre `0.0` et `1.0` est passé, il est utilisé tel quel comme probabilité.

```php theme={null}
Lottery::odds(0.01)             // 1 % de probabilité
    ->winner(fn () => $this->sample())
    ->choose();
```

<Warning>
  En cas de spécification décimale, une valeur supérieure à `1.0` lève une `RuntimeException`.
</Warning>

### Retourner un booléen sans callback

Sans winner / loser définis, `choose()` retourne `true` en cas de gain et `false` en cas de perte.

```php theme={null}
$shouldSample = Lottery::odds(1, 50)->choose(); // bool
```

### Exécuter plusieurs fois

Passer un nombre à `choose($times)` retourne un tableau de résultats.

```php theme={null}
$results = Lottery::odds(1, 2)
    ->winner(fn () => 'win')
    ->loser(fn () => 'lose')
    ->choose(10);

// ex. : ['win', 'lose', 'win', 'win', 'lose', ...]
```

### Passer comme callable

L'instance `Lottery` implémente `__invoke` et peut donc être passée directement à une API attendant un callable.

```php theme={null}
// Exemple : passé en second argument de DB::whenQueryingForLongerThan
DB::whenQueryingForLongerThan(
    Interval::seconds(5),
    Lottery::odds(1, 5)->winner(function ($connection) {
        // Lorsqu'une requête lente est détectée, envoyer une alerte 1 fois sur 5
        Alert::send("Slow query on {$connection->getName()}");
    })
);
```

## Cas d'utilisation concrets

### 1. Élagage du cache (une fois sur cent)

Idéal pour les opérations de maintenance sans nécessité d'exécution systématique, comme la suppression d'enregistrements expirés.

```php theme={null}
Lottery::odds(1, 100)
    ->winner(fn () => Cache::store('database')->flush())
    ->choose();
```

### 2. Échantillonnage de télémétrie (log détaillé sur une partie des requêtes)

Utile quand journaliser toutes les requêtes serait coûteux.

```php theme={null}
Lottery::odds(1, 20)
    ->winner(function () use ($request) {
        Log::channel('telemetry')->info('Request sampled', [
            'url'      => $request->url(),
            'duration' => microtime(true) - LARAVEL_START,
            'memory'   => memory_get_peak_usage(true),
        ]);
    })
    ->choose();
```

### 3. Comportement de type A/B testing

Répartit les utilisateurs de manière probabiliste entre deux chemins de code.

```php theme={null}
$result = Lottery::odds(1, 2)
    ->winner(fn ($user) => $this->newCheckoutFlow($user))
    ->loser(fn ($user) => $this->legacyCheckoutFlow($user))
    ->choose();
```

### 4. En complément du Scheduler pour exécuter aléatoirement une tâche périodique

Utile pour éviter les exécutions redondantes sur plusieurs serveurs tout en exécutant aléatoirement une tâche.

```php theme={null}
// app/Console/Kernel.php
$schedule->call(function () {
    Lottery::odds(1, 3)
        ->winner(fn () => Artisan::call('cache:prune-stale-tags'))
        ->choose();
})->everyMinute();
```

## Schémas probabilistes dans le framework Laravel

Laravel utilise abondamment des traitements de maintenance probabilistes en interne. Certaines implémentations, antérieures à la classe `Lottery`, utilisent directement `random_int()`, mais reposent sur la même idée.

<Steps>
  <Step title="Session : ramasse-miettes">
    `Illuminate\Session\Middleware\StartSession::configHitsLottery()` s'appuie sur la configuration `lottery` de `config/session.php` et utilise `random_int` pour décider probabilistiquement d'exécuter le GC.

    ```php theme={null}
    // config/session.php
    'lottery' => [2, 100], // 2 requêtes sur 100

    // Interne de StartSession (utilisation directe de random_int)
    protected function configHitsLottery(array $config): bool
    {
        return random_int(1, $config['lottery'][1]) <= $config['lottery'][0];
    }
    ```
  </Step>

  <Step title="DatabaseLock : élagage des verrous expirés">
    `Illuminate\Cache\DatabaseLock::acquire()` applique le même ratio à chaque acquisition de verrou pour supprimer les verrous expirés.

    ```php theme={null}
    // config/cache.php (driver database)
    'lock_lottery' => [2, 100], // élagage 2 fois sur 100

    // Interne de DatabaseLock::acquire() (utilisation directe de random_int)
    if (random_int(1, $this->lottery[1]) <= $this->lottery[0]) {
        $this->pruneExpiredLocks();
    }
    ```
  </Step>

  <Step title="DB::whenQueryingForLongerThan — exemple avec la classe Lottery">
    Comme une instance `Lottery` peut être passée en tant que callable, elle s'intègre directement au callback de détection de requêtes lentes.

    ```php theme={null}
    DB::whenQueryingForLongerThan(
        Interval::seconds(5),
        Lottery::odds(1, 5)->winner(function ($connection) {
            Log::warning("Slow query on {$connection->getName()}");
        })
    );
    ```
  </Step>
</Steps>

<Tip>
  Alors que Session et DatabaseLock utilisent directement `random_int()`, la classe `Lottery` offre l'avantage de pouvoir maîtriser les résultats en tests avec `alwaysWin()` / `alwaysLose()` / `fix()`. En développement de packages, choisir `Lottery` améliore la testabilité.
</Tip>

## Utilisation pendant les tests

Pour tester du code intégrant de l'aléatoire, servez-vous des API de test fournies par `Lottery`.

### `Lottery::alwaysWin()` — toujours gagnant

```php theme={null}
public function test_maintenance_runs_on_win(): void
{
    $ranMaintenance = false;

    Lottery::alwaysWin(function () use (&$ranMaintenance) {
        Lottery::odds(1, 100)
            ->winner(function () use (&$ranMaintenance) {
                $ranMaintenance = true;
            })
            ->choose();
    });

    $this->assertTrue($ranMaintenance);
}
```

### `Lottery::alwaysLose()` — toujours perdant

```php theme={null}
public function test_maintenance_skipped_on_lose(): void
{
    $ranMaintenance = false;

    Lottery::alwaysLose(function () use (&$ranMaintenance) {
        Lottery::odds(1, 100)
            ->winner(function () use (&$ranMaintenance) {
                $ranMaintenance = true;
            })
            ->choose();
    });

    $this->assertFalse($ranMaintenance);
}
```

### `Lottery::fix()` — fixer les résultats par séquence

Vous pouvez contrôler les résultats de plusieurs appels avec un tableau de `true`/`false`.

```php theme={null}
public function test_alternating_results(): void
{
    Lottery::fix([true, false, true, false]);

    $results = Lottery::odds(1, 100)
        ->winner(fn () => 'winner')
        ->loser(fn () => 'loser')
        ->choose(4);

    $this->assertSame(['winner', 'loser', 'winner', 'loser'], $results);

    Lottery::determineResultNormally(); // à toujours restaurer après le test
}
```

<Warning>
  `alwaysWin()` / `alwaysLose()` / `fix()` modifient des propriétés statiques globales. Appelez impérativement `Lottery::determineResultNormally()` dans le `tearDown()` du test.
</Warning>

```php theme={null}
protected function tearDown(): void
{
    Lottery::determineResultNormally();

    parent::tearDown();
}
```

### `Lottery::setResultFactory()` — injecter une fabrique personnalisée

Pour un contrôle plus fin, utilisez une fabrique personnalisée.

```php theme={null}
Lottery::setResultFactory(function ($chances, $outOf) {
    // Logique personnalisée : toujours gagner
    return true;
});

// Réinitialiser après le test
Lottery::determineResultNormally();
```

## Utilisation en développement de packages

### Enregistrement dans un service provider

Pour intégrer un traitement de maintenance dans le service provider d'un package, `Lottery` permet d'en répartir la charge.

```php theme={null}
use Illuminate\Support\Lottery;
use Illuminate\Support\ServiceProvider;

class AcmeServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->app->booted(function () {
            Lottery::odds(1, 100)
                ->winner(fn () => $this->pruneExpiredRecords())
                ->choose();
        });
    }

    protected function pruneExpiredRecords(): void
    {
        $this->app['db']->table('acme_logs')
            ->where('expires_at', '<', now())
            ->delete();
    }
}
```

### Charger la probabilité depuis la configuration

Rendre la probabilité configurable via un fichier facilite l'ajustement par l'utilisateur.

```php theme={null}
$lottery = config('acme.prune_lottery', [1, 100]);

Lottery::odds(...$lottery)
    ->winner(fn () => $this->pruneExpiredRecords())
    ->choose();
```

```php theme={null}
// config/acme.php
return [
    // [gains, essais] = élagage 1 requête sur 100
    'prune_lottery' => [1, 100],
];
```

### Échantillonnage dans un middleware

```php theme={null}
use Illuminate\Support\Lottery;

class SampleTelemetryMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        Lottery::odds(1, 50)
            ->winner(fn () => $this->recordTelemetry($request, $response))
            ->choose();

        return $response;
    }
}
```

## Référence API

| Méthode                               | Description                                                       |
| ------------------------------------- | ----------------------------------------------------------------- |
| `Lottery::odds($chances, $outOf)`     | Fabrique statique qui crée une instance Lottery                   |
| `->winner(callable $callback)`        | Définit le callback en cas de gain                                |
| `->loser(callable $callback)`         | Définit le callback en cas de perte                               |
| `->choose($times = null)`             | Exécute Lottery. Un tableau est retourné si `$times` est spécifié |
| `Lottery::alwaysWin($callback)`       | Pour les tests : toujours gagner                                  |
| `Lottery::alwaysLose($callback)`      | Pour les tests : toujours perdre                                  |
| `Lottery::fix($sequence)`             | Pour les tests : fixer les résultats en séquence                  |
| `Lottery::determineResultNormally()`  | Réinitialise la fixation utilisée pour les tests                  |
| `Lottery::setResultFactory(callable)` | Injecte une logique de décision personnalisée                     |

## Pages associées

<Columns cols={2}>
  <Card title="Trait Macroable" icon="puzzle-piece" href="/fr/advanced/macroable">
    Découvrez le pattern d'extension permettant d'ajouter des méthodes à une classe existante.
  </Card>

  <Card title="Trait Conditionable" icon="git-branch" href="/fr/advanced/conditionable">
    Découvrez la conception de chaînes de branchement conditionnel avec `when()` / `unless()`.
  </Card>
</Columns>


## Related topics

- [Classe Fluent](/fr/advanced/fluent.md)
- [Manipulation de chaînes (classe Str)](/fr/strings.md)
- [Casts personnalisés Eloquent](/fr/advanced/eloquent-casts.md)
- [Contrôleurs](/fr/controllers.md)
- [Feed Generator](/fr/packages/laravel-bluesky/feed-generator.md)
