<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Leonardo Losoviz</title>
    <description>The latest articles on Forem by Leonardo Losoviz (@leoloso).</description>
    <link>https://forem.com/leoloso</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F397561%2F22fad974-b2e4-4480-a3a5-701f134b496b.jpeg</url>
      <title>Forem: Leonardo Losoviz</title>
      <link>https://forem.com/leoloso</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/leoloso"/>
    <language>en</language>
    <item>
      <title>🙅‍♀️ Why GraphQL should not be in WordPress core</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Fri, 27 Aug 2021 14:01:56 +0000</pubDate>
      <link>https://forem.com/leoloso/why-graphql-should-not-be-in-wordpress-core-4ie1</link>
      <guid>https://forem.com/leoloso/why-graphql-should-not-be-in-wordpress-core-4ie1</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://graphql-api.com/blog/why-graphql-should-not-be-in-wordpress-core/"&gt;graphql-api.com/blog/why-graphql-should-not-be-in-wordpress-core&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;Yep, you read that title correctly. Even though I am myself the creator of a GraphQL server for WordPress, I've changed my mind concerning if WordPress should ship with GraphQL or not.&lt;/p&gt;

&lt;p&gt;Until not long ago, I believed that &lt;a href="https://graphql-api.com/blog/why-wordpress-should-have-a-graphql-api-in-core/"&gt;GraphQL should be in WordPress core&lt;/a&gt;. The logic was that contributors were spending time and effort on implementing functionality for the WP REST API (batch operations) which is native to GraphQL.&lt;/p&gt;

&lt;p&gt;However, I have lately learnt some new information which made me think again, and now I believe WordPress should not ship with GraphQL, because of the added risks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--htTDolCw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/graphql-wordpress-threat.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--htTDolCw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/graphql-wordpress-threat.jpg" alt="GraphQL in WordPress core? 😁" title="GraphQL in WordPress core? 😁"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are my reasons.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. It doesn't satisfy the 80/20 rule
&lt;/h2&gt;

&lt;p&gt;Historically, a certain functionality is added to WordPress core only if it satisfies &lt;a href="https://wordpress.org/about/philosophy/"&gt;the 80/20 rule&lt;/a&gt;, meaning that 80% or more of the users will use it.&lt;/p&gt;

&lt;p&gt;Would that be the case with GraphQL? I think the answer is "no", based on the precedent from the introduction of the WP REST API to WordPress 4.7.&lt;/p&gt;

&lt;p&gt;In his talk &lt;a href="https://youtu.be/OBVg5x8uQ6M"&gt;WordPress as Data, 5 Years In&lt;/a&gt;, K. Adam White (main lead of the initial development and release of the WP REST API) described that the contributors expected the REST API to be widely used once it was released with core. But that didn't happen: developers kept creating WordPress sites the same way as before, paying little attention to "headless" or the REST API.&lt;/p&gt;

&lt;p&gt;Fortunes changed only later, with the introduction of the Gutenberg editor in WordPress 5.0, which was based on the REST API. Could Gutenberg then justify the addition of GraphQL to WordPress core?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fl0Rh-cm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/society-if-wp-had-a-rest-api-video.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fl0Rh-cm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/society-if-wp-had-a-rest-api-video.png" alt="Expected future with the REST API. Screenshot from K. Adam White's talk" title="Expected future with the REST API. Screenshot from K. Adam White's talk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Headless is already satisfied via the REST API
&lt;/h2&gt;

&lt;p&gt;The WordPress editor can be enhanced with a native GraphQL server, allowing block developers to use GraphQL (in addition to the existing REST API) to fetch data for their blocks. In addition, themes and plugins could make use of GraphQL to power their own internal functionality. These are strong reasons to add GraphQL to WordPress core.&lt;/p&gt;

&lt;p&gt;However, WordPress already has the REST API, and &lt;a href="https://stepzen.com/blog/are-rest-and-graphql-different"&gt;whatever you can do with GraphQL can also be done with REST&lt;/a&gt;. Introducing GraphQL in addition to REST is akin to buying a BMW when you're already driving a Toyota. You will reach your destination faster, and the driving experience will be more appealing. But both cars will take you to where you want to go.&lt;/p&gt;

&lt;p&gt;Because GraphQL will not provide a previously-unavailable functionality, then its inclusion in core is not fully justified. GraphQL would certainly enhance the experience of interacting with the API, but this could be perfectly considered plugin-land.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_E-tf19u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://graphql-api.com/images/graphiql.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_E-tf19u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://graphql-api.com/images/graphiql.gif" alt="GraphQL improves the experience of interacting with the API, but it doesn't create anything new" title="GraphQL improves the experience of interacting with the API, but it doesn't create anything new"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. WordPress themes and plugins can use &lt;code&gt;webonyx/graphql-php&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Public plugins cannot require a website to install either WPGraphQL or the GraphQL API for WP in order to use the plugin, since that will diminish their potential reach. As such, &lt;a href="https://www.smashingmagazine.com/2021/04/making-graphql-work-in-wordpress/#use-graphql-api-if-distributing-blocks-via-a-plugin"&gt;public plugins cannot rely on GraphQL&lt;/a&gt;, and that's a real pity.&lt;/p&gt;

&lt;p&gt;I thought hard about this issue, and I came up with a potential solution: the &lt;a href="https://github.com/leoloso/PoP/issues/519"&gt;GraphQL API Private&lt;/a&gt;, a self-contained GraphQL engine that plugins can embed for their own use, distributed as a Composer package. (I haven't started working on this project yet.)&lt;/p&gt;

&lt;p&gt;But then, a few weeks ago, a GraphQL-powered WordPress plugin &lt;a href="https://twitter.com/swashata/status/1418387987511996418"&gt;was released&lt;/a&gt;. I wondered how the author did it: would it be using WPGraphQL or the GraphQL API for WP under the hood? So I checked &lt;a href="https://plugins.trac.wordpress.org/browser/wpeform-lite/trunk/inc/GraphQL/Schema.php?rev=2570462"&gt;its source code&lt;/a&gt; and, as it turns out, it's directly using &lt;a href="https://github.com/webonyx/graphql-php"&gt;&lt;code&gt;webonyx/graphql-php&lt;/code&gt;&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;This is an interesting solution, which demonstrates that, with a bit of effort, developers currently do have access to GraphQL for their themes and plugins.&lt;/p&gt;

&lt;p&gt;This plugin uses GraphQL to fetch its own data entities, and not those of WordPress (posts, users, comments, etc). Then, it doesn't need to recreate the GraphQL schema containing the WordPress data model, as done by WPGraphQL and the GraphQL API for WP (and eventually the GraphQL API Private). As such, relying on &lt;code&gt;webonyx/graphql-php&lt;/code&gt; makes sense.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5wm8Intl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/webonyx-graphql-php.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5wm8Intl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/webonyx-graphql-php.png" alt="webonyx/graphql-php is a 'PHP port of GraphQL reference implementation'" title="webonyx/graphql-php is a 'PHP port of GraphQL reference implementation'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. GraphQL presents additional risks
&lt;/h2&gt;

&lt;p&gt;All three issues above suggest that GraphQL would enhance WordPress, even though it's not extremely compelling. In this light, we could still add GraphQL to WordPress core, and either benefit from it or nothing happens.&lt;/p&gt;

&lt;p&gt;But this 4th issue suggests that, if GraphQL will not add much value to WordPress, then it should not be added, because of its added risks.&lt;/p&gt;

&lt;p&gt;GraphQL is susceptible to the following attack vectors (among others):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The single endpoint provides access to all information from the website, so we could have private data unintentionally exposed.&lt;/li&gt;
&lt;li&gt;The queries can be very complex and may overwhelm the web and database servers.&lt;/li&gt;
&lt;li&gt;The same mutation can be executed multiple times in a single query, and multiple queries can be executed together in a single request, allowing attackers to attempt gaining access to the back-end by providing many combinations of user/passwords.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These attacks can be really damaging. In his presentation &lt;a href="https://www.youtube.com/watch?v=EVRf708-zq4"&gt;Damn GraphQL - Defending and Attacking APIs&lt;/a&gt;, the cybersecurity researcher Dolev Farhi managed to bring down a WordPress site in less than 30 seconds, by attacking the WPGraphQL endpoint with a batch of complex queries.&lt;/p&gt;

&lt;p&gt;The WPGraphQL team &lt;a href="https://github.com/wp-graphql/wp-graphql/releases/tag/v1.3.6"&gt;fixed the issue immediately&lt;/a&gt;. But how can we be sure that no other exploit can take place? (I mean not only WPGraphQL, but &lt;a href="https://github.com/leoloso/PoP/issues/865"&gt;the GraphQL API for WP too&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;These attacks can happen with GraphQL, and not with REST, because GraphQL is more powerful than REST. While in REST the query is defined in advance and stored in the server, in GraphQL it is provided on runtime by the client (unless using &lt;a href="https://graphql-api.com/features/#heading-persisted-queries"&gt;persisted queries&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;If website admins are sloppy configuring who can access the endpoint, or which data gets exposed, then bad things can happen. And due to the popularity of WordPress, which is used by millions of people who are not tech-savvy, then bad things will most likely happen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--10OVWdLO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/damn-graphql-video.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--10OVWdLO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/damn-graphql-video.png" alt="Attacking the GraphQL endpoint to bring a WordPress site down. Screenshot from Dolev Farhi's talk" title="Attacking the GraphQL endpoint to bring a WordPress site down. Screenshot from Dolev Farhi's talk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Just to be sure: I am not advocating to not use GraphQL in WordPress (of course I am not!), but to use GraphQL responsibly. GraphQL is powerful, which means it is dangerous. When using GraphQL, we need to be sure we know what we are doing.&lt;/p&gt;

&lt;p&gt;Shipping GraphQL in WordPress core would put it in the hands a lot of people, many of which will not be aware of its risks, and not take appropriate measures. It's a recipe for potential disaster. And as such, it is now my opinion, it should be avoided.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>graphql</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Version 0.8 of the GraphQL API for WordPress is now available 🚀</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Sat, 31 Jul 2021 09:46:03 +0000</pubDate>
      <link>https://forem.com/leoloso/version-0-8-of-the-graphql-api-for-wordpress-is-now-available-30me</link>
      <guid>https://forem.com/leoloso/version-0-8-of-the-graphql-api-for-wordpress-is-now-available-30me</guid>
      <description>&lt;p&gt;Version &lt;code&gt;0.8&lt;/code&gt; of the GraphQL API for WordPress is now &lt;a href="https://graphql-api.com/download"&gt;available for download&lt;/a&gt; 🎉&lt;/p&gt;

&lt;p&gt;This release focuses on three areas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Refactoring the codebase to enable extensions&lt;/li&gt;
&lt;li&gt;Further satisfying the GraphQL specification&lt;/li&gt;
&lt;li&gt;Completing the GraphQL schema&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Release notes: &lt;a href="https://graphql-api.com/blog/released-graphql-api-v08/"&gt;https://graphql-api.com/blog/released-graphql-api-v08/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>wordpress</category>
      <category>webdev</category>
      <category>news</category>
    </item>
    <item>
      <title>👨🏻‍💻 Demoing the GraphQL API for WordPress</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Thu, 22 Apr 2021 14:25:39 +0000</pubDate>
      <link>https://forem.com/leoloso/demoing-the-graphql-api-for-wordpress-jfa</link>
      <guid>https://forem.com/leoloso/demoing-the-graphql-api-for-wordpress-jfa</guid>
      <description>&lt;p&gt;Several weeks ago I talked about GraphQL on WordPress, for WordCamp India 2021. I explored these topics:&lt;/p&gt;

&lt;p&gt;👉🏽 What is GraphQL?&lt;br&gt;
👉🏽 How is it different from REST?&lt;br&gt;
👉🏽 How do we use it with WordPress?&lt;/p&gt;

&lt;p&gt;Alongside, I did a demo of the &lt;a href="https://graphql-api.com/"&gt;GraphQL API for WordPress&lt;/a&gt; plugin. It's all me showing how to use the plugin in the wp-admin, for some 30 min.&lt;/p&gt;

&lt;p&gt;Here is the video:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/LnyNyT2RwwI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The following features were covered:&lt;/p&gt;

&lt;p&gt;✅ The GraphiQL and Voyager clients&lt;br&gt;
✅ Persisted queries&lt;br&gt;
✅ Passing parameters by URL to the persisted query&lt;br&gt;
✅ HTTP caching&lt;br&gt;
✅ Access control&lt;br&gt;
✅ Making the API public and private&lt;/p&gt;

&lt;p&gt;We also had a Q&amp;amp;A session, which lasted 15 min. Here is the video:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/-Z6PgfreTxg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The Q&amp;amp;A include the following questions, and at what time they appear:&lt;/p&gt;

&lt;p&gt;❓ Will the hosting provider support GraphQL, or do we need to manage it on our own? &lt;em&gt;(1:25)&lt;/em&gt;&lt;br&gt;
❓ How extensible is it, compared to the WP REST API? &lt;em&gt;(1:55)&lt;/em&gt;&lt;br&gt;
❓ Is multilingual search supported in GraphQL? (answer is not clear, connection problems) &lt;em&gt;(2:39)&lt;/em&gt;&lt;br&gt;
❓ How do we secure the GraphQL API? &lt;em&gt;(3:48)&lt;/em&gt;&lt;br&gt;
❓ Why is the GraphQL API for WordPress plugin not officially available in the WordPress plugin repository? &lt;em&gt;(6:48)&lt;/em&gt;&lt;br&gt;
❓ What is the limit for GraphQL? &lt;em&gt;(8:08)&lt;/em&gt;&lt;br&gt;
❓ If somebody wants to dive into GraphQL, what's your advice for them? &lt;em&gt;(10:21)&lt;/em&gt;&lt;br&gt;
❓ What can we expect from you should you present a new talk about GraphQL in another WordCamp? &lt;em&gt;(12:08)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As I mentioned at the end of the Q&amp;amp;A, if you like what you see, and you'd like to learn more, or get your hands dirty using it and need help, &lt;a href="https://graphql-api.com/contact/"&gt;send me an email&lt;/a&gt; or &lt;a href="https://twitter.com/losoviz"&gt;DM on Twitter&lt;/a&gt;. I'm generally available, and always thrilled to work on the combination of WordPress and GraphQL 🙏&lt;/p&gt;

&lt;p&gt;I hope you enjoy the demo 😀&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>wordpress</category>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>👶🏻 Rejuvenating WordPress through GraphQL</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Thu, 15 Apr 2021 12:52:58 +0000</pubDate>
      <link>https://forem.com/leoloso/rejuvenating-wordpress-through-graphql-3fb</link>
      <guid>https://forem.com/leoloso/rejuvenating-wordpress-through-graphql-3fb</guid>
      <description>&lt;p&gt;WordPress is a legacy CMS: having been invented over 17 years ago, it's filled with PHP code that, given a new chance, it would be coded in a different way.&lt;/p&gt;

&lt;p&gt;GraphQL is a modern interface to access data. Please notice the word "interface": it doesn't care how the underlying data system is implemented, but only how to expose the data.&lt;/p&gt;

&lt;p&gt;What happens when we put these two together? How should we design the GraphQL interface to access data from WordPress?&lt;/p&gt;

&lt;p&gt;There are a couple of obvious strategies that we can put in place:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Respect tradition, and provide a mapping that keeps the WordPress data model as is, including the technical debt it accumulated during the years&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix the technical debt, providing an interface exposing data in an abstract, not-necessarily-fixed-to-WordPress way&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both approaches have benefits and drawbacks, and there is no right or wrong. It's just opinionatedness, prioritizing some behavior over another.&lt;/p&gt;

&lt;p&gt;For plugin &lt;a href="https://graphql-api.com" rel="noopener noreferrer"&gt;GraphQL API for WordPress&lt;/a&gt; I have chosen the latter approach, attempting to create a GraphQL schema that, even though it is based on WordPress and works for WordPress, it is not tied to WordPress (for instance, by removing inconsistent names and relationships).&lt;/p&gt;

&lt;p&gt;The result is that GraphQL rejuvenates WordPress: while we still have WordPress as our underlying CMS, with its legacy PHP code, its data layer can be created anew, based on common sense, not tradition. The data layer goes back from being an adolescent, to become a toddler again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fown-good-together-best.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fown-good-together-best.jpg" alt="GraphQL + WordPress rock"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result is &lt;a href="https://newapi.getpop.org/graphql-interactive/" rel="noopener noreferrer"&gt;this GraphQL schema, representing the WordPress data model&lt;/a&gt;, and also &lt;a href="https://newapi.getpop.org/graphql-interactive/?mutation_scheme=nested" rel="noopener noreferrer"&gt;supporting nested mutations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's check out it was carried out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The WordPress data model
&lt;/h2&gt;

&lt;p&gt;WordPress has the following entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;posts&lt;/li&gt;
&lt;li&gt;pages&lt;/li&gt;
&lt;li&gt;custom posts&lt;/li&gt;
&lt;li&gt;media elements&lt;/li&gt;
&lt;li&gt;users&lt;/li&gt;
&lt;li&gt;user roles&lt;/li&gt;
&lt;li&gt;tags&lt;/li&gt;
&lt;li&gt;categories&lt;/li&gt;
&lt;li&gt;comments&lt;/li&gt;
&lt;li&gt;blocks&lt;/li&gt;
&lt;li&gt;meta properties&lt;/li&gt;
&lt;li&gt;others (options, plugins, themes, etc)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These entities can have a hierarchy. For instance, post, page and media elements are both custom post types, and tags and categories are both taxonomies.&lt;/p&gt;

&lt;p&gt;This is the WordPress database diagram, showing how data for all entities is stored:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fassets%2Fguides%2Fquery%2Fwp-data-model.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fassets%2Fguides%2Fquery%2Fwp-data-model.png" alt="The WordPress database diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the mapping an exact replica of the DB diagram?
&lt;/h2&gt;

&lt;p&gt;When mapping the WordPress database into a GraphQL schema, is the same diagrame above respected 1 to 1?&lt;/p&gt;

&lt;p&gt;No, it is not. While the database diagram is an actual implementation, GraphQL is an interface to access the data from the client. These two are related, but they can be different. GraphQL doesn't care about the database: it doesn't think in SQL commands, or know there are database tables called &lt;code&gt;wp_posts&lt;/code&gt; and &lt;code&gt;wp_users&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So we don't need to worry too much about the database diagram when creating the GraphQL schema for WordPress. That means that we can produce a GraphQL schema that fixes some of the technical debt from the WordPress data model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapping the WordPress data model as a GraphQL schema
&lt;/h2&gt;

&lt;p&gt;Let's do the mapping. First, we map the original entities as types, as much as possible. From the list of entities in the WordPress data model, we produce the following types for the GraphQL schema:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Post&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Page&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Media&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;User&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserRole&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PostTag&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PostCategory&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Comment&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, we add all the expected fields to every type. To represent the schema, we can use the SDL, or Schema Definition Language. (This is used for documentation purposes only; the plugin itself does not use SDL to codify the schema: it's all PHP code).&lt;/p&gt;

&lt;p&gt;These are the fields (among many others) for a &lt;code&gt;Post&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the fields (among many others) for a &lt;code&gt;User&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also create the corresponding connections, which are fields that return another entity (instead of a scalar, such as a number or a string). For instance, we represent a post having an author, and a user owning posts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fields and connections can also accept arguments. For instance, we enable &lt;code&gt;Post.date&lt;/code&gt; to be formatted, and &lt;code&gt;User.posts&lt;/code&gt; to search entries and limit their number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We keep doing this for all entities in the WordPress data model. Once we are done, we'll arrive at the GraphQL schema for WordPress, as visible using the Voyager client (available as "Interactive Schema" on the plugin's menu):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fassets%2Fguides%2Finteractive-schema.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fassets%2Fguides%2Finteractive-schema.png" alt="The GraphQL schema for WordPress"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This schema has similarities to the WordPress database diagram, but also many differences. Let's analyse them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operations without entity are mapped as Root fields
&lt;/h3&gt;

&lt;p&gt;In the WordPress database diagram represents how data is stored, so there is no "beginning". GraphQL, though, is an interface to retrieve data, hence there must be an initial stage from which to execute the query.&lt;/p&gt;

&lt;p&gt;This initial stage is the &lt;code&gt;Root&lt;/code&gt; type, or, to be more precise, the &lt;code&gt;QueryRoot&lt;/code&gt; and &lt;code&gt;MutationRoot&lt;/code&gt; types (to deal with queries and mutations, respectively).&lt;/p&gt;

&lt;p&gt;In these two types, we map all operations that do not depend on an entity, such as when executing &lt;code&gt;get_posts()&lt;/code&gt;, &lt;code&gt;get_users()&lt;/code&gt; or &lt;code&gt;wp_signon()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QueryRoot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MutationRoot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;logUserIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fields do not need to have the same name or signature as the operation they represent. For instance, calling field &lt;code&gt;logUserIn&lt;/code&gt; can be considered more suitable than &lt;code&gt;signOn&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  All mutations go under MutationRoot
&lt;/h3&gt;

&lt;p&gt;There are operations which do depend on an entity, such as &lt;code&gt;wp_update_post()&lt;/code&gt;, which is applied on some post. The corresponding mutation on the GraphQL schema must be added to the &lt;code&gt;MutationRoot&lt;/code&gt; type, because that's how GraphQL works.&lt;/p&gt;

&lt;p&gt;Then, this operation is mapped like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MutationRoot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;updatePost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This plugin also supports nested mutations, which are offered as an opt-in feature (because this is not standad GraphQL behavior). Then, mutations can also be added under any type, not just &lt;code&gt;MutationRoot&lt;/code&gt;. In this case, we obtain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dealing with custom posts
&lt;/h3&gt;

&lt;p&gt;There is no type inheritance in GraphQL. Hence, we can't have a type &lt;code&gt;CustomPost&lt;/code&gt;, and declare that &lt;code&gt;Post&lt;/code&gt; and &lt;code&gt;Page&lt;/code&gt; extend it.&lt;/p&gt;

&lt;p&gt;GraphQL offers two resources to compensate for this lack: interfaces and union types.&lt;/p&gt;

&lt;p&gt;For the first one, we create an interface &lt;code&gt;IsCustomPost&lt;/code&gt; for the schema, declaring all the fields expected from a custom post, and we define types &lt;code&gt;Post&lt;/code&gt; and &lt;code&gt;Page&lt;/code&gt; to implement the interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IsCustomPost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IsCustomPost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IsCustomPost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the second one, we create a &lt;code&gt;CustomPostUnion&lt;/code&gt; type for the schema returning all the custom post types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;union&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CustomPostUnion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And have fields return this type whenever appropriate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QueryRoot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;customPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CustomPostUnion&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;customPosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CustomPostUnion&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;customPosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CustomPostUnion&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Comment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;customPost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CustomPostUnion&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As it can be observed, in the GraphQL schema we need to explicitly assert when we are dealing with posts, and when with custom posts, since they are not the same! Calling these two interchangeably is technical debt from WordPress, which we can fix.&lt;/p&gt;

&lt;p&gt;For this reason, a custom post is always called &lt;code&gt;CustomPost&lt;/code&gt; and not &lt;code&gt;Post&lt;/code&gt;, a field dealing with custom posts is always called &lt;code&gt;customPosts&lt;/code&gt; and not &lt;code&gt;posts&lt;/code&gt;, and a field argument receiving the ID for a custom post is called &lt;code&gt;customPostID&lt;/code&gt; and not &lt;code&gt;postID&lt;/code&gt; (even though that's how it's called in the mapped WordPress function).&lt;/p&gt;

&lt;p&gt;Then, the expectation is always clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;field &lt;code&gt;User.customPosts&lt;/code&gt; can return a list of any custom post, including posts and pages, and &lt;code&gt;User.posts&lt;/code&gt; only returns posts&lt;/li&gt;
&lt;li&gt;field &lt;code&gt;Root.setFeaturedImageOnCustomPost&lt;/code&gt; can add a featured image to any custom post, that's why it's not called &lt;code&gt;setFeaturedImageOnPost&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Not grouping tags (and categories) under a single type
&lt;/h3&gt;

&lt;p&gt;Why is type &lt;code&gt;PostTag&lt;/code&gt; (and same for &lt;code&gt;PostCategory&lt;/code&gt;) called like that, instead of just &lt;code&gt;Tag&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Because, when executing this query (where a product is a CPT), the results from field &lt;code&gt;tags&lt;/code&gt; for posts and products will always be different, non-overlapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tags added to posts will not show up when retrieving tags for products, and the other way around (unless a product also uses the &lt;code&gt;post_tag&lt;/code&gt; taxonomy, but then it can also be represented with the &lt;code&gt;PostTag&lt;/code&gt; type). This does not represent a big deal in WordPress, since these items can be considered different rows from the same database table. But it does matter for GraphQL, which is strongly typed.&lt;/p&gt;

&lt;p&gt;Then, it's a good design decision to keep these entities separate, under their own types, and have tags for posts returned under the &lt;code&gt;PostTag&lt;/code&gt; type and, if a custom plugin implements its own product CPT, it must use the &lt;code&gt;ProductTag&lt;/code&gt; type for its tags.&lt;/p&gt;

&lt;h3&gt;
  
  
  Giving media items their own identity
&lt;/h3&gt;

&lt;p&gt;Media entities in WordPress are custom post types, only because it was convenient from an implementation point of view. However, the GraphQL schema can avoid this technical debt, and model media elements as a distinct entity, not as custom posts.&lt;/p&gt;

&lt;p&gt;This implies the following decisions for the GraphQL schema:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When querying field &lt;code&gt;customPosts&lt;/code&gt;, it will not fetch media elements&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Media&lt;/code&gt; type does not implement the &lt;code&gt;IsCustomPost&lt;/code&gt; interface, and won't be part of the &lt;code&gt;CustomPostUnion&lt;/code&gt; type&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Media&lt;/code&gt; type doesn't have many fields expected from a custom post type, such as &lt;code&gt;excerpt&lt;/code&gt;, &lt;code&gt;date&lt;/code&gt; and &lt;code&gt;status&lt;/code&gt;. Instead, it only has those fields expected from a media element:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Media&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Identifying and mapping enums
&lt;/h3&gt;

&lt;p&gt;In some situations, WordPress uses fixed values from a given set. For instance, the status of a post can only be &lt;code&gt;"publish"&lt;/code&gt;, &lt;code&gt;"draft"&lt;/code&gt;, &lt;code&gt;"pending"&lt;/code&gt; or &lt;code&gt;"trash"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In GraphQL, we can treat these as enums (instead of strings), and create a corresponding enumeration type. Following the GraphQL standard, enums should be written in uppercase, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CUSTOM_POST_STATUS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;PUBLISH&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;DRAFT&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;PENDING&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;TRASH&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, then the query can't be directly used to interact with WordPress, since executing &lt;code&gt;get_posts( [ "post_status" =&amp;gt; "PUBLISH" ] )&lt;/code&gt; doesn't work.&lt;/p&gt;

&lt;p&gt;So, as a compromise, we keep these enum values in lowercase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CUSTOM_POST_STATUS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;draft&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;trash&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mapping additional types
&lt;/h3&gt;

&lt;p&gt;Blocks are not directly visible in the WordPress database diagram, since they are stored in &lt;code&gt;wp_posts&lt;/code&gt; (there is no table &lt;code&gt;wp_blocks&lt;/code&gt;), but nevertheless thay are a distinct entity.&lt;/p&gt;

&lt;p&gt;Hence, we introduce the type &lt;code&gt;Block&lt;/code&gt; to map them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONObject&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>graphql</category>
      <category>wordpress</category>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>🍾 GraphQL API for WordPress is now scoped, thanks to PHP-Scoper!</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Wed, 31 Mar 2021 13:08:38 +0000</pubDate>
      <link>https://forem.com/leoloso/graphql-api-for-wordpress-is-now-scoped-thanks-to-php-scoper-oal</link>
      <guid>https://forem.com/leoloso/graphql-api-for-wordpress-is-now-scoped-thanks-to-php-scoper-oal</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally &lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/"&gt;published in graphql-api.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Plugin GraphQL API for WordPress is now scoped. This means the plugin can be finally uploaded to the &lt;a href="https://wordpress.org/plugins/"&gt;WordPress plugin directory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---ZH5dTpo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/talking-business.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---ZH5dTpo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/talking-business.jpg" alt="Talking business"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To do it, I'm using the wonderful &lt;a href="https://github.com/humbug/php-scoper"&gt;PHP-Scoper&lt;/a&gt;. Using this library with WordPress does not go without its challenges, so I'll explain in this blog post how I managed to pull it out.&lt;/p&gt;

&lt;p&gt;Sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/#heading-taking-the-decision-to-scope"&gt;Taking the decision to scope&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/#heading-checking-out-the-options"&gt;Checking out the options&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/#heading-trying-out-mozart-and-failing"&gt;Trying out Mozart, and failing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/#heading-checking-php-scoper-and-coming-out-in-panic"&gt;Checking PHP-Scoper, and coming out in panic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/#heading-coming-back-to-php-scoper-this-time-for-good"&gt;Coming back to PHP-Scoper, this time for good&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/#heading-php-scoper-the-easy-way"&gt;PHP-Scoper, the easy way 😎&lt;/a&gt; 👈🏽 Here starts my solution&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/#heading-show-me-the-real-stuff"&gt;Show me the real stuff&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/#heading-testing"&gt;Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/#heading-check-out-the-results"&gt;Check out the results&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Taking the decision to scope
&lt;/h2&gt;

&lt;p&gt;A few weeks ago, Matt Mullenweg announced he'll be &lt;a href="https://youtu.be/QI3qCoiuG3w?t=2268"&gt;keeping an eye on "the GraphQL plugin"&lt;/a&gt;, obviously referring to &lt;a href="https://www.wpgraphql.com/"&gt;WPGraphQL&lt;/a&gt;. His expression demonstrates that he believes there is only one GraphQL plugin, when in fact there are two (the one left out is, well, mine). That made me realize how little visibility my plugin has, and I felt bad about it.&lt;/p&gt;

&lt;p&gt;Matt didn't know my plugin existed. Nor does most of the WordPress community, for that matter. Clearly I'm not publicizing it well enough. I know that I suck in marketing and social media; I'm just OK with technical stuff (or so I believe). So I decided to do something about it, at least within my capabilities.&lt;/p&gt;

&lt;p&gt;So this is what I'm working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I just finished coding this same website, &lt;a href="https://graphql-api.com"&gt;graphql-api.com&lt;/a&gt;, and launched it 2 weeks ago (yay! 🥳 Btw, how do you like it? Be welcome to give me feedback, via &lt;a href="https://twitter.com/losoviz/"&gt;DM&lt;/a&gt; or &lt;a href="https://graphql-api.com/contact"&gt;email&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;3 days ago, I finally started scoping the plugin, and finished this task yesterday! (At 3 am, but it was worth it 😅)&lt;/li&gt;
&lt;li&gt;And finally, I'm already working on the upcoming version &lt;code&gt;0.8&lt;/code&gt;, which will be the first one available in the plugin repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scoping the plugin is mandatory to upload it to the repository, because otherwise it could conflict with a different plugin, which requires the same dependency as my plugin, but with a different version. Having done it is a really big milestone; no other development is as important. For instance, I must still complete the GraphQL schema to fully match the WordPress data model, but that will be done steadily on each new release.&lt;/p&gt;

&lt;p&gt;So in a few weeks from now, the plugin will show up when searching for "GraphQL", and the people who are actually needing to implement a GraphQL API will get to know of my plugin's existence.&lt;/p&gt;

&lt;p&gt;Indeed, I do want my plugin to be seriously considered for the future of WordPress. I've been working on it for several years now. &lt;a href="https://github.com/leoloso/PoP"&gt;The repo&lt;/a&gt; was started back in August 2016; that is even before WPGraphQL existed, and at the beginning of GraphQL. But I didn't know that the project would become a GraphQL server; it took that direction only around 1.5 years ago.&lt;/p&gt;

&lt;p&gt;(The project is actually a framework to build applications using server-side components, and a GraphQL server could perfectly be built using this architecture. So then I just built it).&lt;/p&gt;

&lt;p&gt;WPGraphQL is an established plugin, and rightly so: it was started a few years ago, and a community was built around it. The work by &lt;a href="https://twitter.com/jasonbahl"&gt;Jason Bahl&lt;/a&gt; (who was until recently employed by Gatsby) and &lt;a href="https://github.com/wp-graphql/wp-graphql/graphs/contributors"&gt;the contributors to his project&lt;/a&gt; has been outstanding: integrating WordPress into the Jamstack is now easier than ever.&lt;/p&gt;

&lt;p&gt;But one thing is Gatsby and the Jamstack, and another thing is WordPress. WordPress is 40% of the web, not just an input to a static site generator.&lt;/p&gt;

&lt;p&gt;So now, we can consider if WPGraphQL is the right option, without having this decision taken for us out of lack of alternatives. We can now analyze both plugins to see whose goals are more aligned to what's important for WordPress.&lt;/p&gt;

&lt;p&gt;The GraphQL API for WordPress can also work with the Jamstack. But its main objectives are, I believe, more splendid: To "democratize data publishing", so that editing an API becomes as easy as editing a post (something everyone can do), and to make WordPress become the OS of the web.&lt;/p&gt;

&lt;p&gt;Once the plugin is available on the repository, I hope more people will try it out and say "Hey, this is so friking awesome! How comes I didn't know about this stuff before?".&lt;/p&gt;

&lt;p&gt;And then, the choice of "the GraphQL plugin" is not pre-determined, and the WordPress community can consider both WPGraphQL and the GraphQL API for WordPress based on their own merits.&lt;/p&gt;

&lt;p&gt;Now that my motivations are out of the way, let's talk technical stuff 🤓.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking out the options
&lt;/h2&gt;

&lt;p&gt;Scoping a plugin involves running some tooling, that takes the plugin code as input, and spits out the scoped plugin. No big deal, right? How hard can that be?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1LZe6ORj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/dog-scope-plugin.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1LZe6ORj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/dog-scope-plugin.jpg" alt="Talking technical"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, depending on the codebase, just executing the scope command alone won't be enough. After that, we need to check for errors in the console, fix them, test the application thoroughly, identify errors and why they happen, fix them, and iterate. To get it completely right, it might require some time.&lt;/p&gt;

&lt;p&gt;There are 2 libraries for scoping, which have different goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/coenjacobs/mozart"&gt;Mozart&lt;/a&gt;, for WordPress code&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/humbug/php-scoper"&gt;PHP-Scoper&lt;/a&gt;, for any PHP code, particularly when producing PHARs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because I have a WordPress plugin, I tried out Mozart first. Let's see how it fared.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying out Mozart, and failing
&lt;/h2&gt;

&lt;p&gt;I tried Mozart around 1 year ago. For what it says in the documentation, "the &lt;code&gt;mozart compose&lt;/code&gt; command does all the magic". So I expected it all to be very quick and simple, and go enjoy a daiquiri for the rest of the day.&lt;/p&gt;

&lt;p&gt;Alas, Mozart never worked for my codebase. It kept running into issues, so the scoping never materialized. And I couldn't get the required assistance: I submitted a PR, but &lt;a href="https://github.com/coenjacobs/mozart/pull/36#issuecomment-633013728"&gt;it was not considered for merging&lt;/a&gt;, and I was not even notified about it, so I kept waiting until I naturally lost interest in this project.&lt;/p&gt;

&lt;p&gt;I believe that Mozart couldn't handle some of the dependencies in my plugin. I'm making use of several of Symfony's components, including &lt;a href="https://symfony.com/doc/current/components/dependency_injection.html"&gt;DependencyInjection&lt;/a&gt;, &lt;a href="https://symfony.com/doc/current/components/cache.html"&gt;Cache&lt;/a&gt; and &lt;a href="https://github.com/symfony/dotenv"&gt;Dotenv&lt;/a&gt;, with everything managed through Composer.&lt;/p&gt;

&lt;p&gt;Scoping PHP is not just about PHP, so the scoper will have many hurdles to avoid and challenges to solve. For instance, Symfony DependencyInjection uses &lt;code&gt;YAML&lt;/code&gt; files to set-up configuration, and these must be scoped too. And the &lt;code&gt;composer.json&lt;/code&gt; file contains the configuration for &lt;code&gt;PSR-4&lt;/code&gt; autoloading, and this must be scoped too. And, I believe, Mozart couldn't handle these complexities properly.&lt;/p&gt;

&lt;p&gt;But I'm sure that my experience is not the only one, and that there are many happy users our there. Also, my failed attempt happened 1 year ago, so I wonder if the tool has been improved since then. And then, don't forget the saying: "All scoped plugins are alike; each unscoped plugin is unscoped in its own way", so possibly it fails just for me.&lt;/p&gt;

&lt;p&gt;If your WordPress plugin is simple, with self-contained logic, and scoping must be performed within PHP code only, then chances are that Mozart will work. You just gotta find out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking PHP-Scoper, and coming out in panic
&lt;/h2&gt;

&lt;p&gt;So I headed for PHP-Scoper. However, I never even tried to try it, because I got frightened immediately by it.&lt;/p&gt;

&lt;p&gt;To start with, &lt;a href="https://github.com/humbug/php-scoper#wordpress"&gt;this tool does not naturally support WordPress&lt;/a&gt;. And to continue, they recommend to take a look at &lt;a href="https://github.com/humbug/php-scoper/blob/master/Makefile"&gt;their own Makefile&lt;/a&gt;, which looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See https://tech.davis-hansson.com/p/make/&lt;/span&gt;
MAKEFLAGS +&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;--warn-undefined-variables&lt;/span&gt;
MAKEFLAGS +&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;--no-builtin-rules&lt;/span&gt;

.DEFAULT_GOAL :&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;help

&lt;/span&gt;&lt;span class="nv"&gt;PHPBIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;php
&lt;span class="nv"&gt;PHPNOGC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;php &lt;span class="nt"&gt;-d&lt;/span&gt; zend.enable_gc&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;IS_PHP8&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;shell php &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"echo version_compare(PHP_VERSION, '8.0.0', '&amp;gt;=') ? 'true' : 'false';"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;SRC_FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;shell find bin/ src/ &lt;span class="nt"&gt;-type&lt;/span&gt; f&lt;span class="si"&gt;)&lt;/span&gt;

.PHONY: &lt;span class="nb"&gt;help
help&lt;/span&gt;:
    @echo &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[33mUsage:&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[0m&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  make TARGET&lt;/span&gt;&lt;span class="se"&gt;\n\n\0&lt;/span&gt;&lt;span class="s2"&gt;33[32m#&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;# Commands&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;#---------------------------------------------------------------------------&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[0m&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    @fgrep &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="s2"&gt;"##"&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;MAKEFILE_LIST&lt;span class="si"&gt;)&lt;/span&gt; | fgrep &lt;span class="nt"&gt;-v&lt;/span&gt; fgrep | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/\\$$//'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/##//'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN {FS = ":"}; {printf "\033[33m%s:\033[0m%s\n", $$1, $$2}'&lt;/span&gt;


&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Build&lt;/span&gt;
&lt;span class="c"&gt;#---------------------------------------------------------------------------&lt;/span&gt;

.PHONY: clean
clean:   &lt;span class="c"&gt;## Clean all created artifacts&lt;/span&gt;
clean:
    git clean &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.idea/ &lt;span class="nt"&gt;-ffdx&lt;/span&gt;

update-root-version: &lt;span class="c"&gt;## Check the lastest GitHub release and update COMPOSER_ROOT_VERSION accordingly&lt;/span&gt;
update-root-version:
    &lt;span class="nb"&gt;rm&lt;/span&gt; .composer-root-version &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
    &lt;span class="si"&gt;$(&lt;/span&gt;MAKE&lt;span class="si"&gt;)&lt;/span&gt; .composer-root-version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And 600 more lines, all like this. It looks like a riddle. Believing that I needed to understand that code just to scope my plugin, made me flee unceremoniously.&lt;/p&gt;

&lt;p&gt;(Well, understanding that code is their recommendation to test the scoped application, but it is not required. We can also just run the command &lt;code&gt;php-scoper add-prefix&lt;/code&gt;, let it do all the magic, and go drink our daiquiris.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming back to PHP-Scoper, this time for good
&lt;/h2&gt;

&lt;p&gt;So, 3 days ago, I took a decision to implement scoping, somehow. I had to make it happen.&lt;/p&gt;

&lt;p&gt;I came back to PHP-Scoper, to try it out in earnest. I knew that WordPress could be scoped with it from reading &lt;a href="https://deliciousbrains.com/php-scoper-namespace-composer-depencies/"&gt;PHP Scoper: How to Avoid Namespace Issues in your Composer Dependencies&lt;/a&gt; (by the brilliant folks from Delicious Brains). It was just a matter of attitude, and perseverence.&lt;/p&gt;

&lt;p&gt;I explored some of the existing solutions, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/humbug/php-scoper/issues/442#issuecomment-736598602"&gt;This one&lt;/a&gt; by Lucas Bustamante&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoast/wordpress-seo/blob/11.3/src/config/dependency-management.php"&gt;This one&lt;/a&gt; by Yoast&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/google/site-kit-wp/blob/80e3bd88317bf20bc5e5d6600428692fa8e3fc08/includes/loader.php"&gt;This one&lt;/a&gt; by Google Site Kit&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/google/web-stories-wp/blob/main/scoper.inc.php"&gt;This one&lt;/a&gt; by Google Web Stories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But they all look not fully satisfying to me: either the code seems hacky, or fragile and waiting to break at some time or another.&lt;/p&gt;

&lt;p&gt;For instance, the Google Web Stories plugin scopes the code, and then reverts back each one of the conflicts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'patchers'&lt;/span&gt;                   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cm"&gt;/*
             * There is currently no easy way to simply whitelist all global WordPress functions.
             *
             * This list here is a manual attempt after scanning through the AMP plugin, which means
             * it needs to be maintained and kept in sync with any changes to the dependency.
             *
             * As long as there's no built-in solution in PHP-Scoper for this, an alternative could be
             * to generate a list based on php-stubs/wordpress-stubs. devowlio/wp-react-starter/ seems
             * to be doing just this successfully.
             *
             * @see https://github.com/humbug/php-scoper/issues/303
             * @see https://github.com/php-stubs/wordpress-stubs
             * @see https://github.com/devowlio/wp-react-starter/
             */&lt;/span&gt;
            &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;_doing_it_wrong"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\\_doing_it_wrong'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;__"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\\__'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;esc_html_e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\\esc_html_e'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;esc_html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\\esc_html'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;esc_attr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\\esc_attr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;esc_url"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\\esc_url'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;do_action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\\do_action'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$contents&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I understand why they do it, but I don't like it. Whenever a new WordPress function gets referenced, they need to make sure it also makes it to this list. It's too manual, too fragile.&lt;/p&gt;

&lt;p&gt;So this was my challenge: Isn't there a simpler way to scope a plugin, and relying on code that we can present to our friends and colleagues without blushing?&lt;/p&gt;

&lt;h2&gt;
  
  
  PHP-Scoper, the easy way 😎
&lt;/h2&gt;

&lt;p&gt;It actually turned out to be easier than I thought! In just a few hours, I had it all working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BnW_DUcG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/koala-scoping-in-few-hours.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BnW_DUcG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/koala-scoping-in-few-hours.jpg" alt="Scoping in a few hours"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, when I say "easy" and "hours", I actually mean: It all worked immediately, but only after spending 2 months creating the proper structure for the codebase (I'll explain better later on).&lt;/p&gt;

&lt;p&gt;But the important thing is: If you have the right set-up for the project, scoping it can be accomplished in no time.&lt;/p&gt;

&lt;p&gt;The problem with scoping WordPress code is, well, WordPress code. The issue &lt;a href="https://github.com/humbug/php-scoper/issues/303"&gt;is explained here&lt;/a&gt;, but it boils down to all WordPress functions and classes being namespaced too. So if we reference &lt;code&gt;WP_Query&lt;/code&gt; or call &lt;code&gt;get_posts&lt;/code&gt; in our code, these will be transformed to &lt;code&gt;MyPrefixedNamespace\WP_Query&lt;/code&gt; and &lt;code&gt;MyPrefixedNamespace\get_posts&lt;/code&gt;, producing an epic fail on runtime. And that cannot be avoided in PHP-Scoper without hacks.&lt;/p&gt;

&lt;p&gt;So, what is the solution to this? Easy peasy: don't reference &lt;code&gt;WP_Query&lt;/code&gt;, or call &lt;code&gt;get_posts&lt;/code&gt;, or use any WordPress code in the codebase &lt;strong&gt;that will be scoped&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mk0J8Jp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/crazy-bird.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mk0J8Jp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/crazy-bird.jpg" alt="Crazy me?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No, I'm not crazy, and I'm sure you're not either. And yeah, I know that we're building a WordPress plugin... Let me explain.&lt;/p&gt;

&lt;p&gt;How can we not include WordPress code? By splitting the codebase into 2 sets of packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Those containing WordPress code, &lt;strong&gt;without referencing code from any external library&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Those containing business logic, &lt;strong&gt;without containing any WordPress code&lt;/strong&gt;, and &lt;strong&gt;including all required dependencies and references to their code&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, instead of having a single codebase, we have multiple codebases (or packages), where some of them will get scoped and some not, and they all form the plugin, tied together via Composer.&lt;/p&gt;

&lt;p&gt;Then, we do not scope the package containing WordPress code, avoiding the conflict. This works because it doesn't reference any code belonging to any external dependency. All references are internal, such as &lt;code&gt;MyNamespace\MyPlugin\MyClass&lt;/code&gt;. But these need not be scoped, because we can safely assume that there will be only 1 version of the plugin installed in the WordPress site, and we can whitelist our namespace &lt;code&gt;MyNamespace\*&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Moreover, if our plugin can be extended, then whitelisting our own namespace is mandatory. For instance, a field resolver for the GraphQL API for WordPress is implemented by extending from class &lt;code&gt;PoP\ComponentModel\FieldResolvers\AbstractFieldResolver&lt;/code&gt;. If I scoped it, developers would be forced to reference &lt;code&gt;PoP\ComponentModel\FieldResolvers\AbstractFieldResolver&lt;/code&gt; for development, and &lt;code&gt;PrefixedByPoP\PoP\ComponentModel\FieldResolvers\AbstractFieldResolver&lt;/code&gt; for production. That's a no go.&lt;/p&gt;

&lt;p&gt;Then, we only scope the business-logic packages, which contain references to all external libraries but no WordPress code.&lt;/p&gt;

&lt;p&gt;In summary, we are switching this strategy:&lt;/p&gt;

&lt;p&gt;"Have a single codebase, scope it, and then painfully and with plenty of patience undo the damage, while praying that no conflict goes unnoticed and it 💣 booms in production"&lt;/p&gt;

&lt;p&gt;To this one:&lt;/p&gt;

&lt;p&gt;"Split up the codebase into 2 groups, scope only the one containing the references to the external dependencies and no WordPress code, and go have your well-earned daiquiri 🍹".&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the real stuff
&lt;/h2&gt;

&lt;p&gt;It's time open up the sausage and see if it has real meat inside 🌭.&lt;/p&gt;

&lt;p&gt;4 days ago, I had &lt;a href="https://github.com/leoloso/PoP/blob/380f16c46a91bb3d40fff53c6770a31c30d6d457/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/src/ContentProcessors/MarkdownContentParser.php"&gt;the following code&lt;/a&gt; in my plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;GraphQLAPI\GraphQLAPI\ContentProcessors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Parsedown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MarkdownContentParser&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getHTMLContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$fileContent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Parsedown&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$markdownContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Class &lt;code&gt;Parsedown&lt;/code&gt; comes from the external dependency &lt;a href="https://packagist.org/packages/erusev/parsedown"&gt;&lt;code&gt;erusev/parsedown&lt;/code&gt;&lt;/a&gt;, as defined in the &lt;a href="https://github.com/leoloso/PoP/blob/380f16c46a91bb3d40fff53c6770a31c30d6d457/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/composer.json#L19"&gt;plugin's &lt;code&gt;composer.json&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"erusev/parsedown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.7"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hence, my plugin contained references to an external library, so I needed to scope it, to transform &lt;code&gt;Parsedown&lt;/code&gt; into &lt;code&gt;PrefixedByPoP\Parsedown&lt;/code&gt;. But doing so would also scope all WordPress code in the plugin, causing the conflicts.&lt;/p&gt;

&lt;p&gt;So I extracted the code into a separate package, called &lt;a href="https://github.com/leoloso/PoP/tree/cfa98cf9470425016b8cdc88639bafb375d88375/layers/GraphQLAPIForWP/packages/markdown-convertor"&gt;&lt;code&gt;graphql-api/markdown-convertor&lt;/code&gt;&lt;/a&gt;, and replaced the 3rd-party dependency in &lt;code&gt;composer.json&lt;/code&gt; &lt;a href="https://github.com/leoloso/PoP/blob/cfa98cf9470425016b8cdc88639bafb375d88375/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/composer.json#L19"&gt;with my own dependency&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"graphql-api/markdown-convertor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the plugin avoids referencing the external library; instead, it &lt;a href="https://github.com/leoloso/PoP/blob/cfa98cf9470425016b8cdc88639bafb375d88375/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/src/ContentProcessors/MarkdownContentParser.php"&gt;references the service &lt;code&gt;MarkdownConvertorInterface&lt;/code&gt; from the new package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;GraphQLAPI\GraphQLAPI\ContentProcessors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;GraphQLAPI\MarkdownConvertor\MarkdownConvertorInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MarkdownContentParser&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractContentParser&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;MarkdownConvertorInterface&lt;/span&gt; &lt;span class="nv"&gt;$markdownConvertorInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;MarkdownConvertorInterface&lt;/span&gt; &lt;span class="nv"&gt;$markdownConvertorInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;markdownConvertorInterface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$markdownConvertorInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getHTMLContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$fileContent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;markdownConvertorInterface&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;convertMarkdownToHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fileContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Referencing the 3rd-party dependency is done &lt;a href="https://github.com/leoloso/PoP/blob/cfa98cf9470425016b8cdc88639bafb375d88375/layers/GraphQLAPIForWP/packages/markdown-convertor/src/MarkdownConvertor.php"&gt;in the new package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;GraphQLAPI\MarkdownConvertor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Parsedown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MarkdownConvertor&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;MarkdownConvertorInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;convertMarkdownToHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$markdownContent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Parsedown&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$markdownContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scope dependency &lt;code&gt;graphql-api/markdown-convertor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Skip scoping the plugin code&lt;/li&gt;
&lt;li&gt;Whitelist namespace &lt;code&gt;GraphQLAPI\*&lt;/code&gt;, to avoid my own classes from being scoped&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is pretty much the strategy. From now on, it will be a repetition of this same idea, to remove all external dependencies from the code, until voilà, the plugin can be scoped.&lt;/p&gt;

&lt;p&gt;The dependencies to extract are only those from the &lt;code&gt;require&lt;/code&gt; section on your &lt;code&gt;composer.json&lt;/code&gt; file; for &lt;code&gt;require-dev&lt;/code&gt; you can keep any dependency, external or not, since we don't need to scope dependencies used for development; only those ones to create and ship the plugin, for production, need be scoped.&lt;/p&gt;

&lt;p&gt;At the end, the &lt;code&gt;composer.json&lt;/code&gt; from your plugin should not contain any external dependency. For my plugin, it &lt;a href="https://github.com/leoloso/PoP/blob/cfa98cf9470425016b8cdc88639bafb375d88375/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/composer.json"&gt;looks like this&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"php"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.4|^8.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"getpop/engine-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"graphql-api/markdown-convertor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"graphql-by-pop/graphql-clients-for-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"graphql-by-pop/graphql-endpoint-for-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"graphql-by-pop/graphql-server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/basic-directives"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/comment-mutations-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/commentmeta-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/comments-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/custompost-mutations-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/custompostmedia-mutations-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/custompostmedia-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/custompostmeta-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/generic-customposts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/media-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/pages-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/post-mutations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/post-tags-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/posts-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/taxonomymeta-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/taxonomyquery-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/user-roles-access-control"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/user-roles-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/user-state-mutations-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/user-state-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/usermeta-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pop-schema/users-wp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All those packages, with namespaces &lt;code&gt;getpop&lt;/code&gt;, &lt;code&gt;graphql-api&lt;/code&gt;, &lt;code&gt;graphql-by-pop&lt;/code&gt;, and &lt;code&gt;pop-schema&lt;/code&gt;, are all mine: dependencies containing the whole code for the plugin. They are distributed into different namespaces to better manage the code, but you don't need to: using a single namespace works well.&lt;/p&gt;

&lt;p&gt;Now, as the number of packages in your application grows, you'll need to have them all hosted in a monorepo, or you'll go bunkers creating pull requests involving more than one package (believe me, I've been there). In my case, all my packages are hosted in the &lt;a href="https://github.com/leoloso/PoP"&gt;&lt;code&gt;leoloso/PoP&lt;/code&gt; monorepo&lt;/a&gt;, and I keep them in sync via the wonderful &lt;a href="https://github.com/symplify/monorepo-builder"&gt;Monorepo Builder&lt;/a&gt; (I need to write an article about this tool, it's such a life saver!).&lt;/p&gt;

&lt;p&gt;The namespaces for these packages are &lt;code&gt;PoP&lt;/code&gt;, &lt;code&gt;GraphQLAPI&lt;/code&gt;, &lt;code&gt;GraphQLByPoP&lt;/code&gt; and &lt;code&gt;PoPSchema&lt;/code&gt;. Since they are mine, I know they will appear only once in the application, and so I can avoid scoping them.&lt;/p&gt;

&lt;p&gt;To do that, I &lt;a href="https://github.com/leoloso/PoP/blob/858d2fc24b766236f79379a837f4130dfb76a6ff/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/scoper.inc.php#L69:L79"&gt;whitelist them in &lt;code&gt;scoper.inc.php&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'whitelist'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Own namespaces&lt;/span&gt;
    &lt;span class="s1"&gt;'PoPSchema\*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'PoP\*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'GraphQLByPoP\*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'GraphQLAPI\*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Own container cache&lt;/span&gt;
    &lt;span class="s1"&gt;'PoPContainer\*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last entry corresponds to the dependency injection container, which also needs be scoped. By default, this container is assigned name &lt;code&gt;ProjectServiceContainer&lt;/code&gt;, directly in the global namespace. But PHP-Scoper doesn't support whitelisting specific classes from the global namespace. Hence, I added the artificial namespace &lt;code&gt;PoPContainer&lt;/code&gt; to the whitelist, and &lt;a href="https://github.com/leoloso/PoP/blob/27087ec23e6e393aa726f9461ad1c6806249c532/layers/Engine/packages/root/src/Container/ContainerBuilderFactory.php#L141"&gt;assigned this namespace&lt;/a&gt; when dumping the container to disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$dumper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PhpDumper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$containerBuilder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;file_put_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$cacheFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;$dumper&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// Save under own namespace to avoid conflicts&lt;/span&gt;
    &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'namespace'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'PoPContainer'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may notice that, concerning the packages, some of them end with &lt;code&gt;-wp&lt;/code&gt; (like &lt;code&gt;pop-schema/users-wp&lt;/code&gt;) while some don't (like &lt;code&gt;graphql-by-pop/graphql-server&lt;/code&gt;). Yep, you guessed it right: the former ones contain WordPress code and no references to external libraries, and the latter ones can contain references to external libraries, but no WordPress code whatsoever.&lt;/p&gt;

&lt;p&gt;Then, I &lt;a href="https://github.com/leoloso/PoP/blob/858d2fc24b766236f79379a837f4130dfb76a6ff/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/scoper.inc.php#L50:L52"&gt;skip scoping the WordPress packages&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'finders'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Scope packages under vendor/, excluding local WordPress packages&lt;/span&gt;
    &lt;span class="nc"&gt;Finder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;files&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;notPath&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="c1"&gt;// Exclude libraries ending in "-wp"&lt;/span&gt;
        &lt;span class="s1"&gt;'#getpop/[a-zA-Z0-9_-]*-wp/#'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'#pop-schema/[a-zA-Z0-9_-]*-wp/#'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'#graphql-by-pop/[a-zA-Z0-9_-]*-wp/#'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'vendor'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens if some WordPress package needs to reference an external library, and this cannot be extracted into another package? For instance, my package &lt;code&gt;getpop/routing-wp&lt;/code&gt; depends on &lt;a href="https://packagist.org/packages/brain/cortex"&gt;&lt;code&gt;brain/cortex&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://github.com/leoloso/PoP/blob/17936bbd59b51a91fa584ed612e524832bbd03d0/layers/Engine/packages/routing-wp/src/Component.php#L58"&gt;this is unavoidable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I can't scope the whole package, since &lt;code&gt;getpop/routing-wp&lt;/code&gt; contains WordPress code. Instead, what I do is to identify the files where those references are done, and make sure that they do not contain any WordPress code. Then I can scope those files only.&lt;/p&gt;

&lt;p&gt;In this case, the reference to &lt;code&gt;Cortex/Brain&lt;/code&gt; is done in 2 files, including &lt;a href="https://github.com/leoloso/PoP/blob/f805ccafd6240a3467582ff73286b02c0722c25b/layers/Engine/packages/routing-wp/src/Hooks/SetupCortexHookSet.php#L8"&gt;&lt;code&gt;layers/Engine/packages/routing-wp/src/Hooks/SetupCortexHookSet.php&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PoP\RoutingWP\Hooks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PoP\Hooks\AbstractHookSet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Brain\Cortex\Route\RouteCollectionInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Brain\Cortex\Route\RouteInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Brain\Cortex\Route\QueryRoute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PoP\RoutingWP\WPQueries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PoP\Routing\Facades\RoutingManagerFacade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SetupCortexHookSet&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractHookSet&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hooksAPI&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s1"&gt;'cortex.routes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'setupCortex'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cd"&gt;/**
   * @param RouteCollectionInterface&amp;lt;RouteInterface&amp;gt; $routes
   */&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setupCortex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;RouteCollectionInterface&lt;/span&gt; &lt;span class="nv"&gt;$routes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$routingManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RoutingManagerFacade&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$routingManager&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getRoutes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$route&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$routes&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;QueryRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;$route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$matches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;WPQueries&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;STANDARD_NATURE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can you notice the oddity here? This is an implementation of a hook, but no &lt;code&gt;add_action&lt;/code&gt; is called, since I can't have any WordPress code here. Instead, it calls function &lt;code&gt;addAction&lt;/code&gt; from service &lt;a href="https://github.com/leoloso/PoP/blob/f805ccafd6240a3467582ff73286b02c0722c25b/layers/Engine/packages/hooks/src/HooksAPIInterface.php"&gt;&lt;code&gt;HooksAPIInterface&lt;/code&gt;&lt;/a&gt;, and this service is implemented by &lt;a href="https://github.com/leoloso/PoP/blob/b79c5bef5f7abb5b8ebebb374fa1da252011d7ed/layers/Engine/packages/hooks-wp/src/HooksAPI.php#L27"&gt;class &lt;code&gt;HooksAPI&lt;/code&gt; in package &lt;code&gt;getpop/hooks-wp&lt;/code&gt;&lt;/a&gt;, where we can have WordPress code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PoP\HooksWP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PoP\Hooks\HooksAPIInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HooksAPI&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;HooksAPIInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;callable&lt;/span&gt; &lt;span class="nv"&gt;$function_to_add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$accepted_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$function_to_add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$accepted_args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the code is cleanly split, we can &lt;a href="https://github.com/leoloso/PoP/blob/858d2fc24b766236f79379a837f4130dfb76a6ff/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/scoper.inc.php#L64:L67"&gt;scope those 2 files referencing external dependencies&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'finders'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Finder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="s1"&gt;'vendor/getpop/routing-wp/src/Component.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'vendor/getpop/routing-wp/src/Hooks/SetupCortexHookSet.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Earlier on I mentioned that setting-up the scoping took a few hours, but only after 2 months of work. Well, this example demonstrates what I meant: The actual work lies in dividing the codebase cleanly into the 2 sets.&lt;/p&gt;

&lt;p&gt;In my case, the work took 2 months because the level of detail was extreme: The plugin became a composition of 125 packages! But this is an exceptional case, with the goal to have &lt;a href="https://graphql-by-pop.com"&gt;GraphQL by PoP&lt;/a&gt; (the underlying server for the plugin) &lt;a href="https://graphql-by-pop.com/docs/architecture/cms-agnosticism.html"&gt;be CMS-agnostic&lt;/a&gt;, as to support an implementation for other CMSs/frameworks just by reimplementing the corresponding &lt;code&gt;-wp&lt;/code&gt; packages.&lt;/p&gt;

&lt;p&gt;(I wrote in detail about this strategy, in article &lt;a href="https://www.smashingmagazine.com/2019/11/abstracting-wordpress-code-cms-concepts/"&gt;Abstracting WordPress Code To Reuse With Other CMSs: Concepts&lt;/a&gt; and &lt;a href="https://www.smashingmagazine.com/2019/11/abstracting-wordpress-code-reuse-with-other-cms-implementation/"&gt;Implementation&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;It's certainly quite some work, but the improved cleanliness of the code makes it worth it. And not just for scoping the plugin, which came as a total surprise to me, and I'm still gidding in my unexpected happiness. For instance, I run PHPStan and PHPUnit separately on WordPress and non-WordPress code, avoiding me many headaches.&lt;/p&gt;

&lt;p&gt;Once the codebase is tidied-up, the world suddenly becomes such a better place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;So, how do we test this beast?&lt;/p&gt;

&lt;p&gt;The solution I came up with is to rely on &lt;a href="https://github.com/rectorphp/rector"&gt;Rector&lt;/a&gt;, the same tool I use for &lt;a href="https://blog.logrocket.com/transpiling-php-code-from-8-0-to-7-x-via-rector/"&gt;downgrading code from PHP 7.4, for development, to 7.1, for production&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The idea is the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scope the plugin&lt;/li&gt;
&lt;li&gt;Analyze it with Rector, applying any rule (it doesn't matter which one)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If something went wrong when scoping, then Rector won't be able to load some class, and it will throw an error. For instance, if class &lt;code&gt;Brain\Cortex&lt;/code&gt; was scoped as &lt;code&gt;PrefixedByPoP\Brain\Cortex&lt;/code&gt;, but some reference to it was left as &lt;code&gt;Brain\Cortex&lt;/code&gt;, then autoloading this class will fail.&lt;/p&gt;

&lt;p&gt;This is my &lt;a href="https://github.com/leoloso/PoP/blob/acaeeec4bcc3d93bffd8b6875ed3cbfbe1c18de8/.github/workflows/scope_graphql_api_for_wp_tests.yml"&gt;GitHub Action for testing&lt;/a&gt; (&lt;code&gt;working-directory&lt;/code&gt; is being used, because I'm operating from the root of the monorepo, but the scoping happens on the plugin folder):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scope GraphQL API for WP tests&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;null&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;COMPOSER_ROOT_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev-master"&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;layers/GraphQLAPIForWP/plugins/graphql-api-for-wp&lt;/span&gt;

    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scope the plugin code via PHP-Scoper, and execute tests&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set-up PHP&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shivammathur/setup-php@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;php-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7.4&lt;/span&gt;
          &lt;span class="na"&gt;coverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;COMPOSER_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install root dependencies&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ramsey/composer-install@v1"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install plugin dependencies for PROD&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;composer install --no-dev --no-progress --no-interaction --ansi&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install PHP-Scoper&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;composer global config minimum-stability dev&lt;/span&gt;
          &lt;span class="s"&gt;composer global config prefer-stable true&lt;/span&gt;
          &lt;span class="s"&gt;composer global require humbug/php-scoper&lt;/span&gt;

      &lt;span class="c1"&gt;# The scoped results correspond to vendor/, so must generate them in such folder&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scope plugin into separate folder&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;php-scoper add-prefix --output-dir ../../../../build-prefixed/vendor --ansi&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Copy scoped code back into plugin&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rsync -av build-prefixed/ layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/ --quiet&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Regenerate autoloader&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;composer dumpautoload --optimize --classmap-authoritative --ansi&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Rector on the scoped code&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bin/rector process --config=layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/rector-test-scoping.php --ansi&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;a href="https://github.com/leoloso/PoP/blob/96ff3535f288548c2c02892136431ce886015ac9/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/rector-test-scoping.php#L1-L58"&gt;this is my Rector configuration&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Rector\CodeQuality\Rector\LogicalAnd\AndAssignsToSeparateLinesRector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Rector\Core\Configuration\Option&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerConfigurator&lt;/span&gt; &lt;span class="nv"&gt;$containerConfigurator&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$services&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$containerConfigurator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;services&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nv"&gt;$services&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AndAssignsToSeparateLinesRector&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nv"&gt;$parameters&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;AUTO_IMPORT_NAMES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nv"&gt;$parameters&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;AUTOLOAD_PATHS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/scoper-autoload.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/erusev/parsedown/Parsedown.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/jrfnl/php-cast-to-type/cast-to-type.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/jrfnl/php-cast-to-type/class.cast-to-type.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// files to rector&lt;/span&gt;
  &lt;span class="nv"&gt;$parameters&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PATHS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// files to skip&lt;/span&gt;
  &lt;span class="nv"&gt;$parameters&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SKIP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Exclude tests&lt;/span&gt;
    &lt;span class="s1"&gt;'*/tests/*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/nikic/fast-route/test/*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/psr/log/Psr/Log/Test/*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/symfony/service-contracts/Test/*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can notice that some dependency files, such as &lt;code&gt;erusev/parsedown/Parsedown.php'&lt;/code&gt; need to be added to &lt;code&gt;Option::AUTOLOAD_PATHS&lt;/code&gt;. That's because scoping the package's &lt;code&gt;composer.json&lt;/code&gt; is not 100% reliable, and then their autoloading may fail.&lt;/p&gt;

&lt;p&gt;Whenever that happens, Rector will complain that some class failed autoloading. From there, we identify the corresponding file, and manually add it to the autoloading paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check out the results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/leoloso/PoP/tree/master/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp"&gt;This is the plugin's source code&lt;/a&gt;, and &lt;a href="https://github.com/GraphQLAPI/graphql-api-for-wp-dist"&gt;this is its scoped (and downgraded to PHP 7.1) version&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Find the 7 differences 😁. (I give you a hint: search for &lt;code&gt;PrefixedByPoP&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;And &lt;a href="https://github.com/leoloso/PoP/releases/download/0.7.9/graphql-api.zip"&gt;this is the final &lt;code&gt;graphql-api.zip&lt;/code&gt; plugin file&lt;/a&gt;, ready to be installed on your site.&lt;/p&gt;

&lt;p&gt;That's all. I hope this has been useful 😃💪🚀&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>graphql</category>
      <category>php</category>
      <category>devops</category>
    </item>
    <item>
      <title>👷🏽‍♂️ Building the GraphQL API in the Open #1 (March '21)</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Fri, 19 Mar 2021 13:34:25 +0000</pubDate>
      <link>https://forem.com/leoloso/building-the-graphql-api-in-the-open-1-march-21-i5</link>
      <guid>https://forem.com/leoloso/building-the-graphql-api-in-the-open-1-march-21-i5</guid>
      <description>&lt;p&gt;This post has been originally &lt;a href="https://graphql-api.com/blog/building-in-the-open-episode-1/" rel="noopener noreferrer"&gt;published in graphql-api.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Welcome to the very first "Building in the Open"!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a channel to share news concerning the development of the &lt;a href="https://graphql-api.com" rel="noopener noreferrer"&gt;GraphQL API for WordPress&lt;/a&gt; with the community, sent the first week of each month.&lt;/p&gt;

&lt;p&gt;Through this space, we will learn everything that happened during the last month, including:&lt;/p&gt;

&lt;p&gt;✅ What we've been working on, what new features we released&lt;br&gt;
✅ What we will be working on the upcoming month&lt;br&gt;
✅ Amount of traffic we got on the site&lt;br&gt;
✅ How did the plugin do: Number of downloads, newsletter subscriptions, GitHub stars&lt;br&gt;
✅ Progress on achieving financial sustainability&lt;br&gt;
✅ Newly published guides&lt;br&gt;
✅ Summary of our recently published blog posts&lt;br&gt;
✅ Reaching out / Plugin mentions&lt;br&gt;
✅ General news&lt;/p&gt;

&lt;p&gt;If you enjoy this newsletter, please invite your friends to &lt;a href="https://graphql-api.com/newsletter/" rel="noopener noreferrer"&gt;subscribe&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's start!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heads up:&lt;/strong&gt; This newsletter is a two-way communication channel. If there's anything you'd like to say, be welcome to add a comment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Fwelcome.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Fwelcome.png" alt="A welcome to the newsletter, by your host"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What we've been coding on
&lt;/h2&gt;

&lt;p&gt;If you notice the &lt;a href="https://graphql-api.com/guides/" rel="noopener noreferrer"&gt;Guides&lt;/a&gt;, section "Extending the GraphQL API" is still pretty empty:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Fguides-extending-section.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Fguides-extending-section.jpg" title="Guides for 'Extending the plugin' are not yet complete" alt="Guides for "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My priority is to complete these guides. But before I do that, I want the plugin's code to be as simple as possible. The simpler it is, the less documentation is required, and the more anyone and everyone is able to understand it.&lt;/p&gt;

&lt;p&gt;With this in mind, I've decided to refactor the code, to have it be fully based on &lt;a href="https://symfony.com/doc/current/components/dependency_injection.html" rel="noopener noreferrer"&gt;Symfony's DependencyInjection Component&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The idea is that any extension to the plugin (such as a custom &lt;code&gt;TypeResolver&lt;/code&gt;, &lt;code&gt;FieldResolver&lt;/code&gt; or &lt;code&gt;DirectiveResolver&lt;/code&gt;) is simply defined as a service in the container, and the service is automatically configured via &lt;a href="https://symfony.com/doc/current/service_container/compiler_passes.html" rel="noopener noreferrer"&gt;Compiler passes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Fully relying on Symfony's dependency injection has several advantages:&lt;/p&gt;

&lt;p&gt;✅ There is a single, consistent way to create extensions&lt;br&gt;
✅ Just creating a PHP class implementing some interface does the whole job, and the developer needs not be aware of the nitty-gritty details&lt;br&gt;
✅ Symfony's documentation is very extensive. By pointing developers to it, that is documentation that &lt;strong&gt;I do not need to write&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Interested in the code? Check out my latest merged PRs (&lt;a href="https://github.com/leoloso/PoP/pull/453" rel="noopener noreferrer"&gt;#453&lt;/a&gt;, &lt;a href="https://github.com/leoloso/PoP/pull/452" rel="noopener noreferrer"&gt;#452&lt;/a&gt;, &lt;a href="https://github.com/leoloso/PoP/pull/449" rel="noopener noreferrer"&gt;#449&lt;/a&gt; and several others).&lt;/p&gt;

&lt;p&gt;I will keep working on this code for the upcoming weeks, until the migration is 100% complete, and I get to write the missing guides.&lt;/p&gt;
&lt;h2&gt;
  
  
  Traffic to graphql-api.com
&lt;/h2&gt;

&lt;p&gt;Let me be clear on something: I care about how many people visit the plugin's website, as a proxy to know how many people know about the plugin.&lt;/p&gt;

&lt;p&gt;I don't have deep pockets to publicize my plugin. And even if I did, I wouldn't spend my money on promoting it, since that goes against the spirit of open source. (This would be different if open source were just a channel to sell some product or service, but that's not my case.)&lt;/p&gt;

&lt;p&gt;That means that I rely fully on word of mouth to promote it. For that, I've been devoting plenty of effort in writing high-quality content for &lt;a href="https://graphql-api.com/blog/" rel="noopener noreferrer"&gt;the plugin's blog&lt;/a&gt;, hoping this content would get shared around, reaching people who would otherwise not know about the plugin.&lt;/p&gt;

&lt;p&gt;And so far, I'm pretty happy with the results.&lt;/p&gt;

&lt;p&gt;During the past month, I've had 4.5k visitors, with 6k page views:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Ftraffic.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Ftraffic.jpg" title="Show me the money!" alt="Show me the money!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's break down these stats.&lt;/p&gt;

&lt;p&gt;Most of my visitors come from Hacker News, where I managed to pull a few "Show HN" front pages, and Reddit, mostly from &lt;a href="https://www.reddit.com/r/PHP/" rel="noopener noreferrer"&gt;/r/PHP&lt;/a&gt; and &lt;a href="https://www.reddit.com/r/graphql/" rel="noopener noreferrer"&gt;/r/graphql&lt;/a&gt; (where I always share my articles).&lt;/p&gt;

&lt;p&gt;I managed to &lt;a href="https://twitter.com/losoviz/status/1365873932675391488" rel="noopener noreferrer"&gt;rank #1 on Google when searching "wordpress core graphql"&lt;/a&gt;, and that brought plenty of traffic. Unfortunately, it was a one time-off: after 24hs it went away as suddenly as it arrived. Otherwise, on a typical day I get between 3 and 10 visitors from Google.&lt;/p&gt;

&lt;p&gt;Twitter and Facebook bring a sizable amount of traffic, but I don't know from who (not from me, since I am extremely bad at social media). I do share my articles on Twitter, but they seldom get retweeted. And I do not use 👎🏾 Facebook.&lt;/p&gt;

&lt;p&gt;(Btw, for those of you who share my articles on social media, thanks ❤️)&lt;/p&gt;

&lt;p&gt;I get some modest but consistent traffic from the &lt;a href="https://graphql.org/code/#php" rel="noopener noreferrer"&gt;listing of GraphQL servers in PHP on graphql.org&lt;/a&gt;, and from &lt;a href="https://dev.to/leoloso/executing-multiple-queries-in-a-single-operation-in-graphql-goe"&gt;an article I've published on dev.to&lt;/a&gt;, which ranks #1 when &lt;a href="https://www.google.com/search?q=graphql+execute+multiple+queries" rel="noopener noreferrer"&gt;googling "graphql execute multiple queries"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, my articles consistently appear in WordPress' main newsletters (including &lt;a href="https://wpowls.co" rel="noopener noreferrer"&gt;WP Owls&lt;/a&gt;, &lt;a href="https://wpmail.me/" rel="noopener noreferrer"&gt;wpMail.me&lt;/a&gt;, &lt;a href="https://poststatus.com/newsletter/" rel="noopener noreferrer"&gt;Post Status&lt;/a&gt;, &lt;a href="https://wpbuilds.com/" rel="noopener noreferrer"&gt;WP Builds&lt;/a&gt;, and &lt;a href="https://thewpweekly.com/" rel="noopener noreferrer"&gt;The WP Weekly&lt;/a&gt;). I don't know exactly how much traffic each of them brings, since the referrer will appear as Gmail and similar others. However, when taken together, these newsletters produce a sizable number of visitors.&lt;/p&gt;

&lt;p&gt;My blog posts are by far my most popular content, with the last three (&lt;a href="https://graphql-api.com/blog/why-wordpress-should-have-a-graphql-api-in-core/" rel="noopener noreferrer"&gt;this one&lt;/a&gt;, &lt;a href="https://graphql-api.com/blog/graphql-api-vs-wpgraphql-the-fight/" rel="noopener noreferrer"&gt;this one&lt;/a&gt; and &lt;a href="https://graphql-api.com/blog/rejuvenating-wordpress-through-graphql/" rel="noopener noreferrer"&gt;this one&lt;/a&gt;) bringing over 1k visitors each.&lt;/p&gt;

&lt;p&gt;These numbers look pretty good, more since I barely launched the website less than 2 months ago. However, not everything looks good: At 88%, the bounce rate is quite high. I need to work on that.&lt;/p&gt;
&lt;h2&gt;
  
  
  Metrics
&lt;/h2&gt;

&lt;p&gt;Traffic to the site is only a decorative metric, to estimate awareness of the plugin. But far more important to ask is: How many people started using the plugin during the past month?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Fmultiply.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Fmultiply.png" title="My reputation precedes me" alt="My reputation precedes me"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During the past month, the plugin fared like this:&lt;/p&gt;

&lt;p&gt;🎯 Number of plugin downloads: 170&lt;br&gt;
⭐️ GitHub stars: 27&lt;/p&gt;

&lt;p&gt;The number of downloads can be retrieved from the GitHub API, passing param &lt;code&gt;per_page=3&lt;/code&gt; to include only the 3 releases created during the last month:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/vnd.github.v3+json"&lt;/span&gt; https://api.github.com/repos/leoloso/PoP/releases?per_page&lt;span class="o"&gt;=&lt;/span&gt;3 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"download_count"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am neither happy nor unhappy about these numbers. They are not great (and I wish they were better), but they are a good start.&lt;/p&gt;

&lt;p&gt;Concerning &lt;strong&gt;downloads&lt;/strong&gt;, it is said that getting the first user is the most difficult task. Only after a few people start using the plugin, and start talking about it, that its use will become more widespread. I am still within this initial stage of finding the first batch of commited users.&lt;/p&gt;

&lt;p&gt;Concerning &lt;strong&gt;GitHub stars&lt;/strong&gt;, I must say it looks pretty flat: around 1 star per day on average. This is certainly nothing great. If you like what I'm building with the GraphQL API for WordPress, and you don't mind showing some ❤️ love, then please consider &lt;a href="https://github.com/leoloso/PoP" rel="noopener noreferrer"&gt;giving it a ⭐️ star on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Financial Sustainability
&lt;/h2&gt;

&lt;p&gt;This one is the tough issue: the project must be financially sustainable. It either generates a bit of money, or it won't make it for long.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Fcheese.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Fcheese.png" title="In this goes my life" alt="In this goes my life"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If I am able to make an income for myself, then I can keep working on it, for as long as needed. That's all I need: an income. Not investors knocking on my door looking for millions. Just a couple thousands per month, to pay for the roof above my head.&lt;/p&gt;

&lt;p&gt;My goal is to keep the plugin fully open source. For that, I'm currently reaching out to a couple of potential sponsors, asking if they'd like to help fund the development of the plugin. It will be a win-win situation.&lt;/p&gt;

&lt;p&gt;Why am I resorting to some "big guns" sponsors, instead of relying on regular sponsorship, by anyone from the community?&lt;/p&gt;

&lt;p&gt;Yes, I've been trying that too: I am on &lt;a href="https://github.com/sponsors/leoloso" rel="noopener noreferrer"&gt;GitHub Sponsors&lt;/a&gt;. However, it doesn't really work, unless you already have thousands of users, followers, or people subscribed to your mailing list, to whom you can reach out to, expecting many of them to fund you.&lt;/p&gt;

&lt;p&gt;For instance, asking for a standard u$d 5 or 10 per month, I'd need several hundred funders for this approach to fund my work. And I'm nowhere near that stage.&lt;/p&gt;

&lt;p&gt;But even more, who can really succeed with this approach? I know that Caleb Porzio (creator of Livewire) &lt;a href="https://calebporzio.com/i-just-hit-dollar-100000yr-on-github-sponsors-heres-how-i-did-it" rel="noopener noreferrer"&gt;has made it&lt;/a&gt;, and has now &lt;a href="https://github.com/sponsors/calebporzio#sponsors" rel="noopener noreferrer"&gt;reached over 1350 sponsors&lt;/a&gt;! But that's more the exception than the norm.&lt;/p&gt;

&lt;p&gt;Take Composer, for instance. Composer has fundamentally changed how we develop PHP applications, yet &lt;a href="https://github.com/sponsors/composer" rel="noopener noreferrer"&gt;they barely have 90 sponsors&lt;/a&gt;. How could I ever expect to get more sponsors than Composer?&lt;/p&gt;

&lt;p&gt;That's why my current approach is to create a win-win situation for my project and the few companies willing to sponsor it. Let's hope it will work out, and the GraphQL API is free for everyone, for all the features, and I don't need to lock the good stuff behind a paywall.&lt;/p&gt;

&lt;p&gt;(If you'd like to find out how it's a win-win, please &lt;a href="https://graphql-api.com/contact" rel="noopener noreferrer"&gt;send me an email&lt;/a&gt; or &lt;a href="https://twitter.com/losoviz" rel="noopener noreferrer"&gt;DM&lt;/a&gt;. Maybe your company may be interested too?)&lt;/p&gt;

&lt;p&gt;I'll give this approach a few months, hopefully I will make it happen. If I don't succeed, only then I will need to consider building a PRO version of the plugin, and restricting some of the features for the paid version. (Yeah, that would suck, so I hope I can avoid that stage.)&lt;/p&gt;

&lt;p&gt;In the upcoming newsletters, I will keep you updated if I managed to get sponsors or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blog posts
&lt;/h2&gt;

&lt;p&gt;The blog posts have been my absolute pride and joy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heads up:&lt;/strong&gt; Did you know there's an &lt;a href="https://graphql-api.com/feed.xml" rel="noopener noreferrer"&gt;RSS feed on the site&lt;/a&gt;? You can subscribe to receiving all my blog posts, read them on your favorite reader.&lt;/p&gt;

&lt;p&gt;During the past month, I've managed to publish a high-quality blog post every week:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphql-api.com/blog/why-wordpress-should-have-a-graphql-api-in-core/" rel="noopener noreferrer"&gt;🛠 Should WordPress have a GraphQL API in core?&lt;/a&gt; makes the case that WordPress could benefit from GraphQL, since the WP REST API was given a new functionality in WordPress 5.6 (batch operations), that a GraphQL API can deliver natively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphql-api.com/blog/graphql-api-vs-wpgraphql-the-fight/" rel="noopener noreferrer"&gt;🥊 GraphQL API vs WPGraphQL: the fight!&lt;/a&gt; compares my plugin with &lt;a href="https://www.wpgraphql.com/" rel="noopener noreferrer"&gt;WPGraphQL&lt;/a&gt;, on a clash to be remembered for ages to come, and which will keep boxing fans asking for more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphql-api.com/blog/rejuvenating-wordpress-through-graphql/" rel="noopener noreferrer"&gt;👶🏻 Rejuvenating WordPress through GraphQL&lt;/a&gt; demonstrates how a headless WordPress can be decoupled from the WordPress codebase, providing an opportunity to fix (or, at least, bypass) the accumulated technical debt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphql-api.com/blog/graphql-api-for-wp-is-now-scoped-thanks-to-php-scoper/" rel="noopener noreferrer"&gt;🍾 GraphQL API for WordPress is now scoped, thanks to PHP-Scoper!&lt;/a&gt; describes a strategy to scope a WordPress plugin using &lt;a href="https://github.com/humbug/php-scoper" rel="noopener noreferrer"&gt;PHP-Scoper&lt;/a&gt;, as to avoid conflicts with other plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reaching out / Plugin mentions
&lt;/h2&gt;

&lt;p&gt;I'm delighted that the plugin has been featured in a few places.&lt;/p&gt;

&lt;p&gt;✅ I have given talk "Intro to the GraphQL API for WordPress" in WordCamp India 2021, doing a demo of the plugin, and (surprisingly from doing a demo) it all came out perfectly! Check out &lt;a href="https://www.youtube.com/watch?v=LnyNyT2RwwI" rel="noopener noreferrer"&gt;the Youtube video&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;✅ Joe Howard has interviewed me for the &lt;a href="https://wpmrr.com/podcast/" rel="noopener noreferrer"&gt;WPMRR podcast&lt;/a&gt;. The recording will come out soon.&lt;/p&gt;

&lt;p&gt;✅ Chris Coyier featured my plugin in the &lt;a href="https://css-tricks.com/newsletter/239-new-css-tricks-and-design-engineers/" rel="noopener noreferrer"&gt;CSS-Tricks newsletter #239&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Fcss-tricks-newsletter.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Fcss-tricks-newsletter.jpg" title="This made my day" alt="This made my day"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A bit of everything
&lt;/h2&gt;

&lt;p&gt;Some general news, about anything taking place during the last month.&lt;/p&gt;

&lt;h3&gt;
  
  
  Jason Bahl goes to WP Engine
&lt;/h3&gt;

&lt;p&gt;Congrats to &lt;a href="https://www.wpgraphql.com/2021/02/07/whats-next-for-wpgraphql/" rel="noopener noreferrer"&gt;Jason for joining WP Engine&lt;/a&gt;! I hope he'll keep rocking, as he's been doing so far for WPGraphQL.&lt;/p&gt;

&lt;p&gt;Btw, the fact that we're competitors (well, I'm the one competing with him, he's way far ahead) doesn't mean that we can't be friends, or collaborate to improve each other's projects. Indeed, we both share the same goal: to bring GraphQL to WordPress (even though we have different ideas on how that should happen).&lt;/p&gt;

&lt;p&gt;But I believe that competition is good, and it will benefit everyone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Felephant.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Felephant.gif" title="Yeah, competition is good, as long as you're the one on top" alt="Yeah, competition is good, as long as you're the one on top"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  WP Engine launches Atlas, and claims to know everything about headless (do they?)
&lt;/h3&gt;

&lt;p&gt;I also congratulate WP Engine for &lt;a href="https://wpengine.com/blog/wp-engine-launches-atlas-the-future-of-headless-wordpress/" rel="noopener noreferrer"&gt;launching Atlas&lt;/a&gt;, their new headless WordPress solution.&lt;/p&gt;

&lt;p&gt;Unfortunately, they state some inaccurate information:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Companies that use an entirely headless solution will typically host a separate JavaScript application for the front end, which pulls specific WordPress data via APIs—the &lt;a href="https://developer.wordpress.org/rest-api/" rel="noopener noreferrer"&gt;WordPress REST API&lt;/a&gt; or the &lt;a href="https://www.wpgraphql.com/" rel="noopener noreferrer"&gt;WPGraphQL&lt;/a&gt; plugin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yeah, the GraphQL API for WordPress does not exist, right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Finvisible.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-api.com%2Fimages%2Fbuilding-in-the-open-episode-1%2Finvisible.png" title="Hey there, I'm here, or am I not?" alt="Hey there, I'm here, or am I not?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would normally not be troubled about this, since I don't expect everyone to know about my plugin. But I do believe that they know about my project, and they seem to be wilfully ignoring it.&lt;/p&gt;

&lt;p&gt;After they launched &lt;a href="https://developers.wpengine.com/" rel="noopener noreferrer"&gt;developers.wpengine.com&lt;/a&gt; (the "one-stop hub for best practices, tutorials, blogs, and documentation for headless WordPress"), I did reach out to them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Matt Landers, Developer Relations at WP Engine for Headless WordPress, &lt;a href="https://twitter.com/losoviz/status/1359686315004993536" rel="noopener noreferrer"&gt;on Twitter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Their development team, &lt;a href="https://github.com/wpengine/headless-framework/discussions/59" rel="noopener noreferrer"&gt;on their headless framework GitHub repo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Somebody from Torque (the magazine owned by WP Engine), via DMs with &lt;a href="https://twitter.com/TheTorqueMag" rel="noopener noreferrer"&gt;@TheTorqueMag&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I guess they haven't taken my project seriously. Or well, maybe they just didn't care about it, since they are fully invested in WPGraphQL.&lt;/p&gt;

&lt;p&gt;Now, I'm OK if they don't want to mention my plugin. However, stating that the WP REST API and WPGraphQL are the &lt;strong&gt;only&lt;/strong&gt; two options is very misleading. As a consequence, my plugin gets harmed, and the community of developers gets confused.&lt;/p&gt;

&lt;p&gt;So then yeah, I must admit I'm annoyed. This is not cool at all. I hope they will rectify their inaccurate information (I sent them an email already).&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;So this is the end of the first ever "Building the GraphQL API in the open".&lt;/p&gt;

&lt;p&gt;How did you like it? Be welcome to share your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;If you did like it, I will appreciate if you can share the newsletter with your friends (or, even better, invite them to &lt;a href="https://graphql-api.com/newsletter/" rel="noopener noreferrer"&gt;subscribe&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;See you next month!&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>wordpress</category>
      <category>startup</category>
    </item>
    <item>
      <title>🎉 Released v0.7 of the GraphQL API for WordPress , with support for mutations, and nested mutations!</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Fri, 22 Jan 2021 16:07:48 +0000</pubDate>
      <link>https://forem.com/leoloso/released-v0-7-of-the-graphql-api-for-wordpress-with-support-for-mutations-and-nested-mutations-4mki</link>
      <guid>https://forem.com/leoloso/released-v0-7-of-the-graphql-api-for-wordpress-with-support-for-mutations-and-nested-mutations-4mki</guid>
      <description>&lt;p&gt;Version 0.7 of the &lt;a href="https://graphql-api.com"&gt;GraphQL API for WordPress&lt;/a&gt;, supporting mutations, and nested mutations, has been released! 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--apbsonY3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/finally-got-mutations.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--apbsonY3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-api.com/images/finally-got-mutations.jpg" alt="Finally!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read the account of what's new here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphql-api.com/blog/released-graphql-api-v07-with-mutations-and-nested-mutations/"&gt;https://graphql-api.com/blog/released-graphql-api-v07-with-mutations-and-nested-mutations/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>💪 WordPress + GraphQL</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Wed, 20 Jan 2021 15:09:59 +0000</pubDate>
      <link>https://forem.com/leoloso/wordpress-graphql-3fla</link>
      <guid>https://forem.com/leoloso/wordpress-graphql-3fla</guid>
      <description>&lt;p&gt;I created a WordPress plugin to install a GraphQL server, and launched its new site:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphql-api.com"&gt;https://graphql-api.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also has a REST-like behavior: it supports persisted queries, which are normal GraphQL queries, but published and accessed under their own URL, and these endpoints are even cacheable on the server-side.&lt;/p&gt;

&lt;p&gt;Check it out! 😁&lt;/p&gt;

&lt;p&gt;For a bit more info, I wrote on CSS-Tricks about the &lt;a href="https://css-tricks.com/rendering-the-wordpress-philosophy-in-graphql/"&gt;goals and philosophy of this plugin&lt;/a&gt; (that is, how it brings the WordPress philosophy to GraphQL).&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>graphql</category>
      <category>api</category>
    </item>
    <item>
      <title>🚀 Coding in PHP 7.4 and deploying to 7.1 via Rector and GitHub Actions</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Wed, 25 Nov 2020 15:31:56 +0000</pubDate>
      <link>https://forem.com/leoloso/coding-in-php-7-4-and-deploying-to-7-1-via-rector-and-github-actions-1k6m</link>
      <guid>https://forem.com/leoloso/coding-in-php-7-4-and-deploying-to-7-1-via-rector-and-github-actions-1k6m</guid>
      <description>&lt;p&gt;This post has been &lt;a href="https://leoloso.com/posts/coding-with-php74-deploying-to-71-guide/"&gt;originally published in my blog leoloso.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I've written a step-by-step guide about transpiling PHP code, from PHP 7.4 to 7.1, when creating a WordPress plugin:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.logrocket.com/coding-in-php-7-4-and-deploying-to-7-1-via-rector-and-github-actions/"&gt;Coding in PHP 7.4 and deploying to 7.1 via Rector and GitHub Actions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It explains all the hows and whys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why the target is PHP 7.1 and not 5.6&lt;/li&gt;
&lt;li&gt;Which are the PHP features from each PHP version that our code can support&lt;/li&gt;
&lt;li&gt;How to find out which 3rd party dependencies must be transpiled&lt;/li&gt;
&lt;li&gt;How to make Rector work specifically for WordPress&lt;/li&gt;
&lt;li&gt;Testing the downgrade through Travis&lt;/li&gt;
&lt;li&gt;Creating the WordPress plugin via GitHub Actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

</description>
      <category>github</category>
      <category>wordpress</category>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>🧱 Reusing code in WordPress plugins with blocks</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Tue, 17 Nov 2020 12:45:50 +0000</pubDate>
      <link>https://forem.com/leoloso/reusing-code-in-wordpress-plugins-with-blocks-d35</link>
      <guid>https://forem.com/leoloso/reusing-code-in-wordpress-plugins-with-blocks-d35</guid>
      <description>&lt;p&gt;This post was originally &lt;a href="https://leoloso.com/posts/reusing-code-in-wp-plugin-with-blocks/"&gt;published in my blog leoloso.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I've written a new article about Gutenberg (the WordPress editor), exploring:&lt;/p&gt;

&lt;p&gt;👉 What is the most effective way to reuse code, within a (single or multi-block) WordPress plugin?&lt;/p&gt;

&lt;p&gt;The answer is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.designbombs.com/reusing-functionality-for-wordpress-plugins-with-blocks/"&gt;Reusing Functionality for WordPress Plugins with Blocks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

</description>
      <category>wordpress</category>
    </item>
    <item>
      <title>🙅🏻‍♀️ How the Jamstack is failing at comments</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Fri, 06 Nov 2020 15:23:13 +0000</pubDate>
      <link>https://forem.com/leoloso/how-the-jamstack-is-failing-at-comments-3cep</link>
      <guid>https://forem.com/leoloso/how-the-jamstack-is-failing-at-comments-3cep</guid>
      <description>&lt;p&gt;This post was &lt;a href="https://leoloso.com/posts/jamstack-failing-at-comments/"&gt;originally published on my blog leoloso.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;This content &lt;a href="https://wptavern.com/matt-mullenweg-clarifies-jamstack-remarks#comment-344626"&gt;is from a comment I added in WPTavern&lt;/a&gt;, on an article where WordPress founder's Matt Mullenweg clarifies his earlier remarks that the Jamstack is "a regression for the vast majority of the people adopting it".&lt;/p&gt;




&lt;p&gt;I think Matt’s brutal honesty is welcome, because most information out there about the Jamstack praises it. However, it also comes from developers using these modern new tools, evaluating their own convenience and satisfaction. As Matt points out, that doesn’t mean it makes it easier for the end user to use the software, which is what WordPress is good at.&lt;/p&gt;

&lt;p&gt;I actually like the Jamstack, but because of how complex it is, it’s rather limiting, even to support some otherwise basic functionality.&lt;/p&gt;

&lt;p&gt;The definitive example is comments, which should be at the core websites building communities. WordPress is extremely good at supporting comments in the site. The Jamstack is sooooo bad at it. In all these many years, nobody has been able to solve comments for the Jamstack, which for me evidences that it is inherently unsuitable to support this feature.&lt;/p&gt;

&lt;p&gt;All attempts so far have been workarounds, not solutions. Eg:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Netlify forms: no hierarchy, so can post a comment but not a response (unless adding some meta to the comment body? how ugly is that?)&lt;/li&gt;
&lt;li&gt;Storing comments in a GitHub repo: it takes a long time to merge the PR with the comment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, all these solutions are overtly complicated. Do I need to set-up a webhook to trigger a new build just to add a comment? And then, maybe cache the new comment in the client’s LocalStorage for if the user refreshes the page immediately, before the new build is finished? Seriously?&lt;/p&gt;

&lt;p&gt;And then, they don’t provide the killer feature: to send notifications of the new comment to all parties involved in the discussion. That’s how communities get built, and websites become successful. Speed is a factor. But more important than speed, it is dynamic functionality to support communities. The website may look fancy, but it may well become a ghost town.&lt;/p&gt;

&lt;p&gt;(Btw, as an exercise, you can research which websites started as WordPress and then migrated to the Jamstack, and check how many comments they had then vs now… the numbers will, most likely, be waaaaaaay down)&lt;/p&gt;

&lt;p&gt;Another way is to not pre-render the comments, but render them dynamically after fetching it with an API. Yes, this solution works, but then you still have WordPress (or some other CMS) in the back-end to store the comments :P&lt;/p&gt;

&lt;p&gt;The final option is to use 3rd parties such as Disqus to handle this functionality for you. Then, I will be sharing my users’ data with the 3rd party, and they may use it who knows how, and for the benefit of who (most likely, not my users’). Since I care about privacy, that’s a big no for me.&lt;/p&gt;

&lt;p&gt;As a result, my own blog, which is a Jamstack site, doesn’t support comments! What do I do if I want feedback on a blog post? I add a link to a corresponding tweet, asking to add a comment there. I myself feel ashamed at this compromise, but given my site’s stack, I don’t see how I can solve it.&lt;/p&gt;

&lt;p&gt;I still like my blog as a Jamstack, though, because it’s fast, it’s free, and I create all the blog posts in Markdown using VSCode. But I can’t create a community! So, as Matt says, there are things the Jamstack can handle. But certainly not everything. And possibly, not the one(s) that enable your your website to become successful.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>🎉 I got my 1st big sponsor for the GraphQL API for WordPress</title>
      <dc:creator>Leonardo Losoviz</dc:creator>
      <pubDate>Mon, 02 Nov 2020 12:51:57 +0000</pubDate>
      <link>https://forem.com/leoloso/i-got-my-1st-big-sponsor-for-the-graphql-api-for-wordpress-gl5</link>
      <guid>https://forem.com/leoloso/i-got-my-1st-big-sponsor-for-the-graphql-api-for-wordpress-gl5</guid>
      <description>&lt;p&gt;This post was &lt;a href="https://leoloso.com/posts/rector-new-sponsor-graphql-api-for-wp/"&gt;originally published on my blog leoloso.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;My plugin &lt;a href="https://github.com/GraphQLAPI/graphql-api-for-wp"&gt;GraphQL API for WordPress&lt;/a&gt; just got a new sponsor!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bLfp9RMD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leoloso.com/images/tomas-sponsor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bLfp9RMD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leoloso.com/images/tomas-sponsor.png" alt="My new sponsor" title="My new sponsor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is &lt;a href="https://github.com/sponsors/leoloso/"&gt;the second sponsor&lt;/a&gt; that I get, and the first one at the u$d 1400/m tier (the other was is at u$d 70/m).&lt;/p&gt;

&lt;p&gt;This is a huge step forward, since it gives me the economic certainty as to keep developing the plugin (at least for the short/medium term... I still need a few more sponsors for it to become a living wage and long-term economic reliability).&lt;/p&gt;

&lt;p&gt;I'll describe how it happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The road to sponsorship
&lt;/h2&gt;

&lt;p&gt;Several weeks ago, there was a &lt;a href="https://make.wordpress.org/core/2020/08/24/proposal-dropping-support-for-old-php-versions-via-a-fixed-schedule/"&gt;proposal to introduce a fixed schedule to WordPress&lt;/a&gt; to bump the minimum required PHP version. Among the comments, &lt;a href="https://make.wordpress.org/core/2020/08/24/proposal-dropping-support-for-old-php-versions-via-a-fixed-schedule/#comment-39591"&gt;one of them&lt;/a&gt; struck me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So effectively this means that we cannot use PHP 8 syntax in themes/plugins if we want to support all WordPress versions until December 2023, 3 years after it has been released. This is very disappointing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I work with WordPress. My plugin is for WordPress. Not being able to use the latest PHP features in 3 more years feels very disempowering.&lt;/p&gt;

&lt;p&gt;So I decided to look for some solution, and I discovered &lt;a href="https://github.com/rectorphp/rector/"&gt;Rector&lt;/a&gt;, a tool to reconstruct PHP code based on rules. It is like &lt;a href="https://babeljs.io/"&gt;Babel&lt;/a&gt;, but for PHP. I asked if I could use Rector to transpile code from PHP 7.4 to 7.1, and they said yes, it could be done, but the rules to do it had not been created yet.&lt;/p&gt;

&lt;p&gt;So I created them.&lt;/p&gt;

&lt;p&gt;I contributed to this open source project full time for some 2 weeks, and produced some 15 rules to downgrade code, which I have applied to my plugin: Now I can code it &lt;a href="https://github.com/GraphQLAPI/graphql-api-for-wp#supported-php-features"&gt;using features from PHP 7.4&lt;/a&gt; (and even from PHP 8.0), and release it to run on PHP 7.1, so it can still target most of the WordPress user base (only users running PHP 5.6 and 7.0 are out). That's a huge win!&lt;/p&gt;

&lt;p&gt;After implementing those 15 rules, I &lt;a href="https://github.com/rectorphp/rector/issues?q=is%3Aissue+is%3Aopen+%22%5BDowngrade+PHP%5D%22+"&gt;documented the remaining rules&lt;/a&gt; to downgrade PHP code, and called it a day. I didn't mind working on them, but I didn't have the time to do it. Nevertheless, I also created this task as a &lt;a href="https://github.com/GraphQLAPI/graphql-api-for-wp/issues/56"&gt;sponsorable feature for my plugin&lt;/a&gt;; if anyone sponsored my time to work on it, I could then attempt to finish the task.&lt;/p&gt;

&lt;p&gt;Well, &lt;a href="https://tomasvotruba.com/"&gt;Tomáš Votruba&lt;/a&gt;, creator of Rector, liked my contributions so he decided to become my sponsor.&lt;/p&gt;

&lt;p&gt;Yay! 🍾 🎉 🎊 🥳 🍻 🥂&lt;/p&gt;

&lt;p&gt;In exchange, I'll work on the downgrade rules, and even attempt to have Rector itself run on PHP 7.1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nature of the sponsorship
&lt;/h2&gt;

&lt;p&gt;Now, let's be clear: Tomáš is sponsoring me to work on Rector, not on GraphQL API for WordPress. That's why the sponsoring tier is u$d 1400/m, since it involves me working on the sponsor's repo.&lt;/p&gt;

&lt;p&gt;While I get to increase the price for the tier, it makes no difference to me if the code is in my repo or my sponsor's: when I first worked those 2 weeks, it was for the benefit of my own plugin anyway, even if the code does not belong to my project.&lt;/p&gt;

&lt;p&gt;Ultimately, where the code lives is not really important. What is important is that my project (and, for that matter, any project based on PHP) will be able to benefit from it.&lt;/p&gt;

&lt;p&gt;In addition, anyone from the PHP community who starts using Rector because of my work on it, may learn that it was the GraphQL API for WP that made it possible, so I gain face and recognition.&lt;/p&gt;

&lt;p&gt;So I think this is a win-win-win for all parties involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tomáš gets Rector expanded, supporting a broader set of use cases&lt;/li&gt;
&lt;li&gt;I get the sponsorship money, and the associated badge of recognition from having a big sponsor&lt;/li&gt;
&lt;li&gt;My plugin GraphQL API for WordPress can use the new rules to use new PHP features (which was my objective all along)&lt;/li&gt;
&lt;li&gt;The WordPress community can implement this same strategy for their own themes and plugins&lt;/li&gt;
&lt;li&gt;Other PHP-based projects can also use it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Some personal takeaways from this experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contributing to open source projects may pay off&lt;/li&gt;
&lt;li&gt;Looking for synergies may pay off. Rector and my plugin can benefit from each other. Contributing to Rector is contributing to the GraphQL API for WP&lt;/li&gt;
&lt;li&gt;Getting sponsors is tough, mainly at the beginning. They may not come to you. So you need to (directly or indirectly) go to them&lt;/li&gt;
&lt;li&gt;I'm working on my plugin for the long run, and its being successful depends on many small victories, like this one&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Coming next
&lt;/h2&gt;

&lt;p&gt;My next step is to share my work on Rector with the broader WordPress and PHP communities: I've just published &lt;a href="https://blog.logrocket.com/transpiling-php-code-from-8-0-to-7-x-via-rector/"&gt;an intro to downgrading from PHP 8.0 to 7.x&lt;/a&gt;, and in a few weeks I'll publish a step-by-step guide on transpiling code for a WordPress plugin, using my repo as the example.&lt;/p&gt;

&lt;p&gt;Hopefully, along the way I'll be able to get new sponsors, and eventually achieve long-term economic certainty with my plugin 😀&lt;/p&gt;

</description>
      <category>news</category>
      <category>graphql</category>
      <category>wordpress</category>
    </item>
  </channel>
</rss>
