How to Initialize the WordPress Environment

(and create a dynamic CSS page that calls WordPress functions)

Start Button September 2014

Contents



When you request a custom PHP code page that uses WordPress functions, you will get a undefined function fatal error related to the WordPress functions you tried to call unless you load the WordPress environment as part of your code. The easiest way to do this is with a line that looks something like this:

require_once(../../../wp-load.php);

You will see examples that require wp-config.php or wp-blog-header.php. These are flawed in some way, do what the core code does and use wp-load.php. Using wp-config.php misses out on the proper error reporting being set and an important constant being defined. Using wp-blog-header.php also loads theme template information. If your code relies on theme templates to do its thing, then this is the file to require. If not, you are needlessly loading more core code than is necessary to run your custom code.

If you only need basic WordPress functionality, you can first define the constant SHORTINIT as true to really minimize the code loaded. You will more than likely still get the undefined function error this way since the function set loaded is quite minimal. It's likely the function you need is not part of that set. It may be worth a try if you anticipate your code page will be subjected to very heavy traffic.

They key concept about loading the environment this way is the path to wp-load.php must be relative to your code page. And therein lies its tragic flaw. Since your code is more than likely in /wp-content/ somewhere, your code cannot possibly know the relative path to wp-load.php because on any given WordPress installation, the administrator could choose to redefine the /wp-content/ folders to reside somewhere else in the folder structure, breaking your relative reference.

And since WordPress can reside any number of folders deep into a website, you cannot use absolute references either. Requiring wp-load.php is the wrong approach! (as is requiring any other core file) If your code is only meant for your own use on your site only, it's acceptable to use this approach. If you intend to distribute your code to others, you must NOT use this approach. If you try to use this approach in code destined for the wordpress.orgLink is to outside of this site repository, it will be summarily rejected.

So what should you do? Your options are quite limited actually. You can use AJAX to make a server request through admin-ajax.php and have your code run as an AJAX handler. You can create a page templateLink is to outside of this site out of your code, then create a page post type entry based on that template. To run your code, simply request the page by its title slug. Your only other option is to send your request through /wp-admin/admin-post.php. This is actually a great option, but it's not documented anywhere... until now, right here! Since AJAX and page templates are documented elsewhere, this article will focus only on admin-post.php
to Contents

Using admin-post.php


The technique is similar to that used by AJAX. If you are familiar with using AJAX in WordPress, this technique will be very easy to learn. It is actually a fair bit simpler because you do not need JavaScript or jQuery to make a server request. As an example in using admin-post.php, I'll use another sparsely documented technique: a Dynamic CSS page.

The Request


All requests are passed through /wp-admin/admin-post.php. What code is actual run for the request is determined by the value of $_REQUEST['action']

POST

If you are making a POST request, the form action points to admin-post.php. You pass the 'action' value in a field of the same name. Since the user would not need to see this field, the best way to do this is to define it in a hidden field that is part of the complete form:

<form action="<?php echo admin_url( 'admin-post.php' ); ?>" method="POST">
    <input type="hidden" name="action" value="bcw_dyno">
    <input type="submit">
</form>

Add any other fields as needed to pass their values to your request handler.

GET

Despite the name of the file containing "post" and not "get", you can make GET requests through this file with equal success. You can have the form submit as a GET instead of POST, but the most common GET request is the ubiquitous anchor link:

<a href="<?php echo admin_url('admin-post.php?action=bcw_dyno'); ?>">run the code</a>

Note how the 'action' value is simply appended as an URL parameter. You may pass additional data to the request handler by appending other name=value pairs, each separated with the ampersand ( & ) symbol.
to Contents

Dynamic CSS


Since our goal is to cause a dynamically generated CSS sheet to be loaded onto a page, we ultimately want a meta link in the page head section like this:

<link rel='stylesheet' href='http://apache.lan/sfmblog/wp-admin/admin-post.php?action=bcw_dyno'
	type='text/css' media='all' />

If you are familiar with stylesheets in WordPress, you know we cannot just stick a meta link like that on our template, we must enqueue our style:

add_action('wp_enqueue_scripts', 'bcw_load_css');
function bcw_load_css() {
    $queried_object = get_queried_object();	// some requests do not result in an object or one with $ID
    // only enqueue for an actual object that has $ID and test page ID is 1427
    if ( $queried_object && isset( $queried_object->ID ) && 1427 == $queried_object->ID )
        wp_enqueue_style('dynotest', admin_url('admin-post.php?action=bcw_dyno'), array(), '0.10a');
}

How to enqueue styles is reasonably well documented elsewhereLink is to outside of this site, so I'm not going to explain the code in depth here. The key feature is we use the same link PHP to specify the stylesheet URL as we previously did for the simple anchor link, namely passing an 'action' parameter to admin-post.php. All the code above the wp_enqueue_style() line is to ensure the stylesheet is only loaded on the specific page for which it's intended.

fish hook

Action Hook


The above requests all result in $_RESULT['action'] having the value 'bcw_dyno'. The admin-post.php script uses this value to construct an action tag, which tag depends on whether the user is logged in to WordPress or not. If logged in, the action value is appended to 'admin_post_', so the complete action tag is 'admin_post_bcw_dyno'. If the user is not logged in, the generated action tag is 'admin_post_nopriv_bcw_dyno'.

Since we want our dynamic stylesheet loaded regardless of whether the user is logged in or not, we must add action functions for both tags (the same function may or may not be used in each case, depending on your specific need):

add_action('admin_post_bcw_dyno', 'bcw_send_css');
add_action('admin_post_nopriv_bcw_dyno', 'bcw_send_css');
function bcw_send_css() {
    // do something here...
    return;
}

In the above code, 'bcw_send_css' is the callback function (or request handler if you prefer) that actually outputs the CSS stylesheet content. We'll flesh it out a little while later here.

Request Handler


The request handler is the action callback function that runs whatever code you need to execute upon the GET or POST request. This function presumably needs to call some WordPress functions or there would be little point to this exercise. Since the callback function declaration properly resides in either a plugin or child theme functions.php, it is defined along with the complete WordPress environment loaded by admin-post.php. Your function thus has access to all WordPress functions, and other WordPress code has access to your callback function. In particular, the code in admin-post.php that calls your function is do_action(), which ultimately uses call_user_func_array().

Any content you want to be sent to the browser in response to the request is echoed or otherwise output from your request handler. The code may do anything else necessary as well, such as adding something to the database or any other task that's doable with PHP.
to Contents

For the browser to think it is getting an actual CSS file instead of a PHP coded artifact, we must send the proper header:

header('Content-type: text/css');

CSS Content

All that's left for our example to do now is output the CSS content. For the sake of an example, let's assume we need to use a WordPress function to construct the proper path to a background image (typically a static relative reference should suffice here):

$path = get_stylesheet_directory_uri().'/img/test-bg.png';

Now that we have the proper $path, we simply output the CSS content:

echo "#testarea {
	background-image: url( $path );
	background-color: #ddd;
	width: 500px;
	height: 350px;
	color: #0aa;
	padding: 2em;
    }";
to Contents

Code Summary


Here is the page template that the CSS is applied to. A page is created based on it and the ID noted for use in the PHP code in the child theme's functions.php. Unlike using a page template to access the WordPress environment, this template is simply a test bed for our dynamic CSS example. Any other page or post would have sufficed provided the 'testarea' div ID exists somewhere. It is not a vehicle for accessing the WordPress environment in this case, it's just a convenient test bed.


<div id="main" class="wrapper">
    <div id="primary" class="site-content">
	<div id="content" role="main">
	    <h2>CSS Test</h2>
	    <br>
	    <div id="testarea">This area should have a background image</div>
	</div>
    </div>
</div>
<?php get_footer();

The following is the applicable PHP code added to functions.php of the child theme (the page made from the above template ended up as ID 1427, which is used in the conditional enqueue script logic below):

// enqueue stylesheet when test page is requested
add_action('wp_enqueue_scripts', 'bcw_load_css');
function bcw_load_css() {
	$queried_object = get_queried_object();			// some requests do not result in an object
	if ( $queried_object && 1427 == $queried_object->ID )	// only enqueue for an actual object and test page ID 1427
		wp_enqueue_style('dynotest', admin_url('admin-post.php?action=bcw_dyno'), array(), '0.10a');
}

// output the CSS 'file'
add_action('admin_post_bcw_dyno', 'bcw_send_css');
add_action('admin_post_nopriv_bcw_dyno', 'bcw_send_css');
function bcw_send_css() {
	header('Content-type: text/css');
	$path = get_stylesheet_directory_uri().'/img/test-bg.png';
	echo "#testarea {
		background-image: url( $path );
		background-color: #ddd;
		width: 500px;
		height: 350px;
		color: #0aa;
		padding: 2em;
	}";
	return;
}
to Contents

«Back


Comments, feedback, and questions are always welcome. Email me at JavaScript needs to be enabled .