Атрибути переносять налаштування контейнера з провайдера ближче до класу, якого вони стосуються.
#[Bind] на інтерфейсі - яку реалізацію впроваджувати, з урахуванням оточення:
#[Bind(RedisEventPusher::class)]
#[Bind(FakeEventPusher::class, environments: ['local', 'testing'])]
interface EventPusher {}
Замінює $this->app->bind(EventPusher::class, ...) у провайдері.
#[Singleton] і #[Scoped] на класі - скільки екземплярів:
#[Singleton]
class ExchangeRates {}
Singleton - один на весь процес, Scoped - один на запит чи завдання.
Контекстні атрибути на параметрах - що саме впровадити:
public function __construct(
#[Config('app.timezone')] private string $timezone,
#[Storage('s3')] private Filesystem $disk,
#[CurrentUser] private User $user,
) {}
Є також Cache, DB, Log, Auth, Context, Give, RouteParameter, Tag.
Коли що обирати: атрибут зручний, коли правило належить самому класу й читається разом з ним. Провайдер лишається місцем для складної логіки створення, залежної від конфігурації, і для прив'язок до чужих класів, які не можна позначити атрибутом.