Modernisation of legacy PHP applications using Symfony2 - PHP Northeast Conference 2013

6,064
-1

Published on

PHP and its community has evolved really fast in the last few years to allow for professional architectures and solutions. However, there are thousands of existing PHP applications which have not evolved in the meantime and are now crippled and unmaintainable because of that. These applications represent a real threat to the competitiveness of the business that relies on them.

The best approach in terms of business to solve this problem is progressive rewrite. Symfony2 and its modular architecture make it possible. This talk covers the main technical difficulties of the progressive approach when rewriting legacy PHP applications, and the corresponding solutions, some of which rely on the modularity of Symfony2.

Published in: Technology
0 Comments
11 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
6,064
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
0
Comments
0
Likes
11
Embeds 0
No embeds

No notes for slide

Modernisation of legacy PHP applications using Symfony2 - PHP Northeast Conference 2013

  1. 1. Modernisation of legacy PHP applications using Symfony2 19/03/2013 1 THEODO
  2. 2. The need for progressive rewriteThe technical challenges and our solutionsThe future 19/03/2013 2 THEODO
  3. 3. Legacy PHP applications are everywhere History of PHP applications• 79% : websites written in PHP among the top 1 million, according to W3Techs.com• 1994: PHP was created• 2004: PHP5 was released. Start of OOP• 2007: ZF 1.0.0 and SF 1.0 were released. Start of the MVC age 13 years of (mostly) spagetthi-coded web-apps 19/03/2013 3 THEODO
  4. 4. What is the problem of legacy applications?The need for rewrite 19/03/2013 4 THEODO
  5. 5. Starting a new version from scratch is a danger to the business The dangers of total rewrite• New developments are invisible until the new version is finished• You need twice more developers: one team to maintain the old application, while the second team is writing the new version• The probability of forgetting features during the rewrite is high• Transition when the new version is ready becomes costly and very dangerous: everything is impacted at once 19/03/2013 5 THEODO
  6. 6. More dangerous than you think! The black swan blindnessA study led by two Oxford professors on 1,471 IT projects found out that:• 1 project out of 6 ended up costing on average 3 times as much!• large-scale computer spending were 20 times more likely to spiral out of control than expected. Large-scale examples: • the complete revamp of Levi Strauss’ IT, which cost them 192.5 million$ losses • FoxMeyer Drugs’ bankruptcy after switching brutally to SAPStudy: http://users.ox.ac.uk/~mast2876/WP_2011_08_15.pdfFox-Meyer bankruptcy: http://fr.slideshare.net/jmramireza/the-foxmeyer-drugs-bankruptcy-was-it-a-failure-of-erp-2332065 19/03/2013 6 THEODO
  7. 7. Progressive rewrite is the solution… but is very challenging The challenge of progressive rewrite• You have to work with hard-to-read code• All the aspects of an application in production are concerned:  source code of course  system  data  cache  remote webservices, etc.• The major conceptual challenge in all those aspects is decoupling of modules. Just like in scaling! 19/03/2013 7 THEODO
  8. 8. Progressive rewrite is more profitable Unbeatable time-to-marketFunctionalities Functionalities Evolution Time Time • No new features for months • Only 1 app to maintain • 2 apps to maintain • Lower regression risks • Unbeatable time-to-market 19/03/2013 8 THEODO
  9. 9. The need for progressive rewriteThe technical challenges and our solutionsThe future 19/03/2013 9 THEODO
  10. 10. Theodo Evolution Our solution at Theodo• R&D project started at Theodo in 2012• Aims to solve all these issues to make app rewriting agile again• Our dream: be able to code evolutions on the legacy application without touching the legacy code. 19/03/2013 10 THEODO
  11. 11. The technical experts behind Theodo EvolutionTheodo Evolution team 19/03/2013 11 THEODO
  12. 12. The big picture of the different technical aspects to solve The technical challenges• Preventing regressions• Upgrading the system• Routing• Sharing the layout• Sharing the session/authentication• Decoupling the code• Migrating the model and data 19/03/2013 12 THEODO
  13. 13. Preventing regressions 19/03/2013 13 THEODO
  14. 14. Belt and straps are necessary… and won’t be enough…Preventing regressions 19/03/2013 14 THEODO
  15. 15. Functionally test what could harm your business Preventing regressions• By definition, spaghetti code is deeply coupled. Touching one part breaks something at the other end• Create functional tests on the most critical scenarios• Mink + ZombieJS: https://github.com/Behat/Mink 19/03/2013 15 THEODO
  16. 16. Setup good monitoring Preventing regressionsThe most thorough functional testing is done... by putting in production!• Monitor production well. • Errors 500, 404 • response time with Pingdom • newrelic• Make everybody aware of the heightened risks• Monitor business metrics with StatsD, give the marketing team a Graphite dashboard => get free high-level monitoring! 19/03/2013 16 THEODO
  17. 17. And create a fast and easy deployment pipeline Preventing regressions• Deploy often and small updates. Aim ten a day, not just one a week!• Setup a fast rollback system• Setup a fast deployment system, because you want to make it faster to correct small problems than to rollback 19/03/2013 17 THEODO
  18. 18. Simple automated deployment with Fabric Automated deployment@roles(prod)def deploy(): tag = "%s/%s" % (_getrole(), strftime("%Y/%m-%d-%H-%M-%S")) local(git tag -a %s -m "%s" % (tag, _getrole())) local(git push --tags) run(git fetch’) run(git fetch origin --tags) tag = run(git tag -l %s/* | sort | tail -n1 % _getrole()) run(git checkout + tag) run(php composer.phar install) 19/03/2013 18 THEODO
  19. 19. Upgrading the system19/03/2013 19 THEODO
  20. 20. Migrate to a modern environment that supports Symfony2 …and improves performance as a bonus. Upgrading the system• Symfony2 requires: • PHP 5.3.3 • Sqlite3, JSON, ctype • date.timezone set in php.ini• php app/check.php 19/03/2013 20 THEODO
  21. 21. Check that the legacy code will support PHP 5.3/5.4 Upgrading the system• Phpcs CodeSniffs for 5.3 and 5.4 compatibility: https://github.com/wimg/PHPCompatibility• Setup a pre-production environment in the upgraded environment and run your functional tests on it• And this time provision your environment with Puppet or Chef! Check Blueprint: https://github.com/devstructure/blueprint 19/03/2013 21 THEODO
  22. 22. Routing19/03/2013 22 THEODO
  23. 23. Routing between the old and the new Routing• The beauty of PHP: some legacy apps have simply no routing system!• Host the new app next to the old app, with clear URL differences, and proxy at the server level • Subdomain: v2.myapp.com • Subfolder: www.myapp.com/v2/• My favourite: create a catchall route with Symfony2 19/03/2013 23 THEODO
  24. 24. The CatchAll RouteRouting class LegacyController extends Controller { /** * @Route("/{filename}.php", name="_proxy") */ public function proxyAction($filename) { ob_start(); include $filename . .php; // Replace exit()s and die()s // or you might never end up here return new Response(ob_get_clean()); } } 19/03/2013 24 THEODO
  25. 25. Sharing the layout19/03/2013 25 THEODO
  26. 26. For end-users « progressive rewrite » = « same template » Sharing the layout• Copy-pasting the layout is not a good solution• Our solution : crawl the legacy layout and include it as ESIs <esi:include src="{{ path(legacylayout_top) }}" /> {% block body %}{% endblock %} <esi:include src="{{ path(legacylayout_bottom) }}" /> 19/03/2013 26 THEODO
  27. 27. The « crawling » part of the ESI sub-request Sharing the layout$this->client->setHeader(Cookie, $currentCookie);if ($request->headers->has(authorization)) { $this->client->setHeader( Authorization, $request->headers->get(authorization) );}if ($this->session->isStarted()) { $this->session->save();}$this->client->request(GET, $url);$esiContent = $this->client->getResponse()->getContent(); 19/03/2013 27 THEODO
  28. 28. Sharing the session/authentication 19/03/2013 28 THEODO
  29. 29. Make the legacy session accessible from Symfony2 Sharing the session/authentication• You want to access what your legacy app has put in the session from within Symfony2…• BUT Symfony2 expects session information to be neatly stored in arrays in the « _sf2_attributes » namespace to make your legacy session accessible, you need to : • register « Bags » for each of your legacy $_SESSION keys • create a « ScalarBag » type when these values are not arrays 19/03/2013 29 THEODO
  30. 30. Example for a Symfony1 Session Sharing the session/authentication// onKernelRequestforeach ( array(symfony/user/sfUser/credentials, symfony/user/sfUser/attributes’) as $namespace) { $bag = new NamespacedAttributeBag($namespace, .); $bag->setName($namespace); $session->registerBag($bag);} 19/03/2013 30 THEODO
  31. 31. Decoupling the code19/03/2013 31 THEODO
  32. 32. Attacking the spaghetti problemDecoupling the code 19/03/2013 32 THEODO
  33. 33. Decoupling is a major aspect of progressive rewriteDecoupling the code 19/03/2013 33 THEODO
  34. 34. Decoupling is a major aspect of progressive rewriteDecoupling the code 19/03/2013 34 THEODO
  35. 35. Decoupling is a major aspect of progressive rewriteDecoupling the code 19/03/2013 35 THEODO
  36. 36. Decoupling is a major aspect of progressive rewriteDecoupling the code 19/03/2013 36 THEODO
  37. 37. Decoupling is a major aspect of progressive rewriteDecoupling the code 19/03/2013 37 THEODO
  38. 38. Decoupling is a major aspect of progressive rewriteDecoupling the code 19/03/2013 38 THEODO
  39. 39. The dream architectureDecoupling the code 19/03/2013 39 THEODO
  40. 40. What you usually findDecoupling the code 19/03/2013 40 THEODO
  41. 41. You need to create the dream API and slowly refactor around itDecoupling the code A A P P I A I P I A P I A P I A A P P I I 19/03/2013 41 THEODO
  42. 42. You need to create the dream API and slowly refactor around itDecoupling the code A A P P I A I P I A P I A P I A A P P I I 19/03/2013 42 THEODO
  43. 43. You need to create the dream API and slowly refactor around itDecoupling the code A A P P I A I P I A P I A P I A A P P I I 19/03/2013 43 THEODO
  44. 44. You need to create the dream API and slowly refactor around itDecoupling the code A A P P I A I P I A P I A P I A A P P I I 19/03/2013 44 THEODO
  45. 45. This is called « Facade Pattern » Decoupling the codeWikipediaA facade is an object that provides a simplified interface to a largerbody of code, such as a class library.[…]A façade reduces dependencies of outside code on the innerworkings of a library, since most code uses the facade, thus allowingmore flexibility in developing the system; 19/03/2013 45 THEODO
  46. 46. Symfony2 Service Container Decoupling the code• With Symfony2, the new API of the Facade Pattern is a service• Create a bundle for every « module » you identified.• Create a service for every bundle you now have, that will serve as your Facade API• … and start using these services! 19/03/2013 46 THEODO
  47. 47. Example: Model services Decoupling the codenamespace MyCompanyMyFreshBundleServices;class PostService{ public function getById($postId) { $post = PostTable::strangeLegacyMethodById($postId); return $post; }} 19/03/2013 47 THEODO
  48. 48. Migrating the model and data 19/03/2013 48 THEODO
  49. 49. A typical MySQL database is highly coupledMigrating the model and data 19/03/2013 49 THEODO
  50. 50. Decoupling a relational DB is like attacking a giant monsterMigrating the model and data 19/03/2013 50 THEODO
  51. 51. The classical solution is syncing two versions of the dataMigrating the model and data • Syncing can be done with ETL tools like Kettle or with custom code in the new application • To avoid unbearable headaches, it is highly recommended to write in only one DB. Which means to rewrite parts of the legacy code to save, update and delete in the new DB 19/03/2013 51 THEODO
  52. 52. A « bearable headache » solution:Migrating the model and data Legacy Application New application Routing 19/03/2013 52 THEODO
  53. 53. A « bearable headache » solution:Migrating the model and data Legacy Application New application Routing 19/03/2013 53 THEODO
  54. 54. MongoDB and DoctrineODM make it (a little) easier Migrating the model and data• Remember: progressive rewrite relies on decoupling, just like scaling. Non-relational DBs make partial evolutions easier in the long-term• In a document DB like MongoDB you can have in the same collection data which coexist in different versions.• To migrate them « just in time » use /** @MongoDBPreLoad */ from the Doctrine ODM 19/03/2013 54 THEODO
  55. 55. The need for progressive rewriteThe technical challenges and our solutionsThe future 19/03/2013 55 THEODO
  56. 56. The evolution of Theodo Evolution Theodo Evolution strategy• The Theodo team is working currently on 6 progressive rewrites of huge applications to Symfony2• Every technical solution for progressive rewrite to Symfony2 is bundled in our project Theodo Evolution• The goal: make it an out-of-the-box solution to work on legacy apps without touching legacy code 19/03/2013 56 THEODO
  57. 57. How you can profit from Theodo Evolution? Theodo Evolution strategy• Some Theodo Evolution bundles will be open- sourced.• SessionIntegrationBundle is being polished right now by Pierre-Henri and Marek• Contact us @theodo or fabriceb@theodo.fr 19/03/2013 57 THEODO
  58. 58. Questions ? fabriceb@theodo.fr @theodo www.theodo.frFeedback: https://joind.in/8391 19/03/2013 58 THEODO

×