URL: https://linuxfr.org/news/sortie-de-php-7-0-un-nouveau-depart Title: Sortie de PHP 7.0 - un nouveau départ Authors: _jordan_ rogo, Lucas, M5oul, Benoît Sibaud, jcr83, claudex, palm123, Adrien Dorsaz, Xavier Teyssier, Bruno Michel, Storm, Jarvis, jihele et Anonyme Date: 2015年12月04日T17:28:56+01:00 License: CC By-SA Tags: php, php7, web et développement_web Score: 67 La version 7.0 de PHP a été annoncée ce jeudi 3 décembre 2015. Elle améliore très significativement les performances, ajoute de nouvelles fonctionnalités et fait un ménage conséquent. Le pari d'une modernisation complète de ce langage phare du développement web est-il réussi après deux années complètes de développement ? Début de réponse dans cette dépêche. ---- [Annonce de sortie de PHP 7.0](https://secure.php.net/archive/2015.php#id2015-12-03-1) [Article Wikipédia de PHP](https://fr.wikipedia.org/wiki/PHP) [Dépêche LinuxFr.org : sortie de PHP 5.6](https://linuxfr.org/news/sortie-de-php-5-6) [Aide de migration à PHP 7 (incompatibilités, nouvelles features, ...)](http://php.net/manual/en/migration70.php) ---- It is not just a next major PHP version being released today. The release being introduced is an outcome of the almost two years development journey. It is a very special accomplishment of the core team. And, it is a result of incredible efforts of many active community members. Indeed, it is not just a final release being brought out today, it is the rise of a new PHP generation with an enormous potential.> Ce n'est pas seulement une nouvelle version majeure de PHP qui sort aujourd'hui. C'est l'aboutissement de pratiquement deux ans de développement. Une performance remarquable de la « core team ». Et, c'est le résultat d'efforts incroyables de la part de nombreux membres actifs de la communauté. En effet, ce n'est pas simplement une version finale qui sort aujourd'hui, c'est la naissance d'une nouvelle génération de PHP avec un potentiel énorme. C'est par ces mots que l'équipe de développement de PHP a [annoncé](https://secure.php.net/archive/2015.php#id2015-12-03-1) la dernière version de leur langage. Et ce n'est pas rien, on se souviendra que les tentatives de développement de PHP 6 avaient échoué en 2010 poussant l'équipe à fusionner une partie des nouveautés à PHP 5.4 en abandonnant temporairement le travail sur Unicode. # Résumé des plus gros changements PHP 7.0 entre en scène avec un [nouveau moteur](https://wiki.php.net/phpng) Zend Engine et des nouvelles fonctionnalités telles que : + Prise en charge cohérente du 64 bits ; + Amélioration de la hiérarchie des exceptions ; + De nombreuses erreurs fatales converties en exceptions ; + Un générateur de nombres aléatoires sécurisé ; + Suppression des interfaces SAPI et des extensions obsolètes ou non-maintenues ; + Typage des déclarations de fonction : retours et scalaires ; + Classes anonymes ; + Assertions à coût nul ; + Un nouvel [opérateur *null coalescing*](https://en.wikipedia.org/wiki/Null_coalescing_operator) : `??` : + Un nouvel opérateur de comparaison : `<=>`. Comme promis, ce nouveau Zend Engine est beaucoup plus performant. On parle d'une réduction par quatre du nombre d'instructions CPU pour exécuter une requête WordPress (comparé à PHP 5.6 ; voir [source](http://www.zend.com/en/resources/php7_infographic)). Et Zend annonce également de meilleures performances que [HHVM 3.7](http://hhvm.com/). De plus, il aurait réduit significativement l'utilisation de la mémoire. # Dans le détail ## Amélioration des performances Avant toute explication, il est nécessaire de parler de [HHVM](http://hhvm.com/). Cette machine virtuelle pour PHP et [HACK](http://hacklang.org/) ont été développés par Facebook. HHVM a apporté un gain substantiel de performance. Pour ne pas se laisser distancer, l'entreprise Zend lance en janvier 2014 un projet interne pour optimiser les structures de données de PHP, sous la direction de [Dimitri Stogov](https://blog.amasty.com/php-7-and-script-languages-future-insights-from-lead-zend-com-developer/). Les premiers résultats sont prometteurs, et en mars une RFC baptisée [PHPNG](https://wiki.php.net/rfc/phpng) propose de refonder le cœur de PHP. C'est sur ces bases que PHP 7 a été construit ; l'équipe en montre donc fièrement les nouvelles performances. Zend a publié une [longue infographie](http://www.zend.com/en/resources/php7_infographic) d'un banc d'essai comparant le nombre de requêtes par secondes pouvant être effectuées sur différents CMS : Drupal, WordPress, Magento, Laravel, Zend Framework, Sugar CRM. De manière générale on remarquera que PHP 7 sert en moyenne 1,8 fois plus de requêtes que PHP 5.6 et légèrement plus que HHVM 3.7. On prendra ces chiffres avec des pincettes du fait de leur description imprécise et de leur origine (Zend), l'entreprise ayant intérêt à présenter PHP sous le meilleur jour possible. On notera aussi que HHVM s'en sortirait mieux pour le calcul lourd (ce qui n'est pas nécessairement la cible de base du développement de sites web). On peut aussi mesurer l'évolution des performances dans ce [graphique](http://talks.php.net/oz15#/perf2014) retraçant le nombre d'instructions du processeur pour une même page WordPress tout au long du développement de PHP 7 en 2014. PHP 7 devait aussi apporter une compilation à la volée, nous en parlions en ces termes dans la dépêche annonçant la sortie de PHP 5.6 :> Ce gain de performance pourra être accentué par le compilation à la volée (JIT : Just In Time) qui devrait être implémenté par Dmitry Stogov. Il annonce 10 à 20 pourcent de vitesse en plus sur des applications réelles : WordPress 3.6 (+ 20 %) et Drupal 6.1 (+ 11,7 %)... Malheureusement, cette nouvelle évolution se fait attendre. HHVM étant parti pour lancer une nouvelle offensive sur le sujet, PHP fera tout pour rester dans la course. ## Amélioration de la structure du compilateur Auparavant, l'analyse syntaxique d'un code source PHP était imbriquée dans le compilateur PHP. Cette nouvelle version utilise en amont de la compilation un [[arbre syntaxique abstrait]], ou *AST* pour *Abstract Syntax Tree*. Ce [découplage](https://wiki.php.net/rfc/abstract_syntax_tree), avec désormais une phase d'analyse syntaxique avant la production des *opcodes*, a permis de retirer du compilateur quelques subterfuges peu présentables pour obtenir un code de maintenance plus facile. Si l'impact sur les performances est négligeable, cette amélioration ouvre des perspectives sur la syntaxe du langage. Une application pratique immédiate est que `yield` peut maintenant s'utiliser sans parenthèses. L'AST est une structure intermédiaire utilisée durant le processus de compilation. Elle remplace l'utilisation d'*opcodes* (liste d'opérations à effectuer par la suite). Désolidariser l'analyseur syntaxique du compilateur permet de supprimer un grand nombre de bricolages et rend le code plus facilement maintenable et compréhensible en général. De plus, cela permet de mettre en oeuvre des syntaxes qui ne sont pas interprétables avec une seule passe d'analyse. L'implémentation de l'AST est 10 à 15 % plus rapide que la compilation standard. En revanche, elle est plus consommatrice en mémoire en fonction de la taille des fichiers. Quelques changements de syntaxe et de fonctionnement ont été nécessaires pour permettre l'implémentation de l'AST. Un autre changement structurel apporte plus de cohérence dans le support du 64 bits. Avant PHP 7, il n'y avait pas de type *entier 64 bits*. La taille dépendait du système d'exploitation, avec notamment PHP/Windows qui restait en 32 bits et émulait les entiers longs. La proposition d'uniformiser ce comportement a été [acceptée de justesse](https://wiki.php.net/rfc/size_t_and_int64_next) et implémentée en PHP 7, après correction des extensions comme PDO qui avaient des préjugés sur les tailles des entiers. ### Changement dans l'affectation des listes Les listes sont maintenant affectées de gauche à droite. ``` PHP list($array[], $array[], $array[]) = [1, 2, 3]; var_dump($array); // AVANT: $array = [3, 2, 1] // MAINTENANT: $array = [1, 2, 3] ``` D'autres exemples d'affectation : ``` PHP $a = [1, 2]; list($a, $b) = $a; // AVANT: $a = 1, $b = 2 // MAINTENANT: $a = 1, $b = null + "Undefined index 1" $b = [1, 2]; list($a, $b) = $b; // AVANT: $a = null + "Undefined index 0", $b = 2 // MAINTENANT: $a = 1, $b = 2 ``` Les listes vides sont toujours interdites : ``` PHP list() = $a; // INVALID list($b, list()) = $a; // INVALID foreach ($a as list()) // INVALID (c'était déjà le cas avant) ``` ### Influence des parenthèses Une conséquence de l'*Uniform Variable Syntax RFC* était que `($foo)['bar'] = 'baz'` et `$foo['bar'] = 'baz'` n'avaient pas le même comportement car ils étaient compilés avec des méthodes de recherche différentes. Ce problème est maintenant résolu, les comportements que l'on peut attendre seront ceux observés. ### Yield est maintenant associatif à droite Le constructeur `yield` n'a plus besoin de parenthèse et est associatif à droite. ``` PHP echo yield -1; // Etait interprété comme echo (yield) - 1; // Est maintenant interprété comme echo yield (-1); yield $foo or die; // Etait interprété comme yield ($foo or die); // Est maintenant interprété comme (yield $foo) or die; ``` D'autres conséquences et informations peuvent être trouvées sur la page de la [RFC](https://wiki.php.net/rfc/abstract_syntax_tree1). ## Exceptions (hiérarchie et ajouts) Depuis PHP 7, le moteur peut envoyer des exceptions au lieu de lancer des erreurs fatales. En effet, jusqu'à présent, la moindre erreur fatale faisait arrêter le processus immédiatement ce qui peut être génant dans le cas d'un serveur ou d'un démon écrit en PHP. Grâce aux exceptions, il sera par exemple possible d'attraper une exception, fermer proprement les entrées/sorties nécessaires et continuer de servir les autres requêtes. La [RFC](https://wiki.php.net/rfc/engine_exceptions_for_php7#backwards_compatibility) décrit aussi l'exemple d'une batterie de tests en PHPUnit qui peut continuer à s'exécuter même si un des tests renvoie une erreur fatale. La RFC introduit donc deux nouveaux types d'exception : + EngineException : exception envoyée par défaut à l'exécution ; + ParseException : pour les exceptions liées au parsing. Elle change les règles suivantes : + Il est maintenant possible d'utiliser des exceptions dans le moteur PHP ; + Les erreurs précédemments utilisées : `E_ERROR`, `E_RECOVERABLE_ERROR`, `E_PARSE` OU `E_COMPILE_ERROR` sont converties en exceptions ; + Il est maintenant découragé d'introduire de nouvelles erreurs de type `E_ERROR` ou `E_RECOVERABLE_ERROR`. Dans les limites de la faisabilité, il faut préférer les exceptions. Pour conserver une certaine compatibilité, les erreurs classiques avec leurs messages seront conservées si les exceptions de moteur ou de l'analyseur ne sont pas attrapées. Cela pourrait être changé à l'avenir. Il existe donc quelques incompatibilités mais la plupart des anciens codes devrait continuer à fonctionner. ## Générateur de nombres aléatoires sécurisé Un générateur de nombre pseudo-aléatoire cryptographiquement sécurisé a été ajouté. Il apporte ([RFC](https://wiki.php.net/rfc/easy_userland_csprng)) deux nouvelles fonctions : `random_bytes()` et `random_int()`. ``` PHP $randomStr = random_bytes($length = 16); $randomInt = random_int($min = 0, $max = 127); ``` Pour générer ces nombres, le moteur utilisera : + Sur Windows : exclusivement CryptGenRandom ; + arc4random_buf() s'il est disponible (souvent spécifique à BSD) ; + /dev/arandom s'il est disponible ; + /dev/urandom si aucun des précédents n'est disponible ; + Une exception sera envoyée si la source d'aléatoire n'est pas suffisante. ([RFC](https://wiki.php.net/rfc/random-function-exceptions)). Ces ajouts permettent de ne plus avoir à utiliser `mcrypt_create_iv()` qui demandait d'installer l'extension MCrypt ni `openssl_random_pseudo_bytes` apportée par `OpenSSL lib`. ## Nettoyage SAPI et extensions Une modification de taille, annoncée depuis des années, est la suppression de « ext/mysql », l'API historique de MySQL dans PHP. La fameuse `mysql_real_escape_string()` (qui est `real` par opposition à `mysql_escape_string()` qui n'est pas sûre), cette fonction omni-présente au temps de PHP 4 a vécu. L'extension MySQL est avantageusement remplacée par les API (fonctionnelles ou orientées-objets, au choix) de Mysqli ou PDO. Les extensions ereg (expressions rationnelles pre-preg), mssql (PDO s'y substitue) et sybase_ct ont aussi été supprimées. Quinze SAPI ont été retirées du code. Par exemple, la SAPI « apache » était une ancienne méthode d'intégration de PHP à Apache, alors qu'aujourd'hui « fpm » est recommandée, ou à défaut en module avec `mod_php` qui correspond généralement à la SAPI « apache2handler ». Côté SAPI, noter que quand PHP-FPM écoute sur un port donné, il le fait en IPv4, et désormais aussi en IPv6, par défaut. ## Déclarations de type dans les fonctions : retours et scalaires ### Déclaration de type scalaire Les déclarations de type existent depuis PHP 5 et permettent de demander un certain type à l'appel d'une fonction. Par contre seuls les classes, interfaces, tableaux et le type *callable* étaient disponibles. PHP 7 ajoute donc le typage des scalaires (`string`, `int`, `float` et `bool`) avec deux modes : coercitif (par défaut) et strict (voir ci-dessous). À l'exécution, si l'argument donné est de mauvais type, PHP 7 lancera une exception `TypeError`. PHP 5 provoquait une erreur fatale. ``` PHP function foo(int $foo, int $bar) { echo $foo + $bar; } foo(1, 1); // affiche 2 foo(1, '1'); // affiche 2 (conversion car mode coercitif) foo('a', 1); // Uncaught TypeError ``` Toute la documentation concernant la déclaration de type peut être trouvée [ici](https://secure.php.net/manual/fr/functions.arguments.php#functions.arguments.type-declaration). ### Déclaration du type renvoyé PHP 7 permet la déclaration du type de la valeur retournée par une fonction. ``` PHP function countNonBlank(array $lines) : int { return count(array_filter(array_map('trim', $lines))); } ``` ### Typage strict Par défaut, PHP va convertir les mauvais types vers le type scalaire attendu tant que possible. Par exemple, une fonction, qui attend comme paramètre un *integer* (entier), et à laquelle est passée une *string* (chaîne de caractères) recevra une variable de type *string*. Il est possible d'activer un typage strict fichier par fichier. Dans ce mode, seule une variable exactement du type attendu dans la déclaration sera acceptée, sinon une exception du type `TypeError` sera levée. La seule exception à cette règle est qu'un entier (integer) peut être passé à une fonction attendant un nombre flottant (float). Le typage strict affecte aussi les déclarations de type retourné. Alors qu'en typage faible, le valeur de retour est convertie vers le type déclaré, en typage strict une exception sera levée. Ce mode est activé **fichier par fichier** grâce à l'expression `declare`. ``` PHP declare(strict_types=1); function sum(int $a, int $b) { return $a + $b; } var_dump(sum(1, 2)); // retourne int(3) var_dump(sum(1.5, 2.5)); // Uncaught TypeException ``` Le mode strict est à surveiller de près car **il ne s'applique qu'aux appels de fonctions effectués depuis l'intérieur d'un fichier dont le typage strict est actif**. Autrement dit, si un fichier dont le typage strict n'est pas activé effectue un appel à une fonction qui a été définie dans un fichier dont le type strict est actif, la préférence de l'appelant sera respectée, le typage ne sera donc pas strict. ## Classes anonymes L'opérateur `new` peut désormais être suivi de la déclaration d'une classe anonyme, au lieu du nom d'une classe existante. L'exemple suivant illustre ce principe. ``` PHP function createLogger() { return new class implements \IteratorAggregate { private $content = []; public function add(string $item) { $this->content[] = $item; return $this; } public function getIterator() { return new \ArrayIterator($this->content); } }; } $logger = createLogger(); $logger->add("jusqu'ici tout va bien")->add("ça baigne"); foreach ($logger as $log) { echo $log . "\n"; } ``` ## *Expectations*, assertions à coût nul La fonction `assert()` devient une composante du langage PHP 7, ce qui lui permet d'être plus souple et plus performante. Avant PHP 7, le premier paramètre de `assert()` était évalué, même quand les assertions étaient désactivées, avec le risque de ralentir ou perturber l'exécution en production. Une astuce douteuse était de placer le code PHP dans une chaîne de caractère, laquelle n'était évaluée que lorsqu'on avait activé les assertions. ``` PHP // Avant PHP 7 // Activer les assertions assert_options(ASSERT_ACTIVE); // La fonction sera appelée, même sans ASSERT_ACTIVE expect(slowButTrue(), "Problème avec slowButTrue()"); // Le texte sera évalué seulement avec ASSERT_ACTIVE expect("slowButTrue()", "Problème avec slowButTrue()"); ``` En PHP 7, l'ancienne syntaxe reste valable, mais passer le réglage `zend.assertions` à 0 (contre 1 par défaut) permet de désactiver l'évaluation des assertions. On peut aussi déclencher des exceptions, dérivées de la nouvelle classe réservée `AssertionError`. ``` PHP // PHP 7 // Activer les assertions (ou mieux, dans php.ini) ini_set('zend.assertions', 1); // Si les assertions sont désactivées, pas d'évaluation expect(slowButTrue(), "Problème avec slowButTrue()"); // Remplacer les messages d'erreurs par des exceptions ini_set('assert.exception', 1); assert(false, new AssertionError('Ça va mal')); ``` ## Nouvel opérateur null coalescing : ?? Cet opérateur ajoute du [sucre syntaxique](https://fr.wikipedia.org/wiki/Sucre_syntaxique) pour les cas courants d'utilisation d'un opérateur ternaire couplé à la fonction `isset()` qui permet de savoir si une valeur existe. `??` retourne le premier opérande s'il existe et s'il n'est **pas NULL**, sinon il retourne le deuxième. ```PHP // code d'avant PHP 7 return isset($a) ? $a : 0; // code en PHP 7 return $a ?? 0; // combinaison ?? en PHP 7 return $_GET["user"] ?? $_POST["user"] ?? "anonyme"; ``` Comme illustré ci-dessus, l'opérateur peut être chainé, et retournera alors la première valeur définie parmi plusieurs opérandes consécutifs. ## Nouvel opérateur de comparaison : <=> Dérivé de Perl et consorts à travers la RFC « [Combined Comparison (Spaceship) Operator](https://wiki.php.net/rfc/combined-comparison-operator) », cet opérateur fonctionne comme les fonctions tri-valuées `strcmp()` ou `version_compare()` mais il a l'avantage de pouvoir être utilisé quelles que soient les valeurs, avec la même sémantique que `<`, `<=`, `==`, `>=`, `>`. De nombreux exemples sont trouvables sur la page de la [RFC](http://php-internals.afup.org/2015/02/19/rfc-combined-comparison-spaceship-operator/). Il donne les résultats suivant : - `0` si les deux opérandes sont égaux ; - `1` si l’opérande de gauche est plus grand que celui de droite ; - `-1` si l’opérande de gauche est plus petit que celui de droite. # Incompatibilités Comme toujours, la [liste des incompatibilités](https://secure.php.net/manual/en/migration70.incompatible.php) et le [guide des migrations](https://secure.php.net/manual/en/migration70.php) est disponible.