Session 3.7 – Regular Expressions and Server-Side Validation

Module 3: JavaScript and Form Validation | Duration: 1 hr

Learning Objectives

By the end of this session, students will be able to:

  • Understand the fundamentals of regular expressions
  • Write and test regex patterns for common validation scenarios
  • Implement regex-based validation in JavaScript and PHP
  • Implement comprehensive server-side validation
  • Apply security best practices for input validation
  • Combine client-side and server-side validation effectively

Introduction

Regular expressions (regex) are powerful patterns used for matching and manipulating text. Combined with proper server-side validation, they form a crucial defense layer for web applications.

Key Insight

Client-side validation provides user convenience, but server-side validation is essential for security. Never trust client input - always validate on the server!

Regular Expression Basics

Regular expressions consist of literal characters and special metacharacters that define search patterns.

Metacharacters
Character Classes
. - Any character (except newline)
\d - Digit [0-9]
\D - Non-digit
\w - Word character [a-zA-Z0-9_]
\W - Non-word character
\s - Whitespace
\S - Non-whitespace
Quantifiers
* - 0 or more times
+ - 1 or more times
? - 0 or 1 time
{n} - Exactly n times
{n,} - n or more times
{n,m} - Between n and m times
Anchors
^ - Start of string
$ - End of string
\b - Word boundary
\B - Not a word boundary
Groups & Ranges
[abc] - Any of a, b, or c
[^abc] - Not a, b, or c
[a-z] - Range a to z
(abc) - Capture group
| - OR operator

Common Validation Patterns

Email Address
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/

Example matches:
✓ user@example.com
✓ john.doe@company.co.uk
✗ invalid@
✗ @invalid.com
Phone Number
/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/

Example matches:
✓ 123-456-7890
✓ (123) 456-7890
✓ +1234567890
✗ 12-34-56
Strong Password
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/

Requirements:
- At least 8 characters
- 1 uppercase letter
- 1 lowercase letter
- 1 number
- 1 special character
URL
/^(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b/

Example matches:
✓ https://example.com
✓ www.example.com
✓ example.com/page
✗ not a url
Alphanumeric
/^[a-zA-Z0-9]+$/

Example matches:
✓ User123
✓ ABC123xyz
✗ user-name
✗ user_123
Date (YYYY-MM-DD)
/^\d{4}-\d{2}-\d{2}$/

Example matches:
✓ 2024-12-31
✓ 2023-01-01
✗ 12-31-2024
✗ 2024/12/31

PHP Regex Functions

preg_match() - Pattern Matching
<?php
// Validate email
$email = "user@example.com";
if (preg_match("/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/", $email)) {
    echo "Valid email";
} else {
    echo "Invalid email";
}

// Extract phone number parts
$phone = "123-456-7890";
if (preg_match("/^(\d{3})-(\d{3})-(\d{4})$/", $phone, $matches)) {
    echo "Area code: " . $matches[1];  // 123
    echo "Exchange: " . $matches[2];   // 456
    echo "Number: " . $matches[3];     // 7890
}
?>
preg_replace() - Pattern Replacement
<?php
// Remove all non-numeric characters
$phone = "(123) 456-7890";
$clean = preg_replace("/[^0-9]/", "", $phone);
echo $clean;  // 1234567890

// Format phone number
$phone = "1234567890";
$formatted = preg_replace("/^(\d{3})(\d{3})(\d{4})$/", "($1) $2-$3", $phone);
echo $formatted;  // (123) 456-7890

// Strip HTML tags (use strip_tags() in production)
$text = "<p>Hello <b>World</b></p>";
$clean = preg_replace("/<[^>]*>/", "", $text);
echo $clean;  // Hello World
?>
preg_split() - Split by Pattern
<?php
// Split by multiple delimiters
$text = "apple,orange;banana|grape";
$fruits = preg_split("/[,;|]/", $text);
print_r($fruits);
// Array([0]=>apple [1]=>orange [2]=>banana [3]=>grape)

// Split by whitespace
$text = "Hello   World    PHP";
$words = preg_split("/\s+/", $text);
print_r($words);
// Array([0]=>Hello [1]=>World [2]=>PHP)
?>

Comprehensive Server-Side Validation

Complete Validation Example
<?php
// validation.php - Server-side validation functions

class Validator {
    private $errors = [];

    // Validate required field
    public function required($field, $value) {
        if (empty(trim($value))) {
            $this->errors[$field] = ucfirst($field) . " is required";
            return false;
        }
        return true;
    }

    // Validate email
    public function email($field, $value) {
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            $this->errors[$field] = "Invalid email format";
            return false;
        }
        return true;
    }

    // Validate minimum length
    public function minLength($field, $value, $min) {
        if (strlen($value) < $min) {
            $this->errors[$field] = ucfirst($field) . " must be at least $min characters";
            return false;
        }
        return true;
    }

    // Validate maximum length
    public function maxLength($field, $value, $max) {
        if (strlen($value) > $max) {
            $this->errors[$field] = ucfirst($field) . " must not exceed $max characters";
            return false;
        }
        return true;
    }

    // Validate strong password
    public function strongPassword($field, $value) {
        $pattern = "/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/";
        if (!preg_match($pattern, $value)) {
            $this->errors[$field] = "Password must contain uppercase, lowercase, number, and special character";
            return false;
        }
        return true;
    }

    // Validate phone number
    public function phone($field, $value) {
        $pattern = "/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/";
        if (!preg_match($pattern, $value)) {
            $this->errors[$field] = "Invalid phone number format";
            return false;
        }
        return true;
    }

    // Validate numeric
    public function numeric($field, $value) {
        if (!is_numeric($value)) {
            $this->errors[$field] = ucfirst($field) . " must be numeric";
            return false;
        }
        return true;
    }

    // Validate alphanumeric
    public function alphanumeric($field, $value) {
        if (!preg_match("/^[a-zA-Z0-9]+$/", $value)) {
            $this->errors[$field] = ucfirst($field) . " must be alphanumeric";
            return false;
        }
        return true;
    }

    // Validate URL
    public function url($field, $value) {
        if (!filter_var($value, FILTER_VALIDATE_URL)) {
            $this->errors[$field] = "Invalid URL format";
            return false;
        }
        return true;
    }

    // Get all errors
    public function getErrors() {
        return $this->errors;
    }

    // Check if validation passed
    public function isValid() {
        return empty($this->errors);
    }
}
?>
Using the Validator Class
<?php
// process_form.php
require_once 'validation.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $validator = new Validator();

    // Get form data
    $name = $_POST['name'] ?? '';
    $email = $_POST['email'] ?? '';
    $phone = $_POST['phone'] ?? '';
    $password = $_POST['password'] ?? '';
    $age = $_POST['age'] ?? '';

    // Sanitize input
    $name = htmlspecialchars(trim($name));
    $email = filter_var(trim($email), FILTER_SANITIZE_EMAIL);
    $phone = htmlspecialchars(trim($phone));

    // Validate name
    $validator->required('name', $name);
    $validator->minLength('name', $name, 2);
    $validator->maxLength('name', $name, 50);

    // Validate email
    $validator->required('email', $email);
    $validator->email('email', $email);

    // Validate phone
    $validator->required('phone', $phone);
    $validator->phone('phone', $phone);

    // Validate password
    $validator->required('password', $password);
    $validator->strongPassword('password', $password);

    // Validate age
    $validator->required('age', $age);
    $validator->numeric('age', $age);

    // Check validation results
    if ($validator->isValid()) {
        // Process the form (save to database, etc.)
        echo "Form validated successfully!";

        // Example: Save to database
        // $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
        // ... database code ...
    } else {
        // Display errors
        $errors = $validator->getErrors();
        foreach ($errors as $field => $error) {
            echo "<div class='alert alert-danger'>$error</div>";
        }
    }
}
?>
Input Sanitization
<?php
// Always sanitize user input before validation

// Remove HTML tags
$clean = strip_tags($input);

// HTML special characters
$clean = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');

// Filter functions
$email = filter_var($input, FILTER_SANITIZE_EMAIL);
$url = filter_var($input, FILTER_SANITIZE_URL);
$int = filter_var($input, FILTER_SANITIZE_NUMBER_INT);

// Remove specific characters
$clean = preg_replace("/[^a-zA-Z0-9]/", "", $input);

// Database escaping (if not using prepared statements)
$clean = mysqli_real_escape_string($connection, $input);
?>

Validation Best Practices

Do's
  • Always validate on the server side
  • Use prepared statements for database queries
  • Sanitize input before validation
  • Provide clear error messages
  • Use whitelist validation (allow known good)
  • Validate data types and ranges
  • Use HTTPS for sensitive data
  • Hash passwords with password_hash()
  • Implement rate limiting
  • Log validation failures
Don'ts
  • Never trust client-side validation alone
  • Don't use blacklist validation (block known bad)
  • Don't reveal system information in errors
  • Don't store passwords in plain text
  • Don't use outdated hash functions (MD5, SHA1)
  • Don't concatenate SQL queries with user input
  • Don't validate after database insertion
  • Don't rely on JavaScript for security
  • Don't disable error reporting in production
  • Don't expose validation logic to clients
Security Considerations
  • SQL Injection: Use prepared statements and parameterized queries
  • XSS (Cross-Site Scripting): Escape output with htmlspecialchars()
  • CSRF (Cross-Site Request Forgery): Use CSRF tokens
  • File Upload: Validate file type, size, and content
  • Rate Limiting: Prevent brute force attacks
  • Input Length: Limit input size to prevent DoS

Session Summary

Key Points
  • Regular expressions provide powerful pattern matching for validation
  • PHP offers preg_match(), preg_replace(), and preg_split() for regex operations
  • Server-side validation is essential for security
  • Always sanitize input before validation
  • Combine client-side convenience with server-side security
  • Use prepared statements to prevent SQL injection
  • Implement comprehensive validation for all user inputs
  • Follow security best practices to protect against common vulnerabilities
Next Session Preview

In the next session, we will explore Introduction to Server-Side Scripting, understanding how server-side programming differs from client-side, and begin working with PHP for dynamic web applications.