If you haven’t been following this series, make sure you at least skim through WordPress Internals: How WordPress Boots Up and WordPress Internals: How WordPress Boots Up Part 2, where we went from the very moment an HTTP request hits the index.php front-facing WordPress file and up to the quite cumbersome but lighting fast bootstrap process that wp-settings.php leads and sustains.
This third part will deal with the more interesting parts, after the bootstrap routines, which will hopefully not bore you to death and provide some insight into how WordPress works from the inside, helping you understand and leverage all its internal power when developing themes and plugins.
So, wp-settings.php, which is required wp-config.php, which in turn is required by wp-load.php, which in turn is required by wp-blog-header.php, which in turn is required by index.php, which in turn is requested by our visitors returns control over to wp-blog-header.php. Remember how wp-blog-header.php seems to branch off when requiring wp-load.php? Our previous post was dedicated to that branch. Now wp-blog-header.php is going to call the wp() function.
This leads us to our next stop in this long journey to the center of WordPress – the wp() function, which is defined in wp-includes/functions.php, so let’s open it up and see what happens exactly (it’s on line 1565 if you’re lost). wp() brings in three global objects $wp, $wp_query, $wp_the_query which are instances of the WP and WP_Query classes. $wp_query and $wp_the_query reference the same instance of WP_Query as wp-settings.php set it up on line 229 – $wp_query =& $wp_the_query. The comment above this assignment states that $wp_query should be used instead of $wp_the_query. I’m not sure as to why it is setup this way, the source code is sprinkled with both $wp_query and $wp_the_query, yet there are only 4 assignments (2 of each) and they are limited to assigning themselves to one another.
wp-app.php:1068: $wp_query = $GLOBALS['wp_query']; wp-settings.php:229: $wp_query =& $wp_the_query; wp-includes/functions.php:1570: $wp_the_query = $wp_query; wp-settings.php:221: $wp_the_query = new WP_Query();
Back-compatibility? Caching? I can only guess for now, but if you do know please lend us an insight.
Anyways, back on track. The $wp->main( $query_vars ); method is called with an empty$query_vars string. This is what happens inside the main() method:
- $this->init() sets up the current user using the wp_get_current_user() function, defined inwp-includes/pluggable.php. This sets up the $current_user global, an instance of the WP_Userclass.
- $this->parse_request($query_args) which does all the hard work of providing a correct query by utilizing WordPress rewrite rules, the raw $_POST and $_GET server variables (filtered by an allowed set of variables listed in WP::$public_query_vars andWP::$private_query_vars, which can be filtered with the query_vars filter to add/remove anything you want) to finally store a nice and clean query array inside its public $query_varsproperty which can be accessed via the global $wp->query_vars object property. Neat huh?
- $this->send_headers() prepares a list of headers to be sent out with the response. The X-Pingback, a set of no-cache headers if user is logged in or an error occurred, Content-Type andLast-Modified headers for feeds if ‘feed’ is inside the query variables, adds any additional headers by applying the wp_headers filter, and finally setting them as response headers using the PHP header() function. At this point the headers have been set, and WordPress kindly lets you know about this by doing the send_headers action.
- $this->query_posts() – this method sets the $wp_the_query ($wp_query) query property which is then utilized when interfacing with the celebrated WordPress Loop.
- $this->handle_404() – sets the appropriate 404 or 200 headers based on whether$wp_query->posts returns more than 0 posts.
- $this->register_globals() – extracts the query variables back into the global namespace and registers the $query_string, $posts, $post and $request.
- One of the most used WordPress actions is fired off at this point – the wp action.
And it’s back to wp-blog.header.php into its final branch that loads up the WordPress theme. The wp-includes/template-loader.php file is required and here is what happens:
- The template_redirect action is fired, giving plugins the chance to do something before the theme is loaded, possibly exiting so that when displaying the theme is not necessary around 40% of the theme loading time can be saved. Useful for plugins utilizing XHTTPRequests or handling RPC requests among everything else.
- If the request is_robots(), is_feed() or is_trackback() appropriate actions are taken and the template loader returns.
- Next, if WP_USE_THEMES (defined for the first time in index.php) is set to true a template is acquired via the use of the get_???_template() functions, which were all defined inside wp-includes/theme.php. A long if tree checks for various conditions using all sorts of is_????()functions (like is_single(), is_page()) and tries to get the appropriate template. The functions make sure that the highest priority template is loaded, taking into account child themes, missing files, filters, etc.
- If the $template variable has been assigned a positive value, the template file is finally included.
And yes, control is returned to wp-blog-header.php, which returns control to index.php, which has no more code to execute. The original request is blessed with a response in under a couple of seconds (uncached, no fancy stuff), the visitor is probably happy and you are proud to be running WordPress and knowing most of the bare stuff that makes it tick when responding.
Our journey to the center of the WordPress core has come to an end. We’ve explored lots of the initialization and got to know some of WordPress more intimately. It may seem bloated, heavyweight to some, but others find exquisiteness in how it operates, geniality in the flexibility it provides and inspiration in how the small WordPress bits interact with one another under the unpredictable variability of circumstances to bring joy with each and every request. What do you think? After having seen how WordPress responds to the most basic request, is it too complex, too inflated?
Share some thoughts, ask questions via our comment section below, throw a tweet in our directionand stay tuned for more episodes of WordPress Internals and other great WordPress-related stuff. And don’t forget to let us know what part of WordPress you want us to dissect next.