In the first post of this miniseries, we look at Symfony Http Kernel with a critical eye on how it causes project overweight.
In the second post, we looked at bundles from a very raw point of view - what do we need from them?
In the spirit of thesis, antithesis, and synthesis philosophy, today, we'll combine both parts. We'll look for a solution to the original question: How can we build Kernel in Console Application without the Http burden?
"Perfection is achieved, not when there is nothing more to add,
but when there is nothing left to take away."
In previous posts, we defined requirements that we want from Symfony Kernel from Console Applications:
symfony/console
and symfony/dependency-injection
packagesCurrently, we have old projects that use symfony/http-kernel
with a bunch of bundles. But when we look closer at Symfony bundles, we'll see they only add configs and compiler passes. So we can drop bundles and extensions altogether.
Drop dependency on symfony/http-kernel
, but make the project work as before.
In an ideal world, we want a container factory class that loads provided configs:
use Symfony\Component\DependencyInjection\ContainerBuilder;
final class ContainerBuilderFactory
{
/**
* @param string[] $configFiles
* @param CompilerPassInterface[] $compilerPasses
* @param ExtensionInterface[] $extensions
*/
public function create(
array $configFiles,
array $compilerPasses,
array $extensions
): ContainerBuilder {
$containerBuilder = new ContainerBuilder();
foreach ($extensions as $extension) {
$containerBuilder->registerExtension($extension);
}
foreach ($configFiles as $configFile) {
$delegatingLoader->load($configFile);
}
foreach ($compilerPasses as $compilerPass) {
$containerBuilder->addCompilerPass($compilerPass);
}
return $containerBuilder;
}
}
Then we could use it directly in any command-line application Kernel:
use Psr\Container\ContainerInterface;
use Symplify\SymplifyKernel\ContainerBuilderFactory;
use Symplify\ComposerJsonManipulator\ValueObject\ComposerJsonManipulatorConfig;
final class MonorepoBuilderKernel
{
/**
* @param string[] $configFiles
*/
public function createFromConfigs(array $configFiles): ContainerInterface
{
// provide local config here
$configFiles[] = __DIR__ . '/../../config/config.php';
// external configs
$configFiles[] = ComposerJsonManipulatorConfig::FILE_PATH;
$containerBuilderFactory = new ContainerBuilderFactory();
$containerBuilder = $containerBuilderFactory->create($configFiles, [], []);
// build the container
$containerBuilder->compile();
return $containerBuilder;
}
}
How would it meet our requirements?
symfony/dependency-injection
✅
For a couple of years, the Symplify uses own Symfony Kernel wrapper package - the symplify/symplify-kernel
. It abstracts repeated methods and eases testing. What better place we could use for adding a dependency container factory?
1. Install Symplify Kernel
composer require symplify/symplify-kernel
2. Extend AbstractSymplifyKernel
and provide config files
This the full kernel for the EasyCI package looks like:
namespace Symplify\EasyCI\Kernel;
use Psr\Container\ContainerInterface;
use Symplify\Astral\ValueObject\AstralConfig;
use Symplify\ComposerJsonManipulator\ValueObject\ComposerJsonManipulatorConfig;
use Symplify\SymplifyKernel\HttpKernel\AbstractSymplifyKernel;
final class EasyCIKernel extends AbstractSymplifyKernel
{
/**
* @param string[] $configFiles
*/
public function createFromConfigs(array $configFiles): ContainerInterface
{
$configFiles[] = __DIR__ . '/../../config/config.php';
$configFiles[] = ComposerJsonManipulatorConfig::FILE_PATH;
$configFiles[] = AstralConfig::FILE_PATH;
return $this->create([], [], $configFiles);
}
}
3. Boot Kernel in your bin file and enjoy the Symfony DI
$easyCIKernel = new EasyCIKernel();
$easyCIKernel->createFromConfigs([__DIR__ . '/config/config.php']);
$container = $easyCIKernel->getContainer();
/** @var Application $application */
$application = $container->get(Application::class);
exit($application->run());
That's it! In the end, the setup is very simple.
It allowed us to drop the following files from 4 Simplify CLI packages:
Less code to transfer, faster CI pipelines, and environment-friendly code.
What would be even better? If Symfony core would provide a similar factory out of the box. Maybe one day, it will.
Happy coding!
Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors.
I'd really appreciate it!