#SPUG - Legacy applications

692
-1

Published on

Presentation prepared especially for #4 SPUG meeting in Gliwice describes integration an old code, spaghetti code with Symfony2 framework. Two really different engines in one application.

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

  • Be the first to like this

No Downloads
Views
Total Views
692
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

#SPUG - Legacy applications

  1. 1. Legacy applicationsPiotr Pasich @
  2. 2. więc...przychodzi klient do lekarzaZACZNIJMY OD KLIENTAPiotr Pasich @
  3. 3. żeby działałona wczorajewentualnie ASAPCZEGO OCZEKUJE?Piotr Pasich @
  4. 4. Ile to będzie trwało?9 miesięcyPRZEPISZMY TO!Piotr Pasich @
  5. 5. Piotr Pasich @
  6. 6. Ile to będzie trwało?2 miesiąceNADPISZMY TO!Piotr Pasich @
  7. 7. FROM SPAGHETTI TO CODE
  8. 8. PREVENTING REGRESSIONSczyli nic nie ruszaćPiotr Pasich @
  9. 9. TESTY FUNKCJONALNOŚCISelenium IDE, behat, testy jednostkowePiotr Pasich @
  10. 10. ŚRODOWISKOminimum PHP 5.3.3Sqlite3, JSON, ctypephp app/check.phpdate.timezone set in php.iniPhpcs CodeSniffstutaj po raz pierwszy korzystamy z testówPiotr Pasich @
  11. 11. INSTALACJA SYMFONY 2katalog legacynamespacenamespace Legacy {(...)}Piotr Pasich @
  12. 12. LegacyBundleapp/console generate:bundleBundle namespace: XsolveLegacyBundlePiotr Pasich @
  13. 13. AUTOLOADER<?phpnamespace XsolveLegacyBundle;require_once(__DIR__ . "/../../../legacy/index.php"); //disabled execute::runuse Legacy;use SymfonyComponentHttpKernelBundleBundle;use SymfonyComponentDependencyInjectionContainerBuilder;class XsolveLegacyBundle extends Bundle{public function build(ContainerBuilder $container){spl_autoload_register(array(Kohana, auto_load));}}Piotr Pasich @
  14. 14. MainActionclass LegacyController extends Controller{/*** @Route("/", name="main_page")* @Route("/{filename}.html", name="proxy_html", requirements={"filename" = ".+"})* @Route("/{filename}", name="proxy", requirements={"filename" = ".+"})*/public function indexAction($filename=index){$_SERVER[SCRIPT_URL] = $filename..html;$_SERVER[REQUEST_URI] = $filename..html;ob_start();// include_once (../legacy/index.php);Event::run(system.routing);Benchmark::stop(SYSTEM_BENCHMARK._system_initialization);Event::run(system.execute);$response = new Response(ob_get_clean());return $response;}}Piotr Pasich @
  15. 15. KOHANA?system/core/Bootstrap.php//Event::run(system.routing);//Benchmark::stop(SYSTEM_BENCHMARK._system_initialization);//Event::run(system.shutdown);DIE ( ); //!Piotr Pasich @
  16. 16. LAYOUTesiVarnishGuzzle ClientCrawlerPiotr Pasich @
  17. 17. ESI + VARNISHhttp://todsul.com/symfony2-esi-varnishframework: { esi: true }Piotr Pasich @
  18. 18. ESI CONTROLLER/*** @Route(name="esi_center_column")*/public function getCenterColumnAction(Request $request){$url = $request->get(url); //almost like proxy.php$html = $this->get(xsolve.legacy.client)->requestElement($url, .span-center);return $this->get(xsolve.response.cache)->getResponseWithCache($html, 10);}Piotr Pasich @
  19. 19. ESI SERVICEclass LegacyClient{(...)public function requestElement($url, $element){$html = $this->request($url);return $this->filter($html, $element);}(...)Piotr Pasich @
  20. 20. ESI SERVICE/*** @return SymfonyComponentDomCrawlerCrawler*/public function request($url){if (!isset($this->response[$url])) {$client = $this->getClient();$request = $client->get($url);$request->setHeader(Cookie, null);$this->response[$url] = $request->send();}return $this->response[$url]->getBody();}Piotr Pasich @
  21. 21. ESI SERVICEpublic function filter($html, $element){$crawler = new Crawler();$crawler->addHtmlContent($html);$crawler = $crawler->filter($element);$html = ;foreach ($crawler as $domElement) {$html.= $domElement->ownerDocument->saveHTML($domElement);}return $html;}Piotr Pasich @
  22. 22. REVERSE PROXY CACHE// app/AppCache.phprequire_once __DIR__./AppKernel.php;use SymfonyBundleFrameworkBundleHttpCacheHttpCache;class AppCache extends HttpCache{protected function getOptions(){return array(debug => false,default_ttl => 0,private_headers => array(Authorization, Cookie),allow_reload => true,allow_revalidate => false,stale_while_revalidate => 2,stale_if_error => 60,);}}Piotr Pasich @
  23. 23. REVERSE PROXY CACHE<?php// web/app.phprequire_once __DIR__./../app/bootstrap.php.cache;require_once __DIR__./../app/AppKernel.php;require_once __DIR__./../app/AppCache.php;use SymfonyComponentHttpFoundationRequest;$kernel = new AppKernel(prod, false);$kernel->loadClassCache();// wrap the default AppKernel with the AppCache one$kernel = new AppCache($kernel);$request = Request::createFromGlobals();$response = $kernel->handle($request);$response->send();$kernel->terminate($request, $response);Piotr Pasich @
  24. 24. REVERSE PROXY CACHEclass ResponseCache {public function getResponseWithCache($html, $cacheTime=1){$response = new Response($html);$response->setMaxAge($cacheTime);$response->setSharedMaxAge($cacheTime);$date = new DateTime();$date->modify("+$cacheTime seconds");$response->setExpires($date);return $response;}}Piotr Pasich @
  25. 25. HOW TO USE IT?<!DOCTYPE html><html><esi:include src="{{ path(esi_head) }}" /><body><div class="container with-background"><esi:include src="{{ path(esi_left_column) }}" /><div class="span-center">{% block content %}{% endblock %}</div><esi:include src="{{ path(esi_right_column) }}" /><esi:include src="{{ path(esi_footer) }}" /></div></body></html>Piotr Pasich @
  26. 26. RENDER{% render url(latest_news, { max: 5 }) with {}, {standalone: true} %}Piotr Pasich @
  27. 27. SESSIONGdzie jest problem?_s2_(...)Piotr Pasich @
  28. 28. SESSIONclass RequestListener {public function onKernelRequest(GetResponseEvent $event) {$bags = array(total_hits,_kf_flash_,user_agent,last_activity,search.criteria,category.name,auth_user);foreach ($bags as $namespace) {$bag = new AttributeBag($namespace, .);$bag->setName($namespace);$this->session->registerBag($bag);}}}Piotr Pasich @
  29. 29. REQUEST LISTENER<?xml version="1.0" ?><container xmlns="http://symfony.com/schema/dic/services"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.><services><service id="xsolve.legacy.listener.request" class="XsolveLegacyBundleRequestListener"><tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest"/><argument type="service" id="session" /></service></services></container>Piotr Pasich @
  30. 30. NIE DZIAŁA!DLACZEGO?Piotr Pasich @
  31. 31. BO KOHANA!class Session_Core {// (...)public function create($vars = NULL){$this->destroy();// (...)}}Piotr Pasich @
  32. 32. TERAZ DZIAŁAclass Session_Core {// (...)public function create($vars = NULL){//$this->destroy();// (...)}}Piotr Pasich @
  33. 33. TERAZ NIE DZIAŁAPiotr Pasich @
  34. 34. TERAZ DZIAŁAclass Session_Core {// (...)public function create($vars = NULL){// Destroy any current sessionsself::$createCall = self::$createCall+1;if (self::$createCall > 10){$_SESSION = array();}// this->destroy();// (...)}}Piotr Pasich @
  35. 35. BAZA DANYCHponad 100 tabel = 100 encjibrak odpowiednich relacjibrak pełnej zgodności z wymogami Doctrine 2Piotr Pasich @
  36. 36. BAZA DANYCHapp/console doctrine:mapping:import XsolveLegacyBundle anotationPiotr Pasich @
  37. 37. KONFLIKTY I BŁĘDYnaprawiamy ręcznie :(Piotr Pasich @
  38. 38. PRZEPISUJEMY/*** @Route("/{categoryName}.html")*/public function indexAction($categoryName){$criterias = array( category => $categoryName );$offers = $this->get(legacy.offers)->getRandomOffers($criterias);$view = $this->renderView(XsolveOfferBundle:Default:index.html.twig, array(offers => $offers));return $this->get(legacy.response.cache)->getResponseWithCache($view, 2);}Piotr Pasich @
  39. 39. PRZEPISUJEMY{% extends XsolveLegacyBundle:Legacy:layout.html.twig %}{% block content %}<div class="boxer_main anons_set">{% for offer in offers %}<div class="anons"><a href="{{ path(legacy_offers_view , {id: offer.id, city: offer.city.name|makeUri, district: offer.district.name|makeUri,slug: offer.name|makeUri}) }}" target="_blank"><img src="{{ STATIC_URL }}thumbs/gallery/{{ offer.galleryId }}/{{ offer.getRandomPhotoName() }}.128x128" alt=""{#popup_text offer=$offer criteria=$criteria#} /></a><br />{% if offer.isRealphoto %}<a style="display : inline-block;" class="sprite sprite-ptaszek"href="{{ path(legacy_offers_view , {id: offer.id, city: offer.city.name|makeUri, district: offer.district.name|makeUri, slug: offer.name|makeUri}) }}" ></a>{% endif %}<a title="{{ offer.name }}" href="{{ path(legacy_offers_view , {id: offer.id, city: offer.city.name|makeUri, district: offer.district.name|makeUri, slug: offer.name|makeUri}) }}" target="_blank">{{ offer.name }}</a></div>{% endfor %}<div class="clear"></div></div>{% endblock %}Piotr Pasich @
  40. 40. I TO DZIAŁAPiotr Pasich @piotr.pasich@xsolve.plwww.xsolve.plwww.xlab.pl
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×