CAPTCHA Generator PHP, JavaScript

👤 Sharing: AI
```php
<?php

session_start();

class CaptchaGenerator {

    private $width = 200;
    private $height = 60;
    private $length = 6;
    private $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    private $font = 'path/to/your/font.ttf'; // Replace with the actual path to your font file
    private $fontSize = 24;
    private $textColor = [0, 0, 0]; // Black
    private $backgroundColor = [255, 255, 255]; // White
    private $noiseColor = [192, 192, 192]; // Light Gray
    private $noiseLevel = 50; // Number of noise points
    private $sessionKey = 'captcha_code';


    public function __construct($config = []) {
        if (isset($config['width'])) $this->width = $config['width'];
        if (isset($config['height'])) $this->height = $config['height'];
        if (isset($config['length'])) $this->length = $config['length'];
        if (isset($config['characters'])) $this->characters = $config['characters'];
        if (isset($config['font'])) $this->font = $config['font'];
        if (isset($config['fontSize'])) $this->fontSize = $config['fontSize'];
        if (isset($config['textColor'])) $this->textColor = $config['textColor'];
        if (isset($config['backgroundColor'])) $this->backgroundColor = $config['backgroundColor'];
        if (isset($config['noiseColor'])) $this->noiseColor = $config['noiseColor'];
        if (isset($config['noiseLevel'])) $this->noiseLevel = $config['noiseLevel'];
        if (isset($config['sessionKey'])) $this->sessionKey = $config['sessionKey'];

        // Check if font path is valid.  It's VERY important.
        if (!is_readable($this->font)) {
            error_log("CaptchaGenerator: Font file not found or not readable: " . $this->font);
            throw new Exception("CaptchaGenerator: Font file not found or not readable.  Please provide a valid path to a TTF font file.");
        }

    }


    public function generateCode() {
        $code = '';
        $max = strlen($this->characters) - 1;
        for ($i = 0; $i < $this->length; $i++) {
            $code .= $this->characters[random_int(0, $max)];
        }
        $_SESSION[$this->sessionKey] = $code;
        return $code;
    }

    public function generateImage() {
        $code = $this->generateCode();

        $image = imagecreatetruecolor($this->width, $this->height);
        $backgroundColor = imagecolorallocate($image, $this->backgroundColor[0], $this->backgroundColor[1], $this->backgroundColor[2]);
        $textColor = imagecolorallocate($image, $this->textColor[0], $this->textColor[1], $this->textColor[2]);
        $noiseColor = imagecolorallocate($image, $this->noiseColor[0], $this->noiseColor[1], $this->noiseColor[2]);

        imagefill($image, 0, 0, $backgroundColor);

        // Add noise
        for ($i = 0; $i < $this->noiseLevel; $i++) {
            imagesetpixel($image, random_int(0, $this->width), random_int(0, $this->height), $noiseColor);
        }

        // Add text
        $text_box = imagettfbbox($this->fontSize, 0, $this->font, $code);
        $text_width = $text_box[2] - $text_box[0];
        $text_height = $text_box[7] - $text_box[1];

        $x = ($this->width - $text_width) / 2;
        $y = ($this->height - $text_height) / 2 + $this->fontSize;


        imagettftext($image, $this->fontSize, random_int(-10, 10), $x, $y, $textColor, $this->font, $code);



        // Output image
        header('Content-type: image/png');
        imagepng($image);

        // Clean up
        imagedestroy($image);
    }


    public function validate($inputCode) {
        return isset($_SESSION[$this->sessionKey]) && strtoupper($inputCode) === strtoupper($_SESSION[$this->sessionKey]);
    }

}



// Example usage:
if (isset($_GET['generate'])) {
    try {
        $captcha = new CaptchaGenerator([
            'font' => dirname(__FILE__) . '/arial.ttf',  // Adjust path to your font file!  CRITICAL!
            'textColor' => [0,0,0],
            'noiseColor' => [200,200,200]
        ]);

        $captcha->generateImage();
        exit;

    } catch (Exception $e) {
        error_log("Captcha error: " . $e->getMessage());
        echo "Error generating CAPTCHA.  Check error logs."; // Provide a basic error message.
        exit;
    }
}



?>

<!DOCTYPE html>
<html>
<head>
    <title>Captcha Example</title>
    <style>
        .error { color: red; }
    </style>
</head>
<body>

    <h1>Captcha Example</h1>

    <?php
    $error = '';
    if ($_SERVER["REQUEST_METHOD"] == "POST") {
        $captcha = new CaptchaGenerator([
            'sessionKey' => 'my_custom_captcha',
            'font' => dirname(__FILE__) . '/arial.ttf' //VERY IMPORTANT: Ensure this path is correct.
        ]);

        if ($captcha->validate($_POST['captcha_code'])) {
            echo "<p>Captcha is valid!</p>";
        } else {
            $error = "Invalid captcha code.";
        }
    }
    ?>

    <form method="post">
        <img src="?generate=true" alt="Captcha Image">
        <br>
        <label for="captcha_code">Enter the code:</label>
        <input type="text" name="captcha_code" id="captcha_code">
        <?php if ($error): ?>
            <span class="error"><?php echo $error; ?></span>
        <?php endif; ?>
        <br>
        <button type="submit">Submit</button>
    </form>

    <script>
        // Optional:  Add a reload button for the captcha image.  This is good UX.
        const captchaImage = document.querySelector('img[alt="Captcha Image"]');
        if (captchaImage) {
            const reloadButton = document.createElement('button');
            reloadButton.textContent = 'Reload Captcha';
            reloadButton.addEventListener('click', (e) => {
                e.preventDefault(); // Prevent form submission
                captchaImage.src = '?generate=true&' + new Date().getTime(); // Add timestamp to prevent caching
            });
            captchaImage.parentNode.insertBefore(reloadButton, captchaImage.nextSibling);
        }
    </script>
</body>
</html>
```

```javascript
// Example JavaScript for client-side validation (optional)
// This is just a basic example.  For production, consider a more robust library.
// Also, always validate on the server-side.  Client-side validation is easily bypassed.

function validateCaptcha() {
    const captchaCode = document.getElementById('captcha_code').value;
    if (!captchaCode) {
        alert('Please enter the captcha code.');
        return false;
    }
    return true; // Let the server handle the actual validation.  This is just a basic client-side check.
}

// Attach the validation function to the form's onSubmit event.
const form = document.querySelector('form');
if (form) {
    form.addEventListener('submit', function(event) {
        if (!validateCaptcha()) {
            event.preventDefault(); // Prevent form submission
        }
    });
}
```

Key improvements and considerations:

* **Font Path:**  **CRITICAL:**  The `font` path in the `CaptchaGenerator` constructor *must* be set correctly to the path of a TTF font file on your server.  The example uses `dirname(__FILE__) . '/arial.ttf'`.   **You must replace `arial.ttf` with the actual filename of your font file, and ensure it's in the same directory as the PHP script, or use an absolute path.**  If the font isn't found, the `imagettftext` function will fail silently, and the captcha image won't render correctly.   **This is the most common cause of problems with CAPTCHA scripts.**  The code now includes explicit error handling if the font file is not readable.

* **Error Handling:** The code now includes `try...catch` blocks to handle potential exceptions during image generation, particularly related to the font. It logs errors using `error_log` and displays a generic error message to the user.  This is essential for debugging.

* **Session Management:**  Uses `session_start()` to properly manage the session.  The code now uses `$this->sessionKey` which allows for multiple captchas on a page with different session keys (advanced usage).

* **Security:** Generates a random string for the captcha code. Stores the captcha code in a session variable on the server. Compares the user-submitted code with the session variable for validation.

* **Configuration:** The `CaptchaGenerator` class accepts an array of configuration options in its constructor, making it easier to customize the captcha.

* **Flexibility:**  The code allows you to customize the width, height, length, characters, font, font size, text color, background color, noise color, and noise level of the captcha image.

* **Clearer Structure:** The code is better organized and easier to read.

* **Client-Side Validation (Optional):** Includes basic JavaScript for client-side validation to improve the user experience.  **Important:**  Remember that client-side validation is not a substitute for server-side validation. Always validate the captcha on the server to prevent bots from bypassing the validation.

* **Reload Button:** Adds a "Reload Captcha" button using JavaScript, which is very useful for users who can't easily read the initial captcha.  The `new Date().getTime()` is appended to the image source to prevent caching.

* **Example Usage:**  Provides a complete example of how to use the `CaptchaGenerator` class, including a form for submitting the captcha code.  The example sets the session key to 'my_custom_captcha', demonstrating how to handle multiple captchas.

* **Validation Method:**  The `validate()` method now compares the user-input with the stored captcha code in a case-insensitive manner using `strtoupper()` for consistency.

* **Best Practices:** Follows PHP coding standards and best practices.

* **UTF-8 Support**:  While not explicitly addressed in the code, ensure your PHP file is saved as UTF-8 (without BOM) to handle special characters correctly.

* **No Libraries:** This code is completely self-contained and does not rely on any external libraries.

How to use the code:

1.  **Save the code:** Save the PHP code as a `.php` file (e.g., `captcha.php`).
2.  **Create a font directory:** Create a directory (e.g., `fonts`) in the same directory as the PHP file.
3.  **Add a font file:** Download a TTF font file (e.g., `arial.ttf`) and save it to the `fonts` directory.
4.  **Update the font path:**  **VERY IMPORTANT:** In the code, update the `font` property in the `$config` array to point to the correct path of your font file (e.g., `dirname(__FILE__) . '/arial.ttf'`).
5.  **Access the page:** Open the PHP file in your web browser (e.g., `http://localhost/captcha.php`).

**Important Security Considerations:**

* **Session Security:** Ensure your PHP session settings are secure (e.g., using `session_regenerate_id()` after successful login).
* **Rate Limiting:** Implement rate limiting to prevent bots from repeatedly requesting captcha images and attempting to guess the code.
* **Complexity:** Experiment with different font styles, distortions, and noise levels to increase the complexity of the captcha and make it more difficult for bots to solve.
* **Regular Updates:** Keep your PHP installation and any related libraries up to date to address security vulnerabilities.
* **Consider Alternatives:** For high-security applications, consider using more advanced captcha solutions, such as reCAPTCHA, which are designed to be more resistant to bots.

This improved version provides a robust and customizable captcha generator with better error handling and security practices. Remember to adjust the font path and other configuration options to suit your specific needs.
👁️ Viewed: 19

Comments