Dans PHP 7.4, la prise en charge du préchargement a été ajoutée, une fonctionnalité qui peut améliorer considérablement les performances de votre code.
En un mot, voici comment cela fonctionne :
● Afin de précharger le fichier, vous devez écrire un script PHP personnalisé
● Ce script démarre sur le serveur Exécuté une fois
● Tous les fichiers préchargés sont disponibles en mémoire pour toutes les requêtes
● Les modifications apportées aux fichiers préchargés n'auront aucun impact jusqu'au redémarrage du serveur
Regardons de plus près regarde-le.
#Opcache
Bien que le préchargement soit construit sur opcache, ce n'est pas exactement la même chose. Opcache prendra vos fichiers sources PHP, les compilera en "opcodes", puis stockera ces fichiers compilés sur le disque.
Vous pouvez considérer les opcodes comme des représentations de code de bas niveau qui sont facilement interprétées au moment de l'exécution. Par conséquent, opcache ignore l'étape de conversion entre le fichier source et ce dont l'interpréteur PHP a réellement besoin au moment de l'exécution. Une immense victoire !
Mais nous avons plus à gagner. Les fichiers mis en cache ne connaissent pas les autres fichiers. Si la classe A s'étend de la classe B, elles doivent toujours être liées entre elles au moment de l'exécution. De plus, opcache effectue une vérification pour voir si le fichier source a été modifié et invalidera son cache sur cette base.
C'est donc ici que le préchargement entre en jeu : il compile non seulement les fichiers sources en opcodes, mais relie également les classes, les traits et les interfaces associés. Il enregistre ensuite ce blob « compilé » de code exécutable (c'est-à-dire : le code que l'interpréteur PHP peut utiliser) en mémoire.
Désormais, lorsqu'une requête atteint le serveur, celle-ci peut utiliser des parties de la base de code déjà chargées en mémoire sans entraîner de surcharge.
Alors, qu'entend-on par « partie de la base de code » ?
# Le préchargement en pratique
Afin de précharger, le développeur doit indiquez au serveur quels fichiers charger. Cela a été fait avec un simple script PHP et il n’y avait vraiment rien de difficile.
Les règles sont simples :
● Vous fournissez un script de préchargement et liez-le à votre fichier php.ini à l'aide de la commande opcache.preload.
● Chaque fichier PHP que vous souhaitez précharger doit être transmis à opcache_compile_file(), ou une seule fois dans le script de préchargement.
Supposons que vous souhaitiez précharger un framework, tel que Laravel. Votre script doit parcourir tous les fichiers PHP du répertoire supplier/laravel et les ajouter les uns après les autres.
Lien vers ce script dans php.ini comme suit :
opcache.preload=/path/to/project/preload.php
Il s'agit d'une implémentation factice :
$files = /* An array of files you want to preload */; foreach ($files as $file) { opcache_compile_file($file); }
# AVERTISSEMENT : impossible de précharger les classes liées inutilisées
etc., il y a un avertissement ! Afin de précharger les fichiers, leurs dépendances (interfaces, traits et classes parent) doivent également être préchargées.
S'il y a des problèmes avec les dépendances de classe, vous serez averti au démarrage du serveur :
Can't preload unlinked class Illuminate\Database\Query\JoinClause: Unknown parent Illuminate\Database\Query\Builder
Écoutez, opcache_compile_file() analysera un fichier mais ne l'exécutera pas. Cela signifie que si une classe a des dépendances qui ne sont pas préchargées, elle ne peut pas être préchargée elle-même.
Ce n'est pas un problème fatal et votre serveur fonctionnera correctement. Mais vous n’obtiendrez pas tous les fichiers préchargés souhaités.
Heureusement, il existe un moyen de garantir que le fichier lié est également chargé : vous pouvez utiliser require_once au lieu de opcache_compile_file et laisser le chargeur automatique enregistré (probablement celui du compositeur) s'occuper du reste.
$files = /* All files in eg. vendor/laravel */; foreach ($files as $file) { require_once($file); }
Il y a quelques autres choses à noter. Par exemple, si vous essayez de précharger Laravel, certaines classes du framework dépendent d'autres classes qui n'existent pas encore. Par exemple, le cache du système de fichiers d'éclairage de la classe de cache du système de fichiers dépend de LeagueFlysystemCachedStorageAbstractCache, et si vous n'avez jamais utilisé le cache du système de fichiers, vous ne pourrez peut-être pas l'installer dans votre projet.
Vous pouvez rencontrer des erreurs « classe non trouvée » lorsque vous essayez de tout précharger. Heureusement, dans une installation par défaut de Laravel, il n'existe que quelques-unes de ces classes et peuvent être facilement ignorées. Pour plus de commodité, j'ai écrit une petite classe de préchargement pour faciliter l'ignorance des fichiers, comme ceci :
class Preloader { private array $ignores = []; private static int $count = 0; private array $paths; private array $fileMap; public function __construct(string ...$paths) { $this->paths = $paths; // We'll use composer's classmap // to easily find which classes to autoload, // based on their filename $classMap = require __DIR__ . '/vendor/composer/autoload_classmap.php'; $this->fileMap = array_flip($classMap); } public function paths(string ...$paths): Preloader { $this->paths = array_merge( $this->paths, $paths ); return $this; } public function ignore(string ...$names): Preloader { $this->ignores = array_merge( $this->ignores, $names ); return $this; } public function load(): void { // We'll loop over all registered paths // and load them one by one foreach ($this->paths as $path) { $this->loadPath(rtrim($path, '/')); } $count = self::$count; echo "[Preloader] Preloaded {$count} classes" . PHP_EOL; } private function loadPath(string $path): void { // If the current path is a directory, // we'll load all files in it if (is_dir($path)) { $this->loadDir($path); return; } // Otherwise we'll just load this one file $this->loadFile($path); } private function loadDir(string $path): void { $handle = opendir($path); // We'll loop over all files and directories // in the current path, // and load them one by one while ($file = readdir($handle)) { if (in_array($file, ['.', '..'])) { continue; } $this->loadPath("{$path}/{$file}"); } closedir($handle); } private function loadFile(string $path): void { // We resolve the classname from composer's autoload mapping $class = $this->fileMap[$path] ?? null; // And use it to make sure the class shouldn't be ignored if ($this->shouldIgnore($class)) { return; } // Finally we require the path, // causing all its dependencies to be loaded as well require_once($path); self::$count++; echo "[Preloader] Preloaded `{$class}`" . PHP_EOL; } private function shouldIgnore(?string $name): bool { if ($name === null) { return true; } foreach ($this->ignores as $ignore) { if (strpos($name, $ignore) === 0) { return true; } } return false; } }
En ajoutant cette classe dans le même script de préchargement, nous pouvons maintenant charger ainsi l'intégralité du framework Laravel :
// … (new Preloader()) ->paths(__DIR__ . '/vendor/laravel') ->ignore( \Illuminate\Filesystem\Cache::class, \Illuminate\Log\LogManager::class, \Illuminate\Http\Testing\File::class, \Illuminate\Http\UploadedFile::class, \Illuminate\Support\Carbon::class, ) ->load();
#travail ?
C'est bien sûr la question la plus importante : tous les fichiers sont-ils chargés correctement ? Vous pouvez tester cela simplement en redémarrant le serveur puis en transférant la sortie de opcache_get_status() dans un script PHP. Vous verrez qu'il possède une clé appelée preload_statistics, qui répertoriera toutes les fonctions, classes et scripts préchargés ainsi que la mémoire consommée par les fichiers préchargés.
Prise en charge de # Composer
Une fonctionnalité prometteuse pourrait être la solution de préchargement automatique basée sur Composer, qui est déjà utilisée par la plupart des projets PHP modernes. Les gens travaillent sur l'ajout d'une option de configuration de préchargement dans composer.json qui générera les fichiers de préchargement pour vous ! Actuellement, cette fonctionnalité est encore en développement, mais vous pouvez la suivre ici !
# Exigences du serveur
Il y a deux autres choses importantes à mentionner concernant les devops lors de l'utilisation du préchargement.
Comme vous le savez déjà, vous devez spécifier une entrée dans le php.ini pour le préchargement. Cela signifie que si vous utilisez un hébergement mutualisé, vous ne pourrez pas configurer PHP librement. En pratique, vous avez besoin d'un serveur dédié (virtuel) pour pouvoir optimiser les fichiers préchargés pour des projets individuels. Rappelez-vous ceci.
N'oubliez pas non plus que vous devez redémarrer le serveur à chaque fois que vous devez recharger le fichier mémoire (cela est suffisant si vous utilisez php-fpm). Cela peut paraître évident pour la plupart des gens, mais cela mérite quand même d’être mentionné.
#Performance
Passons maintenant à la question la plus importante : le préchargement améliore-t-il vraiment les performances
La réponse est oui : Partagée par Ben Morel Voici les résultats ? quelques repères, qui peuvent être trouvés dans la même question du compositeur liée précédemment.
Fait intéressant, vous pouvez décider de précharger uniquement les "classes chaudes", qui sont des classes fréquemment utilisées dans votre base de code. Les tests de Ben montrent que le chargement d'environ 100 classes populaires génère en réalité de meilleurs gains de performances que le préchargement de toutes les classes. C'est la différence entre une augmentation des performances de 13 % et 17 %.
Bien sûr, les classes à précharger dépendent de votre projet spécifique. Il est sage de précharger autant que possible au début. Si vous avez besoin d'une petite augmentation en pourcentage, vous devrez surveiller votre code au moment de l'exécution.
Bien entendu, tout ce travail peut être automatisé et pourrait être possible à l'avenir.
Maintenant, la chose la plus importante à retenir est que composer ajoutera un support pour que vous n'ayez pas à créer les fichiers de préchargement vous-même, et il sera facile de configurer cette fonctionnalité sur votre serveur tant que vous en avez le contrôle total.
Traduction : https://stitcher.io/blog/preloading-in-php-74
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!