web-dev-qa-db-fra.com

Comment prévenir les conflits de dépendance Composer entre WP des plugins?

Dans notre développement de plug-in, nous utilisons Composer pour installer, par exemple, Symfony\Process que nous utiliserons plus tard dans le code. La grande question est comment faire en sorte que cette dépendance ne soit pas en conflit avec un autre plugin en utilisant également Symfony\Process dans une autre version.

Maintenant, je sais que c’est principalement un problème inhérent à PHP, mais nous souhaitons tout de même proposer à nos utilisateurs une solution qui les avertisse au moins lorsqu'un tel conflit se produit (au lieu d’échouer de manière irréversible). Une approche, par exemple, consisterait à analyser le répertoire des plugins à la recherche de dossiers vendor et à essayer de déterminer si des versions incompatibles sont utilisées dans un autre plugin. Je sais que loin (comme vraiment, très loin) de la perfection, mais j'essaie juste de donner un exemple.

Comment gérez-vous cela? Je sais que Composer n’est pas encore très répandu parmi les développeurs de plug-ins, mais nous aurons tous ce problème tôt ou tard, alors je me demande si quelqu'un a imaginé une sorte de stratégie pour le résoudre.

5
Borek Bernard

La réponse est: vous ne pouvez pas parce que WordPress ne le peut pas. Il n'y a pas de concept de gestion de dépendance dans WP et c'était le même cas avant même que Composer n'existe.

La montée de l'autoload (Composer ou autre) y contribue légèrement, dans la mesure où vous pourriez simplement charger une copie différente (assez proche, espérons-le) plutôt qu'une erreur fatale pure et simple. Vous pouvez vérifier cela si vous concevez une méthode. Par exemple, en vérifiant les chemins, les définitions ont été chargées avec get_included_files(), mais en réalité, ce n'est qu'un pansement, pas une solution.

Le seul scénario dans lequel Composer vraiment vous y aide est celui-ci qui permet de contrôler l’ensemble de l’installation WP, avec un seul dossier de fournisseur partagé.

5
Rarst

Travaillant sur un CMS de petites annonces fortement inspiré de Wordpress (je ne l’ai pas nommé osclass.org), je suis tombé sur votre fil. Nous sommes exactement dans la même situation, devant gérer plusieurs plugins utilisant la même dépendance.

Pour expliquer la solution que nous avons trouvée, nous avons deux plugins:

  • P1 utilise la dépendance D1 dans la version 1.20
  • P2 utilise la dépendance D1 dans la version 1.30

Nous devons nous assurer que la version 1.30 est chargée pour que les deux plugins fonctionnent correctement.

1. Plugins ordre de chargement

Wordpress (et Osclass) ont une option active_plugins (dans la table wp_options) qui définit les plugins actifs et, plus important encore, dans notre cas: plugin loading order .

Ceci est un tableau simple.

2. Vérification de la version

Au début de nos deux plugins, nous vérifions la version de D1 pour nous assurer que celle-ci correspond à nos besoins (ou à une version supérieure).

// Custom autoloader, we'll check it in a moment.
require __DIR__ . "/vendor/composer_components/madhouse/autoloader/autoload.php";

if(!function_exists("mdh_utils") || (function_exists("mdh_utils") && strnatcmp(mdh_utils(), "1.30") === -1)) {
    // Dependency version is lower than 1.30 (which we need), let's bump.
    mdh_bump_me();
} else {
    // Rest of the plugin code.
}

Cas simple: que se passe-t-il lorsque P2 est chargé avant P1?

  • P2 est chargé et D1 avec. Remarque: la version chargée de D1 est 1.30.
  • P1 est chargé ensuite, vérifie si D1 est chargé et si la version est 1.20 ou supérieure et trouve la version 1.30, puis se charge normalement.

Tout est bien.

Cas intéressant: que se passe-t-il lorsque P1 est chargé avant P2?

  • P1 est chargé avant P2, notre dépendance et notre plugin P1 sont chargés normalement. Remarque: la version chargée de D1 est 1.20.
  • P2 est chargé ensuite, il vérifie la version D1 et découvre que D1 a été chargé mais que la version n’est pas assez élevée (1,20 au lieu de 1,30).

Voici la version bosse.

3. Bosse!

N'oubliez pas que les plugins sont chargés dans l'ordre défini dans active_plugins.

La fonction bump - dans ce cas, mdh_bump_me () - place le plugin actuel en haut du active_plugins (première position du tableau).

Ressemble à ça. Ceci est un code lié à Osclass, mais il peut facilement être modifié pour fonctionner avec Wordpress:

function mdh_bump_me()
{
    // Sanitize & get the {PLUGIN_NAME}/index.php.
    $path = str_replace(osc_plugins_path(), '', osc_plugin_path(__FILE__));

    if(osc_plugin_is_installed($path)) {
        // Get the active plugins.
        $plugins_list = unserialize(osc_active_plugins()); // This is the active_plugins fields unserialized.
        if(!is_array($plugins_list)) {
            return false;
        }

        // Remove $path from the active plugins list
        foreach($plugins_list as $k => $v) {
            if($v == $path) {
                unset($plugins_list[$k]);
            }
        }

        // Re-add the $path at the beginning of the active plugins.
        array_unshift($plugins_list, $path);

        // Serialize the new active_plugins list.
        osc_set_preference('active_plugins', serialize($plugins_list));

        if(Params::getParam("page") === "plugins" && Params::getParam("action") === "enable" && Params::getParam("plugin") === $path) {
            //osc_redirect_to(osc_admin_base_url(true) . "?page=plugins");
        } else {
            osc_redirect_to(osc_admin_base_url(true) . "?" . http_build_query(Params::getParamsAsArray("get")));
        }
    }
}

En un mot,

  • Récupère la liste actuelle de active_plugins.
  • Supprimez le plugin actuel.
  • Ajoutez le plugin actuel au début du tableau.
  • Enregistrez l'option dans la base de données.
  • Rediriger vers la même page.

5. Autoloader personnalisé

Un mot sur notre chargeur automatique personnalisé. L'autochargeur de compositeur charge chaque classe (même si elle a déjà été définie), la nouvelle remplaçant l'ancienne.

Notre autochargeur personnalisé ne charge une classe que s'il n'existe pas encore. Charge la première instance de chaque classe et ignore toutes les autres.


J'espère que c'est assez clair.

Loin, loin d’être une solution idéale, mais qui fonctionne pour nous et c’est un pas en avant.

3
Hussard