Christophe Le Bot

  • Navigation rapide
Pratique de la conception numérique

Derniers commentaires

  • Une excellente thèse sur Simile Exhibit
    • Olivier Rossel | Bonjour. Malgre les annees, avez vous toujours en memoire vos usages d’Exhibit? Je serais ravi d’en discuter avec vous. Cordialement, Olivier Rossel.
  • Utiliser le planificateur de tâches OVH (crontab) avec PHP
    • Max | Bonjour, comme faire pour appeler une version de PHP qui n’est plus proposée par défaut dans le manager ? J’essaie de lancer un cron avec php 5.3 mais le log affiche No such file...
    • Christophe | @absolument Merci pour ces précisions. Je n’ai pas mis en place de tâches cron récemment, mais j’ai reçu quelques notifications étranges d’OVH il y a quelques...
  • Récupérer le dernier auto-incrément MySQL avec PHP
    • Thy | Sujet toujours *très* utile en 2015 ! Je réponds à Serge tsimba qui récupère un « Resource id ». (Et à tous ceux qui ont le même souci mais qui ne le disent pas) :)...
  • Régler l’heure de serveurs virtuels sous Debian
    • Ares_XL | Il semble que sur Débian la commande « tzconfig &ra quo; soit dépréciée et remplacée par : « dpkg-reconfigure tzdata » elle donne accès à une...
 

Archives de la catégorie
Bases de données

Sauvons MySQL !

Save MySQL

En janvier 2008, le rachat de MySQL AB par Sun fut un événement majeur et plutôt bien accueilli. Mais l’ambience était tout autre après le rachat de Sun par Oracle. J’émettais de fortes réserves sur la survie de MySQL dans le giron de l’ambitieuse société californienne.

Les craintes se sont confirmées et une grande bataille se joue en ce moment pour sauver MySQL. Michael « Monty » Widenius, le créateur de MySQL, a lui-même tiré la sonnette d’alarme en décembre et a anticipé les problèmes que vont rencontrer les utilisateurs de MYSQL.

Sans réponses claires et pérennes d’Oracle, la communauté s’organise et une pétition a été lancée pour convaincre les pouvoirs publics et les autorités de régulation des marchés d’étudier le cas de la fusion Oracle-Sun-MySQL. Libre à vous de décider de l’avenir de MySQL, mais le danger est, à mon avis, bien réel. J’attends d’être convaincu du contraire…

Pour en savoir plus : Save MySQL !

Barcamp PHP toulousain : la synthèse

Bacrcamp PHP Toulouse

Jeudi dernier se tenait le Barcamp PHP Cheese & Wine. Même si le vin et le fromage ont été très appréciés, nous n’étions pas venus (seulement) pour ça. Alors pour les absents qui ont eu tort de l’être, voici une petite synthèse de cette longue soirée.

Un vrai barcamp

Premier bon point : c’est un vrai barcamp où les participants se présentent et définissent le contenu des ateliers. Tous les barcamps ne respectent pas cette règle de base… Xavier Gorse, président de l’AFUP, a donc joué le rôle de « maître de cérémonie » pour établir le programme d’après les souhaits de chacun  :

  • PHP et sécurité
  • PHP 5.3
  • Déploiement d’applications PHP
  • PHP et les bases de données « NoSQL »
  • Outillage PHP
  • PHP et testing
  • Frameworks PHP

Pour ma part, j’ai participé aux ateliers :

  • Déploiement d’applications PHP
  • PHP et les bases de données « NoSQL »
  • PHP et testing

Je limite donc mon article à ces sujets, sachant que d’autres synthèses ont déjà été publiées :

Déploiement d’applications PHP

Cet atelier a mis en évidence la difficulté de déployer des applications web en général (technologies nombreuses et environnement hétérogène). Tous les outils existants ont été passés en revue, du paquet Linux (.deb) aux outils spécifiques à PHP (PEAR, Phing, Phar) en passant par des intermédiaires parfois plus adaptés (makefile, Puppet, Capistrano, Ant). Deux groupes de participants étaient clairement représentés, avec des besoins très différents :

  • Déploiement d’une solution sur un parc important et hétérogène (cas des éditeurs de solutions, comme Linagora avec OBM)
  • Déploiement d’un projet sur-mesure sur un ou quelques serveurs, mais très fréquemment et avec des contraintes d’intégration de contenus externes (cas des agences web, avec plusieurs déploiements par jour).

Dans le premier cas, la difficulté est d’identifier la configuration des serveurs cibles et de préparer les paquets d’installation correspondants (.deb pour chaque distribution Linux, .msi pour chaque version de Windows, etc.), tout en assurant la compatibilité des données sans toujours les connaître (tests de régression).

Dans le second cas, il faut savoir intégrer pendant le déploiement les données du site en exploitation (base de données, templates gérés par un web designer externe, etc.), avec d’éventuelles transformations (ETL, Extract Transform Load).

J’ai ajouté qu’un déploiement ne se limite pas à la livraison de la partie applicative mais doit aussi savoir traiter la mise à jour des outils liés au projet (plate-forme de gestion de tickets, extranet, feuille de route, tests, sauvegardes, alertes, etc.).

En dehors de PEAR, trés utilisé et qui est un outil de déploiement à l’origine, j’ai une préférence pour Ant + Phing et Capistrano.

Bases de données « NoSQL »

Là, on entre dans une autre dimension. Les bases « NoSQL » sont des bases de données non relationnelles. En gros, on ne retrouve pas le schéma habituel « tables contenant des champs et étant reliées entre elles ». L’avantage est d’obtenir des performances exceptionnelles sur des entrepôts de données énormes. Parmi les acteurs majeurs qui développent et utilisent des bases « NoSQL », on peut citer : Google (projet Big Table qui a inspiré le projet Cassandra), Facebook ou Linkedin.

Si on revient à la dure réalité d’un acteur de dimension modeste, on constate que ces technologies émergentes et prometteuses sont encore très spécifiques. Les bases relationnelles ont de beaux jours devant elles. La difficulté est notamment de réintégrer dans l’application PHP ce qui fait la force des systèmes SQL : sélection, jointures, intégrité référentielle, etc. Le volet testing des projets en prend un coup…

PHP et testing

Atelier en petit comité (6 personnes), en concurrence déloyale avec l’atelier Frameworks qui a fait le plein ! Nous avons tenté de lister les types de tests liés à une application web, en dépassant autant que possible la simple vue du développeur :

  • Tests unitaires (PHP et Javascript)
  • Tests fonctionnels
  • Tests d’IHM (via Selenium Core, Selenium RC et Selenium IDE)
  • Tests de recette
  • Tests de non régression
  • Tests de performance
  • Tests de charge
  • Tests de conformité (normes, W3C, accessibilité, etc.)
  • Tests ergonomiques (tri par cartes, paper prototyping, tests utilisateurs, etc.)
  • A/B testing

Les échanges sur nos expériences ont été très instructifs. Nous étions tous d’accord pour insister sur la définition précise des cas d’utilisation qui facilite la gestion des tests pendant toute la durée du projet avec le client. D’où une phase de spécifications sérieuse qui conditionne la qualité du travail livré. Certains tests peuvent faire l’objet de validation contractuelle, comme les wireframes issus de tests ergonomiques qui servent ensuite de feuille de route aux intégrateurs et développeurs.

La difficulté avec les tests, c’est de savoir placer le curseur pour ne pas s’y noyer. Il n’est pas réaliste d’appliquer les tests de façon exhaustive. C’est un idéal en contradiction avec les budgets et les délais imposés en pratique. Il faut donc savoir réaliser les bons tests, au bon endroit et au bon moment. Par exemple, sur le calcul des prix d’un panier de site e-commerce, sur l’intégration des données lors d’un couplage entre deux systèmes, sur l’ergonomie d’une interface riche, etc.

En résumé

Une excellente soirée qui a largement dépassée les 5 heures prévues ! L’accueil de Linagora et de l’AFUP était parfait, l’ambiance très sympathique et le niveau des échanges très pointu. Il y a des gens qui savent faire des choses avec PHP en Midi-Pyrénées ! Je pense qu’on remettra ça sous peu. Prochaine étape : le Bargento, lundi 9 novembre à Paris. Je serai présent avec l’équipe de l’AFUP pour organiser et animer cette journée qui s’annonce exceptionnelle. Et à la suite, le Forum PHP 2009, tout aussi exceptionnel. Sur ce coup-là, je déclare forfait. Il faut bien travailler un peu !

Oracle acquiert Sun

Oracle acquiert Sun

Le rachat de MySQL par Sun était déjà un événement pour moi l’an dernier. Alors je ne sais plus quels mots utiliser après le rachat de Sun par Oracle aujourd’hui…

Sachant comment finissent les entreprises qui passent entre les mains d’Oracle, il reste une sacrée incertitude sur certains projets (Java et MySQL pour commencer).

Piloter et sécuriser ses bases de données avec MySQL Proxy

MySQL Proxy

MySQL Proxy, proposé par MySQL AB, est le genre d’outils dont le rôle semble si évident qu’on se demande pourquoi il n’existait pas avant. Comme tout proxy, il récupère les requêtes destinées à la base de données, effectue des traitements et adresse une requête modifiée au serveur MySQL. Bien entendu, l’opération inverse est aussi gérée : MySQL Proxy sait récupérer un résultat de requête et le transformer avant de l’adresser au client initial.

Fonctionnalités

Après cette petite présentation, on peut se demander pourquoi on aurait besoin d’un proxy, puisque les services applicatifs (s’ils sont correctement architecturés et codés…) devraient envoyer une requête optimisée au serveur MySQL. Une petite liste des fonctionnalités finit par convaincre de son intérêt :

  • Analyse des requêtes
  • Filtrage et modification des requêtes et des résultats
  • Injection de requêtes
  • Load balancing
  • Exécution de scripts (basés sur le langage LUA)

Cas d’utilisation

Dans la pratique, on peut imaginer les situations suivantes :

  • Corriger les erreurs de syntaxe communes.
  • Garantir le fonctionnement d’anciennes applications utilisant des requêtes non supportées.
  • Supprimer du résultat un mot de passe demandé dans la requête.
  • Récupérer et fusionner avec le résultat des données externes à la base de données (fichiers, flux XML, RPC…)
  • Router les requêtes vers un autre serveur MySQL ou vers une autre base de données du même serveur.
  • Empiler un lot de requêtes et les contrôler avant de mettre à jour les tables.
  • Dupliquer les requêtes en temps réel sur deux bases différentes.
  • Exécuter un script shell avant ou après des modifications dans la base de données.
  • Interdire certains motifs de requêtes.
  • Verrouiller des tables selon le contexte.
  • Contrer les injections SQL non désirées.

On le voit, les possibilités sont infinies et ne dépendent que des contextes de production et de l’imagination des développeurs. MySQL Proxy permet surtout d’éviter d’intégrer la gestion de l’exploitation des bases de données MySQL dans les services applicatifs. Voilà enfin une saine séparation du travail du développeur métier et de l’administrateur MySQL !

Mise en oeuvre

MySQL Proxy est encore en version alpha et son fonctionnement n’est garanti que pour les versions MySQL 5.0.x et ultérieures.

Pour découvrir ses possibilités, O’Reilly Network propose un didacticiel clair et très fourni : Getting Started with MySQL Proxy.

Source : Linuxfr.org

ACID (Atomicity, Consistency, Isolation, Durability)

Alors que je suis en pleine période des acronymes de quatre lettres (CRUD et UDOD), je découvre ce matin un bon résumé d’ACID sur Le Journal du Net.

J’ajouterais à l’article de Xavier Borderie que ces quatre attributs d’une transaction de données (Atomicity, Consistency, Isolation, Durability) ne se limitent pas aux bases de données. Ils sont utiles dans bien d’autres contextes, comme par exemple la gestion d’un annaire LDAP ou les transactions entre un repository Subversion et ses utilisateurs. Sauf à considérer qu’il s’agit là de bases de données au sens large et théorique…

Par contre, il y a un contexte où ACID mériterait d’être strictement appliqué, c’est celui des transactions entre un serveur et un navigateur web ! Nous en sommes très loin car le socle technique n’a pas été prévu pour cela. Par exemple, on ne peut toujours pas savoir quand un utilisateur quitte un site ou une application web, ce qui est gênant quand l’application web fait des opérations par étapes successives avec confirmation de l’utilisateur pour chacune d’elles. Doit-on annuler les étapes précédentes ? Si oui, sous quel délai ? De plus, laisser une session ouverte sur le serveur représente un risque de sécurité.

On peut simuler la persistance des sessions et des objets par les cookies, mais la fiabilité n’est pas au rendez-vous (sauf en dérivant le navigateur par des extensions particulières, comme des applets Java). On peut faire un semblant d’atomicité en plaçant les pages web dans un buffer avant de les servir (pour éviter une validation d’un formulaire incomplet par exemple). On peut assurer la cohérence en contrôlant rigoureusement les requêtes HTTP. On peut gérer l’isolation par l’utilisation de « sémaphores maison ». Quant à la durabilité, je ne vois pas comment l’obtenir…

Il manque donc un maillon pour simplifier le développement et gérer la robustesse des applications web : si ACID est bien présent entre le serveur web (front office) et les entrepôts de données (SGBD, annuaires, systèmes de fichiers, échanges de données…), son absence entre le navigateur et le serveur web rend le développement et l’exploitation difficiles.

Récupérer le dernier auto-incrément MySQL avec PHP

Quand un script ajoute un enregistrement dans une table MySQL, il est parfois utile d’en mettre d’autres à jour. Et pour cela, on peut avoir besoin de récupérer le dernier identifiant d’auto-increment. La fonction PHP mysql_insert_id() le fera très bien… sous certaines conditions.

Petit rappel sur les connexions MySQL

Avant de faire l’opération, voici un petit rappel du comportement des connexions MySQL ouvertes par PHP.

La plupart du temps, la connexion avec MySQL est établie par la fonction PHP mysql_connect(). Si un deuxième appel est fait avec les mêmes arguments, la première connexion sera à nouveau utilisée. Le paramètre new_link modifie ce comportement et permet à mysql_connect() d’ouvrir à chaque fois une nouvelle connexion, même si mysql_connect() a été appelée avec les mêmes paramètres.

Lorsque le script utilise une connexion persistante (par la fonction PHP mysql_pconnect()), le comportement est le même, à deux différences (importantes !) près :

  • Lors de la connexion, la fonction essaie de trouver une connexion persistante déjà ouverte, avec le même nom d’utilisateur et le même mot de passe. Dans ce cas, son identifiant est retourné sans ouvrir de nouvelle connexion.
  • La connexion au serveur MySQL n’est pas coupée à la fin du script. Le lien est conservé pour un prochain accès, même si on utilise la fonction mysql_close().

Au passage, notez que les connexions persitantes ne fonctionnent qu’avec PHP en version module.

Et l’auto-increment dans tout ça ?

L’auto-increment sera retourné par la fonction PHP mysql_insert_id(), mais il y a des risques de ne pas récupérer celui que vous attendez !

mysql_insert_id() retourne le dernier identifiant généré par un champ de type AUTO_INCREMENT, sur la connexion MySQL courante ou sur la connexion spécifiée par le paramètre link_identifier. Mais les surprises sont nombreuses :

  • Si votre colonne AUTO_INCREMENT est une colonne de type BIGINT, la valeur retournée par mysql_insert_id() sera incorrecte. À la place, il faut utiliser la fonction MySQL LAST_INSERT_ID() directement dans une requête SQL.
  • mysql_insert_id() ne fonctionne pas avec un REPLACE.
  • Si vous utilisez dans votre requête SQL un INSERT IGNORE en précisant un identifiant existant dans la table, l’enregistrement ne sera pas créé, ce qui est normal puisqu’il ne peut y avoir deux identifiants égaux. Par contre, mysql_insert_id() vous retournera l’auto-increment suivant, comme si l’enregistrement avait été ajouté !
  • Si vous faites des insertions multiples (par exemple, INSERT INTO table1 (champ1, champs2) SELECT champ1, champs2 FROM table2 WHERE champ1=2), mysql_insert_id() retournera l’identifiant du premier enregistrement ajouté !

J’arrête là le massacre car il y a bien d’autres cas. Pour s’en sortir, il faut privilégier la fonction MySQL LAST_INSERT_ID() (placée directement dans une requête SQL) et verrouiller la table :

mysql_query("LOCK TABLES table_exemple WRITE");
mysql_query("SET AUTOCOMMIT = 0");
mysql_query("INSERT INTO table_exemple (champ1, champ2) VALUES  ('toto1','toto2')");
$mysql_id = mysql_query("SELECT LAST_INSERT_ID()");
mysql_query("COMMIT");
mysql_query("UNLOCK TABLES");

Formatage d’une durée avec PHP

PHP regorge de fonctions et de variables pour gérer le temps. Pourtant, quand on souhaite afficher une durée entre deux dates, il n’y a aucune fonction spécifique ! Voici comment s’en sortir.

Un peu de mathématiques

Le principe est d’utiliser un nombre entier, par exemple une durée exprimée en secondes. A vous de convertir la durée avec vos scripts PHP (la fonction mktime() est toute désignée).

Ensuite, quelques formules mathématiques simples donnent un tableau contenant toutes les valeurs de la durée dans les unités de temps que vous souhaitez utiliser :

$duration = 3695;

// Initialisation
$duration = abs($duration);
$converted_duration = array();

// Conversion en semaines
$converted_duration['weeks']['abs'] = floor($duration / (60*60*24*7));
$modulus = $duration % (60*60*24*7);

// Conversion en jours
$converted_duration['days']['abs'] = floor($duration / (60*60*24));
$converted_duration['days']['rel'] = floor($modulus / (60*60*24));
$modulus = $modulus % (60*60*24);

// Conversion en heures
$converted_duration['hours']['abs'] = floor($duration / (60*60));
$converted_duration['hours']['rel'] = floor($modulus / (60*60));
$modulus = $modulus % (60*60);

// Conversion en minutes
$converted_duration['minutes']['abs'] = floor($duration / 60);
$converted_duration['minutes']['rel'] = floor($modulus / 60);
$modulus = $modulus % 60;

// Conversion en secondes
$converted_duration['seconds']['abs'] = $duration;
$converted_duration['seconds']['rel'] = $modulus;

// Affichage
print_r( $converted_duration);

Ce code affichera le résultat suivant :

Array
   (
      [weeks] => Array
         (
            [abs] => 0
         )

      [days] => Array
         (
            [abs] => 0
            [rel] => 0
         )

      [hours] => Array
        (
            [abs] => 1
            [rel] => 1
         )

      [minutes] => Array
         (
            [abs] => 61
            [rel] => 1
         )

      [seconds] => Array
         (
            [abs] => 3695
            [rel] => 35
         )
   )

Vous obtenez un tableau avec le nombre de semaines, de jours, d’heures, de minutes et de secondes que vous pouvez ensuite utiliser pour l’affichage. Pour chaque unité de temps, la cellule [abs] du tableau stocke la durée totale, alors que la cellule [rel] stocke la durée restante (définie par le modulo).

Prendre la valeur absolue de la durée est capitale ! En effet, sans abs(), la fonction floor() donnera des résultats faux avec un durée négative. C’est logique puisqu’elle retourne toujours l’entier inférieur d’un nombre réel. Si la durée vaut +4.4, vous obtenez +4. Si la durée vaut -4.4, vous obtenez -5 !

Et si j’utilise une base de données ?

Si vos dates à comparer viennent d’une base de données, la conversion en timestamp peut se faire directement dans la requête SQL. Avec MySQL, les champs de type DATE, DATETIME ou TIME sont convertis par la fonction UNIX_TIMESTAMP(). Au passage, attention à la limite des champs TIME (de ‘-838:59:59’ à ‘838:59:59’) !

Si vous utilisez MySQL 5, la fonction TIMESTAMPDIFF() fera tout à votre place… sauf le formatage en PHP que je vous propose !

En guise de conclusion, consultez les pages PHP et MySQL concernant les fonctions de dates et d’heures qui vous épargneront des calculs inutiles :