Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
DE LEGACY À SYMFONY
SYMFONY MONTRÉAL
16 JUIN 2016
Etienne Lachance
@elachance
QUI SUIS-JE
Sys admin de formation
Programmeur depuis ~10 ans
Propriétaire de elcweb.ca
Consultation en entreprise
Program...
CONTEXT
Evolution d'un projet "Legacy" sans affecter la productivité mais en
introduisant les bonne pratique d’un nouveau ...
PAR LEGACY J'ENTEND
peu/pas de documentation
peu/pas de tests
Code procédural
Code spaghetti
Duplication de code (copier-c...
OBJECTIF
Permet de refactoriser le code petit peu par petit peu mais d'avoir des
avantages rapidement.
ATTENTION!
IL EST FORTEMENT RECOMMANDÉ D’ÉCRIRE DES TESTS
AUTOMATISÉS.
PHPUnit
Behat
3 MÉTHODOLOGIES
1. PARALLEL
simple a implémenter (mod_rewrite)
aucune communication direct entre les 2 applications
utilisation de la BD o...
2. PROXY
l'utilisateur voie uniquement une application (Symfony)
necessite plus de travail pour la mise en place
"wrapper"...
3. INTÉGRATION
On veut changer la structure fondamental du code actuel.
une seule application
OPTION PRÉSENTÉ
QU'EST-CE QUE SYMFONY?
Symfony est *
une collection de composante
un framework applicatif
une philosophy
une communauté
Sy...
EXEMPLE
STRUCTURE DE FICHIER
BONNE PRATIQUE: PLACER LE CODE A L'EXTERIEUR DU RÉPERTOIRE PUBLIQUE.
STRUCTURE RÉVISÉ
MODIFIONS LE CODE
EXEMPLE DE TYPE "INCLUDE-CEPTION"
<?php
// index.php
include("includes/common.php");
include("includes/config.php");
$mod ...
LEGACY CONTROLLER
namespace AppBundleController;
use ...;
class LegacyController extends Controller
{
/** @Route("/index.p...
LEGACY CONTROLLER - SUITE
// Include module file
$filename = __DIR__ . '/../modules/' . $mod . '.php';
if (!file_exists($f...
RÉCAPITULATION
déplacer les chiers a l'extérieur du répertoire publique
véri er si le module exist
si le module n'existe p...
PROCHAINE ÉTAPES
Authenti cation/Autorisation (incluant la session)
Isolation de la base de donnée (Repository)
Vue (Templ...
AUTHENTIFICATION/AUTORISATION
AUTHENTIFICATION
Qui es-tu ?
AUTORISATION
Quels sont les accès / droits
MAINTENANT DANS SYMFONY
UTILISATEUR
Implement UserInterface
<?php
namespace AppBundleSecurityUser;
use ...
class User implements UserInterface
{
p...
PROVIDER
Est responsable d'aller chercher l'utilisateur
Implement UserProviderInterface
namespace AppBundleSecurityUser;
u...
ENCODER
Responsable d'encoder et de valider un mot de passe
namespace AppBundleSecurityEncoder;
use SymfonyComponentSecuri...
CONFIGURATION
app/con g/services.yml
services:
app.user_provider:
class: AppBundleSecurityUserUserProvider
app.security.en...
RECOMMANDATION
Utilisation d'un algorithme plus sécuritaire comme bcrypt ou sha512
Convertion des mots de passes "on the y"
DOCTRINE
Permet de représenter en Objet et non en tables.
DOCTRINE / REPOSITORY
Dans le contexte de Doctrine, un Repository est utilisé pour allez chercher
l'information
Centralise...
LEGACY
<?php
// ...
$conn = mysql_connect($db_host, $db_user, $db_password);
if (!$conn) {
echo "Unable to connect to DB: ...
LEGACY - SUITE
$result = mysql_query($sql);
if (!$result) {
echo "Could not successfully run query ($sql) from DB: " . mys...
GÉNÉRATION D'ENTITÉ A PARTIR D'UNE BASE DE DONNÉ
EXISTANTE
$ php bin/console doctrine:mapping:import --force AcmeBlogBundl...
REPOSITORY
namespace AppBundleEntity;
use DoctrineORMEntityRepository;
class ProductRepository extends EntityRepository
{
...
UTILISATION DANS UN CONTROLLEUR
class ProductController extends Controller
{
/**
* @Route("/product.php/category/{id}")
*/...
TEMPLATES
Ne pas convertir les templates pour le "Fun"
On peut retourner:
une réponse directement (aucun template)
un temp...
EXEMPLE D'UN CONTROLLEUR QUI RETOURNE UNE RÉPONSE
BASÉ SUR UN TEMPLATE
public function indexAction()
{
// some logic to re...
EXEMPLE D'UN TEMPLATE EN TWIG
{# app/Resources/views/blog/index.html.twig #}
{% extends 'blog/layout.html.twig' %}
{% bloc...
EXEMPLE DE SMARTY ET TWIG
protected function renderSmarty($template, $outputData)
{
$smarty = new SmartyEngine();
$smarty-...
QUESTIONS ?
MERCI!
http://elcweb.ca
http://etiennelachance.com
@elachance
https://github.com/estheban
https://www.linkedin.com/in/elac...
Upcoming SlideShare
Loading in …5
×

De legacy à symfony

52 views

Published on

Vous avez déjà travaillé avec de vieux projet PHP (3,4), du “include-ception” ou tout simplement un framework non PSR-0? Voici un retour sur les étapes employé dans différent cas de migration de “legacy” vers Symfony 2.

  • Be the first to comment

  • Be the first to like this

De legacy à symfony

  1. 1. DE LEGACY À SYMFONY SYMFONY MONTRÉAL 16 JUIN 2016 Etienne Lachance @elachance
  2. 2. QUI SUIS-JE Sys admin de formation Programmeur depuis ~10 ans Propriétaire de elcweb.ca Consultation en entreprise Programmation Hébergement spécialisé Fin de l'auto promotion
  3. 3. CONTEXT Evolution d'un projet "Legacy" sans affecter la productivité mais en introduisant les bonne pratique d’un nouveau framework.
  4. 4. PAR LEGACY J'ENTEND peu/pas de documentation peu/pas de tests Code procédural Code spaghetti Duplication de code (copier-coller) Include-ception Couplage de responsabilité ... Non SOLID
  5. 5. OBJECTIF Permet de refactoriser le code petit peu par petit peu mais d'avoir des avantages rapidement.
  6. 6. ATTENTION! IL EST FORTEMENT RECOMMANDÉ D’ÉCRIRE DES TESTS AUTOMATISÉS. PHPUnit Behat
  7. 7. 3 MÉTHODOLOGIES
  8. 8. 1. PARALLEL simple a implémenter (mod_rewrite) aucune communication direct entre les 2 applications utilisation de la BD ou Redis pour l'échange entre les 2 apps peu/pas d'impacte sur l'application 1
  9. 9. 2. PROXY l'utilisateur voie uniquement une application (Symfony) necessite plus de travail pour la mise en place "wrapper" pour les requêtes a l'application Legacy authenti cation sécurité entre les 2 applications peu/pas d'impacte sur l'application Legacy
  10. 10. 3. INTÉGRATION On veut changer la structure fondamental du code actuel. une seule application OPTION PRÉSENTÉ
  11. 11. QU'EST-CE QUE SYMFONY? Symfony est * une collection de composante un framework applicatif une philosophy une communauté Symfony est a la base un framework HTTP GESTION DES REQUÊTE / RÉPONSE source: http://symfony.com/what-is-symfony
  12. 12. EXEMPLE
  13. 13. STRUCTURE DE FICHIER BONNE PRATIQUE: PLACER LE CODE A L'EXTERIEUR DU RÉPERTOIRE PUBLIQUE.
  14. 14. STRUCTURE RÉVISÉ
  15. 15. MODIFIONS LE CODE
  16. 16. EXEMPLE DE TYPE "INCLUDE-CEPTION" <?php // index.php include("includes/common.php"); include("includes/config.php"); $mod = $_GET['mod']; if ($mod=="" || !preg_match('/^[A-Za-z1-90_]+$/Ui',$mod)) $mod = "dashboard"; include ("modules/".$mod.".php"); aucun namespace logique basé sur include() / require()
  17. 17. LEGACY CONTROLLER namespace AppBundleController; use ...; class LegacyController extends Controller { /** @Route("/index.php") */ public function legacyAction() { // __DIR__ == 'src/AppBundle/Controller' include __DIR__ . '/../includes/common.php'; include __DIR__ . '/../includes/config.php'; // @todo: renommé $mod pour $module $mod = $_GET['mod']; if ($mod=="" || !preg_match('/^[A-Za-z1-90_]+$/Ui', $mod)) { $mod = "dashboard"; }
  18. 18. LEGACY CONTROLLER - SUITE // Include module file $filename = __DIR__ . '/../modules/' . $mod . '.php'; if (!file_exists($filename)) { throw new NotFoundHttpException('Module ' . $mod . ' not found'); } // ob_start: Turn on output buffering ob_start(); include $filename; return new Response(ob_get_clean()); } }
  19. 19. RÉCAPITULATION déplacer les chiers a l'extérieur du répertoire publique véri er si le module exist si le module n'existe pas, retourne une erreur 404 encapsuler les "echo" du code legacy dans un objet Response
  20. 20. PROCHAINE ÉTAPES Authenti cation/Autorisation (incluant la session) Isolation de la base de donnée (Repository) Vue (Templates)
  21. 21. AUTHENTIFICATION/AUTORISATION AUTHENTIFICATION Qui es-tu ? AUTORISATION Quels sont les accès / droits
  22. 22. MAINTENANT DANS SYMFONY
  23. 23. UTILISATEUR Implement UserInterface <?php namespace AppBundleSecurityUser; use ... class User implements UserInterface { private $username; private $password; private $salt prirate $roles; ... }
  24. 24. PROVIDER Est responsable d'aller chercher l'utilisateur Implement UserProviderInterface namespace AppBundleSecurityUser; use ... class UserProvider implements UserProviderInterface { public function loadUserByUsername($username) { ... } public function refreshUser(UserInterface $user) { ... } public function supportsClass($class) { return $class === 'AppBundleSecurityUserUser'; } }
  25. 25. ENCODER Responsable d'encoder et de valider un mot de passe namespace AppBundleSecurityEncoder; use SymfonyComponentSecurityCoreEncoderBasePasswordEncoder; class LegacyMd5Encoder extends BasePasswordEncoder { public function isPasswordValid($encoded, $raw, $salt = null) :bool { return $this->comparePasswords(strtolower($encoded), strtolower($this->encodePassword($raw, $ } public function encodePassword($raw, $salt = null) :string { return md5($raw . 'secret_global_a_l_application'); } }
  26. 26. CONFIGURATION app/con g/services.yml services: app.user_provider: class: AppBundleSecurityUserUserProvider app.security.encoder.md5: class: AppBundleSecurityEncoderLegacyMd5Encoder app/con g/security.yml security: encoders: AppBundleSecurityUserUser: id: app.security.encoder.md5 providers: legacy: id: app.user_provider firewall: main: pattern: ^/ http_basic: ~
  27. 27. RECOMMANDATION Utilisation d'un algorithme plus sécuritaire comme bcrypt ou sha512 Convertion des mots de passes "on the y"
  28. 28. DOCTRINE Permet de représenter en Objet et non en tables.
  29. 29. DOCTRINE / REPOSITORY Dans le contexte de Doctrine, un Repository est utilisé pour allez chercher l'information Centraliser les requêtes SQL Isoler les requêtes du "controlleur" Classi er par context d'objet (Utilisateur/Produit/Client)
  30. 30. LEGACY <?php // ... $conn = mysql_connect($db_host, $db_user, $db_password); if (!$conn) { echo "Unable to connect to DB: " . mysql_error(); exit; } if (!mysql_select_db($dbname)) { echo "Unable to select mydbname: " . mysql_error(); exit; } $sql = "SELECT * FROM products WHERE category = ".mysql_real_escape_string($_GET['cat']).";"
  31. 31. LEGACY - SUITE $result = mysql_query($sql); if (!$result) { echo "Could not successfully run query ($sql) from DB: " . mysql_error(); exit; } if (mysql_num_rows($result) == 0) { echo "No rows found, nothing to print so am exiting"; exit; } $data = array(); while ($row = mysql_fetch_assoc($result)) { $data = $row; } mysql_free_result($result);
  32. 32. GÉNÉRATION D'ENTITÉ A PARTIR D'UNE BASE DE DONNÉ EXISTANTE $ php bin/console doctrine:mapping:import --force AcmeBlogBundle xml $ php bin/console doctrine:mapping:convert annotation ./src $ php bin/console doctrine:generate:entities AcmeBlogBundle http://symfony.com/doc/current/cookbook/doctrine/reverse_engineering.html
  33. 33. REPOSITORY namespace AppBundleEntity; use DoctrineORMEntityRepository; class ProductRepository extends EntityRepository { public function findByCategory($categoryId) { $sql = "SELECT * FROM products WHERE category = ".mysql_real_escape_string($categoryId $stmt = $this->getEntityManager()->getConnection()->prepare($sql); $stmt->execute(); return $stmt->findAll(); } } RawSQLTrait: https://gist.github.com/estheban/3eae41271f6cf5f3180a
  34. 34. UTILISATION DANS UN CONTROLLEUR class ProductController extends Controller { /** * @Route("/product.php/category/{id}") */ public function productByCategory(Category $category) { // throw 404 si pas de Catégorie trouvée $entityManager = $this->getDoctrine()->getManager(); return $entityManager ->getRepository("AppBundle:Product") ->findByCategory($category->getId()); } } Voir: @ParamConverter
  35. 35. TEMPLATES Ne pas convertir les templates pour le "Fun" On peut retourner: une réponse directement (aucun template) un template en php un template en twig (natif) un template en smarty un template en ...
  36. 36. EXEMPLE D'UN CONTROLLEUR QUI RETOURNE UNE RÉPONSE BASÉ SUR UN TEMPLATE public function indexAction() { // some logic to retrieve the blogs $blogs = ...; $this->render( 'AcmeBlogBundle:Blog:index.html.twig', array('blogs' => $blogs) ); }
  37. 37. EXEMPLE D'UN TEMPLATE EN TWIG {# app/Resources/views/blog/index.html.twig #} {% extends 'blog/layout.html.twig' %} {% block content %} {% for blogPost in blogs %} <h2>{{ blogPost.title }}</h2> <p>{{ blogPost.body }}</p> {% endfor %} {% endblock %}
  38. 38. EXEMPLE DE SMARTY ET TWIG protected function renderSmarty($template, $outputData) { $smarty = new SmartyEngine(); $smarty->assign($outputData); return $this->render( '@ElcwebLegacy/Default/default.html.twig', ['output' => $smarty->fetch($template)] ); } public function bobAction() { $outputData = ['foo' => 'bar']; return $this->renderSmarty('bob.tpl', $outputData); }
  39. 39. QUESTIONS ?
  40. 40. MERCI! http://elcweb.ca http://etiennelachance.com @elachance https://github.com/estheban https://www.linkedin.com/in/elachance

×