2024-05-01 作者 C3P00 /** * Parses the request to find the correct WordPress query. * * Sets up the query variables based on the request. There are also many * filters and actions that can be used to further manipulate the result. * * @since 2.0.0 * @since 6.0.0 A return value was added. * * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * * @param array|string $extra_query_vars Set the extra query variables. * @return bool Whether the request was parsed. */ public function parse_request( $extra_query_vars = '' ) { global $wp_rewrite; /** * Filters whether to parse the request. * * @since 3.5.0 * * @param bool $bool Whether or not to parse the request. Default true. * @param WP $wp Current WordPress environment instance. * @param array|string $extra_query_vars Extra passed query variables. */ if ( ! apply_filters( 'do_parse_request', true, $this, $extra_query_vars ) ) { return false; } $this->query_vars = array(); $post_type_query_vars = array(); if ( is_array( $extra_query_vars ) ) { $this->extra_query_vars = & $extra_query_vars; } elseif ( ! empty( $extra_query_vars ) ) { parse_str( $extra_query_vars, $this->extra_query_vars ); } // Process PATH_INFO, REQUEST_URI, and 404 for permalinks. // Fetch the rewrite rules. $rewrite = $wp_rewrite->wp_rewrite_rules(); if ( ! empty( $rewrite ) ) { // If we match a rewrite rule, this will be cleared. $error = '404'; $this->did_permalink = true; $pathinfo = isset( $_SERVER['PATH_INFO'] ) ? $_SERVER['PATH_INFO'] : ''; list( $pathinfo ) = explode( '?', $pathinfo ); $pathinfo = str_replace( '%', '%25', $pathinfo ); list( $req_uri ) = explode( '?', $_SERVER['REQUEST_URI'] ); $self = $_SERVER['PHP_SELF']; $home_path = parse_url( home_url(), PHP_URL_PATH ); $home_path_regex = ''; if ( is_string( $home_path ) && '' !== $home_path ) { $home_path = trim( $home_path, '/' ); $home_path_regex = sprintf( '|^%s|i', preg_quote( $home_path, '|' ) ); } /* * Trim path info from the end and the leading home path from the front. * For path info requests, this leaves us with the requesting filename, if any. * For 404 requests, this leaves us with the requested permalink. */ $req_uri = str_replace( $pathinfo, '', $req_uri ); $req_uri = trim( $req_uri, '/' ); $pathinfo = trim( $pathinfo, '/' ); $self = trim( $self, '/' ); if ( ! empty( $home_path_regex ) ) { $req_uri = preg_replace( $home_path_regex, '', $req_uri ); $req_uri = trim( $req_uri, '/' ); $pathinfo = preg_replace( $home_path_regex, '', $pathinfo ); $pathinfo = trim( $pathinfo, '/' ); $self = preg_replace( $home_path_regex, '', $self ); $self = trim( $self, '/' ); } // The requested permalink is in $pathinfo for path info requests and $req_uri for other requests. if ( ! empty( $pathinfo ) && ! preg_match( '|^.*' . $wp_rewrite->index . '$|', $pathinfo ) ) { $requested_path = $pathinfo; } else { // If the request uri is the index, blank it out so that we don't try to match it against a rule. if ( $req_uri === $wp_rewrite->index ) { $req_uri = ''; } $requested_path = $req_uri; } $requested_file = $req_uri; $this->request = $requested_path; // Look for matches. $request_match = $requested_path; if ( empty( $request_match ) ) { // An empty request could only match against ^$ regex. if ( isset( $rewrite['$'] ) ) { $this->matched_rule = '$'; $query = $rewrite['$']; $matches = array( '' ); } } else { foreach ( (array) $rewrite as $match => $query ) { // If the requested file is the anchor of the match, prepend it to the path info. if ( ! empty( $requested_file ) && str_starts_with( $match, $requested_file ) && $requested_file !== $requested_path ) { $request_match = $requested_file . '/' . $requested_path; } if ( preg_match( "#^$match#", $request_match, $matches ) || preg_match( "#^$match#", urldecode( $request_match ), $matches ) ) { if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) { // This is a verbose page match, let's check to be sure about it. $page = get_page_by_path( $matches[ $varmatch[1] ] ); if ( ! $page ) { continue; } $post_status_obj = get_post_status_object( $page->post_status ); if ( ! $post_status_obj->public && ! $post_status_obj->protected && ! $post_status_obj->private && $post_status_obj->exclude_from_search ) { continue; } } // Got a match. $this->matched_rule = $match; break; } } } if ( ! empty( $this->matched_rule ) ) { // Trim the query of everything up to the '?'. $query = preg_replace( '!^.+\?!', '', $query ); // Substitute the substring matches into the query. $query = addslashes( WP_MatchesMapRegex::apply( $query, $matches ) ); $this->matched_query = $query; // Parse the query. parse_str( $query, $perma_query_vars ); // If we're processing a 404 request, clear the error var since we found something. if ( '404' === $error ) { unset( $error, $_GET['error'] ); } } // If req_uri is empty or if it is a request for ourself, unset error. if ( empty( $requested_path ) || $requested_file === $self || str_contains( $_SERVER['PHP_SELF'], 'wp-admin/' ) ) { unset( $error, $_GET['error'] ); if ( isset( $perma_query_vars ) && str_contains( $_SERVER['PHP_SELF'], 'wp-admin/' ) ) { unset( $perma_query_vars ); } $this->did_permalink = false; } } /** * Filters the query variables allowed before processing. * * Allows (publicly allowed) query vars to be added, removed, or changed prior * to executing the query. Needed to allow custom rewrite rules using your own arguments * to work, or any other custom query variables you want to be publicly available. * * @since 1.5.0 * * @param string[] $public_query_vars The array of allowed query variable names. */ $this->public_query_vars = apply_filters( 'query_vars', $this->public_query_vars ); foreach ( get_post_types( array(), 'objects' ) as $post_type => $t ) { if ( is_post_type_viewable( $t ) && $t->query_var ) { $post_type_query_vars[ $t->query_var ] = $post_type; } } foreach ( $this->public_query_vars as $wpvar ) { if ( isset( $this->extra_query_vars[ $wpvar ] ) ) { $this->query_vars[ $wpvar ] = $this->extra_query_vars[ $wpvar ]; } elseif ( isset( $_GET[ $wpvar ] ) && isset( $_POST[ $wpvar ] ) && $_GET[ $wpvar ] !== $_POST[ $wpvar ] ) { wp_die( __( 'A variable mismatch has been detected.' ), __( 'Sorry, you are not allowed to view this item.' ), 400 ); } elseif ( isset( $_POST[ $wpvar ] ) ) { $this->query_vars[ $wpvar ] = $_POST[ $wpvar ]; } elseif ( isset( $_GET[ $wpvar ] ) ) { $this->query_vars[ $wpvar ] = $_GET[ $wpvar ]; } elseif ( isset( $perma_query_vars[ $wpvar ] ) ) { $this->query_vars[ $wpvar ] = $perma_query_vars[ $wpvar ]; } if ( ! empty( $this->query_vars[ $wpvar ] ) ) { if ( ! is_array( $this->query_vars[ $wpvar ] ) ) { $this->query_vars[ $wpvar ] = (string) $this->query_vars[ $wpvar ]; } else { foreach ( $this->query_vars[ $wpvar ] as $vkey => $v ) { if ( is_scalar( $v ) ) { $this->query_vars[ $wpvar ][ $vkey ] = (string) $v; } } } if ( isset( $post_type_query_vars[ $wpvar ] ) ) { $this->query_vars['post_type'] = $post_type_query_vars[ $wpvar ]; $this->query_vars['name'] = $this->query_vars[ $wpvar ]; } } } // Convert urldecoded spaces back into '+'. foreach ( get_taxonomies( array(), 'objects' ) as $taxonomy => $t ) { if ( $t->query_var && isset( $this->query_vars[ $t->query_var ] ) ) { $this->query_vars[ $t->query_var ] = str_replace( ' ', '+', $this->query_vars[ $t->query_var ] ); } } // Don't allow non-publicly queryable taxonomies to be queried from the front end. if ( ! is_admin() ) { foreach ( get_taxonomies( array( 'publicly_queryable' => false ), 'objects' ) as $taxonomy => $t ) { /* * Disallow when set to the 'taxonomy' query var. * Non-publicly queryable taxonomies cannot register custom query vars. See register_taxonomy(). */ if ( isset( $this->query_vars['taxonomy'] ) && $taxonomy === $this->query_vars['taxonomy'] ) { unset( $this->query_vars['taxonomy'], $this->query_vars['term'] ); } } } // Limit publicly queried post_types to those that are 'publicly_queryable'. if ( isset( $this->query_vars['post_type'] ) ) { $queryable_post_types = get_post_types( array( 'publicly_queryable' => true ) ); if ( ! is_array( $this->query_vars['post_type'] ) ) { if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types, true ) ) { unset( $this->query_vars['post_type'] ); } } else { $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types ); } } // Resolve conflicts between posts with numeric slugs and date archive queries. $this->query_vars = wp_resolve_numeric_slug_conflicts( $this->query_vars ); foreach ( (array) $this->private_query_vars as $var ) { if ( isset( $this->extra_query_vars[ $var ] ) ) { $this->query_vars[ $var ] = $this->extra_query_vars[ $var ]; } } if ( isset( $error ) ) { $this->query_vars['error'] = $error; } /** * Filters the array of parsed query variables. * * @since 2.1.0 * * @param array $query_vars The array of requested query variables. */ $this->query_vars = apply_filters( 'request', $this->query_vars ); /** * Fires once all query variables for the current request have been parsed. * * @since 2.1.0 * * @param WP $wp Current WordPress environment instance (passed by reference). */ do_action_ref_array( 'parse_request', array( &$this ) ); return true; } 上述代码是WordPress核心类WP中parse_request方法的详细实现。这个方法负责解析传入的URL或请求,以确定应执行哪个WordPress查询。以下是对此方法的作用和工作方式的解释,分解为其关键组件: parse_request的目的 parse_request方法旨在根据传入请求设置查询变量。它确定请求应如何处理——无论是渲染文章、页面、类别列表还是处理404错误。 关键步骤和操作 过滤提前退出:方法首先检查是否应继续解析请求,使用do_parse_request过滤器。插件可以使用此过滤器来跳过解析过程。 初始化: 清除任何之前的查询变量。 初始化本地变量以处理URL的不同组件。 处理额外的查询变量:如果提供了任何额外的查询变量,它们将被整合到查询处理中。 应用重写规则: 从$wp_rewrite检索WordPress重写规则。 处理请求URI和路径信息,去除家庭URL路径,留下将与重写规则匹配的相对路径或查询字符串。 与重写规则匹配: 迭代重写规则以找到与处理后的请求路径的匹配项。 如果找到匹配,相应的WordPress查询变量将根据重写规则设置。 文章类型和分类法处理: 将与文章类型或分类法相关的任何查询变量映射到其相应的查询。 确保只能查询公开可查询的文章类型和分类法。 安全和清洁: 处理GET和POST请求之间潜在的变量冲突。 过滤查询变量以确保最终查询中只包含允许的键。 最终查询变量准备: 应用过滤器以修改或验证查询变量列表。 解决URL中的数字slug冲突。 动作钩子: 执行操作,允许插件在解析查询变量后执行操作。 结论 parse_request函数是WordPress解释和响应URL的能力的核心部分。它使用重写规则将URL映射到查询变量,处理来自GET和POST请求的传入变量,并确保只执行允许的查询。这个方法对于确保WordPress可以为不同的URL提供正确的内容至关重要,无论这些URL指向文章、页面、存档还是自定义文章类型和分类法。 开发中的使用 开发人员可以使用提供的钩子(do_parse_request、query_vars、request和parse_request)来扩展或修改这种行为,以定制WordPress对不同请求的响应,这对于需要专门路由的自定义插件或主题特别有用。
上述代码是WordPress核心类
WP
中parse_request
方法的详细实现。这个方法负责解析传入的URL或请求,以确定应执行哪个WordPress查询。以下是对此方法的作用和工作方式的解释,分解为其关键组件:parse_request
的目的parse_request
方法旨在根据传入请求设置查询变量。它确定请求应如何处理——无论是渲染文章、页面、类别列表还是处理404错误。关键步骤和操作
do_parse_request
过滤器。插件可以使用此过滤器来跳过解析过程。$wp_rewrite
检索WordPress重写规则。结论
parse_request
函数是WordPress解释和响应URL的能力的核心部分。它使用重写规则将URL映射到查询变量,处理来自GET和POST请求的传入变量,并确保只执行允许的查询。这个方法对于确保WordPress可以为不同的URL提供正确的内容至关重要,无论这些URL指向文章、页面、存档还是自定义文章类型和分类法。开发中的使用
开发人员可以使用提供的钩子(
do_parse_request
、query_vars
、request
和parse_request
)来扩展或修改这种行为,以定制WordPress对不同请求的响应,这对于需要专门路由的自定义插件或主题特别有用。