Skip to content

Conversation

@cosmastech
Copy link
Contributor

@cosmastech cosmastech commented Aug 11, 2025

Noticed earlier that this doesn't work as described in the docs 😰


The methodology is that we only check for the Scoped/Singleton on classes (interfaces, abstract classes) that also have Bind.

This raises a few questions:

  1. Should we be caching classes that we have already checked for Scoped/Singleton?
  2. Do we care about parent-child hierarchies? If we have InterfaceB which extends InterfaceA, and InterfaceA is marked as a singleton, when we call resolve(InterfaceB::class), should it be marking it as a singleton?

@github-actions
Copy link

Thanks for submitting a PR!

Note that draft PR's are not reviewed. If you would like a review, please mark your pull request as ready for review in the GitHub user interface.

Pull requests that are abandoned in draft may be closed due to inactivity.

@cosmastech cosmastech changed the title [12.x] Fix usage of Bind with Scoped and Singleton attributes [12.x] Fix usage of Scoped and Singleton on interfaces Aug 11, 2025
@cosmastech cosmastech marked this pull request as ready for review August 12, 2025 01:27
@rodrigopedra
Copy link
Contributor

For @taylorotwell's reference:

The docs says:

Furthermore, Singleton and Scoped attributes may be applied to indicate if the container bindings should be resolved once or once per request / job lifecycle:

use App\Services\RedisEventPusher;
use Illuminate\Container\Attributes\Bind;
use Illuminate\Container\Attributes\Singleton;

#[Bind(RedisEventPusher::class)]
#[Singleton]
interface EventPusher
{
    // ...
}

Reference: https://laravel.com/docs/12.x/container#bind-attribute

But within a new project, the following assertion fails:

<?php // ./routes/console.php

use Illuminate\Container\Attributes\Bind;
use Illuminate\Container\Attributes\Singleton;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Str;

Artisan::command('app:test', function () {
    $one = \resolve(ISingleton::class);
    $other = \resolve(ISingleton::class);

    \assert($one->id === $other->id);
});

#[Bind(ConcreteSingleton::class)]
#[Singleton]
interface ISingleton
{

}

class ConcreteSingleton implements ISingleton
{
    public readonly string $id;

    public function __construct() {
        $this->id = Str::random();
    }
}
$ php artisan app:test

   AssertionError 

  assert($one->id === $other->id)

  at routes/console.php:12

As a workaround, in the current implementation (without this PR), if one applies the Singleton attribute to the concrete class (ConcreteSingleton), the code above works as expected.

But as this PR states, it is different from what is recommended in the docs.

@taylorotwell taylorotwell merged commit bebbf49 into laravel:12.x Aug 12, 2025
41 of 60 checks passed
@cosmastech cosmastech deleted the singleton-on-bind branch August 12, 2025 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants