answers that lead to more questions

WordPress Initialization

I began learning about WordPress by building themes and reading through the multitude of awesome tutorials posted across the net. A lot was learned through trial and error, and a lot was learned from copying and pasting. However, there is one topic for which I have yet to find comprehensive information: the WordPress initialization process. I mean from start to finish, how does the WordPress core take the initial page request and give back such beautiful web pages? It’s time to take the plunge and actually step through the WordPress core, file by file, so I can get a better understanding of how our favorite personal publishing platform initializes itself and then displays my content, all of our content, for the world to see. I hope this post will serve not only as a place that I can return to as a reference but also as a place for all of you to turn to when you need a refresher on the inner workings of the WordPress core structure, or if you’re just starting out and need a better understanding of what WordPress is doing with your theme and your code. Let’s get started…

This post is based on the WordPress trunk code base. All of the links to Core Trac source code point to the trunk version of that code. I’ve done this because the WordPress core will change faster than I can keep this post updated, so following the links will always take you to the latest version of the relevant source, regardless of whether or not the content of this article has been updated. The downside to this decision is that you may follow a link that does not accurately reflect the code base for your version of WordPress. If you need to reference the core code base of a previous version of WordPress, the information in this post will at the very least get you close.

Update: On January 29, 2013 I reviewed the links and content in this post and updated it to reflect changes to the current version of WordPress; which at the time of the update was v3.5.1.

All of the magic flowing out of your server starts with a single file: index.php. This simple file handles the initial request each and every time a page is loaded from your site. It serves two main functions. First it defines a constant named 'WP_USE_THEMES' and sets it’s value to true. This tells the WordPress core that your site should use the active theme to display the pages it’s serving. Next it requires a file named wp-blog-header.php. This file sits in the root directory of your site with index.php.

wp-blog-header is singularly responsible for three fundamental processes during the initialization of WordPress. First it requires a file named wp-load.php, then it executes the function wp() and finally it requires a file named template-loader.php. wp-load.php is also located in the root directory with index.php and wp-blog-header.php. The wp() function is defined in /wp-includes/functions.php. template-loader.php also resides in the /wp-includes directory with functions.php. We will get to the wp-includes directory in a little bit. We first have to step through the responsibilities of wp-load and the files that it requires.

wp-load has a simple yet extremely important role in WordPress’ initialization. It starts by setting a constant named 'ABSPATH' to the root directory of your WordPress install. This constant is used extensively throughout the remainder of the initialization process to easily and consistently refer to config files relative to your root directory. wp-load completes its work by loading wp-config.php. Most of us should be at least a little familiar with this file. It wasn’t too long ago that manual editing of your wp-config was still a requirement during the install process. Today we all enjoy an automated wp-config setup as long as our server’s user permissions are configured correctly. wp-load actually runs through some checks to determine where your wp-config file is located. This is because you can keep your wp-config file outside of your root install directory. I’m not sure why you would but I assume it has a lot to do with security. If any one does know, please explain in the comments. If wp-load fails to find a config file then it throws you a friendly error message with a button to create one.

If wp-load successfully located your wp-config file then this step uses that information to set the last bit of configuration settings before continuing with loading up the WordPress core. The comments at the top of wp-config sum this step up quite nicely “This file has the following configurations: MySQL settings, Table Prefix, Secret Keys, WordPress Language, and ABSPATH“. I can’t put it any more succinctly than that. After wp-config is parsed WordPress has all the information it needs to securely access your database and load your content. I’m not sure why ABSPATH is checked again but it is, it’s the last line of code before ALL the fun begins. The final line of wp-config requires wp-settings.php which is responsible for loading the lion’s share of WordPress.

Finally some action! Up until now we’ve been wandering through mundane information collection. wp-settings.php starts off by defining a new constant named 'WPINC'. This constant points to the /wp-includes directory that was mentioned above. The majority of the predefined functions that WordPress needs for initialization are defined in this directory’s files. After defining WPINC it requires three files from that directory, load.php, default-constants.php and version.php. It’s important to remember that WordPress is not actually executing these functions. They are being included using the PHP function require(), which makes their contents available to WordPress’ execution environment so the functions they contain can be called when they’re needed. load.php includes the following functions that are required to properly setup WordPress:

Once those essential functions have been loaded wp-settings loads six more functions from default-constants.php that will be used to ensure that the global environment for further initialization can be configured properly. The list below outlines these six functions and the constants they define.

Finally version.php is loaded which provides the following six variables:

$wp_version

The WordPress version string

$wp_db_version

Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema

$tinymce_version

Holds the TinyMCE version

$manifest_version

Holds the cache manifest version

$required_php_version

Holds the required PHP version

$required_mysql_version

Holds the required MySQL version

Now we’re cookin’!! The next 40 lines of code in wp-settings call many of the functions included above. wp_initial_constants() is run to setup memory, debug and caching constants; wp_check_php_mysql_versions() makes sure that we’re running the minimum required versions of both PHP and MySQL; magic quotes are disabled because they will be added later; the default timezone is set; the wp_unregister_GLOBALS() function is executed to disable PHP’s register globals setting; wp_fix_server_vars() fixes various $_SERVER related problems across different hosts; wp_favicon_request() ensures that the entire WordPress core isn’t loaded when only a favicon is being requested; wp_maintenance() (a very cool function) checks to see if there is a .maintenance file in your ABSPATH that was created less than 10 minutes ago, if there is it displays a default maintenance message that can be overridden by placing a custom maintenance.php file in your /wp-content directory; timer_start() loads the timer; wp_debug_mode() checks to see if debugging is enabled and turns on PHP’s error reporting if it is; the WP_CACHE constant is checked and if it returns true then /wp-content/advanced-cache.php is included; and finally wp_set_lang_dir() sets the path to the language files.

The WP_CACHE constant is used by 3rd party caching solutions like W3TC, WP Super Cache or Batcache. If WP_CACHE returns true then advanced-cache.php acts as the “bootstrapper” to load the caching engine you’re using (thanks to MarkJaquith for that info).

At this point we have a pretty solid foundation for WordPress to build the rest of it’s initialization on top of. Now it’s time to load some the core files that provide WordPress’ true functionality. Next up wp-settings requires five more files that it classifies as “early WordPress files”. They are compat.php, functions.php, class-wp.php, class-wp-error.php and plugin.php. All of these are located in the /wp-includes directory. While these may be early WordPress files they shouldn’t be underestimated. A tremendous amount of the functionality we expect from WordPress is provided in these five files. They are long and complex, functions.php alone is over 4,500 lines! I am not going to go into the details of each file’s functions the same way I did above. I will, in future posts, address each of these files individually to give them the attention they deserve. I am after all writing this post (and future posts) to learn more about the core myself. The descriptions below have been taken directly from the source code comments on Trac. It’s more important for this post to focus on the overall initialization of WordPress so I’ll just outline these functions here:

Part of the WordPress translation and localization sub-system. I don’t know a whole lot about localization and translation so I will link to this page I found on the Codex and leave further investigation to the reader.

Now that the baseline functionality of the above files has been loaded wp-settings moves on to call three more of the functions that were included in wp-load above. First require_wp_db() is called to load the DB class and set up the global $wpdb object. It accomplishes this by requiring /wp-includes/wp-db.php and then /wp-content/db.php if it exists. wp-db.php is another one of those files with over 1,000 lines of code. I will in the future dedicate a post to it alone. For the purposes of this article it’s only important to understand that wp-db.php is an abstract class file for accessing a MySQL database. The reason that require_wp_db() tries to require /wp-content/db.php is because you can define your own database class to access your WordPress database by extending wp-db.php and placing that code into the /wp-content/db.php file. require_wp_db() finishes up by creating the $wpdb object using the database credentials defined in your wp-config file. Next wp_set_wpdb_vars() furthers the setup of the $wpdb object by setting the field types for the table columns and the table prefix. The $table_prefix variable defined in wp-config is added to the $GLOBALS[] array just prior to this step so WordPress knows exactly which tables it can access. Finally, wp_start_object_cache() is called. It’s a little similar to require_wp_db() in that it’s responsible for loading an abstract class file. In this case its the WordPress Object Cache API. This class is responsible for caching database objects in memory to speed up frequent queries sent to the database. wp_start_object_cache() first checks to see if there is a file named /wp-content/object-cache.php because (like /wp-content/db.php) you can define your own object cache file or class. If wp_start_object_cache() finds /wp-content/object-cache.php then WordPress’ internal object cache WILL NOT be loaded. If there is no /wp-content/object-cache.php then the default cache file cache.php is loaded.

Next up the default-filters.php is required from the /wp-includes directory. The default-filters.php file adds many (but not all) of the default filters and actions to the WordPress Hooks system. This file goes hand-in-hand with plugin.php that was loaded above. Together these two files prepare the WordPress Plugin API. Anyone interested in plugin development should read through the Plugin API on the codex and have at least these two files open while reading it.

I’m going to group the next 17 lines of code together because after they’re executed we’ll be about halfway through wp-settings.php and moving into loading a huge chunk of the WordPress core files and functionality. After default-filters.php is loaded wp-settings checks to see if we are running multisite. If we are then it loads ms-blogs.php and ms-settings.php from /wp-includes. These two files act like wp-settings.php but handle setup specific to sites that have MultiSite enabled in wp-config.php. I don’t have any experience with MultiSite at this time but I plan on researching it in the future. At that time I will post an article similar to this one that concentrates on how multisite is initialized. Next, the value of SHORTINIT is evaluated and if it’s true then initialization is halted at this point. This constant was defined in the wp_initial_constants() function declared in default-constants.php above. In that file, it’s default value is set set to false. There may be times that you need access to WordPress without loading all of it’s functionality, e.g. using it as a file serving proxy in MultiSite mode. Setting this constant to true allows you to halt initialization and only use the limited functionality loaded up to this point (thanks again to MarkJaquith for this info). An additional file related to localization and translation: /wp-includes/l10n.php, is required next and finally wp_not_installed() is called to run the installer if we’ve reached this point and WordPress hasn’t been installed. This function was defined in load.php and runs a few checks to see if certain variables and functions have been defined. If they haven’t, that means WordPress isn’t installed and the initialization process dies; at which point you are redirected to the installer /wp-admin/install.php.

With those 17 lines of code out of the way wp-settings moves on to loading the majority of the remaining core files. The following list is long and each file listed is even longer. Just as above, I am going to list them with brief descriptions and link to each file on Trac. Many of the following files deserve their own posts and I will tackle them as time permits. These 40 files constitute the majority of WordPress’ core functionality.

Main WordPress Formatting API. If you’ve ever wondered how WordPress replaces your double-line breaks with paragraph elements or inserts the smiley faces in your blog posts, this is the file that does it. This file is also responsible for changing all references to ‘WordPress’ to the proper capitalization.

Canonical API to handle WordPress Redirecting. These functions make sure that proper SEO practices are enforced in your site’s URLs and even attempts to guess the correct post/page to load if a visitor types in an incorrect URL.

API for creating dynamic sidebar without hardcoding functionality into themes. This class and the functions it defines are what make creating sidebars and widgets in plugins and your theme’s functions.php file so easy.

Note: If you’d like to read a little more about the inner workings of the Admin Bar (now officially called the Toolbar), check out my first freelance article over at WP Smashing Magazine: Inside The WordPress Toolbar.

Just from reading through these files one at a time I estimate that roughly 75% of the WordPress core has been loaded and we’re about half-way through the wp-settings file. After all the require() statements above wp-settings does another check to see if we’re running multisite. If we are then it requires three additional files: ms-functions.php, ms-default-filters.php and ms-deprecated.php. These are the MultiSite (formerly WordPress MU) versions of some of the files loaded above. After this check for MultiSite the function wp_plugin_directory_constants(), which was defined in default-constants.php, is executed. It sets up seven constants for plugins to use. Now that those constants have been created a foreach() loop is run to include what are known as “must use” plugins. Back in load.php a function named wp_get_mu_plugins() was defined. That function reads the location of the WPMU_PLUGIN_DIR constant (which by default is set to /wp-content/mu-plugins) and loads any plugins that it finds. Once that’s done there is another check to see if we’re running MultiSite. If we are then another foreach() loop is executed to include the wp_get_active_network_plugins() function. This function is defined in ms-load.php and loads any MultiSite related plugins that are needed now from the WP_PLUGIN_DIR constant, which by default is set to the wp-content/plugins directory. ms-load.php was included by the ms-settings.php file. Now that the majority of the Plugin API has been setup the do_action( 'muplugins_loaded' ) hook is executed.

wp-settings then moves on to call two more of the functions defined in default-constants.php. wp_cookie_constants() and wp_ssl_constants() are responsible for guaranteeing that we have unique hashes for our cookies and whether or not to force admin and login SSL connections respectively. Once the cookies and SSL settings have been set the file vars.php is required. This is actually a really cool file and I had no idea that this functionality existed in WordPress until I popped it open. vars.php starts by setting the $pagenow variable which always holds the location of the page that’s currently being viewed. After that it tests to see what browser is being used to view the page and stores that in one of a series of nine possible variables: $is_lynx, $is_gecko, $is_winIE, $is_macIE, $is_opera, $is_NS4, $is_safari, $is_chrome or $is_iphone. How ’bout them apples!! If you’ve ever wanted to serve custom content to your visitors based on their browser from inside your template files these variables allow you to do just that. After the browser detection, vars.php runs a few checks to determine what server software WordPress is running on, either Apache or IIS. Finally, it finishes up by setting one final variable: $is_mobile. The variable is a boolean value (true|false) which will be set to true if the value of $_SERVER['HTTP_USER_AGENT'] matches any of the most common mobile browsers. Pretty fun stuff from a file of only 97 lines!

Up next wp-settings fires off two more functions. create_initial_taxonomies() (which is defined in taxonomies.php) and create_initial_post_types() (which is defined in post.php). The first sets up and initializes the default WordPress taxonomies and the second sets up the default post types (post, page, etc.). There is a warning in wp-settings alerting all you developers out there that the defaults in these two files will be run again on the init hook that executes at the end of wp-settings. Taxonomies are how WordPress organizes information internally. We are all familiar with the two most notable taxonomies already: categories and tags. Custom taxonomies were introduced in v2.3 and updated in v2.8 and v3. There is a great article from Justin Tadlock on custom taxonomies in v2.8 here and another from NetTuts on v3 custom taxonomies available here. If you’ve looked at the admin area of any decent theme you know that there are usually options to change the “type” of post (e.g. – aside, quote, gallery, etc.). Those defaults are all created in the create_initial_post_types() function. v3 of WordPress added the ability to create custom post types. Here’s another great article from Justin Tadlock on custom post types in v3.

At this point most of the core, the Plugin API, taxonomies and post types are loaded so wp-settings starts loading the active theme. It does this by executing register_theme_directory() and passing in get_theme_root() as an argument. Both of these functions were defined in theme.php. They are responsible for determining the location of and loading the active theme’s directory. Once the active theme’s directory has been loaded wp-settings loads all active plugins by running a foreach() loop to step through each plugin found by running wp_get_active_and_valid_plugins(). This function (defined in load.php) looks through /wp-content/plugins and returns any valid plugins that it finds. After the active plugins are loaded two new files are required. pluggable.php and pluggable-deprecated.php include useful functions for working with logged in users, sending mail and getting/setting user info. They are defined and required at this point so they can be overridden by any plugins loaded through the upcoming do_action( 'plugins_loaded' ) hook. The pluggable-deprecated.php file is included to maintain backward compatibility but obviously none of those functions should be used because they will be removed in a future version. The default encoding is set next by running wp_set_internal_encoding() which was defined in load.php, it defaults to UTF-8. Now a quick check is made to see if the constant WP_CACHE is set to true and if the wp_cache_postload() function exists. If it does then it gets executed to setup any remaining caching related functionality that had to wait until more of the core was loaded. At this point the entire Plugin API is up and running so the do_action( 'plugins_loaded' ) hook is fired to enable plugin developers to execute any custom functionality provided in their plugins when they’re loaded.

wp-settings now fires off wp_functionality_constants() and wp_magic_quotes(). wp_functionality_constants(), defined in default-constants.php, sets the AUTOSAVE_INTERVAL, EMPTY_TRASH_DAYS and WP_POST_REVISIONS constants to handle the housekeeping for your posts. wp_magic_quotes(), defined in load.php, takes all the variables from $_GET, $_POST, $_COOKIE and $_SERVER and sanitizes them by running stripslashes_deep() and add_magic_quotes() and then adds them to an array that gets assigned to $_REQUEST. Then the do_action( 'sanitize_comment_cookies' ) hook is executed.

The next section of wp-settings handles the creation and setup of some pretty important objects. First it creates a new WP_Query object and assigns it to the variable $wp_the_query. Then it creates another variable that points to a reference to that variable called $wp_query. This is the default loop that you’re accessing when you run if (have_posts()) : while (have_posts()) : the_post();. After that’s done it creates the WP_Rewrite object (defined in rewrite.php for handling permalinks) and assigns that to the global variables array in $GLOBALS['wp_rewrite']. Then it creates a new WP object (which was defined in class-wp.php and is responsible for creating the whole WordPress environment) and assigns it to the variable $wp. Next it creates a new WP_Widget_Factory object and assigns that to the global variables array at $GLOBALS['wp_widget_factory'], and finally creates a new WP_Roles object, assigning it to the global variables array at $GLOBALS['wp_roles'] Once all those tasks have competed the hook do_action( 'setup_theme' ) is executed to allow plugins to take action right as a theme is getting ready to be loaded.

Now wp-settings moves on to setup a few environment requirements for getting our theme loaded. The function wp_templating_constants() (defined in default-constants.php) is executed at this point. This function is responsible for creating the constants familiar to theme designers, TEMPLATEPATH and STYLESHEETPATH. In addition, it checks to see if the WP_DEFAULT_THEME constant has been set and if not it sets it to (currently) twentytwelve. This default theme is used for any new install of WordPress as well as if the current theme somehow goes missing. After that function the load_default_textdomain() function is run to ensure that proper language settings are loaded. Once the default text domain has been set the default locale file is determined and required. Once both of those steps are complete a WP_Locale object is created and assigned to the global variables array at $GLOBALS['wp_locale']. Anyone interested in how WordPress handles so many multiple languages or wants to help WordPress support more languages, these are the files you want to study.

We are so close to the end I can taste it! This next chunk of code will be interesting to any parent/child theme designer who wonders why a child’s functions.php file is loaded before the parent’s, but all the parent’s template files are loaded before the child’s. I know I always have. After the locale is setup wp-settings runs through an if statement checking the values of TEMPLATEPATH and STYLESHEETPATH. If STYLESHEETPATH is different than TEMPLATEPATH it loads it’s functions.php first then it calls the one in TEMPLATEPATH. I guess if you think it through it makes sense. You want the child to be able to set up any custom functions before the parent can define them. This is why parent themes should check if a function exists before executing it, just incase the child has defined a custom one. Once that code executes wp-settings calls the do_action( 'after_setup_theme' ) hook for any themes or plugins that need to execute custom code when a theme is done with its setup. The init method of the $wp object is called next to setup the current user’s settings and the hook do_action( 'init' ) is run. This is just about it folks, there is only one more check for MultiSite and then the hook do_action( 'wp_loaded' ) is run for any theme or plugin that needs to run any code once WordPress’ core has fully loaded. As the curtain sets on wp-settings the WordPress core has completed loading and execution returns to wp-blog-header.php. I know, it feels like we left that file a few days ago! It always amazes me that all of the functionality we just covered loads in mere seconds while the request is being processed.

If you’ve made it this far I would just like to congratulate you. We’re almost done! Lets just do a really quick recap of where we are at this point. When the visitor’s browser sent our server a request index.php set WP_USE_THEMES to true and required wp-blog-header.php. That file first called wp-load.php, then it executes the function wp() and finally requires template-loader.php. wp-load.php defined the ABSPATH constant and grabbed the site’s configuration settings from wp-config.php before calling wp-settings.php. wp-settings.php (the majority of this article, which we just covered) handled the loading of all core functionality, plugins and the active theme. Now that wp-settings.php is complete and execution has returned to wp-blog-header.php we can move on to the conclusion of our tour through WordPress’ initialization.

wp-blog-header.php now executes a function defined in functions.php named wp(). If you weren’t paying attention, about 3/4 of the way through wp-settings.php a global variable named $wp was defined and instantiated as a WP object (that WP object was defined in class-wp.php). This object is responsible for controlling the entire WordPress environment after it’s been loaded. The wp() function called by wp-blog-header.php finalizes the setup of this object by calling its main() function. This effectively puts the finishing touches on the global WordPress environment and also ensures that the current query has been properly configured so The Loop works the way we all expect it to. I gotta give one final shout-out to MarkJaquith for pointing me to the info on this function. I’m tellin’ you this guy is a badass! If you don’t follow him on Twitter, do yourself a favor and follow him now. I’ll just say this, that thing on his shoulders ain’t just a hat rack my friends!

We can now finally display a page to our visitor! The template-loader.php file is responsible for determining not only what template should be used for displaying the content but also what kind of visitor our server is responding to. If this request is for a robot, a feed or a trackback then it serves the appropriate content for those requests. If the current request is from a robot then it calls the do_robots hook. This hook is defined in functions.php and serves a robots.txt file when called. If the request is for one of our feeds then the function do_feed() is called (also defined in functions.php) and then serves whichever feed format has been requested. If the request is a trackback or pingback then the wp-trackback.php file is required. This file runs through some fancy checks and then builds the response which is served back based on whether a trackback or pingback request was received. If the request is for anything other than what we just covered then WordPress steps through a 25 line elseif statement to determine the type of template to load. This is how the Template Hierarchy is implemented in WordPress. There’s a great overview of the template hierarchy, written by ChrisCoyier, over at his (and JeffStar’s) Digging into WordPress site. I highly recommend that site in general, loads of great tips and tricks!

WHEW!! Well, if you think about it, everything we just covered is what happened when you clicked whatever link brought you to this post. It’s taken me a lot longer than I expected to write this; my back hurts and I really need to get back to learning some Ruby programming! 🙂 So I’m just going to thank you if you’ve made it to the end. Thank you for sticking with me. I hope you’ve learned as much reading this as I did writing it. I intend to be back soon with a (shorter) post on a more specific topic.

Have you ever thought about including a little bit more than just your articles?
I mean, what you say is valuable and all. Nevertheless think of if you added some great visuals or videos to give your posts
more, “pop”! Your content is excellent but with pics and videos, this blog
could undeniably be one of the very best in its field.

Thanks for your marvelous posting! I really enjoyed reading
it, you are a great author.I will be sure to
bookmark your blog and may come back in the future. I want to encourage one to continue your great writing, have a nice afternoon!

Just one minor thing, Somewhere in the post is read as “… wp_not_installed() is called to run the installer if we’ve reached this point and WordPress hasn’t been installed. This function was defined in wp-load and runs a few checks to see if certain variables and functions have been defined..”

But, I believe function wp_not_installed() is defined within load.php not within wp-load.php.

I feel very humble. I am beginner in the wordpress. I am reading and attempting to develop my first theme. I have been wishing to go through wordpress loading process the way you do in this post. But, I am lucky I didn’t start before searching for such thing which led me to this post. I went through the whole content, yet I will go through it multiple times until I get good understanding of it. Really great post!

Also, thank you for introducing the “badass”. You are also an other “badass” (never used this term before, I am

Thank you so much.from the other country and forgive me if I am not using it right).

This is an excellent resource, both for experienced and newbie WordPressers alike. Understanding just how this all hangs together is a real help to building good themes, I haven’t manged to do that yet but this has helped greatly in getting me along that road. Many thanks

To the best of my understanding this process is run through for every request. However, if you look closely at wp-blog-header.php you’ll see that it only kicks off the initialization if $wp_did_header IS NOT set (that’s what !isset($wp_did_header) means). I assume that conditional statement is an attempt to cut down on the amount of times your site needs to run through the init process. But don’t quote me on that, there is still much about the init process that I have to learn.

Wow, this is soooo comprehensive! Thank you so much. Graphics would be great, but who has a double A0 printer for all that information at home? Maybe you could create a wallpaper (a real physical thing)?!? 🙂

It’s a good practice to keep the config file out of the www accessible folder in case any other secure aspect of your site is compromised and the root folder is somehow opened up to http access. Because this file contains authentication credentials, including database credentials and sometimes even ftp authentication info, the results of having this file compromised could be catastrophic. Placing it outside the http path addresses this contingency.