PRINT vs ECHO expliqué aux glands, aux neuneux et aux connards qui se la pètent PERL

J'ai vu passer sur fr.comp.lang.php un magnifique thread sur le fait qu'utiliser 'print' ou 'echo' n'aurait aucune conséquence significative.

Hummm soyons précis, l'auteur dit :

'... j'arrive à la conclusion que c'est bien la même chose pour le développement.'

La phrase prête à confusion ... Signifie t' elle que pendant la phase de développement on ne cherche pas à optimiser et que donc 'print' et 'echo' seraient équivalents ? Je trouve cet argument fallacieux alors puisque 'printf' aussi serait équivalent à ce stade ... débile, mais équivalent ...

Heureusement l'auteur recadre le débat tout de suite après :

'... donne moi une raison technique pour laquelle il serait préférable d'utiliser echo au lieu de print, je suis preneur, je suis certain que j'ai encore des vieux scripts avec des "print" qui traînent.'

HAAAA! et bien tu est prêt John ?

QUE DIT LA DOC ?

ECHO :

  1. echo -- Output one or more strings
  2. echo() is not actually a function (it is a language construct)
  3. Prototype: void echo ( string arg1 [, string argn...])

PRINT :

  1. print -- Output a string
  2. print() is not actually a real function (it is a language construct)
  3. Prototype: int print ( string arg)

Dans les 2 cas nous avons affaire à une construction du langage, 'print' et 'echo' sont donc des mots-clés directement analysés par le lexer et le parser du Zend Engine.

A ce stade n'importe quel couillon de base peut faire la remarque qu'un 'echo' peut afficher successivement plusieurs chaines alors que 'print' n'en n'est pas capable :

echo $A, $B, $C; // Valide
print $A, $B, $C; // Invalide

Bien sûr le premier boutonneux venu va nous dire que l'on peut très bien afficher plusieurs arguments avec 'print' en utilisant la syntaxe suivante:

print $A.$B.$C; // Valide
... nous verrons plus loin ce qu'il en est.

Autre remarque importante, si l'on considère les prototypes on voit que 'print' retourne un int (toujours 1 selon la doc), alors que 'echo' lui ne renvoie rien...

Nous pouvons DEJA conclure que 'print' et 'echo' ne SONT PAS équivalents quand aux fonctionnalités et encore moins des alias l'un de l'autre.

VOYAGE AU COEUR DU ZEND ENGINE

Puisque la documentation nous dit que 'print' et 'echo' sont des constructions du langage, allons faire un tour du coté de la grammaire définie dans le fichier zend_langage_parser.y

Tout d'abord nous vérifions que nous avons bien 2 'identifiants de mot-clé' (tokens) :

%right T_PRINT
%token T_ECHO

le %right associé à T_PRINT nous confirme bien que (pour simplifier) 'print' peut se retrouver à droite d'une expression (donc qu'il est évaluable).

La grammaire ci-dessous montre ce que fait le parseur quand il rencontre un 'echo' :

T_ECHO echo_expr_list ';'

echo_expr_list:	
	echo_expr_list ',' expr { zend_do_echo(&$3 TSRMLS_CC); }
	| expr			{ zend_do_echo(&$1 TSRMLS_CC); }
;
( définition récursive de la liste. -habituelle dans une grammaire- )

Ce qui signifie : si il rencontre un 'echo' il recherche une liste d'expressions séparées par une virgule et il appelle la fonction zend_do_echo() pour chacune des expressions.


La grammaire ci-dessous montre ce que fait le parseur quand il rencontre un 'print' :

T_PRINT expr  { zend_do_print(&$$, &$2 TSRMLS_CC); }

Ce qui signifie : si il rencontre un 'print', il recherche une expression et il appelle la fonction zend_do_print();

Ok ? tout le monde suit ? non ? tant pis!

A cette étape donc le parseur a déterminé soit une action 'print' soit une action 'echo'. Nous allons voir maintenant la génération du pseudo-code pour la machine virtuelle.

Fichier zend_compile.c

Nous entrons dans le fichier qui transforme les actions en op-codes compréhensibles par la machine virtuelle. Voici donc le code C des fonctions 'compilant' respectivement un 'print' et un 'echo' ...

void zend_do_print(znode *result, znode *arg TSRMLS_DC)
{
	zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);

	opline->result.op_type = IS_TMP_VAR;
	opline->result.u.var = get_temporary_variable(CG(active_op_array));
	opline->opcode = ZEND_PRINT;
	opline->op1 = *arg;
	SET_UNUSED(opline->op2);
	*result = opline->result;
}

void zend_do_echo(znode *arg TSRMLS_DC)
{
	zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);

	opline->opcode = ZEND_ECHO;
	opline->op1 = *arg;
	SET_UNUSED(opline->op2);
}

Alors là les choses sont claires, si l'on ne considère que le code de traitement (on ne compte pas la première ligne d'obtention de l'opline) le code de traitement de 'print' est le double de celui du traitement de 'echo' !. Et à moins d'être un imbécile de première classe, il est évident pour tout un chacun que le code de l'instruction 'echo' est bien plus rapide que celui de 'print'!

LA PREUVE PAR LE BENCH

Le sceptique objectera que ce qui l'intéresse est la différence effectivement mesurée dans un exemple concret. Nous allons donc mesurer les performances objectives d'un script utilisant soit 'print' soit 'echo' et affichant un message construit à partir de plusieurs données.

De plus les 2 instructions utiliseront des données concaténées avec un '.' ce qui sera plus équitable.

Voila les 2 scripts :

test_echo.php

<?php
$D = 'Vendredi';
$T = '14 heures';
for($i=0;$i < 1000;$i++) {
   echo 'Nous somme le : '.$D.' et il est '.$T;
}
?>

test_print.php

<?php
$D = 'Vendredi';
$T = '14 heures';
for($i=0;$i < 1000;$i++) {
   print 'Nous somme le : '.$D.' et il est '.$T;
}
?>

Pour mesurer les performances nous utiliserons l'utilitaire 'ab' fournit avec Apache. Il effectuera un total de 1000 requêtes par 20 requêtes simultanées.

Le résultat est conforme à ce qui est prévu: en moyenne le script 'echo' permet d'effectuer 90.5 req/s contre 88.7 req/s pour le 'print'. Une différence certes 'négligeable' (1.8 req/s) mais qui démontre que l'instruction 'echo' est bel et bien plus rapide que le 'print'.

CONCLUSION

Alors me direz vous, tout cela pour si peu ? Oui, parce que ce genre d'attitude montre que le manque de rigueur dans l'utilisation de ces 2 instructions ne peut présager que du même comportement dans le reste de l'utilisation de PHP : regex au lieu de 'strstr' par exemple, ou utilisation systématique des " derrière un print/echo (comme certains abrutis de PEAR [http://pear.php.net/package/HTML_Form]).

Bref pour rester dans le non-politiKement-Korrekt qui me caractérise, je dirai que l'usage du 'print' et de 'echo' définit la frontière entre les porcs et les artisans du code ...

KDO

PS: Au fait John, désolé mais cette page est valide html4/strict, j'espère que malgré tout elle devrait passer sur ton Netscape 4.7 ;)