1

What I tested

Wordpress Version: 4.9.5

PHP Version: 7.1.7/7.1.4/7.2.0/7.0.3/5.6.20/5.5.38/5.4.45

Web Server: Nginx 1.10.1/Apache 2.4.10

Environment/Host: Flywheel local https://local.getflywheel.com/

PCRE Library Version: 8.38 2015-11-23

I have been trying to work with register_rest_route but running into regex issues. It seems that there is some filtering on the regex before its executed.

The function I am trying to create is grabbing a post by the permalink. Basically I have a client that is structuring its url's similar to wordpress so it wants to pass the permalink to wordpress so I can get the post data

register_rest_route( $this->namespace, '/post_by_permalink/(?P<path>[\w-]+)', array(
   'methods'             => WP_REST_Server::READABLE,
   'callback'            => array( $this, 'postByPermalink' ),
   'permission_callback' => array( $this, 'permissions' ),
   'show_in_rest' => true
));

This is the function I am using and it works in some cases. However that is only because the ones that work do not produce any special characters.

Right now my plan was in the client to convert the permalink to base64 to make it easier to pass through the url, though I am open to suggestions if they work (I only thought of base64 since urlencode was even more of a nightmare).

Basically the only regex that work in wordpress seems to be (?P<path>[\w-]+) and (?P<path>[\d]+). Anything else, does not work, even if it was successful testing it in something like http://www.phpliveregex.com

For instance, according to the tester (?P<path>[\S]+) should work, but all I get from wordpress is rest_no_route

Is there anyway to get wordpress to handle regex normally? Or at least tell it to allow an expression that could catch base64? I have seen some options online, but none work, likely due to changes in wp rest over time (all say "just use regex", yet it seems to filter more complex regex)

EDIT: extending example

app/v1/post_by_permalink/(?P<path>[\S-]+)

Is what will work in http://www.phpliveregex.com

The search string being

/wp-json/app/v1/post_by_permalink/dGVzdC90ZXN0

That same regex will not work in wordpress, only (?P<path>[\w-]+) will work, and for the the above example, it will work as well in both since there are no special characters.

However for this string it will not

/wp-json/app/v1/post_by_permalink/dGVzdC90ZXN0LzM1NzM0Ly0=

Since it has an = So although in the regex tester (?P<path>[\S]+) works, it will not work in wordpress.

Edit: further testing

I managed to look up rest_pre_dispatch and get it working to catch the request before it checks the routes.

add_filter('rest_pre_dispatch', 'filter_request');
function filter_request($result) {
    global $wp;

    //print_r($wp->request); die;
    preg_match("/app\/v1\/post_by_permalink\/(?P<path>[\S]+)/", $wp->request, $output);
    print_r($output); die;
    return $result;
}

Is able to set the path correctly, so it does not seem to be a preg_match problem. At least with this I can bypass wp-rest and get around this issue, but I still have not found what is actually causing every environment I try this in to fail, and yet no one else can recreate it.....

After testing more thoroughly I got the below to work, I was using $wp->request witch included wp-json that was causing the regex to fail.

Though when I use preg_match( '@^' . $route . '$@i', $path, $matches ) I cannot get it to work (that is how wp-rest does it, $route being what is passed through register_rest_route), and using preg_match in this way is not supported in the regex tester http://www.phpliveregex.com/ either. It adds more questions then answers.

  • I tried (?P<path>[\S]+) and it works. can you edit your question to show us the complet code ? – mmm Apr 17 '18 at 18:34
  • Ok I will make an edit extending the example, the its not even passing the above register_rest_route and I have a dump inside the function it should call. – Jordan Ramstad Apr 17 '18 at 18:38
  • I can't reproduce your problem either. \S works fine, and will return more or less anything. Don't need base64 here at all from what I can see. – Otto Apr 17 '18 at 20:39
  • Yea, base64 was my initial thought to get around this issue. I see 3 people now who cannot recreate it, but I have managed to in 14 different setups, so I am super confused lol – Jordan Ramstad Apr 17 '18 at 20:44
1

I got curious, so I tested this barebone demo:

add_action( 'rest_api_init', function () {
    register_rest_route( 'wpse/v1', '/post_by_permalink/(?P<path>[\S]+)', [
        'methods'      => WP_REST_Server::READABLE,
        'callback'     => 'wpse_callback',
        'show_in_rest' => true
    ] );
});

function wpse_callback( $request ) {
    $data = [ 'path' => base64_decode( $request['path'] ) ];
    return $data;
}

Testing:

https://example.com/wp-json/wpse/v1/post_by_permalink/aHR0cHM6Ly93b3JkcHJlc3Muc3RhY2tleGNoYW5nZS5jb20vcS8zMDEwNjcv

gives

{
   path: "https://wordpress.stackexchange.com/q/301067/"
}

Tested also

https://example.com/wp-json/wpse/v1/post_by_permalink/aHR0cHM6Ly93b3JkcHJlc3Muc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzMwMTA2Ny9yZWdpc3Rlci1yZXN0LXJvdXRlLXJlZ2V4LW9wdGlvbi1mb3ItYmFzZTY0LW9yLWFsdGVybmF0ZS8zMDEwNzY=

that gave:

{
path: "https://wordpress.stackexchange.com/questions/301067/register-rest-route-regex-option-for-base64-or-alternate/301076"
}

Tested further:

https://example.com/wp-json/wpse/v1/post_by_permalink/dGVzdC90ZXN0LzM1NzM0Ly0=

with output:

{
path: "test/test/35734/-"
}
  • I apologize, I realize using [\w-] in the function made it seem like that was an example of it not working. You need a base64 encoded string with a - in it for it to add an = to demonstrate the problem. example.com/wp-json/wpse/v1/post_by_permalink/… – Jordan Ramstad Apr 17 '18 at 18:48
  • I updated with a test with a base64 encoding string that includes =. That tested ok on my install. Wonder if there's some external things going on? Some hosting providers have e.g. extra security. Did my testing in Chrome. – birgire Apr 17 '18 at 18:58
  • Yes dumping first inside the callback is how I am checking it, even with your string it fails for me. It will do the same even if I try to use a url encoded string (still passing on the regex tester). Basically as soon as I use \S It always fails. – Jordan Ramstad Apr 17 '18 at 19:01
  • I tried birgire's tests on Firefox and everything work the same. I think too that your hosting provider can be the problem. – mmm Apr 17 '18 at 19:54
  • Its a local dev environment, flywheel, just checked switching php versions and also from nginx to apache and ran into the same issue. Considering its giving a rest api error I know its hitting the rest-api, its just for some reason preg_match is not doing what it should from what I can see. – Jordan Ramstad Apr 17 '18 at 20:02
1

Finally figured it out, and I can only blame myself....

When I was testing, I had disabled all plugins, but during that time I had left it using (?P<path>[\w-]+) while testing with a base64 string that contained an =. The whole time I had eliminated plugins as an issue thinking I had recreated it despite having no plugins active.

The culprit was Rest Manager https://wordpress.org/plugins/rest-manager/

I have opened a support ticket to let the developer know.

It utilizes rest_pre_dispatch to filter out routes you set as inactive, problem is if you change routes you need to go in and resave the configuration. Thankfully it was repairable since it has a hook before it filters out the routes, so I could "fix" the function and assume routes that had no configurations should default to active.

Thank you to @mmm, @birire and @Otto for taking the time to try to recreate the issue.

Disable plugins when trying to isolate an issue kids ;-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.