Code Igniter, un framework PHP

Choisir un Framework PHP pour développer des sites web peut vous faire gagner des efforts et du temps. Je ne désire pas écrire ici une liste exhaustive des avantages et inconvénients des différents frameworks PHP disponibles mais veut simplement mettre en évidence pourquoi je crois qu’ils valent la peine qu’on les essaie.

Dans mon cas personnel, le choix d’un framework PHP m’a aussi aidé à acquérir une meilleure pratique de programmation.

Code Igniter - PHP FrameworkKohana - PHP5 FrameworkZend Framework

Le framework avec lequel j’ai développé plusieurs projets Web est Code Igniter (v.1.7.2). J’ai aussi approché Kohana3 (pour le plaisir) parce que je cherchais un framework léger et PHP5 strict. Je n’ai pas approfondi Zend Framework parce qu’il m’a l’air un peu lourd et je cherche un système plus léger. Cela dit, produit développé par Zend Technologies Ltd., il est également une référence dans le domaine des Frameworks PHP.

Si vous ne vous êtes jamais frotté à un framework PHP et être prêt à jeter un oeil à oeil en quelques minutes, visionnez un des tutoriels vidéos officiels sur http://codeigniter.com/tutorials/ : absolument convaincant!

Au départ, j’étais assez frileux de choisir un framework et préférais coder mes propres scripts parce que je pensais qu’un framework me forcerais à utiliser une pratique de programmation que je pourrais regretter ultérieurement (utiliser des règles de programmation propres au framework, efficacité à long terme,…). Tel était mon état d’esprit avant d’en avoir essayé un.

Après ma première expérience avec Code Igniter, j’ai changé d’avis et pense désormais que les framework nous facilitent la vie. Et même si le framework que j’ai choisi n’était plus développé, je pourrais aisément en choisir un autre pour développer mes sites web sans devoir révolutionner ma pratique.

Selon moi, ce qui rend un framework crédible, efficace est :

  • un framework PHP5
  • une architecture MVC (Modèle-Vue-Contrôler)
  • un lot de helpers (scripts qui vous font gagner du temps car ils contiennent des classes pour gérer les urls ou les éléments de formulaire, par exemple)
  • la possibilité de scripter vos propres contrôleurs, vos propres bibliothèques de classes, vos propres helpers
  • une pratique bien documentée (off- et/ou online)
  • un système léger (donc au chargement rapide)

Tout est une question de puissance…

PHP5-powered

La puissance de framework PHP5 comme Kohana est qu’ils sont strictement OOP (Object Oriented Programming), signe d’une meilleure pratique de programmation. Code Igniter est partiellement écrit en PHP5 OOP, les helpers n’incluent pour l’instant que des fonctions PHP4.

MVC-powered

Une architecture MVC vous permet de structurer votre code : vos scripts PHP ne sont plus mêlés à votre HTML et vos requêtes MySql comme ils l’étaient dans des scripts faits maison sans framework. Dans une architecture MVC, vous pouvez définir des répertoires spécifiques qui contiendront vos classes de contrôleurs, vos classes de modèles et vos vues. Pratiquement parlant, une page Web appellera un contrôleur. Ce contrôleur peut définir des variables, appeler un modèle qui se connectera à la base de données pour en extraire des données, par exemple. Donc, le modèle exécute une requête MySql, obtient les données de la base de données et renvoie le résultat au contrôleur. Le contrôleur passera ensuite les résultats à la vue qui est comme un template HTML.

MY_Controller.php : écrire vos propres contrôleurs

Un Framework PHP vous laisse scripter vos propres contrôleurs. Dans Code Igniter, le contrôleur principal se trouve dans /system/libraries/Controller.php. Pour utiliser votre propre contrôleur, créez un fichier dans /system/application/libraries/MY_Controller.php. Ce fichier contient une classe MY_Controller qui étend le contrôleur principal :

<?php if ( ! defined(‘BASEPATH’)) exit(‘No direct script access allowed’);
class MY_Controller extends Controller {
public function __construct() {
parent::__construct();
$this->load->helper(array(‘my_html’,'my_url’,'my_date’));
}
}
?>

Mon contrôleur personnel charge le constructeur du contrôleur principal puis charge quelques helpers. Ces helpeurs ne sont pas les helpers par défaut de Code Igniter qui se trouvent dans /system/helpers/ mais mes propres helpers réécrits : my_html_helper.php, my_url_helper.php et my_date_helper.php qui sont des copies des helpers originaux auxquels j’ai ajouté quelques fonctions personnelles.

Développer des sites Web avec Code Igniter m’a permis d’écrire des scripts très courts, faciles à mettre à jour et redoutablement efficaces.

Webliography

Code Igniter

Code Igniter Video Tutorials

Code Igniter Tutorials

Derek Allard’s tutorials

Kohana

Pages merci en formulaires PHP

Google Analytics filter page names

J’avais l’habitude de scripter des formulaires de login, de contact, d’inscription, de commande,… qui étaient affichés dans une page login.htm, contact.htm, inscription.htm, commande.htm avec un attribut « action » de formulaire qui renvoyait à cette même page. Une fois le formulaire envoyé, PHP validait les champs de formulaires et affichait un message de remerciement (ou un message d’erreur) à l’intérieur de la même page html. Cette méthode n’est pas idéale si vous désirez pister les utilisateurs qui ont complété les utilisateurs qui ont rempli le formulaire correctement, ou ceux qui ont abandonné l’action en cours de remplissage ou ceux qui l’ont rempli de manière erronée et l’on envoyé à nouveau…

Si vous voulez analyser plus profondément les statistiques liées à votre site grâce à un outil comme Google Analytics, par exemple, il serait plus judicieux de générer une page login-merci.htm, contact-merci.htm, inscription-merci.htm ou commande-merci.htm. Une fois le formulaire envoyé, faites valider les champs par PHP à l’aide d’une classe validation de formulaire (vérifiez les champs requis, les champs email,…) et redirigez l’utilisateur vers une page « merci » pour afficher le message de remerciement si les champs ont été correctement complétés. Si ce n’est pas le cas, redirigez l’utilisateur vers la page contact-erreur-email.htm, par exemple.

Après quelque temps, vous vous loggerez sur votre compte Google Analytics / Contenu / Rapport Complet : utilisez le champ filtre en bas du rapport pour filtrer les pages par « contenant le mot-clef » ou « ne contenant pas le mot-clef » avec une valeur comme « merci » pour afficher toutes les pages contenant ce mot-clef et afficher les statistiques de ces pages.

ClickHeat – alternative Crazy Egg

clickheat

Crazy Egg offre un outil d’Heat Mapping qui vous permet de visualiser les clics des visiteurs de votre site web. Vous devrez vous inscrire pour profiter de cet outil et payer entre $9 et $99 selon le pack choisi.

Un autre outil similaire puissant est Clickheat. Il est gratuit, Open Source et facile à installer (30 secondes d’implementation).

Uploadez simplement le répertoire clickheat décompressé dans le root de votre site. Puis suivez l’assistant de configuration. Vous devrez indiquer quels noms de domaine sont autorisés à indexer les clics et définir le login-password d’accès aux données. Puis identifiez-vous sur http://www.mydomain.com/clickheat/ et générez le code javascript à placer dans la balise head de votre fichier index.php.

Après quelques clics de visiteurs, vous obtiendrez les résultats suivants :

clickheat

Serveurs Web locaux

Un développeur web travaillant sur un serveur Linux comme OpenSuse devra simplement activer et configurer Apache, MySql et le support PHP pour faire fonctionner ses sites Web dynamiques.

Si vous travaillez sur une station Windows, vous devrez installer une application Serveur Web pour exécuter vos scripts PHP.

Il y a quelques années, je travaillais avec EasyPhp, qui a un temps cessé d’évoluer. J’ai donc à l’époque changé de solution et choisi WampServer, qui est toujours la solution que j’utilise actuellement. Il est livré avec Apache, Mysql, Phpmyadmin (scripts php pour la gestion de vos bases de données). Il est facile à installer et propose des versions PHP et MySql à jour.

J’ai récemment découvert ZMWS. ZazouMiniWebServer qui est un logiciel serveur Web autonome qui peut être installé sous Windows. Il est livré avec PHP4 et PHP5 mais vous pourriez y ajouter des handlers qui le feront tourner en ASP, PERL,… :

Je l’ai testé aisément sur une clé USB comme serveur Web portable.

Vous pourriez aussi distribuer votre projet Web PHP sur CD et disposer ainsi d’un site web dynamique auto-navigable.

Le site de du projet Open Source ZMWS vous fournira aussi un forum et une documentation.

Webliography :

ZazouMiniWebServer

WampServer

Polymorphisme et PHP5

Le polymorphisme est un concept majeur de la Programmation Orientée Objet (POO). Des langages mûrs tels que C++ ou JAVA intègrent ce concept de manière native. PHP5 permet également d’intégrer le polymorphisme dans le développement PHP.

Qu’est-ce que le polymorphisme?

Son principe réside dans son nom : [poly-] signifie beaucoup et [-morphisme] signifie formes, états. Ainsi, intégrer le polymorphisme dans la programmation permet au développeur d’initialiser un seul objet qui se comportera différemment selon son type. Pratiquement parlant, le développeur écrit une classe de base qui définit les attributs de l’objet et autant de sous-classes que de comportements différents de l’objet seront nécessaires.

Voici un exemple simple de design polymorphique dans le cadre d’un objet véhicule. La classe véhicule a pour attributs des informations qui valent pour tout type de véhicule : marque, date d’achat, prix d’achat :

Class Vehicule {

 

protected $marque;
protected $date_achat;
protected $prix_achat;
public function __construct($marque,$date_achat,$prix_achat) {
$this->marque = $marque;
$this->date_achat = $date_achat;
$this->prix_achat = $prix_achat;
}

}

On peut ensuite créer la classe avion et la classe voiture héritant de la classe véhicule :

Class Voiture Extends Vehicule {

private $cylindree;
private $km;
public function __construct($array) {
parent::__construct($array["marque"],$array["date_achat"],$array["prix_achat"]);
$this->cylindree = $array["cylindree"];
$this->km = $array["km"];
}
}

Class Avion Extends Vehicule {

private $type;
private $heures_vol;
public function __construct($array) {
parent::__construct($array["marque"],$array["date_achat"],$array["prix_achat"]);
$this->type = $array["type"];
$this->heures_vol = $array["heures_vol"];
}

}

Structurer vos classes de cette manière permet de rendre votre programmation orientée objet plus abstraite, et donc plus flexible. Changer un type d’objet particulier après production sera plus aisé et plus stable.

Objets sans design polymorphique

Créer une classe de génération de champs de formulaire sans polymorphisme :

<?php
class Form {
public function genInput($type,$name,$value) {
SWITCH($type) {
Case »text »:
print « <input type=\ »text\ » name=\ »".$name. »\ » value=\ »".$value. »\ » />\r\n »;
break;
Case »textarea »:
print « <textarea name=\ »".$name. »\ »> ».$value. »</textarea>\r\n »;
break;
}
}
}
$form = new Form(); //initialisation de l’objet formulaire
$form->genInput(« text », »field1″, »Lorem ipsum… »); //affiche <input type= »text » name= »field1″ value= »Lorem ipsum… » />
$form->genInput(« textarea », »field2″, »Lorem ipsum… »); //affiche <textarea name= »field2″>Lorem ipsum…</textarea>
?>

Objets avec design polymorphique

Pour qu’un objet puisse revêtir plusieurs formes, qu’il s’agisse d’un champ texte ou d’un champ textarea, nous devons rendre notre classe plus abstraite.
Nous allons donc créer une interface, une classe abstraite et quelques sous-classes (selon le type de champ).
L’interface définira les fonctions que l’objet appellera sans définir ce que ces fonctions feront. L’interface ne définira aucune des fonctionnalités de ces méthodes, elle se contente de les nommer. Chaque sous-classe devra comporter la définition de la méthode genInput().

//INTERFACE
interface Form {

public function genInput();

}

Une classe abstraite est une classe qui nous n’implémentons que partiellement. Elle contient les méthodes (fonctions) abstraites qui offrent des fonctionnalités communes. Le reste des autres fonctioannlités sera défini dans les sous-classes.
//ABSTRACT CLASS
abstract class Input {
protected $name;
protected $value;
public function __construct($name,$value) {
$this->name = $name;
$this->value = $value;
}
}
Puis les classes dérivées appelleront la méthode définie dans la classe abstraite (parent::__construct()) et la méthode nommée dans l’interface (genInput()) :
//TEXT & PASSWORD & HIDDEN INPUT
class Text extends Input implements Form {
private $type;
private $size;
public function __construct($array) {
parent::__construct($array["name"],$array["value"]);
$this->type = $array["type"];
$this->size = $array["size"];
}
public function genInput() {
return « <input type=\ »".$this->type. »\ » name=\ »".$this->name. »\ » value=\ »".$this->value. »\ » size=\ »".$this->size. »\ » />\r\n »;
}
}
//TEXTAREA INPUT
class Textarea extends Input implements Form {
private $cols;
private $rows;
public function __construct($array) {
parent::__construct($array["name"],$array["value"]);
$this->cols = $array["cols"];
$this->rows = $array["rows"];
}
public function genInput() {
return « <textarea name=\ »".$this->name. »\ » cols=\ »".$this->cols. »\ » rows=\ »".$this->rows. »\ »> ».$this->value. »</textarea>\r\n »;
}
}
Voici comment créer un champ texte :
$object = new Text(array(« type »=> »text », »name »=> »form_id », »value »=>$item, »size »=>20));
$hidden = $object->genInput();
Générer un champ textarea ressemble à ceci :
$object = new Textarea(array(« name »=> »form_txta », »value »=>$item , »cols »=>20 , »rows »=>5));
$textarea = $object->genInput();
Webliography :

Abstract classes and interfaces
Devshed polymorphism in PHP5 article
PHP5 Tutorial : Abstract classes and Interfaces
PHP5 OOP abstract classes & interfaces

Array $_POST vide

J’ai été confronté au problème suivant deux fois avec un CMS fait maison : poster un formulaire avec la méthode post n’envoyait aucune information à la page définie dans l’attribut action du formulaire. Afficher $_POST :

print_r($_POST);

donnait systématiquement le même résultat :

array();

Mon fichier .htaccess définit Magic_quotes à 0, Register_Globals à 0 et PHP_VER à 5. Puis il contient quelques redirections 301 :

SetEnv MAGIC_QUOTES 0
SetEnv REGISTER_GLOBALS 0
SetEnv PHP_VER 5
Options +FollowSymlinks -MultiViews
RewriteEngine on
rewritecond %{http_host} ^mydomain.be [nc]
rewriterule ^(.*)$ http://www.mydomain.be/$1 [r=301,nc]
Et le problème de $_POST vide vient des redirections 301 redirect. En effet, mon formulaire était posté à http://mydomain.be/index.php à cause d’une balise « base » définie à l’intérieur de la balise « head » de mon document :
<base href=’http://mydomain.be/’ />
et l’attribut action de la balise « form » était le suivant :
<form method=’post’ action=’./index.php’>
A cause des redirections 301 qui font pointer mydomain.be vers www.mydomain.be et le formulaire qui pointe vers mydomain.be/index.php, Apache n’associe pas le tableau $_POST avec www.mydomain.be mais avec le domaine domain.be. D’où le problème de $_POST vide.
J’espère que cet article vous épargnera quelques longues minutes de debugging.

Recherche Fulltext sous MySql

Scripter un moteur de recherche nécessite :

  • un formulaire côté client pour soumettre des requêtes
  • des champs de table de base de données à l’intérieur desquels la recherche doit s’effectuer
  • des requêtes à la base de données pour rechercher la chaîne postée par le client

Je veux tout d’abord me pencher sur les caractéristiques de la base de données pour effectuer une telle recherche.

Auaparavant, j’utilisais des requêtes avec des sous-requêtes du type :

SELECT id,title,content from table_name WHERE title LIKE ‘% ».$search. »%’ OR content LIKE ‘% ».$search. »%’;

qui effectue une recherche de style expression régulière et peut causer des problèmes de ressources MySql (grand nombre de lignes parcourues, grand nombre de résultats,…).

Sous MySql, une recherche FULLTEXT peut solutionner ce problème de ressources et produire des résultats probants puisqu’elle effectue une recherche sur des données indexées, et donc pas directement dans la table. Ce type de recherche permet l’utilisation :

  • d’une liste de mots rejetés (mots que le serveurs exclut par défaut de la recherche)
  • de recherches booléennes (utilisant + ou -, par exemple, pour ajouter ou enlever des mots de la recherche)
  • d’un score de pertinence

Type de table qui permet l’indexation Fulltext

La recherche FULLTEXT est disponible avec les tables utilisant MyISAM comme moteur de stockage. Bien que le moteur de stockage InnoDB offre l’énorme avantage de pouvoir utiliser des transactions (gestion des erreurs par commit et rollback), la recherche par indexation Fulltext y est absente. Concentrons-nous donc sur les tables MyISAM.

Vous pouvez définir un index Fulltext sur un ou plusieurs champs de type CHAR, VARCHAR et TEXT. Comme mentionné dans la référence MySql reference sur le sujet, créer des données dans une table disposant d’un index Fulltext prendra plus de temps. Ainsi, si un nombre conséquent de données doit être traité avec un index Fulltext (et c’est dans ce cas qu’il sera le plus efficace), il vaut mieux d’abord insérer les données dans la table, puis créer l’index.

Vous pouvez créer un index sur les 2 champs « title » et « content » avec la requête suivante :

ALTER TABLE table_name ADD FULLTEXT index_name (‘title’ , ‘content’);

Une fois l’index Fulltext de la table défini, faites une recherche comme suit :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘ ».$search. »‘);

Les champs de la sous-requête MATCH doivent être les mêmes que ceux définis dans l’index fulltext de la table, et vous pouvez créer un index Fulltext sur un champ ou plus.

Paramétrage de la recherche Fulltext

MySql est configuré avec un lot de paramètres par défaut :

  • les résultats sont classés par score de pertinence que MySql donne à chaque donnée trouvée
  • les mots de moins de 4 caractères seront ignorés
  • l’index Fulltext contient uniquement des mots entiers
  • liste des mots exclus d’une recherche Fulltext. Ce paramètre dépend de la langue d’installation de MySql
  • le score de chaque donnée trouvée dépendra du nombre d’occurences de ce mot dans la table. Si un mot apparaît dans plus de la moitié des lignes de la table, il sera ignoré

Le changement de ces paramètres par défaut ne sera possible que dans le cas où votre serveur Web MySql vous permet de les modifier.

Recherche en mode booléen

La sous-requête MATCH… AGAINST… permet la recherche booléenne :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘ ».$search. »‘ IN BOOLEAN MODE);

MySql retournera toutes les lignes correspondantes même si plus de 50% des lignes contiennent le mot-clef.

Les recherches booléennes permettent en outre l’utilisation d’opérateurs comme + ou – pour forcer MySql à retourner les lignes contenant ‘works’ et cacher les lignes contenant ‘tables’ :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘+works -tables’ IN BOOLEAN MODE);

Vous pouvez aussi utiliser l’opérateur * comme joker en mode booléen pour rechercher une partie de mot :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘peopl*’ IN BOOLEAN MODE);

Webliography :

Référence sur la recherche FULLTEXT sous MySql

Référence sur la recherche FULLTEXT en mode Booléen sous MySql

PHP5 Afficher toutes les requêtes MySql d’une page Web

Le développeur qui veut rationaliser et/ou améliorer ses requêtes MySql peut trouver utile de faire afficher toutes les requêtes qu’une page Web a généré ainsi que le temps que le serveur a dédié à la production de cette page. Ceci n’est utile qu’en phase de développement. Une fois cette phase terminée, il faudra commenter (// ou /* */) ces quelques lignes de script pour gagner du temps de travail CPU.

Changements dans la classe MySql

Pour compter les requêtes MySql générées par une page Web et en afficher la liste, le développeur devrait ajouter une variable statique à sa classe MySql :

public static $queries = array();

Lors de l’initialisation de la classe, la variable $queries deviendra un tableau vide (array). A chaque fois qu’une requête est envoyée à la méthode d’exécution de la classe, cette requête sera ajoutée au tableau de la manière suivante :
array_push(self::$queries,$query);
Avant de détruire l’objet $mysql :
return count(self::$queries);
retournera le nombre de requêtes MySql générées par une page Web.
return self::$queries;
retournera le tableau contenant toutes les requêtes MySql de la page courante.

PHP script execution time

Pour vérifier combien de temps un script prend du début à la fin, utilisez microtime :
<?php
//haut du script PHP
$start_time = microtime(true); // >= PHP 5.0.0 donne ainsi le microtime avec décimales
//bas du script PHP
$end_time = microtime(true);
$total_time = $end_time – $start_time;
?>

Empêcher le submit par [F5]

Le traitement PHP d’un formulaire peut nécessiter la vérification que l’utilisateur n’a pas pressé la touche [F5] pour soumettre le formulaire à nouveau. En effet, si le fait de soumettre un formulaire provoque une requête à la base de données insert into, vous préféreriez que l’insertion n’aie pas lieu 2 fois.

Le principe pour contrer les [F5] est d’utiliser un champ hidden (appelé « process » dans mon exemple) contenant le Unix timestamp du moment de la génération du formulaire par le serveur (microtime(true) en PHP). Ensuite, il reste à créer une fonction qui va :

  • comparer la valeur de process postée avec une variable Session appelée « process ». Si la valeur de la variable process postée est égale à la valeur stockée dans la variable Session, la fonction renvoie l’erreur : « Veuillez utiliser le bouton Submit »
  • si ces valeurs sont différentes, le formulaire est traité, la base de données modifiée au besoin, puis stockez la valeur de la variable process postée dans la variable Session ($_SESSION["process"]).

Editeurs PHP

Il existe de nombreux éditeurs PHP. Je ne les ai pas tous testés et veux seulement me concentrer sur les logiciels de base. Mes critères sont :

  • logiciel libre
  • démarrage rapide
  • et par-dessus tout : pas de code propriétaire

Sous Windows

Je n’ai jamais essayé d’utiliser Dreamweaver à cette fin à cause de son coût et parce que tous les fichiers reçus de Designers Web utilisant Dreamweaver devaient être nettoyés : tags vides répétés, alignements inexacts,… qui pouvaient altérer le résultat final à l’écran.

Notepad2 (freeware), est un éditeur de texte basé sur Scintilla. Je l’utilise encore actuellement en remplacement du Bloc-Notes Windows.

Ses caractéristiques les plus marquantes sont :

  • visual brace matching : permet d’identifier les parenthèses de début et de fin ( ), { }
  • numérotation des lignes
  • sélection du langage de script + mise en évidence de sa structure

Bien entendu, il reste un éditeur de texte basique qui ne permet par exemple pas d’ouvrir plusieurs fichiers texte dans la même fenêtre,…

Notepad++ (sous licence GPL) est un éditeur de code source basé sur Scintilla avec quelques caractéristiques supplémentaires :

  • multi-document + multi-vue
  • zoom,…

L’avantage des notepads pour l’écriture de scripts est que, même pour travailler l’Html, ils aident les développeurs en devenir à entrer plus profondément dans le code source…

Sous Linux

Le seul éditeur que j’ai testé et utilise au quotidien tourne sous KDE et s’appelle Quanta Plus. Il fait partie du package KdeWebDev et s’installe simplement par Yast sous OpenSuse.

Dans l’environnement de bureau Gnome, BlueFish semble le plus étendu et fonctionnel.

éditeurs PHP

Notepad2 (win 32)

Notepad++ (win 32)

Quanta Plus (Linux-KDE)

BlueFish (Linux-GNOME)