Codex

Interested in functions, hooks, classes, or methods? Check out the new WordPress Code Reference!

User:DavidHouse/Wordpress Code Flow

This article is a ROUGH DRAFT. The author is still working on this document, so please do not edit this without the author's permission. The content within this article may not yet be verified or valid. This information is subject to change.

This document is an attempt to track the flow of code through WordPress. It stays fairly high-level and tracks the flow, rather than explaining how a specific block of code works. It assumes a familiarity with PHP.

This document deals with the 1.5 branch.

NB: Line numbers refer to how the code was in 1.5.1.3, these may change with subsequent releases.

WordPress includes files chart

index.php

The request initially comes in to index.php. This is an extremely short file that defines a constant, WP_USE_THEMES, to tell WordPress to use theme redirection, then hands control over to wp-blog-header.php.

wp-blog-header.php

Error creating thumbnail: File with dimensions greater than 12.5 MP
Leave this in as a comparison
This file is where the real magic happens. The responsibilites for wp-blog-header.php (and the files it includes) are to understand the request, fetch the appropriate posts from the database, and display them according to the active theme. It also does a lot of low-level things like sending out the correct HTTP headers.

The first important line in wp-blog-header.php is line 12, where wp-config.php is included.

wp-config.php

Again, a short but crucial file. You'll probably recognise this file if you've already set up your WordPress blog: this is where the constants are defined like username, password and host that allow WordPress to connect to your database. It is also the place where you define your locale if you're not running an English blog. See Editing wp-config.php and WordPress Localization. Control is then handed over to wp-settings.php.

wp-settings.php

This is again a pretty important file. Its roles are to initialise the WordPress global variables and singletons, and include a lot of vital files. Files that wp-settings.php includes:

  • wp-includes/functions.php
  • wp-includes/default-filters.php
  • wp-includes/wp-l10n.php
  • wp-includes/functions-formatting.php
  • wp-includes/functions-post.php
  • wp-includes/classes.php
  • wp-includes/template-functions-general.php
  • wp-includes/template-functions-links.php
  • wp-includes/template-functions-author.php
  • wp-includes/template-functions-post.php
  • wp-includes/template-functions-category.php
  • wp-includes/comment-functions.php
  • wp-includes/feed-functions.php
  • wp-includes/links.php
  • wp-includes/kses.php
  • wp-includes/version.php

These are all just files that define functions or classes the are used by WordPress later on. Important files are functions.php (and derivatives like functions-formatting.php and comment-functions.php) that define useful functions for internal WordPress usage. Also, the template-functions-*.php family, which define the Template Tags.

Another role of wp-settings.php is to load the plugins from the wp-content/plugins directory. This is done with the block of code starting on line 128. It pulls an array of the active plugin from the option 'active_plugins' and loads each one. Thus, any code before this line cannot be affected by plugins.

After the plugins are loaded, wp-includes/pluggable-functions.php is loaded, which load some more important internal WordPress functions, but each function is surrounded by an if () statement that checks to see if any file has already defined a function with that name. If so, that function is not defined. All this gives plugins the chance to use their own functions in place of some core WordPress ones. (This is available with WordPress 1.5.1+). See Pluggable Functions for more detail and a reference.

wp-blog-header.php continued

Path Info

(beginning line 16)

The next big block in wp-blog-header.php deals with $_SERVER['PATH_INFO']. This is a predefined PHP variable. If you request a file, and then append, for example, /bob/charlie (so your request would be http://example.com/file.php/bob/charlie), $_SERVER['PATH_INFO'] gets filled with whatever's after the filename. If you are using the nice permalinks feature of WordPress, WordPress will understand a URL like http://example.com/index.php/category/sausages. This is useful when writing .htaccess RewriteRules.

Collecting query variables

(beginning line 88)

wp-blog-header.php then defines an array of 'known query variables', so to speak: the query variables that WordPress understands: $wpvarstoreset (poor name, yes). wp-blog-header.php then iterates over this array, and for each query variable, sees if there is an associated variable in either $_GET or $_POST. If there is, this variable is filled with the value from $_GET or $_POST. For example, if for the query variable 'category_name' there is a value for $_GET['category_name'] or $_POST['category_name'], $category_name will be filled with that value.

HTTP Headers

(beginning line 109)

The next thing wp-blog-header.php is send out some important HTTP headers. The if () statement here has three branches, corresponding to three different scenarios:

  • We've already picked up that there's a 404 error (probably because error=404 was passed through the query string. In this case, a header of '404 Not Found' is sent.
  • We're not serving a feed (so the $feed query variable is empty): in this case, an 'X-Pingback' header is sent to enable pingback autodiscovery, and a 'Content-type' header is sent so the client knows which charset to interpret our content as. We don't try to send things like '304 Not Modified' status, as there may be dynamic content on this page that doesn't belong to WordPress.
  • We are serving a feed. In this case, we can guarentee that there is no extraneous dynamic content, and we can indeed predict how long it has been since the feed has changed. Thus, this is worked out and sent as a 'Last-Modified' header. An 'ETag' header is also sent, as well as an 'X-Pingback' header as above. We then move on to the code that supports a 'conditional GET', which is where the client tells us the last time it made this exact request (using If-Modified-Since), and if there isn't any new content since that date, we just send a '304 Not Modified' header and exit.

Request Parsing

(beginning line 160)

wp-blog-header.php must now start to figure out what the client has requested. First, a new array, $more_wpvars is defined which contains a list of variables that aren't to be passed in the query string but are still going to be useful for what we're about to do.

A loop begins on line 164 that constructs a new query string that completely defines this request: every query variable that was specified in the query string is collected into a query string. Also, if any of the variables listed in $more_wpvars have values, they're added to query string as well. So we may end up with a query srting that looks something like the following:

category_name=tech&paged=2&posts_per_page=10

That string would be for the category archive for the 'tech' category, page two, displaying 10 posts on every page. The query string is then passed into query_posts() (line 177). This calls $wp_query->query (the WP_Query is defined in wp-includes/classes.php), which calls $wp_query->parse_query. This function disects the query string and determines what kind of query we're looking at: a category page, a dated archive page, a feed? $wp_query->parse_query() sets a load of variables in the $wp_query singleton that allow the is_feed(), is_home(), is_category() etc. functions to work. They will not work before line 177.

$wp_query->query() has a second role as well: it also calls $wp_query->get_posts(), which looks at the query variables and retrieves the correct set of posts. This is assigned to the global $posts variable right back in wp-blog-header.php.

404, take two

(beginning line 190)

There is now a second chance to issue a '404 Not Modified' header: if no posts were found by $wp_query->get_posts() and we haven't already issued a 404 header, then one is sent here.

Template redirection

(beginning line 206)

So, the request has been parsed and all posts have been retrieved. The next thing to do is to display them. The next role for wp-blog-header.php is to give control over to the active theme. This block of code simply determines which theme file to load, and does so. The constant TEMPLATEPATH, defined in wp-settings.php on line 153, contains the path to the active theme. See Theme Development for information on which file is loaded in which scenarios.

Note: WordPress uses its own template files for displaying feeds. If the request is a feed, then no theme is loaded, but control is passed over to wp-feed.php, which loads the correct feed template file (wp-rss.php, wp-rss2.php or wp-atom.php).

Themes

Themes have the important responsibility to display the posts. Typically, there's two scenarios themes might be asked to deal with: display one post, or display a list of posts. The first comes about when the request is a permalink or a Page, the second is normal for any other archive.

Header

A call to get_header() is typical at the top of theme files: it fetches the header.php from the active theme.

The Loop

You've probably heard of The Loop. It's a crucial part of any theme. It is, in essence, a PHP loop which loops over the array of posts ($wp_query->posts is used instead of the global $posts), and displays each post using the Template Tags. The loop typically looks like this:

if (have_posts()): while (have_posts()): the_post();

...

endwhile; endif;

(If the syntax looks unfamiliar, that's because it's using the alternative syntax for PHP control structures). have_posts() and the_posts() are both wrapper functions for WP_Query functions, head over to that page for more information on them.

There should be a loop in each theme file which isn't designed for showing a single post (which would be single.php and page.php).

Sidebar and Footer

After The Loop has completed, sidebar.php is included via get_sidebar() (unless the theme is a one-column design), and then footer.php with get_footer().

And thus ends our story of a WordPress request.