197 $pathinfo = trim( $pathinfo, '/' ); |
198 $pathinfo = trim( $pathinfo, '/' ); |
198 $self = preg_replace( $home_path_regex, '', $self ); |
199 $self = preg_replace( $home_path_regex, '', $self ); |
199 $self = trim( $self, '/' ); |
200 $self = trim( $self, '/' ); |
200 } |
201 } |
201 |
202 |
202 // The requested permalink is in $pathinfo for path info requests and |
203 // The requested permalink is in $pathinfo for path info requests and $req_uri for other requests. |
203 // $req_uri for other requests. |
|
204 if ( ! empty( $pathinfo ) && ! preg_match( '|^.*' . $wp_rewrite->index . '$|', $pathinfo ) ) { |
204 if ( ! empty( $pathinfo ) && ! preg_match( '|^.*' . $wp_rewrite->index . '$|', $pathinfo ) ) { |
205 $requested_path = $pathinfo; |
205 $requested_path = $pathinfo; |
206 } else { |
206 } else { |
207 // If the request uri is the index, blank it out so that we don't try to match it against a rule. |
207 // If the request uri is the index, blank it out so that we don't try to match it against a rule. |
208 if ( $req_uri == $wp_rewrite->index ) { |
208 if ( $req_uri === $wp_rewrite->index ) { |
209 $req_uri = ''; |
209 $req_uri = ''; |
210 } |
210 } |
|
211 |
211 $requested_path = $req_uri; |
212 $requested_path = $req_uri; |
212 } |
213 } |
|
214 |
213 $requested_file = $req_uri; |
215 $requested_file = $req_uri; |
214 |
216 |
215 $this->request = $requested_path; |
217 $this->request = $requested_path; |
216 |
218 |
217 // Look for matches. |
219 // Look for matches. |
224 $matches = array( '' ); |
226 $matches = array( '' ); |
225 } |
227 } |
226 } else { |
228 } else { |
227 foreach ( (array) $rewrite as $match => $query ) { |
229 foreach ( (array) $rewrite as $match => $query ) { |
228 // If the requested file is the anchor of the match, prepend it to the path info. |
230 // If the requested file is the anchor of the match, prepend it to the path info. |
229 if ( ! empty( $requested_file ) && strpos( $match, $requested_file ) === 0 && $requested_file != $requested_path ) { |
231 if ( ! empty( $requested_file ) |
|
232 && str_starts_with( $match, $requested_file ) |
|
233 && $requested_file !== $requested_path |
|
234 ) { |
230 $request_match = $requested_file . '/' . $requested_path; |
235 $request_match = $requested_file . '/' . $requested_path; |
231 } |
236 } |
232 |
237 |
233 if ( preg_match( "#^$match#", $request_match, $matches ) || |
238 if ( preg_match( "#^$match#", $request_match, $matches ) |
234 preg_match( "#^$match#", urldecode( $request_match ), $matches ) ) { |
239 || preg_match( "#^$match#", urldecode( $request_match ), $matches ) |
235 |
240 ) { |
236 if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) { |
241 |
|
242 if ( $wp_rewrite->use_verbose_page_rules |
|
243 && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) |
|
244 ) { |
237 // This is a verbose page match, let's check to be sure about it. |
245 // This is a verbose page match, let's check to be sure about it. |
238 $page = get_page_by_path( $matches[ $varmatch[1] ] ); |
246 $page = get_page_by_path( $matches[ $varmatch[1] ] ); |
|
247 |
239 if ( ! $page ) { |
248 if ( ! $page ) { |
240 continue; |
249 continue; |
241 } |
250 } |
242 |
251 |
243 $post_status_obj = get_post_status_object( $page->post_status ); |
252 $post_status_obj = get_post_status_object( $page->post_status ); |
|
253 |
244 if ( ! $post_status_obj->public && ! $post_status_obj->protected |
254 if ( ! $post_status_obj->public && ! $post_status_obj->protected |
245 && ! $post_status_obj->private && $post_status_obj->exclude_from_search ) { |
255 && ! $post_status_obj->private && $post_status_obj->exclude_from_search |
|
256 ) { |
246 continue; |
257 continue; |
247 } |
258 } |
248 } |
259 } |
249 |
260 |
250 // Got a match. |
261 // Got a match. |
265 |
276 |
266 // Parse the query. |
277 // Parse the query. |
267 parse_str( $query, $perma_query_vars ); |
278 parse_str( $query, $perma_query_vars ); |
268 |
279 |
269 // If we're processing a 404 request, clear the error var since we found something. |
280 // If we're processing a 404 request, clear the error var since we found something. |
270 if ( '404' == $error ) { |
281 if ( '404' === $error ) { |
271 unset( $error, $_GET['error'] ); |
282 unset( $error, $_GET['error'] ); |
272 } |
283 } |
273 } |
284 } |
274 |
285 |
275 // If req_uri is empty or if it is a request for ourself, unset error. |
286 // If req_uri is empty or if it is a request for ourself, unset error. |
276 if ( empty( $requested_path ) || $requested_file == $self || strpos( $_SERVER['PHP_SELF'], 'wp-admin/' ) !== false ) { |
287 if ( empty( $requested_path ) || $requested_file === $self |
|
288 || str_contains( $_SERVER['PHP_SELF'], 'wp-admin/' ) |
|
289 ) { |
277 unset( $error, $_GET['error'] ); |
290 unset( $error, $_GET['error'] ); |
278 |
291 |
279 if ( isset( $perma_query_vars ) && strpos( $_SERVER['PHP_SELF'], 'wp-admin/' ) !== false ) { |
292 if ( isset( $perma_query_vars ) && str_contains( $_SERVER['PHP_SELF'], 'wp-admin/' ) ) { |
280 unset( $perma_query_vars ); |
293 unset( $perma_query_vars ); |
281 } |
294 } |
282 |
295 |
283 $this->did_permalink = false; |
296 $this->did_permalink = false; |
284 } |
297 } |
304 } |
317 } |
305 |
318 |
306 foreach ( $this->public_query_vars as $wpvar ) { |
319 foreach ( $this->public_query_vars as $wpvar ) { |
307 if ( isset( $this->extra_query_vars[ $wpvar ] ) ) { |
320 if ( isset( $this->extra_query_vars[ $wpvar ] ) ) { |
308 $this->query_vars[ $wpvar ] = $this->extra_query_vars[ $wpvar ]; |
321 $this->query_vars[ $wpvar ] = $this->extra_query_vars[ $wpvar ]; |
309 } elseif ( isset( $_GET[ $wpvar ] ) && isset( $_POST[ $wpvar ] ) && $_GET[ $wpvar ] !== $_POST[ $wpvar ] ) { |
322 } elseif ( isset( $_GET[ $wpvar ] ) && isset( $_POST[ $wpvar ] ) |
310 wp_die( __( 'A variable mismatch has been detected.' ), __( 'Sorry, you are not allowed to view this item.' ), 400 ); |
323 && $_GET[ $wpvar ] !== $_POST[ $wpvar ] |
|
324 ) { |
|
325 wp_die( |
|
326 __( 'A variable mismatch has been detected.' ), |
|
327 __( 'Sorry, you are not allowed to view this item.' ), |
|
328 400 |
|
329 ); |
311 } elseif ( isset( $_POST[ $wpvar ] ) ) { |
330 } elseif ( isset( $_POST[ $wpvar ] ) ) { |
312 $this->query_vars[ $wpvar ] = $_POST[ $wpvar ]; |
331 $this->query_vars[ $wpvar ] = $_POST[ $wpvar ]; |
313 } elseif ( isset( $_GET[ $wpvar ] ) ) { |
332 } elseif ( isset( $_GET[ $wpvar ] ) ) { |
314 $this->query_vars[ $wpvar ] = $_GET[ $wpvar ]; |
333 $this->query_vars[ $wpvar ] = $_GET[ $wpvar ]; |
315 } elseif ( isset( $perma_query_vars[ $wpvar ] ) ) { |
334 } elseif ( isset( $perma_query_vars[ $wpvar ] ) ) { |
355 } |
374 } |
356 |
375 |
357 // Limit publicly queried post_types to those that are 'publicly_queryable'. |
376 // Limit publicly queried post_types to those that are 'publicly_queryable'. |
358 if ( isset( $this->query_vars['post_type'] ) ) { |
377 if ( isset( $this->query_vars['post_type'] ) ) { |
359 $queryable_post_types = get_post_types( array( 'publicly_queryable' => true ) ); |
378 $queryable_post_types = get_post_types( array( 'publicly_queryable' => true ) ); |
|
379 |
360 if ( ! is_array( $this->query_vars['post_type'] ) ) { |
380 if ( ! is_array( $this->query_vars['post_type'] ) ) { |
361 if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types, true ) ) { |
381 if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types, true ) ) { |
362 unset( $this->query_vars['post_type'] ); |
382 unset( $this->query_vars['post_type'] ); |
363 } |
383 } |
364 } else { |
384 } else { |
405 * |
425 * |
406 * Sets the Content-Type header. Sets the 'error' status (if passed) and optionally exits. |
426 * Sets the Content-Type header. Sets the 'error' status (if passed) and optionally exits. |
407 * If showing a feed, it will also send Last-Modified, ETag, and 304 status if needed. |
427 * If showing a feed, it will also send Last-Modified, ETag, and 304 status if needed. |
408 * |
428 * |
409 * @since 2.0.0 |
429 * @since 2.0.0 |
410 * @since 4.4.0 `X-Pingback` header is added conditionally after posts have been queried in handle_404(). |
430 * @since 4.4.0 `X-Pingback` header is added conditionally for single posts that allow pings. |
|
431 * @since 6.1.0 Runs after posts have been queried. |
|
432 * |
|
433 * @global WP_Query $wp_query WordPress Query object. |
411 */ |
434 */ |
412 public function send_headers() { |
435 public function send_headers() { |
|
436 global $wp_query; |
|
437 |
413 $headers = array(); |
438 $headers = array(); |
414 $status = null; |
439 $status = null; |
415 $exit_required = false; |
440 $exit_required = false; |
416 $date_format = 'D, d M Y H:i:s'; |
441 $date_format = 'D, d M Y H:i:s'; |
417 |
442 |
427 $expires |
452 $expires |
428 ); |
453 ); |
429 } |
454 } |
430 if ( ! empty( $this->query_vars['error'] ) ) { |
455 if ( ! empty( $this->query_vars['error'] ) ) { |
431 $status = (int) $this->query_vars['error']; |
456 $status = (int) $this->query_vars['error']; |
|
457 |
432 if ( 404 === $status ) { |
458 if ( 404 === $status ) { |
433 if ( ! is_user_logged_in() ) { |
459 if ( ! is_user_logged_in() ) { |
434 $headers = array_merge( $headers, wp_get_nocache_headers() ); |
460 $headers = array_merge( $headers, wp_get_nocache_headers() ); |
435 } |
461 } |
|
462 |
436 $headers['Content-Type'] = get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ); |
463 $headers['Content-Type'] = get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ); |
437 } elseif ( in_array( $status, array( 403, 500, 502, 503 ), true ) ) { |
464 } elseif ( in_array( $status, array( 403, 500, 502, 503 ), true ) ) { |
438 $exit_required = true; |
465 $exit_required = true; |
439 } |
466 } |
440 } elseif ( empty( $this->query_vars['feed'] ) ) { |
467 } elseif ( empty( $this->query_vars['feed'] ) ) { |
443 // Set the correct content type for feeds. |
470 // Set the correct content type for feeds. |
444 $type = $this->query_vars['feed']; |
471 $type = $this->query_vars['feed']; |
445 if ( 'feed' === $this->query_vars['feed'] ) { |
472 if ( 'feed' === $this->query_vars['feed'] ) { |
446 $type = get_default_feed(); |
473 $type = get_default_feed(); |
447 } |
474 } |
|
475 |
448 $headers['Content-Type'] = feed_content_type( $type ) . '; charset=' . get_option( 'blog_charset' ); |
476 $headers['Content-Type'] = feed_content_type( $type ) . '; charset=' . get_option( 'blog_charset' ); |
449 |
477 |
450 // We're showing a feed, so WP is indeed the only thing that last changed. |
478 // We're showing a feed, so WP is indeed the only thing that last changed. |
451 if ( ! empty( $this->query_vars['withcomments'] ) |
479 if ( ! empty( $this->query_vars['withcomments'] ) |
452 || false !== strpos( $this->query_vars['feed'], 'comments-' ) |
480 || str_contains( $this->query_vars['feed'], 'comments-' ) |
453 || ( empty( $this->query_vars['withoutcomments'] ) |
481 || ( empty( $this->query_vars['withoutcomments'] ) |
454 && ( ! empty( $this->query_vars['p'] ) |
482 && ( ! empty( $this->query_vars['p'] ) |
455 || ! empty( $this->query_vars['name'] ) |
483 || ! empty( $this->query_vars['name'] ) |
456 || ! empty( $this->query_vars['page_id'] ) |
484 || ! empty( $this->query_vars['page_id'] ) |
457 || ! empty( $this->query_vars['pagename'] ) |
485 || ! empty( $this->query_vars['pagename'] ) |
460 ) |
488 ) |
461 ) |
489 ) |
462 ) { |
490 ) { |
463 $wp_last_modified_post = mysql2date( $date_format, get_lastpostmodified( 'GMT' ), false ); |
491 $wp_last_modified_post = mysql2date( $date_format, get_lastpostmodified( 'GMT' ), false ); |
464 $wp_last_modified_comment = mysql2date( $date_format, get_lastcommentmodified( 'GMT' ), false ); |
492 $wp_last_modified_comment = mysql2date( $date_format, get_lastcommentmodified( 'GMT' ), false ); |
|
493 |
465 if ( strtotime( $wp_last_modified_post ) > strtotime( $wp_last_modified_comment ) ) { |
494 if ( strtotime( $wp_last_modified_post ) > strtotime( $wp_last_modified_comment ) ) { |
466 $wp_last_modified = $wp_last_modified_post; |
495 $wp_last_modified = $wp_last_modified_post; |
467 } else { |
496 } else { |
468 $wp_last_modified = $wp_last_modified_comment; |
497 $wp_last_modified = $wp_last_modified_comment; |
469 } |
498 } |
474 if ( ! $wp_last_modified ) { |
503 if ( ! $wp_last_modified ) { |
475 $wp_last_modified = gmdate( $date_format ); |
504 $wp_last_modified = gmdate( $date_format ); |
476 } |
505 } |
477 |
506 |
478 $wp_last_modified .= ' GMT'; |
507 $wp_last_modified .= ' GMT'; |
479 |
508 $wp_etag = '"' . md5( $wp_last_modified ) . '"'; |
480 $wp_etag = '"' . md5( $wp_last_modified ) . '"'; |
509 |
481 $headers['Last-Modified'] = $wp_last_modified; |
510 $headers['Last-Modified'] = $wp_last_modified; |
482 $headers['ETag'] = $wp_etag; |
511 $headers['ETag'] = $wp_etag; |
483 |
512 |
484 // Support for conditional GET. |
513 // Support for conditional GET. |
485 if ( isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ) { |
514 if ( isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ) { |
486 $client_etag = wp_unslash( $_SERVER['HTTP_IF_NONE_MATCH'] ); |
515 $client_etag = wp_unslash( $_SERVER['HTTP_IF_NONE_MATCH'] ); |
487 } else { |
516 } else { |
488 $client_etag = false; |
517 $client_etag = ''; |
489 } |
518 } |
490 |
519 |
491 $client_last_modified = empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ? '' : trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); |
520 if ( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { |
|
521 $client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); |
|
522 } else { |
|
523 $client_last_modified = ''; |
|
524 } |
|
525 |
492 // If string is empty, return 0. If not, attempt to parse into a timestamp. |
526 // If string is empty, return 0. If not, attempt to parse into a timestamp. |
493 $client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0; |
527 $client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0; |
494 |
528 |
495 // Make a timestamp for our most recent modification.. |
529 // Make a timestamp for our most recent modification. |
496 $wp_modified_timestamp = strtotime( $wp_last_modified ); |
530 $wp_modified_timestamp = strtotime( $wp_last_modified ); |
497 |
531 |
498 if ( ( $client_last_modified && $client_etag ) ? |
532 if ( ( $client_last_modified && $client_etag ) |
499 ( ( $client_modified_timestamp >= $wp_modified_timestamp ) && ( $client_etag == $wp_etag ) ) : |
533 ? ( ( $client_modified_timestamp >= $wp_modified_timestamp ) && ( $client_etag === $wp_etag ) ) |
500 ( ( $client_modified_timestamp >= $wp_modified_timestamp ) || ( $client_etag == $wp_etag ) ) ) { |
534 : ( ( $client_modified_timestamp >= $wp_modified_timestamp ) || ( $client_etag === $wp_etag ) ) |
|
535 ) { |
501 $status = 304; |
536 $status = 304; |
502 $exit_required = true; |
537 $exit_required = true; |
|
538 } |
|
539 } |
|
540 |
|
541 if ( is_singular() ) { |
|
542 $post = isset( $wp_query->post ) ? $wp_query->post : null; |
|
543 |
|
544 // Only set X-Pingback for single posts that allow pings. |
|
545 if ( $post && pings_open( $post ) ) { |
|
546 $headers['X-Pingback'] = get_bloginfo( 'pingback_url', 'display' ); |
503 } |
547 } |
504 } |
548 } |
505 |
549 |
506 /** |
550 /** |
507 * Filters the HTTP headers before they're sent to the browser. |
551 * Filters the HTTP headers before they're sent to the browser. |
554 * |
598 * |
555 * @since 2.0.0 |
599 * @since 2.0.0 |
556 */ |
600 */ |
557 public function build_query_string() { |
601 public function build_query_string() { |
558 $this->query_string = ''; |
602 $this->query_string = ''; |
|
603 |
559 foreach ( (array) array_keys( $this->query_vars ) as $wpvar ) { |
604 foreach ( (array) array_keys( $this->query_vars ) as $wpvar ) { |
560 if ( '' != $this->query_vars[ $wpvar ] ) { |
605 if ( '' !== $this->query_vars[ $wpvar ] ) { |
561 $this->query_string .= ( strlen( $this->query_string ) < 1 ) ? '' : '&'; |
606 $this->query_string .= ( strlen( $this->query_string ) < 1 ) ? '' : '&'; |
|
607 |
562 if ( ! is_scalar( $this->query_vars[ $wpvar ] ) ) { // Discard non-scalars. |
608 if ( ! is_scalar( $this->query_vars[ $wpvar ] ) ) { // Discard non-scalars. |
563 continue; |
609 continue; |
564 } |
610 } |
|
611 |
565 $this->query_string .= $wpvar . '=' . rawurlencode( $this->query_vars[ $wpvar ] ); |
612 $this->query_string .= $wpvar . '=' . rawurlencode( $this->query_vars[ $wpvar ] ); |
566 } |
613 } |
567 } |
614 } |
568 |
615 |
569 if ( has_filter( 'query_string' ) ) { // Don't bother filtering and parsing if no plugins are hooked in. |
616 if ( has_filter( 'query_string' ) ) { // Don't bother filtering and parsing if no plugins are hooked in. |
698 } elseif ( $wp_query->posts ) { |
746 } elseif ( $wp_query->posts ) { |
699 $content_found = true; |
747 $content_found = true; |
700 |
748 |
701 if ( is_singular() ) { |
749 if ( is_singular() ) { |
702 $post = isset( $wp_query->post ) ? $wp_query->post : null; |
750 $post = isset( $wp_query->post ) ? $wp_query->post : null; |
703 |
751 $next = '<!--nextpage-->'; |
704 // Only set X-Pingback for single posts that allow pings. |
|
705 if ( $post && pings_open( $post ) && ! headers_sent() ) { |
|
706 header( 'X-Pingback: ' . get_bloginfo( 'pingback_url', 'display' ) ); |
|
707 } |
|
708 |
752 |
709 // Check for paged content that exceeds the max number of pages. |
753 // Check for paged content that exceeds the max number of pages. |
710 $next = '<!--nextpage-->'; |
|
711 if ( $post && ! empty( $this->query_vars['page'] ) ) { |
754 if ( $post && ! empty( $this->query_vars['page'] ) ) { |
712 // Check if content is actually intended to be paged. |
755 // Check if content is actually intended to be paged. |
713 if ( false !== strpos( $post->post_content, $next ) ) { |
756 if ( str_contains( $post->post_content, $next ) ) { |
714 $page = trim( $this->query_vars['page'], '/' ); |
757 $page = trim( $this->query_vars['page'], '/' ); |
715 $content_found = (int) $page <= ( substr_count( $post->post_content, $next ) + 1 ); |
758 $content_found = (int) $page <= ( substr_count( $post->post_content, $next ) + 1 ); |
716 } else { |
759 } else { |
717 $content_found = false; |
760 $content_found = false; |
718 } |
761 } |
767 public function main( $query_args = '' ) { |
810 public function main( $query_args = '' ) { |
768 $this->init(); |
811 $this->init(); |
769 |
812 |
770 $parsed = $this->parse_request( $query_args ); |
813 $parsed = $this->parse_request( $query_args ); |
771 |
814 |
772 $this->send_headers(); |
|
773 |
|
774 if ( $parsed ) { |
815 if ( $parsed ) { |
775 $this->query_posts(); |
816 $this->query_posts(); |
776 $this->handle_404(); |
817 $this->handle_404(); |
777 $this->register_globals(); |
818 $this->register_globals(); |
778 } |
819 } |
779 |
820 |
|
821 $this->send_headers(); |
|
822 |
780 /** |
823 /** |
781 * Fires once the WordPress environment has been set up. |
824 * Fires once the WordPress environment has been set up. |
782 * |
825 * |
783 * @since 2.1.0 |
826 * @since 2.1.0 |
784 * |
827 * |