from * to symfony2
TRANSCRIPT
From * to Symfony 2
* Sf1, Zf1, PHP plain, …
Manuel “Kea” Baldassarri
If anything can go wrong, it will
Capt. Edward A. Murphy
Agenda
• rewrite vs. migration
• code organization
• routing
• database
• authentication aka session
Program complexity grows until it exceeds the capability of the programmer who must maintain it.
Murphy's laws
Rewrite vs
migration
Rewrite (theory)Fe
atur
es
0
30
60
90
120
t0 t1 t2 t3 t4 t5
Old site New site
If anything can go wrong, it will
Murphy's laws
Rewrite (real)
0
30
60
90
120
t0 t1 t2 t3 t4 t5
Old site New site real
RewriteEstimations are wrong, the legacy code is worse than your wildest imagination, the customer wants new features, you have to maintain the current site and develop the new code base, you code fast, port the same feature from the old site to the new one, import db, deploy, switch…
MigrationFe
atur
es
0
30
60
90
120
t0 t1 t2 t3 t4 t5
Old tech New tech Total
Rewrite: practice
Only two steps if you decide to rewrite your paper
• Foto con mac e blocco
Rewrite
$ rm -Rf project $ symfony new project
Pre migration
PHP > 5.3
Symfony 1.5
LExpress/symfony1
PHPCompatibility
$ phpcs --standard=PHPCompatibility \ --runtime-set testVersion 5.6
Code structure: as you wish
Routing: multiple entry
point
Catch’em all route/** * @Route("/{filename}.php") */public function catchEmAllAction($filename) { ob_start(); include $filename.”.php”;
$content = ob_get_clean(); $this->doSomeHeadersStuff(headers_list()); return new Response($content); }
Routing: 2 front controllers
Two front controllers
/path/to/legacy/public/index.php
/path/to/sf2/web/app.php
Easy wayREQUEST
WEB SERVER
FRONT CTRL 1 FRONT CTRL 2
Subdomain
http://www.mysite.com
http://new.mysite.com
OLD
NEW
Url prefix
http://www.mysite.com/
http://www.mysite.com/new/
OLD
NEW
Sf2 proxyREQUEST
WEB SERVER
FRONT CTRL 2 FRONT CTRL 1
Event listener
legacy.kernel.listener: class: AppBundle\LegacyBridge\KernelListener tags: - name: kernel.event_listener event: kernel.exception method: onKernelException
NotFoundHttpExceptionpublic function onKernelException($event){ $exception = $event->getException(); if ($exception instanceof NotFoundHttpException) { ob_start(); include "/path/to/old/index.php"; $content = ob_get_clean(); $this->doSomeHeadersStuff(headers_list());
$event->setResponse(new Response($content)); } }
Database
Database
• Cleanup
• Import metadata/structure
• Create entity
• [Add annotation]
Metadata
$ php app/console \ doctrine:mapping:import \ --force AppBundle xml
Entities
$ php app/console \ doctrine:generate:entities AppBundle
Annotation
$ php app/console \ doctrine:mapping:convert annotation ./src
Annotation/** * IngredientTranslation * * @ORM\Table(name=“ingredient_translation", * indexes={ * @ORM\Index(name=“IDX_C1A8BF6BF396750”, * columns={“id"}) * }) * @ORM\Entity */ class IngredientTranslation{…}
Entitiesclass Wow{ /** * @ORM\Column(name="p0", type="integer") * @ORM\Id */ private $p0;
/** * @ORM\Column(name="p1", type="string", …) */ private $p1; … }
Entitiesclass Wow{ /** * @ORM\Column(name="p0", type="integer") * @ORM\Id */ private $id;
/** * @ORM\Column(name="p1", type="string", …) */ private $title; … }
Authentication aka
Session
Login in Sf2 app
Add layer to read Sf2 session
Forge a compatible $_SESSION
or
Login in “Old” app
Use preAuthentication
SessionBagInterfaceMetadataBag
FlashBag AutoExpireFlashBag
AttributeBag NamespacedAttributeBag
$_SESSION (old app logged in)
[ 'App' => [ 'user_id' => 10, ‘username' => 'kea' ] ]
$_SESSION (anonym)
[ '_sf2_attributes' => [ ] '_sf2_flashes' => [ ] '_sf2_meta' => [ ... ]]
$_SESSION (logged in)
[ '_sf2_attributes' => [ '_security_application' =>
<serialised token> …
] …
]
Register bag onKernelRequest
$bag = new NamespacedAttributeBag('App');$bag->setName('App'); $session->registerBag($bag);
SimplePreAuthenticatorpublic function createToken( Request $request, $providerKey){ $session = $request->getSession(); $bag = $session->getBag('App'); if (!$bag->has("user_id") || !$bag->has("username")) { throw new BadCredentialsException(‘!'); } …}
SimplePreAuthenticatorpublic function createToken( Request $request, $providerKey){ … return new PreAuthenticatedToken( 'anon.', [ "id" => $bag->has("user_id"), "username" => $bag->has("username") ], $providerKey );}
SimplePreAuthenticator
public function authenticateToken( TokenInterface $token, UserProviderInterface $userProvider, $providerKey){ $credentials = $token->getCredentials(); $user = $this ->userProvider ->loadUserByIdAndUsername( $credentials[“id"], $credentials[“username"] ); … }
The bundle
TheodoEvolutionSessionBundle
Alternative way
Single Sign On
Templating
Templating
Rewrite pros
•green field
•no old code to manage
Rewrite cons
•data import
•time to market
•two sites to maintain
Migration pros
•time to market
•gradual learning
•fade out of old code
•no data migration
Migration cons
•spaghetti code for long time
•learn how to pair two technologies
Thank You
References and credits
https://github.com/wimg/PHPCompatibility https://github.com/LExpress/symfony1 http://symfony.com/doc/current/cookbook/doctrine/reverse_engineering.html https://github.com/theodo/TheodoEvolutionSessionBundle https://github.com/marfillaster/ButterweedSF1EmbedderBundle https://www.flickr.com/photos/clairity/1267539354 https://www.flickr.com/photos/usfwshq/6777513684