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 thewp_get_current_user()function, defined in wp-includes/pluggable.php. This sets up the$current_userglobal, an instance of theWP_Userclass.$this->parse_request($query_args)which does all the hard work of providing a correct query by utilizing WordPress rewrite rules, the raw$_POSTand$_GETserver variables (filtered by an allowed set of variables listed inWP::$public_query_varsandWP::$private_query_vars, which can be filtered with thequery_varsfilter 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_varsobject 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 and Last-Modified headers for feeds if ‘feed’ is inside the query variables, adds any additional headers by applying thewp_headersfilter, and finally setting them as response headers using the PHPheader()function. At this point the headers have been set, and WordPress kindly lets you know about this by doing thesend_headersaction.$this->query_posts()– this method sets the$wp_the_query($wp_query)queryproperty 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->postsreturns more than 0 posts.$this->register_globals()– extracts the query variables back into the global namespace and registers the$query_string,$posts,$postand$request.- One of the most used WordPress actions is fired off at this point – the
wpaction.
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_redirectaction 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()oris_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 theget_???_template()functions, which were all defined inside wp-includes/theme.php. A longiftree checks for various conditions using all sorts ofis_????()functions (likeis_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
$templatevariable 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 direction and 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.





Hey,
concerning
$wp_queryvs.$wp_the_query:Check out Nacin’s awesome presentation from WordCamp Portland “You Don’t Know Query”
Thanks, Konstantin.
So to summarize,
$wp_queryis a link (reference) to$wp_the_querywhich can be switched to a differentWP_Queryobject and reset back to the original$wp_the_querywith the reset function at any time. Great, thanks for the heads up, that’s something I didn’t know :)Thanks for the walk-through! As a long-time developer on various platforms, but new to WordPress, this was just what I needed to get my bearings…
Just come across your post after I have done a similar work-through. These are great posts and will be helpful for developers.
However, they are just simply going through the code. Do you have any plan to dig a bit ‘deeper’? for example, while working through the code, I can see that the functions do_action(…) and apply_filters(…) are called in a recursive way, which suggests that there could be some kinds of hierarchy in the way actions and filters are invoked(do you have the similar observation?). Think hierarchies of the actions and the filters will definitely help developers in the work.
Hope to see more inside analysis of wordpress
Thanks