Sur la manière de penser Objets en PHP par rapport à Java

Java vs PHP, la différence qui change tout

En Java, chaque concept, même le plus insignifiant, mérite presque toujours une classe. Le nombre d’objets instanciés a peu d’impact sur les performances. Les programmes Java sont certes souvent un peu longs à démarrer, le temps de charger la machine virtuelle mais aussi de créer et de configurer le labyrinthe d’objets interconnectés qui forme le programme. Mais une fois le programme initialisé, il est réactif et stable. Java est particulièrement adapté aux traitements côté serveur. J’ai connu des démons écrits en Java turbinant un an durant sans un redémarrage et sans dire un mot. Java rejoint par endroit la stabilité des AS/400.

Un programme PHP est relancé à chaque requête HTTP reçue. Il n’est pas, comme en Java, chargé en mémoire et en attente des évènements. Il n’existe aucune manière de conserver un objet en mémoire pour le réutiliser d’une requête HTTP à l’autre. Les programmes PHP sont sans cesse réinterprétés et relancés, parfois plusieurs dizaines de fois par secondes.

Aussi la question de la performance du chargement des programmes PHP est cruciale. Les développeurs du langage PHP ont le souci permanent de la performance. Toutes les nouveautés sont orientées, courbées, pliées dans le sens de la performance, ce qui donne parfois des choses étonnantes comme la manière « file system » de penser les namespaces. Mais les créateurs du langage ne développent pas à notre place et, en définitive, si l’on ne fait pas l’effort de penser à notre tour dans le sens de la performance, la vélocité de l’écosystème PHP sera vaine.

Or l’instanciation des objets prend du temps. Nous devons créer nos objets PHP avec parcimonie…

Le couteau suisse de PHP : les tableaux associatifs

Faudrait-il pour autant revenir à un mode de raisonnement procédural ? Non. À tout le moins, pas au sens des langages procéduraux. Car PHP est malgré tout un langage à Objets.

PHP offre un outil optimisé, les tableaux associatifs, afin d’éviter la POO dans les petits détails. PHP incite à utiliser les tableaux associatifs là où en Java nous créerions des classes garnies de setters et de getters.

Prenons un exemple. Nous souhaitons représenter un point coloré. La manière Java d’écrire cela ressemblera à :

// On crée la classe…
 
public class ColoredPoint {
	private int x, y;
	private String colorCode;
 
	public ColoredPoint(int x, int y, String colorCode) {
		this.x = x;
		this.y = y;
		this.colorCode = colorCode;
	}

	public String getColorCode() {
		return this.colorCode;
	}

	public String getX() {
		return this.x;
	}

	public String getY() {
		return this.y;
	}
}
 
// … puis on l'utilise :
 
ColoredPoint cp = new ColoredPoint(10, 12, "99FF99");

En PHP nous écrirons plutôt :

$cp = array('x' => 10, 'y' => 12, 'color-code' => '99FF99');

Le code Java est intéressant à moyen terme. Le programme est compilé une seule fois, puis chargé également une seule fois, aussi les temps de compilation et de chargement ne comptent pas. Ensuite le programme attend les évènements (requêtes HTTP ou autres) et instancie des objets. N’en doutons pas, au bout de quelques dizaines ou centaines de réponses aux évènements, et quelques milliers d’instanciations plus tard, les appels aux getters se révèleront plus rapides qu’une recherche dans un tableau associatif.

Un programme PHP équivalent n’atteindra pas le millier d’instanciations. Il manipulera les quelques dizaines d’éléments nécessaires au traitement de la requête HTTP et rendra la main au bout de quelques microsecondes. Il sera recompilé et relancé une demi-seconde plus tard et pour un résultat similaire. Pas d’inertie en PHP.

Avec l’inertie, Java gagne. Mais est-ce bien certain ? Tout dépend des traitements. Les API Java mettent en jeu une foule d’objets et sont lentes. La complexe API de calcul d’un SHA1 en Java restera toujours plus lente que la fonction bien optimisée de PHP dédiée à cette tâche. En outre il arrive souvent que le temps des traitements soit insignifiant devant celui des I/O. Au final, en Java, nous avons la possibilité de stocker en RAM des choses prêtes à resservir là où PHP est condamné à toujours réitérer ses efforts sans plus d’organisation, et cela est un atout de Java pour qui sait s’en servir.

Pour revenir à notre code PHP, sa concision joue en faveur d’une compilation et d’un chargement plus rapide. Et la création d’un tableau PHP est plus performante que l’instanciation d’une classe.

Mais, me direz-vous, notre « point coloré » fera forcément l’objet de traitements. Si l’on utilise un tableau, on ne peut pas lui adjoindre de méthodes pour ces traitements.

Exact ! D’où la question qui suit.

En PHP, jusqu’où aller dans la POO ?

Le langage PHP pose la question de jusqu’à quel niveau de précision on reste dans l’orienté objet. Un arbitrage doit être fait : cet objet qu’on hésite à créer, l’utilisera-t-on partout dans le programme ou juste pour un traitement particulier ? A-t-on besoin d’une dizaine de méthodes ou de juste les getters et une fonction de traitement ?

En Java on arbitre presque toujours en faveur d’une classe. On va loin dans les détails insignifiants, on va jusqu’à créer des petites classes privées pour les besoin internes d’une classe. En Java tout ce qui n’est pas un type primitif mérite une classe.

En PHP, au contraire, mieux vaut un tableau qu’un petit objet.

En ce qui me concerne voici une règle que je suis : lorsque j’hésite, j’utilise par défaut un tableau associatif. Dès que le code pour manipuler ce tableau devient plus complexe que s’il s’agissait d’un objet, je recode avec une classe.

Un peu plus sur les tableaux PHP, le principe du copy-on-write

Les tableaux en PHP sont implémentés selon le principe du copy-on-write ou copie sur écriture. Le paramètre n’est copié que si on y accède en écriture. Et c’est pourquoi il serait contreproductif, dans un but d’optimisation, de passer par référence un tableau à une fonction.

Un exemple :

function test(array $a) {
	echo $a[0]; // on utilise la tableau en lecture
	// jusqu'ici le $a original n'est pas dupliqué
	$a[1] = 'haricot'; // duplication du $a original et modification de la copie
}
 
$a = array('ananas', 'banane', 'tomate');
test($a);

Ou bien un autre exemple, plus simple encore :

$a = array('ananas', 'banane', 'tomate');
$b = $a;
// ici $b et $a pointent sur le même tableau.
$b[1] = 'haricot'; // copie sur écriture
// ici $b et $a pointent sur deux tableaux différents.

Les tableaux PHP sont toujours passés par référence, et l’opérateur & sert en fait à désactiver la copie sur écriture. Le copy-on-write rend la manipulation des tableaux PHP similaire à celle des objets constants clonables (à l’instar des String de Java).

PHP est un langage à Objets : créons des objets. PHP fournit des tableaux polyvalents et optimisés : dans les méthodes de nos objets, évitons d’instancier trop de petits objets et utilisons des tableaux.

Leave a Reply

Your email address will not be published. Required fields are marked *

Mesure anti-spam. Merci de copier le code « ihfJtg » dans le champ ci-dessous :