Guidelines : WordPress

Ce document rassemble les bonnes pratiques appliquées par l'agence web Alsacreations.fr concernant "WordPress". Ces indications sont destinées à évoluer dans le temps et à s'adapter à chaque nouveau projet.

Structure de projet

On utilise Docker avec une structure-type déjà éprouvée construite avec :

  • Composer pour installer WordPress et ses extensions.
  • WordPlate qui fonctionne avec Vite.
  • Tailwind en tant que framework CSS (optionnel).
  • Timber pour la syntaxe Twig dans les templates (optionnel).
  • ACF pour gérer les champs personnalisés, les CPT, les options.

Environnement de développement

  • Utiliser define('WP_ENVIRONMENT_TYPE','staging'); puis wp_get_environment_type()
  • Utiliser define('WP_DEBUG',true); pour activer le mode debug

Git

On versionne les fichiers de structure et de configuration, tels que :

  • .env.example
  • composer.lock et package.json
  • le thème développé pour le projet
  • les extensions développées pour le projet
  • les fichiers de configuration (vite, eslint, prettier)
  • les fichiers de traduction du thème (dossier /languages) ou de l'extension (dossier de l'extension)

On ne versionne pas (voir fichiers .gitignore) :

  • .env (sauf exception)
  • le dossier public/wordpress et vendor (car installé/mis à jour par composer)
  • les extensions tierces (car installées/mises à jour par composer)
  • les dossiers public/uploads (stockés à part car binaires occupant beaucoup de place), public/upgrade
  • les thèmes installés "par défaut" (Twenty*) qui doivent de toute façon être supprimés

👉 Le fichier README.md à la racine du projet doit contenir toutes les informations pour prendre en main le développement et ré-installer le site rapidement en production.

Thème

👉 On dévelope au maximum à l'aide de techniques natives WordPress (Posts, boucles, CPT, etc), voir https://codex.wordpress.org/Theme_Development.

  • On privilégie de démarrer avec un thème starter épuré https://underscores.me/ ou https://github.com/timber/starter-theme lorsque l'on utilise Timber.
  • On supprime les autres thèmes livrés par défaut.
  • On évite d'utiliser un thème acheté car cela implique qu'on ne pourra pas tout mettre en place dans ces guidelines et qu'on ne maîtrise pas son contenu (code, extensions, évolutions). Si toutefois cela arrive, utiliser le principe de thème enfant pour ne pas modifier le thème parent, qui pourrait être mis à jour par la suite.

🔖 Documentation officielle https://developer.wordpress.org/themes/ et documentation des fonctions https://codex.wordpress.org/Function_Reference

🔖 Voir aussi [https://capitainewp.io/formations/developper-theme-wordpress/](Développer un thème WordPress sur mesure)

Intégration du thème

Outils de vérification (linters)

La liste des linters recommandés est décrite par les guidelines Visual Studio Code, dont eslint pour JavaScript. Les extensions spécifiques WordPress / PHP recommandées sont :

Automatisation

Avec Vite (présent dans WordPlate) on compile CSS et JavaScript depuis le dossier resources, avec HMR (Hot Module Reloading) durant la tâche de développement. Pour ajouter le support de Sass : (p)npm install sass --save-dev.

Moteur de template (optionnel)

🔖 Timber (optionnel, présent dans notre structure-type)

Framework CSS

On privilégie, dans cet ordre et seulement s'il y en a besoin (sinon Sass), les frameworks CSS suivants :

Nommage HTML, CSS, JavaScript et PHP

Voir Guidelines HTML, CSS et JavaScript.

  • Suivre les PHP Coding Standards de WordPress.
  • ⚠️ Ne pas utiliser les classes CSS générées par WordPress qui sont spécifiques à une installation précise et ne sont pas réutilisables : classes spécifiques des wrappers des menus du type .menu-nom-de-mon-menu et la majorité des classes générées par body_class() ou post_class().
  • Placer add_action() et add_filter() après la fonction liée.
  • Toutes les chaînes de caractères d'un thème doivent pouvoir être traduites. Il faut donc les entourer dans la bonne fonction gettext ( __(), _n(), _x() ), couplées à un text-domain cohérent en fonction du contexte (thème, thème enfant, extension, ...).
  • Découper le thème de manière cohérente (boucles à part, etc.) pour pouvoir utiliser get_template_part() correctement.
  • Tout ce qui ne fait pas partie intégrante du thème et/ou optionnel doit être réalisé sous forme d'extension.
  • Utiliser les conditional tags pour ajouter des conditions/contextes (is_category, is_single, has_tag...).

Hiérarchie de fichiers et documentation

👉 Utiliser l'auto-chargement des fichiers PHP du thème par WordPress (selon slug de la catégorie, du Custom Post Type, etc) en suivant la hiérarchie de templates (explications).

🔖 Voir aussi :

À prévoir dans le thème

👉 On ne nomme/préfixe pas le thème ou ses classes/fonctions par alsa_ mais plutôt par le nom du projet.

La structure standard est :

├── assets (dir)/
│   ├── css (dir)
│   ├── images (dir)
│   └── js (dir)
├── inc (dir)
├── template-parts (dir)/
│   ├── footer (dir)
│   ├── header (dir)
│   ├── navigation (dir)
│   ├── page (dir)
│   └── post (dir)
├── 404.php
├── archive.php
├── comments.php
├── footer.php
├── front-page.php
├── functions.php
├── header.php
├── index.php
├── page.php
├── README.txt
├── rtl.css
├── screenshot.png
├── search.php
├── searchform.php
├── sidebar.php
├── single.php
└── style.css

On utilise des fonctions telles que get_header, get_footer pour construire les pages, et get_template_directory_uri pour générer les chemins d'accès.

On charge les ressources dans le thème avec wp_enqueue_style() et wp_enqueue_script(), et en modulant avec strategy pour defer/async.

Ajout des fonctionnalités essentielles dans des mu-plugins

Toutes les fonctions de base, sur lesquelles un non-administrateur ne doit pas avoir la main doivent passer par des extensions indispensables, ou mu-plugins (mu = must use). Elles ne peuvent être désactivées par l'interface web. C'est le cas notamment du renommage de fichiers dès l'upload dans la bibliothèque de médias, mais également du retrait des indices lors des erreurs de connexion au back-office (admin).

Quelques MU Plugins bien utiles : https://gitlab.com/ArmandPhilippot/mu-plugins

function no_wordpress_errors() {
    return __( 'Something is wrong !', 'text-domain' );
}
add_filter( 'login_errors', 'no_wordpress_errors' );

Traductions

🔖 Voir https://www.alsacreations.com/article/lire/1837-wordpress-theme-internationalisation.html

🪛 Convertir les fichiers .po en .mo en ligne : https://po2mo.net/

functions.php

⚠️ Le fichier functions.php fonctionne différemment des autres fichiers “template”, lors de la création d'un thème enfant par exemple, il n'est pas simplement écrasé, mais chargé avant le thème parent. Les deux fichiers déclarant des fonctions cohabitent, et il serait dommage de ne pas pouvoir écraser une fonctionnalité, ou de tomber sur une erreur PHP car une fonction est déclarée deux fois. Il faut donc prendre l'habitude de déclarer TOUTES les fonctions ainsi :

if ( ! function_exists( 'nomdutheme_nom_de_la_fonction' )  {
    function nomdutheme_nom_de_la_fonction() {
        // do something
    }
}
add_filter('filter_name', 'nomdutheme_nom_de_la_fonction');

👉 Idéalement le fichier functions.php du thème inclut d'autres scripts PHP dédiés pour organiser le code (ex : actions.php, filters.php, menu.php, theme-setup.php, etc). Exemple de fichier functions.php :

/**
 * Menus/Sidebar/Theme options definitions
 */
require_once 'includes/theme-setup.php';

/**
 * Filters used to alter front-end rendering
 */
require_once 'includes/menu-filters.php';

/**
 * Actions & filters
 */
require_once 'includes/actions.php';
require_once 'includes/filters.php';

/**
 * Parent theme overload
 */
require_once 'includes/inc-pages-functions-updated.php';

Personnalisation du thème

L'API Customize permet d'ajouter des options de personnalisation au thème, apparaissant dans l'interface d'administration, notamment avec le hook customize_register.

Formulaires

Admin

Développement des contenus éditables

Menus de navigation

On se repose sur un Bloc Navigation ou la fonctionnalité classique native de menu éditable (dans Apparence > Menus) en réservant un emplacement.

🔖 Voir https://wpmarmite.com/menu-wordpress/

Requêtes et boucles

Le Loop est la boucle native de WordPress pour générer des affichages de posts et utiliser des template tags tels que the_title, the_content, the_author, etc. On peut créer ses propres requêtes avec WP_Query. 🔖 Voir https://www.smashingmagazine.com/2013/01/using-wp_query-wordpress/ et https://www.rarst.net/wordpress/wordpress-query-functions/.

Hooks

Les hooks permettent de brancher du code à des moments précis du cycle de génération des pages, et recouvrent :

Shortcodes

Un shortcode est approprié pour insérer rapidement une portion de contenu simple dans tout éditeur, mais non éditable en détails directement, avec passage de quelques paramètres (ex: emplacement de formulaire de contact, carte géographique...).

🔖 Voir https://capitainewp.io/formations/developper-theme-wordpress/shortcode/ et https://kinsta.com/fr/blog/shortcodes-wordpress/

Taxonomies

Les taxonomies gèrent nativement les catégories et tags mais on peut en déclarer avec register_taxonomy et les associer à un ou plusieurs CPT.

register_post_type($cpt_name, $args_cpt);
register_taxonomy($taxo_name, $cpt_name, $args_taxo);

CPT (Custom Post Types)

On utilise des CPT pour toute entité de données allant au-delà des Pages et Posts.

👉 ACF permet de créer des CPT et taxonomies depuis l'interface d'administration, puis d'exporter le code PHP correspondant (ou import/export en JSON) via son onglet Outils ce qui facilite les opérations.

Ajout de CPT dans ACF

🔖 Voir Tutoriel : Créer des Custom Post Types avec WordPress et Types de publications personnalisés WordPress : Le guide tout-en-un pour les créer et les utiliser (Kinsta).

Si le projet nécessite d'utiliser Gutenberg, penser à ajouter "show_in_rest" => true et "supports" => ['editor'] dans la déclaration des CPT.

ACF (Advanced Custom Fields)

On utilise ACF pour

Pour filtrer des requêtes à l'aide de ces valeurs, on utilisera une Meta Query dans WP_Query.

👉 Le dossier acf-json/ doit être présent à la racine du thème et autorisé en écriture permettant de versionner et synchroniser automatiquement les ajouts/modifications ACF par des fichiers JSON. On peut se servir de l'onglet Sync ou utiliser la Bulk action "Activer" après avoir coché toutes les cases pour forcer la génération de ces fichiers JSON. Voir https://www.advancedcustomfields.com/resources/local-json/.

🔖 Voir [https://capitainewp.io/formations/acf/](Des champs administrables avec ACF par Capitaine WP), Tutoriel ACF : Advanced Custom Fields – Le guide complet, Best Practices when Designing Custom Fields et Tutoriel sur Advanced Custom Fields : Votre guide ultime

Blocs sur-mesure, Gutenberg

Utiliser les blocs ACF pour ne rendre modifiables que des champs spécifiques (champ texte, image, colorpicker, etc.) et avoir les fonctionnalités d'ACF (champ relationnel, taxonomies, etc.). Un bloc ACF est mis en place ainsi :

  • Préparation du bloc via l'interface d'administration : définition des champs éditables.
  • Préparation du template PHP de rendu.
  • Association avec acf_register_block_type.
  • Usage dans l'éditeur Gutenberg : le bloc devrait apparaître dans le menu ➕

Dans le cas où on utilise un thème acheté et que les fichiers PHP ne sont pas utilisables, on se tournera vers une extension afin de générer des "patterns" Gutenberg sur-mesure.

🔖 Adapter la palette de couleurs https://speckyboy.com/custom-color-palette-wordpress-gutenberg-editor/.

Compositions de blocs

TODO:

Accessibilité

Extensions

👉 Installation : utiliser composer require avec le nom du plugin préfixé par wpackagist-plugin/ : par exemple composer require wpackagist-plugin/wp-migrate-db

👉 Toute fonctionnalité développée sur-mesure pour le projet se fait dans le cadre d'une extension propre à activer/désactiver.

Obligatoires / fortement recommandées

  • WP fail2ban si hébergement interne équipé de fail2ban permettant de signaler les erreurs d'identification pour bannir les adresses IP tentant du bruteforce ; n'utilisez alors pas d'extension pour changer l'url de wp-admin.
  • WP Migrate Lite pour migrer les données de local > dev > recette > prod (et inversement), à désinstaller par sécurité après mise en production.
  • W3 Total Cache ou WP Fastest Cache ou WP Super Cache : cache/compression de contenu pour améliorer les temps de réponse.
  • SecuPress pour améliorer le score global (permissions de fichiers, bonnes pratiques).
  • Notre plugin Kiwiplate Setup Theme, et/ou mettre en place les actions suivantes (extensions + snippets de code).
  • Disable emojis : désactiver les appels de scripts externes vers WordPress (RGPD) ou ajouter à functions.php ce snippet.
  • Disable comments : désactiver les commentaires sur les posts/pages/médias, au choix (très propre).
  • ACF : ajouter des champs riches aux posts / pages / CPT.

Formulaires (extensions)

Multilangue

  • Polylang : traduction (remplace WPML).

SEO

  • SEOPress : SEO, ou Yoast (rajoute une grosse surcouche de pub très intrusive dans l'admin).

Interface d'administration

  • Utiliser SVG dans WordPress
  • Filebird : File Manager (s'ajoute dans la galerie de médias) : créer des dossiers. Attention, il faut prendre la version premium pour créer des dossiers illimités.
  • Duplicate Post : créer du contenu rapidement en dupliquant d'un simple clic un post, une page, ou un custom post.
  • Adminimize : personnaliser l'aspect de l'admin en fonction des niveaux des utilisateurs. || Hook natif : supprimer les items du menu (pour un rôle spécifique, vérifier le rôle avec fonction current_user_can).
  • Simple Page Ordering : ordonner les pages, et autres CPT ordonnés, par simple glisser/déposer, sans avoir besoin de rentrer dans chaque page.
  • WP All Export : exporter les données au format CSV/XML (fonctionne avec ACF, The Events Calendar) fonctionne aussi pour l'import avec WP All Import
  • Admin Columns : ajouter/modifier des colonnes dans l'interface d'administration

Membres et droits

Divers

  • Photo gallery (Galerie de médias, photos et vidéos) + riche en fonctionnalités que la galerie native (img s'ouvrent dans une popup, slider, bouton de téléchargement, création de groupes de galeries, etc…). N'est pas accessible : fenêtre modale qui ne prend pas le focus, pas d'attributs aria, bouton de fermeture non accessible.
  • Job Manager : Offres d'emploi.
  • Tarteaucitron || Cookie Notice : bannières cookies, code non accessible (boutons qui n'en sont pas, etc.).
  • Relevanssi : améliore les résultats de recherche par critères de pertinence.
  • Multiple Domain Mapping on Single Site pour faire correspondre différentes Pages (d'accueil) à plusieurs domaines ou sous-domaines.

E-commerce

  • WooCommerce : la solution idéale (communauté, support) avec feuilles de style par défaut, un système de coupon, gestion des stocks automatisé, gestion des e-mails client avancés, plein de hooks.
  • WOOF : Filtres plus riche en fonctionnalités que ceux de WooCommerce natif
  • Tickera : Vente de billets, compatible avec WooCommerce.

Sécurité

  • 👉 Supprimer l'utilisateur admin et l'utilisateur avec l'ID 1. Créer un utilisateur de niveau administrateur avec identifiant spécifique différent de “admin”.
  • Créer un ou plusieurs utilisateurs de niveau éditeur pour les intervenants (doit être différent du nom de domaine pour des raisons de sécurité), ayant accès juste aux fonctionnalités utiles : ne pas utiliser de compte admin par défaut pour toutes les personnes car cela permet l'installation d'extensions.
  • Compléter le fichier wp-config.php avec les valeurs de https://vinkla.github.io/salts/
  • Désactiver l'édition du thème et des plugins en ligne dans wp-config.php define('DISALLOW_FILE_EDIT', true);
  • User Name Security supprime les mentions de l'utilisateur (id et username) dans body_class(), entre autres choses.
  • SF Author URL control personnalise le “author” et le slug utilisateur pour sécuriser et personnaliser les URL des pages auteur.
  • Toujours utiliser les nonces pour éviter les CSRF, s'il faut développer des modules admin et/ou pour les utilisateurs identifiés sur le site.
  • Surveiller si le thème / les extensions utilisées font l'objet d'une faille sur wpscan
  • Ajouter le script pour enlever l'avertissement à la connexion qui permet d'indiquer que l'identifiant est le bon mais pas le mot de passe.

Performance

👉 Mettre en place une extension de cache/compression/minification (voir extensions).

Recette

👉 Utiliser wp-migrate-db pour exporter les contenus en adaptant correctement les URLs vers le nouveau domaine.

👉 Ne pas laisser indexer ce site par les robots, en ajoutant une authentification HTTP (par exemple avec .htaccess).

Mise en ligne

👉 Utiliser wp-migrate-db pour exporter les contenus en adaptant correctement les URLs vers le nouveau domaine.

  • Modifier WP_ENVIRONMENT_TYPE/WP_ENV à production et WP_DEBUG à false.
  • Autoriser l'indexation par les robots (dans la configuration) et retirer du fichier .htaccess Header set X-Robots-Tag "noindex,nofollow" s'il est présent.
  • Modifier l'adresse e-mail du compte administrateur.
  • Vérifier que toutes les anciennes URLs de développement ont disparu de la base.
  • Activer le cache.

Si l'hébergement est mutualisé et ne permet de pointer dans le dossier /public, activer la réécriture avec un fichier .htaccess à la racine :

RewriteEngine on
RewriteRule ^(.*)$ /public/$1 [L]

Maintenance

On peut utiliser WP-CLI pour opérations pratiques en ligne de commande.

Forcer la mise à jour par téléchargement direct dans wp-config.php define('FS_METHOD' 'direct');

Désactiver le warning d'update WordPress pour les non-admins :

if ( !current_user_can( 'edit_users' ) ) {
    add_action('admin_menu','wphidenag');
    function wphidenag() {
        remove_action( 'admin_notices', 'update_nag', 3 );
    }
}

Désactiver les notifications de mise à jour pour les non-admins :

function hide_update_notice_to_all_but_admin_users()
{
    if (!current_user_can('update_core')) {
        remove_action( 'admin_notices', 'update_nag', 3 );
        remove_action('load-update-core.php','wp_update_plugins');
        add_filter('pre_site_transient_update_plugins','__return_null');
        echo '<style>#setting-error-tgmpa>.updated settings-error notice is-dismissible, .update-nag, .updated, .core-updates { display: none; }</style>';
    }
}
add_action( 'admin_head', 'hide_update_notice_to_all_but_admin_users', 1 );

Divers, dépannage et astuces

Autres ressources

🔖 Beaucoup de cours chez Capitaine WP et Grafikart