# esc_url_raw avant d’enregistrer une URL en base, esc_url seulement à l’affichage

> Deux fonctions qui portent presque le même nom, mais qui répondent à des moments totalement différents du cycle de vie d'une URL.

- Auteur : WordPress Développement
- Publié le : 2024-02-19
- Mis à jour le : 2024-02-19
- Catégorie : Astuces
- URL : https://www.wpmoderne.fr/tips/esc-url-raw-esc-url-ecriture-lecture/

## L’essentiel

- esc_url_raw() prépare une URL avant son enregistrement en base
- esc_url() échappe une URL uniquement au moment de l'affichage
- Utiliser l'une à la place de l'autre casse rarement le code, mais fausse son intention

`esc_url_raw( $_POST['site_web'] )` avant un `update_post_meta()`, ou `esc_url( get_post_meta( $id, 'site_web', true ) )` juste avant un `echo` dans un attribut `href` : ces deux lignes se ressemblent, mais elles interviennent à des moments radicalement différents du cycle de vie d'une même donnée.

La confusion entre `esc_url()` et `esc_url_raw()` est l'une des plus fréquentes chez les développeurs qui découvrent l'API de sanitization de WordPress, précisément parce que les deux fonctions partagent la même base de traitement, à un détail près qui change tout leur usage recommandé.

## Ce que les deux fonctions ont en commun

En interne, `esc_url()` et `esc_url_raw()` reposent toutes deux sur la même fonction sous-jacente, `_deep_replace()` couplée à un jeu de règles communes : suppression des caractères invalides pour une URL, encodage des caractères spéciaux, validation du protocole autorisé (`http`, `https`, `mailto`, `ftp`, entre autres selon le contexte). Cette base commune explique pourquoi les deux fonctions produisent souvent un résultat très proche sur une URL simple.

## La différence qui compte : l'échappement des esperluettes

> L'essentiel à retenir : esc_url_raw() prépare une URL avant son enregistrement en base ; esc_url() échappe une URL uniquement au moment de l'affichage ; Utiliser l'une à la place de l'autre casse rarement le code, mais fausse son intention

Le point de divergence se situe sur le traitement du caractère `&` dans l'URL. `esc_url()`, pensée pour un affichage direct dans du HTML, encode cette esperluette en entité HTML `&#038;`, conforme aux règles de validation d'un document HTML. `esc_url_raw()`, elle, laisse l'esperluette telle quelle, puisque la donnée n'est pas destinée à un affichage HTML immédiat mais à un enregistrement brut, par exemple en base de données ou dans une requête HTTP sortante :

```
$url = 'https://example.com/page?a=1&b=2';

echo esc_url( $url );
// https://example.com/page?a=1&b=2

echo esc_url_raw( $url );
// https://example.com/page?a=1&b=2
```

Si on enregistre en base une URL déjà passée par `esc_url()`, l'entité HTML `&#038;` se retrouve stockée telle quelle dans la donnée, ce qui casse potentiellement l'URL si elle est ensuite réutilisée pour un appel HTTP réel plutôt que pour un affichage.

## La règle simple à retenir

- `esc_url_raw()` : avant tout enregistrement en base, toute requête HTTP sortante avec `wp_remote_get()`, toute redirection avec `wp_redirect()`.
- `esc_url()` : au moment précis de l'affichage dans un attribut HTML, jamais avant.

```
// À l'enregistrement.
update_post_meta( $post_id, 'site_web', esc_url_raw( $_POST['site_web'] ) );

// À l'affichage, plus tard, potentiellement dans un autre fichier.
$url = get_post_meta( $post_id, 'site_web', true );
printf( '<a href="%s">Visiter le site</a>', esc_url( $url ) );
```

## Ce qui se passe si on inverse les deux fonctions

Le cas le plus problématique consiste à utiliser `esc_url()` au moment de l'enregistrement. La donnée stockée contient alors des entités HTML encodées, ce qui pose problème dès que cette URL doit être réutilisée dans un contexte non HTML : un appel `wp_remote_get()` vers cette URL échoue si l'esperluente encodée n'est pas décodée au préalable, une redirection HTTP peut produire une URL techniquement invalide selon le serveur cible.

L'inverse — utiliser `esc_url_raw()` à l'affichage plutôt que `esc_url()` — casse moins visiblement le rendu, mais laisse passer une esperluette non encodée dans un attribut HTML, ce qui reste techniquement invalide selon les standards du W3C, même si la majorité des navigateurs l'interprètent correctement malgré tout.

## Le filtre commun aux deux fonctions

Les deux fonctions déclenchent le même filtre, `clean_url`, avec un troisième argument qui indique le contexte d'appel (`display` pour `esc_url()`, `db` pour `esc_url_raw()`). Ce détail permet, dans de rares cas avancés, d'appliquer une règle différente selon le contexte réel d'utilisation :

```
add_filter( 'clean_url', function ( $url, $original_url, $context ) {
    if ( 'db' === $context ) {
        // Traitement spécifique avant enregistrement.
    }

    return $url;
}, 10, 3 );
```

> Sur tout formulaire qui collecte une URL, séparer clairement le point d'enregistrement du point d'affichage dans le code aide à se souvenir naturellement de quelle fonction utiliser à quel moment.

## En résumé

Retenir une seule phrase suffit à ne plus jamais confondre les deux : `esc_url_raw()` pour écrire, `esc_url()` pour afficher. Le reste — validation du protocole, retrait des caractères invalides — est déjà partagé entre les deux fonctions et ne nécessite aucune attention particulière.
