Legacy applications - 4Developes konferencja, Piotr Pasich

44
LEGACY APPLICATIONS Piotr Pasich

Transcript of Legacy applications - 4Developes konferencja, Piotr Pasich

Page 1: Legacy applications  - 4Developes konferencja, Piotr Pasich

LEGACY APPLICATIONS

Piotr Pasich

Page 2: Legacy applications  - 4Developes konferencja, Piotr Pasich
Page 3: Legacy applications  - 4Developes konferencja, Piotr Pasich

Piotr Pasich @

FROM SPAGHETTI TO CODEczyli dziedziczymy aplikację

Page 4: Legacy applications  - 4Developes konferencja, Piotr Pasich

DLACZEGO TEGO NIE PRZEPISAĆ?

Piotr Pasich @

Page 5: Legacy applications  - 4Developes konferencja, Piotr Pasich

dużo koduduża ilość funkcjonalności

czaspieniądze

Piotr Pasich @

Page 6: Legacy applications  - 4Developes konferencja, Piotr Pasich

PREVENTING REGRESSIONSczyli nic nie ruszać

Piotr Pasich @

Page 7: Legacy applications  - 4Developes konferencja, Piotr Pasich

TESTY FUNKCJONALNOŚCISelenium IDE, behat, testy jednostkowe

Piotr Pasich @

Page 8: Legacy applications  - 4Developes konferencja, Piotr Pasich

ŚRODOWISKOminimum PHP 5.3.3Sqlite3, JSON, ctypephp app/check.php

date.timezone set in php.iniPhpcs CodeSniffs

tutaj po raz pierwszy korzystamy z testów

Piotr Pasich @

Page 9: Legacy applications  - 4Developes konferencja, Piotr Pasich

INSTALACJA SYMFONY 2katalog legacy

namespace

Piotr Pasich @

namespace Legacy {

(...)

}

Page 10: Legacy applications  - 4Developes konferencja, Piotr Pasich

LegacyBundleapp/console generate:bundle

Bundle namespace: Xsolve\LegacyBundle

Piotr Pasich @

Page 11: Legacy applications  - 4Developes konferencja, Piotr Pasich

AUTOLOADER<?php

namespace Xsolve\LegacyBundle;

require_once(__DIR__ . "/../../../legacy/index.php"); //disabled execute::runuse Legacy;use Symfony\Component\HttpKernel\Bundle\Bundle;use Symfony\Component\DependencyInjection\ContainerBuilder;

class XsolveLegacyBundle extends Bundle{ public function build(ContainerBuilder $container) { spl_autoload_register(array('Kohana', 'auto_load')); }

}

Piotr Pasich @

Page 12: Legacy applications  - 4Developes konferencja, Piotr Pasich

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 @

Page 13: Legacy applications  - 4Developes konferencja, Piotr Pasich

KOHANA?

system/core/Bootstrap.php

//Event::run('system.routing'); //Benchmark::stop(SYSTEM_BENCHMARK.'_system_initialization');//Event::run('system.shutdown');

DIE; //!

Piotr Pasich @

Page 14: Legacy applications  - 4Developes konferencja, Piotr Pasich

LAYOUTesi

VarnishGuzzle Client

Crawler

Piotr Pasich @

Page 15: Legacy applications  - 4Developes konferencja, Piotr Pasich

ESI + VARNISHhttp://todsul.com/symfony2-esi-varnish

framework: { esi: true }

Piotr Pasich @

Page 16: Legacy applications  - 4Developes konferencja, Piotr Pasich

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 @

Page 17: Legacy applications  - 4Developes konferencja, Piotr Pasich

ESI SERVICEclass LegacyClient{ (...) public function requestElement($url, $element) { $html = $this->request($url); return $this->filter($html, $element); }

(...)

Piotr Pasich @

Page 18: Legacy applications  - 4Developes konferencja, Piotr Pasich

ESI SERVICE /** * @return \Symfony\Component\DomCrawler\Crawler */ 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 @

Page 19: Legacy applications  - 4Developes konferencja, Piotr Pasich

ESI SERVICE /** * @return \Guzzle\Http\Client */ public function getClient() { $client = new Client(); return $client; }

Piotr Pasich @

Page 20: Legacy applications  - 4Developes konferencja, Piotr Pasich

ESI SERVICE public 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 @

Page 21: Legacy applications  - 4Developes konferencja, Piotr Pasich

ESI SERVICE public 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 @

Page 22: Legacy applications  - 4Developes konferencja, Piotr Pasich

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 @

Page 23: Legacy applications  - 4Developes konferencja, Piotr Pasich

REVERSE PROXY CACHE// app/AppCache.phprequire_once __DIR__.'/AppKernel.php';

use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;

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 @

Page 24: Legacy applications  - 4Developes konferencja, Piotr Pasich

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 Symfony\Component\HttpFoundation\Request;

$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 @

Page 25: Legacy applications  - 4Developes konferencja, Piotr Pasich

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 @

Page 26: Legacy applications  - 4Developes konferencja, Piotr Pasich

RENDER{% render url('latest_news', { 'max': 5 }) with {}, {'standalone': true} %}

Piotr Pasich @

Page 27: Legacy applications  - 4Developes konferencja, Piotr Pasich

ROUTINGnamespace Xsolve\LegacyBundle\Service;

class LegacyRouter{ /** @var array Legacy routes configuration */ protected $config;

public function __construct(Router $router) { include __DIR__.'/../../../../legacy/application/config/urls.php';

$this->config = $config; $this->router = $router; $this->locale = $this->router->getContext()->getParameter('_locale'); } // (..)}

Piotr Pasich @

Page 28: Legacy applications  - 4Developes konferencja, Piotr Pasich

KONFIGURACJAclass LegacyConfiguration{ protected $configuration; public function __construct($configuration) { $this->configuration = $configuration; }

public function onKernelRequest(GetResponseEvent $event) { global $legacyConfig; $legacyConfig = $this->configuration; }}

Piotr Pasich @

Page 29: Legacy applications  - 4Developes konferencja, Piotr Pasich

KONFIGURACJAglobal $legacyConfig;

$config['default'] = $legacyConfig['database']['default'];$config['import'] = $legacyConfig['database']['import'];

Piotr Pasich @

Page 30: Legacy applications  - 4Developes konferencja, Piotr Pasich

SESSIONGdzie jest problem?

_s2_(...)

Piotr Pasich @

Page 31: Legacy applications  - 4Developes konferencja, Piotr Pasich

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 @

Page 32: Legacy applications  - 4Developes konferencja, Piotr Pasich

SESSIONnamespace Xsolve\LegacyBundle\Session;

use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;use Symfony\Component\HttpFoundation\Session\SessionBagInterface;use Xsolve\LegacyBundle\Session\ScalarBagInterface;/*** This class provides scalar storage of session attributes using* a name spacing character in the key.** @author Piotr Pasich <[email protected]>*/class ScalarBag implements ScalarBagInterface, SessionBagInterface{ // (...)}

Piotr Pasich @

Page 33: Legacy applications  - 4Developes konferencja, Piotr Pasich

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.xsd"> <services> <service id="xsolve.legacy.listener.request" class="Xsolve\LegacyBundle\RequestListener"> <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest"/> <argument type="service" id="session" /> </service> </services>

</container>

Piotr Pasich @

Page 34: Legacy applications  - 4Developes konferencja, Piotr Pasich

NIE DZIAŁA!DLACZEGO?

Piotr Pasich @

Page 35: Legacy applications  - 4Developes konferencja, Piotr Pasich

BO KOHANA!class Session_Core { // (...) public function create($vars = NULL) { $this->destroy(); // (...) }}

Piotr Pasich @

Page 36: Legacy applications  - 4Developes konferencja, Piotr Pasich

TERAZ DZIAŁAclass Session_Core { // (...) public function create($vars = NULL) { //$this->destroy(); // (...) }}

Piotr Pasich @

Page 37: Legacy applications  - 4Developes konferencja, Piotr Pasich

TERAZ NIE DZIAŁA

Piotr Pasich @

Page 38: Legacy applications  - 4Developes konferencja, Piotr Pasich

TERAZ DZIAŁAclass Session_Core { // (...) public function create($vars = NULL) { // Destroy any current sessions self::$createCall = self::$createCall+1;

if (self::$createCall > 10){ $_SESSION = array(); } // this->destroy(); // (...) }}

Piotr Pasich @

Page 39: Legacy applications  - 4Developes konferencja, Piotr Pasich

BAZA DANYCHponad 100 tabel = 100 encjibrak odpowiednich relacji

brak pełnej zgodności z wymogami Doctrine 2

Piotr Pasich @

Page 40: Legacy applications  - 4Developes konferencja, Piotr Pasich

BAZA DANYCHapp/console doctrine:mapping:import XsolveLegacyBundle annotation

Piotr Pasich @

Page 41: Legacy applications  - 4Developes konferencja, Piotr Pasich

KONFLIKTY I BŁĘDYnaprawiamy ręcznie :(

Piotr Pasich @

Page 42: Legacy applications  - 4Developes konferencja, Piotr Pasich

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 @

Page 43: Legacy applications  - 4Developes konferencja, Piotr Pasich

I TO DZIAŁA!

Piotr Pasich @

Page 44: Legacy applications  - 4Developes konferencja, Piotr Pasich

DZIĘKUJĘ ZA UWAGĘPiotr Pasich

[email protected]