{"id":68700,"date":"2024-07-13T06:55:38","date_gmt":"2024-07-13T09:55:38","guid":{"rendered":"https:\/\/kinqsta.com\/pt\/?p=68700&#038;preview=true&#038;preview_id=68700"},"modified":"2024-07-16T06:04:02","modified_gmt":"2024-07-16T09:04:02","slug":"plugin-wordpress-extensivel","status":"publish","type":"post","link":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/","title":{"rendered":"Como Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP"},"content":{"rendered":"<p>Os plugins de WordPress podem ser estendidos com funcionalidades adicionais, conforme demonstrado por plugins populares como <a href=\"https:\/\/kinqsta.com\/pt\/blog\/tutorial-woocommerce\/\">WooCommerce<\/a> e Gravity Forms. No artigo <a href=\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-suporte-extensoes\/\">Como Projetar um Plugin de WordPress para Oferecer Suporte a Extens\u00f5es<\/a>, aprendemos que h\u00e1 duas maneiras principais de tornar um plugin de WordPress extens\u00edvel:<\/p>\n<ol>\n<li>Configurando hooks (a\u00e7\u00f5es e filtros) para que plugins de extens\u00e3o injetem sua pr\u00f3pria funcionalidade<\/li>\n<li>Fornecendo classes PHP que plugins de extens\u00e3o possam herdar<\/li>\n<\/ol>\n<p>O primeiro m\u00e9todo se baseia mais na documenta\u00e7\u00e3o, detalhando os hooks dispon\u00edveis e sua utiliza\u00e7\u00e3o. O segundo m\u00e9todo, por outro lado, oferece c\u00f3digo para extens\u00f5es pronto para uso, reduzindo a necessidade de ampla documenta\u00e7\u00e3o. Isso \u00e9 vantajoso porque criar documenta\u00e7\u00e3o junto com o c\u00f3digo pode complicar o gerenciamento e o lan\u00e7amento do plugin.<\/p>\n<p>Fornecer diretamente as classes <a href=\"https:\/\/kinqsta.com\/php\/\">PHP<\/a> substitui efetivamente a documenta\u00e7\u00e3o pelo c\u00f3digo. Em vez de ensinar como implementar um recurso, o plugin fornece o c\u00f3digo PHP necess\u00e1rio, simplificando a tarefa para desenvolvedores terceiros.<\/p>\n<p>Vamos explorar algumas t\u00e9cnicas para conseguir isso, com o objetivo final de promover um ecossistema de integra\u00e7\u00f5es em torno do nosso plugin de WordPress.<\/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>Definindo classes PHP b\u00e1sicas no plugin de WordPress<\/h2>\n<p>O <a href=\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress\/\">plugin de WordPress<\/a> incluir\u00e1 classes PHP destinadas ao uso por plugins de extens\u00e3o. Essas classes PHP podem n\u00e3o ser usadas pelo pr\u00f3prio plugin principal, mas s\u00e3o fornecidas especificamente para serem usadas por outros.<\/p>\n<p>Vejamos como isso \u00e9 implementado no plugin de c\u00f3digo aberto <a href=\"https:\/\/wordpress.org\/plugins\/gatographql\/\" target=\"_blank\" rel=\"noopener noreferrer\">Gato GraphQL<\/a>.<\/p>\n<h3>Classe AbstractPlugin:<\/h3>\n<p><a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/f918258371ec4c49c68b64fa184212cf0a956c16\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/PluginSkeleton\/AbstractPlugin.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AbstractPlugin<\/code><\/a> representa um plugin, tanto para o plugin principal do Gato GraphQL quanto para suas extens\u00f5es:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractPlugin implements PluginInterface\n{\n  protected string $pluginBaseName;\n  protected string $pluginSlug;\n  protected string $pluginName;\n\n  public function __construct(\n    protected string $pluginFile,\n    protected string $pluginVersion,\n    ?string $pluginName,\n  ) {\n    $this-&gt;pluginBaseName = plugin_basename($pluginFile);\n    $this-&gt;pluginSlug = dirname($this-&gt;pluginBaseName);\n    $this-&gt;pluginName = $pluginName ?? $this-&gt;pluginBaseName;\n  }\n\n  public function getPluginName(): string\n  {\n    return $this-&gt;pluginName;\n  }\n\n  public function getPluginBaseName(): string\n  {\n    return $this-&gt;pluginBaseName;\n  }\n\n  public function getPluginSlug(): string\n  {\n    return $this-&gt;pluginSlug;\n  }\n\n  public function getPluginFile(): string\n  {\n    return $this-&gt;pluginFile;\n  }\n\n  public function getPluginVersion(): string\n  {\n    return $this-&gt;pluginVersion;\n  }\n\n  public function getPluginDir(): string\n  {\n    return dirname($this-&gt;pluginFile);\n  }\n\n  public function getPluginURL(): string\n  {\n    return plugin_dir_url($this-&gt;pluginFile);\n  }\n\n  \/\/ ...\n}\n<\/code><\/pre>\n<h3>Classe AbstractMainPlugin:<\/h3>\n<p><a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/6545e639ceb85180bf869f50dcaae56de37421ea\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/PluginSkeleton\/AbstractMainPlugin.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AbstractMainPlugin<\/code><\/a> estende <code>AbstractPlugin<\/code> para representar o plugin principal:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface\n{\n  public function __construct(\n    string $pluginFile,\n    string $pluginVersion,\n    ?string $pluginName,\n    protected MainPluginInitializationConfigurationInterface $pluginInitializationConfiguration,\n  ) {\n    parent::__construct(\n      $pluginFile,\n      $pluginVersion,\n      $pluginName,\n    );\n  }\n\n  \/\/ ...\n}\n<\/code><\/pre>\n<h3>Classe AbstractExtension:<\/h3>\n<p>Similarmente, <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/2447b3478ecfd012d509ad3075149cc0191fb3a1\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/PluginSkeleton\/AbstractExtension.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AbstractExtension<\/code><\/a> estende <code>AbstractPlugin<\/code> para representar um plugin de extens\u00e3o:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface\n{\n  public function __construct(\n    string $pluginFile,\n    string $pluginVersion,\n    ?string $pluginName,\n    protected ?ExtensionInitializationConfigurationInterface $extensionInitializationConfiguration,\n  ) {\n    parent::__construct(\n      $pluginFile,\n      $pluginVersion,\n      $pluginName,\n    );\n  }\n\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Observe que <code>AbstractExtension<\/code> est\u00e1 inclu\u00edda no plugin principal, fornecendo funcionalidade para registrar e inicializar uma extens\u00e3o. Todavia, \u00e9 usada apenas por extens\u00f5es, n\u00e3o pelo plugin principal.<\/p>\n<p>A classe <code>AbstractPlugin<\/code> cont\u00e9m c\u00f3digo de inicializa\u00e7\u00e3o compartilhado invocado em momentos diferentes. Esses m\u00e9todos s\u00e3o definidos no n\u00edvel do ancestral, mas s\u00e3o invocados pelas classes herdadas de acordo com seus ciclos de vida.<\/p>\n<p>O plugin principal e as extens\u00f5es s\u00e3o inicializados executando o m\u00e9todo <code>setup<\/code> na classe correspondente, invocado de dentro do arquivo principal do plugin de WordPress.<\/p>\n<p>Por exemplo, no Gato GraphQL, isso \u00e9 feito em <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/135a11e2637c76410a1d23d090fc4ec9b87c01fb\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/gatographql.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>gatographql.php<\/code><\/a>:<\/p>\n<pre><code class=\"language-php\">$pluginFile = __FILE__;\n$pluginVersion = '2.4.0';\n$pluginName = __('Gato GraphQL', 'gatographql');\nPluginApp::getMainPluginManager()-&gt;register(new Plugin(\n  $pluginFile,\n  $pluginVersion,\n  $pluginName\n))-&gt;setup();\n<\/code><\/pre>\n<h3>M\u00e9todo setup:<\/h3>\n<p>No n\u00edvel do ancestral, <code>setup<\/code> cont\u00e9m a l\u00f3gica comum ao plugin e suas extens\u00f5es, como o cancelamento do registro delas, quando o plugin \u00e9 desativado. Esse m\u00e9todo n\u00e3o \u00e9 final; pode ser <span style=\"background-color: var(--kmt-sys-color-background-neutral-weak, #f9f5f3);font-weight: var(--kmt-sys-typography-body-font-weight)\" data-darkreader-inline-bgcolor>substitu\u00eddo<\/span> <span style=\"background-color: var(--kmt-sys-color-background-neutral-weak, #f9f5f3);font-weight: var(--kmt-sys-typography-body-font-weight)\" data-darkreader-inline-bgcolor>pelas classes herdadas para adicionar sua funcionalidade:<\/span><\/p>\n<pre><code class=\"language-php\">abstract class AbstractPlugin implements PluginInterface\n{\n  \/\/ ...\n\n  public function setup(): void\n  {\n    register_deactivation_hook(\n      $this-&gt;getPluginFile(),\n      $this-&gt;deactivate(...)\n    );\n  }\n\n  public function deactivate(): void\n  {\n    $this-&gt;removePluginVersion();\n  }\n\n  private function removePluginVersion(): void\n  {\n    $pluginVersions = get_option('gatographql-plugin-versions', []);\n    unset($pluginVersions[$this-&gt;pluginBaseName]);\n    update_option('gatographql-plugin-versions', $pluginVersions);\n  }\n}\n<\/code><\/pre>\n<h3>M\u00e9todo setup do plugin principal:<\/h3>\n<p>O m\u00e9todo <code>setup<\/code> do plugin principal inicializa o ciclo de vida do aplicativo. Executa a funcionalidade do plugin principal por meio de m\u00e9todos como <code>initialize<\/code>, <code>configureComponents<\/code>, <code>configure<\/code> e <code>boot<\/code>, e aciona os hooks de a\u00e7\u00e3o correspondentes para extens\u00f5es:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface\n{\n  public function setup(): void\n  {\n    parent::setup();\n\n    add_action('plugins_loaded', function (): void\n    {\n      \/\/ 1. Initialize main plugin\n      $this-&gt;initialize();\n\n      \/\/ 2. Initialize extensions\n      do_action('gatographql:initializeExtension');\n\n      \/\/ 3. Configure main plugin components\n      $this-&gt;configureComponents();\n\n      \/\/ 4. Configure extension components\n      do_action('gatographql:configureExtensionComponents');\n\n      \/\/ 5. Configure main plugin\n      $this-&gt;configure();\n\n      \/\/ 6. Configure extension\n      do_action('gatographql:configureExtension');\n\n      \/\/ 7. Boot main plugin\n      $this-&gt;boot();\n\n      \/\/ 8. Boot extension\n      do_action('gatographql:bootExtension');\n    }\n\n    \/\/ ...\n  }\n  \n  \/\/ ...\n}\n<\/code><\/pre>\n<h3>M\u00e9todo setup de extens\u00e3o:<\/h3>\n<p>A classe <code>AbstractExtension<\/code> executa sua l\u00f3gica nos hooks correspondentes:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface\n{\n  \/\/ ...\n\n  final public function setup(): void\n  {\n    parent::setup();\n\n    add_action('plugins_loaded', function (): void\n    {\n      \/\/ 2. Initialize extensions\n      add_action(\n        'gatographql:initializeExtension',\n        $this-&gt;initialize(...)\n      );\n\n      \/\/ 4. Configure extension components\n      add_action(\n        'gatographql:configureExtensionComponents',\n        $this-&gt;configureComponents(...)\n      );\n\n      \/\/ 6. Configure extension\n      add_action(\n        'gatographql:configureExtension',\n        $this-&gt;configure(...)\n      );\n\n      \/\/ 8. Boot extension\n      add_action(\n        'gatographql:bootExtension',\n        $this-&gt;boot(...)\n      );\n    }, 20);\n  }\n}\n<\/code><\/pre>\n<p>Os m\u00e9todos <code>initialize<\/code>, <code>configureComponents<\/code>, <code>configure<\/code> e <code>boot<\/code> s\u00e3o comuns ao plugin principal e \u00e0s extens\u00f5es, e podem compartilhar a l\u00f3gica. Essa l\u00f3gica compartilhada \u00e9 armazenada na classe <code>AbstractPlugin<\/code>.<\/p>\n<p>Por exemplo, o m\u00e9todo <code>configure<\/code> configura o plugin ou as extens\u00f5es, chamando <code>callPluginInitializationConfiguration<\/code>, que tem implementa\u00e7\u00f5es diferentes para o plugin principal e as extens\u00f5es e \u00e9 definido como abstrato, e <code>getModuleClassConfiguration<\/code>, que fornece um comportamento padr\u00e3o, mas pode ser <span style=\"background-color: var(--kmt-sys-color-background-neutral-weak, #f9f5f3);font-weight: var(--kmt-sys-typography-body-font-weight)\" data-darkreader-inline-bgcolor>substitu\u00eddo<\/span>\u00a0se necess\u00e1rio:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractPlugin implements PluginInterface\n{\n  \/\/ ...\n\n  public function configure(): void\n  {\n    $this-&gt;callPluginInitializationConfiguration();\n\n    $appLoader = App::getAppLoader();\n    $appLoader-&gt;addModuleClassConfiguration($this-&gt;getModuleClassConfiguration());\n  }\n\n  abstract protected function callPluginInitializationConfiguration(): void;\n\n  \/**\n   * @return array&lt;class-string&lt;ModuleInterface&gt;,mixed&gt; [key]: Module class, [value]: Configuration\n   *\/\n  public function getModuleClassConfiguration(): array\n  {\n    return [];\n  }\n}\n<\/code><\/pre>\n<p>O plugin principal fornece sua implementa\u00e7\u00e3o para <code>callPluginInitializationConfiguration<\/code>:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface\n{\n  \/\/ ...\n\n  protected function callPluginInitializationConfiguration(): void\n  {\n    $this-&gt;pluginInitializationConfiguration-&gt;initialize();\n  }\n}\n<\/code><\/pre>\n<p>Da mesma forma, a classe de extens\u00e3o fornece sua implementa\u00e7\u00e3o:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface\n{\n  \/\/ ...\n\n  protected function callPluginInitializationConfiguration(): void\n  {\n    $this-&gt;extensionInitializationConfiguration?-&gt;initialize();\n  }\n}\n<\/code><\/pre>\n<p>Os m\u00e9todos <code>initialize<\/code>, <code>configureComponents<\/code> e <code>boot<\/code> s\u00e3o definidos no n\u00edvel do ancestral e podem ser substitu\u00eddos por classes herdadas:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractPlugin implements PluginInterface\n{\n  \/\/ ...\n\n  public function initialize(): void\n  {\n    $moduleClasses = $this-&gt;getModuleClassesToInitialize();\n    App::getAppLoader()-&gt;addModuleClassesToInitialize($moduleClasses);\n  }\n\n  \/**\n   * @return array&lt;class-string&lt;ModuleInterface&gt;&gt; List of `Module` class to initialize\n   *\/\n  abstract protected function getModuleClassesToInitialize(): array;\n\n  public function configureComponents(): void\n  {\n    $classNamespace = ClassHelpers::getClassPSR4Namespace(get_called_class());\n    $moduleClass = $classNamespace . '\\\\Module';\n    App::getModule($moduleClass)-&gt;setPluginFolder(dirname($this-&gt;pluginFile));\n  }\n\n  public function boot(): void\n  {\n    \/\/ By default, do nothing\n  }\n}\n<\/code><\/pre>\n<p>Todos os m\u00e9todos podem ser <span style=\"background-color: var(--kmt-sys-color-background-neutral-weak, #f9f5f3);font-weight: var(--kmt-sys-typography-body-font-weight)\" data-darkreader-inline-bgcolor>substitu\u00eddo<\/span>s por <code>AbstractMainPlugin<\/code> ou <code>AbstractExtension<\/code> para estend\u00ea-los com sua funcionalidade personalizada.<\/p>\n<p>No caso do plugin principal, o m\u00e9todo <code>setup<\/code> tamb\u00e9m remove qualquer cache da inst\u00e2ncia do WordPress quando o plugin ou qualquer uma de suas extens\u00f5es \u00e9 ativado, ou desativado:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface\n{\n  public function setup(): void\n  {\n    parent::setup();\n\n    \/\/ ...\n\n    \/\/ Main-plugin specific methods\n    add_action(\n      'activate_plugin',\n      function (string $pluginFile): void {\n        $this-&gt;maybeRegenerateContainerWhenPluginActivatedOrDeactivated($pluginFile);\n      }\n    );\n    add_action(\n      'deactivate_plugin',\n      function (string $pluginFile): void {\n        $this-&gt;maybeRegenerateContainerWhenPluginActivatedOrDeactivated($pluginFile);\n      }\n    );\n  }\n\n  public function maybeRegenerateContainerWhenPluginActivatedOrDeactivated(string $pluginFile): void\n  {\n    \/\/ Removed code for simplicity\n  }\n\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Da mesma forma, o m\u00e9todo <code>deactivate<\/code> remove o cache e o <code>boot<\/code> executa hooks de a\u00e7\u00e3o adicionais somente para o plugin principal:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface\n{\n  public function deactivate(): void\n  {\n    parent::deactivate();\n\n    $this-&gt;removeTimestamps();\n  }\n\n  protected function removeTimestamps(): void\n  {\n    $userSettingsManager = UserSettingsManagerFacade::getInstance();\n    $userSettingsManager-&gt;removeTimestamps();\n  }\n\n  public function boot(): void\n  {\n    parent::boot();\n\n    add_filter(\n      'admin_body_class',\n      function (string $classes): string {\n        $extensions = PluginApp::getExtensionManager()-&gt;getExtensions();\n        $commercialExtensionActivatedLicenseObjectProperties = SettingsHelpers::getCommercialExtensionActivatedLicenseObjectProperties();\n        foreach ($extensions as $extension) {\n          $extensionCommercialExtensionActivatedLicenseObjectProperties = $commercialExtensionActivatedLicenseObjectProperties[$extension-&gt;getPluginSlug()] ?? null;\n          if ($extensionCommercialExtensionActivatedLicenseObjectProperties === null) {\n            continue;\n          }\n          return $classes . ' is-gatographql-customer';\n        }\n        return $classes;\n      }\n    );\n  }\n}\n<\/code><\/pre>\n<p>Com base em todo o c\u00f3digo apresentado acima, fica claro que, ao projetar e codificar um plugin de WordPress, precisamos considerar as necessidades de suas extens\u00f5es e reutilizar o c\u00f3digo entre elas o m\u00e1ximo poss\u00edvel. A implementa\u00e7\u00e3o de padr\u00f5es s\u00f3lidos de <a href=\"https:\/\/kinqsta.com\/pt\/blog\/php-oop\/\">programa\u00e7\u00e3o orientada a objetos<\/a> (como os princ\u00edpios <a href=\"https:\/\/dev.to\/evrtrabajo\/solid-in-php-d8e\" target=\"_blank\" rel=\"noopener noreferrer\">SOLID<\/a>) ajuda a conseguir isso, tornando a base de c\u00f3digo sustent\u00e1vel a longo prazo.<\/p>\n<h2>Declarando e validando a depend\u00eancia de vers\u00e3o<\/h2>\n<p>Como a extens\u00e3o herda de uma classe PHP fornecida pelo plugin, \u00e9 fundamental validar que a vers\u00e3o necess\u00e1ria do plugin est\u00e1 presente. Falhar em fazer isso pode causar conflitos que derrubar\u00e3o o site.<\/p>\n<p>Por exemplo, se a classe <code>AbstractExtension<\/code> for atualizada com altera\u00e7\u00f5es defeituosas e lan\u00e7ar uma vers\u00e3o principal <code>4.0.0<\/code> em rela\u00e7\u00e3o \u00e0 anterior <code>3.4.0<\/code>, carregar a extens\u00e3o sem verificar a vers\u00e3o poder\u00e1 resultar em um erro de PHP, impedindo o carregamento do WordPress.<\/p>\n<p>Para evitar isso, a extens\u00e3o deve validar que o plugin instalado \u00e9 da vers\u00e3o <code>3.x.x<\/code>. Quando a vers\u00e3o <code>4.0.0<\/code> for instalada, a extens\u00e3o ser\u00e1 desativada, evitando assim os erros.<\/p>\n<p>A extens\u00e3o pode realizar essa valida\u00e7\u00e3o usando a seguinte l\u00f3gica, executada no hook <code>plugins_loaded<\/code> (uma vez que o plugin principal j\u00e1 estar\u00e1 carregado) no <a href=\"https:\/\/github.com\/GatoGraphQL\/ExtensionStarter\/blob\/4ce883e3ec5325f15797aaff3042370f25e7e671\/templates\/basic\/layers\/GatoGraphQLForWP\/plugins\/extension-template\/gatographql-extension-template.php\" target=\"_blank\" rel=\"noopener noreferrer\">arquivo de plugin principal da extens\u00e3o<\/a>. Essa l\u00f3gica acessa a classe <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/4d33af1ad5c40318b0c2e612d1114c5ba342b06e\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/PluginManagement\/ExtensionManager.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>ExtensionManager<\/code><\/a> que est\u00e1 inclu\u00edda no plugin principal para gerenciar as extens\u00f5es:<\/p>\n<pre><code class=\"language-php\">\/**\n * Create and set-up the extension\n *\/\nadd_action(\n  'plugins_loaded',\n  function (): void {\n    \/**\n     * Extension's name and version.\n     *\n     * Use a stability suffix as supported by Composer.\n     *\/\n    $extensionVersion = '1.1.0';\n    $extensionName = __('Gato GraphQL - Extension Template');\n\n    \/**\n     * The minimum version required from the Gato GraphQL plugin\n     * to activate the extension.\n     *\/\n    $gatoGraphQLPluginVersionConstraint = '^1.0';\n    \n    \/**\n     * Validate Gato GraphQL is active\n     *\/\n    if (!class_exists(\\GatoGraphQL\\GatoGraphQL\\Plugin::class)) {\n      add_action('admin_notices', function () use ($extensionName) {\n        printf(\n          '&lt;div class=\"notice notice-error\"&gt;&lt;p&gt;%s&lt;\/p&gt;&lt;\/div&gt;',\n          sprintf(\n            __('Plugin &lt;strong&gt;%s&lt;\/strong&gt; is not installed or activated. Without it, plugin &lt;strong&gt;%s&lt;\/strong&gt; will not be loaded.'),\n            __('Gato GraphQL'),\n            $extensionName\n          )\n        );\n      });\n      return;\n    }\n\n    $extensionManager = \\GatoGraphQL\\GatoGraphQL\\PluginApp::getExtensionManager();\n    if (!$extensionManager-&gt;assertIsValid(\n      GatoGraphQLExtension::class,\n      $extensionVersion,\n      $extensionName,\n      $gatoGraphQLPluginVersionConstraint\n    )) {\n      return;\n    }\n    \n    \/\/ Load Composer\u2019s autoloader\n    require_once(__DIR__ . '\/vendor\/autoload.php');\n\n    \/\/ Create and set-up the extension instance\n    $extensionManager-&gt;register(new GatoGraphQLExtension(\n      __FILE__,\n      $extensionVersion,\n      $extensionName,\n    ))-&gt;setup();\n  }\n);\n<\/code><\/pre>\n<p>Observe como a extens\u00e3o declara uma depend\u00eancia na restri\u00e7\u00e3o de vers\u00e3o <code>^1.0<\/code> do plugin principal (usando as <a href=\"https:\/\/getcomposer.org\/doc\/articles\/versions.md#writing-version-constraints\" target=\"_blank\" rel=\"noopener noreferrer\">restri\u00e7\u00f5es de vers\u00e3o do Composer<\/a>). Assim, quando a vers\u00e3o <code>2.0.0<\/code> do Gato GraphQL for instalada, a extens\u00e3o n\u00e3o ser\u00e1 ativada.<\/p>\n<p>A restri\u00e7\u00e3o de vers\u00e3o \u00e9 validada por meio do m\u00e9todo <code>ExtensionManager::assertIsValid<\/code>, que chama <code>Semver::satisfies<\/code> (fornecido pelo <a href=\"https:\/\/packagist.org\/packages\/composer\/semver\" target=\"_blank\" rel=\"noopener noreferrer\">pacote <code>composer\/semver<\/code><\/a>):<\/p>\n<pre><code class=\"language-php\">use Composer\\Semver\\Semver;\n\nclass ExtensionManager extends AbstractPluginManager\n{\n  \/**\n   * Validate that the required version of the Gato GraphQL for WP plugin is installed.\n   *\n   * If the assertion fails, it prints an error on the WP admin and returns false\n   *\n   * @param string|null $mainPluginVersionConstraint the semver version constraint required for the plugin (eg: \"^1.0\" means &gt;=1.0.0 and &lt;2.0.0)\n   *\/\n  public function assertIsValid(\n    string $extensionClass,\n    string $extensionVersion,\n    ?string $extensionName = null,\n    ?string $mainPluginVersionConstraint = null,\n  ): bool {\n    $mainPlugin = \\GatoGraphQL\\GatoGraphQL\\PluginApp::getMainPluginManager()-&gt;getPlugin();\n    $mainPluginVersion = $mainPlugin-&gt;getPluginVersion();\n    if (\n      $mainPluginVersionConstraint !== null && !Semver::satisfies(\n        $mainPluginVersion,\n        $mainPluginVersionConstraint\n      )\n    ) {\n      $this-&gt;printAdminNoticeErrorMessage(\n        sprintf(\n          __('Extension or bundle &lt;strong&gt;%s&lt;\/strong&gt; requires plugin &lt;strong&gt;%s&lt;\/strong&gt; to satisfy version constraint &lt;code&gt;%s&lt;\/code&gt;, but the current version &lt;code&gt;%s&lt;\/code&gt; does not. The extension or bundle has not been loaded.', 'gatographql'),\n          $extensionName ?? $extensionClass,\n          $mainPlugin-&gt;getPluginName(),\n          $mainPluginVersionConstraint,\n          $mainPlugin-&gt;getPluginVersion(),\n        )\n      );\n      return false;\n    }\n\n    return true;\n  }\n\n  protected function printAdminNoticeErrorMessage(string $errorMessage): void\n  {\n    \\add_action('admin_notices', function () use ($errorMessage): void {\n      $adminNotice_safe = sprintf(\n        '&lt;div class=\"notice notice-error\"&gt;&lt;p&gt;%s&lt;\/p&gt;&lt;\/div&gt;',\n        $errorMessage\n      );\n      echo $adminNotice_safe;\n    });\n  }\n}\n<\/code><\/pre>\n<h2>Executando testes de integra\u00e7\u00e3o em um servidor WordPress<\/h2>\n<p>Para facilitar a cria\u00e7\u00e3o de extens\u00f5es para seus plugins por desenvolvedores terceiros, forne\u00e7a a eles ferramentas para desenvolvimento e teste, incluindo fluxos de trabalho para seus processos de <a href=\"https:\/\/kinqsta.com\/pt\/blog\/testes-automatizados\/\">integra\u00e7\u00e3o cont\u00ednua e entrega cont\u00ednua<\/a> (CI\/CD).<\/p>\n<p>Durante o desenvolvimento, qualquer pessoa pode facilmente ativar um servidor web usando o <a href=\"https:\/\/kinqsta.com\/pt\/devkinsta\/\">DevKinsta<\/a>, instalar o plugin para o qual est\u00e1 codificando a extens\u00e3o e validar imediatamente que a extens\u00e3o \u00e9 compat\u00edvel com o plugin.<\/p>\n<p>Para automatizar os testes durante a CI\/CD, precisamos ter o servidor web acess\u00edvel em uma rede para o servi\u00e7o de CI\/CD. Servi\u00e7os como o InstaWP podem criar um site sandbox com o WordPress instalado para essa finalidade.<\/p>\n<p>Se a base de c\u00f3digo da extens\u00e3o estiver hospedada no <a href=\"https:\/\/kinqsta.com\/pt\/blog\/que-github\/\">GitHub<\/a>, os desenvolvedores poder\u00e3o usar o <a href=\"https:\/\/kinqsta.com\/pt\/blog\/como-configurar-pipeline-ci-cd\/#what-is-github-actions\">GitHub Actions<\/a> para executar testes de integra\u00e7\u00e3o no servi\u00e7o InstaWP. O fluxo de trabalho a seguir instala a <a href=\"https:\/\/github.com\/GatoGraphQL\/ExtensionStarter\/blob\/aa574c785a139d8ffc094b3b3703eb516156f77e\/.github\/workflows\/generate_plugins.yml\" target=\"_blank\" rel=\"noopener noreferrer\">extens\u00e3o<\/a> em um site sandbox do InstaWP (juntamente com a vers\u00e3o est\u00e1vel mais recente do plugin principal) e, em seguida, <a href=\"https:\/\/github.com\/GatoGraphQL\/ExtensionStarter\/blob\/a2d5d37ed205264f5ee03ce3ad075a08e89b67a7\/.github\/workflows\/integration_tests.yml\" target=\"_blank\" rel=\"noopener noreferrer\">executa os testes de integra\u00e7\u00e3o<\/a>:<\/p>\n<pre><code class=\"language-yaml\">name: Integration tests (InstaWP)\non:\n  workflow_run:\n    workflows: [Generate plugins]\n    types:\n      - completed\n\njobs:\n  provide_data:\n    if: ${{ github.event.workflow_run.conclusion == 'success' }}\n    name: Retrieve the GitHub Action artifact URLs to install in InstaWP\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n\n      - uses: shivammathur\/setup-php@v2\n        with:\n          php-version: 8.1\n          coverage: none\n        env:\n          COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - uses: \"ramsey\/composer-install@v2\"\n\n      - name: Retrieve artifact URLs from GitHub workflow\n        uses: actions\/github-script@v6\n        id: artifact-url\n        with:\n          script: |\n            const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              run_id: context.payload.workflow_run.id,\n            });\n            const artifactURLs = allArtifacts.data.artifacts.map((artifact) =&gt; {\n              return artifact.url.replace('https:\/\/api.github.com\/repos', 'https:\/\/nightly.link') + '.zip'\n            }).concat([\n              \"https:\/\/downloads.wordpress.org\/plugin\/gatographql.latest-stable.zip\"\n            ]);\n            return artifactURLs.join(',');\n          result-encoding: string\n\n      - name: Artifact URL for InstaWP\n        run: echo \"Artifact URL for InstaWP - ${{ steps.artifact-url.outputs.result }}\"\n        shell: bash\n\n    outputs:\n      artifact_url: ${{ steps.artifact-url.outputs.result }}\n\n  process:\n    needs: provide_data\n    name: Launch InstaWP site from template 'integration-tests' and execute integration tests against it\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n\n      - uses: shivammathur\/setup-php@v2\n        with:\n          php-version: 8.1\n          coverage: none\n        env:\n          COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - uses: \"ramsey\/composer-install@v2\"\n\n      - name: Create InstaWP instance\n        uses: instawp\/wordpress-testing-automation@main\n        id: create-instawp\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          INSTAWP_TOKEN: ${{ secrets.INSTAWP_TOKEN }}\n          INSTAWP_TEMPLATE_SLUG: \"integration-tests\"\n          REPO_ID: 25\n          INSTAWP_ACTION: create-site-template\n          ARTIFACT_URL: ${{ needs.provide_data.outputs.artifact_url }}\n\n      - name: InstaWP instance URL\n        run: echo \"InstaWP instance URL - ${{ steps.create-instawp.outputs.instawp_url }}\"\n        shell: bash\n\n      - name: Extract InstaWP domain\n        id: extract-instawp-domain        \n        run: |\n          instawp_domain=\"$(echo \"${{ steps.create-instawp.outputs.instawp_url }}\" | sed -e s#https:\/\/##)\"\n          echo \"instawp-domain=$(echo $instawp_domain)\" &gt;&gt; $GITHUB_OUTPUT\n\n      - name: Run tests\n        run: |\n          INTEGRATION_TESTS_WEBSERVER_DOMAIN=${{ steps.extract-instawp-domain.outputs.instawp-domain }} \\\n          INTEGRATION_TESTS_AUTHENTICATED_ADMIN_USER_USERNAME=${{ steps.create-instawp.outputs.iwp_wp_username }} \\\n          INTEGRATION_TESTS_AUTHENTICATED_ADMIN_USER_PASSWORD=${{ steps.create-instawp.outputs.iwp_wp_password }} \\\n          vendor\/bin\/phpunit --filter=Integration\n\n      - name: Destroy InstaWP instance\n        uses: instawp\/wordpress-testing-automation@main\n        id: destroy-instawp\n        if: ${{ always() }}\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          INSTAWP_TOKEN: ${{ secrets.INSTAWP_TOKEN }}\n          INSTAWP_TEMPLATE_SLUG: \"integration-tests\"\n          REPO_ID: 25\n          INSTAWP_ACTION: destroy-site\n<\/code><\/pre>\n<p>Esse fluxo de trabalho acessa o arquivo <strong>.zip<\/strong> por meio do <a href=\"https:\/\/nightly.link\" target=\"_blank\" rel=\"noopener noreferrer\">Nightly Link<\/a>, um servi\u00e7o que permite acessar um artefato do GitHub sem fazer login, simplificando a configura\u00e7\u00e3o do InstaWP.<\/p>\n<h2>Lan\u00e7amento do plugin de extens\u00e3o<\/h2>\n<p>Podemos fornecer ferramentas para ajudar a lan\u00e7ar as extens\u00f5es, automatizando os procedimentos o m\u00e1ximo poss\u00edvel.<\/p>\n<p>O <a href=\"https:\/\/github.com\/symplify\/monorepo-builder\" target=\"_blank\" rel=\"noopener noreferrer\">Monorepo Builder<\/a> \u00e9 uma biblioteca para gerenciar qualquer projeto PHP, inclusive um plugin de WordPress. Ele fornece o comando <code>monorepo-builder release<\/code> para lan\u00e7ar uma vers\u00e3o do projeto, incrementando o componente principal, secund\u00e1rio ou de corre\u00e7\u00e3o da vers\u00e3o de acordo com o <a href=\"https:\/\/semver.org\" target=\"_blank\" rel=\"noopener noreferrer\">controle de vers\u00e3o sem\u00e2ntico<\/a>.<\/p>\n<p>Esse comando executa uma s\u00e9rie de <a href=\"https:\/\/github.com\/symplify\/monorepo-builder?tab=readme-ov-file#7-release-flow\" target=\"_blank\" rel=\"noopener noreferrer\">workers de lan\u00e7amento<\/a>, que s\u00e3o classes PHP que executam determinada l\u00f3gica. Os workers padr\u00e3o incluem um que cria um <code>git tag<\/code> com a nova vers\u00e3o e outro que envia a tag para o reposit\u00f3rio remoto. Os workers personalizados podem ser injetados antes, depois ou entre essas etapas.<\/p>\n<p>Os workers de lan\u00e7amento s\u00e3o configurados por meio de um arquivo de configura\u00e7\u00e3o:<\/p>\n<pre><code class=\"language-php\">use Symplify\\MonorepoBuilder\\Config\\MBConfig;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\AddTagToChangelogReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\PushNextDevReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\PushTagReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\SetCurrentMutualDependenciesReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\SetNextMutualDependenciesReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\TagVersionReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\UpdateBranchAliasReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\UpdateReplaceReleaseWorker;\n\nreturn static function (MBConfig $mbConfig): void {\n  \/\/ release workers - in order to execute\n  $mbConfig-&gt;workers([\n    UpdateReplaceReleaseWorker::class,\n    SetCurrentMutualDependenciesReleaseWorker::class,\n    AddTagToChangelogReleaseWorker::class,\n    TagVersionReleaseWorker::class,\n    PushTagReleaseWorker::class,\n    SetNextMutualDependenciesReleaseWorker::class,\n    UpdateBranchAliasReleaseWorker::class,\n    PushNextDevReleaseWorker::class,\n  ]);\n};\n<\/code><\/pre>\n<p>Podemos fornecer workers de lan\u00e7amento personalizados para aumentar o processo de lan\u00e7amento adaptado \u00e0s necessidades de um plugin de WordPress. Por exemplo, o arquivo <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/af21729cd7e88439156ca1b9cf0231a06354b18f\/src\/OnDemand\/Symplify\/MonorepoBuilder\/Release\/ReleaseWorker\/ConvertStableTagVersionForProdInPluginReadmeFileReleaseWorker.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>InjectStableTagVersionInPluginReadmeFileReleaseWorker<\/code><\/a> define a nova vers\u00e3o como a entrada &#8220;Stable tag&#8221; no <a href=\"https:\/\/developer.wordpress.org\/plugins\/wordpress-org\/how-your-readme-txt-works\/\" target=\"_blank\" rel=\"noopener noreferrer\">arquivo <strong>readme.txt<\/strong> da extens\u00e3o<\/a>:<\/p>\n<pre><code class=\"language-php\">use Nette\\Utils\\Strings;\nuse PharIo\\Version\\Version;\nuse Symplify\\SmartFileSystem\\SmartFileInfo;\nuse Symplify\\SmartFileSystem\\SmartFileSystem;\n\nclass InjectStableTagVersionInPluginReadmeFileReleaseWorker implements ReleaseWorkerInterface\n{\n  public function __construct(\n    \/\/ This class is provided by the Monorepo Builder\n    private SmartFileSystem $smartFileSystem,\n  ) {\n  }\n\n  public function getDescription(Version $version): string\n  {\n    return 'Have the \"Stable tag\" point to the new version in the plugin\\'s readme.txt file';\n  }\n\n  public function work(Version $version): void\n  {\n    $replacements = [\n      '\/Stable tag:\\s+[a-z0-9.-]+\/' =&gt; 'Stable tag: ' . $version-&gt;getVersionString(),\n    ];\n    $this-&gt;replaceContentInFiles(['\/readme.txt'], $replacements);\n  }\n\n  \/**\n   * @param string[] $files\n   * @param array&lt;string,string&gt; $regexPatternReplacements regex pattern to search, and its replacement\n   *\/\n  protected function replaceContentInFiles(array $files, array $regexPatternReplacements): void\n  {\n    foreach ($files as $file) {\n      $fileContent = $this-&gt;smartFileSystem-&gt;readFile($file);\n      foreach ($regexPatternReplacements as $regexPattern =&gt; $replacement) {\n        $fileContent = Strings::replace($fileContent, $regexPattern, $replacement);\n      }\n      $this-&gt;smartFileSystem-&gt;dumpFile($file, $fileContent);\n    }\n  }\n}\n<\/code><\/pre>\n<p>Ao adicionar <code>InjectStableTagVersionInPluginReadmeFileReleaseWorker<\/code> \u00e0 lista de configura\u00e7\u00e3o, sempre que voc\u00ea executar o comando <code>monorepo-builder release<\/code> para lan\u00e7ar uma nova vers\u00e3o do plugin, a &#8220;tag Stable&#8221; no arquivo readme <strong>.txt<\/strong> da extens\u00e3o ser\u00e1 atualizada automaticamente.<\/p>\n<h2>Publicando o plugin de extens\u00e3o no diret\u00f3rio WP.org<\/h2>\n<p>Tamb\u00e9m podemos distribuir um fluxo de trabalho para ajudar a lan\u00e7ar a extens\u00e3o no <a href=\"https:\/\/wordpress.org\/plugins\/\" target=\"_blank\" rel=\"noopener noreferrer\">Diret\u00f3rio de Plugins do WordPress<\/a>. Ao marcar o projeto no reposit\u00f3rio remoto, o <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/f8579139f1af05aae171e776c6f53b0c458e339a\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/.github\/workflows\/deploy_wordpress_plugin_to_svn.yml\" target=\"_blank\" rel=\"noopener noreferrer\">fluxo de trabalho a seguir<\/a> publicar\u00e1 o plugin de extens\u00e3o do WordPress no diret\u00f3rio:<\/p>\n<pre><code class=\"language-yml\"># See: https:\/\/github.com\/10up\/action-wordpress-plugin-deploy#deploy-on-pushing-a-new-tag\nname: Deploy to WordPress.org Plugin Directory (SVN)\non:\n  push:\n  tags:\n  - \"*\"\n\njobs:\n  tag:\n  name: New tag\n  runs-on: ubuntu-latest\n  steps:\n  - uses: actions\/checkout@master\n  - name: WordPress Plugin Deploy\n    uses: 10up\/action-wordpress-plugin-deploy@stable\n    env:\n    SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}\n    SVN_USERNAME: ${{ secrets.SVN_USERNAME }}\n    SLUG: ${{ secrets.SLUG }}\n<\/code><\/pre>\n<p>Esse fluxo de trabalho usa a a\u00e7\u00e3o <a href=\"https:\/\/github.com\/10up\/action-wordpress-plugin-deploy\" target=\"_blank\" rel=\"noopener noreferrer\"><code>10up\/action-wordpress-plugin-deploy<\/code><\/a>, que recupera o c\u00f3digo de um reposit\u00f3rio Git e o envia ao <a href=\"https:\/\/kinqsta.com\/blog\/publish-plugin-wordpress-plugin-directory\/#plugin-structure\">reposit\u00f3rio SVN do WordPress.org<\/a>, simplificando a opera\u00e7\u00e3o.<\/p>\n<h2>Resumo<\/h2>\n<p>Ao criar um plugin extens\u00edvel para o WordPress, nosso objetivo \u00e9 facilitar ao m\u00e1ximo para que desenvolvedores terceiros possam estend\u00ea-lo, maximizando assim as chances de promover um ecossistema vibrante em torno de nossos plugins.<\/p>\n<p>Ao passo que fornecer uma ampla documenta\u00e7\u00e3o pode orientar os desenvolvedores sobre como estender o plugin, uma abordagem ainda mais eficaz \u00e9 fornecer o c\u00f3digo PHP e as ferramentas necess\u00e1rias para desenvolver, testar e lan\u00e7ar suas extens\u00f5es.<\/p>\n<p>Ao incluir o c\u00f3digo adicional necess\u00e1rio para as extens\u00f5es diretamente em nosso plugin, simplificamos o processo para os desenvolvedores.<\/p>\n<p><em>Voc\u00ea planeja tornar seu plugin de WordPress extens\u00edvel? Informe-nos na se\u00e7\u00e3o de coment\u00e1rios.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Os plugins de WordPress podem ser estendidos com funcionalidades adicionais, conforme demonstrado por plugins populares como WooCommerce e Gravity Forms. No artigo Como Projetar um Plugin &#8230;<\/p>\n","protected":false},"author":196,"featured_media":68701,"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-68700","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 Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP - Kinsta\u00ae<\/title>\n<meta name=\"description\" content=\"Aprenda a criar um plugin WordPress extens\u00edvel que simplifica o processo de desenvolvimento para desenvolvedores terceiros.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/\" \/>\n<meta property=\"og:locale\" content=\"pt_PT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Como Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP\" \/>\n<meta property=\"og:description\" content=\"Aprenda a criar um plugin WordPress extens\u00edvel que simplifica o processo de desenvolvimento para desenvolvedores terceiros.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/\" \/>\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-07-13T09:55:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-07-16T09:04:02+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-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=\"Aprenda a criar um plugin WordPress extens\u00edvel que simplifica o processo de desenvolvimento para desenvolvedores terceiros.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions-1024x512.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=\"17 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinqsta.com\/pt\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"Como Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP\",\"datePublished\":\"2024-07-13T09:55:38+00:00\",\"dateModified\":\"2024-07-16T09:04:02+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/\"},\"wordCount\":1666,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinqsta.com\/pt\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"inLanguage\":\"pt-PT\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/\",\"url\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/\",\"name\":\"Como Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP - Kinsta\u00ae\",\"isPartOf\":{\"@id\":\"https:\/\/kinqsta.com\/pt\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"datePublished\":\"2024-07-13T09:55:38+00:00\",\"dateModified\":\"2024-07-16T09:04:02+00:00\",\"description\":\"Aprenda a criar um plugin WordPress extens\u00edvel que simplifica o processo de desenvolvimento para desenvolvedores terceiros.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#breadcrumb\"},\"inLanguage\":\"pt-PT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#primaryimage\",\"url\":\"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"contentUrl\":\"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"width\":1460,\"height\":730},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinqsta.com\/pt\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Plugins WordPress\",\"item\":\"https:\/\/kinqsta.com\/pt\/topicos\/plugins-wordpress\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Como Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinqsta.com\/pt\/#website\",\"url\":\"https:\/\/kinqsta.com\/pt\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Solu\u00e7\u00f5es de hospedagem Premium, r\u00e1pida e segura\",\"publisher\":{\"@id\":\"https:\/\/kinqsta.com\/pt\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinqsta.com\/pt\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"pt-PT\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinqsta.com\/pt\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinqsta.com\/pt\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinqsta.com\/pt\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinqsta.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:\/\/kinqsta.com\/pt\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinqsta.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:\/\/kinqsta.com\/pt\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Como Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP - Kinsta\u00ae","description":"Aprenda a criar um plugin WordPress extens\u00edvel que simplifica o processo de desenvolvimento para desenvolvedores terceiros.","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:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/","og_locale":"pt_PT","og_type":"article","og_title":"Como Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP","og_description":"Aprenda a criar um plugin WordPress extens\u00edvel que simplifica o processo de desenvolvimento para desenvolvedores terceiros.","og_url":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinstapt\/","article_published_time":"2024-07-13T09:55:38+00:00","article_modified_time":"2024-07-16T09:04:02+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"Aprenda a criar um plugin WordPress extens\u00edvel que simplifica o processo de desenvolvimento para desenvolvedores terceiros.","twitter_image":"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions-1024x512.jpg","twitter_creator":"@losoviz","twitter_site":"@kinsta_pt","twitter_misc":{"Escrito por":"Leonardo Losoviz","Tempo estimado de leitura":"17 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#article","isPartOf":{"@id":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinqsta.com\/pt\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"Como Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP","datePublished":"2024-07-13T09:55:38+00:00","dateModified":"2024-07-16T09:04:02+00:00","mainEntityOfPage":{"@id":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/"},"wordCount":1666,"commentCount":0,"publisher":{"@id":"https:\/\/kinqsta.com\/pt\/#organization"},"image":{"@id":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#primaryimage"},"thumbnailUrl":"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","inLanguage":"pt-PT","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/","url":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/","name":"Como Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP - Kinsta\u00ae","isPartOf":{"@id":"https:\/\/kinqsta.com\/pt\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#primaryimage"},"image":{"@id":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#primaryimage"},"thumbnailUrl":"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","datePublished":"2024-07-13T09:55:38+00:00","dateModified":"2024-07-16T09:04:02+00:00","description":"Aprenda a criar um plugin WordPress extens\u00edvel que simplifica o processo de desenvolvimento para desenvolvedores terceiros.","breadcrumb":{"@id":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#breadcrumb"},"inLanguage":"pt-PT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/"]}]},{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#primaryimage","url":"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","contentUrl":"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","width":1460,"height":730},{"@type":"BreadcrumbList","@id":"https:\/\/kinqsta.com\/pt\/blog\/plugin-wordpress-extensivel\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinqsta.com\/pt\/"},{"@type":"ListItem","position":2,"name":"Plugins WordPress","item":"https:\/\/kinqsta.com\/pt\/topicos\/plugins-wordpress\/"},{"@type":"ListItem","position":3,"name":"Como Tornar um Plugin de WordPress Extens\u00edvel com Classes PHP"}]},{"@type":"WebSite","@id":"https:\/\/kinqsta.com\/pt\/#website","url":"https:\/\/kinqsta.com\/pt\/","name":"Kinsta\u00ae","description":"Solu\u00e7\u00f5es de hospedagem Premium, r\u00e1pida e segura","publisher":{"@id":"https:\/\/kinqsta.com\/pt\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinqsta.com\/pt\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"pt-PT"},{"@type":"Organization","@id":"https:\/\/kinqsta.com\/pt\/#organization","name":"Kinsta","url":"https:\/\/kinqsta.com\/pt\/","logo":{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinqsta.com\/pt\/#\/schema\/logo\/image\/","url":"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinqsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinqsta.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:\/\/kinqsta.com\/pt\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinqsta.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:\/\/kinqsta.com\/pt\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/posts\/68700","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/comments?post=68700"}],"version-history":[{"count":11,"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/posts\/68700\/revisions"}],"predecessor-version":[{"id":68977,"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/posts\/68700\/revisions\/68977"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinqsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/68700\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinqsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/68700\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinqsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/68700\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinqsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/68700\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinqsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/68700\/translations\/de"},{"embeddable":true,"hreflang":"ja","title":"Japanese","href":"https:\/\/kinqsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/68700\/translations\/jp"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinqsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/68700\/translations\/nl"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinqsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/68700\/translations\/es"},{"href":"https:\/\/kinqsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/68700\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/media\/68701"}],"wp:attachment":[{"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/media?parent=68700"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/tags?post=68700"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinqsta.com\/pt\/wp-json\/wp\/v2\/topic?post=68700"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}