Skip to content

Lando and WordPress Plugin Development

Dear Reader,Lando logo

So I’m working on my next book “Extending the WordPress REST API”. It’s the companion volume to “Using the WordPress REST API”. In this book, I actually have a working WordPress REST API controller and series of endpoints that I am showcasing. This means I’ve got development to do.

One of the hardest things I do in software development is WordPress plugin development. It takes so much to get things stood up and ready that it’s a pain just to start a project. My last few, I would spin up a site on an unused domain I own – don’t look at me funny…you’ve got them too – and then use VS Code to edit the files remotely. Not exactly the best solution but it’s a great excuse for not working when your not connected. (Scuba boat, airplane in times past, etc.)

Now though, that excuse has been taken away from me thanks to Lando. :)

Installing Lando

Installing Lando is not difficult. One of the great things about Lando it’s copious documentation. Anyone familiar with the basics of software development should be able to install Lando in no time regardless of their operating system. Thanks to Microsoft’s new WSL2 and some huge improvements in the file system, Lando now works better under WSL2 than it does on macOS. (I’ve done both)

I wrote up my thoughts on how to get Lando installed, up and running on WSL2 in this post, Making Lando work inside WSL2.

Developing a Plugin

Once you have Lando installed, the next thing we need is a plugin. You can use any plugin you are wanting to do development on. I’m using my wp_podcast_api plugin that I wrote for Voices of the ElePHPant, and that is the sample code for my book.

Once you are in your plugin’s directory, you have to create a .lando.yml file. Lando makes this part easy. Straight from the Lando Docs,

1
2
3
4
5
6
lando init \
--source remote \
--remote-url https://wordpress.org/latest.tar.gz \
--recipe wordpress \
--webroot wordpress \
--name my-first-wordpress-app

Of course you want something more clever than “my-first-wordpress-app” but the rest is exactly what you need. This will create you a basic .lando.yml file. This also downloads and uncompresses the latest version of WordPress. This is necessary for when we fire things up.

Now we start tinkering. You can find the finished product here, in case you are too impatient to read this.

The Basics

The basic .lando.yml that the init process created for us is just that, very basic.

1
2
3
4
name: my-first-wordpress-app
  recipe: wordpress
  config:
  webroot: wordpress

That’s it. It just defines our webroot. By the way, you CAN change that if you want but there’s very little value to be gained unless you’ve got something hard-coded to public.

Config

For our working environment, we want a little more than just the basics. We want ti decked out exactly the way we like to develop, otherwise, whats the point in using Lando to begin with? So I’m going to walk through each part of the finished .lando.yml file explaining why the lines exist and why I set them this way.

1
2
3
4
5
6
7
config:
  via: apache
  php: 7.4
  webroot: wordpress
  ssl: true
  xdebug: false
  database: mariadb

I am now and have always been an Apache fan. So I build with Apache. If you like nginx, that’s cool you can specify that. I’m developing using PHP 7.4 like every good PHP developer should. Also, I like MariaDB over MySQL. I don’t currently use xDebug on this project but I can easily turn it on and then lando rebuild to use it. Of course, everyone should use ssl. It’s not that important for development so it’s fine to leave that off if you like.

Services – Database

Services one gets a bit long so I will break it up into smaller chunks.

1
2
database:
  portforward: true

I want to be able to access my database from outside of the Lando container, so I tell it to port forward. Here’s the thing about Lando and port forwarding you can turn it on or off, but it’s best if you let Lando decide the port to forward. Let me quote the Lando manual for you.

portforward will allow you to access this service externally by assigning a port directly on your host’s localhost. Note that portforward can be set to either true or a specific port but we highly recommend you set it to true unless you have pretty good knowledge of how port assignment works or you have a very compelling reason for needing a locked down port.

portforward: true will prevent inevitable port collisions and provide greater reliability and stability across Lando apps. That said, one downside of portforward: true is that Docker will assign a different port every time you restart your application. You can read more about accessing services externally over here.

I mean seriously, I could expound on it, but why. They said it best.

Services – Appserver (Apache)

This is the big section so let’s break it up into even smaller chunks.

The first is the overrides section. This is where the magic happens for WordPress plugin developers happens. That volume mapping below where we map . to /app/wordpress/wp-content/plugins/wp_podcast_api, that is the secret sauce. See, the .lando.ymlgoes in the root of your plugin directory. This way everything is kept together.

You cd in to your plugin’s working directory (on my laptop I have a directory named Projectsand all of my projects, including all of my plugins, have their own dir there.) and then you lando start. Everything stays together, everything in a single git repo, all nice and tidy. That mapping makes this possible.

1
2
3
4
appserver:
  overrides:
    volumes:
      - '.:/app/wordpress/wp-content/plugins/wp_podcast_api'

Now when lando starts up, you get a directory in /app/wordpress/wp-content/plugins/wp_podcast_apithat is your plugin, just like you need it. Since it’s a mapping, you can still edit the files in the root of your project and your webserver sees those changes.

Literally, after lando startmy next command is code . and I have my editor up and running ready to start building some awesome.

If this were a simple plugin, that would be all I would need. However, this particular example depends on other plugins. I don’t want to have to re-install them every time I lando destroy -y && lando start so let’s use a combination of composer and wp-cli to put them in place and configure them. Of course we don’t do this manually, let let Lando deal with it. :)

Services – Appserver Build and Run

Lando’s .lando.yml has 4 sections under each service where you can define things that are to be executed inside the container.

  • build_as_root
  • build
  • run_as_root
  • run

The *_as root should be obvious from the names, in the case of this sample plugin, we don’t use them, just build and root. These execute every time you lando start from scratch, you lando rebuild, or you lando destroy -y and then lando start. If you just lando stop and then lando start again (or lando restart) these steps do not execute.

build
Build executes before the services have been started. If you need to download stuff, tinker w/config files, etc, build is your friend. (same for build_as_root)

1
2
build:
  - wp core download --force --skip-content

Now I know what you are thinking…WE JUST DOWNLOAD CORE! Yes, we did, for the init. However, if you want to start totally from scratch. (commit this to a repo, delete the dir, clone the rep anew and rebuild it with Lando) then you need this command.

run

run executes after the services have started. If you needs the services up and running to do a task, you want those tasks in run. In our case, we do several tasks here in run.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
run:
  - composer install
  - wp config create --dbname=wordpress --dbuser=wordpress --dbpass=wordpress --dbhost=database --skip-check --force
  - wp core install --url=podcast.lndo.site --title=Podcast --admin_user=admin --admin_password=admin --admin_email=cal@example.com
  - cp assets/.htaccess wordpress
  - perl -p -i -e "s/\/\* That's all, stop editing! Happy publishing. \*\//\ndefine('JWT_AUTH_SECRET_KEY', 'bite-me');\n\/\* That's all, stop editing! Happy publishing. \*\//" wordpress/wp-config.php
  - wp theme install twentytwenty --activate
  - wp post delete 1
  - wp rewrite structure '/%postname%/' --hard
  - wp plugin install classic-editor --activate
  - wp plugin install jwt-auth --activate
  - wp plugin install wordpress-importer --activate
  - wp plugin install wordpress-seo --activate
  - wp plugin install powerpress --activate
  - wp plugin activate wp_podcast_api
  - wp import assets/voicesoftheelephpant.wordpress.2020-07-23.000.xml --authors=create
  - wp user update calevans --rich_editing=false
  - wp user update admin --rich_editing=false

Everything we do falls into two buckets.

  1. composer tasks (exactly 1 of these)
  2. wp-cli tasks

Lando was built for devs. It knows we love our tools. So when you told it you wanted to start with a WordPress recipe, it figured out that you need composer, wp-cli and a few other must-have tools ready to go. So by the time you get to run, they are there and waiting on you.

In out run section we make use of both composer and wp-cli these tools. This plugin requires a couple of other plugins to be installed. We use composer and wpackagist to get those installed properly. After they are installed, we use wp-cli to activate them as well the TwentyTwenty theme.

I’ve also got a few config options in there and yes, I install classic editor and turn off rich-text editing, I’m old skool.

Side note, while I use composer because it’s the tool I am comfortable with, I could have just as easily used wp-cli to download, install, and activate the plugins I a using because they all come from the main WordPress plugin repository. If you are using custom plugins from a private Packagist.com, composer is your best route. If you know how to use both then you can figure out the best tool for the job each time.

Services – mailhog

Finally, one of my favorite things about Lando, I tell it to setup mailhog. This is a real simple email testing service. When you spin up Lando with mailhog it gives you a URL to go to that shows you any email that was sent to anyone. The mails are not sent out over the net but just captured for you to examine.

1
2
3
4
5
mailhog:
  type: mailhog
  portforward: false
  hogfrom:
    - appserver

Since WordPress can send a lot of emails and we want to make sure they are correct, mailhog is a great way to monitor them.

Bring it all together

Ok, if you’ve followed along so far and you’ve tweaked your .lando.yml file, you can now spin it up.

1
$ lando start

That’s all it takes.

If you look at the final lando.yml file, you’ll see that I use the wp importer tool to import a set of posts. You can also use lando db-importto populate your database from a .sql file. If you have awesome friends like Kim Cottrell then you’ve probably got a good companion tool like lando db-download that exports and downloads your production database for you.  Or you can do like I did for this one and just use XML. It doesn’t suck that bad. :)

When you are done, you can stop it with a simple

$ lando stop

Don’t destroy it with lando destroy -y unless you aren’t going to be back for a while. stopping and restarting is a lot faster and preserves you database.

As you can see from my sample plugin’s repo, I keep all of this, .lando.yml, composer.json, and the plugin code, in the same repo. This way, no mater what machine I am on, if Lando is installed, I can git pull and then lando start to be up and running with a full WordPress development site faster than light-speed. (Sorry, couldn’t resit) :)

 

Until next time,
I <3 |<
=C=

Making Lando work inside WSL2

Dear Reader,

About five months ago I started using Lando. I have never been a Docker fan. I know Docker is useful, but for most things I work on it just seemed to be one additional layer on top of everything else I’ve got to build. Then a friend I was working with at the time introduced me to Lando. Lando seemed almost perfect.

  • It was easy to spin up and down
  • It supported PHP, WordPress, Drupal, and Composer out of the box
  • It was easily extensible

Honestly, the only downside was that on Windows 10, it ran in Windows, not in WSL. On Windows all my development takes place inside WSL. Until  I started working a contract that required it, I didn’t even have Git or PHP installed on Windows, only in WSL.

Then WSL2 hit and everything changed. Docker released a new version that would run inside of WSL2 and would take advantage of the WSL2 Linux kernel when running in Windows. I was beginning to see the light at the end of the tunnel.

Still, as with everything in tech, there were hurdles to overcome. This blog post does not delve into the WHY things work the way they do; honestly, in some cases, I do not know. This is designed to be a step-by-step guide to getting Lando and Docker up and running.

Also, where applicable, I have linked to the original posts where I got the information. Most of this info was borrowed from other sites but I had to piece things together to get everything working.

Step, the first: Install Docker

I am going to assume you already have already upgraded Windows 10 and have WSL2 installed and operational. You will need a Linux distribution. I chose Ubuntu because Cent OS isn’t available (grumble).

WARNING

If you have Docker Desktop installed on Windows 10, check the settings. If you have enabled WSL integration make SURE – I mean absolutely, positively SURE – that you do not integrate it with the distro you are going to be using Lando with. If that’s your default Distro then don’t check the “Enable integration with my default WSL distro” checkbox. Make sure the slider is not lit for the distro you will be using Lando with. Failure to do this will lead to hours of heartache and head-banging until you come back to this post, read it again from the top, and see this warning.

Ok, now that you either understood and heeded the warning above, or you ignored it thinking it doesn’t really apply to you, follow the instructions from https://dev.to/bartr/install-docker-on-windows-subsystem-for-linux-v2-ubuntu-5dl7 to get Docker up and running on your WSL2 distro.

For me, these were the commands I executed. Feel free to copy and paste them one command at a time. (They are the same ones from the blog post but without the comments.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common libssl-dev libffi-dev git wget nano
 
sudo groupadd docker
 
sudo usermod -aG docker ${USER}
 
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
 
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
 
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
 
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
 
sudo apt-get update
 
sudo apt-get upgrade -y
 
sudo apt-get autoremove -y
 
sudo apt-get install -y docker-ce containerd.io
 
sudo apt-get install -y kubectl
 
sudo curl -sSL https://github.com/docker/compose/releases/download/`curl -s https://github.com/docker/compose/tags | \
grep "compose/releases/tag" | sed -r 's|.*([0-9]+\.[0-9]+\.[0-9]+).*|\1|p' | head -n 1`/docker-compose-`uname -s`-`uname -m` \
-o /usr/local/bin/docker-compose && sudo chmod +x /usr/local/bin/docker-compose

That got me almost there. right now, if you can run the following command and it works then you are golden.

docker run hello-world

If that worked, you can proceed to Step, the Second.

However, if you get this error:

docker: Error response from daemon: cgroups: cannot found cgroup mount destination: unknown.

Then you need one more step. Reading the thread https://github.com/docker/for-linux/issues/219, I found that Dinar-Dalvi posted this “temporary” fix. Execute these two commands in your WSL2 terminal.

1
2
sudo mkdir /sys/fs/cgroup/systemd
sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd

Now try Docker run hello-world again. It should work. If you’ve gotten this far, you are home free. The rest is pretty easy.

Step, the second: Install Lando

Ok, Lando’s install instructions are pretty dang simple. Go to the Lando Releases Page and grab the package for your distro from the latest version. Since I am using Ubuntu, I grabbed the .deb package. Then, following the instructions on the Lando Install Page I executed:

sudo dpkg -i lando-WHATEVER_I_DOWNLOADED.deb

Yup, it’s that easy.

Step, the third: Spin up your first Lando

I work with WordPress a lot so my Lando Hello World was spinning up a WordPress site. The very easy to use Lando manual has a page for WordPress and to init a container for Lando with WordPress, all I need to do is follow these instructions.

$ lando init \
--source remote \
--remote-url https://wordpress.org/latest.tar.gz \
--recipe wordpress \
--webroot wordpress \
--name my-first-wordpress-app
$ lando start

BOOM!

Two minutes later I have a working WordPress site ready for my “Five Minute Install.”

Before you start the final WordPress install step, make sure you execute:

lando info

Lando has already installed a DB for you and created a user. lando info will give you the info you need to finish up the install and start developing your next great idea with Lando.

Step, the last: Wrap-up

If you are already a Docker aficionado and conversant with how to setup and maintain Docker containers, Lando, will be of little interest to you. However, if you are a developer who just wants to get a container spun up so they can work on a website (WordPress, Drupal, Laravel, whatever) it is a great tool and will fast become one of your regulars.

Until next time,
I <3 |<
=C=

Posted in PHP

The VS Code extensions I use for PHP development in 2019

Dear Reader,

I hate IDEs. I always have, I probably always will. I don’t always want to edit a file that is in a project and I don’t like any of the ‘project-less’ kludges that are out there.  So for the majority of my programming career, I’ve used “program editors”.  Programs just smart enough to know that I’m working in code and give me tools to do that properly, but not so smart that they get in my way. For the last few years I’ve been a huge fan of “Sublime Text”. I’ve owned a license at the last three places I’ve worked and even maintained my own personal license.  All that changed last year when I became one of the last developers on earth to notice VS Code by Microsoft.

Back in April, since I found myself with some time on my hands, I decided I would spend a day actually learning how to use VS Code. Maybe if I had bothered to do that with Sublime Text, I’d still be using it. but that’s a discussion for another day.

Once I got the basics setup, I started exploring Extensions. I’ve loaded more than 40 of them now at one time or another but I’ve paired my list down to my “Essential 12”. Here they are for you.

WAIT! Before we get started though, let me describe my setup.

I use Windows 10, but I use the Windows Subsystem for Linux (WSL) for all of my development. My production servers are all Linux (CentOS if you MUST know) All of my projects are stored on either GitLab or GitHub.  You may be using MacOS or Linux, but since all my development is Linux, almost everything I talk about here will apply.

“Absolutely must have or it’s not worth using” extensions

Visual Studio Code Remote Development Extension Pack

This is the grandaddy of all the extensions. This is what made VSC actually useful to me on a day to day basis. It was only released in May, so I have the least amount of experience with this one but trust me, this is the ONE you have to install if you don’t install anything else.

This is actually an extension pack, not a single extension.

  • Remote – WSL
  • Remote – Containers
  • Remote – SSH: Explorer
  • Remote – SSH: Editing Configuration Files

The first three are the secret sauce. They allow you to edit files natively inside of the WSL, inside of running Docker containers, or on any machine that you can ssh into.

From a command prompt inside the WSL (I use bash so I’m going to say bash prompt form now on. You can translate into your favorite command prompt) developers can open a single file  or a directory by simply typing code.

e.g.

code .

This will open VCS, instantiate a running instance of it’s server piece inside the WSL, and open the current directory ready for us to begin working.

It’s that middle task that makes the magic happen. VSC Remote Development Extension Pack actually has a server piece that it runs in Containers, WSL, and over SSH to allow it to seamlessly manipulate files and directories.

This group of extensions also integrate the remote file system into VSC so you can use File->New/Save/SaveAs/Delete etc on remote systems, WSL, and inside containers. That did away with the need for an extension solely for that purpose.

The fourth item on the list above is a convenience extension that makes editing remote ssh config files easier by giving keyword intellisense and syntax coloring.

BONUS: When you open the terminal window while connected to WSL, a container, or SSH, you get a command prompt in that environment. This means a bash shell in WSL for me, not the PowerShell. (which I still hate) The shell it presents is better and more compatible than any shell I’ve used in Windows so far. If you want, you can even setup forwarders. This could be your complete ssh solution if you so desired.

If you don’t have this extension, you are missing the power of VSC, especially if you work in Win/WSL.

PHP IntelliSense

Since the vast majority of my work in VSC is PHP code, I wanted more than the basic intellisense that is built into VSC. (It is actually recommended that when you install this extension, you turn off the native PHP intellisense) There are a couple of Extensions that do this, I tried them all and this is by far the best and least intrusive.

At least one of them tried to format things as I pasted them in. This caused me to have to do a lot of correcting, removing spaces, and reformatting before I could use the code I just pasted in. This plugin does not do anything too wild. It gives you basic keyword intellisense, tab completion, advanced searching, etc. One of my favorite things about this extension is that if you click on a use statement, it will locate the file that class is defined in and open it for you.

As I said, this plugin does not attempt to format your code for you. It recommends that you use the PHPCS extension for that. (Another reason I love it)

“Sugar coating that make the tool very nice to use” Extensions

Polacode

This one really should be in a category all it’s own called “This is so cool” Extensions. Polacode allows you to create “snapshots” of your code. You activate it, you highlight the code you want snapshoted, and you click the shutter button. BOOK, you’ve got a png of your code ready to share on social media or incorporate into your next slide presentation. It really is that simple. (Well, when you click the button, it asks you where to save it and what to name it. Other than that it’s that simple.)

Outside of the Remote Development Extension Pack, this is probably the coolest extension I use.

vscode-php-cs-fixer

If you do want your code auto-formatted, this is the extension to use. You have complete control over which standards to implement and those can even change on a project by project basis. If you use phpcs in your development process this is a must-have extension. If you install it though, take the time necessary to configure it properly for your base configuration and then modify that as needed on a per-project basis. Since I work in several communities, this allows me to say that my base config is PSR1/1, but my WordPress plugins adhere to the WordPress coding standards. (Mostly) The investment of 30 minutes to get everything setup and configured is time well spent.

Twig Language 2

Some of the work I do requires views and when I use a view, I always use Twig as my templating engine. This extension provides syntax highlighting and hovers for my twig templates. Syntax highlighting might not sound important but once you get used to it, and then you open a file that does not have it, you begin to wonder if your program is broke or if you’ve suddenly been transported into a black & white movie.

Code Spell Checker

Anyone who has ever received an email from me (or read one of my blog posts that pre-dates spellcheckers built into browsers) knows that I seriously suck at spelling. Even when I do have spell checkers they rarely work in code because $somethingImportant will never pass a spell checker. So I have this plugin. This one is hit or miss. It almost always identifies misspelled words in strings or comments (YEA COMMENTS!) but it used to be that I could have it fix them. That stopped working in the latest update. Hopefully they get that fixed soon.

Bracket Pair Colorizer

First, you should not have a lot of nested IF and FOR statements. Down that road is madness. However there are a lot of times where we have function calls nested inside other function calls, nested inside an IF. When you do find yourself in that situation, you want this plugin. This plugin assigns a color to each pair of brackets, braces, and parentheses.  This makes tracing down missing ones trivial and helps you understand the actual nesting of your code.

 

“You may not needs these but I sure do” Extensions

markdownlint

I write a lot. Of late I’ve been writing two books “Using the WordPress REST API” and “Extending the WordPress REST API”. These books are written in MarkDown and stored in private repos on github. (Leanpub refuses to support GitLab, my preferred site. ) Most of the time, MarkDown is easy to do, however, there are some rules that have to be followed. This plugin keeps all of my markdown clean and parse-able.

Prettify JSON

JSON is the lingua franca of APIs. It is heads and shoulders better than XML in every respect except one, readability. XML is human readable. This plugin basically does what PHP’s json_encode($payload, JSON_PRETTY_PRINT) does. It puts linefeeds where we need them and indents where we need them. It also does syntax highlighting and linting for JSON. If you find yourself pasting JSON into a text editor and making it readable by hand, you want this extension.  If on the other hand, you prefer XML, well, you have problems beyond the scope of this article. :)

Word Count

Again, another writing tool. Not even a tool, just a simple little plugin that tells me how many words are in a given MarkDown file. It is not important if you don’t do a lot of writing, but without it I couldn’t tweet out my “how many words I wrote today” tweets.

“I’m keeping an eye on this” Extensions

SQLTools

I’m an old database programmer. I still hand code every line of SQL I write. So I am always looking for new ways to access and manipulate data. I had high hopes for this extension because it looked like it would give me a powerful command line to access my database. To date I’ve not been able to do anything other than get a list of tables on a server.

Still, I have not uninstalled it. Every time there is an update, I try again to see if it is working better.

 

fClose()

That’s it, that’s my current extension list. I hope I’ve introduced you to a new friend or two. If you are new to VS Code, I hope I’ve given you a few new toys to play with.

Until next time,
I <3 |<
=C=

In Celebration of B-Sides

Dear Reader,

One of the most beautiful rock songs of my lifetime, “Beth” by Kiss, was originally released as the “B-Side” to “Detroit, Rick City”.

I know I’m in the minority here but I will go to my grave believing that with “Beth”, Kiss hit it’s pinnacle. Yet, the relegated it to a B-side.

The band, and their producers couldn’t see the beauty they had until much later when fans started clamoring for it to be played on the radio. It was the band’s highest charting single…ever.  (It hit #7 on Billboards Hot 100 chart.)

Tech Conference B-Side Talks

Tech conferences are sometimes like that. Some CFP programs actually ask speakers “Is this your best talk?”. They are looking for the A-Sides.

Honestly, I have no idea what my “best talk” is. I know the ones that I get a lot of comments about, but I can’t say that those are my best.

I can tell you my favorite talks. My favorite talks are not my keynote talks, my favorite talks are the ones where someone comes up afterwards and tells me that they picked up something that they can use in a project they are working on. They are my favorite talks because they actually helped someone.

I recently tweeted out a piece of speaker advice.

Speaker Pro Tip: Write a second talk. Call it your “B-Side” talk. (Kids, ask your parents what that means) Have the slides with you at any Conf or Camp you attend. So far in the past 3 months I’ve scored 2 extra speaking slots because I was prepared.

I didn’t mean to classify the talks by importance, more by “acceptability”.  There are some talks that it’s easy to get accepted at a conference because they push a hot button or they are about the “shiny”.

“B-Side” talks are talks that are important but that conference organizers don’t feel they can take a chance on.

The next time you attend a conference, look for talks that are off the beaten path. Look for those B-side talks.

Hidden in among all those great sessions is the next “ZOMG DID YOU SEE…” and you will be able to say “Yeah. I saw that one last year and it was great.” :)

Until next time,
I <3 |<
=C=

p.s. There’s nothing wrong with “Detroit Rock City” but it’s an obvious attempt to write a rock anthem. “Beth” was art for the sake of art. Thank you Peter Chriss.

WordPress, REST, and RegEx

Dear Reader,

I’m going to add this to “Using the WordPress REST API“, but I thought I would blog it here as well.

I have a tendency to over think things.

Today, I was working on a REST API endpoint for a client and it needed to have RegEx in it. I hate RegEx with a passion usually reserved for XML, but unlike XML, it’s a necessary evil, so I dove into it.

1
/item/(?P<itemId>\d+)

That’s an example.

For the uninitiated, when you are defining a custom WordPress REST endpoint, one of the things you can do is put in Regex into the route definition and WordPress will use that to pull out content and make it a parameter. The code above defines an endpoint for

https://example.com/wp-json/my-namespace/v1/item/4

When run, it will make a parameter named itemId whose value is the 4 from the URI. It’s incredibly handy. They are very easy to work with, especially if you are using numbers like 4, or even 294875.  Strings…well, strings get tricky.

The above example expect a number (so no alpha, just numeric) and numbers are contiguous.  You don’t have numbers with a space in them. Phrases however, have spaces. And what I needed to pull out was a phrase. So, I did what I always do, I pulled out Regex 101, and started figuring this out. This is where the overthinking part comes in.

I got it working in short order, then I started thinking. “What if…”

  • What if There’s a query string at the end
  • What if there’s more to the URI
  • What if there’s a slash at the end
  • …what if

This is where I got into trouble. I lost a good 2-3 hours designing a beautiful piece of RegEx that handled every situation I could think of. It was art, if I do say so myself. The only problem was that once I pasted it into my WordPress REST Controller, it did not work.

So I did what every developer does, I assumed the problem had to be in WordPress. I rolled up my sleeves and found out how WordPress matches routes.

What WordPress does

WordPress matches REST routes in WP_REST_Server::dispatch() (wp-includes/rest-api/class-wp-rest-server.php)

1
$match = preg_match( '@^' . $route . '$@i', $path, $matches );
$path is the URI. IN my case
https://example.com/wp-json/my-namespace/v1/item/this%20item%20name
$route was the route I defined in regex.
1
item/(?P<itemName>[w+].*)[?|/|\$]
(I’m working from memory but I think that was it.)
If – and only if – I could set some pattern modifiers I could make it work…but I couldn’t.
Then I began doing the other thing that PHP developers do a lot, I began throwing var_dump();die(); into WP_REST_Server. I thought I needed to see what was going on. Turns out, the answer was there in front of me all the time.
I was assuming that WordPress was applying my RegEx standalone from everything else. If you look at the line above though, you can see that is uses the route that I define in it’s entirety.
  • It puts a ‘@’ at the beginning of it. This tells PHP that ‘@’ is the regex delimiter, not ‘/’ like usual
  • It adds the caret ‘^’ to match the beginning of the line
  • It concatenates the route I defined
  • It adds the $ to match the end of the line
  • It puts the ‘@’ to signify that this is the end of the RegEx
  • It adds the ‘i’ pattern modifier (the things I needed to tinker with) to indicate that all matches should be case insensitive
WordPress doesn’t worry about the query string, or anything after that because it’s already stripped it off. I don’t have to overthink this thing with subgroups and special characters, WordPress has got my back.
My finished product ended up looking like this:
1
item/(?P<itemName>w+.*)
This gives me a parameter named itemNamethat includes everything past item/ to the end of the line.

Conclusion

Stop over-thinking things. Sometimes just let the framework do it’s job. :)

Did I meantion I hate Regex? :)

Until next time,
I <3 |<
=C=

p.s. the section in the book will be more coherant. I’ve spent the day with RegEx so I’m a bit scatterbrained now. :)