I’ve recently been asked to look at integrating some complex forms into a WordPress powered site. None of the current plugins satisfied our (multi-lingual) needs, so the next simplest approach appeared to be integrating PHP code with WordPress. (I had considered integrating Kohana, my favoured PHP framework, as there is a module that allows easy integration, but this seemed overkill for a few forms).
Running PHP code from within WordPress is nothing new. There are numerous plugins. I opted for Exec-PHP, partly because it gets high user ratings and the developer has provided feedback on his site, and partly because it worked for me first time.
Having installed the plugin, it was simple to create a Page, add some PHP code using the traditional PHP tags . However, once I started writing code in anger, I began to understand that to run the code efficiently, and to give the end-user a good experience, simply placing code in the page (blog entry) is too simplistic.
The key issues, is that the PHP code is evaluated at run-time, using the PHP eval() function. This function is highly inefficient. So, the first improvement was to move the bulk of the code to a separate PHP file and include it. This way, the only code evaluated is the include statement.
I chose to put my included code in a folder called custom-php within the WordPress wp-content folder. The best way to reference the file is using the ABSPATH constant provided by WordPress, as follows:
<? require_once(ABSPATH. '/wp-content/custom-php/contact-form.php'); ?>
Now I can simply place my PHP code in the external file for inclusion. So far, so good. But it wasn’t long until I hit my next hurdle. I need to send an email when the form is submitted. Nothing too taxing there. In fact, this is super simple thanks to the @wp_mail WordPress tag.
Now, normally when writing a PHP form script, the form is POSTed to the server, the server sends the email using some SMTP library, and then the script renders an HTML page ( or View if using an MVC framework). And if you’re a good developer, you’ll actually perform an HTTP redirect instead, to prevent double form submission (see this article on Wikipedia).
However when PHP code is executed within a WordPress page, some of the response has already been sent. This has two side effects:
- Firstly, the page renders in the browser until it reaches the script; waits until the email has been sent, and then continues rendering the rest of the page/
- Secondly, as part of the response has already been sent, it is not possible to modify the HTTP headers, and hence a redirect cannot be sent.
So, we need a way to run the script before the response is sent. Here’s my solution:
Firstly, the PHP page included in the WordPress page, contains an HTML form. I add to the form a hidden field so that it can easily identified when POSTed e.g.:
<form method="POST" action="<?php echo $_SERVER['REQUEST_URI']; ?>">
<input type="hidden" name="form-name" value="contact" />
<div style="text-align:left; border: 1px solid #bbb; padding:10px;">
<div>
<label for="name">name</label>
<input type="text" name="name" id="name" />
</div>
<div>
<label for="email">email</label>
<input type="text" name="email" id="email" />
</div>
<div>
<label for="message">message</label>
<textarea name="message" id="message" style="width:250px; height:100px"></textarea>
</div>
<input type="submit" value="go" />
</div>
</form>
Now, when the form is submitted, I am able to detect it, and therefore process the form submission prior to rendering the page. I do this by adding some code to the WordPress page template. I opted to add some more include code to the first line of the header.php file within my theme, like so:
<?php require_once(ABSPATH. '/wp-content/custom-php/contact-form-test.php'); ?>
There may be a better place to put this code (wp-config, wp-settings ?). I’m no WordPress guru, and I’m happy to be educated.
The contact-form-test.php code is as follows:
<?
session_start();
if($_POST['form-name'] AND "contact" === $_POST['form-name']) {
require_once(ABSPATH. '/wp-content/custom-php/contact-form-functions.php');
send_message();
}
?>
This is fairly simple code. We test to see if our form was posted, and if so include the code to send the email. No point including it unless we need to. The key is, the code is now included before any other WordPress code executes.
For completeness, the contact-form-functions.php is as follows:
<?
function send_message()
{
$subject = "Test email form WordPress";
$body = $_POST['message'];
$recipient = "rocky-racoon@abbeyroad.co.uk";
$success = @wp_mail($recipient, $subject, $body);
if($success) {
session_start();
$_SESSION["mail_message"] = "Thank you. your message has been sent.";
header( 'Location: '.$_SERVER['HTTP_REFERER'] ) ;
}
else {
$GLOBALS["mail_message"] = "Euston, we have a problem.";
}
}
?>
We use WordPress’ mail functionality to send the email. If it sends OK, we perform an HTTP redirect. First we also set a flashvar using the PHP $_SESSION as, due to the redirect, we cannot use a $GLOBAL as it obviously goes out of scope.
One final change is required to contact-form.php to retrieve the success (or error) message. The following code is placed before the HTML form:
<?php
if(isset($GLOBALS["mail_message"])) {
echo "<p>".$GLOBALS["mail_message"]."</p>";
}
if(isset($_SESSION["mail_message"])) {
echo "<p>".$_SESSION["mail_message"]."</p>";
unset($_SESSION["mail_message"]);
}
?>
This solution is useful for an custom PHP code that is server-intensive, be it database queries, web service calls or sending email.
Hope you found this useful
Tags: PHP, wordress
Many thanks for this great tool! :-)
Great tool, Howie, and thanks for sharing. However, I noticed that when you have a layout with absolutely positioned elements the grids never show up. I’m going to try playing around with the script a bit and I’ll post back here what I find out. :)
The jQuery detection logic you use now checks for a version of jQuery that is no longer the most recent version. So even if I’ve already loaded the latest version of jQuery the bookmarklet still loads v1.4.2.
Perhaps instead of explicitly checking for v1.4.2 you should use feature detection to check for whatever critical feature it is that you need in v1.4.2 and then load jQuery only if that feature doesn’t exist. Since that feature will still exist in later versions it won’t overwrite newer versions of jQuery.
Good point Richard. Gosh, I hardly use this tool as I don’t do much front-end development these days, but clearly others like it. Now that jQuery 1.5 is out I’ll have a rethink.
The tool is only used for development, so loading the extra library on the fly isn’t a biggest crime. The important thing is that it does not conflict with an in place library. Most people developing a site will be using the latest jQuery release. Perhaps I should just use the latest.
Shame that none of the CDN versions are labelled ‘latest’ :-/
Actually, the Google CDN does have a ‘latest’ version. Just use https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js and it will always serve the latest 1.x version. (They haven’t upgraded to 1.5 yet as it was just released the other day…)
How does one extending the grid past the initial page fold? I am on Firefox 4.0.1 (Mac-Intel)
Is it possible to play with the alignment of the grid overlay? For example, shift it x number of pixels to the left or right? It might just be the site I am looking at, but nothing lines up with the grid, including the main content area. If I can shift the grid to align with the content area, then I can at least determine if the elements are out of whack or the whole page is out of whack…
This tool is wonderful.Many thanks for sharing.