# Shortcodes API : écrire un shortcode robuste, sûr et paramétrable

> [galerie_clients logo="oui"] : un shortcode d'apparence anodine, mais qui peut charger des styles inutiles sur tout le site s'il est mal écrit. Comment le faire proprement.

- Auteur : Clément Hadrot
- Publié le : 2020-11-18
- Mis à jour le : 2020-11-18
- Catégorie : Extensions
- URL : https://wpmoderne.dev.wordpress-developpement.fr/extensions/shortcodes-api-shortcode-robuste-parametrable/

## L’essentiel

- shortcode_atts fixe des valeurs par défaut et filtre les attributs inconnus
- Le contenu imbriqué doit être traité avec do_shortcode avant retour
- Les scripts et styles ne se chargent que si le shortcode est réellement utilisé

Un studio de photographie m'a demandé un shortcode `[galerie_clients]` pour afficher, sur n'importe quelle page, une sélection de logos de clients avec un lien optionnel vers leur site. Simple en apparence, mais la première version que j'ai livrée chargeait une feuille de styles CSS sur chaque page du site, y compris celles qui n'utilisaient jamais ce shortcode. Voici la version corrigée, et ce qu'elle m'a appris sur l'API des shortcodes.

## La signature complète d'un callback

Un callback de shortcode reçoit toujours trois arguments, même si beaucoup de tutoriels n'en montrent qu'un seul : les attributs, le contenu imbriqué (pour un shortcode à fermeture), et le nom du tag lui-même — utile quand une même fonction gère plusieurs shortcodes proches.

```
add_shortcode( 'galerie_clients', 'studio_shortcode_galerie_clients' );

function studio_shortcode_galerie_clients( $atts, $content = null, $tag = '' ) {
    $atts = shortcode_atts(
        array(
            'categorie' => '',
            'limite'    => 12,
            'lien'      => 'oui',
        ),
        $atts,
        $tag
    );

    // suite du traitement
}
```

`shortcode_atts()` joue un rôle souvent sous-estimé : elle ne se contente pas de fournir des valeurs par défaut, elle filtre aussi les attributs qui ne figurent pas dans le tableau de référence. Un utilisateur qui écrirait `[galerie_clients limitee="12"]` par erreur de frappe verra simplement l'attribut ignoré et la valeur par défaut appliquée, plutôt qu'un comportement imprévisible.

## Traiter le contenu imbriqué correctement

Pour un shortcode à fermeture, comme `[galerie_clients]texte additionnel[/galerie_clients]`, le contenu brut arrive tel quel dans `$content`, sans qu'aucun autre shortcode qu'il contiendrait n'ait été traité.

> L'essentiel à retenir : shortcode_atts fixe des valeurs par défaut et filtre les attributs inconnus ; Le contenu imbriqué doit être traité avec do_shortcode avant retour ; Les scripts et styles ne se chargent que si le shortcode est réellement utilisé

```
function studio_shortcode_galerie_clients( $atts, $content = null, $tag = '' ) {
    $atts = shortcode_atts( array(
        'categorie' => '',
        'limite'    => 12,
        'lien'      => 'oui',
    ), $atts, $tag );

    $sortie = '<div class="studio-galerie">';

    if ( ! empty( $content ) ) {
        $sortie .= '<div class="studio-galerie-intro">' . do_shortcode( $content ) . '</div>';
    }

    $sortie .= studio_generer_grille_logos( $atts );
    $sortie .= '</div>';

    return $sortie;
}
```

Oublier `do_shortcode()` sur le contenu imbriqué est une erreur classique : les shortcodes qu'un utilisateur aurait placés à l'intérieur ne s'exécuteraient jamais, laissant leur code brut affiché tel quel à l'écran.

Autre point essentiel : un callback de shortcode doit toujours retourner sa sortie avec `return`, jamais l'afficher directement avec `echo`. Un shortcode placé dans un widget de texte ou une boucle personnalisée produirait sinon un affichage désordonné, la sortie apparaissant avant le contenu qui l'entoure plutôt qu'à sa place.

## Échapper systématiquement les attributs affichés

Les attributs d'un shortcode proviennent du contenu éditorial, potentiellement saisi par plusieurs rédacteurs avec des niveaux de droits différents. Les traiter comme une source de confiance absolue expose à des injections de balises, même de façon non malveillante.

```
function studio_generer_grille_logos( $atts ) {
    $clients = studio_recuperer_clients( $atts['categorie'], (int) $atts['limite'] );
    $html    = '<ul class="studio-logos">';

    foreach ( $clients as $client ) {
        $html .= '<li>';
        if ( 'oui' === $atts['lien'] && ! empty( $client['url'] ) ) {
            $html .= '<a href="' . esc_url( $client['url'] ) . '">';
        }
        $html .= '<span>' . esc_html( $client['nom'] ) . '</span>';
        if ( 'oui' === $atts['lien'] && ! empty( $client['url'] ) ) {
            $html .= '</a>';
        }
        $html .= '</li>';
    }

    return $html . '</ul>';
}
```

`esc_url()` pour les liens, `esc_html()` pour le texte : ces deux fonctions suffisent à couvrir la quasi-totalité des sorties d'un shortcode de ce type, et leur coût en performance est négligeable comparé au risque évité.

## Charger les assets seulement quand le shortcode est utilisé

C'est le correctif qui a réellement changé la donne sur ce projet. Plutôt que d'enregistrer la feuille de styles sur `wp_enqueue_scripts` sans condition, je vérifie la présence du shortcode dans le contenu de l'article ou de la page courante avant de la charger.

```
add_action( 'wp_enqueue_scripts', 'studio_charger_assets_conditionnels' );

function studio_charger_assets_conditionnels() {
    if ( ! is_singular() ) {
        return;
    }

    global $post;

    if ( $post && has_shortcode( $post->post_content, 'galerie_clients' ) ) {
        wp_enqueue_style( 'studio-galerie-clients', plugins_url( 'css/galerie.css', __FILE__ ), array(), '1.1' );
    }
}
```

`has_shortcode()` reste une vérification textuelle sur le contenu brut : elle ne détecte pas un shortcode généré dynamiquement par un constructeur de pages ou inséré depuis un modèle de bloc réutilisable. Pour ce studio, dont le contenu passait uniquement par l'éditeur classique et Gutenberg, cette limite n'a jamais posé de problème concret.

## Pour aller plus loin

Un shortcode bien écrit reste l'un des moyens les plus simples de donner de l'autonomie à une équipe éditoriale, sans exposer de code PHP ni dépendre d'un constructeur de pages tiers. La rigueur se joue à trois endroits précis : des valeurs par défaut via `shortcode_atts`, un échappement systématique en sortie, et un chargement d'assets réellement conditionné à l'usage du shortcode sur la page affichée.
