Intermediaire 2 min de lecture · 244 mots

Optimisation des performances WordPress : Base de données et caching

Estimated reading time: 1 minute

Introduction

Les performances d’un site WordPress dépendent majoritairement de deux facteurs critiques : l’optimisation de la base de données et l’implémentation d’un système de cache efficace. En 2025, avec l’augmentation des exigences en matière de Core Web Vitals et l’importance croissante du temps de chargement pour le SEO, l’optimisation des performances n’est plus optionnelle. Ce guide complet vous accompagnera dans l’optimisation avancée de votre base de données et la mise en place de stratégies de caching performantes.

Optimisation de la base de données

Comprendre la structure de la base de données WordPress

WordPress utilise plusieurs tables principales :

  • wpposts : Articles, pages, CPT, révisions
  • wppostmeta : Métadonnées des posts
  • wpusers : Utilisateurs
  • wpusermeta : Métadonnées utilisateurs
  • wpoptions : Configuration et options (autoload)
  • wpterms, wptermtaxonomy, wptermrelationships : Taxonomies
  • wpcomments, wpcommentmeta : Commentaires
  • Optimiser les options autoload

    Le problème d’autoload est l’un des plus critiques pour les performances. WordPress charge automatiquement toutes les options avec autoload = 'yes' à chaque requête.


  Analyser et optimiser les options autoload
 /

class AutoloadOptimizer {

    /
      Analyser les options autoload
     /
    public static function analyzeautoload() {
        global $wpdb;

        $autoloaddata = $wpdb->getresults(
            "SELECT optionname, LENGTH(optionvalue) as optionsize
            FROM $wpdb->options
            WHERE autoload = 'yes'
            ORDER BY optionsize DESC
            LIMIT 50"
        );

        $totalsize = 0;
        $report = array();

        foreach ( $autoloaddata as $option ) {
            $totalsize += $option->optionsize;

            $report[] = array(
                'optionname' => $option->optionname,
                'size'        => self::formatbytes( $option->optionsize ),
                'sizebytes'  => $option->optionsize,
            );
        }

        errorlog( sprintf(
            'Total autoload size: %s (%d bytes)',
            self::formatbytes( $totalsize ),
            $totalsize
        ) );

        return array(
            'totalsize'  => $totalsize,
            'totalhuman' => self::formatbytes( $totalsize ),
            'options'     => $report,
        );
    }

    /
      Désactiver l'autoload pour des options spécifiques
     /
    public static function disableautoload( $optionnames ) {
        global $wpdb;

        if ( ! isarray( $optionnames ) ) {
            $optionnames = array( $optionnames );
        }

        foreach ( $optionnames as $optionname ) {
            $wpdb->update(
                $wpdb->options,
                array( 'autoload' => 'no' ),
                array( 'optionname' => $optionname ),
                array( '%s' ),
                array( '%s' )
            );

            errorlog( "Disabled autoload for: $optionname" );
        }

        // Nettoyer le cache
        wpcacheflush();
    }

    /
      Nettoyer les options orphelines
     /
    public static function cleanorphanedoptions() {
        global $wpdb;

        // Supprimer les transients expirés
        $deleted = $wpdb->query(
            "DELETE FROM $wpdb->options
            WHERE optionname LIKE 'transienttimeout%'
            AND optionvalue < UNIXTIMESTAMP()"
        );

        errorlog( "Deleted $deleted expired transient timeouts" );

        // Supprimer les transients correspondants
        $wpdb->query(
            "DELETE FROM $wpdb->options
            WHERE optionname LIKE 'transient%'
            AND optionname NOT LIKE 'transienttimeout%'
            AND optionname NOT IN (
                SELECT REPLACE(optionname, 'timeout', '')
                FROM $wpdb->options
                WHERE optionname LIKE 'transienttimeout%'
            )"
        );

        // Supprimer les options de plugins désactivés
        $deletedpluginoptions = $wpdb->query(
            "DELETE FROM $wpdb->options
            WHERE optionname LIKE '%plugin%'
            AND autoload = 'yes'
            AND optionname NOT IN (
                SELECT DISTINCT metakey
                FROM $wpdb->postmeta
            )"
        );

        errorlog( "Deleted $deletedpluginoptions orphaned plugin options" );

        return $deleted + $deletedpluginoptions;
    }

    /
      Formater les octets en format lisible
     /
    private static function formatbytes( $bytes ) {
        $units = array( 'B', 'KB', 'MB', 'GB' );

        for ( $i = 0; $bytes > 1024 && $i < count( $units ) - 1; $i++ ) {
            $bytes /= 1024;
        }

        return round( $bytes, 2 ) . ' ' . $units[ $i ];
    }
}

// Commande WP-CLI pour analyser l'autoload
if ( defined( 'WPCLI' ) && WPCLI ) {
    WPCLI::addcommand( 'autoload analyze', array( 'AutoloadOptimizer', 'analyzeautoload' ) );
    WPCLI::addcommand( 'autoload clean', array( 'AutoloadOptimizer', 'cleanorphanedoptions' ) );
}

Optimisation des requêtes avec indexation

Les index de base de données accélèrent considérablement les requêtes :


  Gestionnaire d'index de base de données
 /

class DatabaseIndexManager {

    /
      Ajouter des index pour optimiser les requêtes courantes
     /
    public static function addperformanceindexes() {
        global $wpdb;

        $indexes = array();

        // Index sur wppostmeta pour les requêtes méta fréquentes
        $indexes[] = array(
            'table' => $wpdb->postmeta,
            'name'  => 'metakeyvalue',
            'sql'   => "CREATE INDEX metakeyvalue ON {$wpdb->postmeta} (metakey(191), metavalue(100))",
        );

        // Index composite pour posttype et poststatus
        $indexes[] = array(
            'table' => $wpdb->posts,
            'name'  => 'typestatusdate',
            'sql'   => "CREATE INDEX typestatusdate ON {$wpdb->posts} (posttype, poststatus, postdate)",
        );

        // Index pour les recherches de commentaires
        $indexes[] = array(
            'table' => $wpdb->comments,
            'name'  => 'commentapproveddate',
            'sql'   => "CREATE INDEX commentapproveddate ON {$wpdb->comments} (commentapproved, commentdategmt)",
        );

        // Index pour les relations de termes
        $indexes[] = array(
            'table' => $wpdb->termrelationships,
            'name'  => 'objectidtermid',
            'sql'   => "CREATE INDEX objectidtermid ON {$wpdb->termrelationships} (objectid, termtaxonomyid)",
        );

        foreach ( $indexes as $index ) {
            if ( ! self::indexexists( $index['table'], $index['name'] ) ) {
                $result = $wpdb->query( $index['sql'] );

                if ( $result !== false ) {
                    errorlog( "Successfully created index: {$index['name']} on {$index['table']}" );
                } else {
                    errorlog( "Failed to create index: {$index['name']} on {$index['table']}" );
                }
            } else {
                errorlog( "Index already exists: {$index['name']} on {$index['table']}" );
            }
        }
    }

    /
      Vérifier si un index existe
     /
    private static function indexexists( $table, $indexname ) {
        global $wpdb;

        $result = $wpdb->getvar(
            $wpdb->prepare(
                "SHOW INDEX FROM $table WHERE Keyname = %s",
                $indexname
            )
        );

        return ! isnull( $result );
    }

    /
      Analyser et recommander des index
     /
    public static function analyzeslowqueries() {
        global $wpdb;

        // Activer le slow query log (nécessite des privilèges MySQL)
        $wpdb->query( "SET GLOBAL slowquerylog = 'ON'" );
        $wpdb->query( "SET GLOBAL longquerytime = 1" );

        // Analyser les requêtes lentes
        $slowqueries = $wpdb->getresults( "SHOW PROCESSLIST" );

        $recommendations = array();

        foreach ( $slowqueries as $query ) {
            if ( $query->Time > 1 ) {
                $recommendations[] = array(
                    'query'    => $query->Info,
                    'duration' => $query->Time,
                    'state'    => $query->State,
                );
            }
        }

        return $recommendations;
    }

    /
      Optimiser toutes les tables
     /
    public static function optimizetables() {
        global $wpdb;

        $tables = $wpdb->getcol( "SHOW TABLES" );
        $results = array();

        foreach ( $tables as $table ) {
            $result = $wpdb->query( "OPTIMIZE TABLE $table" );
            $results[ $table ] = $result !== false;

            errorlog( "Optimized table: $table - " . ( $results[ $table ] ? 'Success' : 'Failed' ) );
        }

        return $results;
    }

    /
      Nettoyer les révisions de posts
     /
    public static function cleanpostrevisions( $keep = 5 ) {
        global $wpdb;

        // Trouver et supprimer les vieilles révisions
        $deleted = $wpdb->query(
            $wpdb->prepare(
                "DELETE FROM $wpdb->posts
                WHERE posttype = 'revision'
                AND postparent IN (
                    SELECT p.ID FROM (
                        SELECT ID FROM $wpdb->posts
                        WHERE posttype NOT IN ('revision', 'auto-draft')
                    ) p
                )
                AND ID NOT IN (
                    SELECT ID FROM (
                        SELECT r.ID
                        FROM $wpdb->posts r
                        WHERE r.posttype = 'revision'
                        ORDER BY r.postmodified DESC
                        LIMIT %d
                    ) keeprevisions
                )",
                $keep
            )
        );

        errorlog( "Deleted $deleted old revisions (keeping $keep per post)" );

        return $deleted;
    }

    /
      Nettoyer les auto-drafts
     /
    public static function cleanautodrafts() {
        global $wpdb;

        $deleted = $wpdb->query(
            "DELETE FROM $wpdb->posts
            WHERE poststatus = 'auto-draft'
            AND postmodified < DATESUB(NOW(), INTERVAL 7 DAY)"
        );

        errorlog( "Deleted $deleted auto-drafts older than 7 days" );

        return $deleted;
    }
}

// Exécuter au switch de thème ou activation de plugin
addaction( 'afterswitchtheme', array( 'DatabaseIndexManager', 'addperformanceindexes' ) );

// Tâche cron pour maintenance
addaction( 'monthemeweeklymaintenance', function() {
    DatabaseIndexManager::cleanpostrevisions( 5 );
    DatabaseIndexManager::cleanautodrafts();
    AutoloadOptimizer::cleanorphanedoptions();
    DatabaseIndexManager::optimizetables();
} );

// Enregistrer la tâche cron
if ( ! wpnextscheduled( 'monthemeweeklymaintenance' ) ) {
    wpscheduleevent( time(), 'weekly', 'monthemeweeklymaintenance' );
}

Optimiser les requêtes WPQuery


  Optimisations de WPQuery
 /

class QueryOptimizer {

    /
      Exemple de requête optimisée pour l'affichage
     /
    public static function getoptimizedposts( $args = array() ) {
        $defaults = array(
            'posttype'              => 'post',
            'postsperpage'         => 10,
            'nofoundrows'          => true,  // Désactiver le comptage total
            'updatepostmetacache' => false, // Désactiver si pas de meta
            'updateposttermcache' => false, // Désactiver si pas de termes
            'fields'                 => 'ids', // Récupérer seulement les IDs
        );

        $args = wpparseargs( $args, $defaults );

        return new WPQuery( $args );
    }

    /
      Requête avec cache personnalisé
     /
    public static function getcachedposts( $args = array(), $cacheduration = 3600 ) {
        $cachekey = 'query' . md5( serialize( $args ) );

        $cached = wpcacheget( $cachekey, 'monthemequeries' );

        if ( false !== $cached ) {
            return $cached;
        }

        $query = new WPQuery( $args );
        $posts = $query->posts;

        wpcacheset( $cachekey, $posts, 'monthemequeries', $cacheduration );

        return $posts;
    }

    /
      Pré-charger les métadonnées pour éviter les requêtes multiples
     /
    public static function primepostcaches( $postids ) {
        if ( empty( $postids ) ) {
            return;
        }

        // Pré-charger les posts
        primepostcaches( $postids, true, true );

        // Pré-charger les termes
        updateobjecttermcache( $postids, 'post' );

        // Pré-charger les métadonnées
        updatemetacache( 'post', $postids );
    }

    /
      Requête SQL directe pour de meilleures performances
     /
    public static function getpostsraw( $posttype = 'post', $limit = 10 ) {
        global $wpdb;

        $cachekey = "rawposts{$posttype}{$limit}";

        $posts = wpcacheget( $cachekey, 'monthemequeries' );

        if ( false === $posts ) {
            $posts = $wpdb->getresults(
                $wpdb->prepare(
                    "SELECT ID, posttitle, postname, postdate
                    FROM $wpdb->posts
                    WHERE posttype = %s
                    AND poststatus = 'publish'
                    ORDER BY postdate DESC
                    LIMIT %d",
                    $posttype,
                    $limit
                )
            );

            wpcacheset( $cachekey, $posts, 'monthemequeries', 3600 );
        }

        return $posts;
    }

    /
      Désactiver les requêtes inutiles sur certaines pages
     /
    public static function disableunnecessaryqueries() {
        // Désactiver les emojis (économise 2 requêtes)
        removeaction( 'wphead', 'printemojidetectionscript', 7 );
        removeaction( 'adminprintscripts', 'printemojidetectionscript' );
        removeaction( 'wpprintstyles', 'printemojistyles' );
        removeaction( 'adminprintstyles', 'printemojistyles' );

        // Désactiver les embeds (économise plusieurs requêtes)
        removeaction( 'wphead', 'wpoembedadddiscoverylinks' );
        removeaction( 'wphead', 'wpoembedaddhostjs' );

        // Désactiver XML-RPC si non utilisé
        addfilter( 'xmlrpcenabled', 'returnfalse' );

        // Limiter les révisions
        if ( ! defined( 'WPPOSTREVISIONS' ) ) {
            define( 'WPPOSTREVISIONS', 3 );
        }

        // Augmenter l'intervalle d'autosave
        if ( ! defined( 'AUTOSAVEINTERVAL' ) ) {
            define( 'AUTOSAVEINTERVAL', 300 ); // 5 minutes
        }
    }
}

addaction( 'init', array( 'QueryOptimizer', 'disableunnecessaryqueries' ) );

Stratégies de caching avancées

Object Cache avec Redis


  Implémentation du cache objet avec Redis
 /

class RedisObjectCache {

    private $redis;
    private $prefix;
    private $defaultexpiration = 3600;

    /
      Constructeur
     /
    public function construct() {
        $this->prefix = 'wp' . DBNAME . ':';

        if ( classexists( 'Redis' ) ) {
            $this->redis = new Redis();

            try {
                $this->redis->connect(
                    defined( 'WPREDISHOST' ) ? WPREDISHOST : '127.0.0.1',
                    defined( 'WPREDISPORT' ) ? WPREDISPORT : 6379
                );

                if ( defined( 'WPREDISPASSWORD' ) && WPREDISPASSWORD ) {
                    $this->redis->auth( WPREDISPASSWORD );
                }

                $this->redis->setOption( Redis::OPTSERIALIZER, Redis::SERIALIZERPHP );
                $this->redis->setOption( Redis::OPTPREFIX, $this->prefix );

            } catch ( Exception $e ) {
                errorlog( 'Redis connection failed: ' . $e->getMessage() );
                $this->redis = null;
            }
        }
    }

    /
      Récupérer une valeur du cache
     /
    public function get( $key, $group = 'default' ) {
        if ( ! $this->redis ) {
            return wpcacheget( $key, $group );
        }

        $cachekey = $this->buildkey( $key, $group );

        try {
            $value = $this->redis->get( $cachekey );
            return $value !== false ? $value : false;
        } catch ( Exception $e ) {
            errorlog( 'Redis get failed: ' . $e->getMessage() );
            return false;
        }
    }

    /
      Définir une valeur dans le cache
     /
    public function set( $key, $value, $group = 'default', $expiration = 0 ) {
        if ( ! $this->redis ) {
            return wpcacheset( $key, $value, $group, $expiration );
        }

        $cachekey = $this->buildkey( $key, $group );
        $expiration = $expiration ?: $this->defaultexpiration;

        try {
            return $this->redis->setex( $cachekey, $expiration, $value );
        } catch ( Exception $e ) {
            errorlog( 'Redis set failed: ' . $e->getMessage() );
            return false;
        }
    }

    /
      Supprimer une valeur du cache
     /
    public function delete( $key, $group = 'default' ) {
        if ( ! $this->redis ) {
            return wpcachedelete( $key, $group );
        }

        $cachekey = $this->buildkey( $key, $group );

        try {
            return $this->redis->del( $cachekey ) > 0;
        } catch ( Exception $e ) {
            errorlog( 'Redis delete failed: ' . $e->getMessage() );
            return false;
        }
    }

    /
      Vider un groupe de cache
     /
    public function flushgroup( $group ) {
        if ( ! $this->redis ) {
            return wpcacheflushgroup( $group );
        }

        try {
            $pattern = $this->prefix . $group . ':';
            $keys = $this->redis->keys( $pattern );

            if ( ! empty( $keys ) ) {
                return $this->redis->del( $keys ) > 0;
            }

            return true;
        } catch ( Exception $e ) {
            errorlog( 'Redis flush group failed: ' . $e->getMessage() );
            return false;
        }
    }

    /
      Récupérer ou générer avec callback
     /
    public function remember( $key, $group, $callback, $expiration = 0 ) {
        $value = $this->get( $key, $group );

        if ( false === $value ) {
            $value = $callback();
            $this->set( $key, $value, $group, $expiration );
        }

        return $value;
    }

    /
      Construire la clé de cache
     /
    private function buildkey( $key, $group ) {
        return $group . ':' . $key;
    }

    /
      Obtenir les statistiques Redis
     /
    public function getstats() {
        if ( ! $this->redis ) {
            return array();
        }

        try {
            $info = $this->redis->info();

            return array(
                'connected'       => true,
                'memoryused'     => $info['usedmemoryhuman'] ?? 'N/A',
                'totalkeys'      => $this->redis->dbSize(),
                'hits'            => $info['keyspacehits'] ?? 0,
                'misses'          => $info['keyspacemisses'] ?? 0,
                'hitrate'        => $this->calculatehitrate( $info ),
                'evictedkeys'    => $info['evictedkeys'] ?? 0,
                'connectedclients' => $info['connectedclients'] ?? 0,
            );
        } catch ( Exception $e ) {
            errorlog( 'Redis stats failed: ' . $e->getMessage() );
            return array( 'connected' => false );
        }
    }

    /
      Calculer le taux de hits
     /
    private function calculatehitrate( $info ) {
        $hits = $info['keyspacehits'] ?? 0;
        $misses = $info['keyspacemisses'] ?? 0;
        $total = $hits + $misses;

        if ( $total === 0 ) {
            return 0;
        }

        return round( ( $hits / $total )  100, 2 );
    }
}

// Initialiser le cache Redis
global $monthemerediscache;
$monthemerediscache = new RedisObjectCache();

Page Cache avancé


  Système de page cache avancé
 /

class AdvancedPageCache {

    private $cachedir;
    private $cacheduration = 3600;
    private $mobilecache = true;
    private $usercache = false;

    /
      Constructeur
     /
    public function construct() {
        $this->cachedir = WPCONTENTDIR . '/cache/montheme-page-cache/';

        addaction( 'init', array( $this, 'servecachedpage' ), 1 );
        addaction( 'shutdown', array( $this, 'cachepage' ) );
        addaction( 'savepost', array( $this, 'clearcacheforpost' ) );
        addaction( 'commentpost', array( $this, 'clearcacheforpost' ) );
        addaction( 'switchtheme', array( $this, 'clearallcache' ) );
    }

    /
      Servir une page en cache
     /
    public function servecachedpage() {
        // Ne pas mettre en cache si :
        if ( isadmin() ||
             isuserloggedin() ||
             ( defined( 'DOINGAJAX' ) && DOINGAJAX ) ||
             ( defined( 'DOINGCRON' ) && DOINGCRON ) ||
             ( defined( 'WPCLI' ) && WPCLI ) ||
             'GET' !== $SERVER['REQUESTMETHOD'] ) {
            return;
        }

        $cachefile = $this->getcachefilepath();

        if ( ! $cachefile || ! fileexists( $cachefile ) ) {
            return;
        }

        // Vérifier si le cache est expiré
        if ( time() - filemtime( $cachefile ) > $this->cacheduration ) {
            @unlink( $cachefile );
            return;
        }

        // Servir le cache
        $cachedcontent = filegetcontents( $cachefile );

        if ( $cachedcontent ) {
            // Ajouter des en-têtes
            header( 'X-Cache-Status: HIT' );
            header( 'X-Cache-Time: ' . date( 'Y-m-d H:i:s', filemtime( $cachefile ) ) );

            echo $cachedcontent;
            exit;
        }
    }

    /
      Mettre une page en cache
     /
    public function cachepage() {
        // Conditions pour ne pas mettre en cache
        if ( isadmin() ||
             isuserloggedin() ||
             is404() ||
             issearch() ||
             isfeed() ||
             ( defined( 'DOINGAJAX' ) && DOINGAJAX ) ||
             'GET' !== $SERVER['REQUESTMETHOD'] ) {
            return;
        }

        // Récupérer le contenu de la page
        $content = obgetcontents();

        if ( empty( $content ) || strlen( $content ) < 500 ) {
            return;
        }

        $cachefile = $this->getcachefilepath();

        if ( ! $cachefile ) {
            return;
        }

        // Créer le répertoire si nécessaire
        $cachedir = dirname( $cachefile );
        if ( ! fileexists( $cachedir ) ) {
            wpmkdirp( $cachedir );
        }

        // Ajouter un commentaire avec la date de mise en cache
        $content .= "n";

        // Écrire le fichier de cache
        fileputcontents( $cachefile, $content, LOCKEX );

        // Ajouter un en-tête indiquant que c'est un MISS
        header( 'X-Cache-Status: MISS' );
    }

    /
      Obtenir le chemin du fichier de cache
     /
    private function getcachefilepath() {
        $requesturi = $SERVER['REQUESTURI'] ?? '/';

        // Nettoyer l'URI
        $requesturi = trim( $requesturi, '/' );
        $requesturi = strreplace( array( '../', './', '//' ), '', $requesturi );

        if ( empty( $requesturi ) ) {
            $requesturi = 'index';
        }

        // Sous-répertoire pour mobile si activé
        $subdir = '';
        if ( $this->mobilecache && wpismobile() ) {
            $subdir = 'mobile/';
        }

        // Hash pour la sécurité
        $hash = md5( $requesturi );

        return $this->cachedir . $subdir . $hash . '.html';
    }

    /
      Nettoyer le cache pour un post spécifique
     /
    public function clearcacheforpost( $postid ) {
        $post = getpost( $postid );

        if ( ! $post ) {
            return;
        }

        // Nettoyer la page du post
        $posturl = getpermalink( $postid );
        $this->clearurlcache( $posturl );

        // Nettoyer la page d'accueil
        $this->clearurlcache( homeurl( '/' ) );

        // Nettoyer les archives liées
        if ( $post->posttype === 'post' ) {
            $this->clearurlcache( getposttypearchivelink( 'post' ) );

            // Nettoyer les archives de catégories
            $categories = getthecategory( $postid );
            foreach ( $categories as $category ) {
                $this->clearurlcache( getcategorylink( $category->termid ) );
            }
        }
    }

    /
      Nettoyer le cache d'une URL
     /
    private function clearurlcache( $url ) {
        $requesturi = strreplace( homeurl(), '', $url );
        $requesturi = trim( $requesturi, '/' );

        if ( empty( $requesturi ) ) {
            $requesturi = 'index';
        }

        $hash = md5( $requesturi );

        // Desktop
        $cachefile = $this->cachedir . $hash . '.html';
        if ( fileexists( $cachefile ) ) {
            @unlink( $cachefile );
        }

        // Mobile
        if ( $this->mobilecache ) {
            $mobilecachefile = $this->cachedir . 'mobile/' . $hash . '.html';
            if ( fileexists( $mobilecachefile ) ) {
                @unlink( $mobilecachefile );
            }
        }
    }

    /
      Vider tout le cache
     /
    public function clearallcache() {
        $this->deletedirectorycontents( $this->cachedir );

        errorlog( 'Page cache cleared' );
    }

    /
      Supprimer le contenu d'un répertoire
     /
    private function deletedirectorycontents( $dir ) {
        if ( ! isdir( $dir ) ) {
            return;
        }

        $files = glob( $dir . '', GLOBMARK );

        foreach ( $files as $file ) {
            if ( isdir( $file ) ) {
                $this->deletedirectorycontents( $file );
                @rmdir( $file );
            } else {
                @unlink( $file );
            }
        }
    }

    /
      Obtenir les statistiques de cache
     /
    public function getstats() {
        if ( ! isdir( $this->cachedir ) ) {
            return array(
                'totalfiles' => 0,
                'totalsize'  => 0,
                'oldest'      => null,
                'newest'      => null,
            );
        }

        $files = glob( $this->cachedir . '.html' );
        $totalsize = 0;
        $oldest = PHPINTMAX;
        $newest = 0;

        foreach ( $files as $file ) {
            $totalsize += filesize( $file );
            $mtime = filemtime( $file );

            if ( $mtime < $oldest ) {
                $oldest = $mtime;
            }

            if ( $mtime > $newest ) {
                $newest = $mtime;
            }
        }

        return array(
            'totalfiles' => count( $files ),
            'totalsize'  => $this->formatbytes( $totalsize ),
            'oldest'      => $oldest !== PHPINTMAX ? date( 'Y-m-d H:i:s', $oldest ) : null,
            'newest'      => $newest !== 0 ? date( 'Y-m-d H:i:s', $newest ) : null,
        );
    }

    /
      Formater les octets
     /
    private function formatbytes( $bytes ) {
        $units = array( 'B', 'KB', 'MB', 'GB' );

        for ( $i = 0; $bytes > 1024 && $i < count( $units ) - 1; $i++ ) {
            $bytes /= 1024;
        }

        return round( $bytes, 2 ) . ' ' . $units[ $i ];
    }
}

// Initialiser le page cache
new AdvancedPageCache();

Fragment Caching


  Helper pour le fragment caching
 /

class FragmentCache {

    /
      Récupérer ou générer un fragment
     /
    public static function get( $key, $callback, $expiration = 3600 ) {
        $cachekey = 'fragment' . $key;

        $output = gettransient( $cachekey );

        if ( false === $output ) {
            obstart();
            $callback();
            $output = obgetclean();

            settransient( $cachekey, $output, $expiration );
        }

        return $output;
    }

    /
      Invalider un fragment
     /
    public static function delete( $key ) {
        $cachekey = 'fragment' . $key;
        deletetransient( $cachekey );
    }

    /
      Invalider tous les fragments
     /
    public static function flush() {
        global $wpdb;

        $wpdb->query(
            "DELETE FROM $wpdb->options
            WHERE optionname LIKE 'transientfragment%'
            OR optionname LIKE 'transienttimeoutfragment%'"
        );
    }
}

// Exemple d'utilisation dans un template
function monthemedisplaypopularposts() {
    echo FragmentCache::get( 'popularposts', function() {
        $popular = new WPQuery( array(
            'posttype'      => 'post',
            'postsperpage' => 5,
            'metakey'       => 'postviewscount',
            'orderby'        => 'metavaluenum',
            'order'          => 'DESC',
        ) );

        if ( $popular->haveposts() ) {
            echo '

Une remarque, un retour ?

Cet article est vivant — corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisissez celui qui vous convient.