diff -r 7b1b88e27a20 -r 48c4eec2b7e6 wp/wp-includes/class-wp.php --- a/wp/wp-includes/class-wp.php Thu Sep 29 08:06:27 2022 +0200 +++ b/wp/wp-includes/class-wp.php Fri Sep 05 18:40:08 2025 +0200 @@ -5,6 +5,7 @@ * @package WordPress * @since 2.0.0 */ +#[AllowDynamicProperties] class WP { /** * Public query variables. @@ -199,17 +200,18 @@ $self = trim( $self, '/' ); } - // The requested permalink is in $pathinfo for path info requests and - // $req_uri for other requests. + // 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 ) { + if ( $req_uri === $wp_rewrite->index ) { $req_uri = ''; } + $requested_path = $req_uri; } + $requested_file = $req_uri; $this->request = $requested_path; @@ -226,23 +228,32 @@ } 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 ) && strpos( $match, $requested_file ) === 0 && $requested_file != $requested_path ) { + 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 ( 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 ) ) { + 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 ) { + && ! $post_status_obj->private && $post_status_obj->exclude_from_search + ) { continue; } } @@ -267,16 +278,18 @@ parse_str( $query, $perma_query_vars ); // If we're processing a 404 request, clear the error var since we found something. - if ( '404' == $error ) { + 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 || strpos( $_SERVER['PHP_SELF'], 'wp-admin/' ) !== false ) { + if ( empty( $requested_path ) || $requested_file === $self + || str_contains( $_SERVER['PHP_SELF'], 'wp-admin/' ) + ) { unset( $error, $_GET['error'] ); - if ( isset( $perma_query_vars ) && strpos( $_SERVER['PHP_SELF'], 'wp-admin/' ) !== false ) { + if ( isset( $perma_query_vars ) && str_contains( $_SERVER['PHP_SELF'], 'wp-admin/' ) ) { unset( $perma_query_vars ); } @@ -306,8 +319,14 @@ 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( $_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 ] ) ) { @@ -357,6 +376,7 @@ // 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'] ); @@ -407,9 +427,14 @@ * If showing a feed, it will also send Last-Modified, ETag, and 304 status if needed. * * @since 2.0.0 - * @since 4.4.0 `X-Pingback` header is added conditionally after posts have been queried in handle_404(). + * @since 4.4.0 `X-Pingback` header is added conditionally for single posts that allow pings. + * @since 6.1.0 Runs after posts have been queried. + * + * @global WP_Query $wp_query WordPress Query object. */ public function send_headers() { + global $wp_query; + $headers = array(); $status = null; $exit_required = false; @@ -429,10 +454,12 @@ } if ( ! empty( $this->query_vars['error'] ) ) { $status = (int) $this->query_vars['error']; + if ( 404 === $status ) { if ( ! is_user_logged_in() ) { $headers = array_merge( $headers, wp_get_nocache_headers() ); } + $headers['Content-Type'] = get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ); } elseif ( in_array( $status, array( 403, 500, 502, 503 ), true ) ) { $exit_required = true; @@ -445,11 +472,12 @@ if ( 'feed' === $this->query_vars['feed'] ) { $type = get_default_feed(); } + $headers['Content-Type'] = feed_content_type( $type ) . '; charset=' . get_option( 'blog_charset' ); // We're showing a feed, so WP is indeed the only thing that last changed. if ( ! empty( $this->query_vars['withcomments'] ) - || false !== strpos( $this->query_vars['feed'], 'comments-' ) + || str_contains( $this->query_vars['feed'], 'comments-' ) || ( empty( $this->query_vars['withoutcomments'] ) && ( ! empty( $this->query_vars['p'] ) || ! empty( $this->query_vars['name'] ) @@ -462,6 +490,7 @@ ) { $wp_last_modified_post = mysql2date( $date_format, get_lastpostmodified( 'GMT' ), false ); $wp_last_modified_comment = mysql2date( $date_format, get_lastcommentmodified( 'GMT' ), false ); + if ( strtotime( $wp_last_modified_post ) > strtotime( $wp_last_modified_comment ) ) { $wp_last_modified = $wp_last_modified_post; } else { @@ -476,8 +505,8 @@ } $wp_last_modified .= ' GMT'; + $wp_etag = '"' . md5( $wp_last_modified ) . '"'; - $wp_etag = '"' . md5( $wp_last_modified ) . '"'; $headers['Last-Modified'] = $wp_last_modified; $headers['ETag'] = $wp_etag; @@ -485,24 +514,39 @@ if ( isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ) { $client_etag = wp_unslash( $_SERVER['HTTP_IF_NONE_MATCH'] ); } else { - $client_etag = false; + $client_etag = ''; } - $client_last_modified = empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ? '' : trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); + if ( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { + $client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); + } else { + $client_last_modified = ''; + } + // If string is empty, return 0. If not, attempt to parse into a timestamp. $client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0; - // Make a timestamp for our most recent modification.. + // Make a timestamp for our most recent modification. $wp_modified_timestamp = strtotime( $wp_last_modified ); - if ( ( $client_last_modified && $client_etag ) ? - ( ( $client_modified_timestamp >= $wp_modified_timestamp ) && ( $client_etag == $wp_etag ) ) : - ( ( $client_modified_timestamp >= $wp_modified_timestamp ) || ( $client_etag == $wp_etag ) ) ) { + if ( ( $client_last_modified && $client_etag ) + ? ( ( $client_modified_timestamp >= $wp_modified_timestamp ) && ( $client_etag === $wp_etag ) ) + : ( ( $client_modified_timestamp >= $wp_modified_timestamp ) || ( $client_etag === $wp_etag ) ) + ) { $status = 304; $exit_required = true; } } + if ( is_singular() ) { + $post = isset( $wp_query->post ) ? $wp_query->post : null; + + // Only set X-Pingback for single posts that allow pings. + if ( $post && pings_open( $post ) ) { + $headers['X-Pingback'] = get_bloginfo( 'pingback_url', 'display' ); + } + } + /** * Filters the HTTP headers before they're sent to the browser. * @@ -556,12 +600,15 @@ */ public function build_query_string() { $this->query_string = ''; + foreach ( (array) array_keys( $this->query_vars ) as $wpvar ) { - if ( '' != $this->query_vars[ $wpvar ] ) { + if ( '' !== $this->query_vars[ $wpvar ] ) { $this->query_string .= ( strlen( $this->query_string ) < 1 ) ? '' : '&'; + if ( ! is_scalar( $this->query_vars[ $wpvar ] ) ) { // Discard non-scalars. continue; } + $this->query_string .= $wpvar . '=' . rawurlencode( $this->query_vars[ $wpvar ] ); } } @@ -581,6 +628,7 @@ '2.1.0', 'query_vars, request' ); + parse_str( $this->query_string, $this->query_vars ); } } @@ -700,17 +748,12 @@ if ( is_singular() ) { $post = isset( $wp_query->post ) ? $wp_query->post : null; - - // Only set X-Pingback for single posts that allow pings. - if ( $post && pings_open( $post ) && ! headers_sent() ) { - header( 'X-Pingback: ' . get_bloginfo( 'pingback_url', 'display' ) ); - } + $next = ''; // Check for paged content that exceeds the max number of pages. - $next = ''; if ( $post && ! empty( $this->query_vars['page'] ) ) { // Check if content is actually intended to be paged. - if ( false !== strpos( $post->post_content, $next ) ) { + if ( str_contains( $post->post_content, $next ) ) { $page = trim( $this->query_vars['page'], '/' ); $content_found = (int) $page <= ( substr_count( $post->post_content, $next ) + 1 ); } else { @@ -769,14 +812,14 @@ $parsed = $this->parse_request( $query_args ); - $this->send_headers(); - if ( $parsed ) { $this->query_posts(); $this->handle_404(); $this->register_globals(); } + $this->send_headers(); + /** * Fires once the WordPress environment has been set up. *