7 idées pour s’améliorer en programmation PHP

1.L’utilisation des constantes pour le bien de vos yeux

Trop souvent les programmeurs PHP ont utilises les constantes pour différent usage tel que configuration, langage, variable globale. Une constante devrait être une valeur qui ne change pas tout au long de l’existence de l’application et non tout au long de l’exécution du script.

Par exemple si vous avez une classe RGB vous pourriez avoir la constante RGB::RED = #FF0000 cette valeur est une valeur constante elle ne changera jamais. Ce n’est pas d’avoir une valeur qui ne change pas tout au long de l’application, mais bien une valeur qui est constante qui ne change pas.

L’utilité principale des constantes est de clarifier votre code, de donner un nom plus humain et facile a lire. Par exemple, vous avez surmènent déjà eu des fonctions / method telle que

1
$logger->log('blabla', 3, 1 | 4);

Mettez-vous a la place dans les souliers du programmeur neuf qui arrive et regarde cette ligne. Au premier coup d’oeil, il est facile de voir que l’ont veut logger « blabla » mais pour le reste… Aucune idée de ce que c’est.

Voici des exemples d’une meilleur utilisation des constantes.

1
2
3
4
5
$logger->log(
   'blabla',
   ILog::LEVEL_WARNING,
   ILog::OPTION_DELAYED | ILog::OPTION_MERGE
);

De cette façon, nous ne somme pas obliger d’aller voir la définition de la méthode pour la comprendre que le 2e paramètre est le niveau sévérité du message et que le 3e sont les options.

Voici l’exemple du RGB.

1
2
3
4
5
6
7
8
9
10
11
12
class RGB {
  const RED   = '#ff0000';
  const BLUE  = '#0000ff';
  const GREEN = '#00ff00';
  ...
}
 
// Sans constante
$color = new RGB('#ff0000');
 
// Avec constante
$color = new RGB(RGB::RED);

2.Apprenez des autres langages et appliquez leurs principes

En C++ il est possible de définir certaines méthodes d’une classe comment étant constante. En Java un principe similaire est la définition d’interface mutable et non mutable. Le but ici est simple, c’est d’exposer à l’utilisateur de la classe que l’appel à ces méthodes ne changera pas l’état de l’instance.

Si vous appelez getName() sur l’instance utilisateur il ne devrait y avoir aucune variables membre de l’instance utilisateur qui devrait changer. Cette méthode est donc constante / non mutable. Tous les getter de façon générale devraient être constant / none mutable et ne devrait pas altérer l’état de l’instance.

En PHP il n’est pas possible de définir une méthode constante, mais il est toujours possible d’applique le principe que lorsque vous définissez un getter vous n’altérez pas l’état de votre instance.

Voici un exemple d’interface non mutable et mutable.

1
2
3
4
5
6
7
8
9
interface IUser {
  function getFirstName();
  function getLastName();
}
 
interface IUserMutable extends IUser {
  function setFirstName($s);
  function setLastName($s);
}

3.Maîtrisez l’utilisation des interfaces

En C++ et Java, les interfaces ne sont pas simplement une façon de s’assurer l’existence de méthodes. C’est avant tout un contrat qui définit quelles méthodes peuvent être appelées.

Exemple d’une definition d’interface en C++

1
2
3
4
5
6
class IUser {
public:
  virtual ~IUser() {}
  virtual const string getFirstName() const = 0;
  virtual const string getLastName() const = 0;
};

Dans l’exemple ci-haut c’est une définition d’interface IUser avec une méthode getFirstName constante qui retourne une chaîne constante. Au final, l’appel de cette méthode sur une instance ne devrait pas changer l’état de celle çi. De plus, il ne devrait pas non plus être possible de changer la chaîne retourne. Il serait nécessaire de faire une copie pour être en mesure de la changer. Cette démarche assurer l’intégrité de l’espace mémoire utilisé par l’instance User.

Un des points importants est que ces langages empêchent l’appel d’autres méthodes qui ne sont pas définies dans l’interface.

Si dans la définition d’une méthode, vous spécifiez que vous vous attendez a recevoir une interface IUser rien ne vous empêche, d’appeler setFirstName qui fait partie de l’interface (IUserMutable). Or le jour ou vous allez passer à cette méthode une instance qui supporte seulement l’interface IUser votre allez vous apercevoir que votre méthode en fait plus qu’elle ne semble le laisser voir.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
interface IUser {
  function getFirstName();
  function  getLastName();
}
 
interface IUserMutable extends IUser {
  function setFirstName($s);
  function setLastName($s);
}
 
class ConstantUser implements IUser {
  function getFirstName() { return $this->fName; }
  function getLastName() { return $this->lName; }
}
 
class DynamicUser implements IUserMutable {
  function getFirstName() { return $this->fName; }
  function getLastName() { return $this->lName; }
}
 
// Ici la fonction expose a l'utilisateur qu'elle a besoin d'un IUser
// alors qu'en réalité elle devrait exposer qu'elle a besoin d'un IUserMutable
function doMore(IUser $user) {
  // cette méthode fait partie de l'interface IUser
  $user->getFirstName(); 
  // Cette methode ne fait pas partie de l'interface IUser
  $user->setFirstName('Yanik'); 
}
 
$dUser = new DynamicUser();
doMore($dUser); // OK
 
$cUser = new ConstantUser();
doMore($cUser); // CRASH

En C++ ou Java il serait absolument nécessaire de caster le pointer à l’interface voulue.
Exemple de casting C++

1
2
3
4
5
6
void functionName(IUser * user) {
  IUserMutable * userMutable = dynamic_cast<IUserMutable *>(user);
  if (userMutable != null) {
    userMutable->setFirstName('Yanik');
  }
}

Exemple de casting Java

1
2
3
4
5
6
void public function functionName(IUser user) {
  IUserMutable userMutable = (IUserMutable) user;
  if (userMutable != null) {
    userMutable.setFirstName('Yanik');
  }
}

Exemple de faux casting PHP

1
2
3
4
5
6
7
8
9
10
 
function functionName(IUser $user) {
  $userMutable = null;
  // Faux casting ;-) 
  // Le but est simplement de rendre le code plus clair sur l'intention
  if ($user instanceof IUserMutable) {
    $userMutable = $user; 
    $userMutable->setFirstName('Yanik');
  }
}

4.Utilisez la puissance de json_encode et limitez les points de contact

Dans le développement d’application web, il est souvent nécessaire d’utiliser JavaScript. Trop souvent j’ai vu du code PHP générer par condition et concaténation plusieurs segments de code javascript. Que ce soit la construction de tableau, d’objet ou de fonction. Cette façon de produire du javascript rend la lecture et la compréhension du code javascript et PHP presque qu’impossible et le résultat ressemble souvent a un code offuscater.

Limitez les points de contact entre PHP et JavaScript. Dites-vous qu’a chaque fois que PHP produit un bout de JavaScript par un echo ou une concaténation c’est un point de contact.

Exemple A

1
2
3
4
5
6
7
8
<script>
<?php
$js = 'var user = new User({'; // Point de contact
if ($firstName) { $js .= "'{$firstName}',"; } else { $js .= 'null,'; } // Point de contact
if ($lastName) { $js .= "'{$lastName}',"; } else { $js .= 'null,'; } // Point de contact
echo $js + "});"; // Point de contact
?>
</script>

Exemple B

1
2
3
4
5
6
7
<?php $prop = array('firstName' => 'Yanik', 'lastName' => 'Lupien'); ?>
 
<script>
// Section Javascript
var prop = <?php echo jsonencode($prop); ?>; // Point de contact
var user = new User(prop);
</script>

Les deux codes si hauts donnent presque un résultat identique. Dans l’exemple A pour construire l’instance du user l’utilisation de la concaténation de segment javascript et variable PHP. Un des inconvénients de cette pratique est qu’il n’y a pas de prise en charge des valeurs avec quote. Soit le Javasctipt va se briser soit il est possible d’injecter du javascript.

Dans l’exemple B chaque langage est clairement séparer le seul point de contact entre les deux est ou il y a l’assignation de la variable prop.

5.Éradiquez la maladie du singleton / multiton

Qui n’a pas déjà fais quelque chose qui ressemble a User::getInstance(4); ce patron de conception s’appelle un Multiton c’est l’équivalent d’un singleton, mais avec un cle par instance. La classe User fait probablement partie de votre business logique de votre application. Ici se posent deux problématiques.

La première problématique, où se trouve la source de données?

La deuxième comment réutiliser votre classe avec une autre source de donnes ?

Dans la même logique je serais porter a faire une nouvelle méthode User::getInstanceFromFile(‘user-6.txt’) pour récupérer une instance à partir d’un fichier…

Le singleton en soit n’est pas une mauvaise pratique c’est l’utilisation par appel statique qui n’est pas très bonne. Tout dépend du contexte bien sûr.

Conclusion : très rapidement la classe va se retrouver polluer d’une quantité incroyable de méthodes statique qui récupère des User de différentes façons et de plusieurs endroits différents avec plusieurs conditions différentes. Sans parler de dépendances externes que vous ajoutez à vos classes business.

6.Fuyez les variables globales orientées objet

Pour ceux qui ont déjà travaillé avec Zend_Framework, vous avez sûrement déjà vu la classe Zend_Registry. Cette classe est une belle variable globale orientée objet. Le programmeur qui utilise cette classe se sent en paix avec lui même, car il a la certitude de faire du beau code. C’est vrai qu’il n’y a pas le mot GLOBAL. Mais dans la réalité c’est une classe qui wrap une variable globale. Zend_Registry::getInstance() retourne une instance unique du registry peut importe l’emplacement ou nous somme dans l’application. Et par la suite il est possible de récupérer un élément par un nom.

Dite vous qu’a chaque fois que vous faite un appel statique a une classe extérieure sans l’avoir passé a la votre classe vous venez d’ajouter une dépendance externe qui risque de vous causer beaucoup de problèmes lorsque vous allez vouloir utiliser cette même classe dans un autre contexte.

Pour pallier à ceci, utiliser l’injection des dépendances. Si une votre class a besoin d’un logger pour son fonctionnement injectez lui.

7. Définissez toujours de façon explicite l’origine des variables

Avez-vous déjà défini une variable dans un fichier PHP et l’utiliser dans un autre ? Regardez l’exemple si dessous. Si nous somme dans le contexte du fichier index.php il n’est pas très clair de savoir de ou provient la variable $menu pourtant elle est la. Avec cette façon de développer, il y a aussi un gros risque de collision et d’erreurs. Maintenant que se passe t’il si un développeur ajoute au fichier initialize.php une variable $menu ?

config.php

1
2
$config = "myconfig.ini";
$menus = array('Contact', 'About', 'Products')

index.php

1
2
3
require_once('config.php');
require_once('initialize.php');
foreach($menus as $menu) { .. }

Arrêtez vous un instant et placez-vous dans les souliers d’un autre programmeur qui arrive dans le projet. Regardez le fichier index.php et posez-vous la question d’où provient $menus ? Etes vous en mesure de dire si c’est config.php ou initialize.php ?

Pour que le code soit lisible et facile à comprendre, il est toujours important de voir les entrées et sorties. Dans l’exemple il n’est pas possible de voir l’entrée / la provenance de $menu.

Pour corriger cette problématique, une façon simple serait simple d’ajouter une fonction et un return à la fin du fichier. L’utilisation d’une fonction rend toutes les variables utilisées dans le fichier config.php privé donc il n’affecter pas le scope globale. Le code se trouve aussi clarifie, puisque maintenant il est facile de suivre la provenance de la variable $menu.

config.php

1
2
3
4
5
6
function config() {
  $config = "myconfig.ini";
  $menus = array('Contact', 'About', 'Products')
  return array('menus' => $menus);
}
return config();

index.php

1
2
3
$configs = require_once('config.php');
$inits = require_once('initialize.php');
foreach($config['menus'] as $menu) { .. }

Merci pour votre lecture.

Références

Leave a Comment