Upgrade to Pro — share decks privately, control downloads, hide ads and more …

From Legacy to Symfony @ SymfonyCon Cluj 2017

From Legacy to Symfony @ SymfonyCon Cluj 2017

How can one use Symfony 4.0 and PHP 7.2 while working with a legacy application? Starting new projects on latest technology is a no-brainer but how often are you in this position? Most of the time it feels like you're stuck with what you have.

In this talk I'll share my experience migrating a legacy monolith from 2007 (powering a high traffic social network) and an API-centric web application (running an e-commerce marketplace) to Symfony.

Although the talk will be largely technical I'll also share some insights on ROI, non-technical benefits and pitching the idea to your boss.

9a328142924c93e5e148c75356ba6d42?s=128

Sebastian Grodzicki

November 16, 2017
Tweet

More Decks by Sebastian Grodzicki

Other Decks in Programming

Transcript

  1. From Legacy to Symfony Sebastian Grodzicki !@sebgrodzicki

  2. How can one use Symfony 4.0 and PHP 7.2
 while

    working with a legacy application?
  3. phpinfo() Sebastian Grodzicki
 • CTO at SHOWROOM • former dev

    & CTO at GoldenLine • SensioLabs Certified
 Symfony Developer (Expert) • PHP developer for 15+ years !@sebgrodzicki
  4. GoldenLine • business social network founded in 2005 • in-house

    framework (“Xplod”) • LAMP stack • monolith
  5. None
  6. GoldenLine Developer on-boarding
 took almost 6 months

  7. GoldenLine No documentation

  8. GoldenLine No tests

  9. GoldenLine Complexity

  10. “The single worst strategic mistake that any software company can

    make: rewrite the code from scratch.”
  11. GoldenLine @ 2012 Composer

  12. GoldenLine @ 2012 #1 Symfony component
 Console

  13. GoldenLine @ 2012 Symfony 2.1

  14. Proof of Concept

  15. @LegacyIfRoleNot

  16. Routing self::$map = [ [ 'uri' => '^/oauth/authorize/?(.+)?$', 'action' =>

    'oauth/authorize', ], [ 'uri' => '^/m$', 'action' => 'mobile/touch', ], // 746 more like this ];
  17. Routing public function convert(array $rule) { $route = new Route('/{uri}');

    $route->setRequirement('uri', $this->getRequirement($rule['uri'])); return $route; }
  18. Routing private function addRoute( RouteCollection $collection, array $rule, $controller =

    'legacy.web_dispatching_controller:dispatchAction' ) { $name = sprintf( '__legacy_route_%s_%s_%s', (isset($rule['requireSsl']) ? strtolower($rule['requireSsl']) : 1), (isset($rule['method']) ? strtolower($rule['method']) : ''), md5($rule['uri']) ); $route = $this->converter->convert($rule); $route->setDefault('_controller', $controller); $collection->add($name, $route); }
  19. @LegacyIfRoleNot class AutocompleterController extends Controller { const DEFAULT_LIMIT = 15;

    /** * @LegacyIfRoleNot("ROLE_SYMFONY") * * @param Request $request * @throws AccessDeniedException * @return array */ public function getAction(Request $request) { $this->denyAccessUnlessGranted('ROLE_USER'); $query = $request->query->get('query'); $limit = $request->query->get('limit') ?: self::DEFAULT_LIMIT;
  20. Feature flags

  21. Boostrap

  22. Internet Explorer 6

  23. @LegacyIfOldBrowser

  24. @LegacyIfOldBrowser class AuthenticatedController extends Controller { /** * @LegacyIfRoleNot({"IS_ANONYMOUS","ROLE_SYMFONY"}) *

    @LegacyIfOldBrowser */ public function indexAction(Request $request) { $this->denyAccessUnlessGranted('IS_AUTHENTICATED_ANONYMOUSLY'); /** @var $user User */ $user = $this->getUser(); if (!$user) { return $this->forward('HomepageBundle:Anonymous:index'); }
  25. GET vs POST GET /foobar

  26. GET vs POST POST /foobar

  27. CSRF <?php namespace GoldenLine\LegacyBundle\Form\Extension\Csrf\CsrfProvider; use GoldenLine\UserBundle\Model\User; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; class SecretCsrfProvider

    implements CsrfProviderInterface { /** * @inheritdoc */ public function generateCsrfToken($intention) {
  28. CSRF public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults( [ 'csrf_field_name' =>

    'csrf', 'csrf_provider' => $this->csrfProvider, ] ); }
  29. GoldenLine Developer on-boarding took almost 6 months

  30. GoldenLine No documentation

  31. GoldenLine No tests

  32. GoldenLine Complexity

  33. Symfony Awards 2013 Best Social Website

  34. Symfony Awards 2013 Best Symfony Website

  35. SHOWROOM

  36. SHOWROOM • online fashion marketplace founded in 2012 • built

    on Slim micro framework & Phalcon • API-centric
  37. SHOWROOM Locked on
 Phalcon 2 & PHP 5.6

  38. No documentation SHOWROOM

  39. No tests SHOWROOM

  40. Frustration SHOWROOM

  41. • API v2 based on Symfony 3 • store front

    v2 (Symfony 3) • SHOWROOM SDK for PHP SHOWROOM @ 2016
  42. PSQL 5432 PHP 5.6 9056 NGINX
 80/443 Varnish 6081 NGINX

    8080 • SSL Termination • Access logs • Redirect HTTP to HTTPS • HTTP Cache • Load balancer • Edge Side Includes (ESI) • PHP-FPM via FastCGI • Virtual Hosts • Access logs • PHP-FPM • PHP 5.6 Elasticsearch
 9200 RabbitMQ
 5672 Redis 6379
  43. PSQL 5432 PHP 5.6 9056 NGINX
 80/443 Varnish 6081 NGINX

    8080 • SSL Termination • Access logs • Redirect HTTP to HTTPS • HTTP Cache • Load balancer • Edge Side Includes (ESI) • PHP-FPM via FastCGI • Virtual Hosts • Access logs • PHP-FPM • PHP 5.6 • PHP 7.1 Elasticsearch
 9200 RabbitMQ
 5672 Redis 6379 NGINX 8081 PHP 7.1 9071
  44. Varnish

  45. Varnish backend app1a { .host = "10.0.0.1"; .port = "8080";

    } backend app1b { .host = "10.0.0.2"; .port = "8080"; } backend app1c { .host = "10.0.0.3"; .port = "8080"; } backend app2a { .host = "10.0.0.1"; .port = "8081"; } backend app2b { .host = "10.0.0.2"; .port = "8081"; } backend app2c { .host = "10.0.0.3"; .port = “8081"; }
  46. Varnish sub vcl_init { new v1 = directors.round_robin(); v1.add_backend(app1a); v1.add_backend(app1b);

    v1.add_backend(app1c); new v2 = directors.round_robin(); v2.add_backend(app2a); v2.add_backend(app2b); v2.add_backend(app2c); }
  47. Nginx server { listen 8080; server_name api.shwrm.net; root /home/api_v1/public; location

    / { try_files $uri /index.php$is_args$args; } location ~ ^/index\.php(/|$) { fastcgi_pass 127.0.0.1:9056; include fastcgi_params; internal; } }
  48. server { listen 8081; server_name api.shwrm.net; root /home/api_v2/web; location /

    { try_files $uri /app.php$is_args$args; } location ~ ^/app\.php(/|$) { fastcgi_pass 127.0.0.1:9071; include fastcgi_params; internal; } } Nginx
  49. Varnish sub vcl_recv { if (req.http.Accept == "application/vnd.showroom.v2+json") { set

    req.backend_hint = v2.backend(); } else { set req.backend_hint = v1.backend(); } }
  50. sub vcl_recv { if ( req.url ~ "^/_(wdt|profiler|error|docs)/" || req.url

    ~ "^/assets/" || req.url ~ "^/orders/[0-9]+/refund" ) { set req.backend_hint = v2.backend(); } else { set req.backend_hint = v1.backend(); } } Varnish
  51. PostgreSQL

  52. PSQL 5432 PHP 5.6 9056 NGINX
 80/443 Varnish 6081 NGINX

    8080 Elasticsearch
 9200 RabbitMQ
 5672 Redis 6379 NGINX 8081 PHP 7.1 9071
  53. PostgreSQL Table View

  54. ESI ESI

  55. Microservices HTTP Core API Shipping API

  56. Microservices Core API Analytics

  57. SHOWROOM Locked on
 Phalcon 2 & PHP 5.6

  58. No documentation SHOWROOM

  59. No tests SHOWROOM

  60. Frustration SHOWROOM

  61. Questions? https://joind.in/talk/ddace

  62. Thank you! https://joind.in/talk/ddace