{"id":59657,"date":"2024-06-19T11:54:38","date_gmt":"2024-06-19T09:54:38","guid":{"rendered":"https:\/\/kinqsta.com\/nl\/?p=59657&#038;preview=true&#038;preview_id=59657"},"modified":"2024-07-01T15:40:07","modified_gmt":"2024-07-01T13:40:07","slug":"wordpress-plugin-uitbeidbaar-maken","status":"publish","type":"post","link":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/","title":{"rendered":"Zo maak je een WordPress plugin uitbreidbaar met PHP klassen"},"content":{"rendered":"<p>WordPress plugins kunnen worden uitgebreid met extra functionaliteit, zoals populaire plugins als <a href=\"https:\/\/kinqsta.com\/nl\/blog\/woocommerce-handleiding\/\">WooCommerce<\/a> en Gravity Forms laten zien. In het artikel\u00a0 <a href=\"https:\/\/kinqsta.com\/nl\/blog\/pro-gratis-versies-wordpress-plugin\/\">&#8220;Een WordPress plugin maken om extensies te ondersteunen&#8221;<\/a> leren we dat er twee manieren zijn om een WordPress plugin uitbreidbaar te maken:<\/p>\n<ol>\n<li>Door hooks\u00a0 (acties en filters) in te stellen waarmee uitbreidingsplugins hun eigen functionaliteit kunnen injecteren<\/li>\n<li>Door PHP klassen aan te bieden die uitbreidingsplugins kunnen erven (inheriten)<\/li>\n<\/ol>\n<p>De eerste methode is meer gebaseerd op documentatie, waarin de beschikbare hooks en hun gebruik gedetailleerd worden beschreven. De tweede methode biedt daarentegen kant-en-klare code voor extensies, waardoor er minder uitgebreide documentatie nodig is. Dit is voordelig omdat het maken van documentatie naast code het beheer en de uitgave van de plugin kan bemoeilijken.<\/p>\n<p>Het direct aanbieden van <a href=\"https:\/\/kinqsta.com\/php\/\">PHP<\/a> klassen vervangt documentatie effectief door code. In plaats van te leren hoe een functie ge\u00efmplementeerd moet worden, levert de plugin de benodigde PHP code, wat de taak voor externe ontwikkelaars vereenvoudigt.<\/p>\n<p>Laten we een aantal technieken verkennen om dit te bereiken, met als uiteindelijke doel het bevorderen van een ecosysteem van integraties rondom onze WordPress plugin.<\/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>PHP basisklassen defini\u00ebren in de WordPress plugin<\/h2>\n<p>De <a href=\"https:\/\/kinqsta.com\/blog\/wordpress-plugin\/\">WordPress plugin<\/a> zal PHP klassen bevatten die bedoeld zijn voor gebruik door uitbreidingsplugins. Deze PHP klassen worden wellicht niet gebruikt door de hoofdplugin zelf, maar worden specifiek geleverd voor gebruik door anderen.<\/p>\n<p>Laten we eens kijken hoe dit is ge\u00efmplementeerd in de open-source <a href=\"https:\/\/wordpress.org\/plugins\/gatographql\/\" target=\"_blank\" rel=\"noopener noreferrer\">Gato GraphQL<\/a> plugin.<\/p>\n<h3>De klasse AbstractPlugin:<\/h3>\n<p><a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/f918258371ec4c49c68b64fa184212cf0a956c16\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/PluginSkeleton\/AbstractPlugin.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AbstractPlugin<\/code><\/a> staat voor een plugin, zowel voor de hoofd-Gato GraphQL plugin als voor zijn uitbreidingen:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractPlugin implements PluginInterface\n{\n  protected string $pluginBaseName;\n  protected string $pluginSlug;\n  protected string $pluginName;\n\n  public function __construct(\n    protected string $pluginFile,\n    protected string $pluginVersion,\n    ?string $pluginName,\n  ) {\n    $this-&gt;pluginBaseName = plugin_basename($pluginFile);\n    $this-&gt;pluginSlug = dirname($this-&gt;pluginBaseName);\n    $this-&gt;pluginName = $pluginName ?? $this-&gt;pluginBaseName;\n  }\n\n  public function getPluginName(): string\n  {\n    return $this-&gt;pluginName;\n  }\n\n  public function getPluginBaseName(): string\n  {\n    return $this-&gt;pluginBaseName;\n  }\n\n  public function getPluginSlug(): string\n  {\n    return $this-&gt;pluginSlug;\n  }\n\n  public function getPluginFile(): string\n  {\n    return $this-&gt;pluginFile;\n  }\n\n  public function getPluginVersion(): string\n  {\n    return $this-&gt;pluginVersion;\n  }\n\n  public function getPluginDir(): string\n  {\n    return dirname($this-&gt;pluginFile);\n  }\n\n  public function getPluginURL(): string\n  {\n    return plugin_dir_url($this-&gt;pluginFile);\n  }\n\n  \/\/ ...\n}\n<\/code><\/pre>\n<h3>AbstractMainPlugin klasse:<\/h3>\n<p><a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/6545e639ceb85180bf869f50dcaae56de37421ea\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/PluginSkeleton\/AbstractMainPlugin.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AbstractMainPlugin<\/code><\/a> breidt <code>AbstractPlugin<\/code> uit om de hoofdplugin te representeren:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface\n{\n  public function __construct(\n    string $pluginFile,\n    string $pluginVersion,\n    ?string $pluginName,\n    protected MainPluginInitializationConfigurationInterface $pluginInitializationConfiguration,\n  ) {\n    parent::__construct(\n      $pluginFile,\n      $pluginVersion,\n      $pluginName,\n    );\n  }\n\n  \/\/ ...\n}\n<\/code><\/pre>\n<h3>AbstractExtension klasse:<\/h3>\n<p>Evenzo, <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/2447b3478ecfd012d509ad3075149cc0191fb3a1\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/PluginSkeleton\/AbstractExtension.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AbstractExtension<\/code><\/a><code>AbstractPlugin<\/code> om een uitbreidingsplugin weer te geven:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface\n{\n  public function __construct(\n    string $pluginFile,\n    string $pluginVersion,\n    ?string $pluginName,\n    protected ?ExtensionInitializationConfigurationInterface $extensionInitializationConfiguration,\n  ) {\n    parent::__construct(\n      $pluginFile,\n      $pluginVersion,\n      $pluginName,\n    );\n  }\n\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Merk op dat <code>AbstractExtension<\/code> is opgenomen in de hoofdplugin en functionaliteit biedt om een extensie te registreren en te initialiseren. Het wordt echter alleen gebruikt door extensies, niet door de hoofdplugin zelf.<\/p>\n<p>De klasse <code>AbstractPlugin<\/code> bevat gedeelde initialisatiecode die op verschillende momenten wordt gecalld. Deze methoden zijn gedefinieerd op het niveau van de ancestor, maar worden gecalld door de inheriting klassen volgens hun levenscycli.<\/p>\n<p>De hoofdplugin en de uitbreidingen worden ge\u00efnitialiseerd door de methode <code>setup<\/code> uit te voeren op de corresponderende klasse, gecalld vanuit het hoofdbestand van de WordPress plugin.<\/p>\n<p>In Gato GraphQL wordt dit bijvoorbeeld gedaan in <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/135a11e2637c76410a1d23d090fc4ec9b87c01fb\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/gatographql.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>gatographql.php<\/code><\/a>:<\/p>\n<pre><code class=\"language-php\">$pluginFile = __FILE__;\n$pluginVersion = '2.4.0';\n$pluginName = __('Gato GraphQL', 'gatographql');\nPluginApp::getMainPluginManager()-&gt;register(new Plugin(\n  $pluginFile,\n  $pluginVersion,\n  $pluginName\n))-&gt;setup();\n<\/code><\/pre>\n<h3>setup methode:<\/h3>\n<p>Op het niveau van de ancestor bevat <code>setup<\/code> de gemeenschappelijke logica tussen de plugin en zijn uitbreidingen, zoals het uitschrijven van de plugin wanneer deze wordt gedeactiveerd. Deze methode is niet definitief; het kan overschreven worden door de inheriting klassen om hun functionaliteit toe te voegen:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractPlugin implements PluginInterface\n{\n  \/\/ ...\n\n  public function setup(): void\n  {\n    register_deactivation_hook(\n      $this-&gt;getPluginFile(),\n      $this-&gt;deactivate(...)\n    );\n  }\n\n  public function deactivate(): void\n  {\n    $this-&gt;removePluginVersion();\n  }\n\n  private function removePluginVersion(): void\n  {\n    $pluginVersions = get_option('gatographql-plugin-versions', []);\n    unset($pluginVersions[$this-&gt;pluginBaseName]);\n    update_option('gatographql-plugin-versions', $pluginVersions);\n  }\n}\n<\/code><\/pre>\n<h3>De setup methode van de hoofdplugin:<\/h3>\n<p>De methode <code>setup<\/code> van de hoofdplugin initialiseert de levenscyclus van de applicatie. Het voert de functionaliteit van de hoofdplugin uit via methodes als <code>initialize<\/code>, <code>configureComponents<\/code>, <code>configure<\/code>, en <code>boot<\/code>, en activeert overeenkomstige action hooks voor uitbreidingen:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface\n{\n  public function setup(): void\n  {\n    parent::setup();\n\n    add_action('plugins_loaded', function (): void\n    {\n      \/\/ 1. Initialize main plugin\n      $this-&gt;initialize();\n\n      \/\/ 2. Initialize extensions\n      do_action('gatographql:initializeExtension');\n\n      \/\/ 3. Configure main plugin components\n      $this-&gt;configureComponents();\n\n      \/\/ 4. Configure extension components\n      do_action('gatographql:configureExtensionComponents');\n\n      \/\/ 5. Configure main plugin\n      $this-&gt;configure();\n\n      \/\/ 6. Configure extension\n      do_action('gatographql:configureExtension');\n\n      \/\/ 7. Boot main plugin\n      $this-&gt;boot();\n\n      \/\/ 8. Boot extension\n      do_action('gatographql:bootExtension');\n    }\n\n    \/\/ ...\n  }\n  \n  \/\/ ...\n}\n<\/code><\/pre>\n<h3>Extension setup methode:<\/h3>\n<p>De klasse <code>AbstractExtension<\/code> voert zijn logica uit op de overeenkomstige hooks:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface\n{\n  \/\/ ...\n\n  final public function setup(): void\n  {\n    parent::setup();\n\n    add_action('plugins_loaded', function (): void\n    {\n      \/\/ 2. Initialize extensions\n      add_action(\n        'gatographql:initializeExtension',\n        $this-&gt;initialize(...)\n      );\n\n      \/\/ 4. Configure extension components\n      add_action(\n        'gatographql:configureExtensionComponents',\n        $this-&gt;configureComponents(...)\n      );\n\n      \/\/ 6. Configure extension\n      add_action(\n        'gatographql:configureExtension',\n        $this-&gt;configure(...)\n      );\n\n      \/\/ 8. Boot extension\n      add_action(\n        'gatographql:bootExtension',\n        $this-&gt;boot(...)\n      );\n    }, 20);\n  }\n}\n<\/code><\/pre>\n<p>Methoden <code>initialize<\/code>, <code>configureComponents<\/code>, <code>configure<\/code>, en <code>boot<\/code> zijn gemeenschappelijk voor zowel de hoofdplugin als de uitbreidingen en kunnen logica delen. Deze gedeelde logica wordt opgeslagen in de klasse <code>AbstractPlugin<\/code>.<\/p>\n<p>De methode <code>configure<\/code> configureert bijvoorbeeld de plugin of uitbreidingen, door <code>callPluginInitializationConfiguration<\/code> te callen, die verschillende implementaties heeft voor de hoofdplugin en uitbreidingen en gedefinieerd is als abstract en <code>getModuleClassConfiguration<\/code>, die een standaard gedrag geeft maar overschreven kan worden als dat nodig is:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractPlugin implements PluginInterface\n{\n  \/\/ ...\n\n  public function configure(): void\n  {\n    $this-&gt;callPluginInitializationConfiguration();\n\n    $appLoader = App::getAppLoader();\n    $appLoader-&gt;addModuleClassConfiguration($this-&gt;getModuleClassConfiguration());\n  }\n\n  abstract protected function callPluginInitializationConfiguration(): void;\n\n  \/**\n   * @return array&lt;class-string&lt;ModuleInterface&gt;,mixed&gt; [key]: Module class, [value]: Configuration\n   *\/\n  public function getModuleClassConfiguration(): array\n  {\n    return [];\n  }\n}\n<\/code><\/pre>\n<p>De hoofdplugin levert zijn implementatie voor <code>callPluginInitializationConfiguration<\/code>:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface\n{\n  \/\/ ...\n\n  protected function callPluginInitializationConfiguration(): void\n  {\n    $this-&gt;pluginInitializationConfiguration-&gt;initialize();\n  }\n}\n<\/code><\/pre>\n<p>Op dezelfde manier levert de uitbreidingsklasse zijn implementatie:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface\n{\n  \/\/ ...\n\n  protected function callPluginInitializationConfiguration(): void\n  {\n    $this-&gt;extensionInitializationConfiguration?-&gt;initialize();\n  }\n}\n<\/code><\/pre>\n<p>Methoden <code>initialize<\/code>, <code>configureComponents<\/code> en <code>boot<\/code> zijn gedefinieerd op het niveau van de ancestor en kunnen worden overschreven door overervende klassen:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractPlugin implements PluginInterface\n{\n  \/\/ ...\n\n  public function initialize(): void\n  {\n    $moduleClasses = $this-&gt;getModuleClassesToInitialize();\n    App::getAppLoader()-&gt;addModuleClassesToInitialize($moduleClasses);\n  }\n\n  \/**\n   * @return array&lt;class-string&lt;ModuleInterface&gt;&gt; List of `Module` class to initialize\n   *\/\n  abstract protected function getModuleClassesToInitialize(): array;\n\n  public function configureComponents(): void\n  {\n    $classNamespace = ClassHelpers::getClassPSR4Namespace(get_called_class());\n    $moduleClass = $classNamespace . '\\\\Module';\n    App::getModule($moduleClass)-&gt;setPluginFolder(dirname($this-&gt;pluginFile));\n  }\n\n  public function boot(): void\n  {\n    \/\/ By default, do nothing\n  }\n}\n<\/code><\/pre>\n<p>Alle methoden kunnen overschreven worden door <code>AbstractMainPlugin<\/code> of <code>AbstractExtension<\/code> om ze uit te breiden met hun eigen functionaliteit.<\/p>\n<p>Voor de hoofdplugin verwijdert de methode <code>setup<\/code> ook alle caching van de WordPress instantie wanneer de plugin of een van zijn uitbreidingen wordt geactiveerd of gedeactiveerd:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface\n{\n  public function setup(): void\n  {\n    parent::setup();\n\n    \/\/ ...\n\n    \/\/ Main-plugin specific methods\n    add_action(\n      'activate_plugin',\n      function (string $pluginFile): void {\n        $this-&gt;maybeRegenerateContainerWhenPluginActivatedOrDeactivated($pluginFile);\n      }\n    );\n    add_action(\n      'deactivate_plugin',\n      function (string $pluginFile): void {\n        $this-&gt;maybeRegenerateContainerWhenPluginActivatedOrDeactivated($pluginFile);\n      }\n    );\n  }\n\n  public function maybeRegenerateContainerWhenPluginActivatedOrDeactivated(string $pluginFile): void\n  {\n    \/\/ Removed code for simplicity\n  }\n\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Op dezelfde manier verwijdert de methode <code>deactivate<\/code> caching en voert <code>boot<\/code> alleen voor de hoofdplugin extra action hooks uit:<\/p>\n<pre><code class=\"language-php\">abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface\n{\n  public function deactivate(): void\n  {\n    parent::deactivate();\n\n    $this-&gt;removeTimestamps();\n  }\n\n  protected function removeTimestamps(): void\n  {\n    $userSettingsManager = UserSettingsManagerFacade::getInstance();\n    $userSettingsManager-&gt;removeTimestamps();\n  }\n\n  public function boot(): void\n  {\n    parent::boot();\n\n    add_filter(\n      'admin_body_class',\n      function (string $classes): string {\n        $extensions = PluginApp::getExtensionManager()-&gt;getExtensions();\n        $commercialExtensionActivatedLicenseObjectProperties = SettingsHelpers::getCommercialExtensionActivatedLicenseObjectProperties();\n        foreach ($extensions as $extension) {\n          $extensionCommercialExtensionActivatedLicenseObjectProperties = $commercialExtensionActivatedLicenseObjectProperties[$extension-&gt;getPluginSlug()] ?? null;\n          if ($extensionCommercialExtensionActivatedLicenseObjectProperties === null) {\n            continue;\n          }\n          return $classes . ' is-gatographql-customer';\n        }\n        return $classes;\n      }\n    );\n  }\n}\n<\/code><\/pre>\n<p>Uit alle hierboven gepresenteerde code is het duidelijk dat we bij het ontwerpen en coderen van een WordPress plugin rekening moeten houden met de behoeften van de uitbreidingen en dat we code zoveel mogelijk moeten hergebruiken. Het implementeren van goede <a href=\"https:\/\/kinqsta.com\/nl\/blog\/php-oop\/\">Object-geori\u00ebnteerde programmeerpatronen<\/a> (zoals de <a href=\"https:\/\/dev.to\/evrtrabajo\/solid-in-php-d8e\" target=\"_blank\" rel=\"noopener noreferrer\">SOLID<\/a> principes) helpt om dit te bereiken, waardoor de codebase onderhoudbaar wordt voor de lange termijn.<\/p>\n<h2>De versieafhankelijkheid declareren en valideren<\/h2>\n<p>Omdat de extensie erft van een PHP klasse die wordt geleverd door de plugin, is het cruciaal om te valideren dat de vereiste versie van de plugin aanwezig is. Als je dat niet doet, kan dat conflicten veroorzaken die de site platleggen.<\/p>\n<p>Als bijvoorbeeld de klasse <code>AbstractExtension<\/code> wordt bijgewerkt met cruciale wijzigingen en een major versie <code>4.0.0<\/code> uitbrengt ten opzichte van de vorige <code>3.4.0<\/code>, kan het laden van de extensie zonder de versie te controleren resulteren in een PHP fout, waardoor WordPress niet kan worden geladen.<\/p>\n<p>Om dit te voorkomen moet de extensie valideren dat de ge\u00efnstalleerde plugin versie <code>3.x.x<\/code> is. Als versie <code>4.0.0<\/code> is ge\u00efnstalleerd, wordt de extensie uitgeschakeld, waardoor fouten worden voorkomen.<\/p>\n<p>De extensie kan deze validatie uitvoeren met de volgende logica, uitgevoerd op de <code>plugins_loaded<\/code> hook (omdat de hoofdplugin dan al geladen is) in <a href=\"https:\/\/github.com\/GatoGraphQL\/ExtensionStarter\/blob\/4ce883e3ec5325f15797aaff3042370f25e7e671\/templates\/basic\/layers\/GatoGraphQLForWP\/plugins\/extension-template\/gatographql-extension-template.php\" target=\"_blank\" rel=\"noopener noreferrer\">het hoofdpluginbestand van de extensie<\/a>. Deze logica heeft toegang tot de <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/4d33af1ad5c40318b0c2e612d1114c5ba342b06e\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/src\/PluginManagement\/ExtensionManager.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>ExtensionManager<\/code><\/a> klasse, die is opgenomen in de hoofdplugin om extensies te beheren:<\/p>\n<pre><code class=\"language-php\">\/**\n * Create and set-up the extension\n *\/\nadd_action(\n  'plugins_loaded',\n  function (): void {\n    \/**\n     * Extension's name and version.\n     *\n     * Use a stability suffix as supported by Composer.\n     *\/\n    $extensionVersion = '1.1.0';\n    $extensionName = __('Gato GraphQL - Extension Template');\n\n    \/**\n     * The minimum version required from the Gato GraphQL plugin\n     * to activate the extension.\n     *\/\n    $gatoGraphQLPluginVersionConstraint = '^1.0';\n    \n    \/**\n     * Validate Gato GraphQL is active\n     *\/\n    if (!class_exists(\\GatoGraphQL\\GatoGraphQL\\Plugin::class)) {\n      add_action('admin_notices', function () use ($extensionName) {\n        printf(\n          '&lt;div class=\"notice notice-error\"&gt;&lt;p&gt;%s&lt;\/p&gt;&lt;\/div&gt;',\n          sprintf(\n            __('Plugin &lt;strong&gt;%s&lt;\/strong&gt; is not installed or activated. Without it, plugin &lt;strong&gt;%s&lt;\/strong&gt; will not be loaded.'),\n            __('Gato GraphQL'),\n            $extensionName\n          )\n        );\n      });\n      return;\n    }\n\n    $extensionManager = \\GatoGraphQL\\GatoGraphQL\\PluginApp::getExtensionManager();\n    if (!$extensionManager-&gt;assertIsValid(\n      GatoGraphQLExtension::class,\n      $extensionVersion,\n      $extensionName,\n      $gatoGraphQLPluginVersionConstraint\n    )) {\n      return;\n    }\n    \n    \/\/ Load Composer\u2019s autoloader\n    require_once(__DIR__ . '\/vendor\/autoload.php');\n\n    \/\/ Create and set-up the extension instance\n    $extensionManager-&gt;register(new GatoGraphQLExtension(\n      __FILE__,\n      $extensionVersion,\n      $extensionName,\n    ))-&gt;setup();\n  }\n);\n<\/code><\/pre>\n<p>Let op hoe de uitbreiding een dependency verklaart van versiebeperking <code>^1.0<\/code> van de hoofdplugin (met behulp van <a href=\"https:\/\/getcomposer.org\/doc\/articles\/versions.md#writing-version-constraints\" target=\"_blank\" rel=\"noopener noreferrer\">Composer&#8217;s versiebeperkingen<\/a>). Dus als versie <code>2.0.0<\/code> van Gato GraphQL is ge\u00efnstalleerd, zal de extensie niet worden geactiveerd.<\/p>\n<p>De versiebeperking wordt gevalideerd via de methode <code>ExtensionManager::assertIsValid<\/code>, die <code>Semver::satisfies<\/code> callt (geleverd door het <a href=\"https:\/\/packagist.org\/packages\/composer\/semver\" target=\"_blank\" rel=\"noopener noreferrer\">pakket <code>composer\/semver<\/code><\/a>):<\/p>\n<pre><code class=\"language-php\">use Composer\\Semver\\Semver;\n\nclass ExtensionManager extends AbstractPluginManager\n{\n  \/**\n   * Validate that the required version of the Gato GraphQL for WP plugin is installed.\n   *\n   * If the assertion fails, it prints an error on the WP admin and returns false\n   *\n   * @param string|null $mainPluginVersionConstraint the semver version constraint required for the plugin (eg: \"^1.0\" means &gt;=1.0.0 and &lt;2.0.0)\n   *\/\n  public function assertIsValid(\n    string $extensionClass,\n    string $extensionVersion,\n    ?string $extensionName = null,\n    ?string $mainPluginVersionConstraint = null,\n  ): bool {\n    $mainPlugin = \\GatoGraphQL\\GatoGraphQL\\PluginApp::getMainPluginManager()-&gt;getPlugin();\n    $mainPluginVersion = $mainPlugin-&gt;getPluginVersion();\n    if (\n      $mainPluginVersionConstraint !== null && !Semver::satisfies(\n        $mainPluginVersion,\n        $mainPluginVersionConstraint\n      )\n    ) {\n      $this-&gt;printAdminNoticeErrorMessage(\n        sprintf(\n          __('Extension or bundle &lt;strong&gt;%s&lt;\/strong&gt; requires plugin &lt;strong&gt;%s&lt;\/strong&gt; to satisfy version constraint &lt;code&gt;%s&lt;\/code&gt;, but the current version &lt;code&gt;%s&lt;\/code&gt; does not. The extension or bundle has not been loaded.', 'gatographql'),\n          $extensionName ?? $extensionClass,\n          $mainPlugin-&gt;getPluginName(),\n          $mainPluginVersionConstraint,\n          $mainPlugin-&gt;getPluginVersion(),\n        )\n      );\n      return false;\n    }\n\n    return true;\n  }\n\n  protected function printAdminNoticeErrorMessage(string $errorMessage): void\n  {\n    \\add_action('admin_notices', function () use ($errorMessage): void {\n      $adminNotice_safe = sprintf(\n        '&lt;div class=\"notice notice-error\"&gt;&lt;p&gt;%s&lt;\/p&gt;&lt;\/div&gt;',\n        $errorMessage\n      );\n      echo $adminNotice_safe;\n    });\n  }\n}\n<\/code><\/pre>\n<h2>Integratietests uitvoeren tegen een WordPress server<\/h2>\n<p>Om het gemakkelijker te maken voor externe ontwikkelaars om uitbreidingen voor je plugins te maken, moet je ze voorzien van tools voor ontwikkeling en testen, inclusief workflows voor hun <a href=\"https:\/\/kinqsta.com\/nl\/blog\/geautomatiseerd-testen\/\">continue integratie en continue levering<\/a> (CI\/CD) processen.<\/p>\n<p>Tijdens de ontwikkeling kan iedereen eenvoudig een webserver opstarten met <a href=\"https:\/\/kinqsta.com\/nl\/devkinsta\/\">DevKinsta<\/a>, de plugin installeren waarvoor ze de extensie coderen en meteen valideren dat de extensie compatibel is met de plugin.<\/p>\n<p>Om het testen tijdens CI\/CD te automatiseren, moeten we de webserver via een netwerk toegankelijk maken voor de CI\/CD service. Diensten zoals InstaWP kunnen voor dit doel een sandbox site maken waarop WordPress is ge\u00efnstalleerd.<\/p>\n<p>Als de codebase van de extensie wordt gehost op <a href=\"https:\/\/kinqsta.com\/blog\/what-is-github\/\">GitHub<\/a>, dan kunnen ontwikkelaars <a href=\"https:\/\/kinqsta.com\/nl\/blog\/ci-cd-pipeline-maken\/#what-is-github-actions\">GitHub Actions<\/a> gebruiken om integratietests uit te voeren tegen de InstaWP service. De volgende workflow installeert de <a href=\"https:\/\/github.com\/GatoGraphQL\/ExtensionStarter\/blob\/aa574c785a139d8ffc094b3b3703eb516156f77e\/.github\/workflows\/generate_plugins.yml\" target=\"_blank\" rel=\"noopener noreferrer\">extensie<\/a> op een InstaWP sandbox site (naast de laatste stabiele versie van de hoofdplugin) en <a href=\"https:\/\/github.com\/GatoGraphQL\/ExtensionStarter\/blob\/a2d5d37ed205264f5ee03ce3ad075a08e89b67a7\/.github\/workflows\/integration_tests.yml\" target=\"_blank\" rel=\"noopener noreferrer\">voert dan de integratietesten uit<\/a>:<\/p>\n<pre><code class=\"language-yaml\">name: Integration tests (InstaWP)\non:\n  workflow_run:\n    workflows: [Generate plugins]\n    types:\n      - completed\n\njobs:\n  provide_data:\n    if: ${{ github.event.workflow_run.conclusion == 'success' }}\n    name: Retrieve the GitHub Action artifact URLs to install in InstaWP\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n\n      - uses: shivammathur\/setup-php@v2\n        with:\n          php-version: 8.1\n          coverage: none\n        env:\n          COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - uses: \"ramsey\/composer-install@v2\"\n\n      - name: Retrieve artifact URLs from GitHub workflow\n        uses: actions\/github-script@v6\n        id: artifact-url\n        with:\n          script: |\n            const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              run_id: context.payload.workflow_run.id,\n            });\n            const artifactURLs = allArtifacts.data.artifacts.map((artifact) =&gt; {\n              return artifact.url.replace('https:\/\/api.github.com\/repos', 'https:\/\/nightly.link') + '.zip'\n            }).concat([\n              \"https:\/\/downloads.wordpress.org\/plugin\/gatographql.latest-stable.zip\"\n            ]);\n            return artifactURLs.join(',');\n          result-encoding: string\n\n      - name: Artifact URL for InstaWP\n        run: echo \"Artifact URL for InstaWP - ${{ steps.artifact-url.outputs.result }}\"\n        shell: bash\n\n    outputs:\n      artifact_url: ${{ steps.artifact-url.outputs.result }}\n\n  process:\n    needs: provide_data\n    name: Launch InstaWP site from template 'integration-tests' and execute integration tests against it\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n\n      - uses: shivammathur\/setup-php@v2\n        with:\n          php-version: 8.1\n          coverage: none\n        env:\n          COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - uses: \"ramsey\/composer-install@v2\"\n\n      - name: Create InstaWP instance\n        uses: instawp\/wordpress-testing-automation@main\n        id: create-instawp\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          INSTAWP_TOKEN: ${{ secrets.INSTAWP_TOKEN }}\n          INSTAWP_TEMPLATE_SLUG: \"integration-tests\"\n          REPO_ID: 25\n          INSTAWP_ACTION: create-site-template\n          ARTIFACT_URL: ${{ needs.provide_data.outputs.artifact_url }}\n\n      - name: InstaWP instance URL\n        run: echo \"InstaWP instance URL - ${{ steps.create-instawp.outputs.instawp_url }}\"\n        shell: bash\n\n      - name: Extract InstaWP domain\n        id: extract-instawp-domain        \n        run: |\n          instawp_domain=\"$(echo \"${{ steps.create-instawp.outputs.instawp_url }}\" | sed -e s#https:\/\/##)\"\n          echo \"instawp-domain=$(echo $instawp_domain)\" &gt;&gt; $GITHUB_OUTPUT\n\n      - name: Run tests\n        run: |\n          INTEGRATION_TESTS_WEBSERVER_DOMAIN=${{ steps.extract-instawp-domain.outputs.instawp-domain }} \\\n          INTEGRATION_TESTS_AUTHENTICATED_ADMIN_USER_USERNAME=${{ steps.create-instawp.outputs.iwp_wp_username }} \\\n          INTEGRATION_TESTS_AUTHENTICATED_ADMIN_USER_PASSWORD=${{ steps.create-instawp.outputs.iwp_wp_password }} \\\n          vendor\/bin\/phpunit --filter=Integration\n\n      - name: Destroy InstaWP instance\n        uses: instawp\/wordpress-testing-automation@main\n        id: destroy-instawp\n        if: ${{ always() }}\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          INSTAWP_TOKEN: ${{ secrets.INSTAWP_TOKEN }}\n          INSTAWP_TEMPLATE_SLUG: \"integration-tests\"\n          REPO_ID: 25\n          INSTAWP_ACTION: destroy-site\n<\/code><\/pre>\n<p>Deze workflow opent het <strong>.zip<\/strong> bestand via <a href=\"https:\/\/nightly.link\" target=\"_blank\" rel=\"noopener noreferrer\">Nightly Link<\/a>, een service die toegang geeft tot een artefact van GitHub zonder in te loggen, wat de configuratie van InstaWP vereenvoudigt.<\/p>\n<h2>De extensie-plugin vrijgeven<\/h2>\n<p>We kunnen tools leveren om te helpen bij het uitbrengen van de extensies, waarbij we de procedures zoveel mogelijk automatiseren.<\/p>\n<p>De <a href=\"https:\/\/github.com\/symplify\/monorepo-builder\" target=\"_blank\" rel=\"noopener noreferrer\">Monorepo Builder<\/a> is een bibliotheek voor het beheren van elk PHP project, inclusief een WordPress plugin. Het biedt het commando <code>monorepo-builder release<\/code> om een versie van het project vrij te geven, waarbij de major, minor of patch component van de versie wordt verhoogd volgens <a href=\"https:\/\/semver.org\" target=\"_blank\" rel=\"noopener noreferrer\">semantisch versiebeheer<\/a>.<\/p>\n<p>Dit commando voert een reeks <a href=\"https:\/\/github.com\/symplify\/monorepo-builder?tab=readme-ov-file#7-release-flow\" target=\"_blank\" rel=\"noopener noreferrer\">release workers<\/a> uit, wat PHP klassen zijn die bepaalde logica uitvoeren. De standaard workers zijn er een die een <code>git tag<\/code> aanmaakt met de nieuwe versie en een andere die de tag naar de remote repository pusht. Custom workers kunnen voor, na of tussen deze stappen worden ge\u00efnjecteerd.<\/p>\n<p>De release workers worden geconfigureerd via een configuratiebestand:<\/p>\n<pre><code class=\"language-php\">use Symplify\\MonorepoBuilder\\Config\\MBConfig;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\AddTagToChangelogReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\PushNextDevReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\PushTagReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\SetCurrentMutualDependenciesReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\SetNextMutualDependenciesReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\TagVersionReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\UpdateBranchAliasReleaseWorker;\nuse Symplify\\MonorepoBuilder\\Release\\ReleaseWorker\\UpdateReplaceReleaseWorker;\n\nreturn static function (MBConfig $mbConfig): void {\n  \/\/ release workers - in order to execute\n  $mbConfig-&gt;workers([\n    UpdateReplaceReleaseWorker::class,\n    SetCurrentMutualDependenciesReleaseWorker::class,\n    AddTagToChangelogReleaseWorker::class,\n    TagVersionReleaseWorker::class,\n    PushTagReleaseWorker::class,\n    SetNextMutualDependenciesReleaseWorker::class,\n    UpdateBranchAliasReleaseWorker::class,\n    PushNextDevReleaseWorker::class,\n  ]);\n};\n<\/code><\/pre>\n<p>We kunnen aangepaste release workers leveren om het release proces uit te breiden, aangepast aan de behoeften van een WordPress plugin. Bijvoorbeeld, de <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/af21729cd7e88439156ca1b9cf0231a06354b18f\/src\/OnDemand\/Symplify\/MonorepoBuilder\/Release\/ReleaseWorker\/ConvertStableTagVersionForProdInPluginReadmeFileReleaseWorker.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code>InjectStableTagVersionInPluginReadmeFileReleaseWorker<\/code><\/a> stelt de nieuwe versie in als de &#8220;Stable tag&#8221; vermelding in <a href=\"https:\/\/developer.wordpress.org\/plugins\/wordpress-org\/how-your-readme-txt-works\/\">het <strong>readme.txt<\/strong> bestand van de extensie<\/a>:<\/p>\n<pre><code class=\"language-php\">use Nette\\Utils\\Strings;\nuse PharIo\\Version\\Version;\nuse Symplify\\SmartFileSystem\\SmartFileInfo;\nuse Symplify\\SmartFileSystem\\SmartFileSystem;\n\nclass InjectStableTagVersionInPluginReadmeFileReleaseWorker implements ReleaseWorkerInterface\n{\n  public function __construct(\n    \/\/ This class is provided by the Monorepo Builder\n    private SmartFileSystem $smartFileSystem,\n  ) {\n  }\n\n  public function getDescription(Version $version): string\n  {\n    return 'Have the \"Stable tag\" point to the new version in the plugin\\'s readme.txt file';\n  }\n\n  public function work(Version $version): void\n  {\n    $replacements = [\n      '\/Stable tag:\\s+[a-z0-9.-]+\/' =&gt; 'Stable tag: ' . $version-&gt;getVersionString(),\n    ];\n    $this-&gt;replaceContentInFiles(['\/readme.txt'], $replacements);\n  }\n\n  \/**\n   * @param string[] $files\n   * @param array&lt;string,string&gt; $regexPatternReplacements regex pattern to search, and its replacement\n   *\/\n  protected function replaceContentInFiles(array $files, array $regexPatternReplacements): void\n  {\n    foreach ($files as $file) {\n      $fileContent = $this-&gt;smartFileSystem-&gt;readFile($file);\n      foreach ($regexPatternReplacements as $regexPattern =&gt; $replacement) {\n        $fileContent = Strings::replace($fileContent, $regexPattern, $replacement);\n      }\n      $this-&gt;smartFileSystem-&gt;dumpFile($file, $fileContent);\n    }\n  }\n}\n<\/code><\/pre>\n<p>Door <code>InjectStableTagVersionInPluginReadmeFileReleaseWorker<\/code> toe te voegen aan de configuratielijst, wordt bij het uitvoeren van het <code>monorepo-builder release<\/code> commando om een nieuwe versie van de plugin vrij te geven, de &#8220;Stable tag&#8221; in het <strong>readme.txt<\/strong> bestand van de extensie automatisch bijgewerkt.<\/p>\n<h2>De extensie-plugin publiceren naar de WP.org directory<\/h2>\n<p>We kunnen ook een workflow verspreiden om de extensie naar de <a href=\"https:\/\/wordpress.org\/plugins\/\" target=\"_blank\" rel=\"noopener noreferrer\">WordPress Plugin Directory<\/a> te publiceren. Bij het taggen van het project op de externe repository zal de <a href=\"https:\/\/github.com\/GatoGraphQL\/GatoGraphQL\/blob\/f8579139f1af05aae171e776c6f53b0c458e339a\/layers\/GatoGraphQLForWP\/plugins\/gatographql\/.github\/workflows\/deploy_wordpress_plugin_to_svn.yml\" target=\"_blank\" rel=\"noopener noreferrer\">volgende workflow<\/a> de WordPress extensie plugin publiceren naar de directory:<\/p>\n<pre><code class=\"language-yml\"># See: https:\/\/github.com\/10up\/action-wordpress-plugin-deploy#deploy-on-pushing-a-new-tag\nname: Deploy to WordPress.org Plugin Directory (SVN)\non:\n  push:\n  tags:\n  - \"*\"\n\njobs:\n  tag:\n  name: New tag\n  runs-on: ubuntu-latest\n  steps:\n  - uses: actions\/checkout@master\n  - name: WordPress Plugin Deploy\n    uses: 10up\/action-wordpress-plugin-deploy@stable\n    env:\n    SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}\n    SVN_USERNAME: ${{ secrets.SVN_USERNAME }}\n    SLUG: ${{ secrets.SLUG }}\n<\/code><\/pre>\n<p>Deze workflow gebruikt de <a href=\"https:\/\/github.com\/10up\/action-wordpress-plugin-deploy\" target=\"_blank\" rel=\"noopener noreferrer\"><code>10up\/action-wordpress-plugin-deploy<\/code><\/a> actie, die de code ophaalt van een Git repository en het naar de <a href=\"https:\/\/kinqsta.com\/blog\/publish-plugin-wordpress-plugin-directory\/#plugin-structure\">WordPress.org SVN repository<\/a> pusht, wat de operatie vereenvoudigt.<\/p>\n<h2>Samenvatting<\/h2>\n<p>Bij het maken van een uitbreidbare plugin voor WordPress, is het ons doel om het zo gemakkelijk mogelijk te maken voor externe ontwikkelaars om deze uit te breiden, waardoor de kans op een levendig ecosysteem rondom onze plugins wordt gemaximaliseerd.<\/p>\n<p>Hoewel het verstrekken van uitgebreide documentatie ontwikkelaars kan helpen bij het uitbreiden van de plugin, is een nog effectievere aanpak het leveren van de benodigde PHP code en tools voor het ontwikkelen, testen en uitbrengen van hun uitbreidingen.<\/p>\n<p>Door de extra code die uitbreidingen nodig hebben direct in onze plugin op te nemen, vereenvoudigen we het proces voor ontwikkelaars.<\/p>\n<p><em>Ben jij van plan om je WordPress plugin uitbreidbaar te maken? Laat het ons weten in de comments.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>WordPress plugins kunnen worden uitgebreid met extra functionaliteit, zoals populaire plugins als WooCommerce en Gravity Forms laten zien. In het artikel\u00a0 &#8220;Een WordPress plugin maken om &#8230;<\/p>\n","protected":false},"author":196,"featured_media":59658,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[],"topic":[892,899],"class_list":["post-59657","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","topic-wordpress-ontwikkeling","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>Een WordPress plugin uitbreidbaar maken met PHP classes - Kinsta\u00ae<\/title>\n<meta name=\"description\" content=\"Leer hoe je een uitbreidbare WordPress-plugin maakt die het ontwikkelingsproces voor externe ontwikkelaars vereenvoudigt.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/\" \/>\n<meta property=\"og:locale\" content=\"nl_NL\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Zo maak je een WordPress plugin uitbreidbaar met PHP klassen\" \/>\n<meta property=\"og:description\" content=\"Leer hoe je een uitbreidbare WordPress-plugin maakt die het ontwikkelingsproces voor externe ontwikkelaars vereenvoudigt.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Kinsta-Nederland-476213452787823\/\" \/>\n<meta property=\"article:published_time\" content=\"2024-06-19T09:54:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-07-01T13:40:07+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1460\" \/>\n\t<meta property=\"og:image:height\" content=\"730\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Leonardo Losoviz\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:description\" content=\"Leer hoe je een uitbreidbare WordPress-plugin maakt die het ontwikkelingsproces voor externe ontwikkelaars vereenvoudigt.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions-1024x512.jpg\" \/>\n<meta name=\"twitter:creator\" content=\"@losoviz\" \/>\n<meta name=\"twitter:site\" content=\"@Kinsta_NL\" \/>\n<meta name=\"twitter:label1\" content=\"Geschreven door\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leonardo Losoviz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Geschatte leestijd\" \/>\n\t<meta name=\"twitter:data2\" content=\"16 minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinqsta.com\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"Zo maak je een WordPress plugin uitbreidbaar met PHP klassen\",\"datePublished\":\"2024-06-19T09:54:38+00:00\",\"dateModified\":\"2024-07-01T13:40:07+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/\"},\"wordCount\":1486,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinqsta.com\/nl\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"inLanguage\":\"nl-NL\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/\",\"url\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/\",\"name\":\"Een WordPress plugin uitbreidbaar maken met PHP classes - Kinsta\u00ae\",\"isPartOf\":{\"@id\":\"https:\/\/kinqsta.com\/nl\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"datePublished\":\"2024-06-19T09:54:38+00:00\",\"dateModified\":\"2024-07-01T13:40:07+00:00\",\"description\":\"Leer hoe je een uitbreidbare WordPress-plugin maakt die het ontwikkelingsproces voor externe ontwikkelaars vereenvoudigt.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#breadcrumb\"},\"inLanguage\":\"nl-NL\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#primaryimage\",\"url\":\"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"contentUrl\":\"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg\",\"width\":1460,\"height\":730},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinqsta.com\/nl\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"WordPress plugins\",\"item\":\"https:\/\/kinqsta.com\/nl\/onderwerpen\/wordpress-plugins\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Zo maak je een WordPress plugin uitbreidbaar met PHP klassen\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinqsta.com\/nl\/#website\",\"url\":\"https:\/\/kinqsta.com\/nl\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Snelle, veilige, premium hostingoplossingen\",\"publisher\":{\"@id\":\"https:\/\/kinqsta.com\/nl\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinqsta.com\/nl\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"nl-NL\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinqsta.com\/nl\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinqsta.com\/nl\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\/\/kinqsta.com\/nl\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinqsta.com\/nl\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Kinsta-Nederland-476213452787823\/\",\"https:\/\/x.com\/Kinsta_NL\",\"https:\/\/www.instagram.com\/kinstahosting\/\",\"https:\/\/www.linkedin.com\/company\/kinsta\/\",\"https:\/\/www.pinterest.com\/kinstahosting\/\",\"https:\/\/www.youtube.com\/c\/Kinsta\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/kinqsta.com\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\/\/kinqsta.com\/nl\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g\",\"caption\":\"Leonardo Losoviz\"},\"description\":\"Leo writes about innovative web development trends, mostly concerning PHP, WordPress and GraphQL. You can find him at leoloso.com and twitter.com\/losoviz.\",\"sameAs\":[\"https:\/\/leoloso.com\",\"https:\/\/x.com\/losoviz\",\"https:\/\/www.youtube.com\/@GatoGraphQL\"],\"url\":\"https:\/\/kinqsta.com\/nl\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Een WordPress plugin uitbreidbaar maken met PHP classes - Kinsta\u00ae","description":"Leer hoe je een uitbreidbare WordPress-plugin maakt die het ontwikkelingsproces voor externe ontwikkelaars vereenvoudigt.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/","og_locale":"nl_NL","og_type":"article","og_title":"Zo maak je een WordPress plugin uitbreidbaar met PHP klassen","og_description":"Leer hoe je een uitbreidbare WordPress-plugin maakt die het ontwikkelingsproces voor externe ontwikkelaars vereenvoudigt.","og_url":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/Kinsta-Nederland-476213452787823\/","article_published_time":"2024-06-19T09:54:38+00:00","article_modified_time":"2024-07-01T13:40:07+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"Leer hoe je een uitbreidbare WordPress-plugin maakt die het ontwikkelingsproces voor externe ontwikkelaars vereenvoudigt.","twitter_image":"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions-1024x512.jpg","twitter_creator":"@losoviz","twitter_site":"@Kinsta_NL","twitter_misc":{"Geschreven door":"Leonardo Losoviz","Geschatte leestijd":"16 minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#article","isPartOf":{"@id":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinqsta.com\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"Zo maak je een WordPress plugin uitbreidbaar met PHP klassen","datePublished":"2024-06-19T09:54:38+00:00","dateModified":"2024-07-01T13:40:07+00:00","mainEntityOfPage":{"@id":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/"},"wordCount":1486,"commentCount":0,"publisher":{"@id":"https:\/\/kinqsta.com\/nl\/#organization"},"image":{"@id":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#primaryimage"},"thumbnailUrl":"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","inLanguage":"nl-NL","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/","url":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/","name":"Een WordPress plugin uitbreidbaar maken met PHP classes - Kinsta\u00ae","isPartOf":{"@id":"https:\/\/kinqsta.com\/nl\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#primaryimage"},"image":{"@id":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#primaryimage"},"thumbnailUrl":"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","datePublished":"2024-06-19T09:54:38+00:00","dateModified":"2024-07-01T13:40:07+00:00","description":"Leer hoe je een uitbreidbare WordPress-plugin maakt die het ontwikkelingsproces voor externe ontwikkelaars vereenvoudigt.","breadcrumb":{"@id":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#breadcrumb"},"inLanguage":"nl-NL","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/"]}]},{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#primaryimage","url":"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","contentUrl":"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2024\/06\/wp-creating-a-starter-project-on-github-for-wordpress-plugin-extensions.jpg","width":1460,"height":730},{"@type":"BreadcrumbList","@id":"https:\/\/kinqsta.com\/nl\/blog\/wordpress-plugin-uitbeidbaar-maken\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinqsta.com\/nl\/"},{"@type":"ListItem","position":2,"name":"WordPress plugins","item":"https:\/\/kinqsta.com\/nl\/onderwerpen\/wordpress-plugins\/"},{"@type":"ListItem","position":3,"name":"Zo maak je een WordPress plugin uitbreidbaar met PHP klassen"}]},{"@type":"WebSite","@id":"https:\/\/kinqsta.com\/nl\/#website","url":"https:\/\/kinqsta.com\/nl\/","name":"Kinsta\u00ae","description":"Snelle, veilige, premium hostingoplossingen","publisher":{"@id":"https:\/\/kinqsta.com\/nl\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinqsta.com\/nl\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"nl-NL"},{"@type":"Organization","@id":"https:\/\/kinqsta.com\/nl\/#organization","name":"Kinsta","url":"https:\/\/kinqsta.com\/nl\/","logo":{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/kinqsta.com\/nl\/#\/schema\/logo\/image\/","url":"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinqsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinqsta.com\/nl\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Kinsta-Nederland-476213452787823\/","https:\/\/x.com\/Kinsta_NL","https:\/\/www.instagram.com\/kinstahosting\/","https:\/\/www.linkedin.com\/company\/kinsta\/","https:\/\/www.pinterest.com\/kinstahosting\/","https:\/\/www.youtube.com\/c\/Kinsta"]},{"@type":"Person","@id":"https:\/\/kinqsta.com\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/kinqsta.com\/nl\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g","caption":"Leonardo Losoviz"},"description":"Leo writes about innovative web development trends, mostly concerning PHP, WordPress and GraphQL. You can find him at leoloso.com and twitter.com\/losoviz.","sameAs":["https:\/\/leoloso.com","https:\/\/x.com\/losoviz","https:\/\/www.youtube.com\/@GatoGraphQL"],"url":"https:\/\/kinqsta.com\/nl\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/posts\/59657","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/comments?post=59657"}],"version-history":[{"count":4,"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/posts\/59657\/revisions"}],"predecessor-version":[{"id":59782,"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/posts\/59657\/revisions\/59782"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinqsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/59657\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinqsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/59657\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinqsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/59657\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinqsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/59657\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinqsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/59657\/translations\/de"},{"embeddable":true,"hreflang":"ja","title":"Japanese","href":"https:\/\/kinqsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/59657\/translations\/jp"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinqsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/59657\/translations\/nl"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinqsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/59657\/translations\/es"},{"href":"https:\/\/kinqsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/59657\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/media\/59658"}],"wp:attachment":[{"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/media?parent=59657"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/tags?post=59657"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinqsta.com\/nl\/wp-json\/wp\/v2\/topic?post=59657"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}