WordPress Internals: How WordPress Boots Up

Managing a WordPress blog, designing and deploying WordPress themes, and even writing WordPress plugins does not require an in-depth knowledge of how WordPress processes requests to its pages, forms responses based on those requests and spews out a nice and clean HTML code to make the users happy. However, understanding the inner-workings of what makes it all tick proves (at least to us) to be a great advantage when coding advanced plugins or theme features and debugging them.

From those of us who have hacked a theme here and there (hopefully using child themes) once in a while, those who have written their own twitter or other service feed plugins or widgets, to those who are simply interested in PHP and software architecture in general, we invite you to join us on a journey deep into WordPress core code.

And we’ll start off from the very very beginning, from the moment your webserver, processing the HTTP request for http://yoursite.com/ (with index.php hopefully being your setup index file) fetches the requested page and drives it through one of PHP’s SAPIs (the interface between the web server and PHP – cgi, cgi-fcgi, php-fpm, etc.) to produce a final response which is usually interpreted by your web-browser.

As a side-note: index.php is the front-facing and request handling file. Most of the front-facing core functionality links in WordPress, i.e. the permalinks, page links, etc., are actually GET queries to the index.php file. You will see /?p=52 which is /index.php?p=52 which tells WordPress “go fetch page id 52″. Setting up your rewrite rules is important, especially if you want pretty links. Setting up your .htaccess (Apache) or nginx configuration file to state that the index is index.php, will direct all default (/) requests to index.php, which is what we want. As for pretty links without the query symbols the webserver has to be configured with rewrite rules, which are out of the scope of this article, but we’ll get to cover WordPress routing internals pretty soon, so stay tuned.

A few notes before we dive into today’s portion of WordPress internals. I’m working on a fresh installation of WordPress 3.2.1. acquired from official sources with all plugins disabled and the Twenty Eleven theme enabled. Don’t worry if your setup is different, as long as your WordPress version is one of the recent ones you should get the same results. Code excerpts that are provided here are trimmed and compressed where deemed appropriate. So, without further ado, I invite go ahead and open up your index.php (the one that came with WordPress, of course).

What’s inside the WordPress index.php file that kickstarts the magic?

// index.php Lines 16-17
/* Loads the WordPress Environment and Template */
require('./wp-blog-header.php');

The index.php file requests the wp-blog-header.php file, which, as the well commented WordPress code hints, loads the WordPress Environment and Template. Let’s open up wp-blog-header.php and see how exactly it does that. What do we see?

// wp-blog-header.php lines 12-16
require_once( dirname(__FILE__) . '/wp-load.php' );
wp();
require_once( ABSPATH . WPINC . '/template-loader.php' );

If you’re wondering why it’s wrapped inside an isset($wpdidheader) block, it makes sure that the code is executed only once. Anyway, there are three calls here, 2 native PHP calls (require_once()) and a call to the wp() WordPress-defined function. First off is wp-load.php, let’s see what it does. The comment at the very top sums up what the code does – loads up wp-config.php. In default environments this file is located in the WordPress root directory, if it exists – it’s loaded up at that point in the code. WordPress does, however, allow for a setup where wp-config.php is one level higher than its root (for security purposes the higher level may not be accessible via the web-server so the database credentials are a bit safer there). If the file is not found in the root directory or by itself (checked against the presence of wp-settings.php) at a higher level the famous 5-minute installation is launched.

// wp-load.php lines 26-36
if ( file_exists( ABSPATH . 'wp-config.php') ) {
    /** The config file resides in ABSPATH */
    require_once( ABSPATH . 'wp-config.php' );
} elseif ( file_exists( dirname(ABSPATH) . '/wp-config.php' ) && ! file_exists( dirname(ABSPATH) . '/wp-settings.php' ) ) {
    /** The config file resides one level above ABSPATH but is not part of another install*/
    require_once( dirname(ABSPATH) . '/wp-config.php' );
} else {
// ... offer the installer
}

So off to wp-config.php (still inside the root WordPress folder). What does it do? It defines 17 or so constants for WordPress to use. You may have typed in your database credentials inside this file yourself. After all the defining wp-config.php cascades into wp-settings.php and this is where the real fun begins.

wp-settings.php does quite a bit:

  1. Loads wp-includes/load.php which includes lots of initialization functions and helpers, like timer_stop() and timer_start() to time code executions. These are used further on in the bootup process.
  2. Loads wp-includes/default-constants.php, defining 6 functions which “unwrap” default constant definitions into the code (if not defined previously by the user in wp-config.php), like WP_PLUGIN_DIR and LOGGED_IN_COOKIE.
  3. Loads wp-includes/version.php, which as you may have guessed contains the WordPress version alongside the TinyMCE version, required PHP version, etc.
  4. Calls wp_initial_constants() (defined in wp-includes/default-constatnts.php), which “unpacks” a handful definitions into the scope.
  5. Calls wp_check_php_mysql_versions() (defined in wp-includes/load.php), which checks on PHP and MySQL versions and stops if they are inappropriately old (or absent).
  6. Disables magicquotes, sets default timezones, turns registerglobals off, standardizes $_SERVER variables (mostly due to IIS), checks whether maintenance mode is switched on (returns a 503, Retry-After 600, hard-coded HTML message if now maintenance.php page is present with the theme).
  7. Starts a microtimer, check debug mode by setting the appropriate ini strings (displayerrors, logerrors, errorlog, errorreporting), loads an advanced caching plugin if there is one, defines the language directory, and starts loading more core includes.
  8. Includes wp-includes/compat.php which defines implementations of functions that are not usually provided on old or default PHP versions (mb_substr(), hash_hmac(), etc.)
  9. And wp-includes/functions.php which defines the main WordPress API. You most probably are familiar with the likes of add/get/update_option(), they are defined in this file.
  10. Then *wp-includes/class-wp.php which defines the WordPress environment setup class called WP and the helper WP_MatchesMapRegex class. Nothing has been called yet, they’ve just been declared and defined at this point, so you’re not missing out on any of the action.
  11. After that wp-includes/class-wp-error.php is included which defines the WP_Error class which is a container for error messages and their codes and a simple is_wp_error() function to check whether a variable is an instance of the WP_Error class.
  12. Finally the wp-includes/plugin.php is loaded. This file defines your favourite actions and filters functions (do, add, has, etc.) and other parts of the Plugin API.
  13. require_wp_db() (defined in wp-includes/load.php) loads in wp-includes/wp-db.php which defines the wpdb class (WordPress Database) and checks for signs of a db.php file in the content directories, which is used for database caching by some plugins. This function call also instantiates the $wpdb object and a connection with the database is established at this point. This is followed by a call to wp_set_wpdb_vars() which sets up database table column specifiers.
  14. Loads in an external or a native WordPress Object Cache class defined in wp-includes/cache.php. This class stores data inside its cache property and saves on database calls, etc.
  15. Then comes the wp-includes/default-filters.php which adds all the basic filters and actions via the add_action() and add_filter() functions. Can you spot the easter egg near the bottom? (Spoiler alert: line 236)
  16. wp-includes/pomo/mo.php is loaded next, which contains the MO class, necessary to work with translation files, it in turn includes wp-includes/pomo/translations.php and wp-includes/pomo/streams.php, the former of which includes wp-includes/pomo/entry.php. Most of the translation classes are defined at this point.
WordPress Internals: How WordPress Boots Up

Click to enlarge image

Given that multi-site is disabled, WordPress is finally ready to be loaded. This is the end of the SHORTINIT process. At this point WordPress has defined some of its core functionality – Main API, Error API, Plugin API, Object Cache, registered its core actions and filters Languages Classes. This should be useful when integrating the WordPress core into a host core, like that of a framework which is free to continue instantiating and interacting with the defined core WordPress objects and the database.

However, it is not very apparent as to how useful the features loaded so far are. add/get/update_option() does not work, the WP object cannot be initialized (calls to its ::init() method result in errors), $wpdb may be useful as a MySQL database abstraction to some extent. So the host core should continue to load WordPress however it requires, piece by piece. As for how WordPress does it we’ll see in our next airing of WordPress Internals, which will definitely give us an insight into how to use WordPress inside another framework.

If you have questions, suggestions, spotted an inconsistency or variants thereof please don’t hesitate to launch a tweet in our direction or let us know via the comments section below. Stay tuned for another WordPress Internals next week.

Hmmm, update! WordPress Internals: How WordPress Boots Up Part 2 :)