{"id":67419,"date":"2024-03-04T10:18:57","date_gmt":"2024-03-04T13:18:57","guid":{"rendered":"https:\/\/kinsta.com\/pt\/?p=67419&#038;preview=true&#038;preview_id=67419"},"modified":"2024-03-08T09:59:06","modified_gmt":"2024-03-08T12:59:06","slug":"plugin-wordpress-suporte-extensoes","status":"publish","type":"post","link":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/","title":{"rendered":"Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es"},"content":{"rendered":"<p>No ecossistema do WordPress, adotar um <a href=\"https:\/\/kinsta.com\/pt\/blog\/como-os-aplicativos-geram-receita\/\">modelo freemium<\/a> \u00e9 um m\u00e9todo comum para promover e monetizar plugins comerciais. Essa abordagem envolve lan\u00e7ar uma vers\u00e3o b\u00e1sica do plugin gratuitamente, geralmente atrav\u00e9s do <a href=\"https:\/\/wordpress.org\/plugins\/\" target=\"_blank\" rel=\"noopener noreferrer\">diret\u00f3rio de plugins do WordPress<\/a>, e oferecer funcionalidades avan\u00e7adas por meio de uma vers\u00e3o PRO ou complementos, normalmente vendidos no site do plugin.<\/p>\n<p>H\u00e1 tr\u00eas maneiras diferentes de integrar funcionalidades comerciais em um modelo freemium:<\/p>\n<ol>\n<li>Incluir funcionalidades comerciais no plugin gratuito, ativando-as apenas quando a vers\u00e3o comercial estiver instalada no site ou mediante o fornecimento de uma chave de licen\u00e7a comercial.<\/li>\n<li>Criar vers\u00f5es gratuita e PRO como plugins independentes, com a vers\u00e3o PRO projetada para substituir a vers\u00e3o gratuita, garantindo que apenas uma vers\u00e3o seja instalada em um determinado momento.<\/li>\n<li>Instalar a vers\u00e3o PRO com o plugin gratuito, ampliando sua funcionalidade. Para isso, \u00e9 necess\u00e1rio que ambas as vers\u00f5es estejam presentes.<\/li>\n<\/ol>\n<p>No entanto, a primeira abordagem \u00e9 <a href=\"https:\/\/developer.wordpress.org\/plugins\/wordpress-org\/detailed-plugin-guidelines\/#5-trialware-is-not-permitted\" target=\"_blank\" rel=\"noopener noreferrer\">incompat\u00edvel com as diretrizes<\/a> para plugins distribu\u00eddos por meio do diret\u00f3rio de plugins do WordPress, pois essas regras pro\u00edbem a inclus\u00e3o de funcionalidades que s\u00e3o restritos ou bloqueados at\u00e9 que um pagamento ou atualiza\u00e7\u00e3o seja feito.<\/p>\n<p>Isso nos leva \u00e0s duas \u00faltimas op\u00e7\u00f5es dispon\u00edveis, cada uma apresentando seus pr\u00f3s e contras. Nas se\u00e7\u00f5es seguintes, detalharemos os motivos pelos quais a \u00faltima estrat\u00e9gia, denominada &#8220;Vers\u00e3o PRO sobre a Gratuita&#8221;, representa a nossa escolha ideal.<\/p>\n<p>Vamos detalhar a segunda op\u00e7\u00e3o, &#8220;PRO como substituto do gratuito&#8221;, e discutir seus pontos fracos, esclarecendo o motivo pelo qual n\u00e3o a recomendamos.<\/p>\n<p>Em seguida, daremos uma aten\u00e7\u00e3o especial ao &#8220;PRO em adi\u00e7\u00e3o ao gratuito&#8221;, real\u00e7ando os motivos que o tornam a escolha mais vantajosa.<\/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>Vantagens da estrat\u00e9gia &#8220;PRO como substituto do gratuito&#8221;<\/h2>\n<p>A estrat\u00e9gia &#8220;PRO como substituto do gratuito&#8221; \u00e9 relativamente f\u00e1cil de implementar porque os desenvolvedores podem usar uma \u00fanica base de c\u00f3digo para ambos os plugins (gratuito e PRO) e criar dois resultados a partir dela, com a vers\u00e3o gratuita (ou &#8220;padr\u00e3o&#8221;) simplesmente incluindo um subconjunto do c\u00f3digo e a vers\u00e3o PRO incluindo todo o c\u00f3digo.<\/p>\n<p>Por exemplo, a base de c\u00f3digo do projeto poderia ser dividida nos diret\u00f3rios <code>standard\/<\/code> e <code>pro\/<\/code>. O plugin sempre carregaria o c\u00f3digo padr\u00e3o, com o c\u00f3digo PRO sendo carregado condicionalmente, com base na presen\u00e7a do respectivo diret\u00f3rio:<\/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>Ent\u00e3o, ao gerar o plugin por meio de uma ferramenta de <a href=\"https:\/\/kinsta.com\/pt\/blog\/como-configurar-pipeline-ci-cd\/\">integra\u00e7\u00e3o cont\u00ednua<\/a>, podemos criar os dois ativos <code>myplugin-standard.zip<\/code> e <code>myplugin-pro.zip<\/code> a partir do mesmo c\u00f3digo-fonte.<\/p>\n<p>Se voc\u00ea hospedar o projeto no GitHub e gerar os ativos por meio do GitHub Actions, o fluxo de trabalho a seguir far\u00e1 o trabalho:<\/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>Problemas com a estrat\u00e9gia &#8220;PRO como substituto do gratuito&#8221;<\/h2>\n<p>A estrat\u00e9gia &#8220;PRO como substituto do gratuito&#8221; exige a substitui\u00e7\u00e3o do plugin gratuito pela vers\u00e3o PRO. Como consequ\u00eancia, se o plugin gratuito for distribu\u00eddo por meio do diret\u00f3rio de plugins de WordPress, sua contagem de &#8220;instala\u00e7\u00f5es ativas&#8221; diminuir\u00e1 (j\u00e1 que ele rastreia apenas o plugin gratuito, n\u00e3o a vers\u00e3o PRO), dando a impress\u00e3o de que o plugin \u00e9 menos popular do que realmente \u00e9.<\/p>\n<p>Esse resultado anularia o objetivo de usar o diret\u00f3rio de plugins de WordPress em primeiro lugar: Como um canal de descoberta de plugins em que os usu\u00e1rios podem saber mais sobre nosso plugin, baix\u00e1-lo e instal\u00e1-lo. (Depois disso, uma vez instalado, o plugin gratuito pode convidar os usu\u00e1rios a fazer upgrade para a vers\u00e3o PRO).<\/p>\n<p>Se o n\u00famero de instala\u00e7\u00f5es ativas n\u00e3o for expressivo, pode ser dif\u00edcil convencer os usu\u00e1rios a instalar nosso plugin. Um exemplo do que pode dar errado \u00e9 o caso dos desenvolvedores do plugin Newsletter Glue, que optaram por <a href=\"https:\/\/wptavern.com\/newsletter-glue-closes-free-plugin-on-wordpress-org\">retir\u00e1-lo do diret\u00f3rio de plugins do WordPress<\/a>. A decis\u00e3o foi motivada pelo baixo n\u00famero de ativa\u00e7\u00f5es, o que estava comprometendo o potencial de sucesso do plugin.<\/p>\n<p>Como a estrat\u00e9gia &#8220;PRO como substituto do gratuito&#8221; n\u00e3o \u00e9 vi\u00e1vel, isso nos deixa com apenas uma op\u00e7\u00e3o: a estrat\u00e9gia &#8220;PRO baseado no do gratuito&#8221;.<\/p>\n<p>Vamos explorar os pr\u00f3s e contras dessa estrat\u00e9gia.<\/p>\n<h2>Conceituando a estrat\u00e9gia &#8220;PRO em adi\u00e7\u00e3o ao gratuito&#8221;<\/h2>\n<p>A ideia \u00e9 que o plugin gratuito seja instalado no site e sua funcionalidade possa ser ampliada com a instala\u00e7\u00e3o de plugins ou complementos adicionais. Isso pode ser feito por meio de um \u00fanico plugin PRO ou por meio de uma cole\u00e7\u00e3o de extens\u00f5es, ou complementos PRO, com cada um deles fornecendo uma funcionalidade espec\u00edfica.<\/p>\n<p>Nessa situa\u00e7\u00e3o, o plugin gratuito n\u00e3o se importa com os outros plugins que est\u00e3o instalados no site. Tudo o que ele faz \u00e9 fornecer funcionalidade adicional. Esse modelo \u00e9 vers\u00e1til, permitindo a expans\u00e3o tanto pelos desenvolvedores originais quanto por criadores de terceiros, promovendo um ecossistema em que um plugin pode evoluir em dire\u00e7\u00f5es imprevistas.<\/p>\n<p>Note que \u00e9 irrelevante se as vers\u00f5es PRO ser\u00e3o desenvolvidas por n\u00f3s (os mesmos desenvolvedores do plugin padr\u00e3o) ou por terceiros: o c\u00f3digo necess\u00e1rio para gerenciar ambos \u00e9 o mesmo. Portanto, \u00e9 prudente estabelecer uma base que n\u00e3o limite as maneiras como o plugin pode ser ampliado. Isso possibilitar\u00e1 que os desenvolvedores de terceiros ampliem nosso plugin de maneiras que n\u00e3o hav\u00edamos imaginado.<\/p>\n<h2>Abordagens de design: Hooks e cont\u00eaineres de servi\u00e7o<\/h2>\n<p>H\u00e1 duas abordagens principais para tornar o c\u00f3digo PHP extens\u00edvel:<\/p>\n<ol>\n<li>Por meio da <a href=\"https:\/\/kinsta.com\/pt\/blog\/hooks-wordpress\/\">a\u00e7\u00e3o do WordPress e dos hooks de filtro<\/a><\/li>\n<li>Por meio de um <a href=\"https:\/\/kinsta.com\/pt\/blog\/abstracao-wordpress\/#accessing-services-via-the-service-container\">cont\u00eainer de servi\u00e7o<\/a><\/li>\n<\/ol>\n<p>A primeira abordagem \u00e9 a mais comum entre os desenvolvedores WordPress, enquanto a segunda \u00e9 a preferida pela <a href=\"https:\/\/kinsta.com\/php\/\">comunidade PHP<\/a> mais ampla.<\/p>\n<p>Vamos ver exemplos de ambas.<\/p>\n<h2>Tornando o c\u00f3digo extens\u00edvel por meio de hooks de a\u00e7\u00e3o e filtro<\/h2>\n<p>O WordPress oferece <a href=\"https:\/\/developer.wordpress.org\/plugins\/hooks\/\" target=\"_blank\" rel=\"noopener noreferrer\">hooks<\/a> (filtros e a\u00e7\u00f5es) como um mecanismo para modificar o comportamento. Os hooks de filtro s\u00e3o usados para substituir valores e os hooks de a\u00e7\u00e3o para executar funcionalidades personalizadas.<\/p>\n<p>Nosso plugin principal pode ent\u00e3o ser &#8220;cheio&#8221; de hooks em toda a sua base de c\u00f3digo, permitindo que os desenvolvedores modifiquem seu comportamento.<\/p>\n<p>Um bom exemplo disso \u00e9 o <a href=\"https:\/\/kinsta.com\/pt\/blog\/tutorial-woocommerce\/\">WooCommerce<\/a>, que abrangeu um enorme ecossistema de complementos, sendo que a maioria deles pertence a fornecedores terceirizados. Isso \u00e9 poss\u00edvel gra\u00e7as ao <a href=\"https:\/\/woocommerce.github.io\/code-reference\/hooks\/hooks.html\" target=\"_blank\" rel=\"noopener noreferrer\">grande n\u00famero de hooks<\/a> oferecidos por esse plugin.<\/p>\n<p>Os desenvolvedores do WooCommerce adicionaram hooks propositalmente, mesmo que eles pr\u00f3prios n\u00e3o precisem deles. \u00c9 para que outra pessoa os utilize. Observe o grande n\u00famero de hooks de a\u00e7\u00e3o &#8220;antes&#8221; e &#8220;depois&#8221;:<\/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>Como exemplo, o arquivo <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> cont\u00e9m v\u00e1rias a\u00e7\u00f5es para injetar funcionalidade extra, e a URL da loja pode ser substitu\u00eddo por um filtro:<\/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>Tornando o c\u00f3digo extens\u00edvel por meio de cont\u00eaineres de servi\u00e7o<\/h2>\n<p>Um cont\u00eainer de servi\u00e7o \u00e9 um objeto PHP que nos ajuda a gerenciar a instancia\u00e7\u00e3o de todas as classes do projeto, geralmente oferecido como parte de uma biblioteca de &#8220;inje\u00e7\u00e3o de depend\u00eancia&#8221;.<\/p>\n<p>A <a href=\"https:\/\/kinsta.com\/pt\/blog\/abstracao-wordpress\/#using-dependency-injection\">inje\u00e7\u00e3o<\/a> de depend\u00eancia \u00e9 uma estrat\u00e9gia que permite unir todas as partes do aplicativo de forma descentralizada: As classes PHP s\u00e3o injetadas no aplicativo por meio da configura\u00e7\u00e3o, e o aplicativo recupera inst\u00e2ncias dessas classes PHP por meio do cont\u00eainer de servi\u00e7o.<\/p>\n<p>Existem v\u00e1rias bibliotecas de inje\u00e7\u00e3o de depend\u00eancia dispon\u00edveis. As seguintes se destacam por sua popularidade e intercambiabilidade, uma vez que todas atendem \u00e0 <a href=\"https:\/\/www.php-fig.org\/psr\/psr-11\/\">PSR-11<\/a> (PHP Standard Recommendation), que define os requisitos para cont\u00eaineres de inje\u00e7\u00e3o de depend\u00eancia:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/symfony\/dependency-injection\" target=\"_blank\" rel=\"noopener noreferrer\">DependencyInjection do 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\">Cont\u00eainer (inje\u00e7\u00e3o de depend\u00eancia)<\/a><\/li>\n<\/ul>\n<p>O <a href=\"https:\/\/kinsta.com\/pt\/blog\/o-que-e-laravel\/\">Laravel<\/a> tamb\u00e9m cont\u00e9m um <a href=\"https:\/\/laravel.com\/docs\/10.x\/container\" target=\"_blank\" rel=\"noopener noreferrer\">cont\u00eainer de servi\u00e7o<\/a> j\u00e1 integrado ao aplicativo.<\/p>\n<p>Usando a inje\u00e7\u00e3o de depend\u00eancia, o plugin gratuito n\u00e3o precisa saber antecipadamente quais classes PHP est\u00e3o presentes no runtime: Ele simplesmente solicita inst\u00e2ncias de todas as classes ao cont\u00eainer de servi\u00e7o. Embora o plugin gratuito forne\u00e7a v\u00e1rias classes PHP essenciais para sua funcionalidade, outras s\u00e3o disponibilizadas por complementos adicionais instalados no site, com o objetivo de expandir essa funcionalidade.<\/p>\n<p>Um bom exemplo de uso de um cont\u00eainer de servi\u00e7o \u00e9 o <a href=\"https:\/\/wordpress.org\/plugins\/gatographql\/\" target=\"_blank\" rel=\"noopener noreferrer\">Gato GraphQL<\/a>, que depende da biblioteca DependencyInjection do Symfony.<\/p>\n<p>\u00c9 assim que o <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/ce461a7155782823b9d14383e1036a75b564807c\/layers\/Engine\/packages\/root\/src\/Container\/ContainerBuilderFactoryTrait.php\" target=\"_blank\" rel=\"noopener noreferrer\">cont\u00eainer de servi\u00e7o \u00e9 instanciado<\/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>Observe que o cont\u00eainer de servi\u00e7o (acess\u00edvel no objeto PHP com a classe <code>GatoGraphQL\\ServiceContainer<\/code>) \u00e9 gerado na primeira vez que o plugin \u00e9 executado e, em seguida, armazenado em cache no disco (como o arquivo <code>container.php<\/code> em uma pasta tempor\u00e1ria do sistema). Isso ocorre porque a gera\u00e7\u00e3o do cont\u00eainer de servi\u00e7o \u00e9 um processo caro que pode levar v\u00e1rios segundos para ser conclu\u00eddo.<\/p>\n<p>Em seguida, tanto o plugin principal quanto todas as suas extens\u00f5es <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/fd73bf65cd30e164ffaa7860861974cf53683d51\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/config\/services.yaml\" target=\"_blank\" rel=\"noopener noreferrer\">definem quais servi\u00e7os devem ser injetados no cont\u00eainer por meio de um arquivo de configura\u00e7\u00e3o<\/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>Observe que podemos instanciar objetos para classes espec\u00edficas (como <code>GatoGraphQL\\GatoGraphQL\\Log\\Logger<\/code>, acessado por meio de sua interface de contrato <code>GatoGraphQL\\GatoGraphQL\\Log\\LoggerInterface<\/code>) e tamb\u00e9m podemos indicar &#8220;instanciar todas as classes em algum diret\u00f3rio&#8221; (como todos os servi\u00e7os em <code>..\/src\/Services<\/code>).<\/p>\n<p>Por fim, injetamos a <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/409eb230267021735e3326657c9929e94fa08b85\/layers\/Engine\/packages\/root\/src\/Module\/InitializeContainerServicesInModuleTrait.php\" target=\"_blank\" rel=\"noopener noreferrer\">configura\u00e7\u00e3o no cont\u00eainer do servi\u00e7o<\/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>Os servi\u00e7os injetados no cont\u00eainer podem ser configurados para serem inicializados sempre ou somente quando solicitados (modo pregui\u00e7oso).<\/p>\n<p>Por exemplo, para representar um tipo de artigo personalizado, o plugin tem a 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> cujo m\u00e9todo <code>initialize<\/code> executa a l\u00f3gica para inicializ\u00e1-lo de acordo com o 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>Ent\u00e3o, a 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> \u00e9 uma implementa\u00e7\u00e3o de um tipo de artigo personalizado. Ao ser injetada como um servi\u00e7o no cont\u00eainer, ela \u00e9 instanciada e registrada no WordPress:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace GatoGraphQLGatoGraphQLServicesCustomPostTypes;\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>Essa classe est\u00e1 presente no plugin gratuito, e outras classes de tipo de artigo personalizado, estendidas de forma semelhante a partir de <code>AbstractCustomPostType<\/code>, fornecidas pelas extens\u00f5es PRO.<\/p>\n<h2>Compara\u00e7\u00e3o entre hooks e cont\u00eaineres de servi\u00e7o<\/h2>\n<p>Vamos comparar as duas abordagens de design.<\/p>\n<p>A vantagem dos hooks de a\u00e7\u00e3o e filtro reside na sua simplicidade, uma vez que fazem parte do n\u00facleo do WordPress. Qualquer desenvolvedor familiarizado com o WordPress j\u00e1 tem conhecimento sobre como utilizar os hooks, o que significa que a curva de aprendizado \u00e9 m\u00ednima.<\/p>\n<p>Por\u00e9m, a l\u00f3gica est\u00e1 vinculada a um nome de hook, que \u00e9 uma string de caracteres. Isso pode resultar em erros: se o nome do hook for alterado, a l\u00f3gica da extens\u00e3o falhar\u00e1. Al\u00e9m disso, o desenvolvedor pode n\u00e3o notar o problema imediatamente, pois o c\u00f3digo PHP ainda ser\u00e1 compilado sem erros.<\/p>\n<p>Consequentemente, os hooks obsoletos tendem a ser mantidos por muito tempo na base de c\u00f3digo, possivelmente at\u00e9 para sempre. Assim, o projeto acumula c\u00f3digo obsoleto que n\u00e3o pode ser removido por medo de quebrar as extens\u00f5es.<\/p>\n<p>De volta ao WooCommerce, essa situa\u00e7\u00e3o \u00e9 evidenciada no arquivo <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> (observe como os hooks obsoletos s\u00e3o mantidos desde a vers\u00e3o <code>2.6<\/code>, enquanto a \u00faltima vers\u00e3o atual \u00e9 <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>O uso de um cont\u00eainer de servi\u00e7o tem a desvantagem de exigir uma biblioteca externa, o que aumenta ainda mais a complexidade. Al\u00e9m disso, <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\">essa biblioteca deve ter escopo<\/a> (usando <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>), dado o receio de que outra vers\u00e3o da mesma biblioteca possa ser instalada por um plugin diferente no mesmo site, criando potenciais conflitos.<\/p>\n<p>O uso de um cont\u00eainer de servi\u00e7o \u00e9, sem d\u00favida, mais dif\u00edcil de implementar e leva mais tempo de desenvolvimento.<\/p>\n<p>O lado positivo \u00e9 que os cont\u00eaineres de servi\u00e7o lidam com classes PHP sem precisar acoplar a l\u00f3gica a alguma string. Isso faz com que o projeto use mais <a href=\"https:\/\/phptherightway.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">pr\u00e1ticas recomendadas de PHP<\/a>, levando a uma base de c\u00f3digo mais f\u00e1cil de manter a longo prazo.<\/p>\n<h2>Resumo<\/h2>\n<p>Ao criar um plugin para WordPress, \u00e9 recomend\u00e1vel que ele suporte extens\u00f5es, permitindo a n\u00f3s (os criadores do plugin) oferecer recursos comerciais e tamb\u00e9m a terceiros adicionar funcionalidades extras e, com sorte, desenvolver um ecossistema centrado no plugin.<\/p>\n<p>Neste artigo, exploramos quais s\u00e3o as considera\u00e7\u00f5es sobre a arquitetura do projeto PHP para tornar o plugin extens\u00edvel. Como aprendemos, podemos escolher entre duas abordagens de design: Utilizar hooks ou adotar um cont\u00eainer de servi\u00e7o. Analisamos ambas as estrat\u00e9gias, destacando as qualidades e as limita\u00e7\u00f5es de cada uma.<\/p>\n<p><em>Voc\u00ea planeja tornar seu plugin de WordPress extens\u00edvel? <\/em>Compartilhe conosco na se\u00e7\u00e3o de coment\u00e1rios.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>No ecossistema do WordPress, adotar um modelo freemium \u00e9 um m\u00e9todo comum para promover e monetizar plugins comerciais. Essa abordagem envolve lan\u00e7ar uma vers\u00e3o b\u00e1sica do &#8230;<\/p>\n","protected":false},"author":196,"featured_media":67420,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[],"topic":[1026,1033],"class_list":["post-67419","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","topic-desenvolvimento-wordpress","topic-plugins-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>Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es - Kinsta\u00ae<\/title>\n<meta name=\"description\" content=\"Saiba como criar um plugin flex\u00edvel e dimension\u00e1vel que ofere\u00e7a suporte a extens\u00f5es, aprimore a funcionalidade e maximize a compatibilidade.\" \/>\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\/pt\/blog\/plugin-wordpress-suporte-extensoes\/\" \/>\n<meta property=\"og:locale\" content=\"pt_PT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es\" \/>\n<meta property=\"og:description\" content=\"Saiba como criar um plugin flex\u00edvel e dimension\u00e1vel que ofere\u00e7a suporte a extens\u00f5es, aprimore a funcionalidade e maximize a compatibilidade.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/kinstapt\/\" \/>\n<meta property=\"article:published_time\" content=\"2024-03-04T13:18:57+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-03-08T12:59:06+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/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=\"Saiba como criar um plugin flex\u00edvel e dimension\u00e1vel que ofere\u00e7a suporte a extens\u00f5es, aprimore a funcionalidade e maximize a compatibilidade.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/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_pt\" \/>\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leonardo Losoviz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tempo estimado de leitura\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinsta.com\/pt\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es\",\"datePublished\":\"2024-03-04T13:18:57+00:00\",\"dateModified\":\"2024-03-08T12:59:06+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/\"},\"wordCount\":2133,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"inLanguage\":\"pt-PT\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/\",\"url\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/\",\"name\":\"Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es - Kinsta\u00ae\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"datePublished\":\"2024-03-04T13:18:57+00:00\",\"dateModified\":\"2024-03-08T12:59:06+00:00\",\"description\":\"Saiba como criar um plugin flex\u00edvel e dimension\u00e1vel que ofere\u00e7a suporte a extens\u00f5es, aprimore a funcionalidade e maximize a compatibilidade.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#breadcrumb\"},\"inLanguage\":\"pt-PT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"contentUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"width\":1460,\"height\":730},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/pt\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Plugins WordPress\",\"item\":\"https:\/\/kinsta.com\/pt\/topicos\/plugins-wordpress\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/pt\/#website\",\"url\":\"https:\/\/kinsta.com\/pt\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Solu\u00e7\u00f5es de hospedagem Premium, r\u00e1pida e segura\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/pt\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"pt-PT\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/pt\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/pt\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/kinstapt\/\",\"https:\/\/x.com\/kinsta_pt\",\"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\/pt\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinsta.com\/pt\/#\/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\/pt\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es - Kinsta\u00ae","description":"Saiba como criar um plugin flex\u00edvel e dimension\u00e1vel que ofere\u00e7a suporte a extens\u00f5es, aprimore a funcionalidade e maximize a compatibilidade.","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\/pt\/blog\/plugin-wordpress-suporte-extensoes\/","og_locale":"pt_PT","og_type":"article","og_title":"Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es","og_description":"Saiba como criar um plugin flex\u00edvel e dimension\u00e1vel que ofere\u00e7a suporte a extens\u00f5es, aprimore a funcionalidade e maximize a compatibilidade.","og_url":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinstapt\/","article_published_time":"2024-03-04T13:18:57+00:00","article_modified_time":"2024-03-08T12:59:06+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"Saiba como criar um plugin flex\u00edvel e dimension\u00e1vel que ofere\u00e7a suporte a extens\u00f5es, aprimore a funcionalidade e maximize a compatibilidade.","twitter_image":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","twitter_creator":"@losoviz","twitter_site":"@kinsta_pt","twitter_misc":{"Escrito por":"Leonardo Losoviz","Tempo estimado de leitura":"13 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinsta.com\/pt\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es","datePublished":"2024-03-04T13:18:57+00:00","dateModified":"2024-03-08T12:59:06+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/"},"wordCount":2133,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/pt\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","inLanguage":"pt-PT","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/","url":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/","name":"Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es - Kinsta\u00ae","isPartOf":{"@id":"https:\/\/kinsta.com\/pt\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","datePublished":"2024-03-04T13:18:57+00:00","dateModified":"2024-03-08T12:59:06+00:00","description":"Saiba como criar um plugin flex\u00edvel e dimension\u00e1vel que ofere\u00e7a suporte a extens\u00f5es, aprimore a funcionalidade e maximize a compatibilidade.","breadcrumb":{"@id":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#breadcrumb"},"inLanguage":"pt-PT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/"]}]},{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#primaryimage","url":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","contentUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","width":1460,"height":730},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/pt\/"},{"@type":"ListItem","position":2,"name":"Plugins WordPress","item":"https:\/\/kinsta.com\/pt\/topicos\/plugins-wordpress\/"},{"@type":"ListItem","position":3,"name":"Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/pt\/#website","url":"https:\/\/kinsta.com\/pt\/","name":"Kinsta\u00ae","description":"Solu\u00e7\u00f5es de hospedagem Premium, r\u00e1pida e segura","publisher":{"@id":"https:\/\/kinsta.com\/pt\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/pt\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"pt-PT"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/pt\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/pt\/","logo":{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/kinstapt\/","https:\/\/x.com\/kinsta_pt","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\/pt\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinsta.com\/pt\/#\/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\/pt\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts\/67419","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/comments?post=67419"}],"version-history":[{"count":8,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts\/67419\/revisions"}],"predecessor-version":[{"id":67507,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts\/67419\/revisions\/67507"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/67419\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/67419\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/67419\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/67419\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/67419\/translations\/de"},{"embeddable":true,"hreflang":"ja","title":"Japanese","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/67419\/translations\/jp"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/67419\/translations\/es"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/67419\/translations\/nl"},{"href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/67419\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/media\/67420"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/media?parent=67419"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/tags?post=67419"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/topic?post=67419"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}