PHPSymfony - Blog par un programmeur pour les programmeurs

To content | To menu | To search

Tuesday, March 11 2014

[PHP] Modifier la configuration de PHP pour l'exécution d'un script en CLI

Souvent, alors que j'utilise composer sur mon poste de dev, mon script plante car la mémoire est saturée. Pour ce genre d'opérations, il est possible de modifier des configurations du php.ini via l'argument -d (ou --define). Cela donne par exemple :

php -d memory_limit=-1 composer.phar update

[PHP][BEHAT] Utilisation de Behat/Mink pour tester une application web

Au cours du développement d'un projet chez un client, nous avions la volonté de pouvoir livrer plus vite nos versions applicatives. Cela devait nous permettre de pousser rapidement nos fix en production mais également de réduire le time to market des fonctionnalités clés. Difficile de ne pas être d'accord avec cette idée. Quand nous l'avons présenté aux référents Agile, il nous ont alors répondu : "Ok si vous améliorez la qualité de vos livraisons". Nous avons alors fais le point sur comment améliorer la qualité générale de nos livrables. Un point qui est ressorti de ces réunions est la lourdeur de certaines phases de tests.
En effet, nous avons dans notre cycle une phase de préparation pendant laquelle nos testeurs se chargent de vérifier le bon fonctionnement des anciennes et des nouvelles fonctionnalités. Cette phase, avant la mise en production, est très longue (2 semaines) et est essentiellement manuelle. Nous avons alors décidé de mettre en place du test fonctionnel automatisé. Nous avons alors étudié et mis en place la solution behat : http://behat.org/

Présentation


Behat


Behat est un framework pour faire du BDD. BDD pour “Behavior Driven Development”. Qu’est-ce que le Behavior Driven Development ?

Je citerai l’article sur Wikipédia (http://fr.wikipedia.org/wiki/Behavior_Driven_Development) : Il s’agit d’une « méthode Agile qui encourage la collaboration entre les développeurs, les responsables qualité, les intervenants non techniques participants à un projet logiciel. »

« Le processus BDD met en avant le langage naturel et les interactions dans le processus de développement logiciel. Les développeurs utilisant le BDD utilisent leur langue maternelle en combinaison avec le langage du domaine Domaine Driven Design pour décrire l'objectif et le bénéfice de leur code. Cela permet aux développeurs de se concentrer sur les raisons pour lesquelles le code doit être créé, plutôt que les détails techniques, et minimise la traduction entre le langage technique dans laquelle le code est écrit et le domaine de la langue parlée par les entreprises, les utilisateurs, les intervenants, la gestion de projet… ».

En synthétisant : on écrit des scénarios en langage lisible par l’homme qui décrit le comportement de l’application. Ces scénarios peuvent être testés automatiquement sur l’application. Et c’est super.

En PHP, il existe Behat : http://behat.org/

Behat permet de faire du BDD en PHP. C’est un utilitaire à lancer en ligne de commande. Il permet de tester le fonctionnement d’une application. Sur leur site internet, ils proposent un exemple avec la fonction « ls ».

Behat seul ne permet pas de tester des applications avec UI. Il est rare qu’un client vous demande une application en ligne de commande ; le plus souvent le produit en question va être constitué de pages web, d’interfaces graphiques riches. Qu’à cela ne tienne, il existe un outil que l’on couplera avec Behat, il s’agit de Mink.



Mink

Dans une application web, l’une des parties les plus importantes est le navigateur. C’est dans le navigateur que l’internaute va interagir avec l’application. Afin de garantir que notre application fonctionne correctement, nous devons la tester. Nous devons donc trouver un moyen de simuler les interactions d’un navigateur web avec notre application. C’est ici que Mink entre en jeu.

Avant de rentrer dans le détail de Mink, il faut savoir qu’il existe un grand nombre d’émulateurs de navigateurs (Ex : Goutte, Selenium, Sahi), qu’on appellera browser emulators (cela fait plus classe en soirée).  Ils font le même job mais de manière différente. Ils se comportent différemment et disposent d’API différentes. Il existe par ailleurs 2 types de browser emulators.

-          Les émulateurs sans tête (headless)

Ces types d’émulateurs sont simples à installer et à configurer. Par exemple de Goutte de Fabien Potencier. Ils permettent d’envoyer des requêtes HTTP à un serveur web et parsent la réponse. Ils peuvent être facilement lancés sur un serveur sans UI en mode console et sont très rapides. Cependant, le gros inconvénient est qu’ils ne gèrent pas le javascript. Il n’est donc pas possible de tester des interfaces graphiques riches avec du contenu en Ajax.  Néanmoins, ce type d’émulateur peut être pratique pour tester des APIs REST.

-          Les émulateurs qui contrôlent les navigateurs

Ces émulateurs permettent de contrôler de véritables navigateurs. Ce type d’émulateurs va permettre de simuler les interactions d’un utilisateur sur le navigateur. Selenium et Sahi sont 2 exemples connus et utilisés. Ce type d’émulateur à l’avantage de proposer le support du javascript et de l’Ajax pour tester les applications web riches. En revanche, il faut que les navigateurs soient installés sur la machine, la configuration est moins aisée et les tests seront plus longs que les émulateurs headless.

Chaque type d’émulateur a ses avantages et ses inconvénients. De ce fait, il faut les utiliser tous les deux pour tester efficacement. Et Mink va nous le permettre.

Mink permet de travailler avec les différents émulateurs via la notion de driver. Mink est une couche d’abstraction d’émulateur de navigateurs. Il masque les subtilités des différents émulateurs à travers une API unique et simple.




Installation du projet


Pour présenter l'utilisation de behat, j'ai créé un repo sous github https://github.com/guiguiboy/session-behat avec une application sous Silex (histoire d'avoir un site qui ne réponde pas systématiquement Hello world".


mkdir /var/www/session-behat
git clone https://github.com/guiguiboy/session-behat .
php composer.phar install


Pour ceux qui ne sont pas inspirés, vous trouverez dans le dossier doc/ un exemple de virtual host Apache.

Essayez de faire tourner l'application sur votre navigateur. Si il y a un problème il faut le résoudre avant de passer à l'étape de mise en place des tests fonctionnels.


Utilisation

Commencez par vous placer dans le répertoire de votre projet. Sur mon poste, cela donne :

cd /var/www/session-behat

Vous trouverez l'arbo suivante :
  • src : les sources de la web app
  • bin : les exécutables
  • doc : la documentation
  • test : où sont entreposés les fichiers de tests et de config de behat

Ensuite, vous pourrez lancer les tests. Mais d'abord, commençons déjà par voir le dictionnaire de behat. On utilisera l'argument -dl :

php bin/behat -c test/functional/behat.yml -dl

NB : on doit indiquer le path vers le fichier de configuration behat.yml avec l'argument -c si on travaille depuis la racine du projet. On obtient une liste de toutes les étapes que nous pouvons utiliser de manière builtin dans nos fichiers de feature.

Toutes ces étapes pourront être indiquées dans nos fichiers de feature.

Nous allons voir notre premier fichier de feature : un test sur la connexion. J'ai créé des routes /login accessible en GET et en POST pour contrôler les identifiants d'un utilisateur. J'ai identifié des identifiants corrects qui sont guiguiboy:plop (non, ce n'est pas mon vrai mot de passe). J'ai créé le fichier de feature suivant :

[10:15]user@gm21:/var/www/session-behat$ cat test/functional/features/login.feature
Feature: Login

  Scenario: Scenario with bad login values provided
    Given I am on "/login"
    When I fill in "login[username]" with "pwet"
    And I fill in "login[password]" with "lolilol"
    And I press "login[submit]"
    Then I should see "bad login/password provided"

  Scenario: Scenario with correct login values provided
    Given I am on "/login"
    When I fill in "login[username]" with "guiguiboy"
    And I fill in "login[password]" with "plop"
    And I press "login[submit]"
    Then I should see "Welcome, guiguiboy"



Ce fichier de feature est composé de 2 tests : un premier qui doit permettre de contrôler l'affichage d'une erreur si le sidentifiants de connexion ne sont pas reconnus, puis un deuxième permettant de contrôler le bon affichage d'une chaîne lorsque les identifiants sont corrects. Les différentes étapes sont simples, pour identifier les champs du formulaire, on utilise leur name (il est également possible de les identifier par id, label ou valeur).

Pour lancer les tests, on utilise la commande :

php bin/behat -c test/functional/behat.yml

Par défaut, tous les fichiers de feature seront joués. Il est cependant possible de ne spécifier qu'un seul fichier en le passant directement en argument :

php bin/behat -c test/functional/behat.yml test/functional/features/login.feature

Behat affiche alors un rapport sur la suite de tests. Une étape validée est affichée en vert, une étape en erreur est affichée en rouge et des indications sont données.

Un pro-tip, pour "debogger" un test, il est possible d'utiliser 3 étapes built in :
 Then /^print current URL$/
 Then /^print last response$/
 Then /^show last response$/

Ces deux dernières étapes permettent alors d'afficher le code HTML au moment du test, qui permettra de comprendre pourquoi une étape échoue.

Cet article est simple et présente vraiment la base du fonctionnement de behat/mink. Il existe d'autres fonctions très utiles comme les :
  •  scenario outline
  •  possibilité de définir ces propres étapes,
  • mise en place de backgrounds,
  • ...
Ils feront (ou pas !) l'objet d'articles supplémentaires sur ce blog ...

Tuesday, November 26 2013

[PHP] Réflexion - Un requêteur SQL en live sur vos applications PHP : la fausse bonne idée

Au cours d'un de mes projets PHP, un client nous a demandé la possibilité de pouvoir exécuter des requêtes SQL en live via une interface dans l'administration de son site.
Le but n'était pas de recoder un PHPMyAdmin mais uniquement de pouvoir faire des requêtes de type SELECT pour récupérer rapidement des données sans l'intervention des développeurs. Les données s'affichant dans un tableau HTML ou au format CSV. Enfin, notre client souhaitait que les requêtes puissent être enregistrées afin de pouvoir les rejouer à la demande.
Cette fonctionnalité partait d'un bon sentiment, via cette interface, on pouvait sortir rapidement des données. Cela évitait de rentrer dans une phase de développement et réduisait le time to market.

Mais ne tombez pas dans le piège de ce développement.
Voilà les principales raisons qui font du requêteur SQL en live une mauvaise fonctionnalité.

La sécurité de la BDD

La sécurité est bel et bien le premier point à considérer. En tant que développeur, nous nous devons d'assurer le bon fonctionnement de l'application sur laquelle nous travaillons.
De ce fait, même si l'on se dit que l'on veut exécuter uniquement des requêtes de type SELECT, comment peut on s'en assurer ?
Comment peut-on éviter qu'un utilisateur ne contourne le système en faisant des requêtes de type INSERT, ou pire, des DELETE, ALTER TABLE, TRUNCATE, DROP ... ?
On commence alors à chercher un pattern de requête autorisées. Si celles-ci commencent par des commandes interdites, alors c'est qu'elles ne sont pas autorisées.
C'est le développement que la plupart des développeurs feront. Tant et si bien que si la requête SQL est préfixée par un commentaire, /**/, celle-ci sera tout de même exécutée.
Il faudra alors penser à encapsuler les requêtes formulées dans une connexion particulière avec des droits restreints et à switcher correctement entre vos connexions.

Impacts sur la performance de votre site

Une requête SQL avec une erreur de syntaxe est rapidement détectée et n'a pas beaucoup d'impacts négatifs. En revanche, les erreurs de logique sont plus dangereuses. Imaginez qu'un opérateur saisisse une requête qui fait le produit cartésien de 2 tables avec plusieurs milliers de lignes sans clause de LIMIT. Le serveur de base de données va être fortement sollicité pour effectuer le calcul. Les performances de votre site risquent donc d'être directement impactées.

La mémoire elle aussi fortement sollicitée. Si vous utilisez le driver mysqlnd pour PHP, vous savez que les résultats d'une requête sont directement stockées dans la mémoire de PHP. Ainsi, si votre requête vous retourne de nombreux résultats et que vous récupérer la totalité du resultset, vous risquez de saturer la mémoire allouée à PHP.

La sécurité des données

Toujours dans le thème de la sécurité, il faut également considérer la sécurité des données. Vous avez un SI et vous avez défini des droits dans ce SI.
Un utilisateur lambda ne peut pas avoir accès à certaines informations. Avec un système de requêteur SQL en live, vous prenez le risque que certaines personnes non autorisées puissent accéder à des informations sensibles.

Posez vous la question suivante : êtes-vous complètement irréprochables et est-ce que toutes vos données critiques sont masquées ? Par exemple, est-ce que vous stockez en clair les mots de passe de vos clients ? Si oui, c'est vraiment très mal et vous devriez sérieusement songer à changer cela (!). Mais la n'est pas la question, il y a des données sensibles qui peuvent être exploitées par une personne mal intentionnée.

On peut éventuellement restreindre l'accès en lecture à certaines tables à certains utilisateurs. Cependant, cette configuration devra être directement faite sur le SGBD.
Vous perdrez alors en souplesse au niveau de votre application. Pire, vous devrez systématiquement vous demander, à chaque modification de schéma, quels utilisateurs peuvent accéder à vos nouvelles tables pour ensuite transmettre l'information à vos administrateurs système.

Les données exportées sont fausses à un instant t

Qui mieux que les développeurs savent comment sont stockées et organisées les données dans une base ? Personne. Surtout pas le client, qui la plupart du temps n'a aucune connaissance technique.
Du coup, vous aurez besoin d'un développeur pour vous aider à faire une requête. Sans son aide, vous prenez le risque de sortir des mauvaises informations.
En effet, vous n'avez peut-être pas vu ce champs deleted_at qui permet de supprimer logiquement une ligne. Ce qui fait que votre requête ne donnera pas les résultats que vous attendiez.
Ainsi, alors que vous pensiez gagner du temps, vous aller demander à votre développeur de vous sortir telle ou telle requête entre 2 développements.
Vous n'allez de plus certainement pas créer de demande écrite pour une simple requête SQL. Le temps que votre développeur y consacrera n'apparaitra nulle part.

Les données exportées sont fausses à un instant t+1

C'est encore pire lorsque les requêtes sont enregistrées pour être rejouées à la demande. Comme vous le savez, la base de données est liée à une application. Pour que l'application puisse proposer de nouvelles fonctionnalités, la base de données doit évoluer en conséquence.
De ce fait, une requête formulée à un instant t risque de ne plus être valable à un instant t+1.
Par ailleurs, vous n'aurez aucun système pour vous assurer que les données retournées par une requête sont correctes. Vous ne pourrez que tester la syntaxe de votre ordre SQL. Vous ne pourrez pas tester le résultat attendu, comme vous pourriez le faire avec un test unitaire.

Conclusion

Personnellement, pour toutes les raisons évoquées ci-dessus, je décourage fortement l'utilisation d'une fonction de requêteur SQL en live dans votre application. Je recommande le développement d'interfaces spécifiques pour sortir les données en fonction des besoins. En développant ces interfaces, vous allez écrire des tests, unitaires et fonctionnels. Ces tests permettront de garantir le bon fonctionnement de votre application au fur et à mesure des évolutions fonctionnelles. Vous maitriserez mieux le code applicatif.
Vous pourrez également utiliser d'autres outils d'intelligence économique comme Business Objects qui vous offriront plus de possibilités. Néanmoins, le déploiement de ces outils a un coût.

Tuesday, October 22 2013

[PHP] Utiliser firePHP pour debugger les applis sous Firefox

Cela faisait un petit moment que je voulais regarder ce que c'était et comment ça marchait. J'ai profité d'avoir un peu de temps pour installer FirePHP. Voici comment j'ai procédé :

Il y a des actions à réaliser côté client et côté serveur :

Côté client

  • Installer firefox
  • Installer l'extension Firebug via le gestionnaire d'add-ons Firefox : https://addons.mozilla.org/en-US/firefox/addon/firebug/?src=search
  • Installer l'extension FirePHP via le gestionnaire d'add-ons Firefox : https://addons.mozilla.org/en-US/firefox/addon/firephp/

Côté serveur

wget http://www.firephp.org/DownloadRelease/FirePHPLibrary-FirePHPCore-X.X.X

Attention : X.X.X à remplacer par la version souhaitée

unzip FirePHPLibrary-FirePHPCore-0.3.2.1

copiez le dossier créé dans un répertoire auquel votre projet PHP peut accéder

Dans votre script, insérez les lignes suivantes


Si vous lancez votre navigateur, vous verrez alors apparaitre un message de debug dans votre Firebug.

Pour plus d'informations sur FirePHP : http://www.firephp.org/HQ/Use.htm

Je précise que FirePHP n'est à utiliser que sur l'environnement de développement de votre projet.
Vous devez être capables, par configuration, de désactiver son utilisation en fonction de vos environnements.

Cela faisait un petit moment que je voulais regarder cette extension et comment l’implémenter (il n'est jamais trop tard !).
Personnellement, je vais essayer pour voir ce que ça donne. Mais, dans une certaine mesure uniquement. En effet, je me suis familiarisé depuis peu avec le TDD et j'ai de moins en moins besoin de ce genre d'outils.

Monday, September 30 2013

Activer / désactiver phpmyadmin sur un serveur Debian par script

Voici un petit script qui permet d'activer / désactiver phpmyadmin sur un serveur de type Debian.


Pour l'activation  :

./pma.sh "up"


Pour la désactivation :

./pma.sh "down"

Sunday, May 12 2013

Demarrer avec un Raspberry Pi Model B

Dans cet article, je vais expliquer comment faire ses premiers pas avec un raspberry pi. L'idée est de le connecter simplement à un réseau filaire existant et de pouvoir ensuite se connecter via putty.

Si vous ne savez ne savez pas ce qu'est un raspberry pi, je vous encourage à aller lire la page suivante qui présente bien le produit : www.kubii.fr/content/19-raspberry-pi-model-b-512-mo-presentation

Concernant l'achat, je suis passé par le site de farnell : http://fr.farnell.com/

J'y ai acheté :
  • raspberry pi Model B
  • un cable rj45 3M
  • un cable USB vers micro USB

Pour un cout de 52€ livraison comprise.

J'ai commandé la carte mémoire séparément. D'après la liste des cartes compatibles dispo sur http://elinux.org/RPi_SD_cards j'ai choisi, une SANDISK 16Go Extreme SDSDX-016G-X46 (en promo chez grosbill  : http://www.grosbill.com/4-sandisk_16go_extreme_-144521-numerique-memoire_sd_card)

Une fois, votre mini ordinateur et la carte mémoire recus, nous allons pouvoir commencer.

Tout d'abord, munissez vous d'un lecteur de carte SD (la plupart des PC portables en ont un intégré). Télécharger sur la page http://www.raspberrypi.org/downloads la raspbian wheezy. Téléchargez également Win32 diskimager http://sourceforge.net/projects/win32diskimager/?source=dlp si vous êtes sous windows. Insérez la carte dans le lecteur de cartes. Copiez l'image sur la carte grace au logiciel.

Insérez ensuite la carte dans le raspberry pi et connectez le à une source d'alimentation via le cable micro USB. Raccordez le RPi au réseau via le cable RJ45. La distribution installée est équipée d'un serveur ssh et les identifiants par défaut sont :

  • login : pi
  • password : raspberry

Si vous réseau est équipée d'un serveur DHCP, vous pourrez connaitre l'IP du raspberry pi via l'outil http://www.softperfect.com/products/networkscanner/

Les adresses MAC des raspberry pi débutent par B8:27:EB. Une fois l'adresse IP récupérée, vous pourrez vous connecter en ssh via un outil type putty.

Tuesday, October 2 2012

[Linux] Changer l'adresse MAC sous Ubuntu 11.10

Pour changer l'adresse MAC de votre carte réseau sous Ubuntu 11.10 :

  • Affichez les interfaces actives via la commande ifconfig :

[08:55]user@gm21:/home/guigui$ sudo ifconfig
eth0      Link encap:Ethernet  HWaddr 08:00:27:ef:b8:71
          inet adr:172.25.10.60  Bcast:172.25.11.255  Masque:255.255.254.0
          adr inet6: fe80::a00:27ff:feef:b871/64 Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:755506 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2746 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000
          RX bytes:81288103 (77.5 MiB)  TX bytes:354363 (346.0 KiB)

eth1      Link encap:Ethernet  HWaddr 08:00:27:0a:92:d5
          inet adr:192.168.56.101  Bcast:192.168.56.255  Masque:255.255.255.0
          adr inet6: fe80::a00:27ff:fe0a:92d5/64 Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:12701342 errors:0 dropped:0 overruns:0 frame:0
          TX packets:12732288 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000
          RX bytes:2847332852 (2.6 GiB)  TX bytes:2772354787 (2.5 GiB)

lo        Link encap:Boucle locale
          inet adr:127.0.0.1  Masque:255.0.0.0
          adr inet6: ::1/128 Scope:Hôte
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:1667 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1667 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:0
          RX bytes:116698 (113.9 KiB)  TX bytes:116698 (113.9 KiB)$


  • Arrétez  le network manager

sudo service network-manager stop


  • Changez l'adresse MAC de l'interface concernée

sudo ifconfig ethX hw ether xx:xx:xx:xx:xx:xx

où vous remplacerez ethX par une interface existante et xx:xx:xx:xx:xx:xx  par une adresse MAC valide (08:00:27:42:01:11 par exemple). S'il s'agissait d'une carte réseau sans fil, remplacez ethX par wlanX.

 

  • Relancer le network manager

sudo service network-manager start


  • Vérifier la prise en compte des informations par une nouvelle commande ifconfig

sudo ifconfig

Attention, cette modification est temporaire, lorsque vous redémarrerez votre machine, l'ancienne valeur de l'adresse MAC sera rétablie.

Friday, August 17 2012

[Javascript][Mootools] Requêtes périodiques avec Mootools

Dans le cadre d'un développement, j'ai du implémenter une fonctionnalité de requête périodique sur un de mes sites web pour vérifier par exemple :

  • la disponibilité d'un service,
  • un nombre de personnes connectées,
  • ...

J'ai donc créé la classe générique PeriodicRequest pour répondre à ce besoin. J'ai utilisé Mootools V1.4.5.

Classe PeriodicRequest

/**
 * Periodic request class
 *
 * @author gbretou
 */
var PeriodicRequest = new Class({
    Implements: Options,

    url : '',
    request: null,
    periodical: null,

    options: {
        method: 'GET',
        delay: '3000',
        onComplete: function(){},
        onCancel: function(){},
        onSuccess: function(){},
        onFailure: function(){}
    },

    /**
     * Constructor
     *
     * @param url
     * @param options
     */
    initialize: function(url, options)
    {
        this.setOptions(options);
        this.url     = url;
        this.method  = this.options.method;
        this.delay   = this.options.delay;
        this.request = new Request({
            method: this.method,
            url: this.url,
            onComplete: this.options.onComplete,
            onCancel: this.options.onCancel,
            onSuccess: this.options.onSuccess,
            onFailure: this.options.onFailure
        });
    },

    /**
     * Starts the timer
     */
    startTimer: function()
    {
        this.periodical = (function(){
            this.request.send();
        }.bind(this)).periodical(this.delay);
    },

    /**
     * Stops timer
     */
    stopTimer: function()
    {
        clearInterval(this.periodical);
    }
});

Utilisation

Le constructeur prend en paramètres 2 arguments :

  • url : la page à appeler,
  • data : un objet

L'objet data accepte les clés suivantes :

  • method : la méthode à utiliser (GET par défaut),
  • delay : le délai entre chaque requête (3000ms par défaut),
  • onComplete : callback lorsque la requête est terminée.

Dans le code suivant, nous allons créer une exécution périodique qui va requêter la page test.html toutes les 2 secondes. On affichera en console le contenu de la réponse.

data = {
            delay: 2000,
            onComplete: function(content) {console.log(content);}
};
var periodic = new PeriodicRequest('test.html', data);

La construction de l'objet ne lance pas automatiquement les actions périodiques. Vous devrez utiliser la méthode startTimer() :

periodic.startTimer();

Si vous souhaitez arrêtez l'exécution périodique, vous pourrez utiliser la méthode stopTimer() :

periodic.stopTimer();

Tuesday, July 24 2012

[PHP] PHP Cli Progress Bar

Voici une des dernière librairie que je viens de développer. Il s'agit d'une progress bar en PHP pour une utilisation en CLI.

Récupérer les sources

Pour récupérer les sources, vous pouvez utiliser :

  • git

git clone https://github.com/guiguiboy/PHP-CLI-Progress-Bar.git

Puis ajouter les lignes au début de votre script :

<?php

require_once 'ProgressBar/Manager.php';

require_once 'ProgressBar/Registry.php';


  • composer

Ajouter les lignes dans votre composer.json

    "require": {
                "php": ">=5.3.0",
                "guiguiboy/php-cli-progress-bar": "*"

    },
    "minimum-stability": "dev",

puis

php composer.phar install


Utilisation


Quick start

Une fois les fichiers chargés, rien de plus simple :

$progressBar = new \ProgressBar\Manager(0, 10);

for ($i = 0; $i <= 10; $i++)
{

    $progressBar->update($i);
    sleep(1);
}

Le code ci dessus va créer une progress bar pour un traitement de 10 éléments. A chaque itération, il attend 1 seconde.

1/10 [===>----------------------------------------------] 10.00% 00:00:09


Le format d'affichage par défaut est : %current%/%max% [%bar%] %percent%% %eta% où

  • %current% correspond à l'élément actuel
  • %max% correspond à l'élément max
  • %bar% correspond à la progress bar
  • %percent% correspond au pourcentage d'avancement
  • %eta% correspond à une estimation du temps de traitement restant

Configuration


Format

Vous pouvez changer le format de la progress bar avec la méthode setFormat :

$progressBar->setFormat('%current% |%bar%| %max%');


Longueur

La longueur se spécifie au niveau du constructeur. PAr défaut elle est de 80 caractères maximum. La taille de la progress bar se base sur cette information. PAr exemple :

$pb = new \ProgressBar\Manager(0, 20, 120);

Affichera :

1/20 [====>----------------------------------------------------------------------------------------] 5.00% 00:00:00


Caractères de la progress bar

Par défaut la progress bar utilisera les caractères suivants :

  • > : position courante
  • = : partie traitée
  • - : reste à faire

Ces caractères se configurent à l'initialisation. Par exemple :

$pb = new \ProgressBar\Manager(0, 20, 120, '-', ' ', ')');
$pb->update(5);

Affichera :

5/20 [-----------------------)                                                                    ] 25.00% 00:00:00


Extension


Ajouter des règles de remplacement


Vous pouvez ajouter des règles de remplacement : 

$pb = new Manager(0, 213);
$pb->setFormat('Progress : %current%/%max% [%bar%] %foo%');
$pb->addReplacementRule('%foo%', 70, function ($buffer, $registry) {return 'OK!';});
$pb->update(1);

Affichera :

Progress : 1/213 [>---------------------------------------------------] OK!


NB : Cette documentation ne sera pas forcément tenue à jour. Pour la version la plus récente, veuillez consulter le README.md du projet.

Tuesday, June 5 2012

Forum PHP 2012 organisé par l'AFUP : les slides des talks des conférenciers

Vous n'avez pas pu assister au Forum PHP 2012 ? Séance de rattrapage avec les slides des conférenciers :

PHP in 2012 : http://talks.php.net/show/afup12

Annotating with annotations : http://www.slideshare.net/rdohms/annotating-with-annotations-forumphp-2012

Coup de pied dans la LAMP : http://ternel.net/forumphp/Slideshow/#/author-bros

Anatomie du test : http://joind.in/talk/view/6440

Monitoring applicatif : http://joind.in/talk/view/6442

Anatomie, fonctionnement et performances de PHP : http://www.slideshare.net/jpauli/anatomie-et-performances-de-php

Lightning talk : Hip Hop for PHP : http://www.slideshare.net/gplessis/lightning-talk-hiphop-for-php

Gestion des dépendances dans un projet PHP : http://joind.in/talk/view/6458

Varnish pour les développeurs PHP : http://jrenard.info/blog/varnish-for-php-developers-the-slides.html

Drupal comme vous ne l'avez jamais vu : https://speakerdeck.com/u/gagarine/p/drupal-comme-vous-ne-lavez-jamais-vu

La qualité au delà du code : http://joind.in/talk/view/6454

Maitriser structures de données PHP 102 : http://www.slideshare.net/patrick.allaert/maitriser-les-structures-de-donnes-php-102-forum-paris-2012

Démons en PHP de inetd à ZeroMQ : http://www.slideshare.net/geekcto/dmons-en-php-de-inetd-zeromq

Accès concurrents et scalabilité : http://joind.in/6461

TDD avec ATOUM : http://joind.in/talk/view/6463

Vous trouverez en fichier joint mes notes prises lors de la conférence.

[EDIT]

Voici les slides du Symfony Live qui avait lieu peu de temps après : https://gist.github.com/2890651

Saturday, March 10 2012

restaurant.michelin.fr en ligne !

Le site http://restaurant.michelin.fr/ est désormais en ligne. Vous pouvez désormais rechercher des restaurants et laisser des avis.

Le site B2B est disponible à l'adresse : http://pro.restaurant.michelin.fr/

Friday, January 6 2012

[PHP] Spécifier une connexion en utf-8 avec Doctrine DBAL

Lorsque vous utilisez Doctrine DBAL (sur un projet Silex par exemple), voici comment spécifier l'encodage de la base de données.

Ajoutez une classe étendue de DoctrineServiceProvider et surchargez la méthode register(). Vous devrez utiliser le système d'event.

Exemple pour une base de données Mysql



use Silex\Application;
use Doctrine\DBAL\Event\Listeners\MysqlSessionInit;

class DoctrineServiceProvider extends \Silex\Provider\DoctrineServiceProvider
{
  public function register(Application $app)
  {
      parent::register($app);
      $app['db.event_manager']->addEventSubscriber(new MysqlSessionInit('utf8','utf8_unicode_ci'));
  }
}

Notez qu'avec le système d'event, vous pourrez réaliser d'autres actions telles que la mise en place de Behaviors.

Tuesday, December 20 2011

[Java] Débuter avec le développement Android

Ayant reçu un Samsung GALAXY S II (en remplacement de mon HTC Touch HD qui a rendu l’âme), j'ai tout de suite accroché au système des applications disponibles sur l'Android Market. Pour aller plus loin que la simple utilisation, J'ai voulu regarder comment créer une application sous Android.

Comme le veux la coutume, impossible de passer à coté de l'éternel "Hello World" quand on débute avec une nouvelle technologie. Voici les différentes étapes que j'ai suivi pour faire fonctionner mon application sous Windows : 

Installation

Tout d'abord, vous devez vous assurer d'avoir le JDK. Vous pourrez le télécharger ici : http://www.oracle.com/technetwork/java/javase/downloads/index.html

Sélectionnez la version qui correspond à votre OS.

Ensuite, vous devrez télécharger le SDK Android ici : http://developer.android.com/sdk/index.html

Pour simplifier l'utilisation du SDK en ligne de commande, nous allons renseigner le chemin vers le répertoire tools du SDK dans le PATH. Sous Windows 7, démarrer > rechercher "paramètres systèmes avancés". Cliquer sur le bouton "Variables d'environnement". Puis, dans la variables système, changer la valeur de la variable PATH pour y ajouter le path vers le dossier tools du SDK.

Configuration

Dans notre cas, nous considérerons l'utilisation de l'IDE Eclipse. Pour installer les plugins nécessaire, il faut suivre la documentation ici : http://developer.android.com/sdk/eclipse-adt.html#downloading

Notre environnement est désormais prêt et nous allons pouvoir créer l'application;

Commencez par créer un nouveau projet Android dans eclipse. Un Clic droit dans le package explorer puis sélectionnez "New" puis "Project ...".

Sélectionnez "Android Project" puis Next.

Choisissez un nom au projet, nous l'appellerons "HelloAndroid". Pour le reste de cet écran, le paramétrage par défaut est suffisant. Cliquez sur "Next".

Choisissez ensuite le SDK Android dans la liste. On prendra pour cet example la version 2.3.3. Cliquez ensuite sur "Next".

Enfin, choisissez un nom pour le package "HelloWorldAndroid" puis "Finish"

Dans le dernier écran, vous devrez remplir les champs :

  1. Application name : nom de l'application. Dans notre exemple nous utiliserons "Hello Android"
  2. Pckage name : il s'agit du namespace du package. Dans notre exemple, nous utiliserons "com.example.helloandroid". Veuillez à bien respecter les règles de nommage de package java. Sinon, vous pourrez rencontrer l'erreur "Aplication package XXX must have a minimum of 2 segments"
  3. Create Activity : laissez cette case cochée. Cela va permettre la génération d'une classe étendue de Activity sur laquelle nous allons travailler.

Cliquez sur "Finish".

Le système va ensuite générer le projet.

Dans src/com.example.helloandroid/HelloAndroid.java remplacez le code par  :

package com.example.helloandroid;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView tv = new TextView(this);
        tv.setText("Hello, Android");
        setContentView(tv);

    }
}

Astuce : sous eclipse, vous pouvez faire Ctrl Shift O pour importer les packages manquants.

Comme vous vous en doutez, l'idée est d'afficher "Hello, Android" sur l'écran.

Nous allons maintenant tester notre développement. Pour cela, nous allons utiliser un AVD.

Créer un AVD

AVD (pour Android Virtual Device) est un émulateur qui vous permet de choisir un type d'appareil compatible et de le configurer afin de tester votre application.
Nous allons donc créer un AVD pour vérifier que notre application fonctionne bien.

Pour créer un AVD, il faut commencer par installer de nouveaux packages.
Lancer l'utilitaire "SDK Manager" en tant qu'administrateur.
Puis cocher les packages correspondant aux différentes versions que vous souhaitez installer. Veuillez à bien sélectionner le SDK Android 2.3.3 car c'est celui ci que nous utiliserons pour tester notre application Hello world.

Cliquez ensuite sur "Install"
Notez l'emplacement du téléchargement.

Il faudra ensuite enregistrer le bon path pour les SDK dans eclipse. Pour cela, dans eclipse, allez dans Window > Preference > Android.

Hebergement gratuit d'image et photo


Ensuite, cliquez sur Window > AVD Manager
Cliquez sur "New ...". Le système affiche une nouvelle fenêtre vous permettant de configurer votre AVD
  1. name (obligatoire) : nom de l'AVD.
  2. target (obligatoire) : version du SDK d'android. Sélectionnez 2.3.3

Pour le reste, le paramétrage par défaut est suffisant.


Tester l'application


Tester l'application est très simple. Sous eclipse, cliquez sur le projet à tester, puis Run > Run.
Sélectionnez "Android application"
Sélectionner une source de capture pour la vidéo, puis OK.

Le système va lancer le programme dans l'émulateur. Un peu de patience, cela peut prendre du temps...

Vous obtenez alors le tant attendu "Hello, Android" !

Hebergement gratuit d'image et photo


Sources :
http://ydisanto.developpez.com/tutoriels/android/debuter/
http://developer.android.com/resources/tutorials/hello-world.html

Thursday, September 29 2011

[Mysql] Mise à jour d'un mot de passe root de mysql oublié

Vous avez oublié le mot de passe root de mysql ? Pas de problème, il existe une solution pour cela.

Commencez par couper votre serveur mysql :

/etc/init.d/mysqld stop

Lancez la commande suivante :

/usr/bin/mysqld_safe --skip-grant-tables &


Cela permettra de lancer le serveur en arrière plan. L'option --skip-grant-tables vous permet de passer outre l'authentification.
Attention : vous ne pourrez toutefois pas créer d'utilisateur, ni modifier de droits dans ce mode.

Connectez vous en tant que root :

mysql -uroot mysql

mysql> update user SET Password=PASSWORD('MyNewPassword') Where User='root';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

mysql> exit;
Bye

Vous pouvez maintenant killer le processus lancé en arrière plan. Puis, relancez votre serveur mysql :

/etc/init.d/mysqld start

Vérifiez que votre identifiant a bien été pris en compte :

mysql -uroot -pMyNewPassword

C'est terminé !

Tuesday, July 26 2011

[sf2] Symfony 2 : c'est parti !

C'est annoncé sur le blog officiel de Symfony : la release stable 2.0 est sortie ce jour.

A cette occasion, plusieurs "launch parties" sont prévues pour permettre aux développeurs de se réunir pour fêter cet évènement.

Vous trouverez la liste des évènements ici.

Pour ma part, ayant déjà joué un peu avec la béta, je vais préparer un nouveau projet et je posterai différents articles sur son utilisation.

Wednesday, July 13 2011

[Bash] Remplacement de caractères de retour à la ligne par des espaces

Supposons que vous ayez la liste suivante :

gbretou@gunix:~$ cat test.txt
aaa
bbb
ccc
ddd

Et que vous voulez un résultat sur une ligne, vous pouvez utiliser tr pour convertir vos sauts de ligne en espace simple.

gbretou@gunix:~$ cat test.txt | tr '
' ' '
aaa bbb ccc ddd ati@gm21:~$

Si vous avez un svn st long comme le bras et que vous commitez en ligne de commande :

svn st | sed 's/^[AM]//g' | tr '
' ' ' | xargs svn ci -m'My super fun commit message'

Si vous voulez éviter des fichiers, pensez au grep -v après le svn st ;)

Monday, June 13 2011

[Silex] Premiere utilisation du micro-framework PHP 5.3 Silex

Présentation de Silex


Silex est un micro-framework pour PHP 5.3 basé sur les briques de Symfony 2 et de Pimple. Ce framework a été créé dans l'objectif de proposer un système simple permettant de réaliser des applications simples sans avoir à utiliser un framework complexe.
Silex a été créé par Fabien Potencier et Igor Wiedler.

Le projet : Jeudi croissants


Nous allons réaliser un projet que vous devriez aimer (ou pas).
Tous les jeudis, nous avons la coutume suivante : une personne désignée doit ramener les croissants et les pains au chocolat pour tout le monde.
Cette personne change bien évidemment chaque semaine.
Nous allons donc réaliser une application se composant de 3 pages:
 - d'un front permettant de voir qui est la prochaine personne qui doit ramener les croissants
 - d'une page permettant d'augmenter la participation d'un membre
 - d'une page permettant de réduire la participation d'un membre

Cette application n'a pas pour objectif d'être un monstre en matière de sécurité. Il n y aura pas de gestion d'utilisateurs avec des permissions.
Cette coutume est bien évidemment ancrée sous le nom de jeudi croissants (donc pas de Tuesday crescent ou autres ...).

Mise en place du projet et installation de Silex


Commencons par configurer un vhost très simple :


NameVirtualHost *:80
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName jeudi.local
ServerAlias jeudi.local
DocumentRoot /var/www/jeudi/
DirectoryIndex index.php
<Directory /var/www/jeudi>
Options Indexes FollowSymLinks MultiViews
#AllowOverride None
Order allow,deny
allow from all
</Directory>
</VirtualHost>


N'oubliez pas de renseigner jeudi.local dans votre fichier /etc/hosts, d'activer le vhost et de recharger la conf apache.

Pour l'installation de Silex c'est très facile. Il suffit de récupérer le phar et c'est terminé !

cd /var/www/
mkdir jeudi
cd jeudi
wget "http://silex-project.org/get/silex.phar"

Nous aurons besoin du moteur de template Twig pour ce projet.
Twig n'étant pas inclus dans le phar, vous devrez donc le télécharger vous même. Copiez le dans /var/www/jeudi/vendor/twig

Concernant le stockage des données, nous allons utiliser un simple fichier texte data.txt.

cd /var/www/jeudi
mkdir data
touch data/data.txt

Voici un jeu de fixtures pour votre fichier :

gbretou:5
atisset:3
mwolff:3


Assurez vous que le fichier est accessible en écriture.
NB. Il est possible d'utiliser Doctrine 2 avec Silex. Il existe en effet une extension. Cela fera l'objet soit d'une mise à jour de cet article, soit d'un prochain article.

Le code de l'application


On commence donc avec un fichier index.php très basique :

//index.php

<?php
require_once __DIR__.'/silex.phar';
$app = new Silex\Application();
// definitions
$app->run();


Nous allons devoir ajouter un service pour gérer les intéractions avec le fichier. Nous allons donc créer la classe ParticipationService.
Ce service devra être enregistré dans l'application Silex.


//index.php
require_once __DIR__.'/ParticipationService.php';
//...
$app['participation'] = $app->share(function () {
    return new ParticipationService('./data/data.txt');
});

Vous trouverez la classe ParticipationService dans l'archive en pièce jointe.

Afin de pouvoir utiliser Twig, il faudra enregistrer l'extension :

$app->register(new Silex\Extension\TwigExtension(), array(
    'twig.path'       => __DIR__.'/views',
    'twig.class_path' => __DIR__.'/vendor/twig/lib',
));


twig.path doit pointer sur votre dossier contenant les fichiers templates.
twig.class_path doit pointer sur le dossier contenant les classes de Twig.

Nous avons défini que l'application devait se composer de 3 routes :
 - une route pour la visualisation
 - une route pour l'incrémentation
 - une route pour la décrémentation

La visualisation


Nous allons définir la route suivante :

//index.php
$app->get('/', function () use ($app) {
    return $app['twig']->render('index.twig', array(
        'name'           => 'admin',
        'participations' => $app['participation']->getParticipations(),
        'next'           => $app['participation']->getNext(),
        'matrix'         => $app['participation']->getParticipationMatrix(),
    ));
});


Avec ce code, lors de l'accès à la homepage du site, le système traitera le template index.twig dans lequel on injecte plusieurs variables : name, participations, next et matrix. Ces variables sont récupérées par le service ParticipationService.

L'incrémentation et la décrémentation


Voici les routes pour l'incrémentation et la décrémentation :

//index.php
$app->get('/increase/{name}/{count}', function ($name, $count) use ($app) {
    try
    {
        $app['participation']->increaseParticipation($name, $count);
        return $app['twig']->render('update.twig', array(
            'name' => $name,
        ));
    }
    catch (Exception $e)
    {
        return $app['twig']->render('error.twig', array(
            'exception' => $e->getMessage(),
        ));
    }
});

$app->get('/decrease/{name}/{count}', function ($name, $count) use ($app) {
    try
    {
        $app['participation']->decreaseParticipation($name, $count);
        return $app['twig']->render('update.twig', array(
            'name' => $name,
        ));
    }
    catch (Exception $e)
    {
        return $app['twig']->render('error.twig', array(
            'exception' => $e->getMessage(),
        ));
    }
});


Chaque route se compose de parties variables encadrées par des {}. Ces variables sont ensuite récupérées et transférées à la closure manuellement.
Afin d'avoir une application RESTFUL, on pourrait utiliser un put plutôt qu'un get pour les modifications.

Pour vous aider dans vos développements, pensez à activer le mode debug :

$app['debug'] = true;



Edit du 17/06/2011
:
Il est possible de catcher les exceptions mieux que je ne l'ai fait : voir la partie sur les error handlers. Je mettrai le code à jour.

Conclusion


Vous trouverez une archive contenant l'application. N'hésitez pas à me faire part de vos remarques.

J'ai aimé

 - La rapidité et la simplicité : on a une application avec un feedback visuel en quelques secondes
 - La documentation : pour mon utilisation, j'ai trouvé la documentation claire

Je n'ai pas aimé (?)

J'aurais souhaité utiliser des tâches pour mettre à jour les informations de participation. Cependant, d'après ce que j'ai constaté, Silex fournit une console qui ne prend en paramètre que 3 valeurs possibles : check, update et version. Je ne sais pas s'il est possible d'étendre ce système pour définir des tâches personnalisées dans le framework.


Comme l'a souligné scrumasteriswatchingu, si vous avez installé suhosin, vous devez ajouter la directive suivante dans votre php.ini
suhosin.executor.include.whitelist = phar


PS : La coutume Jeudi Croissant a été inventé mwolff ;p

L'application Jeudi croissant est disponible sur bitbucket : https://bitbucket.org/guiguiboy/jeudi-croissant

Edit du 16/03/2012
Le projet a été mis à jour pour utiliser la dernière version de Silex

Tuesday, May 24 2011

[PHP] Durée de vie et expiration des sessions

La gestion de l'expiration des sessions de PHP n'est pas triviale surtout quand on utilise le mécanisme par défaut.

En effet, les informations de sessions sont stockées dans des fichiers sur le serveur. Ces fichiers sont supprimés par une routine (garbage collector) qui détruit les données obsolètes.

Vous devrez jongler entre les 3 paramètres suivants :

 - session.gc_maxlifetime : spécifie une période en secondes pendant laquelle les données sont considérées comme corretes. Une fois cette période passée, les données sont alors considères comme obsolètes et sont susceptibles d'être supprimées par le garbage collector.

- session.gc_probability : à utiliser conjointement avec session.gc_divisor : permet de définir la probabilité que la routine de garbage collection soit lancé à chaque requête

- session.gc_divisor : à utiliser conjointement avec session.gc_probability : permet de définir une probabilité que la routine de garbage collection soit lancée à chaque requête

(Ces trois paramètres doivent être définis avant l'appel à session_start() si vous souhaitez modifier leur valeur via votre script)

Avec les valeurs par défaut (session.gc_probability = 1 et session.gc_divisor = 100), vous aurez donc une probabilité de 1% que la routine de garbage collection soit lancée à chaque requête.

Vous serez donc tenter d'ajuster les valeurs des deux derniers paramètres afin de maximiser la probabilité d'exécution de la routine de garbage collection.

Vous devez être conscient que le mécanisme est couteux et si vous avez de multiples sessions ouvertes sur votre site, une fréquence d'exécution trop importante peut entrainer des ralentissements

Wednesday, March 23 2011

[VI] Permissions root lors de l'édition d'un fichier verrouillé en écriture

Combien de fois, en éditant vos fichiers php.ini par exemple, avez-vous eu ce message  "Alerte: Modification d'un fichier en lecture seule"

Voici la solution : 

:w !sudo tee %

Avec cette commande, vous n'aurez plus besoin de quitter vi pour le relancer en root juste après.

Tuesday, February 22 2011

[Linux] Utilisation courante de tar

tar est un utilitaire d'archivage permettant de stocker et d'extraire des fichiers d'une archive.

Voici des cas d'utilisation de cet utilitaire que vous serez amenés à rencontrer le plus souvent dans votre (belle) vie de programmeur :


Création d'archive à partir d'un répertoire :

tar -cvf archive.tar %DIR%

Création d'archive gz à partir d'un répertoire :

tar -cvzf archive.tar.gz %DIR%

Création d'archive bzip2 à partir d'un répertoire :

tar -cvjf archive.tar.bz %DIR%


Extraction du contenu d'une archive dans le répertoire courant :

tar -xvf archive.tar

Extraction du fichier test.txt contenu dans une archive dans le répertoire courant :

tar -xvf archive.tar test.txt

Extraction du contenu d'une archive gzippée dans le dossier %DIR% : 

tar -xvzf archive.tar.gz -C %DIR%

Extraction du contenu d'une archive bzip2 dans le répertoire courant : 

tar -xvjf archive.tar.bz


Pour tous renseignements supplémentaires : man tar est votre ami.

- page 1 of 3