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:
- Loads wp-includes/load.php which includes lots of initialization functions and helpers, like
timer_stop()andtimer_start()to time code executions. These are used further on in the bootup process. - 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_DIRandLOGGED_IN_COOKIE. - Loads wp-includes/version.php, which as you may have guessed contains the WordPress version alongside the TinyMCE version, required PHP version, etc.
- Calls
wp_initial_constants()(defined in wp-includes/default-constatnts.php), which “unpacks” a handful definitions into the scope. - 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). - 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).
- 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.
- 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.) - 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. - Then *wp-includes/class-wp.php which defines the WordPress environment setup class called
WPand the helperWP_MatchesMapRegexclass. 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. - After that wp-includes/class-wp-error.php is included which defines the
WP_Errorclass which is a container for error messages and their codes and a simpleis_wp_error()function to check whether a variable is an instance of theWP_Errorclass. - 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.
require_wp_db()(defined in wp-includes/load.php) loads in wp-includes/wp-db.php which defines thewpdbclass (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$wpdbobject and a connection with the database is established at this point. This is followed by a call towp_set_wpdb_vars()which sets up database table column specifiers.- 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.
- Then comes the wp-includes/default-filters.php which adds all the basic filters and actions via the
add_action()andadd_filter()functions. Can you spot the easter egg near the bottom? (Spoiler alert: line 236) - wp-includes/pomo/mo.php is loaded next, which contains the
MOclass, 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.
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 :)




SHORTINIT load is highly useful for cases when you need database access, but plugins and themes are unnecessary overhead. Practical example would be Ajax handlers for which speed of execution is more important than rich API functionality, like incremental search.
Rarst, thank you for your comment. Do you know of any plugins that use the SHORTINIT feature? Would be interesting to look at what else can be used from a SHORTINIT’ed WordPress.
Google “SHORTINIT site:plugins.svn.wordpress.org” :) But really this is not too common technique, I have discovered constant only when I was taking apart core load process myself.
Core uses SHORTINIT in wp-includes/ms-files.php to dynamically load static files without the overhead of plugins and themes.
Nice, thanks for pointing that out, John, indeed it does.
Thanks for this, definitely just subscribed to your feed! Very cool reading!
Thanks for reading it through and finding it useful, Rev. Voodoo! Part 2 coming sometime next week, thanks for subscribing.
Great first WordPress Internals post. This series will definitely be at the top of the “must read” list for serious plugin developers.
Thanks!
Thanks for the kind feedback, Japh. Part two is going to air sometime this week, so stay tuned.
Is there a way to improve how WordPress Boots up, like removing files or combining others ?
Daniel, do you mean increase speed or lower memory usage? What would you want to improve?
I mean: is there a way to improve speed with the way WordPress boots up ?
Daniel, that’s a great question, which would require a post of its own, however let’s see if we can outline some strategies.
I’m not going to go over the usual suspects – APC, native WordPress caching plugins, etc. these are pretty much straight-forward and can drastically improve load times.
Looking at things from a core code perspective, altering the way WordPress boots up by hacking the core is not recommended, besides, the core developers make sure everything loads as quickly as possible, and you don’t want to mess with something that works. However, as Part 2 of this series WordPress Internals: How WordPress Boots Up Part 2 shows, the early-on bootup process includes all the active plugins by including their files, so keeping their initialization down to the bare minimum (register actions, filters, etc.) would help loading times.
The bare WordPress installation I was working with takes around 2000-2500 ms to load when logged in as Administrator, and 500ms when not logged in, 70% of which is wp-settings loading and 15% the template-loader (which we’ll cover in one of the next WordPress booting up parts), when not logged in wp-settings take up 50% of loading times, the admin-bar loading stage is quite a killer as you can see, throwing ‘sanitize_comment_cookies’ (wp-settings line 214) accounts for quite a lot when logged in as Administrator, too. Again, most of the core loading is already optimized (further optimizations are possible in some places, like unpacking definitions from default-constants in one go instead of 3-5 function calls) and most probably whatever you come up with won’t make an impact on loading speeds. The amount of plugins, the nature of their loading up (hooks to ‘init’, ‘plugins_loaded’, etc.), the amount of code executed in the functions.php file of the theme will make up the overhead, so that’s the best place to start looking. Again, I’m against hacking the core to get more speed. When a WordPress update rolls you know you’re in trouble.
I recommend lacing your PHP with XDebug and grinding your profiling cachegrinds with KCachegrind or any other flavour thereof. It will offer some very helpful insight into where your bottlenecks are, which plugins are slowing down loading times in which cases. Check out a screenshot of a cachegrind when I’m logged in and Administration bar is enabled, see how cURL takes up 30% of loading inside wp-settings? WordPress Cachegrind – you could investigate who called cURL (probably one of the sanitize_cookie_comments registered hooks)…
Let me know if this offers enough inspiration to grind your way through any speed issues you may have.
Excelent article! Thanks for sharing
Thank you, Marlon!
Great post, thanx! Heading for the second part :)
Thanks for reading and enjoying, part three will be soon here.
Thank you for putting these together, great effort :)
Sean, glad you enjoyed it. Part 3 is on it’s way.
The text “2 Actions 2 Furious” is the easter egg ?
Kudos! ;)
Pingback: WordPress Command Line Fun