While developing a WordPress plugin I stumbled upon the following annoying problem:

My plugin consists of several interlinked pages with forms on them, like a wizzard. They’re integrated into the WordPress admin environment by means of the handy add_menu_page and add_submenu_page-functions. I want to check the result of the forms and advance the user to the next page using wp_redirect whenever I have sufficient input. Pretty easy for a standalone php webpage: check the $_POST-array, do whatever you have to do and redirect the browser to the next page when you’re satisfied before outputting any html (not even a space or carriage return).

However, my pages are never called directly. They exist inside WordPress’ admin.php page, and admin.php outputs the top part of the page before my code is even called. This means that the HTTP-redirect will always fail. You simply cannot send HTTP-Headers somewhere in the middle of the HTTP-body (i.e. the actual html).

Thinking about it, I thought I had two options:

  1. hook into WordPress’ init or send_headers hooks. These are called before any html-output.
  2. call my php-files directly in the forms action and replicate all admin.php-code inside them.

I didn’t like the first option ’cause init or send_headers are called for every pageview, and I’d have to find out whether the request originated from one of my forms. Plus I’d have to seperate all application logic in one central place, away from the actual pages. I didn’t like the second option ’cause I’d have to track the WordPress core pretty tightly to make sure none of my replicated code contained any newfound WordPress bugs. However, upon inspecting admin.php, I found a third option.

admin.php won’t output anything if you call your plugin page with an optional &noheader GET argument, but it will setup the whole WordPress environment for you. I’m quite happy: all I have to do is change my forms action from this

<form method="post" action="admin.php?page=your_plugin_page.php">

to this

<form method="post" action="admin.php?page=your_plugin_page.php&noheader">

and I can redirect to my hearts content. You can make it draw the menu with this bit of code:

/* this strange thing makes WordPress output the admin header whenever we need it
   we turn the header off with "&noheader" so we can redirect with WP_Redirect
*/
if (isset($_GET['noheader']))
	require_once(ABSPATH . 'wp-admin/admin-header.php');

after you’ve done your validation and possible redirection.