| 1 | <?php |
|---|
| 2 | /** |
|---|
| 3 | * WordPress environment setup class. |
|---|
| 4 | * |
|---|
| 5 | * @package WordPress |
|---|
| 6 | * @since 2.0.0 |
|---|
| 7 | */ |
|---|
| 8 | class WP { |
|---|
| 9 | /** |
|---|
| 10 | * Public query variables. |
|---|
| 11 | * |
|---|
| 12 | * Long list of public query variables. |
|---|
| 13 | * |
|---|
| 14 | * @since 2.0.0 |
|---|
| 15 | * @access public |
|---|
| 16 | * @var array |
|---|
| 17 | */ |
|---|
| 18 | var $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', 'comments_popup', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term', 'cpage', 'post_type'); |
|---|
| 19 | |
|---|
| 20 | /** |
|---|
| 21 | * Private query variables. |
|---|
| 22 | * |
|---|
| 23 | * Long list of private query variables. |
|---|
| 24 | * |
|---|
| 25 | * @since 2.0.0 |
|---|
| 26 | * @var array |
|---|
| 27 | */ |
|---|
| 28 | var $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__in', 'post_parent__not_in' ); |
|---|
| 29 | |
|---|
| 30 | /** |
|---|
| 31 | * Extra query variables set by the user. |
|---|
| 32 | * |
|---|
| 33 | * @since 2.1.0 |
|---|
| 34 | * @var array |
|---|
| 35 | */ |
|---|
| 36 | var $extra_query_vars = array(); |
|---|
| 37 | |
|---|
| 38 | /** |
|---|
| 39 | * Query variables for setting up the WordPress Query Loop. |
|---|
| 40 | * |
|---|
| 41 | * @since 2.0.0 |
|---|
| 42 | * @var array |
|---|
| 43 | */ |
|---|
| 44 | var $query_vars; |
|---|
| 45 | |
|---|
| 46 | /** |
|---|
| 47 | * String parsed to set the query variables. |
|---|
| 48 | * |
|---|
| 49 | * @since 2.0.0 |
|---|
| 50 | * @var string |
|---|
| 51 | */ |
|---|
| 52 | var $query_string; |
|---|
| 53 | |
|---|
| 54 | /** |
|---|
| 55 | * Permalink or requested URI. |
|---|
| 56 | * |
|---|
| 57 | * @since 2.0.0 |
|---|
| 58 | * @var string |
|---|
| 59 | */ |
|---|
| 60 | var $request; |
|---|
| 61 | |
|---|
| 62 | /** |
|---|
| 63 | * Rewrite rule the request matched. |
|---|
| 64 | * |
|---|
| 65 | * @since 2.0.0 |
|---|
| 66 | * @var string |
|---|
| 67 | */ |
|---|
| 68 | var $matched_rule; |
|---|
| 69 | |
|---|
| 70 | /** |
|---|
| 71 | * Rewrite query the request matched. |
|---|
| 72 | * |
|---|
| 73 | * @since 2.0.0 |
|---|
| 74 | * @var string |
|---|
| 75 | */ |
|---|
| 76 | var $matched_query; |
|---|
| 77 | |
|---|
| 78 | /** |
|---|
| 79 | * Whether already did the permalink. |
|---|
| 80 | * |
|---|
| 81 | * @since 2.0.0 |
|---|
| 82 | * @var bool |
|---|
| 83 | */ |
|---|
| 84 | var $did_permalink = false; |
|---|
| 85 | |
|---|
| 86 | /** |
|---|
| 87 | * Add name to list of public query variables. |
|---|
| 88 | * |
|---|
| 89 | * @since 2.1.0 |
|---|
| 90 | * |
|---|
| 91 | * @param string $qv Query variable name. |
|---|
| 92 | */ |
|---|
| 93 | function add_query_var($qv) { |
|---|
| 94 | if ( !in_array($qv, $this->public_query_vars) ) |
|---|
| 95 | $this->public_query_vars[] = $qv; |
|---|
| 96 | } |
|---|
| 97 | |
|---|
| 98 | /** |
|---|
| 99 | * Set the value of a query variable. |
|---|
| 100 | * |
|---|
| 101 | * @since 2.3.0 |
|---|
| 102 | * |
|---|
| 103 | * @param string $key Query variable name. |
|---|
| 104 | * @param mixed $value Query variable value. |
|---|
| 105 | */ |
|---|
| 106 | function set_query_var($key, $value) { |
|---|
| 107 | $this->query_vars[$key] = $value; |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | /** |
|---|
| 111 | * Parse request to find correct WordPress query. |
|---|
| 112 | * |
|---|
| 113 | * Sets up the query variables based on the request. There are also many |
|---|
| 114 | * filters and actions that can be used to further manipulate the result. |
|---|
| 115 | * |
|---|
| 116 | * @since 2.0.0 |
|---|
| 117 | * |
|---|
| 118 | * @param array|string $extra_query_vars Set the extra query variables. |
|---|
| 119 | */ |
|---|
| 120 | function parse_request($extra_query_vars = '') { |
|---|
| 121 | global $wp_rewrite; |
|---|
| 122 | |
|---|
| 123 | /** |
|---|
| 124 | * Test if parse_request should be done. |
|---|
| 125 | * |
|---|
| 126 | * Hook here and return false to stop parse_request from proceeding. |
|---|
| 127 | * |
|---|
| 128 | * @param bool true |
|---|
| 129 | * @param WP $this The WordPress environment object |
|---|
| 130 | * @param array|string $extra_query_vars The extra query variables passed. |
|---|
| 131 | * |
|---|
| 132 | * @since 3.5.0 |
|---|
| 133 | */ |
|---|
| 134 | if ( ! apply_filters( 'do_parse_request', true, $this, $extra_query_vars ) ) |
|---|
| 135 | return; |
|---|
| 136 | |
|---|
| 137 | $this->query_vars = array(); |
|---|
| 138 | $post_type_query_vars = array(); |
|---|
| 139 | |
|---|
| 140 | if ( is_array($extra_query_vars) ) |
|---|
| 141 | $this->extra_query_vars = & $extra_query_vars; |
|---|
| 142 | else if (! empty($extra_query_vars)) |
|---|
| 143 | parse_str($extra_query_vars, $this->extra_query_vars); |
|---|
| 144 | |
|---|
| 145 | // Process PATH_INFO, REQUEST_URI, and 404 for permalinks. |
|---|
| 146 | |
|---|
| 147 | // Fetch the rewrite rules. |
|---|
| 148 | $rewrite = $wp_rewrite->wp_rewrite_rules(); |
|---|
| 149 | |
|---|
| 150 | if ( ! empty($rewrite) ) { |
|---|
| 151 | // If we match a rewrite rule, this will be cleared. |
|---|
| 152 | $error = '404'; |
|---|
| 153 | $this->did_permalink = true; |
|---|
| 154 | |
|---|
| 155 | $pathinfo = isset( $_SERVER['PATH_INFO'] ) ? $_SERVER['PATH_INFO'] : ''; |
|---|
| 156 | list( $pathinfo ) = explode( '?', $pathinfo ); |
|---|
| 157 | $pathinfo = str_replace( "%", "%25", $pathinfo ); |
|---|
| 158 | |
|---|
| 159 | list( $req_uri ) = explode( '?', $_SERVER['REQUEST_URI'] ); |
|---|
| 160 | $self = $_SERVER['PHP_SELF']; |
|---|
| 161 | $home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' ); |
|---|
| 162 | |
|---|
| 163 | // Trim path info from the end and the leading home path from the |
|---|
| 164 | // front. For path info requests, this leaves us with the requesting |
|---|
| 165 | // filename, if any. For 404 requests, this leaves us with the |
|---|
| 166 | // requested permalink. |
|---|
| 167 | $req_uri = str_replace($pathinfo, '', $req_uri); |
|---|
| 168 | $req_uri = trim($req_uri, '/'); |
|---|
| 169 | $req_uri = preg_replace("|^$home_path|i", '', $req_uri); |
|---|
| 170 | $req_uri = trim($req_uri, '/'); |
|---|
| 171 | $pathinfo = trim($pathinfo, '/'); |
|---|
| 172 | $pathinfo = preg_replace("|^$home_path|i", '', $pathinfo); |
|---|
| 173 | $pathinfo = trim($pathinfo, '/'); |
|---|
| 174 | $self = trim($self, '/'); |
|---|
| 175 | $self = preg_replace("|^$home_path|i", '', $self); |
|---|
| 176 | $self = trim($self, '/'); |
|---|
| 177 | |
|---|
| 178 | // The requested permalink is in $pathinfo for path info requests and |
|---|
| 179 | // $req_uri for other requests. |
|---|
| 180 | if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) { |
|---|
| 181 | $request = $pathinfo; |
|---|
| 182 | } else { |
|---|
| 183 | // If the request uri is the index, blank it out so that we don't try to match it against a rule. |
|---|
| 184 | if ( $req_uri == $wp_rewrite->index ) |
|---|
| 185 | $req_uri = ''; |
|---|
| 186 | $request = $req_uri; |
|---|
| 187 | } |
|---|
| 188 | |
|---|
| 189 | $this->request = $request; |
|---|
| 190 | |
|---|
| 191 | // Look for matches. |
|---|
| 192 | $request_match = $request; |
|---|
| 193 | if ( empty( $request_match ) ) { |
|---|
| 194 | // An empty request could only match against ^$ regex |
|---|
| 195 | if ( isset( $rewrite['$'] ) ) { |
|---|
| 196 | $this->matched_rule = '$'; |
|---|
| 197 | $query = $rewrite['$']; |
|---|
| 198 | $matches = array(''); |
|---|
| 199 | } |
|---|
| 200 | } else { |
|---|
| 201 | foreach ( (array) $rewrite as $match => $query ) { |
|---|
| 202 | // If the requesting file is the anchor of the match, prepend it to the path info. |
|---|
| 203 | if ( ! empty($req_uri) && strpos($match, $req_uri) === 0 && $req_uri != $request ) |
|---|
| 204 | $request_match = $req_uri . '/' . $request; |
|---|
| 205 | |
|---|
| 206 | if ( preg_match("#^$match#", $request_match, $matches) || |
|---|
| 207 | preg_match("#^$match#", urldecode($request_match), $matches) ) { |
|---|
| 208 | |
|---|
| 209 | if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) { |
|---|
| 210 | // this is a verbose page match, lets check to be sure about it |
|---|
| 211 | if ( ! get_page_by_path( $matches[ $varmatch[1] ] ) ) |
|---|
| 212 | continue; |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | // Got a match. |
|---|
| 216 | $this->matched_rule = $match; |
|---|
| 217 | break; |
|---|
| 218 | } |
|---|
| 219 | } |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | if ( isset( $this->matched_rule ) ) { |
|---|
| 223 | // Trim the query of everything up to the '?'. |
|---|
| 224 | $query = preg_replace("!^.+\?!", '', $query); |
|---|
| 225 | |
|---|
| 226 | // Substitute the substring matches into the query. |
|---|
| 227 | $query = addslashes(WP_MatchesMapRegex::apply($query, $matches)); |
|---|
| 228 | |
|---|
| 229 | $this->matched_query = $query; |
|---|
| 230 | |
|---|
| 231 | // Parse the query. |
|---|
| 232 | parse_str($query, $perma_query_vars); |
|---|
| 233 | |
|---|
| 234 | // If we're processing a 404 request, clear the error var since we found something. |
|---|
| 235 | if ( '404' == $error ) |
|---|
| 236 | unset( $error, $_GET['error'] ); |
|---|
| 237 | } |
|---|
| 238 | |
|---|
| 239 | // If req_uri is empty or if it is a request for ourself, unset error. |
|---|
| 240 | if ( empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) { |
|---|
| 241 | unset( $error, $_GET['error'] ); |
|---|
| 242 | |
|---|
| 243 | if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) |
|---|
| 244 | unset( $perma_query_vars ); |
|---|
| 245 | |
|---|
| 246 | $this->did_permalink = false; |
|---|
| 247 | } |
|---|
| 248 | } |
|---|
| 249 | |
|---|
| 250 | /** |
|---|
| 251 | * Filter the query variables whitelist before processing. |
|---|
| 252 | * |
|---|
| 253 | * Allows (publicly allowed) query vars to be added, removed, or changed changed prior to executing the query. |
|---|
| 254 | * Needed to allow custom rewrite rules using your own arguments to work, or any other |
|---|
| 255 | * custom query variables you want usable on the public side. |
|---|
| 256 | * |
|---|
| 257 | * @since 1.5.2 |
|---|
| 258 | * |
|---|
| 259 | * @param array $public_query_vars The array of whitelisted query variables. |
|---|
| 260 | */ |
|---|
| 261 | $this->public_query_vars = apply_filters('query_vars', $this->public_query_vars); |
|---|
| 262 | |
|---|
| 263 | foreach ( get_post_types( array(), 'objects' ) as $post_type => $t ) |
|---|
| 264 | if ( $t->query_var ) |
|---|
| 265 | $post_type_query_vars[$t->query_var] = $post_type; |
|---|
| 266 | |
|---|
| 267 | foreach ( $this->public_query_vars as $wpvar ) { |
|---|
| 268 | if ( isset( $this->extra_query_vars[$wpvar] ) ) |
|---|
| 269 | $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar]; |
|---|
| 270 | elseif ( isset( $_POST[$wpvar] ) ) |
|---|
| 271 | $this->query_vars[$wpvar] = $_POST[$wpvar]; |
|---|
| 272 | elseif ( isset( $_GET[$wpvar] ) ) |
|---|
| 273 | $this->query_vars[$wpvar] = $_GET[$wpvar]; |
|---|
| 274 | elseif ( isset( $perma_query_vars[$wpvar] ) ) |
|---|
| 275 | $this->query_vars[$wpvar] = $perma_query_vars[$wpvar]; |
|---|
| 276 | |
|---|
| 277 | if ( !empty( $this->query_vars[$wpvar] ) ) { |
|---|
| 278 | if ( ! is_array( $this->query_vars[$wpvar] ) ) { |
|---|
| 279 | $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar]; |
|---|
| 280 | } else { |
|---|
| 281 | foreach ( $this->query_vars[$wpvar] as $vkey => $v ) { |
|---|
| 282 | if ( !is_object( $v ) ) { |
|---|
| 283 | $this->query_vars[$wpvar][$vkey] = (string) $v; |
|---|
| 284 | } |
|---|
| 285 | } |
|---|
| 286 | } |
|---|
| 287 | |
|---|
| 288 | if ( isset($post_type_query_vars[$wpvar] ) ) { |
|---|
| 289 | $this->query_vars['post_type'] = $post_type_query_vars[$wpvar]; |
|---|
| 290 | $this->query_vars['name'] = $this->query_vars[$wpvar]; |
|---|
| 291 | } |
|---|
| 292 | } |
|---|
| 293 | } |
|---|
| 294 | |
|---|
| 295 | // Convert urldecoded spaces back into + |
|---|
| 296 | foreach ( get_taxonomies( array() , 'objects' ) as $taxonomy => $t ) |
|---|
| 297 | if ( $t->query_var && isset( $this->query_vars[$t->query_var] ) ) |
|---|
| 298 | $this->query_vars[$t->query_var] = str_replace( ' ', '+', $this->query_vars[$t->query_var] ); |
|---|
| 299 | |
|---|
| 300 | // Limit publicly queried post_types to those that are publicly_queryable |
|---|
| 301 | if ( isset( $this->query_vars['post_type']) ) { |
|---|
| 302 | $queryable_post_types = get_post_types( array('publicly_queryable' => true) ); |
|---|
| 303 | if ( ! is_array( $this->query_vars['post_type'] ) ) { |
|---|
| 304 | if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) ) |
|---|
| 305 | unset( $this->query_vars['post_type'] ); |
|---|
| 306 | } else { |
|---|
| 307 | $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types ); |
|---|
| 308 | } |
|---|
| 309 | } |
|---|
| 310 | |
|---|
| 311 | foreach ( (array) $this->private_query_vars as $var) { |
|---|
| 312 | if ( isset($this->extra_query_vars[$var]) ) |
|---|
| 313 | $this->query_vars[$var] = $this->extra_query_vars[$var]; |
|---|
| 314 | } |
|---|
| 315 | |
|---|
| 316 | if ( isset($error) ) |
|---|
| 317 | $this->query_vars['error'] = $error; |
|---|
| 318 | |
|---|
| 319 | /** |
|---|
| 320 | * Filter the query variables array before parsing. |
|---|
| 321 | * |
|---|
| 322 | * Allows query vars to be added, removed, or changed changed prior to executing the query. |
|---|
| 323 | * |
|---|
| 324 | * @since 2.1.0 |
|---|
| 325 | * |
|---|
| 326 | * @param array $query_vars The array of requested query variables. |
|---|
| 327 | */ |
|---|
| 328 | $this->query_vars = apply_filters('request', $this->query_vars); |
|---|
| 329 | |
|---|
| 330 | /** |
|---|
| 331 | * Fires actions to be run after parse_request is complete. |
|---|
| 332 | * |
|---|
| 333 | * @since 2.1.0 |
|---|
| 334 | * |
|---|
| 335 | * @param WP &$this The WordPress environment object (passed by reference). |
|---|
| 336 | */ |
|---|
| 337 | do_action_ref_array('parse_request', array(&$this)); |
|---|
| 338 | } |
|---|
| 339 | |
|---|
| 340 | /** |
|---|
| 341 | * Send additional HTTP headers for caching, content type, etc. |
|---|
| 342 | * |
|---|
| 343 | * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing |
|---|
| 344 | * a feed, it will also send last-modified, etag, and 304 status if needed. |
|---|
| 345 | * |
|---|
| 346 | * @since 2.0.0 |
|---|
| 347 | */ |
|---|
| 348 | function send_headers() { |
|---|
| 349 | $headers = array('X-Pingback' => get_bloginfo('pingback_url')); |
|---|
| 350 | $status = null; |
|---|
| 351 | $exit_required = false; |
|---|
| 352 | |
|---|
| 353 | if ( is_user_logged_in() ) |
|---|
| 354 | $headers = array_merge($headers, wp_get_nocache_headers()); |
|---|
| 355 | if ( ! empty( $this->query_vars['error'] ) ) { |
|---|
| 356 | $status = (int) $this->query_vars['error']; |
|---|
| 357 | if ( 404 === $status ) { |
|---|
| 358 | if ( ! is_user_logged_in() ) |
|---|
| 359 | $headers = array_merge($headers, wp_get_nocache_headers()); |
|---|
| 360 | $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset'); |
|---|
| 361 | } elseif ( in_array( $status, array( 403, 500, 502, 503 ) ) ) { |
|---|
| 362 | $exit_required = true; |
|---|
| 363 | } |
|---|
| 364 | } else if ( empty($this->query_vars['feed']) ) { |
|---|
| 365 | $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset'); |
|---|
| 366 | } else { |
|---|
| 367 | // We're showing a feed, so WP is indeed the only thing that last changed |
|---|
| 368 | if ( !empty($this->query_vars['withcomments']) |
|---|
| 369 | || false !== strpos( $this->query_vars['feed'], 'comments-' ) |
|---|
| 370 | || ( empty($this->query_vars['withoutcomments']) |
|---|
| 371 | && ( !empty($this->query_vars['p']) |
|---|
| 372 | || !empty($this->query_vars['name']) |
|---|
| 373 | || !empty($this->query_vars['page_id']) |
|---|
| 374 | || !empty($this->query_vars['pagename']) |
|---|
| 375 | || !empty($this->query_vars['attachment']) |
|---|
| 376 | || !empty($this->query_vars['attachment_id']) |
|---|
| 377 | ) |
|---|
| 378 | ) |
|---|
| 379 | ) |
|---|
| 380 | $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT'; |
|---|
| 381 | else |
|---|
| 382 | $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT'; |
|---|
| 383 | $wp_etag = '"' . md5($wp_last_modified) . '"'; |
|---|
| 384 | $headers['Last-Modified'] = $wp_last_modified; |
|---|
| 385 | $headers['ETag'] = $wp_etag; |
|---|
| 386 | |
|---|
| 387 | // Support for Conditional GET |
|---|
| 388 | if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) |
|---|
| 389 | $client_etag = wp_unslash( $_SERVER['HTTP_IF_NONE_MATCH'] ); |
|---|
| 390 | else $client_etag = false; |
|---|
| 391 | |
|---|
| 392 | $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']); |
|---|
| 393 | // If string is empty, return 0. If not, attempt to parse into a timestamp |
|---|
| 394 | $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0; |
|---|
| 395 | |
|---|
| 396 | // Make a timestamp for our most recent modification... |
|---|
| 397 | $wp_modified_timestamp = strtotime($wp_last_modified); |
|---|
| 398 | |
|---|
| 399 | if ( ($client_last_modified && $client_etag) ? |
|---|
| 400 | (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) : |
|---|
| 401 | (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) { |
|---|
| 402 | $status = 304; |
|---|
| 403 | $exit_required = true; |
|---|
| 404 | } |
|---|
| 405 | } |
|---|
| 406 | |
|---|
| 407 | /** |
|---|
| 408 | * Filter the HTTP headers before they're sent to the browser. |
|---|
| 409 | * |
|---|
| 410 | * @since 2.8.0 |
|---|
| 411 | * |
|---|
| 412 | * @param array $headers The list of headers to be sent. |
|---|
| 413 | * @param WP $this The WordPress environment object. |
|---|
| 414 | */ |
|---|
| 415 | $headers = apply_filters('wp_headers', $headers, $this); |
|---|
| 416 | |
|---|
| 417 | if ( ! empty( $status ) ) |
|---|
| 418 | status_header( $status ); |
|---|
| 419 | |
|---|
| 420 | // If Last-Modified is set to false, it should not be sent (no-cache situation). |
|---|
| 421 | if ( isset( $headers['Last-Modified'] ) && false === $headers['Last-Modified'] ) { |
|---|
| 422 | unset( $headers['Last-Modified'] ); |
|---|
| 423 | |
|---|
| 424 | // In PHP 5.3+, make sure we are not sending a Last-Modified header. |
|---|
| 425 | if ( function_exists( 'header_remove' ) ) { |
|---|
| 426 | @header_remove( 'Last-Modified' ); |
|---|
| 427 | } else { |
|---|
| 428 | // In PHP 5.2, send an empty Last-Modified header, but only as a |
|---|
| 429 | // last resort to override a header already sent. #WP23021 |
|---|
| 430 | foreach ( headers_list() as $header ) { |
|---|
| 431 | if ( 0 === stripos( $header, 'Last-Modified' ) ) { |
|---|
| 432 | $headers['Last-Modified'] = ''; |
|---|
| 433 | break; |
|---|
| 434 | } |
|---|
| 435 | } |
|---|
| 436 | } |
|---|
| 437 | } |
|---|
| 438 | |
|---|
| 439 | foreach( (array) $headers as $name => $field_value ) |
|---|
| 440 | @header("{$name}: {$field_value}"); |
|---|
| 441 | |
|---|
| 442 | if ( $exit_required ) |
|---|
| 443 | exit(); |
|---|
| 444 | |
|---|
| 445 | /** |
|---|
| 446 | * Fires actions to be run after send_headers is complete. |
|---|
| 447 | * |
|---|
| 448 | * @since 2.1.0 |
|---|
| 449 | * |
|---|
| 450 | * @param WP &$this The WordPress environment object (passed by reference). |
|---|
| 451 | */ |
|---|
| 452 | do_action_ref_array('send_headers', array(&$this)); |
|---|
| 453 | } |
|---|
| 454 | |
|---|
| 455 | /** |
|---|
| 456 | * Sets the query string property based off of the query variable property. |
|---|
| 457 | * |
|---|
| 458 | * The 'query_string' filter is deprecated, but still works. Plugins should |
|---|
| 459 | * use the 'request' filter instead. |
|---|
| 460 | * |
|---|
| 461 | * @since 2.0.0 |
|---|
| 462 | */ |
|---|
| 463 | function build_query_string() { |
|---|
| 464 | $this->query_string = ''; |
|---|
| 465 | foreach ( (array) array_keys($this->query_vars) as $wpvar) { |
|---|
| 466 | if ( '' != $this->query_vars[$wpvar] ) { |
|---|
| 467 | $this->query_string .= (strlen($this->query_string) < 1) ? '' : '&'; |
|---|
| 468 | if ( !is_scalar($this->query_vars[$wpvar]) ) // Discard non-scalars. |
|---|
| 469 | continue; |
|---|
| 470 | $this->query_string .= $wpvar . '=' . rawurlencode($this->query_vars[$wpvar]); |
|---|
| 471 | } |
|---|
| 472 | } |
|---|
| 473 | |
|---|
| 474 | // query_string filter deprecated. Use request filter instead. |
|---|
| 475 | if ( has_filter('query_string') ) { // Don't bother filtering and parsing if no plugins are hooked in. |
|---|
| 476 | /** |
|---|
| 477 | * Filter the query string before parsing |
|---|
| 478 | * |
|---|
| 479 | * @depreciated Use query_vars or request instead. |
|---|
| 480 | * |
|---|
| 481 | * @since 1.5.2 |
|---|
| 482 | * |
|---|
| 483 | * @param string $query_string The query string to modify. |
|---|
| 484 | */ |
|---|
| 485 | $this->query_string = apply_filters('query_string', $this->query_string); |
|---|
| 486 | parse_str($this->query_string, $this->query_vars); |
|---|
| 487 | } |
|---|
| 488 | } |
|---|
| 489 | |
|---|
| 490 | /** |
|---|
| 491 | * Set up the WordPress Globals. |
|---|
| 492 | * |
|---|
| 493 | * The query_vars property will be extracted to the GLOBALS. So care should |
|---|
| 494 | * be taken when naming global variables that might interfere with the |
|---|
| 495 | * WordPress environment. |
|---|
| 496 | * |
|---|
| 497 | * @global string $query_string Query string for the loop. |
|---|
| 498 | * @global array $posts The found posts. |
|---|
| 499 | * @global WP_Post|null $post The current post, if available. |
|---|
| 500 | * @global string $request The SQL statement for the request. |
|---|
| 501 | * @global int $more Only set, if single page or post. |
|---|
| 502 | * @global int $single If single page or post. Only set, if single page or post. |
|---|
| 503 | * @global WP_User $authordata Only set, if author archive. |
|---|
| 504 | * |
|---|
| 505 | * @since 2.0.0 |
|---|
| 506 | */ |
|---|
| 507 | function register_globals() { |
|---|
| 508 | global $wp_query; |
|---|
| 509 | |
|---|
| 510 | // Extract updated query vars back into global namespace. |
|---|
| 511 | foreach ( (array) $wp_query->query_vars as $key => $value ) { |
|---|
| 512 | $GLOBALS[ $key ] = $value; |
|---|
| 513 | } |
|---|
| 514 | |
|---|
| 515 | $GLOBALS['query_string'] = $this->query_string; |
|---|
| 516 | $GLOBALS['posts'] = & $wp_query->posts; |
|---|
| 517 | $GLOBALS['post'] = isset( $wp_query->post ) ? $wp_query->post : null; |
|---|
| 518 | $GLOBALS['request'] = $wp_query->request; |
|---|
| 519 | |
|---|
| 520 | if ( $wp_query->is_single() || $wp_query->is_page() ) { |
|---|
| 521 | $GLOBALS['more'] = 1; |
|---|
| 522 | $GLOBALS['single'] = 1; |
|---|
| 523 | } |
|---|
| 524 | |
|---|
| 525 | if ( $wp_query->is_author() && isset( $wp_query->post ) ) |
|---|
| 526 | $GLOBALS['authordata'] = get_userdata( $wp_query->post->post_author ); |
|---|
| 527 | } |
|---|
| 528 | |
|---|
| 529 | /** |
|---|
| 530 | * Set up the current user. |
|---|
| 531 | * |
|---|
| 532 | * @since 2.0.0 |
|---|
| 533 | */ |
|---|
| 534 | function init() { |
|---|
| 535 | wp_get_current_user(); |
|---|
| 536 | } |
|---|
| 537 | |
|---|
| 538 | /** |
|---|
| 539 | * Set up the Loop based on the query variables. |
|---|
| 540 | * |
|---|
| 541 | * @uses WP::$query_vars |
|---|
| 542 | * @since 2.0.0 |
|---|
| 543 | */ |
|---|
| 544 | function query_posts() { |
|---|
| 545 | global $wp_the_query; |
|---|
| 546 | $this->build_query_string(); |
|---|
| 547 | $wp_the_query->query($this->query_vars); |
|---|
| 548 | } |
|---|
| 549 | |
|---|
| 550 | /** |
|---|
| 551 | * Set the Headers for 404, if nothing is found for requested URL. |
|---|
| 552 | * |
|---|
| 553 | * Issue a 404 if a request doesn't match any posts and doesn't match |
|---|
| 554 | * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already |
|---|
| 555 | * issued, and if the request was not a search or the homepage. |
|---|
| 556 | * |
|---|
| 557 | * Otherwise, issue a 200. |
|---|
| 558 | * |
|---|
| 559 | * @since 2.0.0 |
|---|
| 560 | */ |
|---|
| 561 | function handle_404() { |
|---|
| 562 | global $wp_query; |
|---|
| 563 | |
|---|
| 564 | // If we've already issued a 404, bail. |
|---|
| 565 | if ( is_404() ) |
|---|
| 566 | return; |
|---|
| 567 | |
|---|
| 568 | // Never 404 for the admin, robots, or if we found posts. |
|---|
| 569 | if ( is_admin() || is_robots() || $wp_query->posts ) { |
|---|
| 570 | status_header( 200 ); |
|---|
| 571 | return; |
|---|
| 572 | } |
|---|
| 573 | |
|---|
| 574 | // We will 404 for paged queries, as no posts were found. |
|---|
| 575 | if ( ! is_paged() ) { |
|---|
| 576 | |
|---|
| 577 | // Don't 404 for these queries if they matched an object. |
|---|
| 578 | if ( ( is_tag() || is_category() || is_tax() || is_author() || is_post_type_archive() ) && $wp_query->get_queried_object() ) { |
|---|
| 579 | status_header( 200 ); |
|---|
| 580 | return; |
|---|
| 581 | } |
|---|
| 582 | |
|---|
| 583 | // Don't 404 for these queries either. |
|---|
| 584 | if ( is_home() || is_search() ) { |
|---|
| 585 | status_header( 200 ); |
|---|
| 586 | return; |
|---|
| 587 | } |
|---|
| 588 | } |
|---|
| 589 | |
|---|
| 590 | // Guess it's time to 404. |
|---|
| 591 | $wp_query->set_404(); |
|---|
| 592 | status_header( 404 ); |
|---|
| 593 | nocache_headers(); |
|---|
| 594 | } |
|---|
| 595 | |
|---|
| 596 | /** |
|---|
| 597 | * Sets up all of the variables required by the WordPress environment. |
|---|
| 598 | * |
|---|
| 599 | * The action 'wp' has one parameter that references the WP object. It |
|---|
| 600 | * allows for accessing the properties and methods to further manipulate the |
|---|
| 601 | * object. |
|---|
| 602 | * |
|---|
| 603 | * @since 2.0.0 |
|---|
| 604 | * |
|---|
| 605 | * @param string|array $query_args Passed to {@link parse_request()} |
|---|
| 606 | */ |
|---|
| 607 | function main($query_args = '') { |
|---|
| 608 | $this->init(); |
|---|
| 609 | $this->parse_request($query_args); |
|---|
| 610 | $this->send_headers(); |
|---|
| 611 | $this->query_posts(); |
|---|
| 612 | $this->handle_404(); |
|---|
| 613 | $this->register_globals(); |
|---|
| 614 | |
|---|
| 615 | /** |
|---|
| 616 | * Fires actions to be run at the end of WP environment setup. |
|---|
| 617 | * |
|---|
| 618 | * @since 2.1.0 |
|---|
| 619 | * |
|---|
| 620 | * @param WP &$this The WordPress environment object (passed by reference). |
|---|
| 621 | */ |
|---|
| 622 | do_action_ref_array('wp', array(&$this)); |
|---|
| 623 | } |
|---|
| 624 | |
|---|
| 625 | } |
|---|
| 626 | |
|---|
| 627 | /** |
|---|
| 628 | * Helper class to remove the need to use eval to replace $matches[] in query strings. |
|---|
| 629 | * |
|---|
| 630 | * @since 2.9.0 |
|---|
| 631 | */ |
|---|
| 632 | class WP_MatchesMapRegex { |
|---|
| 633 | /** |
|---|
| 634 | * store for matches |
|---|
| 635 | * |
|---|
| 636 | * @access private |
|---|
| 637 | * @var array |
|---|
| 638 | */ |
|---|
| 639 | var $_matches; |
|---|
| 640 | |
|---|
| 641 | /** |
|---|
| 642 | * store for mapping result |
|---|
| 643 | * |
|---|
| 644 | * @access public |
|---|
| 645 | * @var string |
|---|
| 646 | */ |
|---|
| 647 | var $output; |
|---|
| 648 | |
|---|
| 649 | /** |
|---|
| 650 | * subject to perform mapping on (query string containing $matches[] references |
|---|
| 651 | * |
|---|
| 652 | * @access private |
|---|
| 653 | * @var string |
|---|
| 654 | */ |
|---|
| 655 | var $_subject; |
|---|
| 656 | |
|---|
| 657 | /** |
|---|
| 658 | * regexp pattern to match $matches[] references |
|---|
| 659 | * |
|---|
| 660 | * @var string |
|---|
| 661 | */ |
|---|
| 662 | var $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number |
|---|
| 663 | |
|---|
| 664 | /** |
|---|
| 665 | * constructor |
|---|
| 666 | * |
|---|
| 667 | * @param string $subject subject if regex |
|---|
| 668 | * @param array $matches data to use in map |
|---|
| 669 | * @return self |
|---|
| 670 | */ |
|---|
| 671 | function WP_MatchesMapRegex($subject, $matches) { |
|---|
| 672 | $this->_subject = $subject; |
|---|
| 673 | $this->_matches = $matches; |
|---|
| 674 | $this->output = $this->_map(); |
|---|
| 675 | } |
|---|
| 676 | |
|---|
| 677 | /** |
|---|
| 678 | * Substitute substring matches in subject. |
|---|
| 679 | * |
|---|
| 680 | * static helper function to ease use |
|---|
| 681 | * |
|---|
| 682 | * @access public |
|---|
| 683 | * @param string $subject subject |
|---|
| 684 | * @param array $matches data used for substitution |
|---|
| 685 | * @return string |
|---|
| 686 | */ |
|---|
| 687 | public static function apply($subject, $matches) { |
|---|
| 688 | $oSelf = new WP_MatchesMapRegex($subject, $matches); |
|---|
| 689 | return $oSelf->output; |
|---|
| 690 | } |
|---|
| 691 | |
|---|
| 692 | /** |
|---|
| 693 | * do the actual mapping |
|---|
| 694 | * |
|---|
| 695 | * @access private |
|---|
| 696 | * @return string |
|---|
| 697 | */ |
|---|
| 698 | function _map() { |
|---|
| 699 | $callback = array($this, 'callback'); |
|---|
| 700 | return preg_replace_callback($this->_pattern, $callback, $this->_subject); |
|---|
| 701 | } |
|---|
| 702 | |
|---|
| 703 | /** |
|---|
| 704 | * preg_replace_callback hook |
|---|
| 705 | * |
|---|
| 706 | * @access public |
|---|
| 707 | * @param array $matches preg_replace regexp matches |
|---|
| 708 | * @return string |
|---|
| 709 | */ |
|---|
| 710 | function callback($matches) { |
|---|
| 711 | $index = intval(substr($matches[0], 9, -1)); |
|---|
| 712 | return ( isset( $this->_matches[$index] ) ? urlencode($this->_matches[$index]) : '' ); |
|---|
| 713 | } |
|---|
| 714 | |
|---|
| 715 | } |
|---|