Skip to content

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. :)