{"id":76897,"date":"2024-03-04T14:11:09","date_gmt":"2024-03-04T13:11:09","guid":{"rendered":"https:\/\/kinsta.com\/it\/?p=76897&#038;preview=true&#038;preview_id=76897"},"modified":"2024-03-12T18:30:19","modified_gmt":"2024-03-12T17:30:19","slug":"versioni-gratuite-e-pro-plugin-wordpress","status":"publish","type":"post","link":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/","title":{"rendered":"Progettare un plugin di WordPress che supporti le estensioni"},"content":{"rendered":"<p>Nell&#8217;ecosistema WordPress, l&#8217;adozione di un <a href=\"https:\/\/kinsta.com\/it\/blog\/come-guadagnare-con-le-app\/\">modello freemium<\/a> \u00e8 un metodo prevalente per promuovere e monetizzare i plugin commerciali. Questo approccio prevede il rilascio gratuito di una versione di base del plugin, di solito attraverso la <a href=\"https:\/\/wordpress.org\/plugins\/\" target=\"_blank\" rel=\"noopener noreferrer\">directory dei plugin di WordPress<\/a>, e l&#8217;offerta di funzionalit\u00e0 avanzate attraverso una versione PRO o dei componenti aggiuntivi, di solito venduti sul sito web del plugin.<\/p>\n<p>Esistono tre modi diversi per integrare le funzionalit\u00e0 commerciali in un modello freemium:<\/p>\n<ol>\n<li>Inserire le funzionalit\u00e0 commerciali all&#8217;interno del plugin gratuito e attivarle solo quando la versione commerciale viene installata sul sito web o viene fornita una chiave di licenza commerciale.<\/li>\n<li>Creare le versioni gratuite e PRO come plugin indipendenti, con la versione PRO progettata per sostituire la versione gratuita, assicurando che sia installata una sola versione in qualsiasi momento.<\/li>\n<li>Installare la versione PRO accanto al plugin gratuito, estendendone le funzionalit\u00e0. Ci\u00f2 richiede la presenza di entrambe le versioni.<\/li>\n<\/ol>\n<p>Tuttavia, il primo approccio \u00e8 <a href=\"https:\/\/developer.wordpress.org\/plugins\/wordpress-org\/detailed-plugin-guidelines\/#5-trialware-is-not-permitted\" target=\"_blank\" rel=\"noopener noreferrer\">incompatibile con le linee guida<\/a> per i plugin distribuiti tramite la directory dei plugin di WordPress, in quanto queste regole vietano l&#8217;inclusione di funzionalit\u00e0 limitate o bloccate fino al pagamento o all&#8217;aggiornamento.<\/p>\n<p>Rimangono quindi le ultime due opzioni, che presentano vantaggi e svantaggi. Le sezioni seguenti spiegano perch\u00e9 l&#8217;ultima strategia, &#8220;PRO in aggiunta a free&#8221;, \u00e8 la scelta migliore.<\/p>\n<p>Partiremo approfondendo la seconda opzione, &#8220;PRO come sostituto di free&#8221;, i suoi difetti e i motivi per cui \u00e8 sconsigliata.<\/p>\n<p>Successivamente, analizzeremo in modo approfondito la strategia &#8220;PRO in aggiunta a free&#8221;, evidenziando il motivo per cui \u00e8 la scelta migliore.<\/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>Vantaggi della strategia &#8220;PRO come sostituto di free&#8221;<\/h2>\n<p>La strategia &#8220;PRO come sostituto di free&#8221; \u00e8 relativamente facile da implementare perch\u00e9 gli sviluppatori possono utilizzare un&#8217;unica base di codice per entrambi i plugin (free e PRO) e creare due output, con la versione free (o &#8220;standard&#8221;) che include semplicemente un sottoinsieme del codice e la versione PRO che include tutto il codice.<\/p>\n<p>Ad esempio, la base di codice del progetto potrebbe essere suddivisa nelle directory <code>standard\/<\/code> e <code>pro\/<\/code>. Il plugin caricherebbe sempre il codice standard, mentre il codice PRO verrebbe caricato in modo condizionale, in base alla presenza della rispettiva directory:<\/p>\n<pre><code class=\"language-php\">\/\/ Main plugin file: myplugin.php\n\n\/\/ Always load the standard plugin's code\nrequire_once __DIR__ . '\/standard\/load.php';\n\n\/\/ Load the PRO plugin's code only if the folder exists\n$proFolder = __DIR__ . '\/pro';\nif (file_exists($proFolder)) {\n  require_once $proFolder . '\/load.php';\n}\n<\/code><\/pre>\n<p>Quindi, quando si genera il plugin tramite uno strumento di <a href=\"https:\/\/kinsta.com\/it\/blog\/come-creare-una-pipeline-ci-cd\/\">integrazione continua<\/a>, possiamo creare le due risorse <code>myplugin-standard.zip<\/code> e <code>myplugin-pro.zip<\/code> dallo stesso codice sorgente.<\/p>\n<p>Se il progetto \u00e8 ospitato su GitHub e si generano le risorse tramite le GitHub Actions, il flusso di lavoro seguente svolge il suo compito:<\/p>\n<pre><code class=\"language-yaml\">name: Generate the standard and PRO plugins\non:\n  release:\n    types: [published]\n\njobs:\n  process:\n    name: Generate plugins\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v3\n\n      - name: Install zip\n        uses: montudor\/action-zip@v1.0.0\n\n      - name: Create the standard plugin .zip file (excluding all PRO code)\n        run: zip -X -r myplugin-standard.zip . -x **\/src\/\\pro\/\\*\n\n      - name: Create the PRO plugin .zip file\n        run: zip -X -r myplugin-pro.zip . -x myplugin-standard.zip\n\n      - name: Upload both plugins to the release page\n        uses: softprops\/action-gh-release@v1\n        with:\n          files: |\n            myplugin-standard.zip\n            myplugin-pro.zip\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n<h2>Problemi con la strategia &#8220;PRO come sostituto di free&#8221;<\/h2>\n<p>La strategia &#8220;PRO come sostituto di free&#8221; prevede la sostituzione del plugin gratuito con la versione PRO. Di conseguenza, se il plugin gratuito viene distribuito tramite la directory dei plugin di WordPress, il suo conteggio delle &#8220;installazioni attive&#8221; diminuir\u00e0 (poich\u00e9 tiene conto solo del plugin gratuito e non della versione PRO), dando l&#8217;impressione che il plugin sia meno popolare di quanto non sia in realt\u00e0.<\/p>\n<p>Questo risultato vanificherebbe lo scopo dell&#8217;utilizzo della directory dei plugin di WordPress come canale di scoperta dei plugin in cui gli utenti possono scoprire il nostro plugin, scaricarlo e installarlo. (Una volta installato, il plugin gratuito pu\u00f2 invitare gli utenti a passare alla versione PRO).<\/p>\n<p>Se il numero di installazioni attive non \u00e8 elevato, gli utenti potrebbero non essere convinti a installare il nostro plugin. Come esempio di come le cose possono andare male, i proprietari del plugin Newsletter Glue hanno deciso di <a href=\"https:\/\/wptavern.com\/newsletter-glue-closes-free-plugin-on-wordpress-org\" target=\"_blank\" rel=\"noopener noreferrer\">rimuoverlo dalla directory dei plugin di WordPress<\/a>, poich\u00e9 il basso numero di attivazioni stava danneggiando le prospettive del plugin.<\/p>\n<p>Poich\u00e9 la strategia &#8220;PRO come sostituto di free&#8221; non \u00e8 praticabile, ci rimane solo una scelta: la strategia &#8220;PRO in aggiunta a free&#8221;.<\/p>\n<p>Analizziamo i dettagli di questa strategia.<\/p>\n<h2>Concetto di strategia &#8220;PRO in aggiunta a free&#8221;<\/h2>\n<p>L&#8217;idea \u00e8 che il plugin gratuito venga installato sul sito e che le sue funzionalit\u00e0 possano essere estese installando altri plugin o addon. Questo pu\u00f2 avvenire tramite un singolo plugin PRO o tramite una serie di estensioni o addon PRO, ognuno dei quali fornisce una funzionalit\u00e0 specifica.<\/p>\n<p>In questa situazione, il plugin gratuito non si preoccupa di quali altri plugin siano installati sul sito. Tutto ci\u00f2 che fa \u00e8 fornire funzionalit\u00e0 aggiuntive. Questo modello \u00e8 versatile e consente l&#8217;espansione sia agli sviluppatori originali che ai creatori di terze parti, favorendo un ecosistema in cui un plugin pu\u00f2 evolvere in direzioni impreviste.<\/p>\n<p>Notate che non importa se le estensioni PRO saranno prodotte da noi (cio\u00e8 dagli stessi sviluppatori del plugin standard) o da qualcun altro: il codice per gestire entrambi \u00e8 lo stesso. Per questo motivo, \u00e8 una buona idea creare una base che non limiti le modalit\u00e0 di estensione del plugin. In questo modo, gli sviluppatori di terze parti potranno estendere il nostro plugin in modi che non avevamo previsto.<\/p>\n<h2>Approcci progettuali: hook e contenitori di servizi<\/h2>\n<p>Esistono due approcci principali per rendere estensibile il codice PHP:<\/p>\n<ol>\n<li>Tramite gli <a href=\"https:\/\/kinsta.com\/it\/blog\/hook-wordpress\/\">hook per le azioni e i filtri<\/a> di WordPress<\/li>\n<li>Tramite un <a href=\"https:\/\/kinsta.com\/it\/blog\/plugin-astrazione-wordpress\/#accessing-services-via-the-service-container\">contenitore di servizi<\/a><\/li>\n<\/ol>\n<p>Il primo approccio \u00e8 il pi\u00f9 comune tra gli sviluppatori di WordPress, mentre il secondo \u00e8 preferito dalla pi\u00f9 ampia <a href=\"https:\/\/kinsta.com\/php\/\">comunit\u00e0 PHP<\/a>.<\/p>\n<p>Vediamo alcuni esempi di entrambi.<\/p>\n<h2>Rendere il codice estensibile tramite hook per azioni e filtri<\/h2>\n<p>WordPress offre degli <a href=\"https:\/\/developer.wordpress.org\/plugins\/hooks\/\" target=\"_blank\" rel=\"noopener noreferrer\">hook<\/a> (filtri e azioni) come meccanismo per modificare il comportamento. Gli hook per i filtri sono utilizzati per sovrascrivere i valori e gli hook per le azioni per eseguire funzionalit\u00e0 personalizzate.<\/p>\n<p>Il nostro plugin principale pu\u00f2 essere &#8220;disseminato&#8221; di hook in tutto il suo codice, consentendo agli sviluppatori di modificarne il comportamento.<\/p>\n<p>Un buon esempio di ci\u00f2 \u00e8 <a href=\"https:\/\/kinsta.com\/it\/blog\/tutorial-woocommerce\/\">WooCommerce<\/a>, che ha dato vita a un enorme ecosistema di componenti aggiuntivi, la maggior parte dei quali \u00e8 di propriet\u00e0 di fornitori di terze parti. Questo \u00e8 possibile grazie all&#8217;<a href=\"https:\/\/woocommerce.github.io\/code-reference\/hooks\/hooks.html\" target=\"_blank\" rel=\"noopener noreferrer\">ampio numero di hook<\/a> offerti da questo plugin.<\/p>\n<p>Gli sviluppatori di WooCommerce hanno aggiunto di proposito gli hook, anche se loro stessi non ne avevano bisogno. Vengono, per\u00f2, utilizzati da altri. Notate il gran numero di hook per le azioni &#8220;prima&#8221; e &#8220;dopo&#8221;:<\/p>\n<ul>\n<li><code>woocommerce_after_account_downloads<\/code><\/li>\n<li><code>woocommerce_after_account_navigation<\/code><\/li>\n<li><code>woocommerce_after_account_orders<\/code><\/li>\n<li><code>woocommerce_after_account_payment_methods<\/code><\/li>\n<li><code>woocommerce_after_available_downloads<\/code><\/li>\n<li><code>woocommerce_after_cart<\/code><\/li>\n<li><code>woocommerce_after_cart_contents<\/code><\/li>\n<li><code>woocommerce_after_cart_item_name<\/code><\/li>\n<li><code>woocommerce_after_cart_table<\/code><\/li>\n<li><code>woocommerce_after_cart_totals<\/code><\/li>\n<li>&#8230;<\/li>\n<li><code>woocommerce_before_account_downloads<\/code><\/li>\n<li><code>woocommerce_before_account_navigation<\/code><\/li>\n<li><code>woocommerce_before_account_orders<\/code><\/li>\n<li><code>woocommerce_before_account_orders_pagination<\/code><\/li>\n<li><code>woocommerce_before_account_payment_methods<\/code><\/li>\n<li><code>woocommerce_before_available_downloads<\/code><\/li>\n<li><code>woocommerce_before_cart<\/code><\/li>\n<li><code>woocommerce_before_cart_collaterals<\/code><\/li>\n<li><code>woocommerce_before_cart_contents<\/code><\/li>\n<li><code>woocommerce_before_cart_table<\/code><\/li>\n<li><code>woocommerce_before_cart_totals<\/code><\/li>\n<li>&#8230;<\/li>\n<\/ul>\n<p>Ad esempio, il file <a href=\"https:\/\/woocommerce.github.io\/code-reference\/files\/woocommerce-templates-myaccount-downloads.html#source-view.27\" target=\"_blank\" rel=\"noopener noreferrer\"><code>downloads.php<\/code><\/a> contiene diverse azioni per iniettare funzionalit\u00e0 extra e l&#8217;URL del negozio pu\u00f2 essere sovrascritto tramite un filtro:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\n$downloads     = WC()-&gt;customer-&gt;get_downloadable_products();\n$has_downloads = (bool) $downloads;\n\ndo_action( 'woocommerce_before_account_downloads', $has_downloads ); ?&gt;\n\n&lt;?php if ( $has_downloads ) : ?&gt;\n\n  &lt;?php do_action( 'woocommerce_before_available_downloads' ); ?&gt;\n\n  &lt;?php do_action( 'woocommerce_available_downloads', $downloads ); ?&gt;\n\n  &lt;?php do_action( 'woocommerce_after_available_downloads' ); ?&gt;\n\n&lt;?php else : ?&gt;\n\n  &lt;?php\n\n  $wp_button_class = wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '';\n  wc_print_notice( esc_html__( 'No downloads available yet.', 'woocommerce' ) . ' &lt;a class=\"button wc-forward' . esc_attr( $wp_button_class ) . '\" href=\"' . esc_url( apply_filters( 'woocommerce_return_to_shop_redirect', wc_get_page_permalink( 'shop' ) ) ) . '\"&gt;' . esc_html__( 'Browse products', 'woocommerce' ) . '&lt;\/a&gt;', 'notice' );\n  ?&gt;\n\n&lt;?php endif; ?&gt;\n\n&lt;?php do_action( 'woocommerce_after_account_downloads', $has_downloads ); ?&gt;\n<\/code><\/pre>\n<h2>Rendere il codice estensibile tramite i service container<\/h2>\n<p>Un service container, o contenitore di servizi, \u00e8 un oggetto PHP che ci aiuta a gestire l&#8217;istanziazione di tutte le classi del progetto, comunemente offerto come parte di una libreria &#8220;dependency injection&#8221;.<\/p>\n<p>L&#8217;<a href=\"https:\/\/kinsta.com\/it\/blog\/plugin-astrazione-wordpress\/#using-dependency-injection\">iniezione di dipendenze<\/a> \u00e8 una strategia che permette di unire tutte le parti dell&#8217;applicazione in modo decentralizzato: Le classi PHP vengono iniettate nell&#8217;applicazione tramite la configurazione e l&#8217;applicazione recupera le istanze di queste classi PHP tramite il contenitore di servizi.<\/p>\n<p>Esistono numerose librerie di dependency injection. Le seguenti sono quelle pi\u00f9 diffuse e sono intercambiabili poich\u00e9 soddisfano tutte la <a href=\"https:\/\/www.php-fig.org\/psr\/psr-11\/\" target=\"_blank\" rel=\"noopener noreferrer\">PSR-11<\/a> (raccomandazione dello standard PHP) che descrive i contenitori di dependency injection:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/symfony\/dependency-injection\" target=\"_blank\" rel=\"noopener noreferrer\">Symfony\u2019s DependencyInjection<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/PHP-DI\/PHP-DI\" target=\"_blank\" rel=\"noopener noreferrer\">PHP-DI<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/silexphp\/Pimple\" target=\"_blank\" rel=\"noopener noreferrer\">Pimple<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/thephpleague\/container\" target=\"_blank\" rel=\"noopener noreferrer\">Container (Dependency Injection)<\/a><\/li>\n<\/ul>\n<p><a href=\"https:\/\/kinsta.com\/it\/blog\/framework-laravel\/\">Laravel<\/a> contiene anche un <a href=\"https:\/\/laravel.com\/docs\/10.x\/container\" target=\"_blank\" rel=\"noopener noreferrer\">service container<\/a> gi\u00e0 integrato nell&#8217;applicazione.<\/p>\n<p>Utilizzando la dependency injection, il plugin gratuito non ha bisogno di sapere in anticipo quali classi PHP sono presenti in fase di esecuzione: richiede semplicemente le istanze di tutte le classi al contenitore di servizi. Mentre molte classi PHP sono fornite dal plugin gratuito stesso per soddisfare le sue funzionalit\u00e0, altre sono fornite da qualsiasi componente aggiuntivo installato sul sito per estendere le funzionalit\u00e0.<\/p>\n<p>Un buon esempio di utilizzo di un contenitore di servizi \u00e8 <a href=\"https:\/\/wordpress.org\/plugins\/gatographql\/\" target=\"_blank\" rel=\"noopener noreferrer\">Gato GraphQL<\/a>, che si basa sulla libreria DependencyInjection di Symfony.<\/p>\n<p>Ecco <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/ce461a7155782823b9d14383e1036a75b564807c\/layers\/Engine\/packages\/root\/src\/Container\/ContainerBuilderFactoryTrait.php\" target=\"_blank\" rel=\"noopener noreferrer\">come viene istanziato il contenitore di servizi<\/a>:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace GatoGraphQL\\Container;\n\nuse Symfony\\Component\\Config\\ConfigCache;\nuse Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface;\nuse Symfony\\Component\\DependencyInjection\\Dumper\\PhpDumper;\n\ntrait ContainerBuilderFactoryTrait\n{\n  protected ContainerInterface $instance;\n  protected bool $cacheContainerConfiguration;\n  protected bool $cached;\n  protected string $cacheFile;\n\n  \/**\n   * Initialize the Container Builder.\n   * If the directory is not provided, store the\n   * cache in a system temp dir\n   *\/\n  public function init(\n    bool $cacheContainerConfiguration,\n    string $namespace,\n    string $directory\n  ): void {\n    $this-&gt;cacheContainerConfiguration = $cacheContainerConfiguration;\n\n    if ($this-&gt;cacheContainerConfiguration) {\n      if (!$directory) {\n        $directory = sys_get_temp_dir() . \\DIRECTORY_SEPARATOR . 'container-cache';\n      }\n      $directory .= \\DIRECTORY_SEPARATOR . $namespace;\n      if (!is_dir($directory)) {\n        @mkdir($directory, 0777, true);\n      }\n      \n      \/\/ Store the cache under this file\n      $this-&gt;cacheFile = $directory . 'container.php';\n\n      $containerConfigCache = new ConfigCache($this-&gt;cacheFile, false);\n      $this-&gt;cached = $containerConfigCache-&gt;isFresh();\n    } else {\n      $this-&gt;cached = false;\n    }\n\n    \/\/ If not cached, then create the new instance\n    if (!$this-&gt;cached) {\n      $this-&gt;instance = new ContainerBuilder();\n    } else {\n      require_once $this-&gt;cacheFile;\n      \/** @var class-string&lt;ContainerBuilder&gt; *\/\n      $containerFullyQuantifiedClass = \"\\\\GatoGraphQL\\\\ServiceContainer\";\n      $this-&gt;instance = new $containerFullyQuantifiedClass();\n    }\n  }\n\n  public function getInstance(): ContainerInterface\n  {\n    return $this-&gt;instance;\n  }\n\n  \/**\n   * If the container is not cached, then compile it and cache it\n   *\n   * @param CompilerPassInterface[] $compilerPasses Compiler Pass objects to register on the container\n   *\/\n  public function maybeCompileAndCacheContainer(\n    array $compilerPasses = []\n  ): void {\n    \/**\n     * Compile Symfony's DependencyInjection Container Builder.\n     *\n     * After compiling, cache it in disk for performance.\n     *\n     * This happens only the first time the site is accessed\n     * on the current server.\n     *\/\n    if ($this-&gt;cached) {\n      return;\n    }\n\n    \/** @var ContainerBuilder *\/\n    $containerBuilder = $this-&gt;getInstance();\n    foreach ($compilerPasses as $compilerPass) {\n      $containerBuilder-&gt;addCompilerPass($compilerPass);\n    }\n\n    \/\/ Compile the container.\n    $containerBuilder-&gt;compile();\n\n    \/\/ Cache the container\n    if (!$this-&gt;cacheContainerConfiguration) {\n      return;\n    }\n    \n    \/\/ Create the folder if it doesn't exist, and check it was successful\n    $dir = dirname($this-&gt;cacheFile);\n    $folderExists = file_exists($dir);\n    if (!$folderExists) {\n      $folderExists = @mkdir($dir, 0777, true);\n      if (!$folderExists) {\n        return;\n      }\n    }\n\n    \/\/ Save the container to disk\n    $dumper = new PhpDumper($containerBuilder);\n    file_put_contents(\n      $this-&gt;cacheFile,\n      $dumper-&gt;dump(\n        [\n          'class' =&gt; 'ServiceContainer',\n          'namespace' =&gt; 'GatoGraphQL',\n        ]\n      )\n    );\n\n    \/\/ Change the permissions so it can be modified by external processes\n    chmod($this-&gt;cacheFile, 0777);\n  }\n}\n<\/code><\/pre>\n<p>Si noti che il contenitore di servizi (accessibile come oggetto PHP con la classe <code>GatoGraphQLServiceContainer<\/code>) viene generato la prima volta che il plugin viene eseguito e poi memorizzato su disco (come file <code>container.php<\/code> in una cartella temp del sistema). Questo perch\u00e9 la generazione del contenitore di servizi \u00e8 un processo costoso che potrebbe richiedere diversi secondi per essere completato.<\/p>\n<p>In seguito, sia il plugin principale che tutte le sue estensioni <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/fd73bf65cd30e164ffaa7860861974cf53683d51\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/config\/services.yaml\" target=\"_blank\" rel=\"noopener noreferrer\">definiscono quali servizi iniettare nel contenitore tramite un file di configurazione<\/a>:<\/p>\n<pre><code class=\"language-yaml\">services:\n  _defaults:\n    public: true\n    autowire: true\n    autoconfigure: true\n\n  GatoGraphQL\\GatoGraphQL\\Registries\\ModuleTypeRegistryInterface:\n    class: \\GatoGraphQL\\GatoGraphQL\\Registries\\ModuleTypeRegistry\n\n  GatoGraphQL\\GatoGraphQL\\Log\\LoggerInterface:\n    class: \\GatoGraphQL\\GatoGraphQL\\Log\\Logger\n\n  GatoGraphQL\\GatoGraphQL\\Services\\:\n    resource: ..\/src\/Services\/*\n\n  GatoGraphQL\\GatoGraphQL\\State\\:\n    resource: '..\/src\/State\/*'\n\n<\/code><\/pre>\n<p>Possiamo istanziare oggetti per classi specifiche (come <code>GatoGraphQL\\GatoGraphQL\\Log\\Logger<\/code>, a cui si accede tramite l&#8217;interfaccia del contratto <code>GatoGraphQL\\GatoGraphQL\\Log\\LoggerInterface<\/code>) e possiamo anche indicare &#8220;istanziare tutte le classi sotto una certa directory&#8221; (come tutti i servizi sotto <code>..\/src\/Services<\/code>).<\/p>\n<p>Infine, <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/409eb230267021735e3326657c9929e94fa08b85\/layers\/Engine\/packages\/root\/src\/Module\/InitializeContainerServicesInModuleTrait.php\" target=\"_blank\" rel=\"noopener noreferrer\">iniettiamo la configurazione nel contenitore del servizio<\/a>:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace PoP\\Root\\Module;\n\nuse PoP\\Root\\App;\nuse Symfony\\Component\\Config\\FileLocator;\nuse Symfony\\Component\\DependencyInjection\\ContainerBuilder;\nuse Symfony\\Component\\DependencyInjection\\Loader\\YamlFileLoader;\n\ntrait InitializeContainerServicesInModuleTrait\n{\n  \/\/ Initialize the services defined in the YAML configuration file.\n  public function initServices(\n    string $dir,\n    string $serviceContainerConfigFileName\n  ): void {\n    \/\/ First check if the container has been cached. If so, do nothing\n    if (App::getContainerBuilderFactory()-&gt;isCached()) {\n      return;\n    }\n\n    \/\/ Initialize the ContainerBuilder with this module's service implementations\n    \/** @var ContainerBuilder *\/\n    $containerBuilder = App::getContainer();\n    $loader = new YamlFileLoader($containerBuilder, new FileLocator($dir));\n    $loader-&gt;load($serviceContainerConfigFileName);\n  }\n}\n<\/code><\/pre>\n<p>I servizi iniettati nel contenitore possono essere configurati per essere inizializzati sempre o solo quando richiesto (modalit\u00e0 lazy).<\/p>\n<p>Ad esempio, per rappresentare un tipo di post personalizzato, il plugin ha la classe <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/04058ffae6700e3e34e529ebd709247d98106fed\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/Services\/CustomPostTypes\/AbstractCustomPostType.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AbstractCustomPostType<\/code><\/a>il cui metodo <code>initialize<\/code> esegue la logica per inizializzarlo secondo WordPress:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace GatoGraphQL\\GatoGraphQL\\Services\\CustomPostTypes;\n\nuse GatoGraphQL\\GatoGraphQL\\Services\\Taxonomies\\TaxonomyInterface;\nuse PoP\\Root\\Services\\AbstractAutomaticallyInstantiatedService;\n\nabstract class AbstractCustomPostType extends AbstractAutomaticallyInstantiatedService implements CustomPostTypeInterface\n{\n  public function initialize(): void\n  {\n    \\add_action(\n      'init',\n      $this-&gt;initCustomPostType(...)\n    );\n  }\n\n  \/**\n   * Register the post type\n   *\/\n  public function initCustomPostType(): void\n  {\n    \\register_post_type($this-&gt;getCustomPostType(), $this-&gt;getCustomPostTypeArgs());\n  }\n\n  abstract public function getCustomPostType(): string;\n\n  \/**\n   * Arguments for registering the post type\n   *\n   * @return array&lt;string,mixed&gt;\n   *\/\n  protected function getCustomPostTypeArgs(): array\n  {\n    \/** @var array&lt;string,mixed&gt; *\/\n    $postTypeArgs = [\n      'public' =&gt; $this-&gt;isPublic(),\n      'publicly_queryable' =&gt; $this-&gt;isPubliclyQueryable(),\n      'label' =&gt; $this-&gt;getCustomPostTypeName(),\n      'labels' =&gt; $this-&gt;getCustomPostTypeLabels($this-&gt;getCustomPostTypeName(), $this-&gt;getCustomPostTypePluralNames(true), $this-&gt;getCustomPostTypePluralNames(false)),\n      'capability_type' =&gt; 'post',\n      'hierarchical' =&gt; $this-&gt;isAPIHierarchyModuleEnabled() && $this-&gt;isHierarchical(),\n      'exclude_from_search' =&gt; true,\n      'show_in_admin_bar' =&gt; $this-&gt;showInAdminBar(),\n      'show_in_nav_menus' =&gt; true,\n      'show_ui' =&gt; true,\n      'show_in_menu' =&gt; true,\n      'show_in_rest' =&gt; true,\n    ];\n    return $postTypeArgs;\n  }\n\n  \/**\n   * Labels for registering the post type\n   *\n   * @param string $name_uc Singular name uppercase\n   * @param string $names_uc Plural name uppercase\n   * @param string $names_lc Plural name lowercase\n   * @return array&lt;string,string&gt;\n   *\/\n  protected function getCustomPostTypeLabels(string $name_uc, string $names_uc, string $names_lc): array\n  {\n    return array(\n      'name'         =&gt; $names_uc,\n      'singular_name'    =&gt; $name_uc,\n      'add_new'      =&gt; sprintf(\\__('Add New %s', 'gatographql'), $name_uc),\n      'add_new_item'     =&gt; sprintf(\\__('Add New %s', 'gatographql'), $name_uc),\n      'edit_item'      =&gt; sprintf(\\__('Edit %s', 'gatographql'), $name_uc),\n      'new_item'       =&gt; sprintf(\\__('New %s', 'gatographql'), $name_uc),\n      'all_items'      =&gt; $names_uc,\/\/sprintf(\\__('All %s', 'gatographql'), $names_uc),\n      'view_item'      =&gt; sprintf(\\__('View %s', 'gatographql'), $name_uc),\n      'search_items'     =&gt; sprintf(\\__('Search %s', 'gatographql'), $names_uc),\n      'not_found'      =&gt; sprintf(\\__('No %s found', 'gatographql'), $names_lc),\n      'not_found_in_trash' =&gt; sprintf(\\__('No %s found in Trash', 'gatographql'), $names_lc),\n      'parent_item_colon'  =&gt; sprintf(\\__('Parent %s:', 'gatographql'), $name_uc),\n    );\n  }\n}\n<\/code><\/pre>\n<p>Quindi, la classe <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/c870d8906ae1aec3c81acc039c53acc7aab5dff0\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/Services\/CustomPostTypes\/GraphQLCustomEndpointCustomPostType.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>GraphQLCustomEndpointCustomPostType.php<\/code><\/a> \u00e8 un&#8217;implementazione di un tipo di post personalizzato. Dopo essere stata iniettata come servizio nel contenitore, viene istanziata e registrata in WordPress:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace GatoGraphQL\\GatoGraphQL\\Services\\CustomPostTypes;\n\nclass GraphQLCustomEndpointCustomPostType extends AbstractCustomPostType\n{\n  public function getCustomPostType(): string\n  {\n    return 'graphql-endpoint';\n  }\n\n  protected function getCustomPostTypeName(): string\n  {\n    return \\__('GraphQL custom endpoint', 'gatographql');\n  }\n}\n<\/code><\/pre>\n<p>Questa classe \u00e8 presente nel plugin gratuito e altre classi di tipi di post personalizzati, che si estendono in modo simile da <code>AbstractCustomPostType<\/code>, sono fornite dalle estensioni PRO.<\/p>\n<h2>Confronto tra hook e contenitori di servizi<\/h2>\n<p>Confrontiamo i due approcci progettuali.<\/p>\n<p>Il lato positivo di hook per azioni e filtri \u00e8 che si tratta di un metodo pi\u00f9 semplice, in quanto le sue funzionalit\u00e0 fanno parte del nucleo di WordPress. Inoltre, qualsiasi sviluppatore che lavori con WordPress sa gi\u00e0 come gestire gli hook, quindi la curva di apprendimento \u00e8 bassa.<\/p>\n<p>Tuttavia, la sua logica \u00e8 legata al nome dell&#8217;hook, che \u00e8 una stringa e, in quanto tale, pu\u00f2 portare a dei bug: se il nome dell&#8217;hook viene modificato, la logica dell&#8217;estensione viene interrotta. Tuttavia, lo sviluppatore potrebbe non accorgersi del problema perch\u00e9 il codice PHP continua a essere compilato.<\/p>\n<p>Di conseguenza, gli hook deprecati tendono a rimanere per molto tempo nella base di codice, forse addirittura per sempre. Il progetto accumula quindi codice obsoleto che non pu\u00f2 essere rimosso per paura di &#8220;rompere&#8221; le estensioni.<\/p>\n<p>Tornando a WooCommerce, questa situazione \u00e8 evidenziata nel file <a href=\"https:\/\/woocommerce.github.io\/code-reference\/files\/woocommerce-templates-myaccount-dashboard.html#source-view.65\" target=\"_blank\" rel=\"noopener noreferrer\"><code>dashboard.php<\/code><\/a> (si noti come gli hook deprecati siano conservati dalla versione <code>2.6<\/code>, mentre l&#8217;ultima versione attuale \u00e8 <code>8.5<\/code>):<\/p>\n<pre><code class=\"language-php\">&lt;?php\n  \/**\n   * My Account dashboard.\n   *\n   * @since 2.6.0\n   *\/\n  do_action( 'woocommerce_account_dashboard' );\n\n  \/**\n   * Deprecated woocommerce_before_my_account action.\n   *\n   * @deprecated 2.6.0\n   *\/\n  do_action( 'woocommerce_before_my_account' );\n\n  \/**\n   * Deprecated woocommerce_after_my_account action.\n   *\n   * @deprecated 2.6.0\n   *\/\n  do_action( 'woocommerce_after_my_account' );\n<\/code><\/pre>\n<p>L&#8217;utilizzo di un contenitore di servizi ha lo svantaggio di richiedere una libreria esterna, che aggiunge ulteriore complessit\u00e0. Inoltre, <a href=\"https:\/\/gatographql.com\/blog\/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper\/#heading-php-scoper-the-easy-way-%F0%9F%98%8E\" target=\"_blank\" rel=\"noopener noreferrer\">questa libreria deve essere sottoposta a scoping<\/a> (utilizzando <a href=\"https:\/\/github.com\/humbug\/php-scoper\" target=\"_blank\" rel=\"noopener noreferrer\">PHP-Scoper<\/a> o <a href=\"https:\/\/github.com\/BrianHenryIE\/strauss\" target=\"_blank\" rel=\"noopener noreferrer\">Strauss<\/a>) per paura che una versione diversa della stessa libreria sia installata da un altro plugin sullo stesso sito, il che potrebbe generare conflitti.<\/p>\n<p>L&#8217;utilizzo di un contenitore di servizi \u00e8 senza dubbio pi\u00f9 difficile da implementare e richiede tempi di sviluppo pi\u00f9 lunghi.<\/p>\n<p>Il lato positivo \u00e8 che i contenitori di servizi si occupano di classi PHP senza dover accoppiare la logica a qualche stringa. Questo fa s\u00ec che il progetto utilizzi un maggior numero di <a href=\"https:\/\/phptherightway.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">best practice PHP<\/a> e che la base di codice sia pi\u00f9 facile da mantenere a lungo termine.<\/p>\n<h2>Riepilogo<\/h2>\n<p>Quando si crea un plugin per WordPress, \u00e8 bene che supporti le estensioni per consentire a noi (creatori del plugin) di offrire funzionalit\u00e0 commerciali e a chiunque altro di aggiungere funzionalit\u00e0 extra e, auspicabilmente, di creare un ecosistema incentrato sul plugin.<\/p>\n<p>In questo articolo abbiamo analizzato quali sono le considerazioni sull&#8217;architettura del progetto PHP per rendere il plugin estensibile. Come abbiamo appreso, possiamo scegliere tra due approcci progettuali: l&#8217;utilizzo di hook o di un contenitore di servizi. Abbiamo confrontato entrambi gli approcci, individuando i pregi e i difetti di ciascuno.<\/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>Nell&#8217;ecosistema WordPress, l&#8217;adozione di un modello freemium \u00e8 un metodo prevalente per promuovere e monetizzare i plugin commerciali. Questo approccio prevede il rilascio gratuito di una &#8230;<\/p>\n","protected":false},"author":196,"featured_media":76898,"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-76897","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>Progettare un plugin WordPress che supporti le estensioni - Kinsta\u00ae<\/title>\n<meta name=\"description\" content=\"Scopri come creare un plugin flessibile e scalabile che supporti le estensioni, migliori le funzionalit\u00e0 e massimizzi la compatibilit\u00e0.\" \/>\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\/versioni-gratuite-e-pro-plugin-wordpress\/\" \/>\n<meta property=\"og:locale\" content=\"it_IT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Progettare un plugin di WordPress che supporti le estensioni\" \/>\n<meta property=\"og:description\" content=\"Scopri come creare un plugin flessibile e scalabile che supporti le estensioni, migliori le funzionalit\u00e0 e massimizzi la compatibilit\u00e0.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/\" \/>\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-03-04T13:11:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-03-12T17:30:19+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1460\" \/>\n\t<meta property=\"og:image:height\" content=\"730\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Leonardo Losoviz\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:description\" content=\"Scopri come creare un plugin flessibile e scalabile che supporti le estensioni, migliori le funzionalit\u00e0 e massimizzi la compatibilit\u00e0.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\" \/>\n<meta name=\"twitter:creator\" content=\"@losoviz\" \/>\n<meta name=\"twitter:site\" content=\"@Kinsta_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\/versioni-gratuite-e-pro-plugin-wordpress\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinsta.com\/it\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"Progettare un plugin di WordPress che supporti le estensioni\",\"datePublished\":\"2024-03-04T13:11:09+00:00\",\"dateModified\":\"2024-03-12T17:30:19+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/\"},\"wordCount\":1849,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/it\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"inLanguage\":\"it-IT\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/\",\"url\":\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/\",\"name\":\"Progettare un plugin WordPress che supporti le estensioni - Kinsta\u00ae\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/it\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"datePublished\":\"2024-03-04T13:11:09+00:00\",\"dateModified\":\"2024-03-12T17:30:19+00:00\",\"description\":\"Scopri come creare un plugin flessibile e scalabile che supporti le estensioni, migliori le funzionalit\u00e0 e massimizzi la compatibilit\u00e0.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#breadcrumb\"},\"inLanguage\":\"it-IT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"contentUrl\":\"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"width\":1460,\"height\":730},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#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\":\"Progettare un plugin di WordPress che supporti le estensioni\"}]},{\"@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":"Progettare un plugin WordPress che supporti le estensioni - Kinsta\u00ae","description":"Scopri come creare un plugin flessibile e scalabile che supporti le estensioni, migliori le funzionalit\u00e0 e massimizzi la compatibilit\u00e0.","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\/versioni-gratuite-e-pro-plugin-wordpress\/","og_locale":"it_IT","og_type":"article","og_title":"Progettare un plugin di WordPress che supporti le estensioni","og_description":"Scopri come creare un plugin flessibile e scalabile che supporti le estensioni, migliori le funzionalit\u00e0 e massimizzi la compatibilit\u00e0.","og_url":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinstaitalia\/","article_published_time":"2024-03-04T13:11:09+00:00","article_modified_time":"2024-03-12T17:30:19+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"Scopri come creare un plugin flessibile e scalabile che supporti le estensioni, migliori le funzionalit\u00e0 e massimizzi la compatibilit\u00e0.","twitter_image":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.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\/versioni-gratuite-e-pro-plugin-wordpress\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinsta.com\/it\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"Progettare un plugin di WordPress che supporti le estensioni","datePublished":"2024-03-04T13:11:09+00:00","dateModified":"2024-03-12T17:30:19+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/"},"wordCount":1849,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/it\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","inLanguage":"it-IT","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/","url":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/","name":"Progettare un plugin WordPress che supporti le estensioni - Kinsta\u00ae","isPartOf":{"@id":"https:\/\/kinsta.com\/it\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","datePublished":"2024-03-04T13:11:09+00:00","dateModified":"2024-03-12T17:30:19+00:00","description":"Scopri come creare un plugin flessibile e scalabile che supporti le estensioni, migliori le funzionalit\u00e0 e massimizzi la compatibilit\u00e0.","breadcrumb":{"@id":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#breadcrumb"},"inLanguage":"it-IT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/"]}]},{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#primaryimage","url":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","contentUrl":"https:\/\/kinsta.com\/it\/wp-content\/uploads\/sites\/2\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","width":1460,"height":730},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/it\/blog\/versioni-gratuite-e-pro-plugin-wordpress\/#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":"Progettare un plugin di WordPress che supporti le estensioni"}]},{"@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\/76897","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=76897"}],"version-history":[{"count":6,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/posts\/76897\/revisions"}],"predecessor-version":[{"id":77008,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/posts\/76897\/revisions\/77008"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/76897\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/76897\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/76897\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/76897\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/76897\/translations\/de"},{"embeddable":true,"hreflang":"ja","title":"Japanese","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/76897\/translations\/jp"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/76897\/translations\/es"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/76897\/translations\/nl"},{"href":"https:\/\/kinsta.com\/it\/wp-json\/kinsta\/v1\/posts\/76897\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/media\/76898"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/media?parent=76897"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/tags?post=76897"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/it\/wp-json\/wp\/v2\/topic?post=76897"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}