18

I am starting a bit with the REST API. If I am not completly mislead, the init action hook is also executed when its a REST API request. Now, I want to execute some code only, when it is not a REST API request.

So I was looking for a command like is_rest() in order to do something like

<?php
if( ! is_rest() ) echo 'no-rest-request';
?>

But I couldn't find something like this. Is there a is_rest() out there?

  • 1
    Maybe you can elaborate on what you want to do when it's not a REST request? The type of request isn't determined until query parsing, which happens after init. Also note that the parts of the API can be used internally on requests that aren't REST requests, so you risk breaking something if you are relying on that detection. – Milo Mar 20 '16 at 16:39
  • Thanks a lot you both. @birgire: Can you post this as an answer, so I can check it. Basically, it is the answer to my question :) – websupporter Mar 21 '16 at 6:58
14

It's a good point by @Milo, the REST_REQUEST constant is defined as true, within rest_api_loaded() if $GLOBALS['wp']->query_vars['rest_route'] is non-empty.

It's hooked into parse_request via:

add_action( 'parse_request', 'rest_api_loaded' );

but parse_request fires later than init - See for example the Codex here.

There was a suggestion (by Daniel Bachhuber) in ticket #34373 regarding WP_Query::is_rest(), but it was postponed/cancelled.

|improve this answer|||||
  • #42061 might add wp_is_rest_request() or wp_doing_rest(). – Ian Dunn Mar 10 at 19:40
12

Just stumbled over the same problem and wrote a simple function is_rest that allows you to check if the current request is a WP REST API request.

<?php
if ( !function_exists( 'is_rest' ) ) {
    /**
     * Checks if the current request is a WP REST API request.
     *
     * Case #1: After WP_REST_Request initialisation
     * Case #2: Support "plain" permalink settings
     * Case #3: It can happen that WP_Rewrite is not yet initialized,
     *          so do this (wp-settings.php)
     * Case #4: URL Path begins with wp-json/ (your REST prefix)
     *          Also supports WP installations in subfolders
     *
     * @returns boolean
     * @author matzeeable
     */
    function is_rest() {
        $prefix = rest_get_url_prefix( );
        if (defined('REST_REQUEST') && REST_REQUEST // (#1)
                || isset($_GET['rest_route']) // (#2)
                        && strpos( trim( $_GET['rest_route'], '\\/' ), $prefix , 0 ) === 0)
                return true;
        // (#3)
        global $wp_rewrite;
        if ($wp_rewrite === null) $wp_rewrite = new WP_Rewrite();

        // (#4)
        $rest_url = wp_parse_url( trailingslashit( rest_url( ) ) );
        $current_url = wp_parse_url( add_query_arg( array( ) ) );
        return strpos( $current_url['path'], $rest_url['path'], 0 ) === 0;
    }
}

References:

|improve this answer|||||
4

To solve this problem I wrote a simple custom function based on the assumption that if the URI being requested falls under the WordPress site's Rest API URL, then it follows that it's a Rest API request.

Whether it's a valid endpoint, or authenticated, is not for this function to determine. The question is this: is the URL a potential Rest API url?

function isRestUrl() {
    $bIsRest = false;
    if ( function_exists( 'rest_url' ) && !empty( $_SERVER[ 'REQUEST_URI' ] ) ) {
        $sRestUrlBase = get_rest_url( get_current_blog_id(), '/' );
        $sRestPath = trim( parse_url( $sRestUrlBase, PHP_URL_PATH ), '/' );
        $sRequestPath = trim( $_SERVER[ 'REQUEST_URI' ], '/' );
        $bIsRest = ( strpos( $sRequestPath, $sRestPath ) === 0 );
    }
    return $bIsRest;
}

If your $_SERVER['REQUEST_URI'] isn't properly populated, this function will still return false, regardless.

There is no hard-coding of the URL so if you for some reason change your API URL base, this will adapt.

|improve this answer|||||
4

Maybe not right, but I ended up with

if (strpos($_SERVER[ 'REQUEST_URI' ], '/wp-json/') !== false) {
    // Cool API stuff here
}

Feel free to let me know if this isn't right. Trying to make a helpful plugin starter to eventually share: https://gitlab.com/ripp.io/wordpress/plugin-starter

|improve this answer|||||
  • 1
    I think, this would fail, if you would not have pretty permalinks active. – websupporter Jun 10 '19 at 9:34
  • You are definitely correct – Charly Jun 11 '19 at 14:25
  • Ok it requires pretty permalink... but who doesn't want it !!!? This seems to me the safest way to do this. All other solutions are fancy but over time if you want your code to still run on later wp versions... this seems to me a safe way !! – Antony Gibbs Jul 30 '19 at 6:21
1

Two options here really,

  1. Check if REST_REQUEST is defined.
  2. Hook into rest_api_init where you wanted to hook into init.
|improve this answer|||||
0

Here's what I came up with:

/**
 * Determines if the current request we are handling is a REST Request.
 * This function can be called even on mu-plugins.
 *
 * You might want to prefix this function name with
 * something more unique to your project.
 *
 * @return bool
 */
function is_rest(): bool {
    $is_cli              = php_sapi_name() === 'cli';
    $permalink_structure = get_option( 'permalink_structure' );
    $rest_prefix         = rest_get_url_prefix();

    if ( ! empty( $permalink_structure ) && ! $is_cli ) {
        /*
         * HTTP request with Pretty Permalinks.
         */
        if ( substr( $_SERVER['REQUEST_URI'], 0, strlen( $rest_prefix ) ) === $rest_prefix ) {
            return true;
        }
    } elseif ( empty( $permalink_structure ) && ! $is_cli ) {
        /*
         * HTTP Requests with Plain Permalinks
         *
         * We can rely on "?rest_route" for plain permalinks, this value don't change:
         * wp/wp-includes/rest-api.php:145
         *
         * All ?rest_route must start with "/":
         * wp/wp-includes/rest-api.php:159
         */
        if ( isset( $_GET['rest_route'] ) && substr( $_GET['rest_route'], 0, 1 ) === '/' ) {
            return true;
        }
    } elseif ( $is_cli ) {
        /*
         * CLI request
         */
        if ( did_action( 'parse_request' ) ) {
            return defined( 'REST_REQUEST' ) && REST_REQUEST;
        } else {
            throw new RuntimeException( "Maybe someone at StackOverflow can help fill this gap of identifying REST requests on CLI before the parse_request action has fired and the REST_REQUEST constant is available?" );
        }
    }

    return false;
}

I haven't had much time to look into making CLI detect REST requests before the parse_request action has fired, though. I'm open to suggestions!

I'm yet to write some tests around this feature, I'll update this answer once I do.

-- Edit

Just found how WooCommerce handles this. WooCommerce doesn't seem to account for plain permalinks:

public function is_rest_api_request() {
    if ( empty( $_SERVER['REQUEST_URI'] ) ) {
        return false;
    }

    $rest_prefix         = trailingslashit( rest_get_url_prefix() );
    $is_rest_api_request = ( false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix ) );

    return apply_filters( 'woocommerce_is_rest_api_request', $is_rest_api_request );
}
|improve this answer|||||

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.