nyholm / sunflower
The best Kernel for simple applications.
Fund package maintenance!
Nyholm
Installs: 21 806
Dependents: 2
Suggesters: 0
Security: 0
Stars: 17
Watchers: 3
Forks: 1
Open Issues: 0
Requires
- php: >=7.4
- symfony/config: ^5.3 || ^6.0
- symfony/dependency-injection: ^5.3 || ^6.0
Requires (Dev)
- happyr/service-mocking: ^0.2.0
- phpunit/phpunit: ^9.5
- symfony/http-kernel: ^5.3 || ^6.0
- symfony/yaml: ^5.3
README
Sunflower is a super small application kernel that is used to build a dependency injection container. This kernel is useful for microservices and applications that dont use HTTP. Say; reading from a queue or application invoked by AWS Lambda.
With this kernel you can use normal Symfony service definition with auto wiring and all. It even supports Symfony bundles!
The main difference from using symfony/http-kernel
and Symfony FrameworkBundle
is that Sunflower does not use symfony/event-dispatcher
, symfony/console
,
symfony/security
, symfony/cache
and symfony/router
.
Performance
Below is a table of requests per second using a "hello world" application with different frameworks. The exact numbers are not relevant, they depend on the machine the tests was running on. But one should consider how the numbers change between frameworks since all test ran on the same machine.
Using a "hello world" comparison has some drawbacks. It does show performance for small applications with only a few hundreds lines of code, but it does not tell how large applications preform. It also does not give you any indication how fast you can write and maintain your application.
The table above is interesting if you are planning to build a small microservice that are similar to "hello world". Using the Sunflower Kernel is also very interesting if you are familiar with Symfony dependency injections, config and third party bundles.
Install
composer require nyholm/sunflower
Use
// src/Kernel.php namespace App; use Nyholm\SunflowerKernel; class Kernel extends SunflowerKernel { /** * Optionally override the configureContainer() */ protected function configureContainer(ContainerConfigurator $container): void { $container->import('../config/{packages}/*.yaml'); $container->import('../config/{packages}/'.$this->environment.'/*.yaml'); $container->import('../config/{packages}/'.$this->environment.'/*.php'); if (\is_file(\dirname(__DIR__).'/config/services.yaml')) { $container->import('../config/services.yaml'); $container->import('../config/{services}_'.$this->environment.'.yaml'); } else { $container->import('../config/{services}.php'); } } }
use App\Kernel; use App\Service\MyService; require_once dirname(__DIR__).'/vendor/autoload.php'; $kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); $kernel->getContainer()->get(MyService::class)->run();
Use with HTTP
A short example using HTTP and a simple switch-router. This example is using runtime/psr-nyholm.
// public/index.php use Nyholm\Psr7; require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return function (array $context) { $kernel = new \App\Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); $container = $kernel->getContainer(); // This is an example router $urlPath = $context['REQUEST_URI']; switch ($urlPath) { case '/': case '': // This is an RequestHandlerInterface return $container->get(\App\Controller\Startpage::class); case '/foobar': return $container->get(\App\Controller\Foobar::class); default: return new Psr7\Response(404, [], 'The route does not exist'); } };
# config/services.yaml services: _defaults: autowire: true autoconfigure: true _instanceof: Psr\Http\Server\RequestHandlerInterface: public: true App\: resource: '../src/' exclude: - '../src/Kernel.php'
Use with Bref
To create apps that works with Bref you will need the runtime/bref package. Create microservices, SQS readers or react to S3 events etc.
// src/Kernel.php namespace App; use Nyholm\SunflowerKernel; class Kernel extends SunflowerKernel { public function isLambda(): bool { return false !== \getenv('LAMBDA_TASK_ROOT'); } public function getCacheDir(): string { if ($this->isLambda()) { return '/tmp/cache/'.$this->environment; } return parent::getCacheDir(); } public function getLogDir(): string { if ($this->isLambda()) { return '/tmp/log/'; } return parent::getLogDir(); } public function getProjectDir(): string { return \dirname(__DIR__); } }
// bin/container.php use App\Kernel; require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return function (array $context) { $kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); return $kernel->getContainer(); };
# config/services.yaml services: _defaults: autowire: true autoconfigure: true _instanceof: Bref\Event\Handler: public: true App\: resource: '../src/' exclude: - '../src/Kernel.php'
# serverless.yml functions: app: handler: bin/container.php:App\Service\MyHandler
Use with CLI
// bin/console #!/usr/bin/env php <?php use App\Kernel; use Symfony\Component\Console\Application; require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return function (array $context) { $kernel = new Kernel($context['APP_ENV'], (bool)$context['APP_DEBUG']); $container = $kernel->getContainer(); $app = new Application(); // Register all your commands here $app->add($container->get(\App\Command\HelloCommand::class)); return $app; };
declare(strict_types=1); namespace App\Command; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; #[AsCommand(name: 'app:debug:hello')] class HelloCommand extends Command { protected function configure() { $this->setDescription('Test print hello.'); } protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Hello'); return Command::SUCCESS; } }
# config/services.yaml services: _defaults: autowire: true autoconfigure: true _instanceof: Symfony\Component\Console\Command\Command: public: true App\: resource: '../src/' exclude: - '../src/Kernel.php'
History
The Sunflower project was open sourced in 2021. The very first version of the project was created back in 2015. A few private applications was created around the concept of using Symfony's Dependency Injection component but not use the FrameworkBundle or HttpKernel.
The first public version of the project was SuperSlim. That version was a opinionated framework to show what the FrameworkBundle actually did for you behind the scenes. With some more private iterations and many more applications created, we finally removed all unnecessary things and ended up with just the one Kernel.