{"id":40095,"date":"2021-09-22T23:29:49","date_gmt":"2021-09-22T21:29:49","guid":{"rendered":"https:\/\/kinsta.com\/?p=100908"},"modified":"2023-06-06T11:42:59","modified_gmt":"2023-06-06T09:42:59","slug":"wordpress-abstractieplugins","status":"publish","type":"post","link":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/","title":{"rendered":"WordPress abstractie: best practices en WordPress abstractieplugins"},"content":{"rendered":"<p>WordPress is een CMS dat al lang bestaat, maar is ook de <a href=\"https:\/\/kinsta.com\/nl\/wordpress-marktaandeel\/\">meestgebruikte<\/a>. Dankzij de traditie van het ondersteunen van verouderde PHP versies en legacy code, worden nog niet alle moderne programmeergewoonten ge\u00efmplementeerd, waar WordPress abstractie een voorbeeld van is.<\/p>\n<p>Zo zou het bijvoorbeeld veel beter zijn om de codebase van de WordPress core op te splitsen in packages die beheerd kunnen worden via Composer. Of om WordPress classes automatisch te laden via de bestandspaden.<\/p>\n<p>Dit artikel beschrijft hoe je handmatig WordPress code kan abstraheren en abstracte WordPress pluginfuncties kan gebruiken.<\/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>Problemen met het integreren van WordPress en PHP tools<\/h2>\n<p>Dankzij de verouderde architectuur komen we soms problemen tegen wanneer we WordPress integreren met tooling van PHP codebases, zoals de statische analyser <a href=\"https:\/\/phpstan.org\/\">PHPStan<\/a>, de library voor unit testing <a href=\"https:\/\/phpunit.de\/\">PHPUnit<\/a>, en de namespace-scoping library <a href=\"https:\/\/github.com\/humbug\/php-scoper\">PHP-Scoper<\/a>. Denk bijvoorbeeld eens aan de volgende gevallen:<\/p>\n<ul>\n<li>V\u00f3\u00f3r de release van <a href=\"https:\/\/kinsta.com\/nl\/blog\/wordpress-5-6\/\">WordPress 5.6<\/a> met ondersteuning voor <a href=\"https:\/\/kinsta.com\/nl\/blog\/php-8\/\">PHP 8.0<\/a> liet een rapport van Yoast zien hoe het uitvoeren van PHPStan op de WordPress core <a href=\"https:\/\/developer.yoast.com\/blog\/the-2020-wordpress-and-php-8-compatibility-report\/#h-scanning-wordpress-with-phpstan\">duizenden problemen<\/a><\/li>\n<li>Doordat WordPress nog steeds ondersteuning biedt voor PHP 5.6, bieden WordPress testpakketten slechts <a href=\"https:\/\/core.trac.wordpress.org\/ticket\/46149\">ondersteuning voor PHPUnit tot versie 7.5<\/a>, dat inmiddels al afgeschreven is.<\/li>\n<li>Het scopen van WordPress plugins via PHP-Scoper\u00a0<a href=\"https:\/\/github.com\/humbug\/php-scoper#wordpress\">is erg uitdagend<\/a>.<\/li>\n<\/ul>\n<p>De WordPress code binnen onze projecten is meestal maar een klein deel van de totale code, omdat er in een project ook code zit die onafhankelijk werkt van het onderliggende CMS. Maar alleen maar omdat er wel een klein beetje WordPress code in zit, kan een project niet goed meer integreren met bepaalde tools.<\/p>\n<p>Hierdoor kan het soms beter zijn om een project op te splitsen in verschillende packages. Hierbij hebben sommige de WordPress code, en andere alleen de bedrijfsspecifieke code met &#8220;vanilla&#8221; PHP en geen WordPress code. Op die manier wordt die tweede soort dan niet getroffen door bovenstaande problemen, en kunnen die perfect integreren met de gewenste tools.<\/p>\n<h2>Wat is abstractie van code?<\/h2>\n<p>De abstractie van code verwijdert vastgelegde dependencies uit de code, waardoor je packages krijgt die met elkaar samenwerken via zogenoemde contracten. Deze packages kunnen vervolgens voor verschillende toepassingen met verschillende stacks gebruikt worden, waardoor je ze zo effectief mogelijk kan inzetten. Het resultaat van codeabstractie is een mooie modulaire codebase op basis van de volgende principes:<\/p>\n<ol>\n<li>Code voor interfaces, niet voor implementaties.<\/li>\n<li>Maken van packages en ze distribueren via Composer.<\/li>\n<li>Alles samenvoegen via dependency injectie.<\/li>\n<\/ol>\n\n<h2>Code voor interfaces, niet voor implementaties<\/h2>\n<p>Code voor interfaces, of het programmeren voor interfaces, is de methode waarbij je contracten gebruiken om delen van je code met elkaar te laten samenwerken en communiceren. Een contract is in feite een PHP interface (of in een andere programmeertaal) waarmee gedefinieerd wordt wat de beschikbare functies zijn, inclusief de verwachte inputs en outputs.<\/p>\n<p>Een interface bepaalt wat het doel van de functionaliteit is, zonder uit te leggen hoe de functionaliteit praktisch ge\u00efmplementeerd wordt. Door functionaliteit via interfaces te gebruiken kan onze <a href=\"https:\/\/kinsta.com\/nl\/blog\/application-performance-monitoring\/\">toepassing<\/a> onafhankelijke stukken code gebruiken die een bepaald doel bereiken, zonder dat het uitmaakt hoe dat precies gedaan wordt. Op die manier hoeft de toepassing niet aangepast te worden om aan te sluiten op een ander stuk code dat hetzelfde doel bereikt, bijvoorbeeld wanneer er een andere provider komt.<\/p>\n<h3>Voorbeeld van contracten<\/h3>\n<p>De onderstaande code gebruikt Symfony\u2019s contract\u00a0<a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/302b844\/src\/Symfony\/Contracts\/Cache\/CacheInterface.php\"><code>CacheInterface<\/code><\/a> <span style=\"font-family: Roboto, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;font-size: 1rem\">en het PHP Standard Recommendation (PSR) contract\u00a0<\/span><a style=\"font-family: Roboto, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;font-size: 1rem\" href=\"https:\/\/github.com\/php-fig\/cache\/blob\/0a7c67d\/src\/CacheItemInterface.php\"><code>CacheItemInterface<\/code><\/a><span style=\"font-family: Roboto, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;font-size: 1rem\">\u00a0voor het implementeren van caching functionaliteit:<\/span><\/p>\n<pre><code class=\"language-php\">use Psr\\Cache\\CacheItemInterface;\nuse Symfony\\Contracts\\Cache\\CacheInterface;\n\n$value = $cache-&gt;get('my_cache_key', function (CacheItemInterface $item) {\n    $item-&gt;expiresAfter(3600);\n    return 'foobar';\n});<\/code><\/pre>\n<p><code>$cache<\/code>\u00a0implementeert\u00a0<code>CacheInterface<\/code>, die de methode <code>get<\/code> definieert om een object uit de cache op te halen. Door deze functionaliteit te openen via het contract, maakt het voor de toepassing niet uit waar de cache precies is. Of dit nou in het geheugen is, op schijf, in de database of het netwerk, of nog heel ergens anders, dat maakt allemaal niet uit. Maar het moet wel de functie uit kunnen voeren.\u00a0<code>CacheItemInterface<\/code>\u00a0definieert de methode <code>expiresAfter<\/code>\u00a0om te bepalen hoe lang een item in de cache bewaard moet worden. De toepassing kan deze methode gebruiken zonder dat het uitmaakt wat het object in de cache precies is, het draait alleen maar om hoe lang het in de cache moet zijn.<\/p>\n<h2>Programmeren voor interfaces in WordPress<\/h2>\n<p>Aangezien het nu draait om de abstractie van WordPress code, zal het resultaat zijn dat de toepassing geen direct gebruik maakt van <a href=\"https:\/\/kinsta.com\/nl\/blog\/bewerken-wordpress-code\/\">WordPress code<\/a>, maar uitsluitend via een interface. Zo heeft de WordPress functie <code>get_posts<\/code> deze &#8220;signature&#8221;, een handtekening, oftewel een combinatie van inputs en outputs:<\/p>\n<pre><code class=\"language-php\">\/**\n * @param array $args\n * @return WP_Post[]|int[] Array of post objects or post IDs.\n *\/\nfunction get_posts( $args = null )\n<\/code><\/pre>\n<p>In plaats van deze methode direct te gebruiken, kunnen we er gebruik van maken via het contract\u00a0<code>Owner\\MyApp\\Contracts\\PostsAPIInterface<\/code>:<\/p>\n<pre><code class=\"language-php\">namespace Owner\\MyApp\\Contracts;\n\ninterface PostAPIInterface\n{\n  public function get_posts(array $args = null): PostInterface[]|int[];\n}\n<\/code><\/pre>\n<p>Hierbij zie je dat de WordPress functie <code>get_posts<\/code> objecten van de class <code>WP_Post<\/code> kan terugsturen, die specifiek zijn voor WordPress. Bij abstractie van code moeten we dergelijke vastgelegde dependencies zien te verwijderen. De methode <code>get_posts<\/code> in het contract stuurt objecten van het type <code>PostInterface<\/code> terug, waardoor je de class <code>WP_Post<\/code> kan gebruiken zonder deze expliciet te noemen. De class <code>PostInterface<\/code> moet toegang geven tot alle methoden en eigenschappen van <code>WP_Post<\/code>:<\/p>\n<pre><code class=\"language-php\">namespace Owner\\MyApp\\Contracts;\n\ninterface PostInterface\n{\n  public function get_ID(): int;\n  public function get_post_author(): string;\n  public function get_post_date(): string;\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Door deze strategie toe te passen kunnen we beter leren begrijpen waar WordPress in onze stack past. In plaats van WordPress te zien als de toepassing zelf, waarmee we thema&#8217;s en plugins installeren, kunnen we het gaan zien als een dependency binnen de toepassing, die net zo goed vervangbaar is als andere onderdelen. Alhoewel we in de praktijk WordPress natuurlijk niet zullen vervangen, is het <em>conceptueel<\/em> gezien wel vervangbaar.<\/p>\n<h2>Het maken en distribueren van packages<\/h2>\n<p><a href=\"https:\/\/getcomposer.org\/\">Composer<\/a>\u00a0is een packagemanager voor PHP. Hiermee kunnen PHP toepassingen packages (stukken code) ophalen uit een repository, en die installeren als dependencies, oftewel afhankelijkheden. Om de toepassing los te koppen van WordPress, moeten we de code in twee soorten packages onderverdelen: packages met WordPress code en packages met de zakelijke toepassing en zonder WordPress code.<\/p>\n<p>Als laatste stap voegen we alle packages toe als dependencies in de toepassing, en installeren we die via de Composer. Aangezien de tooling toegepast kan worden op de packages met de bedrijfsspecifieke code, moeten deze het grootste deel van de code van de toepassing bevatten: hoe hoger het percentage hoe beter. Een mooie doelstelling is om te proberen om 90% van de totale code in die packages te hebben.<\/p>\n<h2>Extractie van WordPress code naar packages<\/h2>\n<p>Doorgaand met het eerdere voorbeeld, worden de contracten\u00a0<code>PostAPIInterface<\/code>\u00a0en <code>PostInterface<\/code> toegevoegd aan de packages met de bedrijfsspecifieke code, en andere packages zullen de WordPress implementatie van deze contracten bevatten. Om aan de eisen van <code>PostInterface<\/code> te voldoen, maken we een\u00a0<code>PostWrapper<\/code>\u00a0class die alle eigenschappen van een <code>WP_Post<\/code>\u00a0object ophaalt:<\/p>\n<pre><code class=\"language-php\">namespace Owner\\MyAppForWP\\ContractImplementations;\n\nuse Owner\\MyApp\\Contracts\\PostInterface;\nuse WP_Post;\n\nclass PostWrapper implements PostInterface\n{\n  private WP_Post $post;\n  \n  public function __construct(WP_Post $post)\n  {\n    $this-&gt;post = $post;\n  }\n\n  public function get_ID(): int\n  {\n    return $this-&gt;post-&gt;ID;\n  }\n\n  public function get_post_author(): string\n  {\n    return $this-&gt;post-&gt;post_author;\n  }\n\n  public function get_post_date(): string\n  {\n    return $this-&gt;post-&gt;post_date;\n  }\n\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Bij het implementeren van <code>PostAPI<\/code> moeten we de objecten van\u00a0<code>WP_Post<\/code>\u00a0omzetten naar <code>PostWrapper<\/code>, aangezien de methode\u00a0<code>get_posts<\/code>\u00a0antwoordt met <code>PostInterface[]<\/code>:<\/p>\n<pre><code class=\"language-php\">namespace Owner\\MyAppForWP\\ContractImplementations;\n\nuse Owner\\MyApp\\Contracts\\PostAPIInterface;\nuse WP_Post;\n\nclass PostAPI implements PostAPIInterface\n{\n  public function get_posts(array $args = null): PostInterface[]|int[]\n  {\n    \/\/ This var will contain WP_Post[] or int[]\n    $wpPosts = \\get_posts($args);\n\n    \/\/ Convert WP_Post[] to PostWrapper[]\n    return array_map(\n      function (WP_Post|int $post) {\n        if ($post instanceof WP_Post) {\n          return new PostWrapper($post);\n        }\n        return $post\n      },\n      $wpPosts\n    );\n  }\n}\n<\/code><\/pre>\n<h2>Gebruik maken van injectie van dependencies<\/h2>\n<p>De injectie van dependencies, oftewel stukken code waar het programma van afhankelijk is, is een ontwerpmethode waarmee je alle onderdelen van de toepassing kan samenvoegen. Met dependency injectie kan de toepassing services gebruiken via de bijbehorende contracten, en de implementaties van die contracten worden dan &#8220;ge\u00efnjecteerd&#8221; in de toepassing via de configuratie.<\/p>\n<p>Door alleen de configuratie te wijzigen, kunnen we dan eenvoudig wisselen tussen contractproviders. Er zijn diverse libraries voor dependency injection die we kunnen gebruiken. We raden je aan om \u00e9\u00e9n te kiezen die de\u00a0<a href=\"https:\/\/www.php-fig.org\/\">PHP Standard Recommendations<\/a>\u00a0(PSR, oftewel PHP standaard aanbevelingen) volgt, zodat we de library eenvoudiger kunnen vervangen, mocht dat nodig zijn. Voor de dependency injectie moet de library voldoen aan <a href=\"https:\/\/www.php-fig.org\/psr\/psr-11\/\">PSR-11<\/a>, die de specificatie bepaalt voor een &#8220;container interface&#8221;. De volgende libraries, onder andere, voldoen aan PSR-11:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/symfony\/dependency-injection\">Symfony\u2019s DependencyInjection<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/PHP-DI\/PHP-DI\">PHP-DI<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/auraphp\/Aura.Di\" target=\"_blank\" rel=\"noopener noreferrer\">Aura.Di<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/thephpleague\/container\">Container (Dependency Injection)<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/yiisoft\/di\">Yii Dependency Injection<\/a><\/li>\n<\/ul>\n<h3>Services gebruiken via de service container<\/h3>\n<p>De library voor dependency injectie zal een &#8220;service container&#8221; beschikbaar maken, die een contract verwerkt naar de bijbehorende class voor implementatie. De toepassing moet de service container gebruiken om toegang te krijgen tot alle functionaliteit. Ter illustratie, zo zouden we normaliter direct WordPress functies gebruiken:<\/p>\n<pre><code class=\"language-php\">$posts = get_posts();\n<\/code><\/pre>\n<p>&#8230; maar met de servicecontainer, moeten we eerst de service vinden die voldoet aan <code>PostAPIInterface<\/code> en de functie via die interface uitvoeren:<\/p>\n<pre><code class=\"language-php\">use Owner\\MyApp\\Contracts\\PostAPIInterface;\n\n\/\/ Obtain the service container, as specified by the library we use\n$serviceContainer = ContainerBuilderFactory::getInstance();\n\n\/\/ The obtained service will be of class Owner\\MyAppForWP\\ContractImplementations\\PostAPI\n$postAPI = $serviceContainer-&gt;get(PostAPIInterface::class);\n\n\/\/ Now we can invoke the WordPress functionality\n$posts = $postAPI-&gt;get_posts();\n<\/code><\/pre>\n<h3>Symfony\u2019s DependencyInjection gebruiken<\/h3>\n<p><a href=\"https:\/\/symfony.com\/doc\/current\/components\/dependency_injection.html\">Symfony\u2019s DependencyInjection component<\/a>\u00a0is momenteel de meest gebruikte library voor dependency injectie. Je kan de servicecontainer hierbij configureren via PHP, YAML of XML. Om bijvoorbeeld te defini\u00ebren dat het contract <code>PostAPIInterface<\/code> wordt geregeld via de class <code>PostAPI<\/code> kan je in YAML zo configureren:<\/p>\n<pre><code class=\"language-yaml\">services:\n  Owner\\MyApp\\Contracts\\PostAPIInterface:\n    class: \\Owner\\MyAppForWP\\ContractImplementations\\PostAPI\n<\/code><\/pre>\n<p>Symfony\u2019s DependencyInjection biedt ook de mogelijkheid dat instances van de ene service automatisch ge\u00efnjecteerd worden (ook wel &#8220;autowired&#8221; genoemd) in een andere service die dit nodig heeft. Daarnaast is het makkelijk om een class te defini\u00ebren die een implementatie is van de eigen service: Kijk bijvoorbeeld eens naar <a href=\"https:\/\/github.com\/leoloso\/PoP\/blob\/8602e9041ac92f4683966eed5c4a90686b9395fa\/layers\/GraphQLAPIForWP\/plugins\/graphql-api-for-wp\/config\/hybrid-services.yaml\">onderstaande YAML configuratie<\/a>:<\/p>\n<pre><code class=\"language-yaml\">services:\n  _defaults:\n    public: true\n    autowire: true\n\n  GraphQLAPI\\GraphQLAPI\\Registries\\UserAuthorizationSchemeRegistryInterface:\n    class: '\\GraphQLAPI\\GraphQLAPI\\Registries\\UserAuthorizationSchemeRegistry'\n\n  GraphQLAPI\\GraphQLAPI\\Security\\UserAuthorizationInterface:\n    class: '\\GraphQLAPI\\GraphQLAPI\\Security\\UserAuthorization'\n    \n  GraphQLAPI\\GraphQLAPI\\Security\\UserAuthorizationSchemes\\:\n    resource: '..\/src\/Security\/UserAuthorizationSchemes\/*'\n<\/code><\/pre>\n<p>Deze configuratie definieert het volgende:<\/p>\n<ul>\n<li>Contract\u00a0<code>UserAuthorizationSchemeRegistryInterface<\/code> wordt voldaan via class\u00a0<code>UserAuthorizationSchemeRegistry<\/code><\/li>\n<li>Contract\u00a0<code>UserAuthorizationInterfacewordt<\/code> voldaan via class\u00a0<code>UserAuthorization<\/code><\/li>\n<li>Alle classes onder de map\u00a0<code>UserAuthorizationSchemes<\/code>\/zijn een implementatie van zichzelf<\/li>\n<li>Services moeten automatisch bij elkaar ge\u00efnjecteerd worden (<code>autowire: true<\/code>)<\/li>\n<\/ul>\n<p>Laten we eens kijken hoe dit &#8220;autowiring&#8221; werkt. De class\u00a0<a href=\"https:\/\/github.com\/leoloso\/PoP\/blob\/bd56f23b52a3e02966fdcbde4bc58f246a47a6e5\/layers\/GraphQLAPIForWP\/plugins\/graphql-api-for-wp\/src\/Security\/UserAuthorization.php\"><code>UserAuthorization<\/code><\/a> is afhankelijk van een service met het contract <code>UserAuthorizationSchemeRegistryInterface<\/code>:<\/p>\n<pre><code class=\"language-php\">class UserAuthorization implements UserAuthorizationInterface\n{\n  public function __construct(\n      protected UserAuthorizationSchemeRegistryInterface $userAuthorizationSchemeRegistry\n  ) {\n  }\n\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Dankzij\u00a0<code>autowire<\/code>: true, zal de component DependencyInjection component automatisch zorgen dat de service\u00a0<code>UserAuthorization<\/code>\u00a0de vereiste dependency ontvangt, wat in dit geval een instance is van <code>UserAuthorizationSchemeRegistry<\/code>.<\/p>\n<h2>Wanneer je abstractie moet gebruiken<\/h2>\n<p>Abstractie van code kan veel tijd en moeite kosten, dus we moeten er alleen aan beginnen als het moeite waard is. Onderstaande voorbeelden zijn situaties waarin abstractie de investering waard is. Je kan dit dan doen door de stukjes code uit dit artikel te gebruiken, of via de WordPress abstractieplugins die we verderop aanbevelen.<\/p>\n<h3>Gaining Access to Tooling<\/h3>\n<p>Zoals eerder genoemd is het <a href=\"https:\/\/github.com\/humbug\/php-scoper\/issues\/303\">uitvoeren van PHP-Scoper op WordPress erg moeilijk<\/a>. Door de WordPress code te ontkoppelen via specifieke packages, wordt het mogelijk om <a href=\"https:\/\/graphql-api.com\/blog\/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper\/\">een WordPress plugin direct te scopen<\/a>.<\/p>\n<h3>Verminderen van tijd en kosten van tooling<\/h3>\n<p>Het uitvoeren van een PHPUnit testpakket duurt langer wanneer WordPress ook ge\u00efnitialiseerd en uitgevoerd moet worden. Wanneer je deze extra tijd kan besparen, kan dat ook minder kosten betekenen voor het uitvoeren van de tests. Zo betaal je bij <a href=\"https:\/\/kinsta.com\/blog\/what-is-github\/\">GitHub<\/a> Actions bijvoorbeeld voor GitHub-hosted runners op basis van de hoeveelheid gebruikte tijd.<\/p>\n<h3>Minder ingrijpende refactoring nodig<\/h3>\n<p>Een bestaand project kan ingrijpende en vergaande refactoring nodig hebben om vereiste architectuur te kunnen implementeren (dependency injectie, opsplitsen naar packages, etc.), wat ergens veeleisend kan worden. Door codeabstractie toe te passen vanaf het begin van een project, kan een project beter beheersbaar blijven.<\/p>\n<h3>Code voor verschillende platforms maken<\/h3>\n<p>Door 90% van de code in een CMS-onafhankelijk package te zetten, maken we een versie van onze toepassing die op een andere CMS of framework kan werken na een aanpassing van slechts 10% van de code.<\/p>\n<h3>Migreren naar een ander platform<\/h3>\n<p>Als we een project moeten migreren van <a href=\"https:\/\/kinsta.com\/nl\/blog\/wordpress-versus-drupal\/\">Drupal naar WordPress<\/a>, van WordPress naar <a href=\"https:\/\/kinsta.com\/nl\/blog\/laravel-9\/\">Laravel<\/a>, of een andere combinatie, hoeft ook slechts 10% van de code herschreven te worden: een enorme besparing.<\/p>\n<h2>Best practices<\/h2>\n<p>Bij het ontwerpen van de contracten voor codeabstractie zijn er een aantal verbeteringen die we meteen op de codebase kunnen doorvoeren.<\/p>\n<h3>PSR-12 volgen<\/h3>\n<p>Bij het defini\u00ebren van de interface voor de toegang tot WordPress methoden, zouden we ons moeten houden aan <a href=\"https:\/\/www.php-fig.org\/psr\/psr-12\/\">PSR-12<\/a>. Deze recente specificatie is bedoeld om de cognitieve inspanning te verminderen bij het scannen van code van verschillende auteurs. Het volgen van PSR-12 betekent een nieuwe naam geven aan WordPress functies.<\/p>\n<p>WordPress gebruikt namen voor functies op basis van het format <strong>snake_case<\/strong>, terwijl PSR-12 <strong>camelCase<\/strong> aanraadt (dus een hoofdletter bij een nieuw woord, in plaats van underscores). <code>get_posts<\/code> zou dus bijvoorbeeld <code>getPosts<\/code> worden:<\/p>\n<pre><code class=\"language-php\">interface PostAPIInterface\n{\n  public function getPosts(array $args = null): PostInterface[]|int[];\n}\n<\/code><\/pre>\n<p>&#8230;en:<\/p>\n<pre><code class=\"language-php\">class PostAPI implements PostAPIInterface\n{\n  public function getPosts(array $args = null): PostInterface[]|int[]\n  {\n    \/\/ This var will contain WP_Post[] or int[]\n    $wpPosts = \\get_posts($args);\n\n    \/\/ Rest of the code\n    \/\/ ...\n  }\n}\n<\/code><\/pre>\n<h3>Methoden splitsen<\/h3>\n<p>Methoden in de interface hoeven geen kopie te zijn van de versies uit WordPress. We kunnen ze transformeren wanneer dat zin heeft. De WordPress functie\u00a0<code>get_user_by($field, $value)<\/code>\u00a0weet bijvoorbeeld hoe de gebruiker uit de database opgehaald kan worden via de parameter\u00a0<code>$field<\/code>, die gebruik kan maken van de waarden <code>\"id\"<\/code>,\u00a0<code>\"ID\"<\/code>,\u00a0<code>\"slug\"<\/code>,\u00a0<code>\"email\"<\/code>\u00a0of\u00a0<code>\"login\"<\/code>. Maar hier zitten enkele nadelen aan:<\/p>\n<ul>\n<li>Er zal geen fout doorgegeven worden bij de compilatie als we een verkeerde string invoeren<\/li>\n<li>Parameter <code>$value<\/code> moet alle verschillende typen voor alle opties accepteren, terwijl bij <code>\"ID\"<\/code> een <code>int<\/code> verwacht zou moeten worden, en bij <code>\"email\"<\/code> alleen een <code>string<\/code> gebruikt kan worden<\/li>\n<\/ul>\n<p>We kunnen deze situatie verbeteren door de functie op te splitsen:<\/p>\n<pre><code class=\"language-php\">namespace Owner\\MyApp\\Contracts;\n\ninterface UserAPIInterface\n{\n  public function getUserById(int $id): ?UserInterface;\n  public function getUserByEmail(string $email): ?UserInterface;\n  public function getUserBySlug(string $slug): ?UserInterface;\n  public function getUserByLogin(string $login): ?UserInterface;\n}\n<\/code><\/pre>\n<p>Dit contract wordt dan zo verwerkt bij WordPress (aangenomen dat we <code>UserWrapper<\/code> en <code>UserInterface<\/code>, al gemaakt hebben, zoals hierboven):<\/p>\n<pre><code class=\"language-php\">namespace Owner\\MyAppForWP\\ContractImplementations;\n\nuse Owner\\MyApp\\Contracts\\UserAPIInterface;\n\nclass UserAPI implements UserAPIInterface\n{\n  public function getUserById(int $id): ?UserInterface\n  {\n    return $this-&gt;getUserByProp('id', $id);\n  }\n\n  public function getUserByEmail(string $email): ?UserInterface\n  {\n    return $this-&gt;getUserByProp('email', $email);\n  }\n\n  public function getUserBySlug(string $slug): ?UserInterface\n  {\n    return $this-&gt;getUserByProp('slug', $slug);\n  }\n\n  public function getUserByLogin(string $login): ?UserInterface\n  {\n    return $this-&gt;getUserByProp('login', $login);\n  }\n\n  private function getUserByProp(string $prop, int|string $value): ?UserInterface\n  {\n    if ($user = \\get_user_by($prop, $value)) {\n      return new UserWrapper($user);\n    }\n    return null;\n  }\n}\n<\/code><\/pre>\n<h3>Verwijderen van details van de implementatie uit de handtekening van de functie<\/h3>\n<p>Functies in WordPress kunnen informatie bieden over hoe ze ge\u00efmplementeerd worden in hun eigen &#8216;handtekening&#8217;, oftewel de signature. Deze informatie kan verwijderd worden om de functie vanuit een abstract principe te kunnen gebruiken. Zo kan bijvoorbeeld het ophalen van de achternaam van een gebruiker in WordPress gedaan worden via <code>get_the_author_meta<\/code>, waarbij het expliciet is dat de achternaam van de gebruiker opgeslagen is als een &#8220;meta&#8221; waarde (in de tabel <code>wp_usermeta<\/code>):<\/p>\n<pre><code class=\"language-php\">$userLastname = get_the_author_meta(\"user_lastname\", $user_id);\n<\/code><\/pre>\n<p>Maar je hoeft deze informatie niet over te brengen naar het contract. Interfaces zijn alleen maar ge\u00efnteresseerd in het &#8220;wat&#8221;, niet in het &#8220;hoe&#8221;. Daarom kan het contract ook een methode <code>get_UserLastname<\/code> hebben, zonder verdere informatie over de implementatie:<\/p>\n<pre><code class=\"language-php\">interface UserAPIInterface\n{\n  public function getUserLastname(UserWrapper $userWrapper): string;\n  ...\n}\n<\/code><\/pre>\n<h3>Voeg striktere types toe<\/h3>\n<p>Sommige WordPress functies kunnen op verschillende manieren parameters ontvangen, wat onduidelijkheid veroorzaakt. Zo kan bijvoorbeeld de functie <code>add_query_arg<\/code> een enkele combinatie van key en value accepteren:<\/p>\n<pre><code class=\"language-php\">$url = add_query_arg('id', 5, $url);\n<\/code><\/pre>\n<p>&#8230;of een hele array van <code>key =&gt; value<\/code>:<\/p>\n<pre><code class=\"language-php\">$url = add_query_arg(['id' =&gt; 5], $url);\n<\/code><\/pre>\n<p>Onze interface kan een veel vollediger doel defini\u00ebren door de functies in verschillende aparte delen op te splitsen, die allemaal een eigen unieke combinatie van inputs accepteren:<\/p>\n<pre><code class=\"language-php\">public function addQueryArg(string $key, string $value, string $url);\npublic function addQueryArgs(array $keyValues, string $url);\n<\/code><\/pre>\n<h3>Technische achterstanden verwijderen<\/h3>\n<p>De WordPress functie <code>get_posts<\/code> stuurt niet alleen &#8220;posts&#8221; terug, maar ook &#8220;pages&#8221; van elke entiteit van het type &#8220;custom posts&#8221;, maar die entiteiten zijn niet uitwisselbaar. Zowel artikelen als pagina&#8217;s zijn custom artikelen, maar een pagina is geen artikel. Daarom kan het uitvoeren van <code>get_posts<\/code> pagina&#8217;s terugsturen. Dit gedrag is conceptueel gezien vreemd.<\/p>\n<p>Om dat te corrigeren, zou <code>get_posts<\/code> eigenlijk <code>get_customposts<\/code> genoemd moeten worden, maar dat is nooit gebeurd in de WordPress core. Dit is een probleem dat je vaker ziet bij lang bestaande software en wordt &#8220;technische schuld&#8221; genoemd, namelijk code met problemen, die nooit opgelost worden omdat dat grote veranderingen zou betekenen.<\/p>\n<p>Maar bij het maken van onze contracten hebben we de mogelijkheid om dergelijke achterstanden te corrigeren. In dit geval maken we een nieuwe interface <code>ModelAPIInterface<\/code> die met entiteiten van verschillende typen kan werken, en maken we diverse methoden, die allemaal met een specifiek type kunnen werken:<\/p>\n<pre><code class=\"language-php\">interface ModelAPIInterface\n{\n  public function getPosts(array $args): array;\n  public function getPages(array $args): array;\n  public function getCustomPosts(array $args): array;\n}\n<\/code><\/pre>\n<p>Op deze manier verdwijnt deze discrepantie, en krijg je deze resultaten:<\/p>\n<ul>\n<li><code>getPosts<\/code> stuurt alleen artikelen<\/li>\n<li><code>getPages<\/code> stuurt alleen pagina&#8217;s<\/li>\n<li><code>getCustomPosts<\/code> stuurt zowel artikelen als pagina&#8217;s<\/li>\n<\/ul>\n<h2>Voordelen van abstractie van code<\/h2>\n<p>De voornaamste voordelen van de abstractie van de code van een toepassing zijn:<\/p>\n<ul>\n<li>Tooling kan op packages met alleen bedrijfsspecifieke code uitgevoerd worden, wat makkelijker op te zetten is en sneller (en dus goedkoper) is bij het uitvoeren.<\/li>\n<li>We kunnen tools gebruiken die niet met WordPress werken, zoals het scopen van een plugin met PHP-Scoper.<\/li>\n<li>De uiteindelijke packages kunnen eenvoudig autonoom zijn in het gebruik bij andere toepassingen.<\/li>\n<li>Het migreren van een toepassing naar een ander platform wordt veel makkelijker.<\/li>\n<li>We kunnen onze manier van denken losmaken van puur WordPress en meer op een zakelijke manier gaan denken.<\/li>\n<li>De contracten omschrijven de bedoeling van de toepassing, wat makkelijker te begrijpen is.<\/li>\n<li>De toepassing kan georganiseerd worden via packages, waardoor je een lean toepassing krijgt met alleen dat wat echt nodig is, en die verbeterd wordt waar nodig.<\/li>\n<li>We kunnen technische schulden wegwerken.<\/li>\n<\/ul>\n<h2>Problemen met abstractie van code<\/h2>\n<p>Er zijn ook nadelen aan de abstractie van de code van een toepassing:<\/p>\n<ul>\n<li>Het betekent een aanzienlijke hoeveelheid werk.<\/li>\n<li>Code wordt uitgebreider, je hebt meer lagen code nodig voor hetzelfde resultaat.<\/li>\n<li>Het zou zomaar kunnen dat je <a href=\"https:\/\/graphql-api.com\/blog\/why-to-support-cms-agnosticism-the-graphql-api-split-to-around-90-packages\/\">tientallen packages<\/a> moet maken die allemaal beheerd en onderhouden moeten worden.<\/li>\n<li>Je hebt wellicht een monorepo nodig om alle packages te kunnen beheren.<\/li>\n<li>Dependency injectie kan te veel werk zijn voor eenvoudige toepassingen.<\/li>\n<li>Je kan nooit tot volledige abstractie van de code komen, omdat er meestal een algemene impliciete voorkeur verwerkt zit in de architectuur van het CMS.<\/li>\n<\/ul>\n<h2>Mogelijkheden voor abstractie met WordPress plugins<\/h2>\n<p>Although it&#8217;s generally wisest to extract your code to a <a href=\"https:\/\/kinsta.com\/nl\/blog\/lokale-wordpress-installatie\/\">local environment<\/a> before working on it, some WordPress plugins can help you toward your abstraction goals. These are our top picks.<\/p>\n<h3>1. WPide<\/h3>\n<p>De populaire <a href=\"https:\/\/wordpress.org\/plugins\/wpide\/\">WPide plugin<\/a> van WebFactory breidt de functionaliteit van de standaard WordPress code-editor aanzienlijk uit. Het dient als een WordPress abstractieplugin omdat je de code op de plek van implementatie kan bekijken, waardoor je beter ziet wat er nodig is.<\/p>\n<figure>\n<p><figure style=\"width: 900px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/08\/wpide-plugin.jpg\" alt=\"De WPide plugin\" width=\"900\" height=\"291\"><figcaption class=\"wp-caption-text\">De WPide plugin<\/figcaption><\/figure><\/figure>\n<p>WPide heeft ook een functie voor zoeken en vervangen, waarmee je snel verouderde code kan opzoeken en meteen kan vervangen met een verbeterde versie.<\/p>\n<p>Daarnaast biedt WPide nog allerlei andere features, zoals:<\/p>\n<ul>\n<li>Markeren van syntax en blocks<\/li>\n<li><a href=\"https:\/\/kinsta.com\/nl\/docs\/wordpress-hosting\/wordpress-backups\/#wordpress-backup\">Automatische back-ups<\/a><\/li>\n<li>Aanmaken van bestanden en mappen<\/li>\n<li>Browser voor het beheren van bestanden in de structuur<\/li>\n<li>Toegang tot de WordPress filesystem API<\/li>\n<\/ul>\n<h3>2. Ultimate DB Manager<\/h3>\n<p>De <a href=\"https:\/\/wordpress.org\/plugins\/ultimate-db-manager-lite\/\">Ultimate WP DB Manager plugin<\/a>\u00a0van WPHobby biedt een snelle manier om je databases te downloaden voor extractie en refactoring.<\/p>\n<figure>\n<p><figure style=\"width: 900px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/08\/ultimate-db-manager-plugin.jpg\" alt=\"De Ultimate DB Manager plugin\" width=\"900\" height=\"250\"><figcaption class=\"wp-caption-text\">De Ultimate DB Manager plugin<\/figcaption><\/figure><\/figure>\n<p>Natuurlijk zijn dit soort plugins niet echt nodig voor gebruikers van Kinsta, aangezien je bij Kinsta <a href=\"https:\/\/kinsta.com\/nl\/docs\/wordpress-hosting\/databasebeheer\/wordpress-database-toegang\/\">directe toegang tot je database<\/a> krijgt, voor alle klanten. Maar als je momenteel niet genoeg toegang tot je database krijgt van je hostingprovider, kan Ultimate DB Manager een handige WordPress plugin voor abstractie zijn.<\/p>\n<h3>3. Je eigen custom WordPress abstractieplugin<\/h3>\n<p>Uiteindelijk is de beste keus voor abstractie altijd het maken van je eigen plugin. Dat lijkt misschien een groot project, maar als je maar beperkte directe toegang tot de bestanden van je WordPress core hebt, kan dit een handige oplossing zijn.<\/p>\n<p>Er zijn ook andere voordelen:<\/p>\n<ul>\n<li>Abstractie van je functies van je themabestanden<\/li>\n<li>Bewaar je code ook bij updates van je thema en database<\/li>\n<\/ul>\n<p>Je kan leren hoe je een WordPress abstractieplugin kan maken via het <a href=\"https:\/\/developer.wordpress.org\/plugins\/\">WordPress&#8217; Plugin Developer Handbook<\/a>.<\/p>\n\n<h2>Samenvatting<\/h2>\n<p>Moeten we abstractie gebruiken voor de code in onze toepassingen? Zoals met alles, is er niet \u00e9\u00e9n perfect antwoord, aangezien het altijd verschilt per project. Projecten die veel tijd kwijt zijn aan analyses met PHPUnit of PHPStan zullen het meeste voordeel hebben, maar het zal niet altijd de aanzienlijke investering waard zijn.<\/p>\n<p>Je hebt nu alles gelezen wat je moet weten om te beginnen met de abstractie van WordPress code.<\/p>\n<p><em>Ben je van plan om deze strategie ook toe te passen in je eigen project? Zo ja, ben je van plan om een WordPress abstractieplugin te gebruiken? Laat het ons weten in de reacties hieronder!<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>WordPress is een CMS dat al lang bestaat, maar is ook de meestgebruikte. Dankzij de traditie van het ondersteunen van verouderde PHP versies en legacy code, &#8230;<\/p>\n","protected":false},"author":196,"featured_media":40098,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[315,406],"topic":[892,899],"class_list":["post-40095","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-code","tag-web-development","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>WordPress abstractie: best practices en WordPress abstractieplugins<\/title>\n<meta name=\"description\" content=\"Leer hoe je met stukjes code of een WordPress abstractieplugin stationaire dependencies verwijdert uit de code en packages produceert die als interactor optreden.\" \/>\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\/nl\/blog\/wordpress-abstractieplugins\/\" \/>\n<meta property=\"og:locale\" content=\"nl_NL\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"WordPress abstractie: best practices en WordPress abstractieplugins\" \/>\n<meta property=\"og:description\" content=\"Leer hoe je met stukjes code of een WordPress abstractieplugin stationaire dependencies verwijdert uit de code en packages produceert die als interactor optreden.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/\" \/>\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=\"2021-09-22T21:29:49+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-06-06T09:42:59+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg\" \/>\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 met stukjes code of een WordPress abstractieplugin stationaire dependencies verwijdert uit de code en packages produceert die als interactor optreden.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg\" \/>\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:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinsta.com\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"WordPress abstractie: best practices en WordPress abstractieplugins\",\"datePublished\":\"2021-09-22T21:29:49+00:00\",\"dateModified\":\"2023-06-06T09:42:59+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/\"},\"wordCount\":3056,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/nl\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg\",\"keywords\":[\"code\",\"web development\"],\"articleSection\":[\"WordPress handleidingen\"],\"inLanguage\":\"nl-NL\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/\",\"url\":\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/\",\"name\":\"WordPress abstractie: best practices en WordPress abstractieplugins\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/nl\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg\",\"datePublished\":\"2021-09-22T21:29:49+00:00\",\"dateModified\":\"2023-06-06T09:42:59+00:00\",\"description\":\"Leer hoe je met stukjes code of een WordPress abstractieplugin stationaire dependencies verwijdert uit de code en packages produceert die als interactor optreden.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#breadcrumb\"},\"inLanguage\":\"nl-NL\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg\",\"width\":1460,\"height\":730,\"caption\":\"WordPress abstractie: best practices en WordPress abstractieplugins\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/nl\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"WordPress ontwikkeling\",\"item\":\"https:\/\/kinsta.com\/nl\/onderwerpen\/wordpress-ontwikkeling\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"WordPress abstractie: best practices en WordPress abstractieplugins\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/nl\/#website\",\"url\":\"https:\/\/kinsta.com\/nl\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Snelle, veilige, premium hostingoplossingen\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/nl\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/nl\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"nl-NL\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/nl\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/nl\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\/\/kinsta.com\/nl\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.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:\/\/kinsta.com\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\/\/kinsta.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:\/\/kinsta.com\/nl\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"WordPress abstractie: best practices en WordPress abstractieplugins","description":"Leer hoe je met stukjes code of een WordPress abstractieplugin stationaire dependencies verwijdert uit de code en packages produceert die als interactor optreden.","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\/nl\/blog\/wordpress-abstractieplugins\/","og_locale":"nl_NL","og_type":"article","og_title":"WordPress abstractie: best practices en WordPress abstractieplugins","og_description":"Leer hoe je met stukjes code of een WordPress abstractieplugin stationaire dependencies verwijdert uit de code en packages produceert die als interactor optreden.","og_url":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/Kinsta-Nederland-476213452787823\/","article_published_time":"2021-09-22T21:29:49+00:00","article_modified_time":"2023-06-06T09:42:59+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"Leer hoe je met stukjes code of een WordPress abstractieplugin stationaire dependencies verwijdert uit de code en packages produceert die als interactor optreden.","twitter_image":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg","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:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinsta.com\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"WordPress abstractie: best practices en WordPress abstractieplugins","datePublished":"2021-09-22T21:29:49+00:00","dateModified":"2023-06-06T09:42:59+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/"},"wordCount":3056,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/nl\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg","keywords":["code","web development"],"articleSection":["WordPress handleidingen"],"inLanguage":"nl-NL","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/","url":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/","name":"WordPress abstractie: best practices en WordPress abstractieplugins","isPartOf":{"@id":"https:\/\/kinsta.com\/nl\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg","datePublished":"2021-09-22T21:29:49+00:00","dateModified":"2023-06-06T09:42:59+00:00","description":"Leer hoe je met stukjes code of een WordPress abstractieplugin stationaire dependencies verwijdert uit de code en packages produceert die als interactor optreden.","breadcrumb":{"@id":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#breadcrumb"},"inLanguage":"nl-NL","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/"]}]},{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#primaryimage","url":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg","contentUrl":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/09\/wordpress-abstractieplugins.jpeg","width":1460,"height":730,"caption":"WordPress abstractie: best practices en WordPress abstractieplugins"},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/nl\/blog\/wordpress-abstractieplugins\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/nl\/"},{"@type":"ListItem","position":2,"name":"WordPress ontwikkeling","item":"https:\/\/kinsta.com\/nl\/onderwerpen\/wordpress-ontwikkeling\/"},{"@type":"ListItem","position":3,"name":"WordPress abstractie: best practices en WordPress abstractieplugins"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/nl\/#website","url":"https:\/\/kinsta.com\/nl\/","name":"Kinsta\u00ae","description":"Snelle, veilige, premium hostingoplossingen","publisher":{"@id":"https:\/\/kinsta.com\/nl\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/nl\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"nl-NL"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/nl\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/nl\/","logo":{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/kinsta.com\/nl\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.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:\/\/kinsta.com\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/kinsta.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:\/\/kinsta.com\/nl\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/posts\/40095","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/comments?post=40095"}],"version-history":[{"count":6,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/posts\/40095\/revisions"}],"predecessor-version":[{"id":53017,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/posts\/40095\/revisions\/53017"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40095\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40095\/translations\/it"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40095\/translations\/fr"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40095\/translations\/es"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40095\/translations\/pt"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40095\/translations\/de"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40095\/translations\/nl"},{"href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40095\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/media\/40098"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/media?parent=40095"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/tags?post=40095"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/topic?post=40095"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}