WP

WordPress environment setup class.

Defined (1)

The class is defined in the following location(s).

/wp-includes/class-wp.php  
  1. class WP { 
  2. /** 
  3. * Public query variables. 
  4. * Long list of public query variables. 
  5. * @since 2.0.0 
  6. * @access public 
  7. * @var array 
  8. */ 
  9. public $public_query_vars = array('m', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'static', 'pagename', 'page_id', 'error', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term', 'cpage', 'post_type', 'embed' ); 
  10.  
  11. /** 
  12. * Private query variables. 
  13. * Long list of private query variables. 
  14. * @since 2.0.0 
  15. * @access public 
  16. * @var array 
  17. */ 
  18. public $private_query_vars = array( 'offset', 'posts_per_page', 'posts_per_archive_page', 'showposts', 'nopaging', 'post_type', 'post_status', 'category__in', 'category__not_in', 'category__and', 'tag__in', 'tag__not_in', 'tag__and', 'tag_slug__in', 'tag_slug__and', 'tag_id', 'post_mime_type', 'perm', 'comments_per_page', 'post__in', 'post__not_in', 'post_parent', 'post_parent__in', 'post_parent__not_in', 'title' ); 
  19.  
  20. /** 
  21. * Extra query variables set by the user. 
  22. * @since 2.1.0 
  23. * @access public 
  24. * @var array 
  25. */ 
  26. public $extra_query_vars = array(); 
  27.  
  28. /** 
  29. * Query variables for setting up the WordPress Query Loop. 
  30. * @since 2.0.0 
  31. * @access public 
  32. * @var array 
  33. */ 
  34. public $query_vars; 
  35.  
  36. /** 
  37. * String parsed to set the query variables. 
  38. * @since 2.0.0 
  39. * @access public 
  40. * @var string 
  41. */ 
  42. public $query_string; 
  43.  
  44. /** 
  45. * The request path, e.g. 2015/05/06. 
  46. * @since 2.0.0 
  47. * @access public 
  48. * @var string 
  49. */ 
  50. public $request; 
  51.  
  52. /** 
  53. * Rewrite rule the request matched. 
  54. * @since 2.0.0 
  55. * @access public 
  56. * @var string 
  57. */ 
  58. public $matched_rule; 
  59.  
  60. /** 
  61. * Rewrite query the request matched. 
  62. * @since 2.0.0 
  63. * @access public 
  64. * @var string 
  65. */ 
  66. public $matched_query; 
  67.  
  68. /** 
  69. * Whether already did the permalink. 
  70. * @since 2.0.0 
  71. * @access public 
  72. * @var bool 
  73. */ 
  74. public $did_permalink = false; 
  75.  
  76. /** 
  77. * Add name to list of public query variables. 
  78. * @since 2.1.0 
  79. * @access public 
  80. * @param string $qv Query variable name. 
  81. */ 
  82. public function add_query_var($qv) { 
  83. if ( !in_array($qv, $this->public_query_vars) ) 
  84. $this->public_query_vars[] = $qv; 
  85.  
  86. /** 
  87. * Removes a query variable from a list of public query variables. 
  88. * @since 4.5.0 
  89. * @access public 
  90. * @param string $name Query variable name. 
  91. */ 
  92. public function remove_query_var( $name ) { 
  93. $this->public_query_vars = array_diff( $this->public_query_vars, array( $name ) ); 
  94.  
  95. /** 
  96. * Set the value of a query variable. 
  97. * @since 2.3.0 
  98. * @access public 
  99. * @param string $key Query variable name. 
  100. * @param mixed $value Query variable value. 
  101. */ 
  102. public function set_query_var($key, $value) { 
  103. $this->query_vars[$key] = $value; 
  104.  
  105. /** 
  106. * Parse request to find correct WordPress query. 
  107. * Sets up the query variables based on the request. There are also many 
  108. * filters and actions that can be used to further manipulate the result. 
  109. * @since 2.0.0 
  110. * @access public 
  111. * @global WP_Rewrite $wp_rewrite 
  112. * @param array|string $extra_query_vars Set the extra query variables. 
  113. */ 
  114. public function parse_request($extra_query_vars = '') { 
  115. global $wp_rewrite; 
  116.  
  117. /** 
  118. * Filters whether to parse the request. 
  119. * @since 3.5.0 
  120. * @param bool $bool Whether or not to parse the request. Default true. 
  121. * @param WP $this Current WordPress environment instance. 
  122. * @param array|string $extra_query_vars Extra passed query variables. 
  123. */ 
  124. if ( ! apply_filters( 'do_parse_request', true, $this, $extra_query_vars ) ) 
  125. return; 
  126.  
  127. $this->query_vars = array(); 
  128. $post_type_query_vars = array(); 
  129.  
  130. if ( is_array( $extra_query_vars ) ) { 
  131. $this->extra_query_vars = & $extra_query_vars; 
  132. } elseif ( ! empty( $extra_query_vars ) ) { 
  133. parse_str( $extra_query_vars, $this->extra_query_vars ); 
  134. // Process PATH_INFO, REQUEST_URI, and 404 for permalinks. 
  135.  
  136. // Fetch the rewrite rules. 
  137. $rewrite = $wp_rewrite->wp_rewrite_rules(); 
  138.  
  139. if ( ! empty($rewrite) ) { 
  140. // If we match a rewrite rule, this will be cleared. 
  141. $error = '404'; 
  142. $this->did_permalink = true; 
  143.  
  144. $pathinfo = isset( $_SERVER['PATH_INFO'] ) ? $_SERVER['PATH_INFO'] : ''; 
  145. list( $pathinfo ) = explode( '?', $pathinfo ); 
  146. $pathinfo = str_replace( "%", "%25", $pathinfo ); 
  147.  
  148. list( $req_uri ) = explode( '?', $_SERVER['REQUEST_URI'] ); 
  149. $self = $_SERVER['PHP_SELF']; 
  150. $home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' ); 
  151. $home_path_regex = sprintf( '|^%s|i', preg_quote( $home_path, '|' ) ); 
  152.  
  153. // Trim path info from the end and the leading home path from the 
  154. // front. For path info requests, this leaves us with the requesting 
  155. // filename, if any. For 404 requests, this leaves us with the 
  156. // requested permalink. 
  157. $req_uri = str_replace($pathinfo, '', $req_uri); 
  158. $req_uri = trim($req_uri, '/'); 
  159. $req_uri = preg_replace( $home_path_regex, '', $req_uri ); 
  160. $req_uri = trim($req_uri, '/'); 
  161. $pathinfo = trim($pathinfo, '/'); 
  162. $pathinfo = preg_replace( $home_path_regex, '', $pathinfo ); 
  163. $pathinfo = trim($pathinfo, '/'); 
  164. $self = trim($self, '/'); 
  165. $self = preg_replace( $home_path_regex, '', $self ); 
  166. $self = trim($self, '/'); 
  167.  
  168. // The requested permalink is in $pathinfo for path info requests and 
  169. // $req_uri for other requests. 
  170. if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) { 
  171. $requested_path = $pathinfo; 
  172. } else { 
  173. // If the request uri is the index, blank it out so that we don't try to match it against a rule. 
  174. if ( $req_uri == $wp_rewrite->index ) 
  175. $req_uri = ''; 
  176. $requested_path = $req_uri; 
  177. $requested_file = $req_uri; 
  178.  
  179. $this->request = $requested_path; 
  180.  
  181. // Look for matches. 
  182. $request_match = $requested_path; 
  183. if ( empty( $request_match ) ) { 
  184. // An empty request could only match against ^$ regex 
  185. if ( isset( $rewrite['$'] ) ) { 
  186. $this->matched_rule = '$'; 
  187. $query = $rewrite['$']; 
  188. $matches = array(''); 
  189. } else { 
  190. foreach ( (array) $rewrite as $match => $query ) { 
  191. // If the requested file is the anchor of the match, prepend it to the path info. 
  192. if ( ! empty($requested_file) && strpos($match, $requested_file) === 0 && $requested_file != $requested_path ) 
  193. $request_match = $requested_file . '/' . $requested_path; 
  194.  
  195. if ( preg_match("#^$match#", $request_match, $matches) || 
  196. preg_match("#^$match#", urldecode($request_match), $matches) ) { 
  197.  
  198. if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) { 
  199. // This is a verbose page match, let's check to be sure about it. 
  200. $page = get_page_by_path( $matches[ $varmatch[1] ] ); 
  201. if ( ! $page ) { 
  202. continue; 
  203.  
  204. $post_status_obj = get_post_status_object( $page->post_status ); 
  205. if ( ! $post_status_obj->public && ! $post_status_obj->protected 
  206. && ! $post_status_obj->private && $post_status_obj->exclude_from_search ) { 
  207. continue; 
  208.  
  209. // Got a match. 
  210. $this->matched_rule = $match; 
  211. break; 
  212.  
  213. if ( isset( $this->matched_rule ) ) { 
  214. // Trim the query of everything up to the '?'. 
  215. $query = preg_replace("!^.+\?!", '', $query); 
  216.  
  217. // Substitute the substring matches into the query. 
  218. $query = addslashes(WP_MatchesMapRegex::apply($query, $matches)); 
  219.  
  220. $this->matched_query = $query; 
  221.  
  222. // Parse the query. 
  223. parse_str($query, $perma_query_vars); 
  224.  
  225. // If we're processing a 404 request, clear the error var since we found something. 
  226. if ( '404' == $error ) 
  227. unset( $error, $_GET['error'] ); 
  228.  
  229. // If req_uri is empty or if it is a request for ourself, unset error. 
  230. if ( empty($requested_path) || $requested_file == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) { 
  231. unset( $error, $_GET['error'] ); 
  232.  
  233. if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) 
  234. unset( $perma_query_vars ); 
  235.  
  236. $this->did_permalink = false; 
  237.  
  238. /** 
  239. * Filters the query variables whitelist before processing. 
  240. * Allows (publicly allowed) query vars to be added, removed, or changed prior 
  241. * to executing the query. Needed to allow custom rewrite rules using your own arguments 
  242. * to work, or any other custom query variables you want to be publicly available. 
  243. * @since 1.5.0 
  244. * @param array $public_query_vars The array of whitelisted query variables. 
  245. */ 
  246. $this->public_query_vars = apply_filters( 'query_vars', $this->public_query_vars ); 
  247.  
  248. foreach ( get_post_types( array(), 'objects' ) as $post_type => $t ) { 
  249. if ( is_post_type_viewable( $t ) && $t->query_var ) { 
  250. $post_type_query_vars[$t->query_var] = $post_type; 
  251.  
  252. foreach ( $this->public_query_vars as $wpvar ) { 
  253. if ( isset( $this->extra_query_vars[$wpvar] ) ) 
  254. $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar]; 
  255. elseif ( isset( $_POST[$wpvar] ) ) 
  256. $this->query_vars[$wpvar] = $_POST[$wpvar]; 
  257. elseif ( isset( $_GET[$wpvar] ) ) 
  258. $this->query_vars[$wpvar] = $_GET[$wpvar]; 
  259. elseif ( isset( $perma_query_vars[$wpvar] ) ) 
  260. $this->query_vars[$wpvar] = $perma_query_vars[$wpvar]; 
  261.  
  262. if ( !empty( $this->query_vars[$wpvar] ) ) { 
  263. if ( ! is_array( $this->query_vars[$wpvar] ) ) { 
  264. $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar]; 
  265. } else { 
  266. foreach ( $this->query_vars[$wpvar] as $vkey => $v ) { 
  267. if ( !is_object( $v ) ) { 
  268. $this->query_vars[$wpvar][$vkey] = (string) $v; 
  269.  
  270. if ( isset($post_type_query_vars[$wpvar] ) ) { 
  271. $this->query_vars['post_type'] = $post_type_query_vars[$wpvar]; 
  272. $this->query_vars['name'] = $this->query_vars[$wpvar]; 
  273.  
  274. // Convert urldecoded spaces back into + 
  275. foreach ( get_taxonomies( array() , 'objects' ) as $taxonomy => $t ) 
  276. if ( $t->query_var && isset( $this->query_vars[$t->query_var] ) ) 
  277. $this->query_vars[$t->query_var] = str_replace( ' ', '+', $this->query_vars[$t->query_var] ); 
  278.  
  279. // Don't allow non-publicly queryable taxonomies to be queried from the front end. 
  280. if ( ! is_admin() ) { 
  281. foreach ( get_taxonomies( array( 'publicly_queryable' => false ), 'objects' ) as $taxonomy => $t ) { 
  282. /** 
  283. * Disallow when set to the 'taxonomy' query var. 
  284. * Non-publicly queryable taxonomies cannot register custom query vars. See register_taxonomy(). 
  285. */ 
  286. if ( isset( $this->query_vars['taxonomy'] ) && $taxonomy === $this->query_vars['taxonomy'] ) { 
  287. unset( $this->query_vars['taxonomy'], $this->query_vars['term'] ); 
  288.  
  289. // Limit publicly queried post_types to those that are publicly_queryable 
  290. if ( isset( $this->query_vars['post_type']) ) { 
  291. $queryable_post_types = get_post_types( array('publicly_queryable' => true) ); 
  292. if ( ! is_array( $this->query_vars['post_type'] ) ) { 
  293. if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) ) 
  294. unset( $this->query_vars['post_type'] ); 
  295. } else { 
  296. $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types ); 
  297.  
  298. // Resolve conflicts between posts with numeric slugs and date archive queries. 
  299. $this->query_vars = wp_resolve_numeric_slug_conflicts( $this->query_vars ); 
  300.  
  301. foreach ( (array) $this->private_query_vars as $var) { 
  302. if ( isset($this->extra_query_vars[$var]) ) 
  303. $this->query_vars[$var] = $this->extra_query_vars[$var]; 
  304.  
  305. if ( isset($error) ) 
  306. $this->query_vars['error'] = $error; 
  307.  
  308. /** 
  309. * Filters the array of parsed query variables. 
  310. * @since 2.1.0 
  311. * @param array $query_vars The array of requested query variables. 
  312. */ 
  313. $this->query_vars = apply_filters( 'request', $this->query_vars ); 
  314.  
  315. /** 
  316. * Fires once all query variables for the current request have been parsed. 
  317. * @since 2.1.0 
  318. * @param WP &$this Current WordPress environment instance (passed by reference). 
  319. */ 
  320. do_action_ref_array( 'parse_request', array( &$this ) ); 
  321.  
  322. /** 
  323. * Sends additional HTTP headers for caching, content type, etc. 
  324. * Sets the Content-Type header. Sets the 'error' status (if passed) and optionally exits. 
  325. * If showing a feed, it will also send Last-Modified, ETag, and 304 status if needed. 
  326. * @since 2.0.0 
  327. * @since 4.4.0 `X-Pingback` header is added conditionally after posts have been queried in handle_404(). 
  328. * @access public 
  329. */ 
  330. public function send_headers() { 
  331. $headers = array(); 
  332. $status = null; 
  333. $exit_required = false; 
  334.  
  335. if ( is_user_logged_in() ) 
  336. $headers = array_merge($headers, wp_get_nocache_headers()); 
  337. if ( ! empty( $this->query_vars['error'] ) ) { 
  338. $status = (int) $this->query_vars['error']; 
  339. if ( 404 === $status ) { 
  340. if ( ! is_user_logged_in() ) 
  341. $headers = array_merge($headers, wp_get_nocache_headers()); 
  342. $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset'); 
  343. } elseif ( in_array( $status, array( 403, 500, 502, 503 ) ) ) { 
  344. $exit_required = true; 
  345. } elseif ( empty( $this->query_vars['feed'] ) ) { 
  346. $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset'); 
  347. } else { 
  348. // Set the correct content type for feeds 
  349. $type = $this->query_vars['feed']; 
  350. if ( 'feed' == $this->query_vars['feed'] ) { 
  351. $type = get_default_feed(); 
  352. $headers['Content-Type'] = feed_content_type( $type ) . '; charset=' . get_option( 'blog_charset' ); 
  353.  
  354. // We're showing a feed, so WP is indeed the only thing that last changed 
  355. if ( !empty($this->query_vars['withcomments']) 
  356. || false !== strpos( $this->query_vars['feed'], 'comments-' ) 
  357. || ( empty($this->query_vars['withoutcomments']) 
  358. && ( !empty($this->query_vars['p']) 
  359. || !empty($this->query_vars['name']) 
  360. || !empty($this->query_vars['page_id']) 
  361. || !empty($this->query_vars['pagename']) 
  362. || !empty($this->query_vars['attachment']) 
  363. || !empty($this->query_vars['attachment_id']) 
  364. $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT'; 
  365. else 
  366. $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT'; 
  367. $wp_etag = '"' . md5($wp_last_modified) . '"'; 
  368. $headers['Last-Modified'] = $wp_last_modified; 
  369. $headers['ETag'] = $wp_etag; 
  370.  
  371. // Support for Conditional GET 
  372. if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) 
  373. $client_etag = wp_unslash( $_SERVER['HTTP_IF_NONE_MATCH'] ); 
  374. else $client_etag = false; 
  375.  
  376. $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']); 
  377. // If string is empty, return 0. If not, attempt to parse into a timestamp 
  378. $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0; 
  379.  
  380. // Make a timestamp for our most recent modification... 
  381. $wp_modified_timestamp = strtotime($wp_last_modified); 
  382.  
  383. if ( ($client_last_modified && $client_etag) ? 
  384. (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) : 
  385. (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) { 
  386. $status = 304; 
  387. $exit_required = true; 
  388.  
  389. /** 
  390. * Filters the HTTP headers before they're sent to the browser. 
  391. * @since 2.8.0 
  392. * @param array $headers The list of headers to be sent. 
  393. * @param WP $this Current WordPress environment instance. 
  394. */ 
  395. $headers = apply_filters( 'wp_headers', $headers, $this ); 
  396.  
  397. if ( ! empty( $status ) ) 
  398. status_header( $status ); 
  399.  
  400. // If Last-Modified is set to false, it should not be sent (no-cache situation). 
  401. if ( isset( $headers['Last-Modified'] ) && false === $headers['Last-Modified'] ) { 
  402. unset( $headers['Last-Modified'] ); 
  403.  
  404. // In PHP 5.3+, make sure we are not sending a Last-Modified header. 
  405. if ( function_exists( 'header_remove' ) ) { 
  406. @header_remove( 'Last-Modified' ); 
  407. } else { 
  408. // In PHP 5.2, send an empty Last-Modified header, but only as a 
  409. // last resort to override a header already sent. #WP23021 
  410. foreach ( headers_list() as $header ) { 
  411. if ( 0 === stripos( $header, 'Last-Modified' ) ) { 
  412. $headers['Last-Modified'] = ''; 
  413. break; 
  414.  
  415. foreach ( (array) $headers as $name => $field_value ) 
  416. @header("{$name}: {$field_value}"); 
  417.  
  418. if ( $exit_required ) 
  419. exit(); 
  420.  
  421. /** 
  422. * Fires once the requested HTTP headers for caching, content type, etc. have been sent. 
  423. * @since 2.1.0 
  424. * @param WP &$this Current WordPress environment instance (passed by reference). 
  425. */ 
  426. do_action_ref_array( 'send_headers', array( &$this ) ); 
  427.  
  428. /** 
  429. * Sets the query string property based off of the query variable property. 
  430. * The {@see 'query_string'} filter is deprecated, but still works. Plugins should 
  431. * use the {@see 'request'} filter instead. 
  432. * @since 2.0.0 
  433. * @access public 
  434. */ 
  435. public function build_query_string() { 
  436. $this->query_string = ''; 
  437. foreach ( (array) array_keys($this->query_vars) as $wpvar) { 
  438. if ( '' != $this->query_vars[$wpvar] ) { 
  439. $this->query_string .= (strlen($this->query_string) < 1) ? '' : '&'; 
  440. if ( !is_scalar($this->query_vars[$wpvar]) ) // Discard non-scalars. 
  441. continue; 
  442. $this->query_string .= $wpvar . '=' . rawurlencode($this->query_vars[$wpvar]); 
  443.  
  444. if ( has_filter( 'query_string' ) ) { // Don't bother filtering and parsing if no plugins are hooked in. 
  445. /** 
  446. * Filters the query string before parsing. 
  447. * @since 1.5.0 
  448. * @deprecated 2.1.0 Use 'query_vars' or 'request' filters instead. 
  449. * @param string $query_string The query string to modify. 
  450. */ 
  451. $this->query_string = apply_filters( 'query_string', $this->query_string ); 
  452. parse_str($this->query_string, $this->query_vars); 
  453.  
  454. /** 
  455. * Set up the WordPress Globals. 
  456. * The query_vars property will be extracted to the GLOBALS. So care should 
  457. * be taken when naming global variables that might interfere with the 
  458. * WordPress environment. 
  459. * @since 2.0.0 
  460. * @access public 
  461. * @global WP_Query $wp_query 
  462. * @global string $query_string Query string for the loop. 
  463. * @global array $posts The found posts. 
  464. * @global WP_Post|null $post The current post, if available. 
  465. * @global string $request The SQL statement for the request. 
  466. * @global int $more Only set, if single page or post. 
  467. * @global int $single If single page or post. Only set, if single page or post. 
  468. * @global WP_User $authordata Only set, if author archive. 
  469. */ 
  470. public function register_globals() { 
  471. global $wp_query; 
  472.  
  473. // Extract updated query vars back into global namespace. 
  474. foreach ( (array) $wp_query->query_vars as $key => $value ) { 
  475. $GLOBALS[ $key ] = $value; 
  476.  
  477. $GLOBALS['query_string'] = $this->query_string; 
  478. $GLOBALS['posts'] = & $wp_query->posts; 
  479. $GLOBALS['post'] = isset( $wp_query->post ) ? $wp_query->post : null; 
  480. $GLOBALS['request'] = $wp_query->request; 
  481.  
  482. if ( $wp_query->is_single() || $wp_query->is_page() ) { 
  483. $GLOBALS['more'] = 1; 
  484. $GLOBALS['single'] = 1; 
  485.  
  486. if ( $wp_query->is_author() && isset( $wp_query->post ) ) 
  487. $GLOBALS['authordata'] = get_userdata( $wp_query->post->post_author ); 
  488.  
  489. /** 
  490. * Set up the current user. 
  491. * @since 2.0.0 
  492. * @access public 
  493. */ 
  494. public function init() { 
  495. wp_get_current_user(); 
  496.  
  497. /** 
  498. * Set up the Loop based on the query variables. 
  499. * @since 2.0.0 
  500. * @access public 
  501. * @global WP_Query $wp_the_query 
  502. */ 
  503. public function query_posts() { 
  504. global $wp_the_query; 
  505. $this->build_query_string(); 
  506. $wp_the_query->query($this->query_vars); 
  507.  
  508. /** 
  509. * Set the Headers for 404, if nothing is found for requested URL. 
  510. * Issue a 404 if a request doesn't match any posts and doesn't match 
  511. * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already 
  512. * issued, and if the request was not a search or the homepage. 
  513. * Otherwise, issue a 200. 
  514. * This sets headers after posts have been queried. handle_404() really means "handle status." 
  515. * By inspecting the result of querying posts, seemingly successful requests can be switched to 
  516. * a 404 so that canonical redirection logic can kick in. 
  517. * @since 2.0.0 
  518. * @access public 
  519. * @global WP_Query $wp_query 
  520. */ 
  521. public function handle_404() { 
  522. global $wp_query; 
  523.  
  524. /** 
  525. * Filters whether to short-circuit default header status handling. 
  526. * Returning a non-false value from the filter will short-circuit the handling 
  527. * and return early. 
  528. * @since 4.5.0 
  529. * @param bool $preempt Whether to short-circuit default header status handling. Default false. 
  530. * @param WP_Query $wp_query WordPress Query object. 
  531. */ 
  532. if ( false !== apply_filters( 'pre_handle_404', false, $wp_query ) ) { 
  533. return; 
  534.  
  535. // If we've already issued a 404, bail. 
  536. if ( is_404() ) 
  537. return; 
  538.  
  539. // Never 404 for the admin, robots, or if we found posts. 
  540. if ( is_admin() || is_robots() || $wp_query->posts ) { 
  541.  
  542. $success = true; 
  543. if ( is_singular() ) { 
  544. $p = false; 
  545.  
  546. if ( $wp_query->post instanceof WP_Post ) { 
  547. $p = clone $wp_query->post; 
  548.  
  549. // Only set X-Pingback for single posts that allow pings. 
  550. if ( $p && pings_open( $p ) ) { 
  551. @header( 'X-Pingback: ' . get_bloginfo( 'pingback_url' ) ); 
  552.  
  553. // check for paged content that exceeds the max number of pages 
  554. $next = '<!--nextpage-->'; 
  555. if ( $p && false !== strpos( $p->post_content, $next ) && ! empty( $this->query_vars['page'] ) ) { 
  556. $page = trim( $this->query_vars['page'], '/' ); 
  557. $success = (int) $page <= ( substr_count( $p->post_content, $next ) + 1 ); 
  558.  
  559. if ( $success ) { 
  560. status_header( 200 ); 
  561. return; 
  562.  
  563. // We will 404 for paged queries, as no posts were found. 
  564. if ( ! is_paged() ) { 
  565.  
  566. // Don't 404 for authors without posts as long as they matched an author on this site. 
  567. $author = get_query_var( 'author' ); 
  568. if ( is_author() && is_numeric( $author ) && $author > 0 && is_user_member_of_blog( $author ) ) { 
  569. status_header( 200 ); 
  570. return; 
  571.  
  572. // Don't 404 for these queries if they matched an object. 
  573. if ( ( is_tag() || is_category() || is_tax() || is_post_type_archive() ) && get_queried_object() ) { 
  574. status_header( 200 ); 
  575. return; 
  576.  
  577. // Don't 404 for these queries either. 
  578. if ( is_home() || is_search() || is_feed() ) { 
  579. status_header( 200 ); 
  580. return; 
  581.  
  582. // Guess it's time to 404. 
  583. $wp_query->set_404(); 
  584. status_header( 404 ); 
  585. nocache_headers(); 
  586.  
  587. /** 
  588. * Sets up all of the variables required by the WordPress environment. 
  589. * The action {@see 'wp'} has one parameter that references the WP object. It 
  590. * allows for accessing the properties and methods to further manipulate the 
  591. * object. 
  592. * @since 2.0.0 
  593. * @access public 
  594. * @param string|array $query_args Passed to parse_request(). 
  595. */ 
  596. public function main($query_args = '') { 
  597. $this->init(); 
  598. $this->parse_request($query_args); 
  599. $this->send_headers(); 
  600. $this->query_posts(); 
  601. $this->handle_404(); 
  602. $this->register_globals(); 
  603.  
  604. /** 
  605. * Fires once the WordPress environment has been set up. 
  606. * @since 2.1.0 
  607. * @param WP &$this Current WordPress environment instance (passed by reference). 
  608. */ 
  609. do_action_ref_array( 'wp', array( &$this ) );