Generate a CAPTCHA Image in PHP (GD Library)

CAPTCHA images help to prevent automated form submission. Learn how to add a CAPTCHA image in PHP as an extra layer to your HTML forms and prevent automated form submission.

Generate a CAPTCHA Image in PHP

CAPTCHA images are used in online forms to confirm that the form is submitted by a real human and not some robot. CAPTCHA images contain some random characters with some distortion, blur, and some random lines that are not readable by a robot. Thus ensuring the form is submitted by a human. 

In this post, we will create a PHP CAPTCHA image class with some image manipulation functions. We will then use this class to generate our CAPTCHA image. The main functions to mention in this class are add_noise(), add_blur(), and get_random_string().

Files we are going to create for this post are:

  1. captcha.php: The PHP class to create the captcha image.
  2. captcha-image.php: The PHP script to create an instance of the captcha class and generate a captcha image.
  3. index.php: An index page that contains an HTML form with a CAPTCHA field.
  4. style.css: The CSS styles for our HTML page and CAPTCHA form.

How to Generate and Validate a CAPTCHA Image in PHP

Generating a CAPTCHA image in PHP can be achieved in a few simple steps. The steps to generate a CAPTCHA image in PHP are:

  • Create a PHP image CAPTCHA class for image manipulation. This file will generate a CAPTCHA image with a random string.
  • Create another PHP file to receive the submitted form for processing and compare the CAPTCHA string passed in the form against the string generated by the CAPTCHA class.
  • Create a user interface with an HTML form for submission, along with the CAPTCHA string.
  • Add CSS styles for the user interface and test the CAPTCHA verification implementation.
 

Step 1: Create Image Captcha Class in PHP (GD Library)

Our captcha class will be using the GD Library to generate an image. This class will house methods for generating a GD image resource, adding noise to the image, adding blur to the image, and returning the image to the browser. The constructor method of this class will generate a CAPTCHA image with the provided background color. The class definition is as follows:

  • $image: Holds the image resource created by the GD library.
  • $color: Holds the color used for the captcha text.
  • $width: Holds the width of the captcha image.
  • $height: Holds the height of the captcha image.

And the following methods will be used:

  • add_noise(): Adds noise to the image by setting random pixels to a specified color.
  • add_blur(): Applies a gaussian blur filter to the image multiple times.
  • get_image(): Generates the captcha text, renders the image as a JPEG binary string, and destroys the image resource.
  • get_decimal_color: Converts a hexadecimal color string to its RGB equivalent and returns an array of colors containing "red", "green", and "blue" colors.
  • get_true_color(): Allocates a color for the image using RGB values and returns an integer color.
  • get_random_string(): Generates and returns a random string of the specified length using alphanumeric characters.

captcha.php

<?php

class captcha
{

/**
*
* @var GdImage|false
*/

private $image;
/**
*
* @var int
*/
private $color;

/**
*
* @var int
*/
public $width;

/**
*
* @var int
*/
public $height;

/**
*
* @param int $width
* @param int $height
* @param string $background
* @param string $color
*/
function __construct(int $width = 120, int $height = 60, string $background = '#4F5B93', string $color = '#ffffff')
{

// Create captcha image, Set height & width, text color when object is initialized
$this->image = imagecreatetruecolor($width, $height);
$this->width = intval($width);
$this->height = intval($height);
$color = $this->get_decimal_color($color); // Get decimal color value from hex color value
$this->color = $this->get_true_color($color); // Set true color from decimal color

$background = $this->get_decimal_color($background);
$background = $this->get_true_color($background);

// Fill background color to image
imagefill($this->image, 0, 0, $background);
}

/**
* Function to add noise to image
* @param string $color
* @return void
*/
public function add_noise(string $color = '#8892BF'): void
{
$font_color = $this->get_decimal_color($color);
$font_color = $this->get_true_color($font_color);

// Loop x co-ordinates of image
for ($r = 0; $r < $this->width; $r++) {
// Loop y co-ordinates of image
for ($c = 0; $c < $this->height; $c++) {
// Add noise on step of 1 pixel, skipping one pixel and noise to next pixel
if (mt_rand(0, 1) == 1)
imagesetpixel($this->image, $r, $c, $font_color);
}
}
}

/**
* Adds blur filter to image
* @param int $count
* @return void
*/
public function add_blur(int $count = 3): void
{
$count = intval($count) > 10 ? 10 : $count;
for ($i = 1; $i <= $count; $i++) {
imagefilter($this->image, IMG_FILTER_GAUSSIAN_BLUR);
}
}

/**
* Returns the image as binary string
* @return string
*/
public function get_image(): string
{
$this->add_captcha_text();

$image = imagejpeg($this->image, NULL, 100);

imagedestroy($this->image);

return $image;
}

/**
* Returns decimal equivalent of hex color string
* @param string $color
* @return array
*/
private function get_decimal_color(string $color): array
{
$color = str_starts_with($color, '#') !== false ? substr($color, 1) : $color;

if (strlen($color) === 6) {
$red = hexdec(substr($color, 0, 2));
$green = hexdec(substr($color, 2, 2));
$blue = hexdec(substr($color, 4, 2));
} else {
$red = hexdec(str_repeate(substr($color, 0, 1), 2));
$green = hexdec(str_repeate(substr($color, 1, 1), 2));
$blue = hexdec(str_repeate(substr($color, 2, 1), 2));
}

return ['red' => $red, 'green' => $green, 'blue' => $blue];
}

/**
*
* @param array $color
* @return int
*/
private function get_true_color(array $color): int
{
return imagecolorallocate($this->image, $color['red'], $color['green'], $color['blue']);
}

/**
* Generates and returns random string
* @param int $length
* @return string
*/
private function get_random_string(int $length): string
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$random_string = '';

for ($i = 0; $i < $length; $i++) {
$random_string .= $characters[mt_rand(0, strlen($characters) - 1)];
}

return $random_string;
}

/**
* Adds random string as text to captcha image
* @return void
*/
private function add_captcha_text(): void
{
$font_file = realpath(dirname(__FILE__) . '/fonts/sketch_block.ttf');
$font_size = $this->height * 0.6;
$font_color = $this->color;

$random_string = $this->get_random_string(5);

$ttf_box = imagettfbbox($font_size, 0, $font_file, $random_string);
$xr = abs(max($ttf_box[2], $ttf_box[4]));
$xy = abs(max($ttf_box[5], $ttf_box[7]));
$x = intval(($this->width - $xr) / 2);
$y = intval(($this->height + $xy) / 2);

imagettftext($this->image, $font_size, 0, $x, $y, $font_color, $font_file, $random_string);

$_SESSION['captcha'] = $random_string;
}
}
 

Step 2: Create a CAPTCHA Image using The CAPTCHA Class

Our captcha-image.php file will create a CAPTCHA image and return the image as the response to the page.

  • Create an instance of the captcha class, providing the width and height for the CAPTCHA image.
  • Add noise to the image.
  • Add blur to the image.
  • Set the Content-Type of the page to image/jpeg.
  • Return the image as a response.

captcha-image.php

<?php
if (!session_id()) {
session_start();
}

include_once 'captcha.php';

$captcha = new captcha(280, 60);
$captcha->add_noise(); // Add noise to captcha image
$captcha->add_blur(); // Blur captcha image

header('Content-Type: image/jpeg');

$captcha_image = $captcha->get_image();

die($captcha_image);
 

Step 3: Create an HTML Form with a CAPTCHA Field

Create an HTML page with a form containing the CAPTCHA image and an input field to enter the CAPTCHA. Initially, load the image returned from the captcha-image.php file. Then refresh the image with a JavaScript click event. We also validate the CAPTCHA in this file.

index.php

<?php
if (!session_id()) {
session_start();
}

if (!empty($_POST)) {
$_SESSION['captcha_status'] = intval($_POST['captcha'] === $_SESSION['captcha']);
header(sprintf('Location: %s', $_SERVER['REQUEST_URI']));
exit();
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Generate Captcha Image in PHP - Demo</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport"/>
<link rel="stylesheet" href="css/style.css"/>

<script type="text/javascript">
function refresh_captcha() {
let random = Math.random();
document.querySelector(".captcha-image").src = "captcha-image.php?" + random;
}
</script>
</head>
<body>
<section class="section py-4">
<div class="container">
<div class="captcha-box">
<?php
if (isset($_SESSION['captcha_status'])) {
if ($_SESSION['captcha_status']) {
?>
<div class="captcha-message">Captcha verified</div>
<?php
} else {
?>
<div class="captcha-message error">Captcha verification failed</div>
<?php
}
unset($_SESSION['captcha_status']);
} ?>
<form name="captcha_form" method="POST">
<img class="captcha-image" src="captcha-image.php"/>
<div><input type="text" name="captcha" class="form-control"/></div>
<p>Can't read? Click <a href="javascript:refresh_captcha();">here</a> to refresh</p>
<button type="submit" class="btn btn-green btn-block">Verify</button>
</form>
</div>
</div>
</section>
</body>
</html>
 

Step 4: Add CSS CAPTCHA Interface Styles

Add CSS styles for the entire HTML page and CAPTCHA form.

style.css

* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
}
body {
background-color: #f6f6f6;
font-family: "Segoe UI", "Roboto", "Helvetica", sans-serif;
font-size: 15px;
font-weight: normal;
font-style: normal;
line-height: 1.5;
}
.container {
width: 100%;
max-width: 1140px;
margin-right: auto;
margin-left: auto;
padding-right: 15px;
padding-left: 15px;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.form-control {
padding: 10px;
border: 1px solid #ddd;
width: 100%;
margin-bottom: 5px;
color: #444;
}
.btn {
display: inline-block;
padding: 5px 10px;
cursor: pointer;
font: inherit;
}
.btn-block {
width: 100%;
}
.btn-green {
background-color: #00a65a;
border: 1px solid #00a65a;
color: #ffffff;
}
.captcha-box {
max-width: 300px;
background: #dddddd;
padding: 0.5rem;
}
.captcha-message {
background: #ffffff;
padding: 0.25rem;
text-align: center;
color: #009549;
border: 1px solid #009549;
margin-bottom: 1rem;
}
.captcha-message.error {
color: #dd1a16;
border: 1px solid #dd1a16;
}

We now have a basic CAPTCHA image implementation in a PHP application. This implementation can be used for basic use cases and can be improved by adding noise and random lines to image.