Contenu principal
Accélérer WordPress en divisant functions.php

Accélérer WordPress en divisant le fichier functions.php

A l’heure où la vitesse de navigation est un des facteurs les plus importants pour nos sites et blogs alors que ceux-ci se remplissent de plus en plus de fonctionnalités pour nos visiteurs, toutes les méthodes sont bonnes pour gagner ces précieuses millisecondes.
Voici une petite astuce que je viens de trouver qui va donner un petit coup de boost à votre site WordPress, sans pour autant sacrifier une quelconque fonctionnalité.

Je ne sais pas pour vous, mais de mon côté le fichier functions.php de mon thème a plutôt tendance à devenir obèse au fil de mes personnalisations. Il suffit de jeter un œil à mon article sur la personnalisation de l’administration de WordPress pour voir les lignes de code faire des bébés. Il ne s’agit pas seulement du poids du fichier, mais surtout de toutes ces fonctions que devra lire WordPress à chaque chargement de page, même s’il n’en a pas besoin dans l’instant présent.

Mon idée consiste à diviser ce fichier functions.php en plusieurs parties : une partie commune à tout le site, une partie réservée à l’administration, et une uniquement pour le frontend. On peut même imaginer d’autres parties selon ce que vous avez mis dans votre fichier functions.php.

Mise à jour du 4 janvier 2014
J’ai modifié ma façon de faire, qui, je pense, est plus pertinente que ce qui est décrit dans l’article d’origine.
Différences :

  1. Diviser le code en 3 parties : ajax, admin, frontend.
  2. Utilisation de include() au lieu de get_template_part() : aucun besoin de fallback, si un fichier manque c’est qu’on a plus important à se soucier qu’un fichier de fallback.
01020304050607080910111213141516
$templatepath = get_template_directory();
$stylesheetpath = get_stylesheet_directory();

if ( defined('DOING_AJAX') && DOING_AJAX && is_admin() ) {

	include( $templatepath.'/inc/ajax.php' );

} elseif ( is_admin() ) {

	include( $templatepath.'/inc/admin.php' );

} elseif ( !defined( 'XMLRPC_REQUEST' ) && !defined( 'DOING_CRON' ) ) {

	include( $templatepath.'/inc/frontend.php' );

}

Remarques :
Dans un thème enfant il vous faudra utiliser $stylesheetpath au lieu de $templatepath.
Ne mettez pas de ?> à la fin de vos fichiers, cela aura un impact positif sur la performance.
Rien ne vous empêche ensuite de diviser votre code en fonction de son utilité, par exemple, créer un fichier widgets.php afin d’y mettre tous vos widgets persos. Ce point ne jouera en rien sur la performance du site mais permet d’avoir un code plus clair et rangé, c’est un point positif pour la maintenance. Attention à ne pas faire trop de petits non plus, certains développeurs n’aiment pas les portées nombreuses.
Pour finir, un petit rappel sécurité. Au tout début de chacun de vos fichiers, pensez à mettre ceci :

12345
<?php
if( !defined( 'ABSPATH' ) )
	die( 'Cheatin\' uh?' );

// Votre code ici

Cela aura pour effet de ne pas afficher un message d’erreur si quelqu’un tente d’accéder directement au fichier avec son navigateur, le message contient une information qui peut être utile à une personne malveillante.

Suite de l’article du 9 juin 2011

Structurer le code

La première chose à faire est d’étudier le code présent dans notre fichier functions.php pour voir ce qui est utile uniquement pour l’administration, uniquement pour le front-end, ou aux deux. Ce sera en fait la partie la plus difficile, surtout si on a ajouté du code sans trop savoir comment il fonctionne. Ensuite il suffira de séparer ces parties en se basant sur la condition is_admin(). Avant tout, je vous conseille de sauvegarder votre fichier original dans un coin.
Exemple :

1234567
// Code utile partout

if (is_admin()) {
	// Code utile uniquement dans l'administration
} else {
	// Code utile uniquement dans le front-end
}

Déjà, rien qu’en faisant ça, vous devriez sentir une différence dans le temps de chargement de vos pages.
NOTA : La page de login n’est pas considérée comme faisant partie de l’administration. Toutes les personnalisations concernant cette page devront se trouver dans la partie else.
NOTA 2 : Les fenêtres « média », c’est à dire les fenêtres qui s’ouvrent lorsque l’on clique sur l’un des boutons situés au-dessus de l’éditeur visuel ou html (pour mettre une image à la une dans un article par exemple). Je ne sais pas si c’est valable pour toutes ces fenêtres « média », mais j’ai un plugin qui utilise une telle fenêtre pour insérer du contenu dans mes articles, et je me suis rendu compte que dans cette fenêtre is_admin() me renvoie false. Plutôt étrange.

Diviser pour mieux régner

Allons encore plus loin et créons un fichier functions.php par partie, et utilisons seulement ceux dont nous avons besoin.
Pour cela nous allons utiliser une fonction apparue avec WordPress 3, très utile lors de la conception d’un thème WordPress, et nous allons légèrement détourner son utilisation. Il s’agit de get_template_part(). C’est tout simplement une sorte de require() php.
D’abord nous avons besoin de créer les fichiers. Nous avons déjà functions.php, qui servira pour le code commun à tout le site. Créez les fichiers functs-adminonly.php et functs-frontendonly.php qui serviront respectivement pour l’administration et le front-end.
Ensuite, copiez tout ce qui est à l’intérieur de la condition is_admin() et collez le dans functs-adminonly.php. Même chose pour le front-end : copiez tout ce qui est à l’intérieur du else et collez le dans functs-frontendonly.php. Pour finir, il suffit de remplacer le code dupliqué par des get_template_part() avec les paramètres adéquats dans functions.php.
NOTA : J’ai délibérément appelé les fichiers functs-XXXXX.php au lieu de functions-XXXXX.php. Si vous avez regardé la page du codex réservée à cette fonction (en lien plus haut), vous pouvez voir sous la partie « Examples » que la fonction va d’abord chercher le fichier functions-XXXXX.php, et si elle ne le trouve pas elle va chercher le fichier functions.php à la place, et l’inclure. Donc si votre functions-XXXXX.php disparait pour une raison ou pour une autre, functions.php va s’inclure dans lui-même à l’infini, et par la même occasion, déclarer des fonctions déjà existantes. Bref, le site sera en croix. En appelant nos fichiers functs-XXXXX.php on contourne ce problème puisque functs.php n’existe pas.
Si la fonction ne trouve aucun fichier, elle va générer une erreur et stopper le script php. Du coup, il n’est pas idiot de créer un fichier functs.php vide, et par la même occasion, mettre le code commun en premier dans functions.php, et le test is_admin() à la fin du fichier.

functions.php :

1234567
<?php
// Code commun à tout le site

if (is_admin())
	get_template_part( 'functs', 'adminonly' );
else
	get_template_part( 'functs', 'frontendonly' );

functs-adminonly.php :

123
<?php
// Code uniquement pour l'administration
?>

functs-frontendonly.php :

123
<?php
// Code uniquement pour le front-end
?>

[update]
Plutôt que de créer un fichier de fallback functs.php vide, pourquoi ne pas y ajouter une alerte qui nous préviendrait dans l’administration que quelque chose foire? Avec le hook suivant, nous aurons une alerte sur fond rouge en haut de toutes les pages de l’administration, dès lors qu’un des deux fichiers manquera à l’appel.

functs.php :

123
<?php
add_action( 'admin_notices', create_function( '' , "echo '<div class=\"error\"><p>Attention - L\'un des fichiers functs-xxxxx.php de votre thème n\'a pas été trouvé. Vérifiez les fichiers de votre thème.</p></div>';" ) );
?>

Mais il faut aussi vérifier que le ficher pour le front-end existe bien, car dans l’admin, on n’essaie pas de l’inclure, donc il ne fera pas de fallback vers functs.php.

functions.php :

1234567
if (is_admin()) {
	get_template_part( 'functs', 'adminonly' );
	if ( !file_exists(TEMPLATEPATH.'/functs-frontendonly.php') && file_exists(TEMPLATEPATH.'/functs-adminonly.php') ) {
		get_template_part( 'functs' );
	}
} else
	get_template_part( 'functs', 'frontendonly' );

[update]
Astuce bonus : dans le fichier functions.php, utiliser des is_page(), is_single() ou autre, directement ne fonctionne pas (car au moment où le fichier est « lu », WordPress ne sait pas encore si nous nous trouvons dans une page, un article, ou autre). Il faut donc utiliser un hook :

12345
function single_setup() {
	if (is_single())
		get_template_part( 'functs', 'single' );
}
add_action( 'template_redirect', 'single_setup' );

Ce qui donne au final pour functions.php :

functions.php :

01020304050607080910111213
if (is_admin()) {
	get_template_part( 'functs', 'adminonly' );
	if ( (!file_exists(TEMPLATEPATH.'/functs-frontendonly.php') || !file_exists(TEMPLATEPATH.'/functs-single.php')) && file_exists(TEMPLATEPATH.'/functs-adminonly.php') ) {
		get_template_part( 'functs' );
	}
} else {
	get_template_part( 'functs', 'frontendonly' );
	function single_setup() {
		if (is_single())
			get_template_part( 'functs', 'single' );
	}
	add_action( 'template_redirect', 'single_setup' );
}

Conclusion

Voilà une méthode assez simple que j’ai testé ici-même, et je dois dire que j’ai vu une différence. Certes cela ne va pas diviser par deux vos temps de chargements mais le gain n’est pas du tout négligeable.

Points positifs :

  • Le nombre de lignes de code lues a drastiquement diminué : nous évitons à WordPress de lire des fonctions qui ne lui sont pas utiles à un moment donné.
  • Le poids des fichiers chargés : de 48ko pour le fichier functions.php, je suis passé à 6+30=36ko pour le front-end et 6+12=18ko pour l’administration (ok, la différence n’est pas énorme).
  • La clarté du code : en séparant en plusieurs fichiers, je trouve qu’il est plus facile de se repérer.

Points négatifs :

  • La première étape qui consiste à séparer son code en trois parties est la plus difficile. Le tout est de ne pas se planter à ce moment et de vérifier ensuite que tout fonctionne correctement.
  • L’astuce ne sera efficace seulement si le fichier functions.php original est assez conséquent, sinon la différence sera imperceptible.

See ya!