PHP Extension — Zend OPcache

OPcache : guide complet

Comprendre, configurer et administrer le cache d'opcodes de PHP pour maximiser les performances des applications web en environnement universitaire et production.

PHP 5.5+ intégré Très utile en production PHP 7.4 / PHP 8.x JIT PHP 8 Mémoire partagée Opcodes Zend

01 Qu'est-ce qu'OPcache ?

OPcache (Optimizer + Cache) est un moteur de cache d'opcodes fourni par Zend Technologies et intégré nativement à PHP depuis la version 5.5. Il remplace l'ancien APC (Alternative PHP Cache) pour la partie cache de bytecode.

Sans OPcache, chaque requête HTTP qui déclenche un script PHP passe par ce cycle complet :

① Lecture disque du fichier .php ② Analyse lexicale (tokenisation) ③ Analyse syntaxique (AST) ④ Compilation en opcodes Zend ⑤ Exécution par la Zend VM

Avec OPcache, les étapes ①–④ ne se font qu'une seule fois. Les opcodes compilés sont stockés en mémoire partagée et réutilisés directement par tous les workers PHP-FPM/Apache, sans relire ni recompiler le fichier.

⚡ Gains typiques mesurés en production : 2× à 10× de réduction du temps CPU selon la taille du projet, le nombre de fichiers et la charge serveur.

02 Architecture interne

OPcache s'appuie sur trois zones mémoire distinctes allouées au démarrage du processus PHP :

🗄 Shared Memory Segment

Zone mémoire partagée entre tous les workers (FPM, prefork). Contient les opcodes compilés. Taille contrôlée par memory_consumption.

🔤 Interned Strings Buffer

Stocke les chaînes dupliquées (noms de fonctions, clés de tableaux…) en un seul exemplaire partagé. Contrôlé par interned_strings_buffer.

📁 File Cache (optionnel)

Depuis PHP 7.0, OPcache peut persister les opcodes sur disque via opcache.file_cache. Utile lors des redémarrages de processus.

🔒 Hash Table interne

Index des scripts mis en cache (chemin → pointeur opcode). Taille maximale fixée par max_accelerated_files.

03 Installation & activation

OPcache est livré avec PHP depuis PHP 5.5 mais peut nécessiter une activation explicite.

Linux / Unix (php.ini)

; Charger l'extension (chemin selon la distro)
zend_extension=opcache.so

Windows

zend_extension=php_opcache.dll

Packages Debian / Ubuntu

sudo apt install php8.3-opcache
sudo phpenmod opcache
sudo systemctl restart php8.3-fpm

Docker (exemple Dockerfile)

FROM php:8.3-fpm-alpine
RUN docker-php-ext-enable opcache
💡 Après toute modification de php.ini, redémarrer le service concerné : php-fpm, apache2, nginx ou iis.

04 Configuration complète

Toutes les directives se placent dans php.ini ou dans un fichier /etc/php/8.x/conf.d/10-opcache.ini.

; ── Activation ───────────────────────────────────
opcache.enable=1
opcache.enable_cli=1

; ── Mémoire ──────────────────────────────────────
opcache.memory_consumption=128         ; Mo de RAM partagée
opcache.interned_strings_buffer=16     ; Mo pour les strings
opcache.max_accelerated_files=20000    ; slots dans la hash table

; ── Validation des fichiers source ───────────────
opcache.validate_timestamps=1          ; 0 en prod maîtrisée
opcache.revalidate_freq=2              ; secondes entre 2 vérifs
opcache.validate_permission=0         ; vérif droits fichiers
opcache.validate_root=0               ; protection symlinks

; ── Comportement ─────────────────────────────────
opcache.save_comments=1               ; garder annotations PHPDoc
opcache.fast_shutdown=1               ; libération mémoire rapide
opcache.consistency_checks=0         ; vérif checksums (coûteux)
opcache.huge_code_pages=0            ; huge pages Linux (avancé)

; ── Cache fichier (PHP 7.0+) ─────────────────────
opcache.file_cache=/tmp/php-opcache  ; persiste sur disque
opcache.file_cache_only=0            ; 1 = désactive cache RAM
opcache.file_cache_consistency_checks=1

; ── Sécurité ─────────────────────────────────────
opcache.restrict_api=""              ; préfixe chemin autorisé

; ── JIT (PHP 8.0+ seulement) ─────────────────────
opcache.jit=tracing                  ; tracing | function | off
opcache.jit_buffer_size=64M

05 Tableau des directives

DirectiveValeur défautRôle et conseil
opcache.enable1Active le cache en mode web. Toujours 1 en prod.
opcache.enable_cli0Active OPcache pour les scripts CLI / workers / cron.
opcache.memory_consumption128Mo de mémoire partagée. 128–256 pour gros projets.
opcache.interned_strings_buffer8Mo pour les chaînes internées. 16 pour frameworks.
opcache.max_accelerated_files10000Slots hash table. Monter à 30000+ pour Symfony/Laravel.
opcache.validate_timestamps1Détecte les fichiers modifiés. Mettre 0 en prod contrôlée.
opcache.revalidate_freq2Intervalle en secondes entre deux contrôles de timestamp.
opcache.save_comments1Conserver les docblocks. Obligatoire pour Doctrine, Symfony.
opcache.fast_shutdown0PHP 7.2+ : comportement intégré, directive ignorée.
opcache.file_cache""Chemin de cache disque. Accélère le warm-up après redémarrage.
opcache.restrict_api""Restreint opcache_*() à un préfixe de chemin. Sécurité admin.
opcache.jitoffPHP 8+ : active la compilation JIT. "tracing" est le plus efficace.
opcache.jit_buffer_size0Mo de RAM pour le code natif JIT. 64–128 Mo conseillé.
opcache.huge_code_pages0Huge Pages Linux (2 Mo). Gain marginal, nécessite configuration OS.
opcache.consistency_checks0Vérification CRC des opcodes. Impact performance, réservé au debug.

06 Développement vs Production

🔧 Développement

Changements de fichiers visibles immédiatement.

opcache.enable=1
opcache.validate_timestamps=1
opcache.revalidate_freq=0
opcache.file_cache=""

🚀 Production standard

Bon équilibre fraîcheur / performance.

opcache.enable=1
opcache.validate_timestamps=1
opcache.revalidate_freq=2
opcache.memory_consumption=128

⚡ Production maximale

Reset manuel requis lors des déploiements.

opcache.enable=1
opcache.validate_timestamps=0
opcache.memory_consumption=256
opcache.max_accelerated_files=30000
⚠️ En production avec validate_timestamps=0, tout déploiement sans opcache_reset() ou redémarrage FPM peut servir l'ancienne version du code.

07 Vérification et diagnostic

Vérification rapide via CLI

php -r "var_dump(extension_loaded('Zend OPcache'), ini_get('opcache.enable'));"

Via phpinfo()

<?php phpinfo(); // Chercher la section « Zend OPcache »

Script de diagnostic complet

<?php
header('Content-Type: text/plain; charset=utf-8');

if (!extension_loaded('Zend OPcache')) {
    exit("OPcache non chargé.\n");
}

$status = opcache_get_status(false);
$config = opcache_get_configuration();

// Statistiques mémoire
$mem     = $status['memory_usage'];
$usedMb  = round($mem['used_memory']      / 1048576, 2);
$freeMb  = round($mem['free_memory']      / 1048576, 2);
$wasteMb = round($mem['wasted_memory']    / 1048576, 2);

echo "=== OPcache Diagnostics ===\n";
echo "Activé     : " . ($status['opcache_enabled'] ? 'oui' : 'non') . "\n";
echo "RAM utilisée: {$usedMb} Mo\n";
echo "RAM libre   : {$freeMb} Mo\n";
echo "RAM gaspillée: {$wasteMb} Mo\n";

$stats = $status['opcache_statistics'];
$ratio = $stats['opcache_hit_rate'] ?? 0;

echo "Scripts en cache: {$stats['num_cached_scripts']}\n";
echo "Hits   : {$stats['hits']}\n";
echo "Misses : {$stats['misses']}\n";
echo "Taux de hit : " . round($ratio, 2) . "%\n";

08 Fonctions PHP disponibles

FonctionDescriptionCas d'usage
opcache_get_status()Retourne statistiques, hits, mémoire, liste des scripts.Monitoring, dashboard admin.
opcache_get_configuration()Retourne la config effective (toutes directives).Audit configuration serveur.
opcache_reset()Vide tout le cache en mémoire.Après un déploiement.
opcache_invalidate($file, $force)Invalide un fichier précis. $force=true force la suppression.Déploiement partiel, hot-reload.
opcache_compile_file($file)Précompile un fichier sans l'exécuter.Warm-up après déploiement.
opcache_is_script_cached($file)Retourne true si le fichier est dans le cache.Vérification de warm-up.

09 Script d'administration sécurisé

🔒 Protéger tout endpoint d'administration OPcache par IP, token ou authentification. Une réinitialisation publique est une faille de sécurité.
<?php
/**
 * opcache-admin.php — Administration sécurisée
 * À placer hors du webroot public ou à protéger par htpasswd / IP.
 */

// Sécurité : token secret
define('SECRET_TOKEN', 'votre_token_secret_ici');

if (($_GET['token'] ?? '') !== SECRET_TOKEN) {
    http_response_code(403);
    exit('Accès refusé.');
}

header('Content-Type: application/json; charset=utf-8');

$action = $_GET['action'] ?? 'status';
$result = ['ok' => false, 'action' => $action];

switch ($action) {
    case 'reset':
        $result['ok'] = opcache_reset();
        break;

    case 'invalidate':
        $file = realpath($_GET['file'] ?? '');
        if ($file && file_exists($file)) {
            $result['ok']   = opcache_invalidate($file, true);
            $result['file'] = $file;
        }
        break;

    case 'warmup':
        $dir   = realpath($_GET['dir'] ?? __DIR__);
        $count = 0;
        if ($dir) {
            $iter = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($dir)
            );
            foreach ($iter as $f) {
                if ($f->getExtension() === 'php') {
                    opcache_compile_file($f->getRealPath());
                    $count++;
                }
            }
        }
        $result['ok']            = true;
        $result['compiled_files'] = $count;
        break;

    default: // status
        $result['ok']     = true;
        $result['status'] = opcache_get_status(false);
        $result['config'] = opcache_get_configuration();
}

echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

10 OPcache et JIT (PHP 8+)

PHP 8.0 a introduit la compilation Just-In-Time dans OPcache. Le JIT traduit les opcodes Zend en code machine natif lors de l'exécution.

Mode function

Compile les fonctions entières en code natif. Simple, prédictible.

Mode tracing

Observe les chemins chauds et les compile dynamiquement. Meilleur gain pour code CPU-intensif.

Mode off

JIT désactivé. OPcache reste actif. Recommandé pour la majorité des apps web CRUD.

; Configuration JIT recommandée (PHP 8+)
opcache.jit=tracing
opcache.jit_buffer_size=64M
ℹ️ Le JIT apporte peu de gain pour des applications web classiques (I/O-bound). Son avantage est mesurable surtout pour des traitements mathématiques, ML, rendu d'image, etc. Les benchmarks de PHP 8.3 montrent +10–30% sur code CPU-intensif en mode tracing.

11 OPcache, APCu & WinCache

ExtensionRôle principalComplémentarité
OPcacheCache d'opcodes compilés (bytecode PHP).Obligatoire en production.
APCuCache de données applicatives clé/valeur en RAM.Complémentaire à OPcache : sessions, résultats de requêtes.
Memcached / RedisCache distribué multi-serveur.Pour architectures multi-instances.
WinCacheCache opcodes + données sous Windows/IIS.Alternative sur environnement Windows uniquement.

12 Symptômes fréquents et solutions

🔄 Code non mis à jour

Souvent validate_timestamps=0 oublié après déploiement. Solution : opcache_reset() ou redémarrage FPM.

💾 Mémoire saturée

Augmenter memory_consumption (256 Mo) ou max_accelerated_files. Vérifier les wasted_memory dans opcache_get_status().

♻️ Trop de restarts

OPcache se réinitialise car il n'a plus de place. Augmenter memory_consumption et max_accelerated_files.

🖥 CLI ≠ Web

Deux php.ini différents. Vérifier avec php --ini vs phpinfo() en web, et opcache.enable_cli.

📝 Annotations ignorées

Mettre save_comments=0 casse Doctrine, Symfony, PHPUnit. Toujours conserver à 1.

🐳 Docker / conteneurs

Chaque conteneur a sa propre mémoire partagée. Pas de partage inter-conteneurs. Configurer par instance.

13 Déploiement propre

La stratégie de déploiement la plus sûre avec OPcache :

  1. Déployer les fichiers dans un nouveau dossier (ex : /var/www/app-v2).
  2. Vérifier que l'application démarre correctement.
  3. Effectuer le switch de symlink : ln -sfn /var/www/app-v2 /var/www/current.
  4. Exécuter opcache_reset() ou systemctl reload php8.3-fpm.
  5. Optionnel : lancer un warm-up pour précompiler les fichiers critiques.
# Script de déploiement Shell
NEW_RELEASE=/var/www/releases/$(date +%Y%m%d_%H%M)
rsync -avz ./dist/ $NEW_RELEASE/
ln -sfn $NEW_RELEASE /var/www/current
php -r "opcache_reset();"
echo "Déploiement terminé."

14 Configuration conseillée — gros projet PHP 8

; /etc/php/8.3/fpm/conf.d/10-opcache.ini
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=24
opcache.max_accelerated_files=50000
opcache.validate_timestamps=0       ; reset manuel au déploiement
opcache.save_comments=1
opcache.file_cache=/tmp/php-opcache
opcache.file_cache_consistency_checks=1
opcache.jit=tracing
opcache.jit_buffer_size=128M
✅ OPcache est l'une des optimisations les plus rentables pour PHP — souvent la première à activer avant tout autre réglage serveur. Sur PHP 8.x, combiner OPcache + JIT tracing donne les meilleures performances pour des traitements lourds.

15 Pièges classiques — checklist

  • Modifier le mauvais php.ini (CLI vs FPM vs Apache).
  • Oublier de redémarrer le service après modification de la config.
  • Penser que le navigateur cache alors que c'est OPcache qui sert l'ancienne version.
  • Mettre save_comments=0 avec Doctrine, PHPUnit ou Symfony.
  • Exposer publiquement un endpoint opcache_reset().
  • Sous-dimensionner memory_consumption sur un projet Composer volumineux.
  • Oublier le warm-up après un déploiement en production maximale.
  • Activer le JIT sans tester : peut ralentir certaines apps web classiques.
  • Confondre OPcache (bytecode) et APCu (données applicatives).