# Strauss et PHP-Scoper : préfixer les dépendances Composer d’une extension

> Deux extensions du même site embarquent Guzzle, mais dans deux versions incompatibles. Résultat : une erreur fatale de classe déjà déclarée. Voici comment l'éviter dès la construction du plugin.

- Auteur : Clément Hadrot
- Publié le : 2022-07-06
- Mis à jour le : 2022-07-06
- Catégorie : Extensions
- URL : https://wpmoderne.dev.wordpress-developpement.fr/extensions/strauss-php-scoper-prefixer-dependances-composer/

## L’essentiel

- WordPress ne fournit aucun isolement entre les dépendances Composer de plugins différents
- Préfixer les espaces de noms des bibliothèques embarquées évite les collisions de classes
- Le build final ne doit jamais dépendre d'une exécution manuelle oubliable

Sur un site e-commerce utilisant à la fois une extension de facturation et une extension de synchronisation avec un comparateur de prix, développées par deux prestataires différents mais toutes deux construites avec Composer et embarquant la bibliothèque Guzzle, le site s'est mis à afficher une erreur fatale : `Cannot declare class GuzzleHttp\Client, because the name is already in use`. Les deux extensions embarquaient des versions incompatibles de la même bibliothèque, chargées dans le même espace de noms global PHP.

WordPress n'offre aucun isolement natif entre les dépendances Composer de plugins distincts : tout tourne dans le même processus PHP, avec un seul espace de noms partagé. La solution consiste à préfixer les espaces de noms des bibliothèques embarquées, pour que chaque extension transporte sa propre version sous un nom unique.

## Pourquoi Composer seul ne suffit pas

Composer gère très bien la résolution de versions au sein d'un seul projet, mais WordPress n'est pas un projet Composer unique : chaque extension embarque généralement son propre répertoire `vendor/`, chargé indépendamment via son propre `autoload.php`. Rien n'empêche deux extensions de déclarer, dans deux fichiers distincts, la même classe `GuzzleHttp\Client`, avec des implémentations différentes selon la version.

La seule façon fiable d'éviter ce type de collision consiste à renommer l'espace de noms de chaque bibliothèque embarquée, pour qu'elle devienne unique à chaque extension, même si le code source original est strictement identique d'un plugin à l'autre.

## Strauss : l'outil pensé pour l'écosystème WordPress

> L'essentiel à retenir : WordPress ne fournit aucun isolement entre les dépendances Composer de plugins différents ; Préfixer les espaces de noms des bibliothèques embarquées évite les collisions de classes ; Le build final ne doit jamais dépendre d'une exécution manuelle oubliable

Strauss est un outil né spécifiquement pour ce contexte WordPress, en complément de Composer plutôt qu'en remplacement. Il copie les dépendances déclarées dans une section dédiée du `composer.json`, et réécrit leurs espaces de noms avec un préfixe propre à l'extension.

```
{
    "require": {
        "guzzlehttp/guzzle": "^7.4"
    },
    "extra": {
        "strauss": {
            "namespace_prefix": "KaolinComparateur\\Dependencies\\",
            "classmap_prefix": "KaolinComparateur_",
            "target_directory": "vendor-prefixed",
            "packages": [
                "guzzlehttp/guzzle"
            ]
        }
    }
}
```

Après exécution de Strauss, les classes de Guzzle ne sont plus accessibles sous `GuzzleHttp\Client`, mais sous `KaolinComparateur\Dependencies\GuzzleHttp\Client`, un espace de noms qu'aucune autre extension n'a de raison de réutiliser à l'identique.

```
use KaolinComparateur\Dependencies\GuzzleHttp\Client;

$client = new Client( array( 'timeout' => 10 ) );
```

## Intégrer Strauss dans le processus de build

Le piège le plus fréquent avec Strauss n'est pas sa configuration, mais son oubli : si l'exécution du préfixage dépend d'une commande lancée manuellement par un développeur avant chaque publication, elle finit tôt ou tard par être oubliée, livrant une version non préfixée en production.

```
{
    "scripts": {
        "post-install-cmd": "strauss",
        "post-update-cmd": "strauss"
    }
}
```

Brancher Strauss sur les scripts Composer `post-install-cmd` et `post-update-cmd` garantit que le préfixage se produit systématiquement à chaque installation ou mise à jour des dépendances, sans dépendre de la mémoire du développeur qui exécute la commande.

## PHP-Scoper : une alternative plus large

PHP-Scoper vise un objectif proche, mais avec un périmètre plus large : il peut préfixer l'ensemble du code d'un projet, pas uniquement ses dépendances tierces, et s'intègre bien dans un pipeline de build produisant un fichier PHAR ou une archive de distribution complète.

- Strauss reste plus simple à intégrer pour le cas courant d'une extension WordPress qui souhaite seulement isoler ses dépendances
- PHP-Scoper convient mieux à des projets plus complexes, où l'ensemble du code, y compris propriétaire, doit être isolé dans un espace de noms unique
- Les deux outils reposent sur le même principe de réécriture statique des espaces de noms à la construction, jamais à l'exécution

> Préfixer ses propres classes métier n'est généralement pas nécessaire : un bon préfixe de fonction et de classe suffit. Le préfixage automatisé concerne avant tout les bibliothèques tierces qu'on ne maîtrise pas et qui risquent de se retrouver, sans le vouloir, embarquées ailleurs sur le même site.

## En résumé

Le conflit rencontré sur ce site e-commerce n'avait rien d'exceptionnel : c'est un scénario qui se reproduit dès qu'un site cumule plusieurs extensions construites indépendamment avec Composer, sans isolation des dépendances. Strauss, intégré directement au processus de build via les scripts Composer, a permis de livrer une nouvelle version de l'extension de comparateur qui coexiste désormais sans le moindre conflit avec l'extension de facturation, quelle que soit la version de Guzzle que cette dernière embarque de son côté.
