Download - Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013
![Page 1: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/1.jpg)
Case study: eZ Publish
Symfony2 http kernel for legacy apps
Gaetano Giunta | PUG Milano | Maggio 2013
![Page 2: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/2.jpg)
The drivers
![Page 3: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/3.jpg)
Existing codebase is 10 years old
High maintenance cost
Started with no unit tests
Layers and roles not properly defined / documented
OOP before php had
Private/protected/static
Closures
Namespaces
Late static binding
And much more
Not built for an Ajax and REST world
5/18/2013 [email protected] Slide 3
Why change? Everyone loves NEW!
![Page 4: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/4.jpg)
Existing codebase is 10 years old
Widely deployed
Well debugged
Pitfalls have probably been uncovered by now
Proven to scale
Well known:
Documentation improved over years
Tutorials, forums, blogs, aggregators
Active community of practitioners
Official training courses
5/18/2013 [email protected] Slide 4
Why change? Do not forget drawbacks
![Page 5: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/5.jpg)
Focus on our core business Experience Management
Content Management
NOT Framework maintenance
Durable Architecture
API stability
Battle tested / not (only) the latest trend
Scalability
Lively Community!
5/18/2013 [email protected] Slide 5
Picking a framework for a platform rebuild
![Page 6: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/6.jpg)
• Simple Integration with existing API
• HMVC (Hierarchical Model View Controller) stack
• Decoupled Components
• Dependency Injection
• Good Template Engine
• Extensible, Open, Reliable ;-)
5/18/2013 [email protected] Slide 6
Prerequisites
![Page 7: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/7.jpg)
• Home brew
• Zeta Components
• Zend Framework 2
• Symfony 2 (Full Stack!)
5/18/2013 [email protected] Slide 7
Candidates
![Page 9: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/9.jpg)
The challenge
![Page 10: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/10.jpg)
Product Management SCRUM Story:
«As an existing user, I don’t want to be pissed off by a new #@!$% version!»
5/18/2013 [email protected] Slide 10
Backwards compatibility (life sucks)
![Page 11: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/11.jpg)
Product Management SCRUM Story:
«As an existing user, I don’t want to be pissed off by a new #@!$% version!»
• 100% Data Compatible (same DB scheme)
• Possibility to include legacy templates in the new ones
• Routing fallback
• Load legacy content templates with legacy rules
• Settings
• Access Symfony services from legacy modules
5/18/2013 [email protected] Slide 11
Backwards compatibility: the objectives
![Page 12: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/12.jpg)
Product Management SCRUM Story:
«As an existing user, I don’t want to be pissed off by a new #@!$% version!»
• 100% Data Compatible (same DB scheme)
• Possibility to include legacy templates in the new ones
• Routing fallback
• Load legacy content templates with legacy rules
• Settings
• Access Symfony services from legacy modules
5/18/2013 [email protected] Slide 12
Backwards compatibility: the objectives
![Page 13: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/13.jpg)
A new architecture
![Page 14: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/14.jpg)
Product Management SCRUM Story:
«As an existing user, I don’t want to be pissed off by a new #@!$% version!»
• 100% Data Compatible (same DB scheme)
• Possibility to include legacy templates in the new ones
• Routing fallback
• Load legacy content templates with legacy rules
• Settings
• Access Symfony services from legacy modules
Challenge Accepted
5/18/2013 [email protected] Slide 14
BC: the challenge
![Page 16: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/16.jpg)
Legacy version still works perfectly standalone
5/18/2013 [email protected] Slide 16
BC: icing on the cake
![Page 17: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/17.jpg)
Isn’t this what you have been waiting for?
The HTTP Kernel
![Page 18: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/18.jpg)
Request => process() => Response
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$input = $request->get('name', 'World'); // allows a default value
$response = new Response('Hello ' . htmlspecialchars($input, ENT_QUOTES, 'UTF-8'));
$response->send(); // takes care of http headers
The HTTPFoundation Component eases mundane tasks
5/18/2013 [email protected] Slide 18
Use the HTTP, Luke A very, very simple frontend controller
![Page 19: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/19.jpg)
The HTTPKernel component “formalizes the process of starting with a request
and creating the appropriate response”
interface HttpKernelInterface
{
const MASTER_REQUEST = 1;
const SUB_REQUEST = 2;
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);
}
Returns a Response instance
The «H» in HMVC: subrequests are baked in from the beginning
Looks simple so far, isn’t it?
5/18/2013 [email protected] Slide 19
The heart of the application
![Page 20: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/20.jpg)
The HttpKernel defines a complex flexible workflow
The controller to execute is found via a ControllerResolver
«Framework» work is done via an event system / event listeners
5/18/2013 [email protected] Slide 20
Adding the magic
![Page 21: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/21.jpg)
$request = Request::createFromGlobals();
$dispatcher = new EventDispatcher();
// ... add some event listeners, eg: routing, security checking
// create the controller resolver
$resolver = new MyControllerResolver();
// instantiate the kernel
$kernel = new HttpKernel( $dispatcher, $resolver );
$response = $kernel->handle( $request );
$response->send();
$kernel->terminate( $request, $response );
5/18/2013 [email protected] Slide 21
Building a frontend controller
![Page 22: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/22.jpg)
Any class implementing the ControllerResolverInterface can be used
interface ControllerResolverInterface
{
// must return a callable
public function getController(Request $request);
// returns an array of arguments for the controller
public function getArguments(Request $request, $controller);
}
...
5/18/2013 [email protected] Slide 22
Finding the Controller
![Page 23: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/23.jpg)
The Event Dispatcher Component can be used
use Symfony\Component\EventDispatcher\Event;
$dispatcher->addListener('foo.action', $callable);
Depending on returned value, workflow might be altered (see docs online)
Dispatched events:
Name Name as constant Argument passed to the listener
kernel.request KernelEvents::REQUEST GetResponseEvent
kernel.controller KernelEvents::CONTROLLER FilterControllerEvent
kernel.view KernelEvents::VIEW GetResponseForControllerResultEvent
kernel.response KernelEvents::RESPONSE FilterResponseEvent
kernel.terminate KernelEvents::TERMINATE PostResponseEvent
kernel.exception KernelEvents::EXCEPTION GetResponseForExceptionEvent
5/18/2013 [email protected] Slide 23
Adding event listeners
![Page 24: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/24.jpg)
Taming the beast
![Page 25: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/25.jpg)
New Core: a standard Simfony app («ezpublish» = «app»)
«Legacy Stack» isolated in a dedicated directory
5/18/2013 [email protected] Slide 25
Refactoring: directory layout
![Page 27: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/27.jpg)
use Symfony\Component\HttpFoundation\Request;
require_once __DIR__ . '/../ezpublish/autoload.php'; // set up class autoloading
require_once __DIR__ . '/../ezpublish/EzPublishKernel.php';
$kernel = new EzPublishKernel( 'dev', true ); // extends the Sf Kernel class
$kernel->loadClassCache(); // a method from parent class
$request = Request::createFromGlobals();
$response = $kernel->handle( $request );
$response->send();
$kernel->terminate( $request, $response );
The Kernel class wraps the HTTPKernel
It adds a Service Container
It allows to register bundles via registerBundles()
5/18/2013 [email protected] Slide 27
The final frontend controller Using Symfony Full Stack
![Page 28: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/28.jpg)
Sandbox legacy code in a closure
Index.php had to be refactored (from 1100 lines to 20)
Logic moved to a php class
Separated environment setup from execution and teardown
runCallback() sets up the global legacy environment
5/18/2013 [email protected] Slide 28
Refactoring: bridging Legacy code
![Page 29: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/29.jpg)
Routing
![Page 30: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/30.jpg)
eZPublish 4 uses a custom MVC implementation
Frontend controller: index.php
Bootstraps configuration system, logging, “siteaccess”
Controllers are “plain php” files, properly declared
Url syntax: http:// site / module / controller / parameters
Parameters use a custom format instead of the query string
Virtual aliases can be added on top
For all content nodes, a nice alias is always generated by the system
Good for SEO
Technical debt
No DIC anywhere (registry pattern used)
No nested controllers
No provision for REST / AJAX
Implemented ad-hoc in many plugins (code/functionality duplication)
Policies are tied to controllers, not to the underlying content model
5/18/2013 [email protected] Slide 30
Routing
![Page 32: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/32.jpg)
The ChainRouter from the Sf CMF project is used
Routes for new controllers can be declared in different ways
In a configuration file
app/config/routing.yml
Mybundle/Resources/config/routing.yml (loaded from main routing file)
Via annotations (phpdoc comments)
needs the SensioFrameworkExtraBundle bundle
Command line to dump them
php app/console router:debug
Maximum flexibility for parameters: required/optionsl, default values,
validation, restrict http method, extra support for locale and format, ...
5/18/2013 [email protected] Slide 32
Routing: how it works
![Page 33: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/33.jpg)
Caching
![Page 34: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/34.jpg)
eZ Publish 4 has a complicated advanced caching system
For viewing content, cache is generated on access, invalidated on editing
TTL = infinite
When editing a content, cache is also invalidated for all related contents
Extra invalidation rules can be configured
Can be set up to be pregenerated at editing time (tradeoff: editing speed)
Cache keys include policies of current user, query string, custom session data
“Cache-blocks” can also be added anywhere in the templates
Expiry rules can be set on each block, TTL-based or content-editing based
Breaks mvc principle
Most powerful AND misunderstood feature in the CMS
5/18/2013 [email protected] Slide 34
eZ4 Caching: basics
![Page 35: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/35.jpg)
eZ has a built-in “full-page cache” (stores html on disk)
Currently deprecated, in favour of using a caching reverse Proxy
Performances same if not better
Delegate maintenance of part of the stack (Varnish, Squid)
Holy grail of caching: high TTL and support for PURGE command
1. When RP requests page from server, he gets a high TTL => cache page forever
2. When page changes, server tells to RP to purge that url from cache
Best reduction in number of requests to server while always showing fresh data
Downside: extremely hard to cache pages for connected users
ESI support as well
Hard to make efficient, as eZ can not regenerate an ESI block without full page
context
5/18/2013 [email protected] Slide 35
eZ4 Caching: integration with Reverse Proxies
![Page 36: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/36.jpg)
HTTP Expiration and Validation are used
By setting caching headers on response object
Integrates with a Gateway Cache (a.k.a Reverse Proxy)
Native (built-in, php)
$kernel = new Kernel('prod', false);
$kernel = new HTTPCache($kernel);
External (Varnish, Squid, ...)
Native support for ESI
Using {{ render_esi() }} in twig
5/18/2013 [email protected] Slide 36
Symfony Caching: basics
![Page 37: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/37.jpg)
REST
![Page 38: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/38.jpg)
eZ4 had an incomplete REST API
Only functionality available: reading content
Based on Zeta Components MVC component
A new API has been implemented
Full reading and writing of content is possible
All “dictionary” data is also available
Content-type for response can be JSON or XML (with an XSD!)
Fully restful
Usage of all HTTP verbs (and then some: PATCH)
Respect http headers of request (eg: “Accept”)
HATEOAS: use urls as resource ids
No separate request handling framework needed: pure Symfony routing
Bonus points: a client for the REST API, implements the same interfaces exposed by the local PHP API – network transparency!!!
5/18/2013 [email protected] Slide 40
REST API
![Page 39: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/39.jpg)
More info
![Page 40: Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013](https://reader033.vdocuments.mx/reader033/viewer/2022052821/554a0fb1b4c905825d8b4976/html5/thumbnails/40.jpg)
Tutorials:
http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-
symfony2-components-part-1
http://symfony.com/doc/current/components/http_kernel/introduction.html
Sf2 book – jolly good looking docs:
http://symfony.com/doc/current/book/index.html
eZ Publish:
Community: http://share.ez.no
Source code: https://github.com/ezsystems
API docs: http://pubsvn.ez.no/preview.html
Contact me: @gggeek, [email protected]
5/18/2013 [email protected] Slide 42
The usual suspects