Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Symfony: Your Next Microframework (SymfonyCon 2015)

6,518 views

Published on

Microservices are a huge trend, and microframeworks are perfect for them: put together just a few files, write some code, and your done!

But Symfony is a big framework, right? Wrong! Symfony can be as small as a single file!

In this talk, we'll learn how to use Symfony as a micro-framework for your next project. Your app will stay small and clear, but without needing to give up the features or third-party bundles that you love. And if the project grows, it can evolve naturally into a full Symfony project.

So yes, Symfony can also be a microframework. Tell the world!

Published in: Technology
  • Slide 41+42: How do you managed to put full stack framework into 52 lines of code in 2 files? What about those thousands of lines in /vendor, which have to be processed as well on each request?
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Silex can evolve to a full stack framework https://github.com/saxulum/saxulum
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Symfony: Your Next Microframework (SymfonyCon 2015)

  1. 1. Symfony: Your Next Microframework by your friend: Ryan Weaver @weaverryan by your friend: Ryan Weaver @weaverryan
  2. 2. KnpUniversity.com github.com/weaverryan Who is this guy? > Lead for the Symfony documentation
 > KnpLabs US - Symfony Consulting, training & general Kumbaya > Writer for KnpUniversity.com Tutorials > Husband of the much more talented @leannapelham
  3. 3. Thinking about 2 Problems @weaverryan
  4. 4. Problem 1: Symfony Sucks @weaverryan
  5. 5. @weaverryan
  6. 6. @weaverryan
  7. 7. @weaverryan
  8. 8. @weaverryan
  9. 9. Symfony is too hard @weaverryan
  10. 10. The Symfony Framework is too hard @weaverryan The components are not usually the problem
  11. 11. Why? @weaverryan
  12. 12. Route Controller Response @weaverryan WTF? Useful Objects
  13. 13. 1) Common tasks require
 too much code @weaverryan
  14. 14. 2) Symfony is too big @weaverryan
  15. 15. Too many files == A Perceived Complexity @weaverryan
  16. 16. @weaverryan ~ 25 files ~ 10 directories for Hello World
  17. 17. Problem 2: Is my project macro or micro? @weaverryan
  18. 18. Macro => Use Symfony @weaverryan
  19. 19. Micro => Use Silex @weaverryan
  20. 20. The fact we have this option is incredible but… @weaverryan
  21. 21. Silex has a slightly different tech stack @weaverryan
  22. 22. Silex doesn’t have bundles @weaverryan
  23. 23. Silex can’t evolve to a full stack Symfony App @weaverryan
  24. 24. What if we just made Symfony smaller? @weaverryan
  25. 25. 6 files 62 lines of code
  26. 26. <?php
 
 use SymfonyComponentHttpKernelKernel;
 use SymfonyComponentConfigLoaderLoaderInterface;
 
 class AppKernel extends Kernel
 {
 public function registerBundles()
 {
 return array(
 new SymfonyBundleFrameworkBundleFrameworkBundle(),
 new SymfonyBundleTwigBundleTwigBundle(),
 );
 }
 
 public function registerContainerConfiguration($loader)
 {
 $loader->load(
 __DIR__.'/config/config_'.$this->getEnvironment().'.yml'
 );
 }
 }
  27. 27. How small can we go? @weaverryan
  28. 28. What is a Symfony Application? @weaverryan
  29. 29. What is a Symfony App? @weaverryan 1.A set of bundles 2.A container of services 3.Routes
  30. 30. Let’s create a new Symfony project from nothing @weaverryan
  31. 31. {
 "require": {
 "symfony/symfony": "^2.8"
 }
 } @weaverryan composer.json
  32. 32. <?php
 
 use SymfonyComponentConfigLoaderLoaderInterface;
 use SymfonyComponentHttpKernelKernel;
 
 require __DIR__.'/vendor/autoload.php';
 
 class AppKernel extends Kernel
 {
 public function registerBundles()
 {
 }
 
 public function registerContainerConfiguration($loader)
 {
 }
 }
 index.php
  33. 33. <?php
 
 use SymfonyBundleFrameworkBundleKernelMicroKernelTrait;
 use SymfonyComponentDependencyInjectionContainerBuilder;
 use SymfonyComponentRoutingRouteCollectionBuilder; // ...
 
 class AppKernel extends Kernel
 {
 use MicroKernelTrait;
 
 public function registerBundles()
 {
 }
 
 protected function configureRoutes(RouteCollectionBuilder $routes)
 {
 }
 
 protected function configureContainer(ContainerBuilder $c, $loader)
 {
 }
 }
 index.php 1) A set of bundles 2) Routes 3) A container of services
  34. 34. public function registerBundles()
 {
 return array(
 new SymfonyBundleFrameworkBundleFrameworkBundle()
 );
 } AppKernel
  35. 35. protected function configureContainer(ContainerBuilder $c, $loader)
 {
 $c->loadFromExtension('framework', array(
 'secret' => 'S0ME_SECRET',
 ));
 } AppKernel // config.yml framework:
 secret: S0ME_SECRET
  36. 36. protected function configureRoutes(RouteCollectionBuilder $routes)
 {
 $routes->add('/random/{limit}', 'kernel:randomAction');
 } AppKernel New in 2.8! service:methodName (Symfony’s controller as a service syntax)
  37. 37. public function randomAction($limit)
 {
 return new JsonResponse(array(
 'number' => rand(0, $limit)
 ));
 } AppKernel
  38. 38. <?php
 
 // ...
 
 require __DIR__.'/vendor/autoload.php';
 
 
 class AppKernel extends Kernel
 {
 // ...
 }
 
 $kernel = new AppKernel('dev', true);
 $request = Request::createFromGlobals();
 $response = $kernel->handle($request);
 $response->send();
 $kernel->terminate($request, $response);
 index.php
  39. 39. How many files? @weaverryan How many lines of code?
  40. 40. 2 files 52 lines of code
  41. 41. This is a full stack framework @weaverryan
  42. 42. @weaverryan 1. Service Container 2. Routing 3. Events 4. ESI & Sub-Requests 5. Compatible with 3rd party bundles
  43. 43. Fast as Hell @weaverryan
  44. 44. The goal is not to create single-file apps @weaverryan
  45. 45. Clarity & Control @weaverryan
  46. 46. Building a Realistic App @weaverryan github.com/weaverryan/docs-micro_kernel
  47. 47. Requirements: @weaverryan 1. Add some organization 2. Load annotation routes 3. Web Debug Toolbar + Profiler 4. Twig
  48. 48. Reorganize class AppKernel extends Kernel
 {
 }
 // web/index.php
 $kernel = new AppKernel('dev', true);
 $request = Request::createFromGlobals();
 $response = $kernel->handle($request);
 $response->send();
  49. 49. public function registerBundles()
 {
 $bundles = array(
 new FrameworkBundle(),
 new TwigBundle(),
 new SensioFrameworkExtraBundle()
 );
 
 if ($this->getEnvironment() == 'dev') {
 $bundles[] = new WebProfilerBundle();
 }
 
 return $bundles;
 } app/AppKernel.php
  50. 50. protected function configureContainer(ContainerBuilder $c, $loader)
 {
 $loader->load(__DIR__.'/config/config.yml');
 
 if (isset($this->bundles['WebProfilerBundle'])) {
 $c->loadFromExtension('web_profiler', array(
 'toolbar' => true,
 'intercept_redirects' => false,
 ));
 }
 } app/AppKernel.php @weaverryan
  51. 51. app/config/config.yml framework:
 secret: S0ME_SECRET
 templating:
 engines: ['twig']
 profiler: { only_exceptions: false } @weaverryan
  52. 52. protected function configureContainer(ContainerBuilder $c, $loader)
 {
 $loader->load(__DIR__.'/config/config.yml');
 
 if (isset($this->bundles['WebProfilerBundle'])) {
 $c->loadFromExtension('web_profiler', array(
 'toolbar' => true,
 'intercept_redirects' => false,
 ));
 }
 } app/AppKernel.php @weaverryan Goodbye config_dev.yml
  53. 53. protected function configureRoutes(RouteCollectionBuilder $routes)
 {
 if (isset($this->bundles['WebProfilerBundle'])) {
 $routes->import(
 '@WebProfilerBundle/Resources/config/routing/wdt.xml',
 '_wdt'
 );
 $routes->import(
 '@WebProfilerBundle/Resources/config/routing/profiler.xml',
 '/_profiler'
 );
 }
 
 $routes->import(__DIR__.'/../src/App/Controller/', '/', 'annotation')
 } app/AppKernel.php Goodbye routing_dev.yml
  54. 54. Clarity & Control @weaverryan
  55. 55. @weaverryan protected function configureContainer(ContainerBuilder $c, $loader)
 {
 $loader->load(__DIR__ . '/config/config.yml');
 
 $c->setParameter('secret', getenv('SECRET'));
 $c->loadFromExtension('doctrine', [
 'dbal' => [
 'driver' => 'pdo_mysql',
 'host' => getenv('DATABASE_HOST'),
 'user' => getenv('DATABASE_USER'),
 'password' => getenv('DATABASE_PASS'),
 ]
 ]);
 // ...
 } Environment Variables
  56. 56. @weaverryan protected function configureContainer(ContainerBuilder $c, $loader)
 {
 $loader->load(__DIR__.'/config/config.yml');
 
 if (in_array($this->getEnvironment(), ['dev', 'test'])) {
 
 $c->loadFromExtension('framework', [
 'profiler' => ['only_exceptions' => false]
 ]);
 
 }
 
 // ...
 } Environment Control
  57. 57. @weaverryan Build Services protected function configureContainer(ContainerBuilder $c, $loader)
 {
 // ...
 
 $c->register('santa.controller', SantaController::class)
 ->setAutowired(true);
 
 }
  58. 58. @weaverryan Build Routes protected function configureRoutes(RouteCollectionBuilder $routes)
 {
 // ...
 $routes->add('/santa', 'AppBundle:Santa:northPole');
 
 $routes->add(‘/naughty-list/{page}’, 'AppBundle:Santa:list')
 ->setRequirement('list', 'd+')
 ->setDefault('page', 1);
 }
  59. 59. @weaverryan Bundless Applications?
  60. 60. @weaverryan Wait, what does a bundle even give me?
  61. 61. A bundle gives you: @weaverryan 1. Services 2. A resource root (e.g. path to load templates) 3. Magic functionality (e.g. commands) 4. Shortcuts (_controller, AppBundle:User)
  62. 62. @weaverryan 1) Services protected function configureContainer(ContainerBuilder $c, $loader)
 {
 // ...
 
 $c->register('santa.controller', SantaController::class)
 ->setAutowired(true);
 
 }
  63. 63. @weaverryan 2) Resource Root
  64. 64. 2) Resource Root protected function configureContainer(ContainerBuilder $c, $loader)
 {
 // ...
 
 $c->loadFromExtension('twig', [
 'paths' => [__DIR__.'/Resources/views' => 'north_pole']
 ]);
 } public function randomAction($limit)
 {
 $number = rand(0, $limit);
 
 return $this->render(‘@north_pole/micro/random.html.twig’, [
 'number' => $number
 ]);
 }
  65. 65. @weaverryan 3) Magic Functionality 1. Register commands as services 2. Configure Doctrine mappings to load your Entity directory
  66. 66. @weaverryan 4) Shortcuts santa:
 controller: AppBundle:Santa:xmas
 controller: AppBundleControllerSantaController::xmasAction $em->getRepository('AppBundle:App');
 $em->getRepository('AppBundleEntityApp');
  67. 67. @weaverryan One New Trick protected function configureRoutes(RouteCollectionBuilder $routes)
 {
 $routes->import(__DIR__.’@AppBundle/Controller/‘, '/', 'annotation')
 } protected function configureRoutes(RouteCollectionBuilder $routes)
 {
 $routes->import(__DIR__.'/../src/App/Controller/', '/', 'annotation')
 }
  68. 68. Multiple Kernels? @weaverryan
  69. 69. Multiple kernels, why? @weaverryan 1. micro service architecture in monolithic repository 2. performance (less routes, services & listeners)
  70. 70. Multiple kernels was always possible @weaverryan
  71. 71. Now they’re obvious @weaverryan
  72. 72. // app/ApiKernel.php class ApiKernel extends Kernel
 {
 use MicroKernelTrait;
 
 public function registerBundles()
 {
 $bundles = array(
 new FrameworkBundle(),
 new SensioFrameworkExtraBundle()
 );
 
 return $bundles;
 }
 } No TwigBundle
  73. 73. class ApiKernel extends Kernel
 {
 // ...
 
 protected function configureContainer($c, $loader)
 {
 $loader->load(__DIR__.'/config/config.yml');
 $loader->load(__DIR__.'/config/api.yml');
 }
 } Use PHP logic to load share config, and custom config
  74. 74. class ApiKernel extends Kernel
 {
 // ...
 
 protected function configureRoutes($routes)
 {
 $routes->import(
 __DIR__.'/../src/Api/Controller/',
 '/api',
 'annotation'
 );
 }
 
 public function getCacheDir()
 {
 return __DIR__.’/../var/cache/api/' .$this->getEnvironment();
 }
 } Load different routes cacheDir ~= the cache key
  75. 75. Boot the correct kernel however you want @weaverryan
  76. 76. // web/index.php use SymfonyComponentHttpFoundationRequest;
 
 require __DIR__.'/../app/autoload.php';
 
 $request = Request::createFromGlobals();
 
 if (strpos($request->getPathInfo(), '/api') === 0) {
 require __DIR__.'/../app/ApiKernel.php';
 $kernel = new ApiKernel('dev', true);
 } else {
 require __DIR__.'/../app/WebKernel.php';
 $kernel = new WebKernel('dev', true);
 }
 
 $response = $kernel->handle($request);
 $response->send();

  77. 77. But how does it work? @weaverryan
  78. 78. There is one person who *hates* the name MicroKernelTrait @weaverryan
  79. 79. @weaverryan
  80. 80. @weaverryan
  81. 81. trait MicroKernelTrait
 {
 abstract protected function configureRoutes(RouteCollectionBuilder $routes);
 abstract protected function configureContainer(ContainerBuilder $c, $loader);
 
 public function registerContainerConfiguration($loader)
 {
 $loader->load(function ($container) use ($loader) {
 $container->loadFromExtension('framework', array(
 'router' => array(
 'resource' => 'kernel:loadRoutes',
 'type' => 'service',
 ),
 ));
 
 $this->configureContainer($container, $loader);
 });
 }
 
 public function loadRoutes(LoaderInterface $loader)
 {
 $routes = new RouteCollectionBuilder($loader);
 $this->configureRoutes($routes);
 
 return $routes->build();
 }
 } Closure Loader New service route loader
  82. 82. So what now? @weaverryan
  83. 83. I have a big project… @weaverryan Use it for clarity
  84. 84. I’m teaching @weaverryan Show it for simplicity
  85. 85. I have a small app @weaverryan Show it for power
  86. 86. @weaverryan PHP & Symfony Video Tutorials KnpUniversity.com Thank You!

×