{"id":69327,"date":"2024-03-04T13:57:24","date_gmt":"2024-03-04T12:57:24","guid":{"rendered":"https:\/\/kinsta.com\/de\/?p=69327&#038;preview=true&#038;preview_id=69327"},"modified":"2024-03-12T18:30:00","modified_gmt":"2024-03-12T17:30:00","slug":"pro-kostenlose-versionen-wordpress-plugin","status":"publish","type":"post","link":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/","title":{"rendered":"Ein WordPress-Plugin f\u00fcr die Unterst\u00fctzung von Erweiterungen entwickeln"},"content":{"rendered":"<p>Im WordPress-\u00d6kosystem ist das <a href=\"https:\/\/kinsta.com\/de\/blog\/wie-verdient-man-mit-apps-geld\/\">Freemium-Modell<\/a> eine g\u00e4ngige Methode, um kommerzielle Plugins zu bewerben und zu monetarisieren. Bei diesem Ansatz wird eine Basisversion des Plugins kostenlos ver\u00f6ffentlicht &#8211; in der Regel \u00fcber das <a href=\"https:\/\/wordpress.org\/plugins\/\" target=\"_blank\" rel=\"noopener noreferrer\">WordPress-Plugin-Verzeichnis<\/a> &#8211; underweiterte Funktionen \u00fcber eine PRO-Version oder Add-ons angeboten, die in der Regel auf der Website des Plugins verkauft werden.<\/p>\n<p>Es gibt drei verschiedene M\u00f6glichkeiten, kommerzielle Funktionen in ein Freemium-Modell zu integrieren:<\/p>\n<ol>\n<li>Binde die kommerziellen Funktionen in das kostenlose Plugin ein und aktiviere sie nur, wenn die kommerzielle Version auf der Website installiert ist oder ein kommerzieller Lizenzschl\u00fcssel bereitgestellt wird.<\/li>\n<li>Erstelle die kostenlose und die PRO-Version als unabh\u00e4ngige Plugins, wobei die PRO-Version die kostenlose Version ersetzen soll, um sicherzustellen, dass immer nur eine Version installiert ist.<\/li>\n<li>Installiere die PRO-Version neben dem kostenlosen Plugin und erweitere dessen Funktionen. Dazu m\u00fcssen beide Versionen vorhanden sein.<\/li>\n<\/ol>\n<p>Der erste Ansatz ist jedoch <a href=\"https:\/\/developer.wordpress.org\/plugins\/wordpress-org\/detailed-plugin-guidelines\/#5-trialware-is-not-permitted\" target=\"_blank\" rel=\"noopener noreferrer\">unvereinbar mit den Richtlinien<\/a> f\u00fcr Plugins, die \u00fcber das WordPress-Plugin-Verzeichnis vertrieben werden, da diese Regeln die Aufnahme von Funktionen verbieten, die eingeschr\u00e4nkt oder gesperrt sind, bis eine Zahlung oder ein Upgrade erfolgt ist.<\/p>\n<p>Damit bleiben uns die letzten beiden Optionen, die Vor- und Nachteile bieten. In den folgenden Abschnitten wird erkl\u00e4rt, warum die letzte Strategie, &#8222;PRO zus\u00e4tzlich zu kostenlos&#8220;, unsere beste Wahl ist.<\/p>\n<p>Die zweite Option, &#8222;PRO als Ersatz f\u00fcr kostenlos&#8220;, hat ihre M\u00e4ngel und ist nicht zu empfehlen.<\/p>\n<p>Anschlie\u00dfend gehen wir n\u00e4her auf die Strategie &#8222;PRO zus\u00e4tzlich zu kostenlos&#8220; ein und zeigen auf, warum sie die beste Wahl ist.<\/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>Vorteile der Strategie &#8222;PRO als Ersatz f\u00fcr kostenlos&#8220;<\/h2>\n<p>Die Strategie &#8222;PRO als Ersatz f\u00fcr kostenlos&#8220; ist relativ einfach umzusetzen, da die Entwickler\/innen eine einzige Codebasis f\u00fcr beide Plugins (Free und PRO) verwenden und daraus zwei Ausgaben erstellen k\u00f6nnen, wobei die Free-Version (oder &#8222;Standard&#8220;-Version) einfach eine Teilmenge des Codes enth\u00e4lt und die PRO-Version den gesamten Code umfasst.<\/p>\n<p>Zum Beispiel k\u00f6nnte die Codebasis des Projekts in die Verzeichnisse <code>standard\/<\/code> und <code>pro\/<\/code> aufgeteilt werden. Das Plugin w\u00fcrde immer den Standardcode laden, w\u00e4hrend der PRO-Code abh\u00e4ngig vom Vorhandensein des jeweiligen Verzeichnisses geladen wird:<\/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>Wenn wir das Plugin dann mit einem <a href=\"https:\/\/kinsta.com\/de\/blog\/ci-cd-pipeline-github-aktionen-erstellen\/\">Continuous Integration<\/a> Tool erstellen, k\u00f6nnen wir die beiden Assets <code>myplugin-standard.zip<\/code> und <code>myplugin-pro.zip<\/code> aus demselben Quellcode erzeugen.<\/p>\n<p>Wenn das Projekt auf GitHub gehostet wird und die Assets \u00fcber GitHub Actions erstellt werden, funktioniert der folgende Workflow:<\/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>Probleme mit der Strategie &#8222;PRO als Ersatz f\u00fcr kostenlos&#8220;<\/h2>\n<p>Die Strategie &#8222;PRO als Ersatz f\u00fcr kostenlose Plugin&#8220; verlangt, dass das kostenlose Plugin durch die PRO-Version ersetzt wird. Wenn das kostenlose Plugin \u00fcber das WordPress-Plugin-Verzeichnis verbreitet wird, sinkt die Anzahl der &#8222;aktiven Installationen&#8220; (da nur das kostenlose Plugin, nicht aber die PRO-Version erfasst wird), was den Eindruck erweckt, dass das Plugin weniger beliebt ist als es tats\u00e4chlich ist.<\/p>\n<p>Dies w\u00fcrde den Zweck des WordPress-Plugin-Verzeichnisses zunichte machen: Als Plugin-Entdeckungskanal, \u00fcber den Nutzer\/innen unser Plugin kennenlernen, herunterladen und installieren k\u00f6nnen. (Nach der Installation kann das kostenlose Plugin dann zum Upgrade auf die PRO-Version einladen).<\/p>\n<p>Wenn die Anzahl der aktiven Installationen nicht hoch ist, kann es sein, dass Nutzer\/innen nicht \u00fcberzeugt werden k\u00f6nnen, unser Plugin zu installieren. Ein Beispiel daf\u00fcr, wie es schief gehen kann, ist das Plugin &#8222;Newsletter Glue&#8220;, das aus <a href=\"https:\/\/wptavern.com\/newsletter-glue-closes-free-plugin-on-wordpress-org\" target=\"_blank\" rel=\"noopener noreferrer\">dem WordPress-Plugin-Verzeichnis entfernt wurde<\/a>, weil die niedrige Aktivierungszahl die Erfolgsaussichten des Plugins beeintr\u00e4chtigte.<\/p>\n<p>Da die Strategie &#8222;PRO als Ersatz f\u00fcr kostenlos&#8220; nicht praktikabel ist, bleibt uns nur eine M\u00f6glichkeit: die Strategie &#8222;PRO zus\u00e4tzlich zu kostenlos&#8220;.<\/p>\n<p>Schauen wir uns die Vor- und Nachteile dieser Strategie an.<\/p>\n<h2>Das Konzept der &#8222;PRO zus\u00e4tzlich zu kostenlos&#8220;-Strategie<\/h2>\n<p>Die Idee ist, dass das kostenlose Plugin auf der Website installiert wird und seine Funktionalit\u00e4t durch die Installation von zus\u00e4tzlichen Plugins oder Addons erweitert werden kann. Dies kann \u00fcber ein einzelnes PRO-Plugin oder \u00fcber eine Sammlung von PRO-Erweiterungen oder Addons geschehen, wobei jede von ihnen eine bestimmte Funktionalit\u00e4t bietet.<\/p>\n<p>In diesem Fall ist es f\u00fcr das kostenlose Plugin unerheblich, welche anderen Plugins auf der Website installiert sind. Es stellt lediglich zus\u00e4tzliche Funktionen zur Verf\u00fcgung. Dieses Modell ist vielseitig und erm\u00f6glicht Erweiterungen sowohl durch die urspr\u00fcnglichen Entwickler als auch durch Drittanbieter.<\/p>\n<p>Bitte beachte, dass es keine Rolle spielt, ob die PRO-Erweiterungen von uns (d. h. von denselben Entwicklern, die auch das Standard-Plugin erstellen) oder von jemand anderem entwickelt werden: Der Code f\u00fcr beide Varianten ist derselbe. Daher ist es eine gute Idee, eine Grundlage zu schaffen, die die Erweiterungsm\u00f6glichkeiten f\u00fcr das Plugin nicht einschr\u00e4nkt. Das erm\u00f6glicht es Entwicklern von Drittanbietern, unser Plugin auf eine Weise zu erweitern, an die wir nicht gedacht haben.<\/p>\n<h2>Design-Ans\u00e4tze: Hooks und Service-Container<\/h2>\n<p>Es gibt zwei Hauptans\u00e4tze, um PHP-Code erweiterbar zu machen:<\/p>\n<ol>\n<li>\u00dcber die <a href=\"https:\/\/kinsta.com\/de\/blog\/wordpress-hooks\/\">WordPress-Action- und Filter-Hooks<\/a><\/li>\n<li>\u00dcber einen <a href=\"https:\/\/kinsta.com\/de\/blog\/wordpress-abstraktions-plugins\/#accessing-services-via-the-service-container\">Service-Container<\/a><\/li>\n<\/ol>\n<p>Der erste Ansatz ist unter WordPress-Entwicklern am weitesten verbreitet, w\u00e4hrend der zweite Ansatz von der breiteren <a href=\"https:\/\/kinsta.com\/php\/\">PHP-Gemeinde<\/a> bevorzugt wird.<\/p>\n<p>Sehen wir uns Beispiele f\u00fcr beide an.<\/p>\n<h2>Code \u00fcber Action- und Filter-Hooks erweiterbar machen<\/h2>\n<p>WordPress bietet <a href=\"https:\/\/developer.wordpress.org\/plugins\/hooks\/\" target=\"_blank\" rel=\"noopener noreferrer\">Hooks<\/a> (Filter und Aktionen) als Mechanismus zur Verhaltens\u00e4nderung. Filter-Hooks werden verwendet, um Werte au\u00dfer Kraft zu setzen, und Action-Hooks, um benutzerdefinierte Funktionen auszuf\u00fchren.<\/p>\n<p>Unser Haupt-Plugin kann dann in seiner gesamten Codebasis mit Hooks &#8222;\u00fcbers\u00e4t&#8220; werden, damit Entwickler sein Verhalten \u00e4ndern k\u00f6nnen.<\/p>\n<p>Ein gutes Beispiel daf\u00fcr ist <a href=\"https:\/\/kinsta.com\/de\/blog\/woocommerce-tutorial\/\">WooCommerce<\/a>, das ein riesiges \u00d6kosystem von Add-ons umfasst, von denen die meisten von Drittanbietern stammen. Dies ist dank der <a href=\"https:\/\/woocommerce.github.io\/code-reference\/hooks\/hooks.html\" target=\"_blank\" rel=\"noopener noreferrer\">zahlreichen Hooks<\/a> m\u00f6glich, die dieses Plugin bietet.<\/p>\n<p>Die Entwickler von WooCommerce haben absichtlich Hooks hinzugef\u00fcgt, obwohl sie sie selbst nicht brauchen. Jemand anderes soll sie nutzen. Beachte die gro\u00dfe Anzahl von &#8222;Vorher&#8220;- und &#8222;Nachher&#8220;-Action-Hooks:<\/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>Zum Beispiel enth\u00e4lt die Datei <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> enth\u00e4lt mehrere Aktionen, um zus\u00e4tzliche Funktionen einzubauen, und die Shop-URL kann \u00fcber einen Filter \u00fcberschrieben werden:<\/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>Code \u00fcber Service-Container erweiterbar machen<\/h2>\n<p>Ein Service-Container ist ein PHP-Objekt, das uns hilft, die Instanziierung aller Klassen im Projekt zu verwalten, und das \u00fcblicherweise als Teil einer &#8222;Dependency Injection&#8220;-Bibliothek angeboten wird.<\/p>\n<p><a href=\"https:\/\/kinsta.com\/de\/blog\/wordpress-abstraktions-plugins\/#using-dependency-injection\">Dependency Injection<\/a> ist eine Strategie, die es erm\u00f6glicht, alle Teile der Anwendung dezentral zusammenzukleben: PHP-Klassen werden \u00fcber die Konfiguration in die Anwendung injiziert, und die Anwendung ruft Instanzen dieser PHP-Klassen \u00fcber den Service-Container ab.<\/p>\n<p>Es gibt eine Vielzahl von Bibliotheken f\u00fcr Dependency Injection. Die folgenden sind beliebt und austauschbar, da sie alle die <a href=\"https:\/\/www.php-fig.org\/psr\/psr-11\/\" target=\"_blank\" rel=\"noopener noreferrer\">PSR-11<\/a> (PHP-Standardempfehlung) erf\u00fcllen, die Container f\u00fcr Dependency Injection beschreibt:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/symfony\/dependency-injection\" target=\"_blank\" rel=\"noopener noreferrer\">DependencyInjection von Symfony<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/PHP-DI\/PHP-DI\" target=\"_blank\" rel=\"noopener noreferrer\">PHP-DI<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/silexphp\/Pimple\" target=\"_blank\" rel=\"noopener noreferrer\">Pimple<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/thephpleague\/container\" target=\"_blank\" rel=\"noopener noreferrer\">Container (Dependency Injection)<\/a><\/li>\n<\/ul>\n<p><a href=\"https:\/\/kinsta.com\/de\/blog\/was-ist-laravel\/\">Laravel<\/a> enth\u00e4lt auch einen <a href=\"https:\/\/laravel.com\/docs\/10.x\/container\" target=\"_blank\" rel=\"noopener noreferrer\">Service-Container<\/a>, der bereits in die Anwendung integriert ist.<\/p>\n<p>Durch Dependency Injection muss das kostenlose Plugin nicht im Voraus wissen, welche PHP-Klassen zur Laufzeit vorhanden sind: Es fordert einfach Instanzen aller Klassen beim Service-Container an. Viele PHP-Klassen werden vom kostenlosen Plugin selbst bereitgestellt, um seine Funktionen zu erf\u00fcllen, andere werden von den Addons bereitgestellt, die auf der Website installiert sind, um die Funktionen zu erweitern.<\/p>\n<p>Ein gutes Beispiel f\u00fcr die Verwendung eines Service-Containers ist <a href=\"https:\/\/wordpress.org\/plugins\/gatographql\/\" target=\"_blank\" rel=\"noopener noreferrer\">Gato GraphQL<\/a>, das auf die DependencyInjection-Bibliothek von Symfony zur\u00fcckgreift.<\/p>\n<p>So <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/ce461a7155782823b9d14383e1036a75b564807c\/layers\/Engine\/packages\/root\/src\/Container\/ContainerBuilderFactoryTrait.php\" target=\"_blank\" rel=\"noopener noreferrer\">wird der Service-Container instanziiert<\/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>Bitte beachte, dass der Service-Container (erreichbar unter PHP-Objekt mit der Klasse <code>GatoGraphQLServiceContainer<\/code>) bei der ersten Ausf\u00fchrung des Plugins erzeugt und dann auf der Festplatte zwischengespeichert wird (als Datei <code>container.php<\/code> in einem System-Temp-Ordner). Das liegt daran, dass die Erstellung des Service-Containers ein kostspieliger Prozess ist, der unter Umst\u00e4nden mehrere Sekunden dauern kann.<\/p>\n<p>Anschlie\u00dfend <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/fd73bf65cd30e164ffaa7860861974cf53683d51\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/config\/services.yaml\" target=\"_blank\" rel=\"noopener noreferrer\">legen<\/a> sowohl das Haupt-Plugin als auch alle seine Erweiterungen <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/fd73bf65cd30e164ffaa7860861974cf53683d51\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/config\/services.yaml\" target=\"_blank\" rel=\"noopener noreferrer\">\u00fcber eine Konfigurationsdatei fest, welche Dienste in den Container integriert werden sollen<\/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>Wir k\u00f6nnen Objekte f\u00fcr bestimmte Klassen instanziieren (z. B. <code>GatoGraphQL\\GatoGraphQL\\Log\\Logger<\/code>, auf die \u00fcber die Vertragsschnittstelle <code>GatoGraphQL\\GatoGraphQL\\Log\\LoggerInterface<\/code> zugegriffen wird), und wir k\u00f6nnen auch angeben, dass alle Klassen unter einem bestimmten Verzeichnis instanziiert werden sollen (z. B. alle Dienste unter <code>..\/src\/Services<\/code>).<\/p>\n<p>Schlie\u00dflich <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/409eb230267021735e3326657c9929e94fa08b85\/layers\/Engine\/packages\/root\/src\/Module\/InitializeContainerServicesInModuleTrait.php\" target=\"_blank\" rel=\"noopener noreferrer\">injizieren wir die Konfiguration in den Dienstcontainer<\/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>Die in den Container injizierten Dienste k\u00f6nnen so konfiguriert werden, dass sie immer oder nur bei Bedarf initialisiert werden (Lazy Mode).<\/p>\n<p>Um zum Beispiel einen benutzerdefinierten Beitragstyp zu repr\u00e4sentieren, hat das Plugin die Klasse <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>, deren Methode <code>initialize<\/code> die Logik zur Initialisierung gem\u00e4\u00df WordPress ausf\u00fchrt:<\/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>Dann ist die Klasse <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> eine Implementierung eines benutzerdefinierten Beitragstyps. Nachdem sie als Dienst in den Container injiziert wurde, wird sie instanziiert und in WordPress registriert:<\/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>Diese Klasse ist im kostenlosen Plugin enthalten und andere benutzerdefinierte Post-Type-Klassen, die ebenfalls auf <code>AbstractCustomPostType<\/code> basieren, werden von PRO-Erweiterungen bereitgestellt.<\/p>\n<h2>Vergleich von Hooks und Service-Containern<\/h2>\n<p>Vergleichen wir die beiden Designans\u00e4tze.<\/p>\n<p>Der Vorteil von Action- und Filter-Hooks ist, dass sie die einfachere Methode sind, da ihre Funktionalit\u00e4t Teil des WordPress-Kerns ist. Und jeder Entwickler, der mit WordPress arbeitet, wei\u00df bereits, wie man mit Hooks umgeht, sodass die Lernkurve niedrig ist.<\/p>\n<p>Allerdings ist die Logik an einen Hook-Namen gebunden, der eine Zeichenkette ist, und kann daher zu Fehlern f\u00fchren: Wenn der Hook-Name ge\u00e4ndert wird, bricht die Logik der Erweiterung. Der Entwickler bemerkt das Problem jedoch m\u00f6glicherweise nicht, weil der PHP-Code weiterhin kompiliert wird.<\/p>\n<p>Daher werden veraltete Hooks oft sehr lange in der Codebasis aufbewahrt, vielleicht sogar f\u00fcr immer. Im Projekt sammelt sich dann veralteter Code an, der nicht entfernt werden kann, aus Angst, die Erweiterungen zu zerst\u00f6ren.<\/p>\n<p>Zur\u00fcck zu WooCommerce: Diese Situation wird in der Datei <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> belegt (beachte, dass die veralteten Hooks seit der Version <code>2.6<\/code> beibehalten werden, w\u00e4hrend die aktuellste Version <code>8.5<\/code> ist):<\/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>Die Verwendung eines Service-Containers hat den Nachteil, dass er eine externe Bibliothek ben\u00f6tigt, was die Komplexit\u00e4t weiter erh\u00f6ht. Au\u00dferdem <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\">muss diese Bibliothek gescannt werden<\/a> (mit <a href=\"https:\/\/github.com\/humbug\/php-scoper\" target=\"_blank\" rel=\"noopener noreferrer\">PHP-Scoper<\/a> oder <a href=\"https:\/\/github.com\/BrianHenryIE\/strauss\" target=\"_blank\" rel=\"noopener noreferrer\">Strauss<\/a>), da zu bef\u00fcrchten ist, dass eine andere Version derselben Bibliothek von einem anderen Plugin auf derselben Website installiert wird, was zu Konflikten f\u00fchren k\u00f6nnte.<\/p>\n<p>Die Verwendung eines Service-Containers ist zweifelsohne schwieriger zu implementieren und erfordert mehr Entwicklungszeit.<\/p>\n<p>Der Vorteil ist, dass Service-Container mit PHP-Klassen arbeiten, ohne dass die Logik an einen String gekoppelt werden muss. Das f\u00fchrt dazu, dass im Projekt mehr <a href=\"https:\/\/phptherightway.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">PHP-Best-Practices<\/a> verwendet werden, was langfristig zu einer leichter zu wartenden Codebasis f\u00fchrt.<\/p>\n<h2>Zusammenfassung<\/h2>\n<p>Wenn du ein Plugin f\u00fcr WordPress erstellst, ist es eine gute Idee, dass es Erweiterungen unterst\u00fctzt, damit wir (die Ersteller des Plugins) kommerzielle Funktionen anbieten k\u00f6nnen und auch jeder andere zus\u00e4tzliche Funktionen hinzuf\u00fcgen kann und so hoffentlich ein \u00d6kosystem rund um das Plugin entsteht.<\/p>\n<p>In diesem Artikel haben wir untersucht, welche \u00dcberlegungen es bez\u00fcglich der Architektur des PHP-Projekts gibt, um das Plugin erweiterbar zu machen. Wir haben gelernt, dass wir zwischen zwei Designans\u00e4tzen w\u00e4hlen k\u00f6nnen: der Verwendung von Hooks oder einem Service-Container. Wir haben beide Ans\u00e4tze miteinander verglichen und dabei die Vorz\u00fcge und Schw\u00e4chen beider Ans\u00e4tze herausgearbeitet.<\/p>\n<p><em>Hast du vor, dein WordPress-Plugin erweiterbar zu machen? Lass es uns in den Kommentaren wissen.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im WordPress-\u00d6kosystem ist das Freemium-Modell eine g\u00e4ngige Methode, um kommerzielle Plugins zu bewerben und zu monetarisieren. Bei diesem Ansatz wird eine Basisversion des Plugins kostenlos ver\u00f6ffentlicht &#8230;<\/p>\n","protected":false},"author":196,"featured_media":69328,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[],"topic":[999,1006],"class_list":["post-69327","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","topic-wordpress-entwicklung","topic-wordpress-plugins"],"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>WordPress-Plugins zur Unterst\u00fctzung von Erweiterungen - Kinsta\u00ae<\/title>\n<meta name=\"description\" content=\"Erfahre wie du ein flexibles, skalierbares Plugin erstellen, kannst das Erweiterungen unterst\u00fctzt, die Funktionalit\u00e4t verbessert und die Kompatibilit\u00e4t maximiert.\" \/>\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\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Ein WordPress-Plugin f\u00fcr die Unterst\u00fctzung von Erweiterungen entwickeln\" \/>\n<meta property=\"og:description\" content=\"Erfahre wie du ein flexibles, skalierbares Plugin erstellen, kannst das Erweiterungen unterst\u00fctzt, die Funktionalit\u00e4t verbessert und die Kompatibilit\u00e4t maximiert.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Kinsta-Deutschland-207459890108303\/\" \/>\n<meta property=\"article:published_time\" content=\"2024-03-04T12:57:24+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-03-12T17:30:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/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=\"Erfahre wie du ein flexibles, skalierbares Plugin erstellen, kannst das Erweiterungen unterst\u00fctzt, die Funktionalit\u00e4t verbessert und die Kompatibilit\u00e4t maximiert.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/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_DE\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leonardo Losoviz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"12\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinsta.com\/de\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"Ein WordPress-Plugin f\u00fcr die Unterst\u00fctzung von Erweiterungen entwickeln\",\"datePublished\":\"2024-03-04T12:57:24+00:00\",\"dateModified\":\"2024-03-12T17:30:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/\"},\"wordCount\":1792,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/de\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/\",\"url\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/\",\"name\":\"WordPress-Plugins zur Unterst\u00fctzung von Erweiterungen - Kinsta\u00ae\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/de\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"datePublished\":\"2024-03-04T12:57:24+00:00\",\"dateModified\":\"2024-03-12T17:30:00+00:00\",\"description\":\"Erfahre wie du ein flexibles, skalierbares Plugin erstellen, kannst das Erweiterungen unterst\u00fctzt, die Funktionalit\u00e4t verbessert und die Kompatibilit\u00e4t maximiert.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"contentUrl\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg\",\"width\":1460,\"height\":730},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/de\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"WordPress-Plugins\",\"item\":\"https:\/\/kinsta.com\/de\/thema\/wordpress-plugins\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Ein WordPress-Plugin f\u00fcr die Unterst\u00fctzung von Erweiterungen entwickeln\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/de\/#website\",\"url\":\"https:\/\/kinsta.com\/de\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Schnelle, sichere und hochwertige Hosting-L\u00f6sungen\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/de\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/de\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/de\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/de\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/kinsta.com\/de\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/de\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Kinsta-Deutschland-207459890108303\/\",\"https:\/\/x.com\/Kinsta_DE\",\"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\/de\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/kinsta.com\/de\/#\/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\/de\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"WordPress-Plugins zur Unterst\u00fctzung von Erweiterungen - Kinsta\u00ae","description":"Erfahre wie du ein flexibles, skalierbares Plugin erstellen, kannst das Erweiterungen unterst\u00fctzt, die Funktionalit\u00e4t verbessert und die Kompatibilit\u00e4t maximiert.","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\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/","og_locale":"de_DE","og_type":"article","og_title":"Ein WordPress-Plugin f\u00fcr die Unterst\u00fctzung von Erweiterungen entwickeln","og_description":"Erfahre wie du ein flexibles, skalierbares Plugin erstellen, kannst das Erweiterungen unterst\u00fctzt, die Funktionalit\u00e4t verbessert und die Kompatibilit\u00e4t maximiert.","og_url":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/Kinsta-Deutschland-207459890108303\/","article_published_time":"2024-03-04T12:57:24+00:00","article_modified_time":"2024-03-12T17:30:00+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"Erfahre wie du ein flexibles, skalierbares Plugin erstellen, kannst das Erweiterungen unterst\u00fctzt, die Funktionalit\u00e4t verbessert und die Kompatibilit\u00e4t maximiert.","twitter_image":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","twitter_creator":"@losoviz","twitter_site":"@Kinsta_DE","twitter_misc":{"Verfasst von":"Leonardo Losoviz","Gesch\u00e4tzte Lesezeit":"12\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinsta.com\/de\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"Ein WordPress-Plugin f\u00fcr die Unterst\u00fctzung von Erweiterungen entwickeln","datePublished":"2024-03-04T12:57:24+00:00","dateModified":"2024-03-12T17:30:00+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/"},"wordCount":1792,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/de\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/","url":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/","name":"WordPress-Plugins zur Unterst\u00fctzung von Erweiterungen - Kinsta\u00ae","isPartOf":{"@id":"https:\/\/kinsta.com\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","datePublished":"2024-03-04T12:57:24+00:00","dateModified":"2024-03-12T17:30:00+00:00","description":"Erfahre wie du ein flexibles, skalierbares Plugin erstellen, kannst das Erweiterungen unterst\u00fctzt, die Funktionalit\u00e4t verbessert und die Kompatibilit\u00e4t maximiert.","breadcrumb":{"@id":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#primaryimage","url":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","contentUrl":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2024\/03\/wp-architecting-a-wordpress-plugin-to-support-extensions.jpg","width":1460,"height":730},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/de\/blog\/pro-kostenlose-versionen-wordpress-plugin\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/de\/"},{"@type":"ListItem","position":2,"name":"WordPress-Plugins","item":"https:\/\/kinsta.com\/de\/thema\/wordpress-plugins\/"},{"@type":"ListItem","position":3,"name":"Ein WordPress-Plugin f\u00fcr die Unterst\u00fctzung von Erweiterungen entwickeln"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/de\/#website","url":"https:\/\/kinsta.com\/de\/","name":"Kinsta\u00ae","description":"Schnelle, sichere und hochwertige Hosting-L\u00f6sungen","publisher":{"@id":"https:\/\/kinsta.com\/de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/de\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/kinsta.com\/de\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/de\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Kinsta-Deutschland-207459890108303\/","https:\/\/x.com\/Kinsta_DE","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\/de\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/kinsta.com\/de\/#\/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\/de\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/posts\/69327","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/comments?post=69327"}],"version-history":[{"count":6,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/posts\/69327\/revisions"}],"predecessor-version":[{"id":69429,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/posts\/69327\/revisions\/69429"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/69327\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/69327\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/69327\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/69327\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/69327\/translations\/de"},{"embeddable":true,"hreflang":"ja","title":"Japanese","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/69327\/translations\/jp"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/69327\/translations\/es"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/69327\/translations\/nl"},{"href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/69327\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/media\/69328"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/media?parent=69327"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/tags?post=69327"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/topic?post=69327"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}