{"id":76416,"date":"2024-03-04T14:08:19","date_gmt":"2024-03-04T13:08:19","guid":{"rendered":"https:\/\/kinsta.com\/fr\/?p=76416&#038;preview=true&#038;preview_id=76416"},"modified":"2024-03-12T18:30:10","modified_gmt":"2024-03-12T17:30:10","slug":"plugin-wordpress-extensions-pro-versions-gratuites","status":"publish","type":"post","link":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/","title":{"rendered":"Construire un plugin WordPress pour supporter les extensions"},"content":{"rendered":"<p>Dans l&rsquo;\u00e9cosyst\u00e8me WordPress, l&rsquo;adoption d&rsquo;un <a href=\"https:\/\/kinsta.com\/fr\/blog\/comment-applications-gagnent-argent\/\">mod\u00e8le freemium<\/a> est une m\u00e9thode courante de promotion et de mon\u00e9tisation d&rsquo;extensions commercialles.\u00a0 Cette approche consiste \u00e0 publier une version basique de l&rsquo;extension gratuitement &#8211; g\u00e9n\u00e9ralement par l&rsquo;interm\u00e9diaire du <a href=\"https:\/\/wordpress.org\/plugins\/\" target=\"_blank\" rel=\"noopener noreferrer\">r\u00e9pertoire des extensions WordPress <\/a>&#8211; et \u00e0 offrir des fonctionnalit\u00e9s am\u00e9lior\u00e9es par le biais d&rsquo;une version PRO ou de modules, g\u00e9n\u00e9ralement vendus sur le site web de l&rsquo;extension.<\/p>\n<p>Il existe trois fa\u00e7ons diff\u00e9rentes d&rsquo;int\u00e9grer des fonctionnalit\u00e9s commerciales dans un mod\u00e8le freemium :<\/p>\n<ol>\n<li>Int\u00e9grer ces fonctionnalit\u00e9s commerciales dans l&rsquo;extension gratuite et ne les activer que lorsque la version commerciale est install\u00e9e sur le site web ou qu&rsquo;une cl\u00e9 de licence commerciale est fournie.<\/li>\n<li>Cr\u00e9ez les versions gratuite et PRO comme des extensions ind\u00e9pendantes, la version PRO \u00e9tant con\u00e7ue pour remplacer la version gratuite, de sorte qu&rsquo;une seule version soit install\u00e9e \u00e0 tout moment.<\/li>\n<li>Installez la version PRO \u00e0 c\u00f4t\u00e9 de l&rsquo;extension gratuite, en \u00e9tendant ses fonctionnalit\u00e9s. Pour cela, les deux versions doivent \u00eatre pr\u00e9sentes.<\/li>\n<\/ol>\n<p>Cependant, la premi\u00e8re approche est <a href=\"https:\/\/developer.wordpress.org\/plugins\/wordpress-org\/detailed-plugin-guidelines\/#5-trialware-is-not-permitted\" target=\"_blank\" rel=\"noopener noreferrer\">incompatible avec les lignes directrices<\/a> relatives aux extensions distribu\u00e9es via le r\u00e9pertoire des extensions WordPress, car ces r\u00e8gles interdisent l&rsquo;inclusion de fonctionnalit\u00e9s restreintes ou verrouill\u00e9es jusqu&rsquo;\u00e0 ce qu&rsquo;un paiement ou une mise \u00e0 niveau soit effectu\u00e9.<\/p>\n<p>Il nous reste donc les deux derni\u00e8res options, qui pr\u00e9sentent des avantages et des inconv\u00e9nients. Les sections ci-dessous expliquent pourquoi la derni\u00e8re strat\u00e9gie, \u00ab PRO en plus de gratuit \u00bb, est notre meilleur choix.<\/p>\n<p>Nous allons nous pencher sur la seconde option, \u00ab PRO en remplacement de gratuit \u00bb, sur ses d\u00e9fauts et sur les raisons pour lesquelles elle n&rsquo;est finalement pas recommand\u00e9e.<\/p>\n<p>Ensuite, nous explorerons en profondeur la strat\u00e9gie \u00ab PRO en plus de gratuit \u00bb, en soulignant les raisons pour lesquelles elle constitue le meilleur choix.<\/p>\n<div><\/div><kinsta-auto-toc heading=\"Table of Contents\" exclude=\"last\" list-style=\"arrow\" selector=\"h2\" count-number=\"-1\"><\/kinsta-auto-toc>\n<h2>Avantages de la strat\u00e9gie\u00a0 \u00ab PRO en remplacement de gratuit \u00bb<\/h2>\n<p>La strat\u00e9gie w PRO en remplacement de free \u00bb est relativement facile \u00e0 mettre en \u0153uvre, car les d\u00e9veloppeurs peuvent utiliser une base de code unique pour les deux extensions (gratuite et PRO) et cr\u00e9er deux sorties \u00e0 partir de celle-ci, la version gratuite (ou \u00ab standard \u00bb) comprenant simplement un sous-ensemble du code, et la version PRO comprenant tout le code.<\/p>\n<p>Par exemple, la base de code du projet pourrait \u00eatre divis\u00e9e en deux r\u00e9pertoires : <code>standard\/<\/code> et <code>pro\/<\/code>. L&rsquo;extension chargerait toujours le code standard, le code PRO \u00e9tant charg\u00e9 conditionnellement, en fonction de la pr\u00e9sence du r\u00e9pertoire concern\u00e9 :<\/p>\n<pre><code class=\"language-php\">\/\/ Main plugin file: myplugin.php\n\n\/\/ Always load the standard plugin's code\nrequire_once __DIR__ . '\/standard\/load.php';\n\n\/\/ Load the PRO plugin's code only if the folder exists\n$proFolder = __DIR__ . '\/pro';\nif (file_exists($proFolder)) {\n  require_once $proFolder . '\/load.php';\n}\n<\/code><\/pre>\n<p>Ensuite, lors de la g\u00e9n\u00e9ration de l&rsquo;extension via un outil d&rsquo;<a href=\"https:\/\/kinsta.com\/fr\/blog\/configurer-pipeline-ci-cd\/\">int\u00e9gration continue<\/a>, nous pouvons cr\u00e9er les deux ressources <code>myplugin-standard.zip<\/code> et <code>myplugin-pro.zip<\/code> \u00e0 partir du m\u00eame code source.<\/p>\n<p>Si vous h\u00e9bergez le projet sur GitHub et que vous g\u00e9n\u00e9rez les ressources via GitHub Actions, le flux de travail suivant fait le travail :<\/p>\n<pre><code class=\"language-yaml\">name: Generate the standard and PRO plugins\non:\n  release:\n    types: [published]\n\njobs:\n  process:\n    name: Generate plugins\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v3\n\n      - name: Install zip\n        uses: montudor\/action-zip@v1.0.0\n\n      - name: Create the standard plugin .zip file (excluding all PRO code)\n        run: zip -X -r myplugin-standard.zip . -x **\/src\/\\pro\/\\*\n\n      - name: Create the PRO plugin .zip file\n        run: zip -X -r myplugin-pro.zip . -x myplugin-standard.zip\n\n      - name: Upload both plugins to the release page\n        uses: softprops\/action-gh-release@v1\n        with:\n          files: |\n            myplugin-standard.zip\n            myplugin-pro.zip\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n<h2>Probl\u00e8mes li\u00e9s \u00e0 la strat\u00e9gie\u00a0 \u00ab PRO en remplacement de gratuit \u00bb<\/h2>\n<p>La strat\u00e9gie \u00ab PRO en remplacement de gratuit \u00bb demande de remplacer l&rsquo;extension gratuite par la version PRO. Par cons\u00e9quent, si l&rsquo;extension gratuite est distribu\u00e9e via le r\u00e9pertoire des extensions de WordPress, son nombre d&rsquo;<em>installations actives<\/em> diminuera (car il ne tient compte que de l&rsquo;extension gratuite, et non de la version PRO), ce qui donnera l&rsquo;impression que l&rsquo;extension est moins populaire qu&rsquo;elle ne l&rsquo;est en r\u00e9alit\u00e9.<\/p>\n<p>Ce r\u00e9sultat irait \u00e0 l&rsquo;encontre de l&rsquo;objectif premier de l&rsquo;utilisation de l&rsquo;annuaire des extensions WordPress : Il s&rsquo;agit d&rsquo;un canal de d\u00e9couverte d&rsquo;extensions o\u00f9 les utilisateurs peuvent d\u00e9couvrir notre extension, la t\u00e9l\u00e9charger et l&rsquo;installer. (Ensuite, une fois install\u00e9e, l&rsquo;extension gratuite peut inviter les utilisateurs \u00e0 passer \u00e0 la version PRO).<\/p>\n<p>Si le nombre d&rsquo;installations actives n&rsquo;est pas \u00e9lev\u00e9, les utilisateurs risquent de ne pas \u00eatre convaincus d&rsquo;installer notre extension. \u00c0 titre d&rsquo;exemple, les propri\u00e9taires de l&rsquo;extension Newsletter Glue ont d\u00e9cid\u00e9 de <a href=\"https:\/\/wptavern.com\/newsletter-glue-closes-free-plugin-on-wordpress-org\" target=\"_blank\" rel=\"noopener noreferrer\">retirer l&rsquo;extension du r\u00e9pertoire des extensions WordPress<\/a>, car le faible nombre d&rsquo;installations actives nuisait aux perspectives d&rsquo;avenir de l&rsquo;extension.<\/p>\n<p>Comme la strat\u00e9gie \u00ab PRO en remplacement de gratuit \u00bb n&rsquo;est pas viable, il ne nous reste qu&rsquo;un seul choix : la strat\u00e9gie \u00ab PRO en plus de gratuit \u00bb.<\/p>\n<p>Explorons les tenants et les aboutissants de cette strat\u00e9gie.<\/p>\n<h2>Conceptualisation de la strat\u00e9gie \u00ab PRO en plus du gratuit \u00bb<\/h2>\n<p>L&rsquo;id\u00e9e est que l&rsquo;extension gratuite est install\u00e9e sur le site et que ses fonctionnalit\u00e9s peuvent \u00eatre \u00e9tendues par l&rsquo;installation d&rsquo;extensions ou de modules suppl\u00e9mentaires. Il peut s&rsquo;agir d&rsquo;une seule extension PRO ou d&rsquo;une collection d&rsquo;extensions ou de modules PRO, chacun d&rsquo;entre eux offrant une fonctionnalit\u00e9 sp\u00e9cifique.<\/p>\n<p>Dans ce cas, l&rsquo;extension gratuite ne se soucie pas des autres extensions install\u00e9es sur le site. Tout ce qu&rsquo;elle fait, c&rsquo;est de fournir des fonctionnalit\u00e9s suppl\u00e9mentaires. Ce mod\u00e8le est polyvalent et permet aux d\u00e9veloppeurs d&rsquo;origine et aux cr\u00e9ateurs tiers de se d\u00e9velopper, ce qui favorise un \u00e9cosyst\u00e8me dans lequel une extension peut \u00e9voluer dans des directions impr\u00e9vues.<\/p>\n<p>Notez qu&rsquo;il importe peu que les extensions PRO soient produites par nous (c&rsquo;est-\u00e0-dire par les m\u00eames d\u00e9veloppeurs que l&rsquo;extension standard) ou par quelqu&rsquo;un d&rsquo;autre : le code pour traiter les deux est le m\u00eame. En tant que tel, c&rsquo;est une bonne id\u00e9e de cr\u00e9er une base qui ne restreint pas la fa\u00e7on dont l&rsquo;extension peut \u00eatre \u00e9tendue. Cela permettra aux d\u00e9veloppeurs tiers d&rsquo;\u00e9tendre notre extension d&rsquo;une mani\u00e8re que nous n&rsquo;avions pas imagin\u00e9e.<\/p>\n<h2>Approches de conception : crochets et conteneurs de services<\/h2>\n<p>Il existe deux approches principales pour rendre le code PHP extensible :<\/p>\n<ol>\n<li>Via les <a href=\"https:\/\/kinsta.com\/fr\/blog\/hooks-wordpress\/\">crochets d&rsquo;action et de filtre<\/a> de WordPress<\/li>\n<li>Via un <a href=\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-abstraction\/#accessing-services-via-the-service-container\">conteneur de service<\/a><\/li>\n<\/ol>\n<p>La premi\u00e8re approche est la plus courante parmi les d\u00e9veloppeurs WordPress, tandis que la seconde est pr\u00e9f\u00e9r\u00e9e par l&rsquo;ensemble de la <a href=\"https:\/\/kinsta.com\/php\/\">communaut\u00e9 PHP<\/a>.<\/p>\n<p>Voyons des exemples des deux.<\/p>\n<h2>Rendre le code extensible via des crochets d&rsquo;action et de filtre<\/h2>\n<p>WordPress propose des <a href=\"https:\/\/developer.wordpress.org\/plugins\/hooks\/\" target=\"_blank\" rel=\"noopener noreferrer\">crochets<\/a> (filtres et actions) comme m\u00e9canisme pour modifier le comportement. Les crochets de filtre sont utilis\u00e9s pour remplacer des valeurs, et les crochets d&rsquo;action pour ex\u00e9cuter des fonctionnalit\u00e9s personnalis\u00e9es.<\/p>\n<p>Notre extension principale peut alors \u00eatre \u00ab jonch\u00e9e \u00bb de crochets (ou hooks) \u00e0 travers sa base de code, permettant aux d\u00e9veloppeurs de modifier son comportement.<\/p>\n<p>Un bon exemple de ceci est <a href=\"https:\/\/kinsta.com\/fr\/blog\/tutoriel-woocommerce\/\">WooCommerce<\/a>, qui a couvert un \u00e9norme \u00e9cosyst\u00e8me de modules, avec la majorit\u00e9 d&rsquo;entre eux \u00e9tant d\u00e9tenus par des fournisseurs tiers. Cela est possible gr\u00e2ce au <a href=\"https:\/\/woocommerce.github.io\/code-reference\/hooks\/hooks.html\" target=\"_blank\" rel=\"noopener noreferrer\">grand nombre de crochets<\/a> propos\u00e9s par cette extension.<\/p>\n<p>Les d\u00e9veloppeurs de WooCommerce ont d\u00e9lib\u00e9r\u00e9ment ajout\u00e9 des crochets, m\u00eame s&rsquo;ils n&rsquo;en ont pas besoin eux-m\u00eames. C&rsquo;est \u00e0 quelqu&rsquo;un d&rsquo;autre de les utiliser. Remarquez le grand nombre de crochets d&rsquo;action \u00ab before \u00bb\u00a0 et \u00ab after \u00bb :<\/p>\n<ul>\n<li><code>woocommerce_after_account_downloads<\/code><\/li>\n<li><code>woocommerce_after_account_navigation<\/code><\/li>\n<li><code>woocommerce_after_account_orders<\/code><\/li>\n<li><code>woocommerce_after_account_payment_methods<\/code><\/li>\n<li><code>woocommerce_after_available_downloads<\/code><\/li>\n<li><code>woocommerce_after_cart<\/code><\/li>\n<li><code>woocommerce_after_cart_contents<\/code><\/li>\n<li><code>woocommerce_after_cart_item_name<\/code><\/li>\n<li><code>woocommerce_after_cart_table<\/code><\/li>\n<li><code>woocommerce_after_cart_totals<\/code><\/li>\n<li>&#8230;<\/li>\n<li><code>woocommerce_before_account_downloads<\/code><\/li>\n<li><code>woocommerce_before_account_navigation<\/code><\/li>\n<li><code>woocommerce_before_account_orders<\/code><\/li>\n<li><code>woocommerce_before_account_orders_pagination<\/code><\/li>\n<li><code>woocommerce_before_account_payment_methods<\/code><\/li>\n<li><code>woocommerce_before_available_downloads<\/code><\/li>\n<li><code>woocommerce_before_cart<\/code><\/li>\n<li><code>woocommerce_before_cart_collaterals<\/code><\/li>\n<li><code>woocommerce_before_cart_contents<\/code><\/li>\n<li><code>woocommerce_before_cart_table<\/code><\/li>\n<li><code>woocommerce_before_cart_totals<\/code><\/li>\n<li>&#8230;<\/li>\n<\/ul>\n<p>Par exemple, le fichier <a href=\"https:\/\/woocommerce.github.io\/code-reference\/files\/woocommerce-templates-myaccount-downloads.html#source-view.27\" target=\"_blank\" rel=\"noopener noreferrer\"><code>downloads.php<\/code><\/a> contient plusieurs actions pour injecter des fonctionnalit\u00e9s suppl\u00e9mentaires, et l&rsquo;URL de la boutique peut \u00eatre remplac\u00e9e par un filtre :<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\n$downloads     = WC()-&gt;customer-&gt;get_downloadable_products();\n$has_downloads = (bool) $downloads;\n\ndo_action( 'woocommerce_before_account_downloads', $has_downloads ); ?&gt;\n\n&lt;?php if ( $has_downloads ) : ?&gt;\n\n  &lt;?php do_action( 'woocommerce_before_available_downloads' ); ?&gt;\n\n  &lt;?php do_action( 'woocommerce_available_downloads', $downloads ); ?&gt;\n\n  &lt;?php do_action( 'woocommerce_after_available_downloads' ); ?&gt;\n\n&lt;?php else : ?&gt;\n\n  &lt;?php\n\n  $wp_button_class = wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '';\n  wc_print_notice( esc_html__( 'No downloads available yet.', 'woocommerce' ) . ' &lt;a class=\"button wc-forward' . esc_attr( $wp_button_class ) . '\" href=\"' . esc_url( apply_filters( 'woocommerce_return_to_shop_redirect', wc_get_page_permalink( 'shop' ) ) ) . '\"&gt;' . esc_html__( 'Browse products', 'woocommerce' ) . '&lt;\/a&gt;', 'notice' );\n  ?&gt;\n\n&lt;?php endif; ?&gt;\n\n&lt;?php do_action( 'woocommerce_after_account_downloads', $has_downloads ); ?&gt;\n<\/code><\/pre>\n<h2>Rendre le code extensible gr\u00e2ce aux conteneurs de services<\/h2>\n<p>Un conteneur de service est un objet PHP qui nous aide \u00e0 g\u00e9rer l&rsquo;instanciation de toutes les classes du projet, g\u00e9n\u00e9ralement propos\u00e9 dans le cadre d&rsquo;une biblioth\u00e8que \u00ab d&rsquo;injection de d\u00e9pendances \u00bb.<\/p>\n<p>L&rsquo;<a href=\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-abstraction\/#using-dependency-injection\">injection de d\u00e9pendances<\/a> est une strat\u00e9gie qui permet de coller toutes les parties de l&rsquo;application de mani\u00e8re d\u00e9centralis\u00e9e : Les classes PHP sont inject\u00e9es dans l&rsquo;application via la configuration, et l&rsquo;application r\u00e9cup\u00e8re les instances de ces classes PHP via le conteneur de services.<\/p>\n<p>Il existe de nombreuses biblioth\u00e8ques d&rsquo;injection de d\u00e9pendances. Les biblioth\u00e8ques suivantes sont populaires et interchangeables car elles satisfont toutes \u00e0 la <a href=\"https:\/\/www.php-fig.org\/psr\/psr-11\/\" target=\"_blank\" rel=\"noopener noreferrer\">PSR-11<\/a> (recommandation du standard PHP) qui d\u00e9crit les conteneurs d&rsquo;injection de d\u00e9pendances :<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/symfony\/dependency-injection\" target=\"_blank\" rel=\"noopener noreferrer\">DependencyInjection de Symfony<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/PHP-DI\/PHP-DI\" target=\"_blank\" rel=\"noopener noreferrer\">PHP-DI<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/silexphp\/Pimple\" target=\"_blank\" rel=\"noopener noreferrer\">Pimple<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/thephpleague\/container\" target=\"_blank\" rel=\"noopener noreferrer\">Container (Injection de d\u00e9pendances)<\/a><\/li>\n<\/ul>\n<p><a href=\"https:\/\/kinsta.com\/fr\/blog\/qu-est-ce-que-laravel\/\">Laravel<\/a> contient \u00e9galement un <a href=\"https:\/\/laravel.com\/docs\/10.x\/container\" target=\"_blank\" rel=\"noopener noreferrer\">conteneur de service<\/a> qui est d\u00e9j\u00e0 int\u00e9gr\u00e9 \u00e0 l&rsquo;application.<\/p>\n<p>En utilisant l&rsquo;injection de d\u00e9pendance, l&rsquo;extension gratuite n&rsquo;a pas besoin de savoir \u00e0 l&rsquo;avance quelles classes PHP sont pr\u00e9sentes au moment de l&rsquo;ex\u00e9cution : Elle demande simplement des instances de toutes les classes au conteneur de service. Alors que de nombreuses classes PHP sont fournies par l&rsquo;extension gratuite elle-m\u00eame pour satisfaire ses fonctionnalit\u00e9s, d&rsquo;autres sont fournies par les modules install\u00e9s sur le site pour \u00e9tendre les fonctionnalit\u00e9s.<\/p>\n<p>Un bon exemple d&rsquo;utilisation d&rsquo;un conteneur de service est <a href=\"https:\/\/wordpress.org\/plugins\/gatographql\/\" target=\"_blank\" rel=\"noopener noreferrer\">Gato GraphQL<\/a>, qui s&rsquo;appuie sur la biblioth\u00e8que DependencyInjection de Symfony.<\/p>\n<p>Voici <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/ce461a7155782823b9d14383e1036a75b564807c\/layers\/Engine\/packages\/root\/src\/Container\/ContainerBuilderFactoryTrait.php\" target=\"_blank\" rel=\"noopener noreferrer\">comment le conteneur de service est instanci\u00e9<\/a>:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace GatoGraphQL\\Container;\n\nuse Symfony\\Component\\Config\\ConfigCache;\nuse Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface;\nuse Symfony\\Component\\DependencyInjection\\Dumper\\PhpDumper;\n\ntrait ContainerBuilderFactoryTrait\n{\n  protected ContainerInterface $instance;\n  protected bool $cacheContainerConfiguration;\n  protected bool $cached;\n  protected string $cacheFile;\n\n  \/**\n   * Initialize the Container Builder.\n   * If the directory is not provided, store the\n   * cache in a system temp dir\n   *\/\n  public function init(\n    bool $cacheContainerConfiguration,\n    string $namespace,\n    string $directory\n  ): void {\n    $this-&gt;cacheContainerConfiguration = $cacheContainerConfiguration;\n\n    if ($this-&gt;cacheContainerConfiguration) {\n      if (!$directory) {\n        $directory = sys_get_temp_dir() . \\DIRECTORY_SEPARATOR . 'container-cache';\n      }\n      $directory .= \\DIRECTORY_SEPARATOR . $namespace;\n      if (!is_dir($directory)) {\n        @mkdir($directory, 0777, true);\n      }\n      \n      \/\/ Store the cache under this file\n      $this-&gt;cacheFile = $directory . 'container.php';\n\n      $containerConfigCache = new ConfigCache($this-&gt;cacheFile, false);\n      $this-&gt;cached = $containerConfigCache-&gt;isFresh();\n    } else {\n      $this-&gt;cached = false;\n    }\n\n    \/\/ If not cached, then create the new instance\n    if (!$this-&gt;cached) {\n      $this-&gt;instance = new ContainerBuilder();\n    } else {\n      require_once $this-&gt;cacheFile;\n      \/** @var class-string&lt;ContainerBuilder&gt; *\/\n      $containerFullyQuantifiedClass = \"\\\\GatoGraphQL\\\\ServiceContainer\";\n      $this-&gt;instance = new $containerFullyQuantifiedClass();\n    }\n  }\n\n  public function getInstance(): ContainerInterface\n  {\n    return $this-&gt;instance;\n  }\n\n  \/**\n   * If the container is not cached, then compile it and cache it\n   *\n   * @param CompilerPassInterface[] $compilerPasses Compiler Pass objects to register on the container\n   *\/\n  public function maybeCompileAndCacheContainer(\n    array $compilerPasses = []\n  ): void {\n    \/**\n     * Compile Symfony's DependencyInjection Container Builder.\n     *\n     * After compiling, cache it in disk for performance.\n     *\n     * This happens only the first time the site is accessed\n     * on the current server.\n     *\/\n    if ($this-&gt;cached) {\n      return;\n    }\n\n    \/** @var ContainerBuilder *\/\n    $containerBuilder = $this-&gt;getInstance();\n    foreach ($compilerPasses as $compilerPass) {\n      $containerBuilder-&gt;addCompilerPass($compilerPass);\n    }\n\n    \/\/ Compile the container.\n    $containerBuilder-&gt;compile();\n\n    \/\/ Cache the container\n    if (!$this-&gt;cacheContainerConfiguration) {\n      return;\n    }\n    \n    \/\/ Create the folder if it doesn't exist, and check it was successful\n    $dir = dirname($this-&gt;cacheFile);\n    $folderExists = file_exists($dir);\n    if (!$folderExists) {\n      $folderExists = @mkdir($dir, 0777, true);\n      if (!$folderExists) {\n        return;\n      }\n    }\n\n    \/\/ Save the container to disk\n    $dumper = new PhpDumper($containerBuilder);\n    file_put_contents(\n      $this-&gt;cacheFile,\n      $dumper-&gt;dump(\n        [\n          'class' =&gt; 'ServiceContainer',\n          'namespace' =&gt; 'GatoGraphQL',\n        ]\n      )\n    );\n\n    \/\/ Change the permissions so it can be modified by external processes\n    chmod($this-&gt;cacheFile, 0777);\n  }\n}\n<\/code><\/pre>\n<p>Veuillez noter que le conteneur de service (accessible sous l&rsquo;objet PHP avec la classe <code>GatoGraphQLServiceContainer<\/code>) est g\u00e9n\u00e9r\u00e9 la premi\u00e8re fois que l&rsquo;extension est ex\u00e9cut\u00e9e et ensuite mise en cache sur le disque (en tant que fichier <code>container.php<\/code> dans un dossier temporaire du syst\u00e8me). En effet, la g\u00e9n\u00e9ration du conteneur de service est un processus co\u00fbteux qui peut prendre plusieurs secondes.<\/p>\n<p>Ensuite, l&rsquo;extension principale et toutes ses extensions <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/fd73bf65cd30e164ffaa7860861974cf53683d51\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/config\/services.yaml\" target=\"_blank\" rel=\"noopener noreferrer\">d\u00e9finissent les services \u00e0 injecter dans le conteneur via un fichier de configuration<\/a>:<\/p>\n<pre><code class=\"language-yaml\">services:\n  _defaults:\n    public: true\n    autowire: true\n    autoconfigure: true\n\n  GatoGraphQL\\GatoGraphQL\\Registries\\ModuleTypeRegistryInterface:\n    class: \\GatoGraphQL\\GatoGraphQL\\Registries\\ModuleTypeRegistry\n\n  GatoGraphQL\\GatoGraphQL\\Log\\LoggerInterface:\n    class: \\GatoGraphQL\\GatoGraphQL\\Log\\Logger\n\n  GatoGraphQL\\GatoGraphQL\\Services\\:\n    resource: ..\/src\/Services\/*\n\n  GatoGraphQL\\GatoGraphQL\\State\\:\n    resource: '..\/src\/State\/*'\n\n<\/code><\/pre>\n<p>Remarquez que nous pouvons instancier des objets pour des classes sp\u00e9cifiques (comme <code>GatoGraphQL\\GatoGraphQL\\Log\\Logger<\/code>, accessible via son interface contractuelle <code>GatoGraphQL\\GatoGraphQL\\Log\\LoggerInterface<\/code>), et nous pouvons \u00e9galement indiquer \u00ab instancier toutes les classes sous un certain r\u00e9pertoire \u00bb (comme tous les services sous <code>..\/src\/Services<\/code>).<\/p>\n<p>Enfin, nous <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/409eb230267021735e3326657c9929e94fa08b85\/layers\/Engine\/packages\/root\/src\/Module\/InitializeContainerServicesInModuleTrait.php\" target=\"_blank\" rel=\"noopener noreferrer\">injectons la configuration dans le conteneur de services<\/a>:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace PoP\\Root\\Module;\n\nuse PoP\\Root\\App;\nuse Symfony\\Component\\Config\\FileLocator;\nuse Symfony\\Component\\DependencyInjection\\ContainerBuilder;\nuse Symfony\\Component\\DependencyInjection\\Loader\\YamlFileLoader;\n\ntrait InitializeContainerServicesInModuleTrait\n{\n  \/\/ Initialize the services defined in the YAML configuration file.\n  public function initServices(\n    string $dir,\n    string $serviceContainerConfigFileName\n  ): void {\n    \/\/ First check if the container has been cached. If so, do nothing\n    if (App::getContainerBuilderFactory()-&gt;isCached()) {\n      return;\n    }\n\n    \/\/ Initialize the ContainerBuilder with this module's service implementations\n    \/** @var ContainerBuilder *\/\n    $containerBuilder = App::getContainer();\n    $loader = new YamlFileLoader($containerBuilder, new FileLocator($dir));\n    $loader-&gt;load($serviceContainerConfigFileName);\n  }\n}\n<\/code><\/pre>\n<p>Les services inject\u00e9s dans le conteneur peuvent \u00eatre configur\u00e9s pour \u00eatre initialis\u00e9s en permanence ou uniquement sur demande (mode diff\u00e9r\u00e9).<\/p>\n<p>Par exemple, pour repr\u00e9senter un type de publication personnalis\u00e9, l&rsquo;extension dispose de la classe <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/04058ffae6700e3e34e529ebd709247d98106fed\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/Services\/CustomPostTypes\/AbstractCustomPostType.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AbstractCustomPostType<\/code><\/a>dont la m\u00e9thode <code>initialize<\/code> ex\u00e9cute la logique pour l&rsquo;initialiser selon WordPress :<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace GatoGraphQL\\GatoGraphQL\\Services\\CustomPostTypes;\n\nuse GatoGraphQL\\GatoGraphQL\\Services\\Taxonomies\\TaxonomyInterface;\nuse PoP\\Root\\Services\\AbstractAutomaticallyInstantiatedService;\n\nabstract class AbstractCustomPostType extends AbstractAutomaticallyInstantiatedService implements CustomPostTypeInterface\n{\n  public function initialize(): void\n  {\n    \\add_action(\n      'init',\n      $this-&gt;initCustomPostType(...)\n    );\n  }\n\n  \/**\n   * Register the post type\n   *\/\n  public function initCustomPostType(): void\n  {\n    \\register_post_type($this-&gt;getCustomPostType(), $this-&gt;getCustomPostTypeArgs());\n  }\n\n  abstract public function getCustomPostType(): string;\n\n  \/**\n   * Arguments for registering the post type\n   *\n   * @return array&lt;string,mixed&gt;\n   *\/\n  protected function getCustomPostTypeArgs(): array\n  {\n    \/** @var array&lt;string,mixed&gt; *\/\n    $postTypeArgs = [\n      'public' =&gt; $this-&gt;isPublic(),\n      'publicly_queryable' =&gt; $this-&gt;isPubliclyQueryable(),\n      'label' =&gt; $this-&gt;getCustomPostTypeName(),\n      'labels' =&gt; $this-&gt;getCustomPostTypeLabels($this-&gt;getCustomPostTypeName(), $this-&gt;getCustomPostTypePluralNames(true), $this-&gt;getCustomPostTypePluralNames(false)),\n      'capability_type' =&gt; 'post',\n      'hierarchical' =&gt; $this-&gt;isAPIHierarchyModuleEnabled() && $this-&gt;isHierarchical(),\n      'exclude_from_search' =&gt; true,\n      'show_in_admin_bar' =&gt; $this-&gt;showInAdminBar(),\n      'show_in_nav_menus' =&gt; true,\n      'show_ui' =&gt; true,\n      'show_in_menu' =&gt; true,\n      'show_in_rest' =&gt; true,\n    ];\n    return $postTypeArgs;\n  }\n\n  \/**\n   * Labels for registering the post type\n   *\n   * @param string $name_uc Singular name uppercase\n   * @param string $names_uc Plural name uppercase\n   * @param string $names_lc Plural name lowercase\n   * @return array&lt;string,string&gt;\n   *\/\n  protected function getCustomPostTypeLabels(string $name_uc, string $names_uc, string $names_lc): array\n  {\n    return array(\n      'name'         =&gt; $names_uc,\n      'singular_name'    =&gt; $name_uc,\n      'add_new'      =&gt; sprintf(\\__('Add New %s', 'gatographql'), $name_uc),\n      'add_new_item'     =&gt; sprintf(\\__('Add New %s', 'gatographql'), $name_uc),\n      'edit_item'      =&gt; sprintf(\\__('Edit %s', 'gatographql'), $name_uc),\n      'new_item'       =&gt; sprintf(\\__('New %s', 'gatographql'), $name_uc),\n      'all_items'      =&gt; $names_uc,\/\/sprintf(\\__('All %s', 'gatographql'), $names_uc),\n      'view_item'      =&gt; sprintf(\\__('View %s', 'gatographql'), $name_uc),\n      'search_items'     =&gt; sprintf(\\__('Search %s', 'gatographql'), $names_uc),\n      'not_found'      =&gt; sprintf(\\__('No %s found', 'gatographql'), $names_lc),\n      'not_found_in_trash' =&gt; sprintf(\\__('No %s found in Trash', 'gatographql'), $names_lc),\n      'parent_item_colon'  =&gt; sprintf(\\__('Parent %s:', 'gatographql'), $name_uc),\n    );\n  }\n}\n<\/code><\/pre>\n<p>Ainsi, la classe <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/c870d8906ae1aec3c81acc039c53acc7aab5dff0\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/Services\/CustomPostTypes\/GraphQLCustomEndpointCustomPostType.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>GraphQLCustomEndpointCustomPostType.php<\/code><\/a> est une impl\u00e9mentation d&rsquo;un type de publication personnalis\u00e9. Une fois inject\u00e9e dans le conteneur en tant que service, elle est instanci\u00e9e et enregistr\u00e9e dans WordPress :<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace GatoGraphQL\\GatoGraphQL\\Services\\CustomPostTypes;\n\nclass GraphQLCustomEndpointCustomPostType extends AbstractCustomPostType\n{\n  public function getCustomPostType(): string\n  {\n    return 'graphql-endpoint';\n  }\n\n  protected function getCustomPostTypeName(): string\n  {\n    return \\__('GraphQL custom endpoint', 'gatographql');\n  }\n}\n<\/code><\/pre>\n<p>Cette classe est pr\u00e9sente dans l&rsquo;extension gratuite et d&rsquo;autres classes de post-types personnalis\u00e9s, s&rsquo;\u00e9tendant de la m\u00eame mani\u00e8re \u00e0 partir de <code>AbstractCustomPostType<\/code>, sont fournies par les extensions PRO.<\/p>\n<h2>Comparaison des crochets et des conteneurs de services<\/h2>\n<p>Comparons les deux approches de conception.<\/p>\n<p>Les crochets d&rsquo;action et de filtre ont l&rsquo;avantage d&rsquo;\u00eatre la m\u00e9thode la plus simple, leur fonctionnalit\u00e9 faisant partie du noyau de WordPress. Et tout d\u00e9veloppeur travaillant avec WordPress sait d\u00e9j\u00e0 comment manipuler les crochets, la courbe d&rsquo;apprentissage est donc faible.<\/p>\n<p>Cependant, sa logique est attach\u00e9e \u00e0 un nom de crochet, qui est une cha\u00eene de caract\u00e8res, et, en tant que tel, peut conduire \u00e0 des bogues : Si le nom du crochet est modifi\u00e9, la logique de l&rsquo;extension est rompue. Cependant, le d\u00e9veloppeur peut ne pas remarquer qu&rsquo;il y a un probl\u00e8me parce que le code PHP se compile toujours.<\/p>\n<p>Par cons\u00e9quent, les crochets d\u00e9pr\u00e9ci\u00e9s ont tendance \u00e0 rester tr\u00e8s longtemps dans la base de code, voire \u00e0 jamais. Le projet accumule alors du code p\u00e9rim\u00e9 qui ne peut \u00eatre supprim\u00e9 de peur de casser les extensions.<\/p>\n<p>Pour en revenir \u00e0 WooCommerce, cette situation est mise en \u00e9vidence dans le fichier <a href=\"https:\/\/woocommerce.github.io\/code-reference\/files\/woocommerce-templates-myaccount-dashboard.html#source-view.65\" target=\"_blank\" rel=\"noopener noreferrer\"><code>dashboard.php<\/code><\/a> (remarquez comment les crochets d\u00e9pr\u00e9ci\u00e9s sont conserv\u00e9s depuis la version <code>2.6<\/code>, alors que la derni\u00e8re version actuelle est <code>8.5<\/code>) :<\/p>\n<pre><code class=\"language-php\">&lt;?php\n  \/**\n   * My Account dashboard.\n   *\n   * @since 2.6.0\n   *\/\n  do_action( 'woocommerce_account_dashboard' );\n\n  \/**\n   * Deprecated woocommerce_before_my_account action.\n   *\n   * @deprecated 2.6.0\n   *\/\n  do_action( 'woocommerce_before_my_account' );\n\n  \/**\n   * Deprecated woocommerce_after_my_account action.\n   *\n   * @deprecated 2.6.0\n   *\/\n  do_action( 'woocommerce_after_my_account' );\n<\/code><\/pre>\n<p>L&rsquo;utilisation d&rsquo;un conteneur de service a l&rsquo;inconv\u00e9nient de n\u00e9cessiter une biblioth\u00e8que externe, ce qui ajoute encore \u00e0 la complexit\u00e9. De plus, <a href=\"https:\/\/gatographql.com\/blog\/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper\/#heading-php-scoper-the-easy-way-%F0%9F%98%8E\" target=\"_blank\" rel=\"noopener noreferrer\">cette biblioth\u00e8que doit \u00eatre scop\u00e9e<\/a> (en utilisant <a href=\"https:\/\/github.com\/humbug\/php-scoper\" target=\"_blank\" rel=\"noopener noreferrer\">PHP-Scoper<\/a> ou <a href=\"https:\/\/github.com\/BrianHenryIE\/strauss\" target=\"_blank\" rel=\"noopener noreferrer\">Strauss<\/a>) de peur qu&rsquo;une version diff\u00e9rente de la m\u00eame biblioth\u00e8que soit install\u00e9e par une autre extension sur le m\u00eame site, ce qui pourrait produire des conflits.<\/p>\n<p>L&rsquo;utilisation d&rsquo;un conteneur de service est sans doute plus difficile \u00e0 mettre en \u0153uvre et prend plus de temps de d\u00e9veloppement.<\/p>\n<p>D&rsquo;un autre c\u00f4t\u00e9, les conteneurs de services traitent les classes PHP sans avoir \u00e0 coupler la logique \u00e0 une cha\u00eene de caract\u00e8res. Il en r\u00e9sulte que le projet utilise davantage de <a href=\"https:\/\/phptherightway.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">bonnes pratiques PHP<\/a>, ce qui conduit \u00e0 une base de code plus facile \u00e0 maintenir \u00e0 long terme.<\/p>\n<h2>R\u00e9sum\u00e9<\/h2>\n<p>Lorsque vous cr\u00e9ez une extension pour WordPress, c&rsquo;est une bonne id\u00e9e qu&rsquo;elle supporte des extensions pour nous permettre \u00e0 nous (les cr\u00e9ateurs de l&rsquo;extension) d&rsquo;offrir des fonctionnalit\u00e9s commerciales et aussi \u00e0 n&rsquo;importe qui d&rsquo;autre d&rsquo;ajouter des fonctionnalit\u00e9s suppl\u00e9mentaires et, avec un peu de chance, de cr\u00e9er un \u00e9cosyst\u00e8me centr\u00e9 sur l&rsquo;extension.<\/p>\n<p>Dans cet article, nous avons explor\u00e9 les consid\u00e9rations concernant l&rsquo;architecture du projet PHP pour rendre l&rsquo;extension extensible. Comme nous l&rsquo;avons appris, nous pouvons choisir entre deux approches de conception : utiliser des crochets ou utiliser un conteneur de service. Nous avons compar\u00e9 les deux approches, en identifiant les vertus et les faiblesses de chacune.<\/p>\n<p><em>Avez-vous l&rsquo;intention de rendre votre extension WordPress extensible ? Faites-le nous savoir dans la section des commentaires.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dans l&rsquo;\u00e9cosyst\u00e8me WordPress, l&rsquo;adoption d&rsquo;un mod\u00e8le freemium est une m\u00e9thode courante de promotion et de mon\u00e9tisation d&rsquo;extensions commercialles.\u00a0 Cette approche consiste \u00e0 publier une version basique &#8230;<\/p>\n","protected":false},"author":196,"featured_media":76417,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[],"topic":[1028,1035],"class_list":["post-76416","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","topic-developpement-wordpress","topic-extensions-wordpress"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v24.6 (Yoast SEO v24.6) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Construire un plugin WordPress pour supporter les extensions - Kinsta\u00ae<\/title>\n<meta name=\"description\" content=\"Apprenez \u00e0 cr\u00e9er un plugin flexible et \u00e9volutif qui supporte les extensions, am\u00e9liore les fonctionnalit\u00e9s et maximise la compatibilit\u00e9.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Construire un plugin WordPress pour supporter les extensions\" \/>\n<meta property=\"og:description\" content=\"Apprenez \u00e0 cr\u00e9er un plugin flexible et \u00e9volutif qui supporte les extensions, am\u00e9liore les fonctionnalit\u00e9s et maximise la compatibilit\u00e9.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/kinstafrance\/\" \/>\n<meta property=\"article:published_time\" content=\"2024-03-04T13:08:19+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-03-12T17:30:10+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1460\" \/>\n\t<meta property=\"og:image:height\" content=\"730\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Leonardo Losoviz\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:description\" content=\"Apprenez \u00e0 cr\u00e9er un plugin flexible et \u00e9volutif qui supporte les extensions, am\u00e9liore les fonctionnalit\u00e9s et maximise la compatibilit\u00e9.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\" \/>\n<meta name=\"twitter:creator\" content=\"@losoviz\" \/>\n<meta name=\"twitter:site\" content=\"@kinsta_fr\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leonardo Losoviz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"Construire un plugin WordPress pour supporter les extensions\",\"datePublished\":\"2024-03-04T13:08:19+00:00\",\"dateModified\":\"2024-03-12T17:30:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/\"},\"wordCount\":2264,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/\",\"url\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/\",\"name\":\"Construire un plugin WordPress pour supporter les extensions - Kinsta\u00ae\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"datePublished\":\"2024-03-04T13:08:19+00:00\",\"dateModified\":\"2024-03-12T17:30:10+00:00\",\"description\":\"Apprenez \u00e0 cr\u00e9er un plugin flexible et \u00e9volutif qui supporte les extensions, am\u00e9liore les fonctionnalit\u00e9s et maximise la compatibilit\u00e9.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"contentUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"width\":1460,\"height\":730},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/fr\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Extensions WordPress\",\"item\":\"https:\/\/kinsta.com\/fr\/sujets\/extensions-wordpress\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Construire un plugin WordPress pour supporter les extensions\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/fr\/#website\",\"url\":\"https:\/\/kinsta.com\/fr\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Solutions d&#039;h\u00e9bergement premium, rapides et s\u00e9curis\u00e9es\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/fr\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/fr\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/fr\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/kinstafrance\/\",\"https:\/\/x.com\/kinsta_fr\",\"https:\/\/www.instagram.com\/kinstahosting\/\",\"https:\/\/www.linkedin.com\/company\/kinsta\/\",\"https:\/\/www.pinterest.com\/kinstahosting\/\",\"https:\/\/www.youtube.com\/c\/Kinsta\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g\",\"caption\":\"Leonardo Losoviz\"},\"description\":\"Leo writes about innovative web development trends, mostly concerning PHP, WordPress and GraphQL. You can find him at leoloso.com and twitter.com\/losoviz.\",\"sameAs\":[\"https:\/\/leoloso.com\",\"https:\/\/x.com\/losoviz\",\"https:\/\/www.youtube.com\/@GatoGraphQL\"],\"url\":\"https:\/\/kinsta.com\/fr\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Construire un plugin WordPress pour supporter les extensions - Kinsta\u00ae","description":"Apprenez \u00e0 cr\u00e9er un plugin flexible et \u00e9volutif qui supporte les extensions, am\u00e9liore les fonctionnalit\u00e9s et maximise la compatibilit\u00e9.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/","og_locale":"fr_FR","og_type":"article","og_title":"Construire un plugin WordPress pour supporter les extensions","og_description":"Apprenez \u00e0 cr\u00e9er un plugin flexible et \u00e9volutif qui supporte les extensions, am\u00e9liore les fonctionnalit\u00e9s et maximise la compatibilit\u00e9.","og_url":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinstafrance\/","article_published_time":"2024-03-04T13:08:19+00:00","article_modified_time":"2024-03-12T17:30:10+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"Apprenez \u00e0 cr\u00e9er un plugin flexible et \u00e9volutif qui supporte les extensions, am\u00e9liore les fonctionnalit\u00e9s et maximise la compatibilit\u00e9.","twitter_image":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","twitter_creator":"@losoviz","twitter_site":"@kinsta_fr","twitter_misc":{"\u00c9crit par":"Leonardo Losoviz","Dur\u00e9e de lecture estim\u00e9e":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"Construire un plugin WordPress pour supporter les extensions","datePublished":"2024-03-04T13:08:19+00:00","dateModified":"2024-03-12T17:30:10+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/"},"wordCount":2264,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/fr\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","inLanguage":"fr-FR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/","url":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/","name":"Construire un plugin WordPress pour supporter les extensions - Kinsta\u00ae","isPartOf":{"@id":"https:\/\/kinsta.com\/fr\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","datePublished":"2024-03-04T13:08:19+00:00","dateModified":"2024-03-12T17:30:10+00:00","description":"Apprenez \u00e0 cr\u00e9er un plugin flexible et \u00e9volutif qui supporte les extensions, am\u00e9liore les fonctionnalit\u00e9s et maximise la compatibilit\u00e9.","breadcrumb":{"@id":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#primaryimage","url":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","contentUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","width":1460,"height":730},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/fr\/blog\/plugin-wordpress-extensions-pro-versions-gratuites\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/fr\/"},{"@type":"ListItem","position":2,"name":"Extensions WordPress","item":"https:\/\/kinsta.com\/fr\/sujets\/extensions-wordpress\/"},{"@type":"ListItem","position":3,"name":"Construire un plugin WordPress pour supporter les extensions"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/fr\/#website","url":"https:\/\/kinsta.com\/fr\/","name":"Kinsta\u00ae","description":"Solutions d&#039;h\u00e9bergement premium, rapides et s\u00e9curis\u00e9es","publisher":{"@id":"https:\/\/kinsta.com\/fr\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/fr\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/fr\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/fr\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/kinstafrance\/","https:\/\/x.com\/kinsta_fr","https:\/\/www.instagram.com\/kinstahosting\/","https:\/\/www.linkedin.com\/company\/kinsta\/","https:\/\/www.pinterest.com\/kinstahosting\/","https:\/\/www.youtube.com\/c\/Kinsta"]},{"@type":"Person","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g","caption":"Leonardo Losoviz"},"description":"Leo writes about innovative web development trends, mostly concerning PHP, WordPress and GraphQL. You can find him at leoloso.com and twitter.com\/losoviz.","sameAs":["https:\/\/leoloso.com","https:\/\/x.com\/losoviz","https:\/\/www.youtube.com\/@GatoGraphQL"],"url":"https:\/\/kinsta.com\/fr\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts\/76416","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/comments?post=76416"}],"version-history":[{"count":6,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts\/76416\/revisions"}],"predecessor-version":[{"id":76509,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts\/76416\/revisions\/76509"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/76416\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/76416\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/76416\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/76416\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/76416\/translations\/de"},{"embeddable":true,"hreflang":"ja","title":"Japanese","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/76416\/translations\/jp"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/76416\/translations\/es"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/76416\/translations\/nl"},{"href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/76416\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/media\/76417"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/media?parent=76416"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/tags?post=76416"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/topic?post=76416"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}