Introduction
Les shortcodes WordPress sont des marqueurs textuels puissants qui permettent d’insérer du contenu dynamique et des fonctionnalités complexes directement dans vos articles, pages ou widgets. Introduits dans WordPress 2.5, ils constituent un pont élégant entre le contenu statique et les fonctionnalités PHP avancées.
Table of Contents
- Introduction
- Table des matières
- 1. Comprendre l’architecture des shortcodes
- 2. Créer un shortcode simple
- 3. Shortcodes avec attributs
- 4. Shortcodes avec contenu encapsulé
- 5. Shortcodes imbriqués et auto-fermants
- 6. Sécurité et validation des données
- 7. Optimisation des performances
- 8. Cas d’usage avancés
- 9. Debugging et résolution de problèmes
- 10. Bonnes pratiques professionnelles
- Conclusion
Dans ce guide complet, nous explorerons l’architecture des shortcodes, leur création, leur sécurisation et leur optimisation pour des applications professionnelles.
Table des matières
- Comprendre l’architecture des shortcodes
- Créer un shortcode simple
- Shortcodes avec attributs
- Shortcodes avec contenu encapsulé
- Shortcodes imbriqués et auto-fermants
- Sécurité et validation des données
- Optimisation des performances
- Cas d’usage avancés
- Debugging et résolution de problèmes
- Bonnes pratiques professionnelles
1. Comprendre l’architecture des shortcodes
Le cycle de vie d’un shortcode
Lorsque WordPress génère une page, il passe par plusieurs phases de traitement du contenu :
Récupération du contenu depuis la BDD
↓
Application des filtres 'thecontent'
↓
Parsing et détection des shortcodes
↓
Exécution des callbacks associés
↓
Remplacement des balises par le contenu généré
↓
Affichage final
Structure interne
WordPress utilise une expression régulière pour identifier les shortcodes dans le contenu :
// Pattern simplifié utilisé par WordPress
$pattern = '/[(w+)([^]])](?:(.?)[/1])?/s';
Les shortcodes sont stockés dans un tableau global $shortcodetags qui associe chaque nom de shortcode à sa fonction callback.
2. Créer un shortcode simple
Exemple basique : Affichage de la date du jour
Shortcode simple pour afficher la date actuelle
@return string Date formatée
/
function yawcdateshortcode() {
// Récupérer le format de date WordPress
$dateformat = getoption('dateformat');
// Générer la date localisée
$currentdate = datei18n($dateformat);
// Retourner le HTML (ne jamais faire echo dans un shortcode)
return sprintf(
'',
date('c'), // Format ISO 8601
eschtml($currentdate)
);
}
addshortcode('dateactuelle', 'yawcdateshortcode');
Utilisation :
[dateactuelle]
Points importants :
echoeschtml(), escattr(), etc.datei18n() pour la localisationShortcode avec buffer output
Si vous devez intégrer du code complexe avec des templates :
weatherwidgetshortcode() {
// Démarrer le buffer de sortie
obstart();
// Inclure un template
include locatetemplate('template-parts/weather-widget.php');
// Récupérer et nettoyer le buffer
$output = obgetclean();
return $output;
}
addshortcode('meteo', 'yawcweatherwidgetshortcode');
3. Shortcodes avec attributs
Gestion avancée des attributs
Shortcode pour afficher les derniers articles
@param array $atts Attributs du shortcode
@return string HTML généré
/
function yawcrecentpostsshortcode($atts) {
// Définir les valeurs par défaut et fusionner avec les attributs fournis
$atts = shortcodeatts(
array(
'nombre' => 5,
'categorie' => '',
'ordre' => 'DESC',
'orderby' => 'date',
'exclu' => '', // IDs à exclure
'afficher' => 'titre,extrait,image', // Éléments à afficher
'classe' => '',
),
$atts,
'articlesrecents' // Tag du shortcode pour le filtrage
);
// Valider et nettoyer les entrées
$nombre = absint($atts['nombre']);
$nombre = min($nombre, 50); // Limiter à 50 pour les performances
$ordre = inarray(strtoupper($atts['ordre']), array('ASC', 'DESC'))
? strtoupper($atts['ordre'])
: 'DESC';
// Liste blanche des orderby autorisés
$orderbyallowed = array('date', 'title', 'rand', 'commentcount', 'modified');
$orderby = inarray($atts['orderby'], $orderbyallowed)
? $atts['orderby']
: 'date';
// Construire les arguments de requête
$args = array(
'postsperpage' => $nombre,
'order' => $ordre,
'orderby' => $orderby,
'ignorestickyposts' => true,
'nofoundrows' => true, // Performance : pas besoin de pagination
);
// Ajouter le filtre de catégorie si spécifié
if (!empty($atts['categorie'])) {
$args['categoryname'] = sanitizetextfield($atts['categorie']);
}
// Gérer les exclusions
if (!empty($atts['exclu'])) {
$excluids = arraymap('absint', explode(',', $atts['exclu']));
$args['postnotin'] = $excluids;
}
// Exécuter la requête
$query = new WPQuery($args);
if (!$query->haveposts()) {
return 'Aucun article trouvé.
';
}
// Déterminer quels éléments afficher
$afficher = arraymap('trim', explode(',', $atts['afficher']));
// Construire le HTML
$classecontainer = 'yawc-recent-posts';
if (!empty($atts['classe'])) {
$classecontainer .= ' ' . sanitizehtmlclass($atts['classe']);
}
obstart();
?>
haveposts()) : $query->thepost(); ?>
array('image', $afficher) && haspostthumbnail()) : ?>
array('titre', $afficher)) : ?>
excerpt(); ?>
array('lire-plus', $afficher)) : ?>
Lire la suite →
resetpostdata();
return obgetclean();
}
addshortcode('articlesrecents', 'yawcrecentpostsshortcode');
Exemples d’utilisation :
[articlesrecents nombre="3"]
[articlesrecents nombre="5" categorie="tutoriels" ordre="ASC"]
[articlesrecents nombre="4" afficher="titre,image,date" classe="grid-layout"]
[articlesrecents orderby="rand" nombre="3" exclu="15,28,42"]
Filtrage des attributs
Permettre aux développeurs de modifier les attributs :
atts()
$atts = applyfilters('yawcrecentpostsatts', $atts);
Utilisation du filtre :
filter('yawcrecentpostsatts', function($atts) {
// Forcer l'ordre aléatoire sur la page d'accueil
if (isfrontpage()) {
$atts['orderby'] = 'rand';
}
return $atts;
});
4. Shortcodes avec contenu encapsulé
Box de mise en évidence
Shortcode pour créer des boîtes stylisées
@param array $atts Attributs
@param string $content Contenu entre les balises
@return string HTML généré
/
function yawcboxshortcode($atts, $content = null) {
$atts = shortcodeatts(array(
'type' => 'info', // info, warning, success, error
'titre' => '',
'icone' => 'true',
'fermable' => 'false',
), $atts, 'box');
// Validation du type
$typesvalides = array('info', 'warning', 'success', 'error');
$type = inarray($atts['type'], $typesvalides) ? $atts['type'] : 'info';
// Icônes par type
$icones = array(
'info' => '',
'warning' => '',
'success' => '',
'error' => '',
);
// Traiter le contenu (important pour les shortcodes imbriqués)
$content = doshortcode($content);
// Construire le HTML
$html = sprintf(
'',
escattr($type)
);
if ($atts['fermable'] === 'true') {
$html .= '';
}
if (!empty($atts['titre']) || $atts['icone'] === 'true') {
$html .= '';
if ($atts['icone'] === 'true') {
$html .= $icones[$type];
}
if (!empty($atts['titre'])) {
$html .= sprintf(
'%s
',
eschtml($atts['titre'])
);
}
$html .= '';
}
$html .= sprintf(
'%s',
wpautop($content) // Ajouter les paragraphes automatiquement
);
$html .= '';
return $html;
}
addshortcode('box', 'yawcboxshortcode');
Utilisation :
[box type="warning" titre="Attention"]
Cette fonctionnalité est en version bêta.
[/box]
[box type="success" icone="true" fermable="true"]
Votre inscription a été enregistrée avec succès !
[/box]
CSS associé
/ yawc-shortcodes.css /
.yawc-box {
position: relative;
padding: 1.5rem;
margin: 1.5rem 0;
border-radius: 8px;
border-left: 4px solid;
}
.yawc-box-info {
background-color: #e3f2fd;
border-left-color: #2196f3;
color: #0d47a1;
}
.yawc-box-warning {
background-color: #fff3e0;
border-left-color: #ff9800;
color: #e65100;
}
.yawc-box-success {
background-color: #e8f5e9;
border-left-color: #4caf50;
color: #1b5e20;
}
.yawc-box-error {
background-color: #ffebee;
border-left-color: #f44336;
color: #b71c1c;
}
.yawc-box-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.yawc-box-title {
margin: 0;
font-size: 1.1rem;
font-weight: 600;
}
.yawc-icon {
width: 24px;
height: 24px;
fill: currentColor;
}
.yawc-box-close {
position: absolute;
top: 0.5rem;
right: 0.5rem;
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
opacity: 0.6;
transition: opacity 0.2s;
}
.yawc-box-close:hover {
opacity: 1;
}
.yawc-box-content p:last-child {
margin-bottom: 0;
}
JavaScript pour les boîtes fermables
// yawc-shortcodes.js
document.addEventListener('DOMContentLoaded', function() {
const closeButtons = document.querySelectorAll('.yawc-box-close');
closeButtons.forEach(button => {
button.addEventListener('click', function() {
const box = this.closest('.yawc-box');
box.style.transition = 'opacity 0.3s, transform 0.3s';
box.style.opacity = '0';
box.style.transform = 'translateY(-10px)';
setTimeout(() => {
box.remove();
}, 300);
});
});
});
5. Shortcodes imbriqués et auto-fermants
Système de tabs avec imbrication
Shortcode container pour les onglets
/
function yawctabsshortcode($atts, $content = null) {
static $tabgroupid = 0;
$tabgroupid++;
$atts = shortcodeatts(array(
'style' => 'default', // default, pills, underline
), $atts, 'tabs');
// Reset le compteur d'onglets pour chaque groupe
global $yawctabindex;
$yawctabindex = 0;
// Stocker l'ID du groupe pour les onglets enfants
global $yawccurrenttabgroup;
$yawccurrenttabgroup = $tabgroupid;
// Parser le contenu pour extraire les titres des onglets
$content = doshortcode($content);
return sprintf(
'%s',
escattr($atts['style']),
$tabgroupid,
$content
);
}
addshortcode('tabs', 'yawctabsshortcode');
/
Shortcode pour un onglet individuel
/
function yawctabshortcode($atts, $content = null) {
global $yawctabindex, $yawccurrenttabgroup;
$atts = shortcodeatts(array(
'titre' => 'Onglet ' . ($yawctabindex + 1),
'icone' => '',
'actif' => 'false',
), $atts, 'tab');
$isactive = ($yawctabindex === 0 || $atts['actif'] === 'true');
$yawctabindex++;
$tabid = sprintf('tab-%d-%d', $yawccurrenttabgroup, $yawctabindex);
$panelid = sprintf('panel-%d-%d', $yawccurrenttabgroup, $yawctabindex);
// Construire le bouton d'onglet (sera repositionné par JS)
$tabbutton = sprintf(
'',
$isactive ? ' active' : '',
$isactive ? 'true' : 'false',
escattr($panelid),
escattr($tabid),
$yawctabindex,
!empty($atts['icone']) ? '' : '',
eschtml($atts['titre'])
);
// Construire le panneau de contenu
$tabpanel = sprintf(
'%s',
$isactive ? ' active' : '',
escattr($tabid),
escattr($panelid),
$yawctabindex,
doshortcode($content)
);
// Retourner le bouton + panneau (le JS réorganisera)
return sprintf(
'%s',
escattr($tabbutton),
$tabpanel
);
}
addshortcode('tab', 'yawctabshortcode');
JavaScript pour les tabs
// yawc-tabs.js
class YAWCTabs {
constructor(container) {
this.container = container;
this.tabGroup = container.dataset.tabGroup;
this.init();
}
init() {
// Créer la liste des onglets
const tabList = document.createElement('div');
tabList.className = 'yawc-tab-list';
tabList.setAttribute('role', 'tablist');
// Extraire tous les boutons et les ajouter à la liste
const wrappers = this.container.querySelectorAll('.yawc-tab-wrapper');
const panels = [];
wrappers.forEach(wrapper => {
const buttonHTML = wrapper.dataset.tabButton;
const panel = wrapper.querySelector('.yawc-tab-panel');
// Créer le bouton depuis le HTML stocké
const temp = document.createElement('div');
temp.innerHTML = buttonHTML;
const button = temp.firstChild;
tabList.appendChild(button);
panels.push(panel);
// Ajouter l'écouteur d'événements
button.addEventListener('click', () => this.switchTab(button, panel));
});
// Réorganiser le DOM
this.container.innerHTML = '';
this.container.appendChild(tabList);
const panelsContainer = document.createElement('div');
panelsContainer.className = 'yawc-tab-panels';
panels.forEach(panel => panelsContainer.appendChild(panel));
this.container.appendChild(panelsContainer);
// Ajouter la navigation au clavier
this.addKeyboardNavigation(tabList);
}
switchTab(button, panel) {
// Désactiver tous les onglets
this.container.querySelectorAll('.yawc-tab-button').forEach(btn => {
btn.classList.remove('active');
btn.setAttribute('aria-selected', 'false');
});
this.container.querySelectorAll('.yawc-tab-panel').forEach(pnl => {
pnl.classList.remove('active');
});
// Activer l'onglet sélectionné
button.classList.add('active');
button.setAttribute('aria-selected', 'true');
panel.classList.add('active');
// Focus sur le panneau pour l'accessibilité
panel.focus();
}
addKeyboardNavigation(tabList) {
const buttons = tabList.querySelectorAll('.yawc-tab-button');
buttons.forEach((button, index) => {
button.addEventListener('keydown', (e) => {
let targetIndex = index;
if (e.key === 'ArrowRight') {
targetIndex = (index + 1) % buttons.length;
} else if (e.key === 'ArrowLeft') {
targetIndex = (index - 1 + buttons.length) % buttons.length;
} else if (e.key === 'Home') {
targetIndex = 0;
} else if (e.key === 'End') {
targetIndex = buttons.length - 1;
} else {
return;
}
e.preventDefault();
buttons[targetIndex].click();
buttons[targetIndex].focus();
});
});
}
}
// Initialiser tous les groupes d'onglets
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.yawc-tabs').forEach(container => {
new YAWCTabs(container);
});
});
Utilisation :
[tabs style="pills"]
[tab titre="Introduction" actif="true"]
Contenu de l'introduction avec du Markdown si vous utilisez un parser.
[/tab]
[tab titre="Configuration"]
Instructions de configuration détaillées.
[/tab]
[tab titre="Exemples de code"]
php
echo « Hello World »;
[/tab]
[/tabs]
6. Sécurité et validation des données
Principes essentiels
Shortcode sécurisé avec formulaire
Shortcode de formulaire de contact sécurisé
/
function yawccontactformshortcode($atts) {
$atts = shortcodeatts(array(
'email' => getoption('adminemail'),
'objet' => 'Nouveau message depuis le site',
'redirect' => '',
), $atts, 'contactform');
// Valider l'email
$emaildestination = isemail($atts['email']) ? $atts['email'] : getoption('adminemail');
// Générer un ID unique pour ce formulaire
$formid = 'yawc-contact-' . wpgeneratepassword(8, false);
// Traitement du formulaire si soumis
$messagesent = false;
$errormessage = '';
if (isset($POST['yawccontactsubmit']) &&
isset($POST['yawcformid']) &&
$POST['yawcformid'] === $formid) {
// Vérifier le nonce
if (!isset($POST['yawccontactnonce']) ||
!wpverifynonce($POST['yawccontactnonce'], 'yawccontactaction')) {
$errormessage = 'Erreur de sécurité. Veuillez réessayer.';
} else {
// Valider et nettoyer les données
$nom = isset($POST['nom']) ? sanitizetextfield($POST['nom']) : '';
$email = isset($POST['email']) ? sanitizeemail($POST['email']) : '';
$sujet = isset($POST['sujet']) ? sanitizetextfield($POST['sujet']) : '';
$message = isset($POST['message']) ? sanitizetextareafield($POST['message']) : '';
// Validation
$errors = array();
if (empty($nom)) {
$errors[] = 'Le nom est requis.';
}
if (empty($email) || !isemail($email)) {
$errors[] = 'Une adresse email valide est requise.';
}
if (empty($message)) {
$errors[] = 'Le message est requis.';
}
// Protection anti-spam basique
if (isset($POST['website']) && !empty($POST['website'])) {
// Champ honeypot rempli = spam
$errors[] = 'Détection de spam.';
}
if (empty($errors)) {
// Préparer l'email
$to = $emaildestination;
$subject = $atts['objet'] . ' - ' . $sujet;
$body = sprintf(
"Nouveau message de contactnn" .
"Nom: %sn" .
"Email: %sn" .
"Sujet: %snn" .
"Message:n%snn" .
"---n" .
"Envoyé depuis: %s",
$nom,
$email,
$sujet,
$message,
homeurl()
);
$headers = array(
'Reply-To: ' . $nom . ' <' . $email . '>',
'Content-Type: text/plain; charset=UTF-8'
);
// Envoyer l'email
if (wpmail($to, $subject, $body, $headers)) {
$messagesent = true;
// Hook pour les actions post-envoi
doaction('yawccontactformsent', array(
'nom' => $nom,
'email' => $email,
'sujet' => $sujet,
'message' => $message,
));
// Redirection si configurée
if (!empty($atts['redirect'])) {
wpsaferedirect(escurl($atts['redirect']));
exit;
}
} else {
$errormessage = 'Erreur lors de l'envoi du message. Veuillez réessayer.';
}
} else {
$errormessage = implode('
', $errors);
}
}
}
// Afficher le formulaire
obstart();
?>
sent) : ?>
message)) : ?>
getclean();
}
addshortcode('contactform', 'yawccontactformshortcode');
Protection contre les injections
Exemple de validation stricte pour un shortcode de recherche
/
function yawcsearchshortcode($atts) {
$atts = shortcodeatts(array(
'placeholder' => 'Rechercher...',
'buttontext' => 'Rechercher',
'posttype' => 'post',
), $atts, 'search');
// Valider le posttype contre une liste blanche
$allowedposttypes = getposttypes(array('public' => true), 'names');
$posttypes = arraymap('trim', explode(',', $atts['posttype']));
$posttypes = arrayfilter($posttypes, function($pt) use ($allowedposttypes) {
return inarray($pt, $allowedposttypes, true);
});
if (empty($posttypes)) {
$posttypes = array('post');
}
obstart();
?>
getclean();
}
addshortcode('search', 'yawcsearchshortcode');
7. Optimisation des performances
Mise en cache des shortcodes
Shortcode avec mise en cache transient
/
function yawccachedstatsshortcode($atts) {
$atts = shortcodeatts(array(
'cacheduration' => 3600, // 1 heure par défaut
), $atts, 'stats');
// Créer une clé de cache unique basée sur les attributs
$cachekey = 'yawcstats' . md5(serialize($atts));
// Essayer de récupérer depuis le cache
$cachedoutput = gettransient($cachekey);
if ($cachedoutput !== false) {
return $cachedoutput . '';
}
// Générer le contenu (opération coûteuse)
$output = yawcgeneratestatshtml();
// Mettre en cache
settransient($cachekey, $output, absint($atts['cacheduration']));
return $output;
}
addshortcode('stats', 'yawccachedstatsshortcode');
/
Nettoyer le cache lors de la publication d'un article
/
addaction('savepost', function($postid) {
if (wpispostrevision($postid) || wpispostautosave($postid)) {
return;
}
// Supprimer tous les transients de stats
global $wpdb;
$wpdb->query(
"DELETE FROM $wpdb->options
WHERE optionname LIKE 'transientyawcstats%'
OR optionname LIKE 'transienttimeoutyawcstats%'"
);
});
function yawcgeneratestatshtml() {
global $wpdb;
$stats = array(
'posts' => wpcountposts('post')->publish,
'pages' => wpcountposts('page')->publish,
'comments' => wpcountcomments()->approved,
'users' => countusers()['totalusers'],
);
obstart();
?>
formati18n($stats['posts']); ?>
Articles
formati18n($stats['pages']); ?>
Pages
formati18n($stats['comments']); ?>
Commentaires
formati18n($stats['users']); ?>
Utilisateurs
getclean();
}
Chargement conditionnel des assets
Enregistrer les assets mais ne les charger que si le shortcode est présent
/
class YAWCShortcodeAssets {
private static $shortcodesfound = array();
public static function init() {
addaction('wpenqueuescripts', array(CLASS, 'registerassets'));
addfilter('thecontent', array(CLASS, 'detectshortcodes'), 1);
addaction('wpfooter', array(CLASS, 'enqueuedetectedassets'));
}
public static function registerassets() {
// Enregistrer mais ne pas charger
wpregisterstyle(
'yawc-tabs',
pluginsurl('assets/css/tabs.css', FILE),
array(),
'1.0.0'
);
wpregisterscript(
'yawc-tabs',
pluginsurl('assets/js/tabs.js', FILE),
array(),
'1.0.0',
true
);
wpregisterstyle(
'yawc-box',
pluginsurl('assets/css/box.css', FILE),
array(),
'1.0.0'
);
wpregisterscript(
'yawc-box',
pluginsurl('assets/js/box.js', FILE),
array(),
'1.0.0',
true
);
}
public static function detectshortcodes($content) {
// Détecter les shortcodes dans le contenu
if (hasshortcode($content, 'tabs')) {
self::$shortcodesfound[] = 'tabs';
}
if (hasshortcode($content, 'box')) {
self::$shortcodesfound[] = 'box';
}
return $content;
}
public static function enqueuedetectedassets() {
// Charger uniquement les assets nécessaires
foreach (arrayunique(self::$shortcodesfound) as $shortcode) {
if (wpstyleis($shortcode, 'registered')) {
wpenqueuestyle('yawc-' . $shortcode);
}
if (wpscriptis($shortcode, 'registered')) {
wpenqueuescript('yawc-' . $shortcode);
}
}
}
}
YAWCShortcodeAssets::init();
8. Cas d’usage avancés
Shortcode avec requête Ajax
Shortcode de chargement de contenu Ajax
/
function yawcajaxloadershortcode($atts) {
$atts = shortcodeatts(array(
'action' => '',
'buttontext' => 'Charger plus',
'container' => 'posts',
), $atts, 'ajaxloader');
if (empty($atts['action'])) {
return 'Erreur: action requise
';
}
$loaderid = 'ajax-loader-' . wpgeneratepassword(8, false);
// Passer les données au JavaScript
wplocalizescript('yawc-ajax-loader', 'yawcAjaxLoader', array(
'ajaxurl' => adminurl('admin-ajax.php'),
'nonce' => wpcreatenonce('yawcajaxloader'),
));
obstart();
?>
getclean();
}
addshortcode('ajaxloader', 'yawcajaxloadershortcode');
/
Handler Ajax pour charger plus d'articles
/
function yawcloadmorepostsajax() {
checkajaxreferer('yawcajaxloader', 'nonce');
$page = isset($POST['page']) ? absint($POST['page']) : 1;
$args = array(
'posttype' => 'post',
'postsperpage' => 6,
'paged' => $page,
'poststatus' => 'publish',
);
$query = new WPQuery($args);
if (!$query->haveposts()) {
wpsendjsonerror(array('message' => 'Aucun article supplémentaire'));
}
obstart();
while ($query->haveposts()) {
$query->thepost();
?>
### title(); ?>
excerpt(); ?>
resetpostdata();
$html = obgetclean();
wpsendjsonsuccess(array(
'html' => $html,
'hasmore' => $page < $query->maxnumpages,
'nextpage' => $page + 1,
));
}
addaction('wpajaxloadmoreposts', 'yawcloadmorepostsajax');
addaction('wpajaxnoprivloadmoreposts', 'yawcloadmorepostsajax');
JavaScript correspondant:
// yawc-ajax-loader.js
(function($) {
'use strict';
class AjaxLoader {
constructor(element) {
this.element = $(element);
this.action = this.element.data('action');
this.button = this.element.find('.yawc-load-more');
this.container = this.element.find('.yawc-ajax-content');
this.spinner = this.element.find('.yawc-ajax-spinner');
this.page = 1;
this.button.on('click', () => this.loadMore());
}
loadMore() {
this.page++;
this.button.prop('disabled', true);
this.spinner.show();
$.ajax({
url: yawcAjaxLoader.ajaxurl,
type: 'POST',
data: {
action: this.action,
nonce: yawcAjaxLoader.nonce,
page: this.page
},
success: (response) => {
if (response.success) {
this.container.append(response.data.html);
if (!response.data.hasmore) {
this.button.text('Aucun article supplémentaire').prop('disabled', true);
}
} else {
alert(response.data.message);
this.button.hide();
}
},
error: () => {
alert('Erreur lors du chargement');
},
complete: () => {
this.spinner.hide();
this.button.prop('disabled', false);
}
});
}
}
$(document).ready(function() {
$('.yawc-ajax-loader').each(function() {
new AjaxLoader(this);
});
});
})(jQuery);
Shortcode avec géolocalisation
Shortcode pour afficher du contenu basé sur la géolocalisation
/
function yawcgeocontentshortcode($atts, $content = null) {
$atts = shortcodeatts(array(
'pays' => '',
'ville' => '',
'fallback' => 'true',
), $atts, 'geocontent');
// Détecter le pays de l'utilisateur
$usercountry = yawcgetusercountry();
$usercity = yawcgetusercity();
$showcontent = false;
if (!empty($atts['pays'])) {
$allowedcountries = arraymap('trim', explode(',', $atts['pays']));
$showcontent = inarray($usercountry, $allowedcountries, true);
}
if (!empty($atts['ville']) && $showcontent) {
$allowedcities = arraymap('trim', explode(',', $atts['ville']));
$showcontent = inarray($usercity, $allowedcities, true);
}
if ($showcontent || $atts['fallback'] === 'true') {
return doshortcode($content);
}
return '';
}
addshortcode('geocontent', 'yawcgeocontentshortcode');
/
Obtenir le pays de l'utilisateur via API ou cache
/
function yawcgetusercountry() {
// Vérifier le cache de session
if (isset($SESSION['yawcusercountry'])) {
return $SESSION['yawcusercountry'];
}
$ip = yawcgetuserip();
// Utiliser une API de géolocalisation
$response = wpremoteget('http://ip-api.com/json/' . $ip);
if (iswperror($response)) {
return 'UNKNOWN';
}
$data = jsondecode(wpremoteretrievebody($response), true);
$country = isset($data['countryCode']) ? $data['countryCode'] : 'UNKNOWN';
// Mettre en cache
$SESSION['yawcusercountry'] = $country;
return $country;
}
function yawcgetuserip() {
$ip = '';
if (!empty($SERVER['HTTPCLIENTIP'])) {
$ip = $SERVER['HTTPCLIENTIP'];
} elseif (!empty($SERVER['HTTPXFORWARDEDFOR'])) {
$ip = $SERVER['HTTPXFORWARDEDFOR'];
} else {
$ip = $SERVER['REMOTEADDR'];
}
return filtervar($ip, FILTERVALIDATEIP) ? $ip : '0.0.0.0';
}
9. Debugging et résolution de problèmes
Mode debug pour shortcodes
Classe de debugging pour shortcodes / class YAWCShortcodeDebug { private static $logs = array(); public static function init() { if (!defined('WPDEBUG') || !WPDEBUG) { return; } addfilter('doshortcodetag', array(CLASS, 'logshortcode'), 10, 4); addaction('wpfooter', array(CLASS, 'displaylogs')); } public static function logshortcode($output, $tag, $attr, $m) { $starttime = microtime(true); self::$logs[] = array( 'tag' => $tag, 'attributes' => $attr, 'outputlength' => strlen($output), 'time' => microtime(true) - $starttime, 'memory' => memorygetusage(), ); return $output; } public static function displaylogs() { if (empty(self::$logs)) { return; } echo ''; echo 'Shortcode Debug Info
'; echo ''; echo '
'; foreach (self::$logs as $log) { printf( ' Tag Attributes Output Length Time (ms) Memory (MB) %s %s%d %.2f %.2f ',
eschtml($log['tag']),
eschtml(printr($log['attributes'], true)),
$log['outputlength'],
$log['time'] 1000,
$log['memory'] / 1024 / 1024
);
}echo '
';
}
}YAWCShortcodeDebug::init();
### Vérification des shortcodes non enregistrés
Détecter les shortcodes utilisés mais non enregistrés / function yawcdetectunregisteredshortcodes($content) { if (!isadmin() && currentusercan('manageoptions')) { global $shortcodetags; // Pattern pour trouver tous les shortcodes pregmatchall('/[(w+)/', $content, $matches); if (!empty($matches[1])) { $foundshortcodes = arrayunique($matches[1]); $unregistered = array(); foreach ($foundshortcodes as $tag) { if (!arraykeyexists($tag, $shortcodetags)) { $unregistered[] = $tag; } } if (!empty($unregistered)) { addaction('wpfooter', function() use ($unregistered) { echo ''; echo 'Shortcodes non enregistrés détectés: '; echo implode(', ', arraymap('eschtml', $unregistered)); echo ''; }); } } } return $content; } addfilter('thecontent', 'yawcdetectunregisteredshortcodes', 999);Tester les shortcodes en isolation
Fonction utilitaire pour tester les shortcodes / function yawctestshortcode($tag, $atts = array(), $content = null) { if (!functionexists('doshortcode')) { requireonce ABSPATH . 'wp-includes/shortcodes.php'; } $attsstring = ''; foreach ($atts as $key => $value) { $attsstring .= sprintf(' %s="%s"', $key, escattr($value)); } if ($content !== null) { $shortcodestring = sprintf('[%s%s]%s[/%s]', $tag, $attsstring, $content, $tag); } else { $shortcodestring = sprintf('[%s%s]', $tag, $attsstring); } echo '### Test: ' . eschtml($shortcodestring) . ''; echo ''; echo doshortcode($shortcodestring); echo ''; } // Utilisation dans un template de test if (currentusercan('manageoptions') && isset($GET['testshortcodes'])) { yawctestshortcode('dateactuelle'); yawctestshortcode('articlesrecents', array('nombre' => 3)); yawctestshortcode('box', array('type' => 'warning'), 'Contenu de test'); }10. Bonnes pratiques professionnelles
Organisation du code
Structure organisée pour un plugin de shortcodes / // Structure de fichiers recommandée: / /mon-plugin-shortcodes/ ├── mon-plugin-shortcodes.php (fichier principal) ├── includes/ │ ├── class-shortcode-base.php │ ├── shortcodes/ │ │ ├── class-tabs-shortcode.php │ │ ├── class-box-shortcode.php │ │ └── class-contact-form-shortcode.php │ └── traits/ │ ├── trait-cacheable.php │ └── trait-ajax-handler.php ├── assets/ │ ├── css/ │ ├── js/ │ └── images/ └── templates/ ├── tabs.php ├── box.php └── contact-form.php / // Classe de base pour tous les shortcodes abstract class YAWCShortcodeBase { protected $tag; protected $defaults = array(); abstract public function render($atts, $content = null); public function construct($tag) { $this->tag = $tag; addshortcode($tag, array($this, 'process')); } public function process($atts, $content = null) { $atts = shortcodeatts($this->defaults, $atts, $this->tag); $atts = $this->validateatts($atts); return $this->render($atts, $content); } protected function validateatts($atts) { // Validation de base, à surcharger dans les classes enfants return $atts; } protected function gettemplate($name, $vars = array()) { extract($vars); $templatepath = plugindirpath(FILE) . 'templates/' . $name . '.php'; if (!fileexists($templatepath)) { return ''; } obstart(); include $templatepath; return obgetclean(); } } // Exemple de shortcode utilisant la classe de base class YAWCTabsShortcode extends YAWCShortcodeBase { protected $defaults = array( 'style' => 'default', ); public function construct() { parent::construct('tabs'); } protected function validateatts($atts) { $validstyles = array('default', 'pills', 'underline'); if (!inarray($atts['style'], $validstyles)) { $atts['style'] = 'default'; } return $atts; } public function render($atts, $content = null) { wpenqueuestyle('yawc-tabs'); wpenqueuescript('yawc-tabs'); return $this->gettemplate('tabs', array( 'style' => $atts['style'], 'content' => doshortcode($content), )); } } // Initialisation new YAWCTabsShortcode();Documentation des shortcodes
Ajouter une page d'aide dans l'admin / function yawcshortcodeshelppage() { addmenupage( 'Shortcodes Disponibles', 'Shortcodes', 'editposts', 'yawc-shortcodes', 'yawcrendershortcodeshelp', 'dashicons-shortcode', 30 ); } addaction('adminmenu', 'yawcshortcodeshelppage'); function yawcrendershortcodeshelp() { global $shortcodetags; $yawcshortcodes = array( 'dateactuelle' => array( 'description' => 'Affiche la date actuelle', 'attributes' => array(), 'example' => '[dateactuelle]', ), 'articlesrecents' => array( 'description' => 'Affiche les articles récents', 'attributes' => array( 'nombre' => 'Nombre d'articles à afficher (défaut: 5)', 'categorie' => 'Slug de la catégorie à filtrer', 'ordre' => 'ASC ou DESC (défaut: DESC)', 'afficher' => 'Éléments à afficher: titre,extrait,image,date', ), 'example' => '[articlesrecents nombre="3" categorie="tutoriels"]', ), 'box' => array( 'description' => 'Crée une boîte stylisée', 'attributes' => array( 'type' => 'info, warning, success, error (défaut: info)', 'titre' => 'Titre de la boîte', 'icone' => 'true/false - Afficher l'icône', 'fermable' => 'true/false - Ajouter un bouton de fermeture', ), 'example' => '[box type="warning" titre="Attention"]Contenu[/box]', ), ); ?># Shortcodes Disponibles shortcodes as $tag => $info) : ?>## html($tag); ?>html($info['description']); ?>
### Attributs disponibles:$desc) : ?>
### Exemple d'utilisation:- html($attr); ?>: html($desc); ?>
html($info['example']); ?>Bouton TinyMCE pour insérer des shortcodes
Ajouter un bouton dans l'éditeur visuel */ function yawcaddshortcodebutton() { if (!currentusercan('editposts') && !currentusercan('editpages')) { return; } if (getuseroption('richediting') !== 'true') { return; } addfilter('mceexternalplugins', 'yawcaddtinymceplugin'); addfilter('mcebuttons', 'yawcregistershortcodebutton'); } addaction('adminhead', 'yawcaddshortcodebutton'); function yawcaddtinymceplugin($pluginarray) { $pluginarray['yawcshortcodes'] = pluginsurl('assets/js/tinymce-plugin.js', FILE); return $pluginarray; } function yawcregistershortcodebutton($buttons) { $buttons[] = 'yawcshortcodes'; return $buttons; }Fichier tinymce-plugin.js:
(function() { tinymce.PluginManager.add('yawcshortcodes', function(editor, url) { const shortcodes = [ { text: 'Date actuelle', value: '[dateactuelle]' }, { text: 'Articles récents', value: '[articlesrecents nombre="5"]' }, { text: 'Boîte Info', value: '[box type="info" titre="Information"]Votre contenu ici[/box]' }, { text: 'Tabs', value: '[tabs]n[tab titre="Onglet 1"]Contenu 1[/tab]n[tab titre="Onglet 2"]Contenu 2[/tab]n[/tabs]' } ]; editor.addButton('yawcshortcodes', { text: 'Shortcodes', icon: false, type: 'menubutton', menu: shortcodes.map(sc => ({ text: sc.text, onclick: function() { editor.insertContent(sc.value); } })) }); }); })();Conclusion
Les shortcodes WordPress sont un outil puissant pour étendre les fonctionnalités de votre site tout en maintenant une interface utilisateur simple. En suivant les bonnes pratiques présentées dans ce guide, vous pouvez créer des shortcodes professionnels, sécurisés et performants.
Points clés à retenir
Ressources complémentaires
Exercices pratiques
En maîtrisant les shortcodes, vous ajoutez un outil précieux à votre arsenal de développeur WordPress, permettant de créer des sites riches en fonctionnalités tout en préservant l'expérience utilisateur pour les éditeurs de contenu.