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.