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 :
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.
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
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
| Directive | Valeur défaut | Rôle et conseil |
|---|---|---|
| opcache.enable | 1 | Active le cache en mode web. Toujours 1 en prod. |
| opcache.enable_cli | 0 | Active OPcache pour les scripts CLI / workers / cron. |
| opcache.memory_consumption | 128 | Mo de mémoire partagée. 128–256 pour gros projets. |
| opcache.interned_strings_buffer | 8 | Mo pour les chaînes internées. 16 pour frameworks. |
| opcache.max_accelerated_files | 10000 | Slots hash table. Monter à 30000+ pour Symfony/Laravel. |
| opcache.validate_timestamps | 1 | Détecte les fichiers modifiés. Mettre 0 en prod contrôlée. |
| opcache.revalidate_freq | 2 | Intervalle en secondes entre deux contrôles de timestamp. |
| opcache.save_comments | 1 | Conserver les docblocks. Obligatoire pour Doctrine, Symfony. |
| opcache.fast_shutdown | 0 | PHP 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.jit | off | PHP 8+ : active la compilation JIT. "tracing" est le plus efficace. |
| opcache.jit_buffer_size | 0 | Mo de RAM pour le code natif JIT. 64–128 Mo conseillé. |
| opcache.huge_code_pages | 0 | Huge Pages Linux (2 Mo). Gain marginal, nécessite configuration OS. |
| opcache.consistency_checks | 0 | Vé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
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
| Fonction | Description | Cas 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é
<?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
11 OPcache, APCu & WinCache
| Extension | Rôle principal | Complémentarité |
|---|---|---|
| OPcache | Cache d'opcodes compilés (bytecode PHP). | Obligatoire en production. |
| APCu | Cache de données applicatives clé/valeur en RAM. | Complémentaire à OPcache : sessions, résultats de requêtes. |
| Memcached / Redis | Cache distribué multi-serveur. | Pour architectures multi-instances. |
| WinCache | Cache 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 :
- Déployer les fichiers dans un nouveau dossier (ex :
/var/www/app-v2). - Vérifier que l'application démarre correctement.
- Effectuer le switch de symlink :
ln -sfn /var/www/app-v2 /var/www/current. - Exécuter
opcache_reset()ousystemctl reload php8.3-fpm. - 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
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=0avec Doctrine, PHPUnit ou Symfony. - Exposer publiquement un endpoint
opcache_reset(). - Sous-dimensionner
memory_consumptionsur 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).