深入理解 WordPress `get_rest_url()` 函数源码:生成 REST API 基础 URL 的工作原理。

各位观众老爷们,大家好!今天咱们来聊聊 WordPress 里一个看起来不起眼,但实际上非常重要的函数:get_rest_url()。这货是干啥的呢?简单来说,它就是生成 WordPress REST API 的基础 URL 的。

你可能会想,URL 不就是个字符串吗?直接拼不就行了?理论上是没错,但 WordPress 这么成熟的系统,肯定不会让你这么粗暴。get_rest_url() 考虑了各种情况,比如子目录安装、自定义域名、HTTPS 等等,保证生成的 URL 是正确且可靠的。

咱们今天就来扒一扒 get_rest_url() 的源码,看看它是怎么工作的。准备好你的咖啡和键盘,Let’s go!

一、get_rest_url() 的基本用法

首先,咱们先看看 get_rest_url() 的基本用法。这货可以接受两个参数:

  • $path (string, 可选): 要附加到基础 URL 的路径。默认为空字符串。
  • $scheme (string, 可选): 要使用的 URL 协议(’http’ 或 ‘https’)。如果为空,则使用当前请求的协议。
<?php
// 获取 REST API 的基础 URL
$base_url = get_rest_url();
echo $base_url; // 输出类似:http://example.com/wp-json/ (或者 https://example.com/wp-json/)

// 获取指定命名空间的 URL
$namespace_url = get_rest_url(null, 'myplugin/v1');
echo $namespace_url; // 输出类似:http://example.com/wp-json/myplugin/v1

// 强制使用 HTTPS 协议
$https_url = get_rest_url(null, 'myplugin/v1', 'https');
echo $https_url; // 输出类似:https://example.com/wp-json/myplugin/v1
?>

是不是很简单?接下来,咱们就要深入到源码里,看看这些 URL 到底是怎么生成的。

二、get_rest_url() 源码分析

get_rest_url() 的源码位于 wp-includes/rest-api.php 文件中。咱们来一段一段地剖析:

<?php
/**
 * Retrieves the REST URL.
 *
 * @since 4.4.0
 *
 * @param string|null $path   Optional. REST API path. Default null.
 * @param string|null $scheme Optional. Scheme to use. Default null.
 * @return string REST URL.
 */
function get_rest_url( $path = null, $scheme = null ) {
    return get_url_from_relative( rest_get_url_prefix(), $path, $scheme );
}

嗯?就这么点?别急,这只是个入口函数。它调用了 get_url_from_relative()rest_get_url_prefix() 这两个函数。咱们先来看看 rest_get_url_prefix()

<?php
/**
 * Retrieves the REST API URL prefix.
 *
 * @since 4.4.0
 *
 * @return string REST prefix.
 */
function rest_get_url_prefix() {
    /**
     * Filters the REST API URL prefix.
     *
     * @since 4.4.0
     *
     * @param string $prefix URL prefix. Default 'wp-json'.
     */
    return apply_filters( 'rest_url_prefix', 'wp-json' );
}

这个函数更简单,直接返回了 'wp-json' 字符串,并且提供了一个 rest_url_prefix 过滤器,允许你自定义 REST API 的 URL 前缀。如果你想把 'wp-json' 改成 'api',就可以使用这个过滤器。

接下来,咱们再来看看 get_url_from_relative()

<?php
/**
 * Retrieves a full URL from a relative path.
 *
 * @since 4.7.0
 *
 * @param string      $relative_url Relative URL.
 * @param string|null $path         Path to add to the URL.
 * @param string|null $scheme       Optional. Scheme to use. Default null.
 * @return string Full URL.
 */
function get_url_from_relative( $relative_url, $path = null, $scheme = null ) {
    $url = get_home_url( null, $relative_url, $scheme );

    if ( ! empty( $path ) && is_string( $path ) ) {
        $url .= '/' . ltrim( $path, '/' );
    }

    return $url;
}

这个函数才是真正的核心!它接收三个参数:

  • $relative_url: 相对 URL,这里就是 rest_get_url_prefix() 返回的 'wp-json'
  • $path: 要附加到 URL 的路径。
  • $scheme: URL 协议。

这个函数首先使用 get_home_url() 获取 WordPress 的主页 URL,然后把相对 URL 附加到主页 URL 后面。最后,如果 $path 参数不为空,就把 $path 也附加到 URL 后面。

三、get_home_url() 的秘密

咱们上面提到了 get_home_url() 这个函数,它负责获取 WordPress 的主页 URL。这个函数也挺复杂的,咱们也来简单看一下:

<?php
/**
 * Retrieves the URL for the home page.
 *
 * @since 2.2.0
 *
 * @param string|int|null $blog_id Optional. The blog ID. Default null.
 * @param string          $path    Optional. Path relative to the home URL. Default null.
 * @param string          $scheme  Optional. Scheme to use. Default null.
 * @return string Home URL.
 */
function get_home_url( $blog_id = null, $path = '', $scheme = null ) {
    return get_site_url( $blog_id, $path, $scheme );
}

嗯?又是一个简单的入口函数?没错,get_home_url() 实际上是调用了 get_site_url() 函数。

<?php
/**
 * Retrieves the URL for a given site.
 *
 * @since 3.0.0
 *
 * @param int|null    $blog_id Optional. Site ID. Default null (current site).
 * @param string      $path    Optional. Path relative to the site URL. Default null.
 * @param string|null $scheme  Optional. Scheme to use. Default null.
 * @return string Site URL.
 */
function get_site_url( $blog_id = null, $path = '', $scheme = null ) {
    global $current_site;

    if ( ! is_multisite() ) {
        $url = home_url( $path, $scheme );
    } else {
        if ( empty( $blog_id ) ) {
            $blog_id = get_current_blog_id();
        }

        $current_site = get_current_site();

        $scheme = wp_allowed_protocols( $scheme );

        if ( ! in_array( $scheme, array( 'http', 'https', 'relative' ), true ) ) {
            $scheme = is_ssl() ? 'https' : 'http';
        }

        $site_url = get_network()->siteurl;
        $path     = ltrim( $path, '/' );

        if ( $blog_id == get_main_site_id() || ( defined( 'DOMAIN_CURRENT_SITE' ) && DOMAIN_CURRENT_SITE == parse_url( $site_url, PHP_URL_HOST ) ) ) {
            $url = set_url_scheme( $site_url, $scheme );
        } else {
            switch_to_blog( $blog_id );
            $url = home_url( $path, $scheme );
            restore_current_blog();
        }
    }

    return $url;
}

get_site_url() 函数就比较复杂了,它考虑了单站点和多站点的情况。

  • 单站点: 直接调用 home_url() 函数。
  • 多站点: 首先获取当前站点的 ID,然后根据站点 ID 获取站点 URL。如果当前站点是主站点,就直接使用主站点的 URL。否则,就切换到当前站点,然后调用 home_url() 函数,最后再恢复到原来的站点。

咱们再来看看 home_url() 函数:

<?php
/**
 * Retrieves the URL for the current site where the front end is accessible.
 *
 * @since 2.2.0
 *
 * @param string      $path    Optional. Path relative to the home URL. Default null.
 * @param string|null $scheme  Optional. Scheme to use. Default null.
 * @return string Home URL.
 */
function home_url( $path = '', $scheme = null ) {
    $orig_scheme = $scheme;

    if ( defined( 'WP_HOME' ) ) {
        $url = untrailingslashit( WP_HOME );
    } else {
        $url = get_option( 'home' );
    }

    if ( empty( $url ) ) {
        $url = site_url();
    }

    $url = set_url_scheme( $url, $scheme );

    if ( $path && is_string( $path ) ) {
        $url .= '/' . ltrim( $path, '/' );
    }

    /**
     * Filters the home URL.
     *
     * @since 2.9.0
     *
     * @param string      $url         The complete home URL including scheme and path.
     * @param string      $path        Path relative to the home URL.
     * @param string|null $orig_scheme Scheme to use.
     */
    return apply_filters( 'home_url', $url, $path, $orig_scheme );
}

home_url() 函数首先检查是否定义了 WP_HOME 常量。如果定义了,就使用 WP_HOME 的值作为主页 URL。否则,就从 wp_options 表中获取 home 选项的值。如果 home 选项为空,就调用 site_url() 函数。最后,把 URL 协议设置为 $scheme 参数指定的值,并把 $path 参数附加到 URL 后面。

四、set_url_scheme() 的作用

咱们上面提到了 set_url_scheme() 函数,它负责设置 URL 的协议。这个函数也挺重要的,咱们也来简单看一下:

<?php
/**
 * Sets the scheme for a URL.
 *
 * @since 2.7.0
 *
 * @param string      $url    The URL to set the scheme for.
 * @param string|null $scheme The scheme to use. Must be 'http', 'https', 'relative', or null.
 * @return string The URL with the updated scheme.
 */
function set_url_scheme( $url, $scheme = null ) {
    $original_scheme = parse_url( $url, PHP_URL_SCHEME );

    if ( ! $original_scheme ) {
        $original_scheme = 'http';
    }

    if ( ! $scheme ) {
        $scheme = is_ssl() ? 'https' : 'http';
    }

    if ( 'relative' === $scheme ) {
        $scheme = '//';
    }

    $url = preg_replace( '|^' . preg_quote( $original_scheme, '|' ) . '://|', $scheme . '://', $url );

    return $url;
}

set_url_scheme() 函数首先解析 URL,获取原来的协议。然后,如果 $scheme 参数为空,就根据当前是否使用 HTTPS 来设置协议。如果 $scheme 参数为 'relative',就把协议设置为 '//',表示使用相对协议。最后,使用正则表达式替换原来的协议。

五、总结

咱们来总结一下 get_rest_url() 函数的工作流程:

  1. get_rest_url() 函数接收一个可选的路径和一个可选的协议作为参数。
  2. get_rest_url() 函数调用 rest_get_url_prefix() 函数获取 REST API 的 URL 前缀,默认为 'wp-json'
  3. get_rest_url() 函数调用 get_url_from_relative() 函数,把 URL 前缀附加到 WordPress 的主页 URL 后面。
  4. get_url_from_relative() 函数调用 get_home_url() 函数获取 WordPress 的主页 URL。
  5. get_home_url() 函数根据单站点和多站点的情况,调用 home_url() 函数获取主页 URL。
  6. home_url() 函数首先检查是否定义了 WP_HOME 常量,然后从 wp_options 表中获取 home 选项的值。
  7. home_url() 函数调用 set_url_scheme() 函数设置 URL 的协议。
  8. get_url_from_relative() 函数把路径附加到 URL 后面。
  9. get_rest_url() 函数返回最终的 REST API URL。

为了更清晰地了解整个流程,咱们可以看下面这张表格:

函数名 功能
get_rest_url() 生成 REST API 的基础 URL,是入口函数。
rest_get_url_prefix() 获取 REST API 的 URL 前缀,默认为 'wp-json',允许使用过滤器自定义。
get_url_from_relative() 把相对 URL 附加到 WordPress 的主页 URL 后面,并处理路径参数。
get_home_url() 获取 WordPress 的主页 URL,根据单站点和多站点的情况调用不同的函数。
get_site_url() 获取指定站点的 URL,区分单站点和多站点,多站点时需要切换站点。
home_url() 获取当前站点的主页 URL,考虑了 WP_HOME 常量和 home 选项,并允许使用过滤器自定义。
set_url_scheme() 设置 URL 的协议,根据 $scheme 参数和当前是否使用 HTTPS 来设置协议,并处理 'relative' 协议。

六、实际应用场景

了解了 get_rest_url() 函数的工作原理,咱们来看看它在实际开发中有什么应用场景:

  • 自定义 REST API 接口: 在开发自定义 REST API 接口时,需要使用 get_rest_url() 函数生成接口的 URL。

    <?php
    add_action( 'rest_api_init', function () {
        register_rest_route( 'myplugin/v1', '/items', array(
            'methods'  => 'GET',
            'callback' => 'my_get_items',
        ) );
    } );
    
    function my_get_items( WP_REST_Request $request ) {
        // 处理请求
        return array( 'message' => 'Hello from my plugin!' );
    }
    
    // 获取接口的 URL
    $api_url = get_rest_url( null, 'myplugin/v1/items' );
    echo $api_url; // 输出类似:http://example.com/wp-json/myplugin/v1/items
    ?>
  • 前端 AJAX 请求: 在前端使用 AJAX 请求 REST API 接口时,需要使用 get_rest_url() 函数生成接口的 URL。

    // 获取接口的 URL
    var apiUrl = '<?php echo esc_url_raw( get_rest_url( null, 'myplugin/v1/items' ) ); ?>';
    
    // 发送 AJAX 请求
    fetch(apiUrl)
        .then(response => response.json())
        .then(data => {
            console.log(data); // 输出:{message: "Hello from my plugin!"}
        });
  • 插件设置页面: 在插件设置页面中,需要显示 REST API 接口的 URL,可以使用 get_rest_url() 函数生成 URL。

七、注意事项

在使用 get_rest_url() 函数时,需要注意以下几点:

  • URL 编码: 如果 $path 参数包含特殊字符,需要进行 URL 编码,可以使用 urlencode() 函数。
  • HTTPS: 如果需要使用 HTTPS 协议,可以设置 $scheme 参数为 'https'
  • 缓存: get_rest_url() 函数的结果可能会被缓存,如果需要获取最新的 URL,可以禁用缓存。

八、总结的总结

get_rest_url() 函数虽然看起来简单,但实际上考虑了很多情况,保证生成的 REST API URL 是正确且可靠的。通过深入了解 get_rest_url() 函数的源码,我们可以更好地理解 WordPress REST API 的工作原理,并在实际开发中更加灵活地使用它。

希望今天的讲解对大家有所帮助!下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注