Préchargement en php 7.4: Compositeur et sélection des fichiers à précharger

Chez Badoo, nous passons activement à PHP 7.4 et sommes très enthousiastes à l'idée d'utiliser la nouvelle fonction de préchargement. Il n'y a pas si longtemps, nous avons parlé de nos expériences avec elle.

Apparemment, la communauté est aussi excitée que nous. Les développeurs de framework discutent activement de la possibilité d'introduire une précharge (et certains ont déjà fait son support ). C'est maintenant au tour du gestionnaire de dépendances de Composer.



Italo Baeza a écrit un article dans lequel il exprime son opinion sur la manière dont Composer devrait travailler avec la précharge. J'ai décidé de partager une traduction de ce texte, et en même temps une traduction de son autre article , sur la façon dont les développeurs Composer eux-mêmes ont répondu à la proposition, ainsi que sur un nouvel outil qui facilite le travail avec la précharge.

Comment le compositeur doit précharger en PHP 7.4


Le préchargement est l'une des fonctionnalités importantes que PHP 7.4 offre aux développeurs qui ont besoin de meilleures performances. Cette fonction peut être appelée "échauffement" avant d'implémenter le moteur JIT, qui apparaîtra (ou devrait apparaître) en PHP 8. Avant cela, le préchargement sera suffisant, et qui sait, peut-être qu'ils peuvent travailler en tandem.

La fonction de précharge est expliquée dans cet article . Le résultat est très simple: php.ini spécifie un script PHP pour lequel lorsque le processus démarre, les fichiers sont chargés en mémoire (préchargement). En combinaison avec OPCache et la fonction de chargement automatique , les fichiers Composer peuvent également être compilés et liés une fois, après quoi ils seront disponibles pour toutes les demandes ultérieures. Grâce à cela, PHP n'a pas besoin de télécharger et de compiler des fichiers à chaque demande.

Cependant, les développeurs de Composer n'étaient pas d'accord sur la façon dont il devrait aider au préchargement, en plus de fournir des fonctions de démarrage. Les faits sont les suivants:

  • le préchargement a été annoncé pour la première fois en PHP 7.4;
  • il n'y a pas de directive Composer pour aider à précharger les fichiers;
  • pour précharger, vous devez avoir accès à php.ini, c'est-à-dire au processus lui-même;
  • le préchargement de tous les fichiers n'améliorera pas nécessairement les performances par rapport au préchargement uniquement des fichiers les plus demandés.

En d'autres termes, seuls ceux qui ont un accès complet aux serveurs peuvent utiliser le préchargement. Cela exclut les serveurs partagés et certaines solutions PaaS qui n'impliquent pas de travailler avec php.ini.

Alors, comment Composer peut-il aider à précharger étant donné qu'il s'agit d'une innovation? Voici mon avis.

Fonctionnement de la précharge


Le mécanisme de préchargement doit être basé sur une liste de fichiers qui seront chargés et stockés en mémoire au démarrage. Et comme il s'agit d'une liste, nous devons travailler avec un tableau de fichiers et laisser Composer faire tout le travail, plutôt que de charger chaque fichier manuellement.

Le compositeur doit prendre la liste des fichiers spécifiés par l'application (le projet racine) et tout compiler dans des fichiers que PHP peut utiliser sans aucune difficulté.
Dans le même temps, nous devons pouvoir ajouter et supprimer des packages du mécanisme de précharge.
Le préchargement ne devrait jamais fonctionner au niveau du package, car il est de la responsabilité du développeur d'activer ou de désactiver le préchargement de chaque package.

Le préchargement dans Composer doit être facultatif. Le développeur doit pouvoir le désactiver pour que PHP utilise son propre préchargeur, qui peut fonctionner sur la base de l'analyse OPCache - cela dépend de la charge de l'application et fonctionne beaucoup plus efficacement que le préchargement de tous les fichiers.

Tout commence à preload.json


Afin de ne pas compliquer le système, placez le fichier preload.json à la racine du projet. Il répertorie les fichiers de préchargement que Composer peut sélectionner. Puisqu'il s'agit d'un fichier JSON, le développeur peut même le générer à l'aide d'une commande spéciale. Je pense que ce serait formidable si Composer avait un utilitaire pour créer un tel fichier JSON basé sur un script.

{ "pre-compile": [ "my-script.php", "my-other-script.php" ], "extensions": [ "php" ], "files": [ "app/*", "config/", "helpers.php", "app/Models/*", "app/Controllers/*/Http/*", "app/Views/Compiled*.php" ], "namespace": [ "App\\Models", "App\\Controllers\\", "App\\Views\\MainView", "Vendor\\Package\\*", ], "packages": { "symfony/http-client": true, "robert/*-client": true, "vendor/package": { "files": true, "namespace": true }, "foo/bar": { "files": [ "helpers.php", "loaders/*" ], "namespace": [ "Foo\\Bar\\DynamicLoaders\\*", "Foo\\Bar\\Clients" ] } }, "output": "preload-compiled.php" } 

L'utilisation de preload.json vous permet de vérifier rapidement si le préchargement est inclus dans le projet: si le fichier est manquant, le préchargement n'est pas pris en charge ou indésirable.

Voyons ce que font les clés.

précompiler

Ces fichiers seront exécutés par Composer. Chaque script doit renvoyer un tableau de chemins de fichiers absolus pour les ajouter à la liste de préchargement, qui jouera le rôle de la liste principale.

 "pre-compile": [ "my-script.php", "my-other-script.php" ] 

Ces fichiers seront exécutés dans l'ordre spécifié.

L'objectif est que le développeur crée une liste de fichiers comme bon lui semble, plutôt que de s'appuyer sur un seul fichier JSON. Ces fichiers seront d'abord exécutés. Et oui, vous ne pouvez implémenter preload.json qu'avec cette clé. Puisque nous parlons de fichiers PHP, lors de la compilation d'un tableau, vous pouvez même ajouter d'autres fichiers.

extensions

Il s'agit d'une liste d'extensions de fichiers qui doivent être préchargées. Par défaut, seuls les fichiers avec l'extension php sont pris.

 "extensions": ["php", "php5", "php7"] 

Par exemple, vous pouvez ajouter un répertoire rempli de fichiers * .phtml, y compris certains fichiers PHP utiles, et Composer ne les sélectionnera pas, et non l'intégralité du contenu du répertoire.
Comme vous le comprenez, ce processus peut être remplacé en ajoutant des fichiers manuellement.

les fichiers

Cette clé indique à Composer de télécharger tous les fichiers de la liste dont les chemins sont relatifs à l'emplacement de composer.json.

 "files": [ "helpers.php", "app/Models/*", "app/Controllers/*/Http/*", "app/Views/Compiled*.php", ] 

Il est facile de trouver la liste:

  • utiliser des chemins relatifs pour ajouter des fichiers et des répertoires;
  • à partir des répertoires, seuls les fichiers enfants stockés dans ces répertoires seront ajoutés (pas de manière récursive);
  • les chemins récursifs sont indiqués par un astérisque se terminant (*);
  • en utilisant ce symbole, vous pouvez également, par exemple, ajouter certains fichiers et répertoires: src/Clients/*/Stores ou src/Model*.php .

L'ajout de fichiers par masque sans sélectionner ou créer manuellement des scripts spécifiques à l'application est particulièrement utile lors du développement de grandes applications.

Si vous avez juste besoin de précharger tous les fichiers à l' aide de la clé de chargement automatique dans le fichier Composer JSON, définissez-la sur true .

espace de noms

Cette clé indique à Composer de charger des fichiers avec un espace de noms ou un nom de classe donné, comme un file ou un directory . Le même mécanisme vous permet d'appeler dynamiquement des noms d'espace à partir d'autres packages installés.

 "namespaces": [   "App\\Models",   "App\\Controllers\\",   "App\\Views\\MainView",   "Vendor\\Package\\*", ] 

Cela est également pratique lorsque vous travaillez sur des applications volumineuses qui dépendent davantage des espaces de noms plutôt que des fichiers qui peuvent changer à tout moment. Composer extrait automatiquement les fichiers en fonction de l'espace de noms et les place dans une liste.

packages

Cette clé vous permet de charger d'autres fichiers enregistrés à partir de packages externes, tels que des fichiers auxiliaires ou des classes associées à un espace de noms.

 "packages": {   "symfony/http-client": true,   "robert/*-client": true,   "vendor/package": {       "files": true,       "namespace": true   },   "foo/bar": {       "files": {           "helpers.php",           "loaders/*"       },       "namespace": [           "Foo\\Bar\\DynamicLoaders\\*",           "Foo\\Bar\\Clients"       ]   } } 

Tout est très simple ici: si la valeur est vraie, alors tout le contenu de la clé de chargement automatique dans le fichier composer.json de ce paquet sera chargé. Sinon, vous pouvez contrôler plus finement l'ajout de précharge.

Si la valeur de la clé est vraie, elle téléchargera tous les fichiers enregistrés en autoload . La valeur par défaut est false . Cela est également vrai pour la clé d' namespace .

Vous pouvez également sélectionner des fichiers ou des espaces de noms individuels avec cette règle. Mais dans ce cas, la clé de autoload ne sera pas utilisée.

sortie

Il s'agit simplement du nom du fichier de liste de préchargement compilé.

 "output": "preload-compiled.php" 

Assemblage facile


Notre liste de préchargement est prête, et nous pouvons appeler Composer pour compiler le script de préchargement principal.

 composer preload 

Par conséquent, preload-compiled.php sera créé avec tous les fichiers que PHP doit précharger. Bien sûr, vous pouvez modifier le nom du fichier comme vous le souhaitez.

Vous devez également remplacer les clés de preload paramètres.

 composer preload \   --input=my-custom-preload-list.json \   --output=my-preload.php 

Désactivé par défaut


Les projets sans preload.json renverront une erreur si vous essayez de compiler un fichier pour le préchargement. La raison en est que Composer ne devinera pas (et ne devrait pas) deviner ce qui doit être préchargé.

Permettez-moi de vous rappeler que la précharge n'interfère pas avec les fonctionnalités normales de Composer. Comme il s'agit d'une commande console, avec développement local, vous pouvez complètement abandonner le préchargement. La seule chose dont le mécanisme de préchargement Composer a besoin est un fichier de chargement automatique, qui devrait être généré s'il est manquant. Eh bien, après tout, presque la 2020e année est dans la cour, le PSR-4 est utilisé partout, non?

Résultat


Vous devriez obtenir un fichier php avec quelque chose comme ceci:

 <?php /** * Preloading @generated by Composer */ // Autoload the classes so those can be preloaded using `require_once`. require_once __DIR__.'/../autoload.php'; // File list $files = [ '/var/www/app/Foo.php', '/var/www/app/Bar.php', '/var/www/helpers/basic.php', '/var/www/helpers/advanced.php', '/var/www/vendor/Foo/Bar/src/Class.php', '/var/www/vendor/Foo/Bar/helpers/helpers.php', '/var/www/vendor/Foo/Bar/config.php', // ... ]; // Preload all root project files foreach ($files as $file) { require_once $file; } 

En fait, ce n'est qu'une liste de fichiers qui seront préchargés à l'aide de la fonction de chargement automatique dans Composer. PHP exécutera ce fichier une fois et il deviendra historique.


J'espère sincèrement que Composer aura un moyen de précharger les fichiers sans avoir à écrire un hack.

Étant donné que la méthode décrite ci-dessus ne fait pas partie du noyau Composer, vous pouvez toujours sélectionner les fichiers les plus importants pour le préchargement en fonction de l'analyse OPCache sans toucher aux fichiers les moins nécessaires. Imaginez qu'au lieu de précharger 1 500 fichiers d'une capacité de 100 Mo, vous ne pouvez télécharger que 150 fichiers d'une capacité de 10 Mo, tout en conservant 99% des performances d'origine.

Nous préchargeons le projet PHP 7.4 en une seule ligne


Peu de temps après avoir écrit un article sur la façon dont Composer peut vous aider à précharger un projet, Seldaek (un membre de l'équipe de développement de Composer) a tué tout espoir que Composer aurait une option facile pour précharger le projet dans le processus PHP à partir du gestionnaire de packages.

(...) Je vais vous expliquer: je suis sûr que dans un proche avenir, nous n’ajouterons rien de lié au préchargement dans Composer.

Pourquoi? Le préchargement en PHP est plus un problème de développement (plutôt qu'une dépendance); il est résolu en modifiant manuellement php.ini - seuls les développeurs peuvent le faire s'ils gèrent eux-mêmes PHP.

Mais cela ne m'empêche pas de créer mon propre package pour précharger le projet. Et toi aussi.

Précharge et métriques


Le préchargement peut être un bon outil pour une augmentation simple et bon marché de la productivité sans traitement sérieux.

Mais le problème n'est pas de savoir comment précharger, mais quoi . Le préchargement de frameworks entiers et de milliers de fichiers épuisera rapidement la mémoire, donc le faire aveuglément n'est pas une option, du moins dans les grands projets. Il est conseillé de télécharger uniquement les fichiers les plus demandés. Mais comment les définir?

Heureusement, OPCache permet à opcache_get_status () de collecter des données sur les fichiers les plus consultés. Vous pouvez non seulement découvrir quels fichiers sont les plus demandés, mais même la quantité de mémoire qu'ils consomment quelque temps après le démarrage de l'application.

Il est recommandé d'attendre soit une semaine soit jusqu'au moment où OPCache enregistre un certain nombre de hits. Tout dépend de l'application, mais vous obtenez le point.
Créons donc une liste de préchargement basée sur les statistiques des fichiers les plus populaires. J'ai fait un paquet pour ça.

Imaginez ... Preloader!


Ce package créera automatiquement une liste de préchargement pour votre application. Il collectera les statistiques d'utilisation d'OPCache, triera les fichiers par le nombre de hits et créera une liste afin que la taille totale du fichier ne dépasse pas le seuil spécifié.



J'ai longtemps été perplexe à la recherche de la meilleure stratégie pour créer une liste. Et je suis arrivé à la conclusion qu'il est préférable d'y ajouter tous les fichiers jusqu'à ce que vous rencontriez la limite de mémoire, qui pour les packages est de 32 Mo par défaut. Les fichiers seront triés par nombre de hits et le package lui-même sera automatiquement exclu.

En d'autres termes, PHP améliorera les performances des applications lors du traitement de la plupart des requêtes.

Et comment l'utiliser? Dites à Composer Autoloader où écrire le script Preloader, et vous avez terminé.

 use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->autoload('vendor/autoload.php') ->output('preload.php') ->generate(); 

Bien sûr, vous devez choisir le moment de générer, mais c'est tout. Vous pouvez même le faire au hasard et réécrire la liste, par exemple, pour chaque 100e demande.

 use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->whenOneIn(100) ->autoload('vendor/autoload.php') ->output('preload.php') ->overwrite() ->generate(); 

Vous obtiendrez un script de préchargement prêt à l'emploi que vous pouvez mettre dans php.ini.

 <?php /** * This file is generated automatically by Preloader. * * This script uses Composer Autoload file and `require_once` to preload the files in this * list. Add this file to your `php.ini` in `opcache.preload` to preload this list into * PHP at startup. Additionally, this file also includes information about Opcache. * * * Add (or update) this line in `php.ini`: * * opcache.preload=/www/app/vendor/preload.php * * --- Config --- * Generated at: 2019-11-20 15:20:49 UTC * Opcache * - Used Memory: 130585 B * - Free Memory: 294896 B * - Wasted Memory: 347764 B * - Cached files: 2675 * - Hit rate: 94% * - Misses: 542 * Preloader config * - Memory limit: 32 MB * - Overwrite: false * - Files excluded: 0 * - Files appended: 0 */ require_once '/www/app/vendor/autoload.php'; $files = [ '/www/app/ClassFoo.php', '/www/app/ClassBar.php', '/www/app/ClassBaz.php', '/www/app/ClassQuz.php', '/www/app/ClassQux.php', '/www/app/vendor/author/package/src/Example.php', // ... ]; foreach ($files as $file) { require_once $file; } 

Et c'est tout. Essayez par vous-même: darkghosthunter / preloader - Packagist .

Source: https://habr.com/ru/post/fr480746/


All Articles