CAPTCHAs can greatly reduce automated registrations and other malicious form submissions. WordPress does not include a built-in CAPTCHA system, so many site owners install a plugin. For developers, though, it is often cleaner to implement a lightweight solution directly in a theme or custom plugin.
This article introduces a simple method based on Gregwar Captcha. It avoids a full-featured CAPTCHA plugin and keeps the implementation focused on your own business logic.
Install the URL router and CAPTCHA library
Gregwar Captcha is easy to use. You mainly need to instantiate CaptchaBuilder and expose a route that returns the generated image. In the example below, WordPress Dispatch is used to create the custom request URL.
require_once dirname(__FILE__) . '/../vendor/autoload.php';
use GregwarCaptchaCaptchaBuilder;
use TheFoldWordPressDispatch;
new Dispatch(array(
'captcha/([a-z0-9]+)' => function ($request, $type) {
$builder = new CaptchaBuilder();
$builder->build();
update_option($type . '_captcha', $builder->getPhrase());
header('Content-type: image/jpeg');
$builder->output();
exit;
}
));
Show the CAPTCHA in the form and refresh it on click
Displaying the CAPTCHA on the front end is straightforward. Set the image src to the custom route created above. The string after captcha/ identifies the CAPTCHA type so you can distinguish different forms if needed.
<div class="form-group">
<label for="captcha">Captcha</label>
<span class="text-msg"></span>
<img
alt="captcha"
id="captcha_img"
onclick="RefreshCode(this)"
src="<?= home_url('captcha/account'); ?>"
title="Click to refresh"
/>
<input type="text" name="captcha" value="" />
</div>
The JavaScript used to refresh the image is also very small. It simply appends a random string to the URL so the browser does not reuse a cached image.
<script>
function RefreshCode(obj) {
obj.src = obj.src + '?code=' + Math.random();
}
</script>
Compare the submitted code with the generated code
When the form is submitted, retrieve the value entered by the user and compare it with the code stored when the image was generated. If the comparison passes, continue with registration or whatever operation the form is meant to perform. If not, return an error message.
$account_captcha = $_POST['captcha'];
if (strcasecmp(get_option('account_captcha'), $account_captcha) === 0) {
// Continue with registration or the next business step.
} else {
$msg = array(
'registered' => false,
'message' => __('Invalid captcha code.', 'wizhi'),
);
}
That is the full workflow. The amount of code is quite small, and it keeps the CAPTCHA system focused on the form you are actually building instead of adding a larger general-purpose plugin.
