theofidry/sfcontext-bundle
A Symfony bundle that integrates SfContext, giving your application a simple way to define and access shared context from services, controllers, and templates. It keeps context handling centralized and consistent across your Symfony project.
Know what you're installing first. theofidry/sfcontext-bundle is a deliberately tiny bundle whose sole job is to hand you the Symfony container from a static call. It is archived (last release December 2019) and has effectively zero adoption, so treat it as a legacy-code bridge, not a foundation.
composer require theofidry/sfcontext-bundle, read its composer.json constraints — expect symfony/framework-bundle: ^3.4 || ^4.x and PHP 7.x. On a modern app the install will fail or pull ancient transitive deps; on a Symfony 4.4 LTS app it slots in cleanly.config/bundles.php (SfContext\SfContextBundle::class => ['all' => true]) or AppKernel::registerBundles() for older skeletons.Context class in src/) plus the bundle class that populates it during kernel boot. That's the entire API surface — a getContainer()/get($id)-style accessor pair. Confirm the exact method names and namespace from the repo, because the README is minimal and the package is too small to have generated years of blog posts you can crib from.include-style script, or a third-party library that calls back into a global. Write one smoke test that boots the kernel and asserts Context::get('some_service') returns the expected instance — that single test tells you whether the bundle's boot wiring is even working in your Symfony version.If that smoke test is the only thing you need, seriously consider skipping the bundle and using KernelInterface/ContainerInterface passed explicitly, or ServiceSubscriberInterface. The bundle earns its place only when you have many call sites in code you don't control.
Use it as a boundary adapter, not as a service locator sprinkled everywhere. The healthy pattern is: static call at the outer edge (legacy controller, CLI entry point, test bootstrap), then immediately hand the concrete service to plain classes via constructor injection. That keeps the static surface to a handful of files that you can grep and remove later.
// Legacy entry point only
$mailer = Context::get('app.mailer');
$notifier = new OrderNotifier($mailer); // everything below is DI-clean
Wrap string IDs behind typed helpers. Don't scatter Context::get('app.foo') across the codebase — string IDs silently break on refactors and are invisible to debug:container. Put one small facade per domain in front of it:
final class Services
{
public static function mailer(): MailerInterface
{
return Context::get(MailerInterface::class);
}
}
Now a rename is one edit, and static analysis can at least see the return type.
Testing pattern — reset the holder explicitly. The container reference is process-global, so it survives tearDown() and leaks between tests. In your base TestCase:
protected function setUp(): void
{
self::bootKernel(); // populates the static holder via kernel boot
}
protected function tearDown(): void
{
Context::reset(); // or whatever the bundle exposes; otherwise null the reference manually
parent::tearDown();
}
If the bundle has no reset method, keep your own Context::setContainer(null)-style helper in the test bootstrap rather than touching production code for test convenience.
Reuse WebTestCase. Inside functional tests, self::bootKernel() gives you a real container, so you can assert against services fetched statically without mocking. This is the fastest way to prove a migration path: write the assertion against Context::get(...) today, then swap to self::getContainer()->get(...) when you drop the bundle.
Migrating away (the actual long-term pattern). Because the bundle is archived, every adoption should come with an exit plan: replace static calls with constructor injection, one facade method at a time. Because you funnelled everything through Services::mailer()-style helpers, that's a mechanical change — make each helper resolve a service you inject at bootstrap instead of reading the container.
#[Autowire], ServiceSubscriberInterface, or an explicit ContainerInterface parameter instead — all built in, all maintained.Context::get('app.foo') will throw or return null for anything not explicitly public: true or aliased. That's a real constraint: you'll be tempted to mark services public, which disables inlining and container optimizations and undoes the main perf benefit of the DIC.Class not found errors that a bin/console cache:clear fixes. If something works in prod but not dev, suspect the static holder before your config.debug:container --deprecations, linting, and "unused service" reporting all miss it. Keep a grep list of static call sites in the README so the next developer can find them.ContainerAwareInterface/ContainerAwareTrait in 4.1 and removed in 6.0. Expect reviewers and static analysers to flag it; document why each call site exists (third-party callback, legacy script) so it doesn't look like an accident.How can I help you explore Laravel packages today?