{"id":78043,"date":"2024-06-19T10:51:21","date_gmt":"2024-06-19T09:51:21","guid":{"rendered":"https:\/\/kinsta.com\/it\/?p=78043&#038;preview=true&#038;preview_id=78043"},"modified":"2024-06-19T14:45:01","modified_gmt":"2024-06-19T13:45:01","slug":"plugin-wordpress-estensibile","status":"publish","type":"post","link":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/","title":{"rendered":"Come rendere estensibile un plugin di WordPress con le classi PHP"},"content":{"rendered":"<p>I plugin di WordPress possono essere estesi con funzionalit\u00e0 aggiuntive, come dimostrano plugin popolari come <a href=\"https:\/\/kinsta.com\/it\/blog\/tutorial-woocommerce\/\">WooCommerce<\/a> e Gravity Forms. Nell&#8217;articolo &#8220;<a href=\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/\">Progettare un plugin WordPress che supporti le estensioni<\/a>&#8220;, abbiamo visto che esistono due modi principali per rendere estensibile un plugin WordPress:<\/p>\n<ol>\n<li>Creando degli hook (azioni e filtri) che permettano ai plugin di estendere le proprie funzionalit\u00e0.<\/li>\n<li>Fornendo delle classi PHP che i plugin di estensione possono ereditare.<\/li>\n<\/ol>\n<p>Il primo metodo si basa maggiormente sulla documentazione, che illustra in dettaglio gli hook disponibili e il loro utilizzo. Il secondo metodo, invece, offre codice pronto all&#8217;uso per le estensioni, riducendo la necessit\u00e0 di una documentazione esaustiva. Questo \u00e8 un vantaggio perch\u00e9 la creazione di documentazione accanto al codice pu\u00f2 complicare la gestione e il rilascio del plugin.<\/p>\n<p>La fornitura diretta di classi <a href=\"https:\/\/kinsta.com\/php\/\">PHP<\/a> sostituisce efficacemente la documentazione con il codice. Invece di insegnare come implementare una funzione, il plugin fornisce il codice PHP necessario, semplificando il compito agli sviluppatori di terze parti.<\/p>\n<p>Esploriamo alcune tecniche per raggiungere questo obiettivo, con l&#8217;obiettivo finale di promuovere un ecosistema di integrazioni intorno al nostro plugin 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>Definire le classi PHP di base nel plugin WordPress<\/h2>\n<p>Il <a href=\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress\/\">plugin di WordPress<\/a> includer\u00e0 classi PHP destinate all&#8217;utilizzo da parte dei plugin di estensione. Queste classi PHP potrebbero non essere utilizzate dal plugin principale, ma sono fornite appositamente per essere utilizzate da altri.<\/p>\n<p>Vediamo come questo viene implementato nel plugin open-source <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> rappresenta un plugin, sia per il plugin principale di Gato GraphQL che per le sue estensioni:<\/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> per rappresentare il plugin principale:<\/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>AbstractExtension:<\/h3>\n<p>Allo stesso modo, <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> per rappresentare un plugin di estensione:<\/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>Da notare che <code>AbstractExtension<\/code> \u00e8 incluso nel plugin principale e fornisce le funzionalit\u00e0 per registrare e inizializzare un&#8217;estensione. Tuttavia, viene utilizzato solo dalle estensioni e non dal plugin principale.<\/p>\n<p>La classe <code>AbstractPlugin<\/code> contiene codice di inizializzazione condiviso invocato in momenti diversi. Questi metodi sono definiti a livello di antenato ma vengono invocati dalle classi ereditarie in base al loro ciclo di vita.<\/p>\n<p>Il plugin principale e le estensioni vengono inizializzati eseguendo il metodo <code>setup<\/code> sulla classe corrispondente, invocato dal file principale del plugin WordPress.<\/p>\n<p>Ad esempio, in Gato GraphQL, questo avviene in <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>Metodo setup:<\/h3>\n<p>A livello di antenati, <code>setup<\/code> contiene la logica comune tra il plugin e le sue estensioni, come ad esempio la loro disiscrizione quando il plugin viene disattivato. Questo metodo non \u00e8 definitivo; pu\u00f2 essere sovrascritto dalle classi ereditarie per aggiungere le loro funzionalit\u00e0:<\/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>Metodo setup del plugin principale:<\/h3>\n<p>Il metodo <code>setup<\/code> del plugin principale inizializza il ciclo di vita dell&#8217;applicazione. Esegue le funzionalit\u00e0 del plugin principale attraverso metodi come <code>initialize<\/code>, <code>configureComponents<\/code>, <code>configure<\/code> e <code>boot<\/code> e attiva gli hook di azione corrispondenti per le estensioni:<\/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>Metodo di configurazione delle estensioni:<\/h3>\n<p>La classe <code>AbstractExtension<\/code> esegue la sua logica sugli hook corrispondenti:<\/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>I metodi <code>initialize<\/code>, <code>configureComponents<\/code>, <code>configure<\/code> e <code>boot<\/code> sono comuni sia al plugin principale che alle estensioni e possono condividere la logica. Questa logica condivisa \u00e8 memorizzata nella classe <code>AbstractPlugin<\/code>.<\/p>\n<p>Ad esempio, il metodo <code>configure<\/code> configura il plugin o le estensioni, chiamando <code>callPluginInitializationConfiguration<\/code>, che ha implementazioni diverse per il plugin principale e le estensioni ed \u00e8 definito come astratto e <code>getModuleClassConfiguration<\/code>, che fornisce un comportamento predefinito ma pu\u00f2 essere sovrascritto se necessario:<\/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>Il plugin principale fornisce la sua implementazione per <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>Allo stesso modo, la classe di estensione fornisce la sua implementazione:<\/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>I metodi <code>initialize<\/code>, <code>configureComponents<\/code> e <code>boot<\/code> sono definiti a livello di antenato e possono essere sovrascritti dalle classi ereditarie:<\/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>Tutti i metodi possono essere sovrascritti da <code>AbstractMainPlugin<\/code> o <code>AbstractExtension<\/code> per estenderli con le loro funzionalit\u00e0 personalizzate.<\/p>\n<p>Per il plugin principale, il metodo <code>setup<\/code> rimuove anche la cache dall&#8217;istanza di WordPress quando il plugin o una delle sue estensioni viene attivata o disattivata:<\/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>Allo stesso modo, il metodo <code>deactivate<\/code> rimuove la cache e <code>boot<\/code> esegue hook di azione aggiuntivi solo per il plugin principale:<\/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>Da tutto il codice presentato sopra, \u00e8 chiaro che quando si progetta e si codifica un plugin di WordPress, \u00e8 necessario considerare le esigenze delle sue estensioni e riutilizzare il codice il pi\u00f9 possibile. L&#8217;implementazione di validi modelli di <a href=\"https:\/\/kinsta.com\/it\/blog\/php-oop\/\">programmazione orientata agli oggetti<\/a> (come i principi <a href=\"https:\/\/dev.to\/evrtrabajo\/solid-in-php-d8e\" target=\"_blank\" rel=\"noopener noreferrer\">SOLID<\/a> ) aiuta a raggiungere questo obiettivo, rendendo la base di codice manutenibile a lungo termine.<\/p>\n<h2>Dichiarare e convalidare la dipendenza dalla versione<\/h2>\n<p>Poich\u00e9 l&#8217;estensione eredita da una classe PHP fornita dal plugin, \u00e8 fondamentale convalidare la presenza della versione richiesta del plugin. In caso contrario, potrebbero verificarsi conflitti che potrebbero causare il blocco del sito.<\/p>\n<p>Ad esempio, se la classe <code>AbstractExtension<\/code> viene aggiornata con modifiche di rottura e rilascia una versione maggiore <code>4.0.0<\/code> rispetto alla precedente <code>3.4.0<\/code>, caricare l&#8217;estensione senza verificare la versione potrebbe causare un errore PHP, impedendo il caricamento di WordPress.<\/p>\n<p>Per evitare questo problema, l&#8217;estensione deve convalidare che il plugin installato sia la versione <code>3.x.x<\/code>. Quando viene installata la versione <code>4.0.0<\/code>, l&#8217;estensione viene disabilitata, evitando cos\u00ec gli errori.<\/p>\n<p>L&#8217;estensione pu\u00f2 effettuare questa convalida utilizzando la seguente logica, eseguita sull&#8217;hook <code>plugins_loaded<\/code> (poich\u00e9 il plugin principale sar\u00e0 gi\u00e0 stato caricato) nel <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\">file del plugin principale dell&#8217;estensione<\/a>. Questa logica accede alla 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> che \u00e8 inclusa nel plugin principale per gestire le estensioni:<\/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>Da notare come l&#8217;estensione dichiari una dipendenza dal vincolo di versione <code>^1.0<\/code> del plugin principale (utilizzando i <a href=\"https:\/\/getcomposer.org\/doc\/articles\/versions.md#writing-version-constraints\" target=\"_blank\" rel=\"noopener noreferrer\">vincoli di versione di Composer<\/a>). Pertanto, quando viene installata la versione <code>2.0.0<\/code> di Gato GraphQL, l&#8217;estensione non verr\u00e0 attivata.<\/p>\n<p>Il vincolo di versione viene convalidato tramite il metodo <code>ExtensionManager::assertIsValid<\/code>, che chiama <code>Semver::satisfies<\/code> (fornito dal <a href=\"https:\/\/packagist.org\/packages\/composer\/semver\" target=\"_blank\" rel=\"noopener noreferrer\">pacchetto <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>Eseguire test di integrazione su un server WordPress<\/h2>\n<p>Per facilitare gli sviluppatori di terze parti nella creazione di estensioni per i vostri plugin, fornite loro strumenti per lo sviluppo e il test, compresi i flussi di lavoro per i loro processi di <a href=\"https:\/\/kinsta.com\/it\/blog\/test-automatizzati\/\">continuous integration e continuous delivery<\/a> (CI\/CD).<\/p>\n<p>Durante lo sviluppo, chiunque pu\u00f2 facilmente avviare un server web utilizzando <a href=\"https:\/\/kinsta.com\/it\/devkinsta\/\">DevKinsta<\/a>, installare il plugin per il quale si sta progettando l&#8217;estensione e convalidare immediatamente la compatibilit\u00e0 dell&#8217;estensione con il plugin.<\/p>\n<p>Per automatizzare i test durante il CI\/CD, \u00e8 necessario che il server web sia accessibile in rete al servizio CI\/CD. Servizi come InstaWP possono creare un sito sandbox con WordPress installato a questo scopo.<\/p>\n<p>Se la base di codice dell&#8217;estensione \u00e8 ospitata su <a href=\"https:\/\/kinsta.com\/it\/blog\/cosa-e-github\/\">GitHub<\/a>, gli sviluppatori possono utilizzare <a href=\"https:\/\/kinsta.com\/it\/blog\/come-creare-una-pipeline-ci-cd\/#what-is-github-actions\">GitHub Actions<\/a> per eseguire test di integrazione contro il servizio InstaWP. Il seguente flusso di lavoro installa l&#8217;<a href=\"https:\/\/github.com\/GatoGraphQL\/ExtensionStarter\/blob\/aa574c785a139d8ffc094b3b3703eb516156f77e\/.github\/workflows\/generate_plugins.yml\" target=\"_blank\" rel=\"noopener noreferrer\">estensione<\/a> su un sito sandbox InstaWP (insieme all&#8217;ultima versione stabile del plugin principale) e poi <a href=\"https:\/\/github.com\/GatoGraphQL\/ExtensionStarter\/blob\/a2d5d37ed205264f5ee03ce3ad075a08e89b67a7\/.github\/workflows\/integration_tests.yml\" target=\"_blank\" rel=\"noopener noreferrer\">esegue i test di integrazione<\/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>Questo flusso di lavoro accede al file <strong>.zip<\/strong> tramite <a href=\"https:\/\/nightly.link\" target=\"_blank\" rel=\"noopener noreferrer\">Nightly Link<\/a>, un servizio che consente di accedere a un artefatto da GitHub senza effettuare il login, semplificando la configurazione di InstaWP.<\/p>\n<h2>Rilasciare il plugin di estensione<\/h2>\n<p>Possiamo fornire strumenti per aiutare a rilasciare le estensioni, automatizzando il pi\u00f9 possibile le procedure.<\/p>\n<p><a href=\"https:\/\/github.com\/symplify\/monorepo-builder\" target=\"_blank\" rel=\"noopener noreferrer\">Monorepo Builder<\/a> \u00e8 una libreria per la gestione di qualsiasi progetto PHP, compreso un plugin WordPress. Fornisce il comando <code>monorepo-builder release<\/code> per rilasciare una versione del progetto, incrementando la componente maggiore, minore o patch della versione in base al <a href=\"https:\/\/semver.org\" target=\"_blank\" rel=\"noopener noreferrer\">versioning semantico<\/a>.<\/p>\n<p>Questo comando esegue una serie di <a href=\"https:\/\/github.com\/symplify\/monorepo-builder?tab=readme-ov-file#7-release-flow\" target=\"_blank\" rel=\"noopener noreferrer\">release worker<\/a>, ovvero classi PHP che eseguono una determinata logica. I worker predefiniti includono uno che crea un <code>git tag<\/code> con la nuova versione e un altro che invia il tag al repository remoto. I worker personalizzati possono essere iniettati prima, dopo o tra questi passaggi.<\/p>\n<p>I worker di rilascio sono configurati tramite un file di configurazione:<\/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>Possiamo fornire dei release worker personalizzati per aumentare il processo di rilascio in base alle esigenze di un plugin WordPress. Ad esempio, il file <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> imposta la nuova versione come voce &#8220;Stable tag&#8221; nel <a href=\"https:\/\/developer.wordpress.org\/plugins\/wordpress-org\/how-your-readme-txt-works\/\" target=\"_blank\" rel=\"noopener noreferrer\">file <strong>readme.txt<\/strong> dell&#8217;estensione<\/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>Aggiungendo <code>InjectStableTagVersionInPluginReadmeFileReleaseWorker<\/code> all&#8217;elenco di configurazione, ogni volta che si esegue il comando <code>monorepo-builder release<\/code> per rilasciare una nuova versione del plugin, il &#8220;tag Stable&#8221; nel file <strong>readme.txt<\/strong> dell&#8217;estensione verr\u00e0 aggiornato automaticamente.<\/p>\n<h2>Pubblicare il plugin di estensione nella directory di WP.org<\/h2>\n<p>Possiamo anche distribuire un workflow per aiutare a rilasciare l&#8217;estensione nella <a href=\"https:\/\/wordpress.org\/plugins\/\" target=\"_blank\" rel=\"noopener noreferrer\">directory dei plugin di WordPress<\/a>. Quando si assegna il tag al progetto sul repository remoto, il <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\">seguente flusso di lavoro<\/a> pubblicher\u00e0 il plugin di estensione WordPress nella directory:<\/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>Questo flusso di lavoro utilizza l&#8217;azione <a href=\"https:\/\/github.com\/10up\/action-wordpress-plugin-deploy\" target=\"_blank\" rel=\"noopener noreferrer\"><code>10up\/action-wordpress-plugin-deploy<\/code><\/a> che recupera il codice da un repository Git e lo invia al <a href=\"https:\/\/kinsta.com\/blog\/publish-plugin-wordpress-plugin-directory\/#plugin-structure\">repository SVN di WordPress.org<\/a>, semplificando l&#8217;operazione.<\/p>\n<h2>Riepilogo<\/h2>\n<p>Quando creiamo un plugin estensibile per WordPress, il nostro obiettivo \u00e8 quello di rendere il pi\u00f9 semplice possibile l&#8217;estensione da parte di sviluppatori terzi, massimizzando cos\u00ec le possibilit\u00e0 di promuovere un ecosistema vivace intorno ai nostri plugin.<\/p>\n<p>Sebbene fornire un&#8217;ampia documentazione possa guidare gli sviluppatori su come estendere il plugin, un approccio ancora pi\u00f9 efficace \u00e8 quello di fornire il codice PHP e gli strumenti necessari per sviluppare, testare e rilasciare le loro estensioni.<\/p>\n<p>Includendo il codice aggiuntivo necessario alle estensioni direttamente nel nostro plugin, semplifichiamo il processo per gli sviluppatori.<\/p>\n<p><em>Avete intenzione di rendere estensibile il vostro plugin per WordPress? Fatecelo sapere nella sezione commenti.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I plugin di WordPress possono essere estesi con funzionalit\u00e0 aggiuntive, come dimostrano plugin popolari come WooCommerce e Gravity Forms. Nell&#8217;articolo &#8220;Progettare un plugin WordPress che supporti &#8230;<\/p>\n","protected":false},"author":196,"featured_media":78044,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[],"topic":[25900,25873],"class_list":["post-78043","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","topic-plugin-wordpress","topic-sviluppo-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>Rendere estensibile un plugin di WordPress con le classi PHP - Kinsta\u00ae<\/title>\n<meta name=\"description\" content=\"Impara a creare un plugin estensibile per WordPress che semplifichi il processo di sviluppo per gli sviluppatori di terze parti.\" \/>\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\/it\/blog\/plugin-wordpress-estensibile\/\" \/>\n<meta property=\"og:locale\" content=\"it_IT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Come rendere estensibile un plugin di WordPress con le classi PHP\" \/>\n<meta property=\"og:description\" content=\"Impara a creare un plugin estensibile per WordPress che semplifichi il processo di sviluppo per gli sviluppatori di terze parti.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/kinstaitalia\/\" \/>\n<meta property=\"article:published_time\" content=\"2024-06-19T09:51:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-06-19T13:45:01+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/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=\"Impara a creare un plugin estensibile per WordPress che semplifichi il processo di sviluppo per gli sviluppatori di terze parti.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/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_IT\" \/>\n<meta name=\"twitter:label1\" content=\"Scritto da\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leonardo Losoviz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tempo di lettura stimato\" \/>\n\t<meta name=\"twitter:data2\" content=\"15 minuti\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinsta.com\/it\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"Come rendere estensibile un plugin di WordPress con le classi PHP\",\"datePublished\":\"2024-06-19T09:51:21+00:00\",\"dateModified\":\"2024-06-19T13:45:01+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/\"},\"wordCount\":1456,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/it\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"inLanguage\":\"it-IT\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/\",\"url\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/\",\"name\":\"Rendere estensibile un plugin di WordPress con le classi PHP - Kinsta\u00ae\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/it\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"datePublished\":\"2024-06-19T09:51:21+00:00\",\"dateModified\":\"2024-06-19T13:45:01+00:00\",\"description\":\"Impara a creare un plugin estensibile per WordPress che semplifichi il processo di sviluppo per gli sviluppatori di terze parti.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#breadcrumb\"},\"inLanguage\":\"it-IT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"contentUrl\":\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"width\":1460,\"height\":730},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/it\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Plugin WordPress\",\"item\":\"https:\/\/kinsta.com\/it\/argomenti\/plugin-wordpress\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Come rendere estensibile un plugin di WordPress con le classi PHP\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/it\/#website\",\"url\":\"https:\/\/kinsta.com\/it\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Soluzioni di hosting premium, veloci e sicure\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/it\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/it\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"it-IT\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/it\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/it\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\/\/kinsta.com\/it\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/it\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/kinstaitalia\/\",\"https:\/\/x.com\/Kinsta_IT\",\"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\/it\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\/\/kinsta.com\/it\/#\/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\/it\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Rendere estensibile un plugin di WordPress con le classi PHP - Kinsta\u00ae","description":"Impara a creare un plugin estensibile per WordPress che semplifichi il processo di sviluppo per gli sviluppatori di terze parti.","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\/it\/blog\/plugin-wordpress-estensibile\/","og_locale":"it_IT","og_type":"article","og_title":"Come rendere estensibile un plugin di WordPress con le classi PHP","og_description":"Impara a creare un plugin estensibile per WordPress che semplifichi il processo di sviluppo per gli sviluppatori di terze parti.","og_url":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinstaitalia\/","article_published_time":"2024-06-19T09:51:21+00:00","article_modified_time":"2024-06-19T13:45:01+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/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":"Impara a creare un plugin estensibile per WordPress che semplifichi il processo di sviluppo per gli sviluppatori di terze parti.","twitter_image":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions-1024x512.jpg","twitter_creator":"@losoviz","twitter_site":"@Kinsta_IT","twitter_misc":{"Scritto da":"Leonardo Losoviz","Tempo di lettura stimato":"15 minuti"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinsta.com\/it\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"Come rendere estensibile un plugin di WordPress con le classi PHP","datePublished":"2024-06-19T09:51:21+00:00","dateModified":"2024-06-19T13:45:01+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/"},"wordCount":1456,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/it\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","inLanguage":"it-IT","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/","url":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/","name":"Rendere estensibile un plugin di WordPress con le classi PHP - Kinsta\u00ae","isPartOf":{"@id":"https:\/\/kinsta.com\/it\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","datePublished":"2024-06-19T09:51:21+00:00","dateModified":"2024-06-19T13:45:01+00:00","description":"Impara a creare un plugin estensibile per WordPress che semplifichi il processo di sviluppo per gli sviluppatori di terze parti.","breadcrumb":{"@id":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#breadcrumb"},"inLanguage":"it-IT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/"]}]},{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#primaryimage","url":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","contentUrl":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","width":1460,"height":730},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/it\/blog\/plugin-wordpress-estensibile\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/it\/"},{"@type":"ListItem","position":2,"name":"Plugin WordPress","item":"https:\/\/kinsta.com\/it\/argomenti\/plugin-wordpress\/"},{"@type":"ListItem","position":3,"name":"Come rendere estensibile un plugin di WordPress con le classi PHP"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/it\/#website","url":"https:\/\/kinsta.com\/it\/","name":"Kinsta\u00ae","description":"Soluzioni di hosting premium, veloci e sicure","publisher":{"@id":"https:\/\/kinsta.com\/it\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/it\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"it-IT"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/it\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/it\/","logo":{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/kinsta.com\/it\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/it\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/kinstaitalia\/","https:\/\/x.com\/Kinsta_IT","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\/it\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/kinsta.com\/it\/#\/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\/it\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/posts\/78043","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/comments?post=78043"}],"version-history":[{"count":5,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/posts\/78043\/revisions"}],"predecessor-version":[{"id":78063,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/posts\/78043\/revisions\/78063"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/78043\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/78043\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/78043\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/78043\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/78043\/translations\/de"},{"embeddable":true,"hreflang":"ja","title":"Japanese","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/78043\/translations\/jp"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/78043\/translations\/nl"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/78043\/translations\/es"},{"href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/78043\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/media\/78044"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/media?parent=78043"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/tags?post=78043"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/topic?post=78043"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}