SlideShare una empresa de Scribd logo
1 de 40
Descargar para leer sin conexión
Legacy applications
Piotr Pasich @
więc...
przychodzi klient do lekarza
ZACZNIJMY OD KLIENTA
Piotr Pasich @
żeby działało
na wczoraj
ewentualnie ASAP
CZEGO OCZEKUJE?
Piotr Pasich @
Ile to będzie trwało?
9 miesięcy
PRZEPISZMY TO!
Piotr Pasich @
Piotr Pasich @
Ile to będzie trwało?
2 miesiące
NADPISZMY TO!
Piotr Pasich @
FROM SPAGHETTI TO CODE
PREVENTING REGRESSIONS
czyli nic nie ruszać
Piotr Pasich @
TESTY FUNKCJONALNOŚCI
Selenium IDE, behat, testy jednostkowe
Piotr Pasich @
ŚRODOWISKO
minimum PHP 5.3.3
Sqlite3, JSON, ctype
php app/check.php
date.timezone set in php.ini
Phpcs CodeSniffs
tutaj po raz pierwszy korzystamy z testów
Piotr Pasich @
INSTALACJA SYMFONY 2
katalog legacy
namespace
namespace Legacy {
(...)
}
Piotr Pasich @
LegacyBundle
app/console generate:bundle
Bundle namespace: XsolveLegacyBundle
Piotr Pasich @
AUTOLOADER
<?php
namespace XsolveLegacyBundle;
require_once(__DIR__ . "/../../../legacy/index.php"); //disabled execute::run
use Legacy;
use SymfonyComponentHttpKernelBundleBundle;
use SymfonyComponentDependencyInjectionContainerBuilder;
class XsolveLegacyBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
spl_autoload_register(array('Kohana', 'auto_load'));
}
}
Piotr Pasich @
MainAction
class 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 @
KOHANA?
system/core/Bootstrap.php
//Event::run('system.routing');
//Benchmark::stop(SYSTEM_BENCHMARK.'_system_initialization');
//Event::run('system.shutdown');
DIE ( ); //!
Piotr Pasich @
LAYOUT
esi
Varnish
Guzzle Client
Crawler
Piotr Pasich @
ESI + VARNISH
http://todsul.com/symfony2-esi-varnish
framework: { esi: true }
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 @
ESI SERVICE
class LegacyClient
{
(...)
public function requestElement($url, $element)
{
$html = $this->request($url);
return $this->filter($html, $element);
}
(...)
Piotr Pasich @
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 @
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 @
REVERSE PROXY CACHE
// app/AppCache.php
require_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 @
REVERSE PROXY CACHE
<?php
// web/app.php
require_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 @
REVERSE PROXY CACHE
class 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 @
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 @
RENDER
{% render url('latest_news', { 'max': 5 }) with {}, {'standalone': true} %}
Piotr Pasich @
SESSION
Gdzie jest problem?
_s2_(...)
Piotr Pasich @
SESSION
class 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 @
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 @
NIE DZIAŁA!
DLACZEGO?
Piotr Pasich @
BO KOHANA!class Session_Core {
// (...)
public function create($vars = NULL)
{
$this->destroy();
// (...)
}
}
Piotr Pasich @
TERAZ DZIAŁA
class Session_Core {
// (...)
public function create($vars = NULL)
{
//$this->destroy();
// (...)
}
}
Piotr Pasich @
TERAZ NIE DZIAŁA
Piotr Pasich @
TERAZ DZIAŁA
class 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 @
BAZA DANYCH
ponad 100 tabel = 100 encji
brak odpowiednich relacji
brak pełnej zgodności z wymogami Doctrine 2
Piotr Pasich @
BAZA DANYCH
app/console doctrine:mapping:import XsolveLegacyBundle anotation
Piotr Pasich @
KONFLIKTY I BŁĘDY
naprawiamy ręcznie :(
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 @
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 @
I TO DZIAŁA
Piotr Pasich @
piotr.pasich@xsolve.pl
www.xsolve.pl
www.xlab.pl

Más contenido relacionado

La actualidad más candente

BASH Variables Part 1: Basic Interpolation
BASH Variables Part 1: Basic InterpolationBASH Variables Part 1: Basic Interpolation
BASH Variables Part 1: Basic InterpolationWorkhorse Computing
 
Nigel hamilton-megameet-2013
Nigel hamilton-megameet-2013Nigel hamilton-megameet-2013
Nigel hamilton-megameet-2013trexy
 
Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.Workhorse Computing
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryMauro Rocco
 
An Introduction to Celery
An Introduction to CeleryAn Introduction to Celery
An Introduction to CeleryIdan Gazit
 
Puppet: What _not_ to do
Puppet: What _not_ to doPuppet: What _not_ to do
Puppet: What _not_ to doPuppet
 
PuppetCamp Ghent - What Not to Do with Puppet
PuppetCamp Ghent - What Not to Do with PuppetPuppetCamp Ghent - What Not to Do with Puppet
PuppetCamp Ghent - What Not to Do with PuppetWalter Heck
 
Django Celery - A distributed task queue
Django Celery - A distributed task queueDjango Celery - A distributed task queue
Django Celery - A distributed task queueAlex Eftimie
 
V2 and beyond
V2 and beyondV2 and beyond
V2 and beyondjimi-c
 
Perl Sucks - and what to do about it
Perl Sucks - and what to do about itPerl Sucks - and what to do about it
Perl Sucks - and what to do about it2shortplanks
 
Great Developers Steal
Great Developers StealGreat Developers Steal
Great Developers StealBen Scofield
 
Building Cloud Castles - LRUG
Building Cloud Castles - LRUGBuilding Cloud Castles - LRUG
Building Cloud Castles - LRUGBen Scofield
 
Perl web app 테스트전략
Perl web app 테스트전략Perl web app 테스트전략
Perl web app 테스트전략Jeen Lee
 
Kansai.pm 10周年記念 Plack/PSGI 入門
Kansai.pm 10周年記念 Plack/PSGI 入門Kansai.pm 10周年記念 Plack/PSGI 入門
Kansai.pm 10周年記念 Plack/PSGI 入門lestrrat
 

La actualidad más candente (20)

Memory Manglement in Raku
Memory Manglement in RakuMemory Manglement in Raku
Memory Manglement in Raku
 
Lies, Damn Lies, and Benchmarks
Lies, Damn Lies, and BenchmarksLies, Damn Lies, and Benchmarks
Lies, Damn Lies, and Benchmarks
 
Nubilus Perl
Nubilus PerlNubilus Perl
Nubilus Perl
 
BASH Variables Part 1: Basic Interpolation
BASH Variables Part 1: Basic InterpolationBASH Variables Part 1: Basic Interpolation
BASH Variables Part 1: Basic Interpolation
 
Nigel hamilton-megameet-2013
Nigel hamilton-megameet-2013Nigel hamilton-megameet-2013
Nigel hamilton-megameet-2013
 
Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & Celery
 
An Introduction to Celery
An Introduction to CeleryAn Introduction to Celery
An Introduction to Celery
 
Puppet: What _not_ to do
Puppet: What _not_ to doPuppet: What _not_ to do
Puppet: What _not_ to do
 
PuppetCamp Ghent - What Not to Do with Puppet
PuppetCamp Ghent - What Not to Do with PuppetPuppetCamp Ghent - What Not to Do with Puppet
PuppetCamp Ghent - What Not to Do with Puppet
 
Django Celery - A distributed task queue
Django Celery - A distributed task queueDjango Celery - A distributed task queue
Django Celery - A distributed task queue
 
Fake My Party
Fake My PartyFake My Party
Fake My Party
 
Getting testy with Perl
Getting testy with PerlGetting testy with Perl
Getting testy with Perl
 
V2 and beyond
V2 and beyondV2 and beyond
V2 and beyond
 
Perl Sucks - and what to do about it
Perl Sucks - and what to do about itPerl Sucks - and what to do about it
Perl Sucks - and what to do about it
 
Great Developers Steal
Great Developers StealGreat Developers Steal
Great Developers Steal
 
Building Cloud Castles - LRUG
Building Cloud Castles - LRUGBuilding Cloud Castles - LRUG
Building Cloud Castles - LRUG
 
Findbin libs
Findbin libsFindbin libs
Findbin libs
 
Perl web app 테스트전략
Perl web app 테스트전략Perl web app 테스트전략
Perl web app 테스트전략
 
Kansai.pm 10周年記念 Plack/PSGI 入門
Kansai.pm 10周年記念 Plack/PSGI 入門Kansai.pm 10周年記念 Plack/PSGI 入門
Kansai.pm 10周年記念 Plack/PSGI 入門
 

Destacado

A2 Media Studies - Music Video Analysis
A2 Media Studies - Music Video AnalysisA2 Media Studies - Music Video Analysis
A2 Media Studies - Music Video AnalysisPheebs023
 
2008 smid
2008 smid2008 smid
2008 smid604381
 
eToro DevOps presentation
eToro DevOps presentationeToro DevOps presentation
eToro DevOps presentationDavid Virtser
 
Project management and unveiling risks
Project management and unveiling risksProject management and unveiling risks
Project management and unveiling risksmds-web
 
васькин мнск13
васькин мнск13васькин мнск13
васькин мнск13vaskinyy
 
Reaching & Connecting with the BOOMER CONSUMER - by EtMM Aaron D. Murphy
Reaching & Connecting with the BOOMER CONSUMER - by EtMM Aaron D. MurphyReaching & Connecting with the BOOMER CONSUMER - by EtMM Aaron D. Murphy
Reaching & Connecting with the BOOMER CONSUMER - by EtMM Aaron D. MurphyAaron D. Murphy, Architect / CAPS
 
PHPConPl 2013 - Allowed memory size of X bytes exhausted
PHPConPl 2013 - Allowed memory size of X bytes exhaustedPHPConPl 2013 - Allowed memory size of X bytes exhausted
PHPConPl 2013 - Allowed memory size of X bytes exhaustedPiotr Pasich
 
Performance
PerformancePerformance
PerformanceKoay Hui
 
Tracking norm
Tracking normTracking norm
Tracking normy_anni
 
ідеї для вечірнього макіяжу
ідеї для вечірнього макіяжуідеї для вечірнього макіяжу
ідеї для вечірнього макіяжуAnnaBoyko1
 
устюгова катя 145 группа
устюгова катя 145 группаустюгова катя 145 группа
устюгова катя 145 группаKatya Ustyugova
 
Water customer presentation
Water customer presentationWater customer presentation
Water customer presentationSuj Kang
 
PPM (Private Equtiy/Stock Offering)
PPM (Private Equtiy/Stock Offering)PPM (Private Equtiy/Stock Offering)
PPM (Private Equtiy/Stock Offering)Scott Davies
 

Destacado (19)

A2 Media Studies - Music Video Analysis
A2 Media Studies - Music Video AnalysisA2 Media Studies - Music Video Analysis
A2 Media Studies - Music Video Analysis
 
2008 smid
2008 smid2008 smid
2008 smid
 
eToro DevOps presentation
eToro DevOps presentationeToro DevOps presentation
eToro DevOps presentation
 
Project management and unveiling risks
Project management and unveiling risksProject management and unveiling risks
Project management and unveiling risks
 
Twitter
TwitterTwitter
Twitter
 
Empowering The Mature Mind - SUMMER 2014 Newsletter
Empowering The Mature Mind - SUMMER 2014 NewsletterEmpowering The Mature Mind - SUMMER 2014 Newsletter
Empowering The Mature Mind - SUMMER 2014 Newsletter
 
васькин мнск13
васькин мнск13васькин мнск13
васькин мнск13
 
Reaching & Connecting with the BOOMER CONSUMER - by EtMM Aaron D. Murphy
Reaching & Connecting with the BOOMER CONSUMER - by EtMM Aaron D. MurphyReaching & Connecting with the BOOMER CONSUMER - by EtMM Aaron D. Murphy
Reaching & Connecting with the BOOMER CONSUMER - by EtMM Aaron D. Murphy
 
PHPConPl 2013 - Allowed memory size of X bytes exhausted
PHPConPl 2013 - Allowed memory size of X bytes exhaustedPHPConPl 2013 - Allowed memory size of X bytes exhausted
PHPConPl 2013 - Allowed memory size of X bytes exhausted
 
Performance
PerformancePerformance
Performance
 
Time management
Time managementTime management
Time management
 
Tracking norm
Tracking normTracking norm
Tracking norm
 
ідеї для вечірнього макіяжу
ідеї для вечірнього макіяжуідеї для вечірнього макіяжу
ідеї для вечірнього макіяжу
 
Tugas kkpi
Tugas kkpiTugas kkpi
Tugas kkpi
 
Task groups
Task groupsTask groups
Task groups
 
Users in casual
Users in casualUsers in casual
Users in casual
 
устюгова катя 145 группа
устюгова катя 145 группаустюгова катя 145 группа
устюгова катя 145 группа
 
Water customer presentation
Water customer presentationWater customer presentation
Water customer presentation
 
PPM (Private Equtiy/Stock Offering)
PPM (Private Equtiy/Stock Offering)PPM (Private Equtiy/Stock Offering)
PPM (Private Equtiy/Stock Offering)
 

Similar a #SPUG - Legacy applications

Advanced PHPUnit Testing
Advanced PHPUnit TestingAdvanced PHPUnit Testing
Advanced PHPUnit TestingMike Lively
 
Ch ch-changes cake php2
Ch ch-changes cake php2Ch ch-changes cake php2
Ch ch-changes cake php2markstory
 
EPHPC Webinar Slides: Unit Testing by Arthur Purnama
EPHPC Webinar Slides: Unit Testing by Arthur PurnamaEPHPC Webinar Slides: Unit Testing by Arthur Purnama
EPHPC Webinar Slides: Unit Testing by Arthur PurnamaEnterprise PHP Center
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applicationschartjes
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Michelangelo van Dam
 
Zendcon 2007 Api Design
Zendcon 2007 Api DesignZendcon 2007 Api Design
Zendcon 2007 Api Designunodelostrece
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryTatsuhiko Miyagawa
 
Workshop quality assurance for php projects - phpbelfast
Workshop quality assurance for php projects - phpbelfastWorkshop quality assurance for php projects - phpbelfast
Workshop quality assurance for php projects - phpbelfastMichelangelo van Dam
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Elena Kolevska
 
REST with Eve and Python
REST with Eve and PythonREST with Eve and Python
REST with Eve and PythonPiXeL16
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015Fernando Daciuk
 
Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Michelangelo van Dam
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Michelangelo van Dam
 
Best Practices in Plugin Development (WordCamp Seattle)
Best Practices in Plugin Development (WordCamp Seattle)Best Practices in Plugin Development (WordCamp Seattle)
Best Practices in Plugin Development (WordCamp Seattle)andrewnacin
 
Debugging: Rules And Tools - PHPTek 11 Version
Debugging: Rules And Tools - PHPTek 11 VersionDebugging: Rules And Tools - PHPTek 11 Version
Debugging: Rules And Tools - PHPTek 11 VersionIan Barber
 
Introduction to PHP
Introduction to PHPIntroduction to PHP
Introduction to PHPBradley Holt
 
Debugging: Rules & Tools
Debugging: Rules & ToolsDebugging: Rules & Tools
Debugging: Rules & ToolsIan Barber
 
Build restful ap is with python and flask
Build restful ap is with python and flaskBuild restful ap is with python and flask
Build restful ap is with python and flaskJeetendra singh
 

Similar a #SPUG - Legacy applications (20)

Advanced PHPUnit Testing
Advanced PHPUnit TestingAdvanced PHPUnit Testing
Advanced PHPUnit Testing
 
Ch ch-changes cake php2
Ch ch-changes cake php2Ch ch-changes cake php2
Ch ch-changes cake php2
 
EPHPC Webinar Slides: Unit Testing by Arthur Purnama
EPHPC Webinar Slides: Unit Testing by Arthur PurnamaEPHPC Webinar Slides: Unit Testing by Arthur Purnama
EPHPC Webinar Slides: Unit Testing by Arthur Purnama
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applications
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013
 
Zendcon 2007 Api Design
Zendcon 2007 Api DesignZendcon 2007 Api Design
Zendcon 2007 Api Design
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
Workshop quality assurance for php projects - phpbelfast
Workshop quality assurance for php projects - phpbelfastWorkshop quality assurance for php projects - phpbelfast
Workshop quality assurance for php projects - phpbelfast
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
REST with Eve and Python
REST with Eve and PythonREST with Eve and Python
REST with Eve and Python
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015
 
Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12
 
Best Practices in Plugin Development (WordCamp Seattle)
Best Practices in Plugin Development (WordCamp Seattle)Best Practices in Plugin Development (WordCamp Seattle)
Best Practices in Plugin Development (WordCamp Seattle)
 
Debugging: Rules And Tools - PHPTek 11 Version
Debugging: Rules And Tools - PHPTek 11 VersionDebugging: Rules And Tools - PHPTek 11 Version
Debugging: Rules And Tools - PHPTek 11 Version
 
Introduction to PHP
Introduction to PHPIntroduction to PHP
Introduction to PHP
 
Fatc
FatcFatc
Fatc
 
Debugging: Rules & Tools
Debugging: Rules & ToolsDebugging: Rules & Tools
Debugging: Rules & Tools
 
Build restful ap is with python and flask
Build restful ap is with python and flaskBuild restful ap is with python and flask
Build restful ap is with python and flask
 

Más de Piotr Pasich

Messaging queues with RabbitMQ
Messaging queues with RabbitMQMessaging queues with RabbitMQ
Messaging queues with RabbitMQPiotr Pasich
 
How to write applications prepared for every cataclysm with Event Sourcing an...
How to write applications prepared for every cataclysm with Event Sourcing an...How to write applications prepared for every cataclysm with Event Sourcing an...
How to write applications prepared for every cataclysm with Event Sourcing an...Piotr Pasich
 
Varnish - Tips & Tricks - 4Developers 2015
Varnish - Tips & Tricks - 4Developers 2015Varnish - Tips & Tricks - 4Developers 2015
Varnish - Tips & Tricks - 4Developers 2015Piotr Pasich
 
Arduino - SymfonyCon 2014 Lighting Talks
Arduino - SymfonyCon 2014 Lighting TalksArduino - SymfonyCon 2014 Lighting Talks
Arduino - SymfonyCon 2014 Lighting TalksPiotr Pasich
 
Game of performance - Message Brokers behind the scenes
Game of performance - Message Brokers behind the scenesGame of performance - Message Brokers behind the scenes
Game of performance - Message Brokers behind the scenesPiotr Pasich
 
Simplify your code with annotations - SymfonyCon Warsaw 2013
Simplify your code with annotations - SymfonyCon Warsaw 2013Simplify your code with annotations - SymfonyCon Warsaw 2013
Simplify your code with annotations - SymfonyCon Warsaw 2013Piotr Pasich
 
SpreadIT - Make your project SOLID!
SpreadIT - Make your project SOLID! SpreadIT - Make your project SOLID!
SpreadIT - Make your project SOLID! Piotr Pasich
 

Más de Piotr Pasich (7)

Messaging queues with RabbitMQ
Messaging queues with RabbitMQMessaging queues with RabbitMQ
Messaging queues with RabbitMQ
 
How to write applications prepared for every cataclysm with Event Sourcing an...
How to write applications prepared for every cataclysm with Event Sourcing an...How to write applications prepared for every cataclysm with Event Sourcing an...
How to write applications prepared for every cataclysm with Event Sourcing an...
 
Varnish - Tips & Tricks - 4Developers 2015
Varnish - Tips & Tricks - 4Developers 2015Varnish - Tips & Tricks - 4Developers 2015
Varnish - Tips & Tricks - 4Developers 2015
 
Arduino - SymfonyCon 2014 Lighting Talks
Arduino - SymfonyCon 2014 Lighting TalksArduino - SymfonyCon 2014 Lighting Talks
Arduino - SymfonyCon 2014 Lighting Talks
 
Game of performance - Message Brokers behind the scenes
Game of performance - Message Brokers behind the scenesGame of performance - Message Brokers behind the scenes
Game of performance - Message Brokers behind the scenes
 
Simplify your code with annotations - SymfonyCon Warsaw 2013
Simplify your code with annotations - SymfonyCon Warsaw 2013Simplify your code with annotations - SymfonyCon Warsaw 2013
Simplify your code with annotations - SymfonyCon Warsaw 2013
 
SpreadIT - Make your project SOLID!
SpreadIT - Make your project SOLID! SpreadIT - Make your project SOLID!
SpreadIT - Make your project SOLID!
 

Último

UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
Landscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdfLandscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdfAarwolf Industries LLC
 
Infrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsInfrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsYoss Cohen
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...Jeffrey Haguewood
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)Mark Simos
 
Glenn Lazarus- Why Your Observability Strategy Needs Security Observability
Glenn Lazarus- Why Your Observability Strategy Needs Security ObservabilityGlenn Lazarus- Why Your Observability Strategy Needs Security Observability
Glenn Lazarus- Why Your Observability Strategy Needs Security Observabilityitnewsafrica
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024TopCSSGallery
 
Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...itnewsafrica
 
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxGenerative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxfnnc6jmgwh
 
Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Karmanjay Verma
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integrationmarketing932765
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Alkin Tezuysal
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkPixlogix Infotech
 

Último (20)

UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
Landscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdfLandscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdf
 
Infrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsInfrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platforms
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
 
Glenn Lazarus- Why Your Observability Strategy Needs Security Observability
Glenn Lazarus- Why Your Observability Strategy Needs Security ObservabilityGlenn Lazarus- Why Your Observability Strategy Needs Security Observability
Glenn Lazarus- Why Your Observability Strategy Needs Security Observability
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024
 
Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...
 
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxGenerative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
 
Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App Framework
 

#SPUG - Legacy applications

  • 2. więc... przychodzi klient do lekarza ZACZNIJMY OD KLIENTA Piotr Pasich @
  • 3. żeby działało na wczoraj ewentualnie ASAP CZEGO OCZEKUJE? Piotr Pasich @
  • 4. Ile to będzie trwało? 9 miesięcy PRZEPISZMY TO! Piotr Pasich @
  • 6. Ile to będzie trwało? 2 miesiące NADPISZMY TO! Piotr Pasich @
  • 8. PREVENTING REGRESSIONS czyli nic nie ruszać Piotr Pasich @
  • 9. TESTY FUNKCJONALNOŚCI Selenium IDE, behat, testy jednostkowe Piotr Pasich @
  • 10. ŚRODOWISKO minimum PHP 5.3.3 Sqlite3, JSON, ctype php app/check.php date.timezone set in php.ini Phpcs CodeSniffs tutaj po raz pierwszy korzystamy z testów Piotr Pasich @
  • 11. INSTALACJA SYMFONY 2 katalog legacy namespace namespace Legacy { (...) } Piotr Pasich @
  • 13. AUTOLOADER <?php namespace XsolveLegacyBundle; require_once(__DIR__ . "/../../../legacy/index.php"); //disabled execute::run use Legacy; use SymfonyComponentHttpKernelBundleBundle; use SymfonyComponentDependencyInjectionContainerBuilder; class XsolveLegacyBundle extends Bundle { public function build(ContainerBuilder $container) { spl_autoload_register(array('Kohana', 'auto_load')); } } Piotr Pasich @
  • 14. MainAction class 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 @
  • 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. ESI SERVICE class LegacyClient { (...) public function requestElement($url, $element) { $html = $this->request($url); return $this->filter($html, $element); } (...) Piotr Pasich @
  • 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. 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 @
  • 22. REVERSE PROXY CACHE // app/AppCache.php require_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. REVERSE PROXY CACHE <?php // web/app.php require_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. REVERSE PROXY CACHE class 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. 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. RENDER {% render url('latest_news', { 'max': 5 }) with {}, {'standalone': true} %} Piotr Pasich @
  • 28. SESSION class 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. 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 @
  • 31. BO KOHANA!class Session_Core { // (...) public function create($vars = NULL) { $this->destroy(); // (...) } } Piotr Pasich @
  • 32. TERAZ DZIAŁA class Session_Core { // (...) public function create($vars = NULL) { //$this->destroy(); // (...) } } Piotr Pasich @
  • 34. TERAZ DZIAŁA class 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 @
  • 35. BAZA DANYCH ponad 100 tabel = 100 encji brak odpowiednich relacji brak pełnej zgodności z wymogami Doctrine 2 Piotr Pasich @
  • 36. BAZA DANYCH app/console doctrine:mapping:import XsolveLegacyBundle anotation Piotr Pasich @
  • 37. KONFLIKTY I BŁĘDY naprawiamy ręcznie :( Piotr Pasich @
  • 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. 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. I TO DZIAŁA Piotr Pasich @ piotr.pasich@xsolve.pl www.xsolve.pl www.xlab.pl