Dev

Développement PHP, actionScript2, Ruby, XHTML, CSS, javascript, etc.

Fil des billets - Fil des commentaires

vendredi 25 juillet 2008

Partager la session utilisateur entre Flash/Flex et symfony avec AmfPHP

Pour les besoins d'un projet récent, j'ai eu besoin de valider la possibilité de gérer l'authentification et l'accès à la session symfony (côté serveur) depuis une interface générée par Adobe Flex (en Flash, donc côté client).

Pour cela, j'ai utilisé la librairie AmfPHP en version 1.9beta2, certes pas très récente mais suffisament fonctionnelle pour satisfaire à ce besoin précis. Voyons comment ça se passe concrètement. L'avantage de la démonstration ci-dessous est qu'elle ne nécessite pas d'installer Flex puisque AmfPHP fournit un navigateur de services (browser) qui nous suffira pour valider notre concept.

Installation du plugin sfGuard

Je pars du principe que tout le monde a une installation de symfony 1.1 nanti d'une application main, ainsi qu'un projet et un virtual host apache fonctionnels pointant sur local.mademo.org. Si ce n'est pas le cas, voila de quoi vous mettre à jour.

On commence par installer le plugin sfGuard, qui se chargera de la persistance des droits et permissions utilisateurs en base de données, et fournira les utilitaires d'authentification et de manipulation de la session côté serveur :

$ ./symfony plugin:install sfGuardPlugin
$ ./symfony propel-build-all
$ ./symfony cc

On charge quelques données de test dans notre base de données nouvellement mise à jour :

$ mkdir data/fixtures 
$ cp plugins/sfGuardPlugin/data/fixtures.yml.sample data/fixtures/fixtures.yml
$ ./symfony propel:data-load main

Ce jeu de données de test nous fournit par défaut un compte admin (mot de passe admin) qui nous servira à tester notre service d'authentification.

Ensuite, il nous faut modifier notre classe apps/main/lib/myUser.php gérant la session utilisateur afin qu'elle étende désormais la classe sfGuardSecurityUser, fournie par le plugin sfGuard :

<?php
// Fichier apps/main/lib/myUser.php
class myUser extends sfGuardSecurityUser
{
}

Installation et configuration d'AmfPHP

Nous allons installer la librairie AmfPHP dans le sous-répertoire web/ de notre projet[1], et aménager quelque peu notre arborescence pour accueillir les services AmfPHP :

$ cd /path/to/project
$ svn export https://amfphp.svn.sourceforge.net/svnroot/amfphp/tags/1.9beta2 web/amfphp
$ mkdir lib/amfphp-services
$ mv web/amfphp/services/amfphp lib/amfphp-services/

Ceci fait, nous allons éditer plusieurs fichiers d'amfphp afin de l'adapter à notre environnement symfony. Tout d'abord, commençons par éditer la valeur de la variable $servicesPath dans le fichier web/amfphp/globals.php :

<?php
// ...
$servicesPath = dirname(__FILE__).'/../../lib/amfphp-services/';

Enfin, il nous faut "patcher"[2] le fichier web/amfphp/core/amf/app/Filters.php, qui initialise la session PHP sans définir le nom de la session. Ici, nous utiliserons le nom de la session symfony par défaut, "symfony" (ligne 105 du fichier) :

102     //Fix for godaddy not allowing ini_get
103     $sessionName = "PHPSESSID";
104   }
105   session_name('symfony');
106   session_start();
107   $session_id = session_id();

Création d'un service permettant le partage de la session utilisateur

Voila, nous pouvons maintenant créer un service de gestion de l'authentification, que nous nommerons pompeusement UserSessionService et que nous enregistrerons dans le fichier lib/amfphp-services/UserSessionService.php :

<?php
require_once dirname(__FILE__).'/../../config/ProjectConfiguration.class.php';
 
/**
 * This class tests the symfony session within an AmfPHP context
 *
 */
class UserSessionService
{
  /**
   * Symfony context
   * @var sfContext
   */
  protected $context = null;
  
  /**
   * Symfony session
   * @var sfGuardSecurityUser
   */
  protected $user = null;
  
  /**
   * Public constructor
   *
   */
  public function __construct()
  {
    $configuration = ProjectConfiguration::getApplicationConfiguration('main', 'dev', true);
    $this->context = sfContext::createInstance($configuration);
    $this->user = $this->context->getUser();
  }
  
  /**
   * Checks wheter user is authenticated or not
   *
   * @return boolean
   */
  public function isAuthenticated()
  {
    return $this->getUser()->isAuthenticated();
  }
  
  /**
   * Authenticates user
   *
   * @param  string  $username
   * @param  string  $password
   * @return boolean True if user has been successfully authenticated
   */
  public function login($username, $password)
  {
    if ($this->isAuthenticated())
    {
      return true;
    }
    
    $user = sfGuardUserPeer::retrieveByUsername($username);
    
    if (!is_null($user) && $user->checkPassword($password))
    {
      $this->getUser()->signIn($user);
      return true;
    }
    
    return false;
  }
  
  /**
   * Signs out a user 
   *
   */
  public function logout()
  {
    return $this->getUser()->signOut();
  }
  
  /**
   * Retrieves the current symfony context
   *
   * @return sfContext
   */
  protected function getContext()
  {
    return $this->context;
  }
  
  /**
   * Retrieves the current symfony user session
   *
   * @return sfGuardSecurityUser
   */
  protected function getUser()
  {
    return $this->user;
  }
}

Pour tester notre service, utilisons le navigateur de service proposé par AmfPHP. Pour cela, il faut lancer un navigateur sur http://local.mademo.org/amfphp/browser/index.html :

Naviagateur de services AmfPHP

Via cette interface, elle même réalisée en Flex, on peut tester les méthodes publiques définies dans notre service, manipuler les arguments, et constater que nous arrivons à nous authentifier et que nous accédons bien à la même session utilisateur que dans symfony : login, logout et test du statut d'authentification.

En conclusion

On pourrait aller beaucoup plus loin dans cet exemple, en proposant par exemple une classe proxy en ActionScript 3 représentant un utilisateur du système (dans notre cas, une instance de la classe sfGuardUser), cette dernière reproduisant tout ou partie de ses méthodes et propriétés, et donc d'utiliser l'ORM Propel directement depuis Flash... Je vous laisse faire vos tests si le coeur vous en dit.

D'autre part, même si la librairie AmfPHP semble un peu passée au niveau architecture, elle reste néanmoins très efficace pour publier des services PHP dans Flash au travers du protocole AMF. J'ai eu vent d'autres librairies comme WebORB ou SabreAMF, mais je ne sais pas vraiment ce qu'elles valent... Des avis dans l'assistance ?

Notes

[1] Du coup, on expose certains scripts AmfPHP, mais la librairie n'est malheureusement que prévue pour fonctionner en ce sens...

[2] Oui, c'est terriblement crade, je ne comprend d'ailleurs pas qu'AmfPHP n'aie pas prévu ce cas de figure...

mercredi 23 juillet 2008

Mes conventions de codage...

... sont celles des projets sur lesquels je me greffe. C'est en effet pour moi une forme de respect que d'appliquer les standards de codage partagés par une communauté (ou une équipe) de développeurs : ainsi, on maximise les chances de se comprendre et on minimise les coûteuses phases de communication entre geeks introvertis[1] :p

En effet, rien de plus pénible que de reprendre le code de quelqu'un qui a pris des libertés avec des conventions établies à ce niveau, l'apothéose étant obtenue avec ce genre de code :

<?php
  class Ma_superClasse {
 
    function dire_coucou ( $popol) {
  echo 'coucou ' . $popol   . ' !' ;
}
     function DireAuRevoir($Popol )
{ print "Au revoir $Popol !";
     }
  }

Je force bien évidemment ici le trait, mais tout le monde est déjà tombé sur ce genre de code illisible, qui multiplie par 10 votre temps d'intervention sur ce dernier et divise par 1000 votre passion pour la TMA.

Bien entendu, il peut arriver de produire du code sur un projet ne nécessitant l'utilisation d'aucune brique logicielle existante. Auquel cas vous pouvez librement appliquer vos propres standards de codage, l'important étant ici qu'ils soient cohérents et constamment appliqués. S'il peuvent être ceux d'un projet open source existant reconnu, cela augmentera la sympathie potentielle à votre égard de futurs intervenants sur votre code ;)

Je noterai quand même en vrac quelques bonnes pratiques générales globalement reconnues et appréciées :

  • être explicite,
  • indenter son code,
  • documenter son code,
  • à choisir entre les deux, privilégier la lisibilité à la concision,
  • utiliser des noms de variables, de classes, de méthodes, de fonctions et d'arguments parlants,
  • utiliser des noms anglophones,
  • utiliser des motifs de conception connus.

Personnellement, j'ai mes petites préférences et tout comme Oncle Tom - qui m'a gentiment refilé cette chaîne[2] - j'ai tendance à appliquer les standards de codage de symfony, que je trouve homogènes et cohérents. Mais ce sont là bien évidemment essentiellement des questions de goûts et de couleurs.

Notes

[1] Voire les trolls genre les tabulations ça pue, vive l'indentation à trois espaces...

[2] Salopard, ça va se payer ! ;-)

dimanche 20 juillet 2008

Debug PHP facile avec Firefox, Firebug et FirePHP

Tous ceux qui ont déjà eux à batailler avec du code javascript connaissent certainement la fabuleuse extension Firebug pour Firefox. L'outil propose une console permettant d'examiner l'environnement d'exécution javascript mais aussi HTML et CSS de n'importe quelle page web.

FirePHP est une autre extension qui a pour but de proposer le même service mais pour le langage PHP. L'extension repose elle-même sur Firebug et propose, une fois installée, l'affichage dans la console des messages de debug émis depuis vos scripts PHP :

Démo FirePHP

Une fois l'extension Firefox installée, pour pouvoir envoyer un message de log dans la console depuis vos scripts, il faut utiliser une librairie spécifique PHP fournie téléchargeable depuis la page d'accueil du projet FirePHP. Cette librairie très simple est d'ailleurs documentée ici. Une fois l'archive récupérée, décompressez-la et appelez FirePHP de cette façon depuis un script PHP standard :

require_once '/path/to/firephp/lib/FirePHPCore/FirePHP.class.php';
 
$f = FirePHP::getInstance(true);
$f->fb('Hello FirePHP console', FirePHP::INFO);
$f->fb(array('hello' => 'how are you?'));
$f->fb(array('hello' => array('how', 'are', 'you')));
$f->fb(array('foo', 'bar'), 'Results', FirePHP::WARN);
 
$o = new stdClass();
$o->foo = 'foofoo';
$o->bar = 'barbar';
 
$f->fb($o);

Pour envoyer les informations de debug à la console, la librairie PHP envoie les données sérialisées au format JSON dans un entête HTTP personnalisé dédié (X-FirePHP-Data). Ainsi, aucune interférence n'est possible avec vos scripts existants, la seule condition étant bien entendu de ne pas lancer la sortie standard PHP avant que ces entêtes aient été envoyés.

Données JSON passées dans un entête dédié

En bref, un outil génialement simple et efficace.

lundi 30 juin 2008

Symfony 1.1 est dans les bacs

Nous venons de releaser symfony 1.1, le framework qui blanchit les dents et rafraîchit l'haleine.

C'est une étape importante puisque l'architecture même du framework a été entièrement repensée, pour être notamment plus découplée, et favoriser ainsi l'extensibilité et la configurabilité. Voici les autres améliorations phares de cette nouvelle version :

  • Le nouveau framework de formulaires, entièrement orienté objet, permet une réutilisation maximale des éléments et en garantit la sécurité de façon transparente,
  • Le système de gestion de tâches en ligne de commande est une grosse tuerie, il vous permet de créer des scripts en ligne de commande avec gestion de l'aide, des arguments et des options, le tout avec une sortie en couleur au besoin !
  • Le parser YAML a été réécrit from scratch et propose maintenant des messages d'erreurs contextualisés, bien utile à l'heure du débogage,
  • La gestion des formats web permet à votre application de fournir une réponse et un format différents en fonction du type de requête entrante ; concrètement, vous gardez le même contrôleur mais vous proposez des vues adaptées dans le format qui va bien (html, xml, json, etc.) :)
  • La gestion des plugins a été entièrement revue et propose maintenant une totalement API compatible avec le standard PEAR, et gère notamment les dépendances,
  • La couche ORM Propel, toujours en version 1.2, est maintenant proposée sous la forme d'un plugin (activé par défaut), permettant ainsi plus facilement d'utiliser Doctrine ou Propel 1.3 si vous préferez,
  • Le routing a été grandement amélioré tant en performances qu'en extensibilité et souplesse de configuration,
  • La compatibilité avec symfony 1.0 est assurée grâce au plugin sfCompat10, fourni par défaut et activable en une ligne de configuration,
  • Enfin, plus de 8500 tests unitaires et fonctionnels garantissent la stabilité de l'API et préviennent de l'apparition de régressions.

Voila, l'accouchement fut un peu long mais le bébé est là, rose et bien portant. Et il n'attend plus que vous pour jouer avec lui :p

mardi 24 juin 2008

ESC_RAWquerie

Le saviez-vous ? Dans symfony, il est possible de désactiver l'échappement des contenus en passant la constante ESC_RAW en dernier paramètre d'une fonction ou méthode de classe depuis une template :

<?php echo $article->getTitle(ESC_RAW) ?>

Plutôt donc que :

<?php echo $article->getRawValue()->getTitle() ?>

Ou l'infâme :

<?php echo $sf_data->getRaw('article')->getTitle() ?>

Voila, c'est tout, c'était surtout l'occasion de faire en titre le pire jeu de mot de ce blog, et de pousser le billet d'humeur sur la RATP un peu plus bas sur la home.

dimanche 8 juin 2008

Un plugin jQuery pour générer des plans automatiquement

Avec un temps pareil, autant coder des plugins jQuery, non ?

Quelques heures de boulot plus tard, jqplanize[1] est né et permet de générer automatiquement une table des matières extraite de la hiérarchie induite de la séquence des titres d'un document HTML[2].

La doc est sur la page d'accueil du projet (hébergée chez Google), ou plus exhaustivement sur cette page dédiée du présent site, avec quelques exemples live.

Le code semble fonctionner sous Firefox 3 RC2 et Safari 3.1, mais ça ne marche curieusement pas sous Opera 9, et j'ai pas testé sous IE donc attendez vous à des updates[3].

Vivement l'été.

Notes

[1] Non mais vous avez déjà vu un nom aussi naze ?

[2] Si tant est que le balisage des titres soit sémantique, hein.

[3] Ou pas, bien entendu.

dimanche 4 mai 2008

QOTD

Une belle quote comme ça, je me la note dans un coin pour la ressortir la prochaine fois qu'on trolle sur les performances du framework X ou du langage Y :

Languages, libraries and frameworks don't scale. Architectures do.

En français approchant :

Les langages de programmation, les librairies et les frameworks ne tiennent pas la charge. Les architectures, si.

(via, apparement attribuable à Cal Henderson)

lundi 24 mars 2008

Le code de Symfonians en open source

Tout est dans le titre, je viens de mettre en place une instance Trac/SVN dédiée au projet, le code est libéré et publié sous licence CC-BY-SA 3.0.

Je pense que la publication du code source de l'application est essentiellement utile dans le cadre d'un apprentissage de Symfony, le code étant tout de même très orienté métier.

Edit : Rapido, un petit screencast d'installation de Symfonians à partir des sources depuis subversion :

Télécharger le screencast (QuickTime MOV, ~88 Mo)

- page 1 de 8