PHP le mal-aimé

J’ai suivi une conf de Frédéric Bouchery au BreizhCamp la semaine dernière dont l’intitulé était « Pourquoi PHP ?« . Frédéric Bouchery n’est pas le dernier des manchots, puisqu’il est quand même lead dev chez CCM-Benchmark. Il était là pour défendre le langage PHP, qui s’en prend un peu plein la gueule dans le monde de l’informatique : largement critiqué, il est perçu comme un pseudo-langage fait pour les débutants ; les grandes personnes, elles, codent dans les vrais langages comme Java ou Ruby.

Les principales critiques portent sur deux points.

Le langage lui-même

Les fonctions en elles-mêmes manquent parfois de cohérence dans leur nommage (str_replace vs strpos), semblent plutôt useless (nl2br) ou sont très permissives (implode qui prend ses arguments dans les deux sens).

En outre pendant longtemps quelques mauvaises pratiques étaient possibles avec PHP, par exemple les super globales, qui permettaient des failles de sécurité grosses comme l’étoile de la mort (et si les super globales sont désormais derrière nous, il y en a d’autres).

Sa facilité d’utilisation

Sa force, sa prise en main très facile, est aussi son point faible. PHP est très simple à utiliser, donc les débutants l’utilisent beaucoup et font de la merde en barre avec. On trouve BEAUCOUP de petits scripts sur internet, des bouts de code « pour aider », qui sont juste horribles, pas optimisés, pas sécurisés, etc. Forcément, ça donne pas envie.

Mais comme je le disais à mes étudiants, si PHP permet de coder n’importe comment, on n’est pas obligé de coder n’importe comment, et en plus des standards de nommage, de code, etc, il existe plusieurs sites qui reprennent les bonnes pratiques, dont ces deux-là :
http://www.phptherightway.com/
http://www.php-fig.org/
Je ne les connaissais pas, je pense que je vais me plonger dedans avec plaisir.

J’étais heureuse d’entendre ses arguments, qui rejoignent et complètent les miens. En plus d’être fière d’être développeuse, je suis désormais fière d’être développeuse PHP 😉

Merci à Thomas Gratier et à Frédéric Bouchery pour les liens.

 

Génération de CSV, WTF ?

Parfois j’ai l’impression que le plus gros de mon boulot consiste à générer des fichiers CSV… J’ai donc une librairie toute prête, améliorée au fil des projets. Elle fonctionne, elle est testée, éprouvée par les ans, elle ne peut pas bugger, pas possible… Eh ben si, et on en apprend tous les jours.

Ce matin j’ai donc généré un CSV en php, avec une ligne d’entête en majuscules. Tout bêtement, ma première colonne est l’ID. J’ai donc mis ID dans ma cellule A1. Normal.

Mais en fait non.

En ouvrant mon CSV avec LibreOffice pour voir s’il s’était bien généré, rien dedans… Ouvert avec un éditeur de texte, le contenu est bien là, mais avec LibreOffice, rien.

Je teste en remplaçant ID par un lot d’insultes, et ça passe… ID serait-il un mot réservé ? ce serait con… et pourtant… Une recherche rapide me fait tomber sur un commentaire sur PHP.net. Le problème n’est pas nouveau et il concerne apparemment tous les tableurs. Si la cellule A1 contient ID en majuscule, le tableur ne lit pas la ligne. Eh ouais c’est comme ça ma brave dame.

Solution : remplacer ID par id, Id, n’importe quoi d’autre fera l’affaire.

Conclusion : insulter le programme est toujours utile pour pister les bugs.

 

Installer un cron chez 1and1

Cron ?

cron est le nom d’un programme qui permet aux utilisateurs des systèmes Unix d’exécuter automatiquement des scripts, des commandes ou des logiciels à une date et une heure spécifiées à l’avance, ou selon un cycle défini à l’avance.

http://fr.wikipedia.org/wiki/Cron

crontab est le nom du programme sous Unix (ou Linux) qui permet d’éditer des tables de configuration du programme cron.

http://fr.wikipedia.org/wiki/Crontab

L’autre jour, je m’amusais à installer un cron pour une petite appli  que je suis en train de développer.
Les crons, je connais, mais pas par coeur, et surtout pas la syntaxe. Comme j’installais ça chez 1and1, j’ai voulu bêtement suivre les instructions qu’ils donnaient. Evidemment, si je n’avais pas galéré pendant 3 jours, cette note de blog n’existerait pas…

Structure d’un cron

Pour commencer, je vous refais un cours vite fait sur la structure d’un cron. Vous aurez la même chose et mieux documenté ici.
Le fichier crontab est composé ainsi :
# m h dom mon dow command

  • m : minutes (0-59)
  • h : heures (0-23)
  • dom : day of month (1-31)
  • mon : month (1-12)
  • dow : day of week (0-6, 0 = dimanche, 1 = lundi, etc)
  • command : la commande à exécuter

Pour les minutes, heures, dom, dow et mois, plusieurs syntaxes sont possibles :

  • * : à chaque unité de temps
  • 5 : à chaque unité de temps 5 (j’ai mis 5, ça peut être n’importe quoi du moment que c’est dans l’intervalle 0-59 pour les minutes, 0-23 pour les heures…)
  • */5 : toutes les 5 unités de temps
  • 5,10 : les unités de temps 5 et 10
  • 5-10 : les unités de temps 5,6,7,8,9,10

C’est mieux avec des exemples (backup.sh est mon script de sauvegarde) :

5 * * * * backup.sh # backup.sh est exécuté toutes les heures passées de 5 minutes, tous les jours de tous les mois
*/5 * * * backup.sh # backup.sh est exécuté toutes les 5 minutes de toutes les heures, tous les jours, tous les mois
30 1 * * 6 # backup.sh est exécuté tous les samedi à 1h30
Inutile de préciser que la notation suivante est risquée :
* * * * * backup.sh # chaque minute de chaque heure...
La notation
30 1 * * * backup.sh >> log.txtou
30 1 * * * backup.sh > log.txtpermet de préciser que l’on souhaite que le résultat soit loggé dans le fichier log.txt. A défaut, le résultat de l’opération est envoyé par mail. Pour ne rien logger, spécifiez > /dev/null

Pendant ce temps-là, chez 1and1…

1and1 propose l’utilisation des crons à partir de leur pack pro. Les exemples donnés dans leur guide sont justes, mais ce ne sont pas des tutos, et les suivre aveuglément comme je l’ai fait, c’est s’exposer à ce que ça ne marche pas (ça m’apprendra, j’avais fait pareil pour SVN chez OVH).

Pour accéder au crontab, il faut se connecter en SSH sur l’hébergement, puis on peut éditer le fichier avec VI (de belles réjouissances en perspective…).
Pour vous connecter en SSH sous Windows, utilisez Putty. Pour utiliser VI, c’est par là (vous pouvez également suivre le tuto de 1and1).
Pour vos tests, je vous recommande chaudement de mettre en début de fichier
MAILTO=votre@adresse-email.com
A chaque exécution du cron vous serez prévenu du résultat.
Pour faire vos tests, mettez un intervalle de 5 minutes, c’est le minimum autorisé par l’hébergeur et ça vous évite d’attendre 24h entre deux tests.

Ça marche pas !

Bon, la première chose à faire quand on installe un cron, c’est d’appeler à la main le fichier qu’on veut exécuter avec le cron. Histoire de vérifier qu’il s’exécute bien, ça fait gagner du temps à tout le monde.
Ensuite, vérifiez les droits sur le fichier. Concrètement, si vous recevez un message « Permission denied », c’est que les droits ne sont pas bons. Vous pouvez modifier cela avec votre logiciel FTP ou en SSH. Un 777 devrait suffire 😉

Si vous appelez un fichier PHP
*/5 * * * * $HOME/test.php
vous aurez droit à :
?php: No such file or directory
syntax error near unexpected token blablabla

Le bougre n’a pas compris qu’il fallait interpréter PHP.

Essai suivant :
*/5 * * * * php $HOME/test.php
Normalement, y’a du mieux… sauf si
<b>Parse error</b>:  syntax error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or '}' in ...
Bon, là c’est pas de pot, vous avez utilisé du PHP5, par défaut il attend du PHP4 (qui a dit « boulet » ?)
La bonne syntaxe est la suivante :
*/5 * * * * /usr/local/bin/php5 $HOME/test.php

Voilou, c’est à vous !

Renvoyer une image avec php sous code igniter

Si, pour une raison ou pour une autre, vous ne souhaitez pas que les medias sur votre site soient récupérables, vous pouvez les renvoyer avec PHP et la fonction readfile.

Un bon tutoriel est disponible à cette adresse :
http://www.editeurjavascript.com/trucs/14,afficher_une_image_avec_du_php.php

Le double avantage de cette méthode est que l’image n’est pas enregistrable facilement (une capture d’écran est toujours possible, bien sûr), et que le visiteur ne peut pas savoir dans quel répertoire vous l’avez stockée.
Une autre raison pour laquelle vous voudriez utiliser cette méthode, pourrait être de vérifier l’identité de votre visiteur avant d’afficher l’image. J’y reviendrai plus bas.

J’ai voulu intégrer cette fonctionnalité sous Code Igniter, certains aménagements sont nécessaires pour arriver au même résultat.

Je pars du principe que $_GET est désactivé, ce qui est le schéma par défaut (et recommandé) sur CI. Mes medias sont enregistrés en base, j’ai donc un ID pour chacun. C’est avec cet ID que je vais les manipuler : je vais passer en paramètre l’ID du media à afficher.

Si vos medias ne sont pas enregistrés en base, vous devrez alors passer le nom de l’image en paramètre, sans les dossiers. En effet, si vous passez un dossier, vous aurez une url du genre
controller/methode/mon_dossier/monimage.gif, ce qui vous oblige à récupérer votre dossier d’un côté, le nom de l’image de l’autre.
Cette méthode vous fait perdre l’intérêt premier de la manoeuvre, à savoir cacher le dossier et le nom de l’image.

Méthode

Mon contrôleur (ou controller) s’appelle Files et ma méthode, returnFiles.

Dans votre vue, pour afficher le media, indiquez le code suivant (où 45 est l’id de l’image à afficher) :

<img src="<?php echo site_url('files/returnFile/45); ?>" alt="votre image" />

Passons à la création du contrôleur :

class Files extends Controller {
/**
* constructeur
*
*/
function __contruct() {
parent::Controller();
}
/**
* la méthode qui va retourner le media
*
* @param int $image_id, l'ID de votre image
*
*/
function returnFile ($image_id) {
// on va cherche les infos sur l'image
// utilisez ici un model à vous pour récupérer l'url complète
// dans mon exemple, je vais récupérer quelque chose comme :
// http://www.mondomaine.com/images/image45.png
$path_to_file = Mon_model::getUrl($image_id);
// on cherche le type mime du fichier
// on charge le helper qui va nous permettre de faire ça rapidement
$this->load->helper('file');
// puis on cherche le type mime avec la fonction get_mime_by_extension, chargée avec le helper ci-dessus
$mime = get_mime_by_extension($path_to_file);
// il n'y a plus qu'à renvoyer le fichier
// on définit le content-type
header("Content-type:" . $mime);
// puis on renvoie le fichier
readfile($path_to_file);
}
}

Pour plus d’info sur le chemin du fichier (chemin absolu ou URL), je vous renvoie vers la doc de readfile sur php.net :
http://fr2.php.net/manual/fr/function.readfile.php

Vérifier l’identité de la personne qui veut afficher le media

Je le disais plus haut, on pourrait vouloir vérifier l’identité du visiteur avant d’afficher l’image.
On ajoute alors un bout de code au début de la méthode returnFiles. Vous pouvez également utiliser un pré-contrôleur.

Dans l’exemple suivant, on va simplement vérifier que l’utilisateur est connecté avec une variable de session.

/**
* la méthode qui va retourner le media
*
* @param int $image_id, l'ID de votre image
*
*/
function returnFile ($image_id) {
// vérifie que le visiteur est connecté
$is_connected = $this->session->userdata('is_connected');
if($is_connected != TRUE) {
exit;
}
// suite du script...

Dans cet exemple, on récupère une variable de session qui est initialisée à TRUE lorsque le visiteur se connecte. Si cette variable n’existe pas, ou si elle n’est pas égale à TRUE, on sort du script avec exit. On peut également renvoyer un message d’erreur, rediriger vers une page de connexion…

Compléments

Bon à savoir, cette méthode fonctionne avec les images (testée avec les gif, jpg et png), les sons (mp3) et les animations flash (swf).
J’ai eu quelques soucis pour la faire fonctionner avec les FLV. Le type mime n’était pas reconnu par mon serveur local (EasyPHP) et j’ai dû ajouter les types mimes correspondants dans le fichier httpd.conf.

AddType video/x-flv .flv
AddType video/flv .flv
AddType flv-application/octet-stream .flv

Pour autant que je sache, on peut faire la même chose avec un .htaccess.