Core PHP 9 programs with output 15 Q/A 5 MCQs

Learning Objectives – after this chapter you will be able to:

  • Explain client-side and server-side validation and when each is needed.
  • Write regular expressions using quantifiers, metacharacters and character classes.
  • Use preg_match(), preg_match_all(), preg_replace(), preg_replace_callback(), preg_split() and preg_grep().
  • Validate a registration form on the server and show the error messages on the same page.
Client-Side and Server-Side Validation

User input should be validated in the browser whenever possible (with HTML5 attributes such as required, type="email", pattern, or with JavaScript). Browser validation is faster and reduces the server load.

But browser validation can be switched off or bypassed by anyone, so you must always validate again on the server – especially when the input is inserted into a database. A good way to validate a form on the server is to post the form to itself instead of jumping to a different page. The user then gets the error messages on the same page as the form, which makes it easier to find and correct the mistakes.

Regular Expressions

A regular expression (regex) is a pattern that describes a set of strings. It confirms that an entry matches a pattern – for example the format of an e-mail address, a telephone number, a PIN code or a password. In PHP, regular expressions are written between delimiters, usually slashes: "/pattern/flags". The flag i makes the match case-insensitive.

Quantifiers

Quantifiers specify how many times a character, or a group of characters in parentheses, may repeat. A quantifier always refers to the item immediately to its left.

QuantifierMeaning
*0 or more occurrences
+1 or more occurrences
?0 or 1 occurrence (optional)
{n}exactly n occurrences
{min,}at least min occurrences
{min,max}between min and max occurrences
Metacharacters

Characters with special meaning in a regular expression are called metacharacters: ^ $ . * + ? { } [ ] ( ) | and \. To match one of them literally, escape it with a backslash, e.g. \. for a dot.

SymbolMeaning
.any single character (except a new line)
^start of the string (inside [ ] it means "not")
$end of the string
|alternation – either the left or the right part, e.g. cat|dog
( )grouping and capturing a sub-pattern
[ ]a character class
\escapes the next character
Character classes

A character class, written in square brackets, matches one character from the list. [aeiou] matches any vowel. Ranges use a hyphen: [0-9] any digit, [a-z] any lowercase letter, [A-Z] any uppercase letter, [a-zA-Z0-9] any letter or digit. [^0-9] matches any character that is not a digit. To include a hyphen, put it first: [-.?].

Predefined character classes
ClassMatches
\dany decimal digit – same as [0-9]
\Dany non-digit
\wany word character – letters, digits and underscore [a-zA-Z0-9_]
\Wany non-word character
\sany white-space character (space, tab, new line)
\Sany non-white-space character
\ba word boundary
Matching Patterns – preg_match()

preg_match() performs Perl-compatible pattern matching on a string:

Syntax
preg_match(pattern, subject [, &matches [, flags [, offset]]])

It returns 1 if a match is found and 0 otherwise. The optional $matches array receives the text that matched: $matches[0] is the whole match, $matches[1] the first parenthesised group, and so on. Let us search the string "Hello World!" for the letters "ell":

File: htdocs/phpbook/ch12/match1.php
<?php
if (preg_match("/ell/", "Hello World!", $matches)) {
    echo "Match was found <br>";
    echo $matches[0];
}
?>
OUTPUT browser · http://localhost/phpbook/ch12/match1.php
Match was found
ell

The next pattern looks for the letters "ll" followed by any characters (.*):

File: htdocs/phpbook/ch12/match2.php
<?php
if (preg_match("/ll.*/", "The History of Halloween", $matches)) {
    echo "Match was found <br>";
    echo $matches[0];
}
?>
OUTPUT browser · http://localhost/phpbook/ch12/match2.php
Match was found
lloween
Validating a strong password

A popular use of regular expressions is validation. The example checks whether a password is strong: at least 8 characters with at least one lowercase letter, one uppercase letter and one digit.

File: htdocs/phpbook/ch12/password.php
<?php
$passwords = ["Fyfjk34sdfjfsjq7", "secret12", "Short1A"];
$pattern = "/^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).*$/";
foreach ($passwords as $password) {
    if (preg_match($pattern, $password)) {
        echo "$password : Your password is strong.<br>";
    } else {
        echo "$password : Your password is weak.<br>";
    }
}
?>
OUTPUT browser · http://localhost/phpbook/ch12/password.php
Fyfjk34sdfjfsjq7 : Your password is strong.
secret12 : Your password is weak.
Short1A : Your password is weak.

^ and $ anchor the pattern at the start and the end of the string, and .* at both ends allows any characters. In between are look-ahead groups (?=…) – "the following text must be like this" – which do not consume characters, so the order of the requirements does not matter:

  • (?=.{8,}) – there are at least 8 characters;
  • (?=.*\d) – somewhere there is a digit;
  • (?=.*[a-z]) and (?=.*[A-Z]) – somewhere there is a lowercase and an uppercase letter.
Validating an e-mail address
File: htdocs/phpbook/ch12/email.php
<?php
$emails = ["firstname.lastname@aaa.bbb.com", "1abc@site.com", "wrong@site"];
$regexp = "/^[^0-9][A-z0-9_]+([.][A-z0-9_]+)*[@][A-z0-9_]+([.][A-z0-9_]+)*[.][A-z]{2,4}$/";
foreach ($emails as $email) {
    if (preg_match($regexp, $email)) {
        echo "$email : Email address is valid.<br>";
    } else {
        echo "$email : Email address is <u>not</u> valid.<br>";
    }
}
?>
OUTPUT browser · http://localhost/phpbook/ch12/email.php
firstname.lastname@aaa.bbb.com : Email address is valid.
1abc@site.com : Email address is not valid.
wrong@site : Email address is not valid.

This expression rejects an address that starts with a digit and allows several dots in the user name and domain. In real projects it is simpler and more reliable to use filter_var($email, FILTER_VALIDATE_EMAIL) (Chapter 21).

For speed, preg_match() stops at the first match. preg_match_all() finds all matches and returns their number:

File: htdocs/phpbook/ch12/matchall.php
<?php
$text = "Call 9876543210 or 9123456789; office 0755-2551234.";
$n = preg_match_all("/\b[6-9]\d{9}\b/", $text, $m);
echo "$n mobile numbers found:<br>";
echo implode("<br>", $m[0]);
?>
OUTPUT browser · http://localhost/phpbook/ch12/matchall.php
2 mobile numbers found:
9876543210
9123456789
Replacing Patterns – preg_replace()

preg_replace() looks for sub-strings that match a pattern and replaces them with new text:

Syntax
preg_replace(pattern, replacement, subject [, limit])

It returns the changed string (or an unchanged copy if nothing matched). In the replacement, back references $1, $2 … insert the text matched by the corresponding parenthesised group; $0 is the whole match.

File: htdocs/phpbook/ch12/replace.php
<?php
// replace the year in a copyright notice
echo preg_replace("/([Cc]opyright) 200(3|4|5|6)/", "$1 2026", "Copyright 2005");
echo "<br>";
// change the date format from yyyy-mm-dd to dd/mm/yyyy
echo preg_replace("/(\d+)-(\d+)-(\d+)/", "$3/$2/$1", "2026-10-22");
echo "<br>";
// arrays of patterns and replacements
$search  = ["/\s+/", "/(\d{4})-(\d{2})-(\d{2})/"];
$replace = [" ", "$3/$2/$1"];
echo preg_replace($search, $replace, "Posted   by John   on 2026-02-15");
?>
OUTPUT browser · http://localhost/phpbook/ch12/replace.php
Copyright 2026
22/10/2026
Posted by John on 15/02/2026
preg_replace_callback() instead of the /e modifier

Old PHP versions had an /e modifier that executed the replacement as PHP code – for example to convert a matched name to capitals. It was a serious security risk and was removed in PHP 7.0. Use preg_replace_callback(), which calls a function for every match:

File: htdocs/phpbook/ch12/callback.php
<?php
$string = "Posted by John | 2026-02-15 02:43:41";
echo preg_replace_callback(
    "/(Posted by) (\w+)/",
    fn($m) => $m[1] . " " . strtoupper($m[2]),
    $string
);
?>
OUTPUT browser · http://localhost/phpbook/ch12/callback.php
Posted by JOHN | 2026-02-15 02:43:41
Splitting and Filtering – preg_split() and preg_grep()

preg_split() breaks a string apart at a pattern – useful when the separator is not one fixed string. preg_grep() returns the array elements that match a pattern.

File: htdocs/phpbook/ch12/split.php
<?php
$keywords = preg_split("/[\s,]+/", "php, regular expressions,  mysqli");
echo "<pre>";
print_r($keywords);
$names = ['Andrew', 'John', 'Peter', 'Nastin', 'Bill'];
$output = preg_grep('/^[a-j]/i', $names);     // names starting with A to J
print_r($output);
echo "</pre>";
?>
OUTPUT browser · http://localhost/phpbook/ch12/split.php
Array
(
    [0] => php
    [1] => regular
    [2] => expressions
    [3] => mysqli
)
Array
(
    [0] => Andrew
    [1] => John
    [4] => Bill
)
Complete Example – Server-Side Form Validation

The registration form below posts to itself, validates each field and re-displays the form with error messages and the values already typed. When everything is correct it shows a success message.

File: htdocs/phpbook/ch12/register.php
<?php
$name = $email = $mobile = "";
$errors = [];
function clean($v) { return htmlspecialchars(trim($v)); }

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $name   = clean($_POST["name"] ?? "");
    $email  = clean($_POST["email"] ?? "");
    $mobile = clean($_POST["mobile"] ?? "");

    if ($name === "") {
        $errors['name'] = "Name is required";
    } elseif (!preg_match("/^[a-zA-Z ]{3,40}$/", $name)) {
        $errors['name'] = "Only letters and spaces (3 to 40)";
    }
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors['email'] = "Invalid e-mail address";
    }
    if (!preg_match("/^[6-9][0-9]{9}$/", $mobile)) {
        $errors['mobile'] = "Enter a 10-digit mobile number";
    }
}
$ok = $_SERVER["REQUEST_METHOD"] == "POST" && !$errors;
?>
<html><body style="font-family:Arial;font-size:13px">
<style>.err{color:#c00;font-size:12px}</style>
<?php if ($ok): ?>
  <p style="color:green">Thank you, <?= $name ?>. Registration successful.</p>
<?php else: ?>
<form method="post" action="register.php">
  Name: <input id="name" name="name" value="<?= $name ?>">
  <span class="err"><?= $errors['name'] ?? '' ?></span><br>
  E-mail: <input id="email" name="email" value="<?= $email ?>">
  <span class="err"><?= $errors['email'] ?? '' ?></span><br>
  Mobile: <input id="mobile" name="mobile" value="<?= $mobile ?>">
  <span class="err"><?= $errors['mobile'] ?? '' ?></span><br>
  <input type="submit" id="go" value="Register">
</form>
<?php endif; ?>
</body></html>
OUTPUT browser · register.php submitted with invalid data
Browser output

Submitting correct data gives the success message:

OUTPUT browser · register.php submitted with valid data
Thank you, Rahul Verma. Registration successful.
Key Takeaways
  • Validate in the browser for convenience and always on the server for safety.
  • Regular expressions are written between delimiters: /pattern/flags.
  • Quantifiers: * + ? {n} {min,max}; anchors ^ $; classes [a-z], \d \w \s.
  • preg_match() finds the first match; preg_match_all() finds all.
  • preg_replace() replaces with back references $1; /e was removed in PHP 7 – use preg_replace_callback().
  • preg_split() splits by a pattern; preg_grep() filters an array.
Program File

Extract the ZIP into C:\xampp\htdocs. It creates the folder phpbook/ch12 used in the file labels above; open the programs at http://localhost/phpbook/… with Apache (and MySQL, where needed) started in the XAMPP Control Panel.

Question / Answer (15 questions)

Answer : Browser validation can be disabled or bypassed (for example by sending a request directly), so only server-side validation guarantees that bad or malicious data does not reach the program or database.

Answer : The errors can be displayed next to the fields on the same page, together with the values already typed, so the user can correct them easily.

Answer : A pattern, written in a special syntax, that describes a set of strings. It is used to search, validate, replace and split text.

Answer : The characters that enclose the pattern, usually slashes: "/abc/". Flags such as i (case-insensitive) follow the closing delimiter.

Answer : * means zero or more occurrences, + one or more, and ? zero or one (optional) of the preceding item.

Answer : The preceding item must occur at least 2 and at most 4 times.

Answer : ^ anchors the match to the start of the string and $ to the end. Inside a character class, ^ as the first character negates the class.

Answer : A set of characters in square brackets that matches any one of them: [aeiou] a vowel, [0-9] a digit, [A-Za-z] a letter, [^0-9] any non-digit.

Answer : \d a digit, \w a word character (letter, digit or underscore), \s a white-space character. The uppercase forms \D, \W, \S match the opposites.

Answer : 1 if the pattern matches, 0 if it does not (and false on error). The matched text is stored in the optional $matches array.

Answer : preg_match() stops at the first match; preg_match_all() finds every match in the subject and returns the number of matches.

Answer : $1, $2 … in the replacement string refer to the text captured by the first, second … parenthesised groups of the pattern; $0 is the whole match.

Answer : /e evaluated the replacement as PHP code, which allowed code injection. It was removed in PHP 7.0; preg_replace_callback() calls a function for each match instead.

Answer : It splits a string into an array wherever the pattern matches, e.g. preg_split("/[\s,]+/", $text) splits at any run of commas or white space.

Answer : The elements of an array that match the pattern, keeping their original keys.
MCQ Test : Check Your Understanding (5 questions)

Select an option and press Check Answer, or answer all questions and press Submit Test to see your score. Each answer comes with an explanation.

Q1. The quantifier + means:

Show Answer

Correct Answer : C. 1 or more
+ requires at least one occurrence.

Q2. Which pattern matches exactly ten digits?

Show Answer

Correct Answer : A. /^\d{10}$/
^ and $ anchor the string; \d{10} means exactly ten digits.

Q3. What does preg_match() return when no match is found?

Show Answer

Correct Answer : B. 0
It returns 0 (or false on error).

Q4. Which function should replace the removed /e modifier?

Show Answer

Correct Answer : B. preg_replace_callback()
It calls a function for each match safely.

Q5. [^0-9] matches:

Show Answer

Correct Answer : B. Any non-digit character
^ at the start of a class negates it.

Bibliography/References

The notes on this page are the author’s own compilation from the e-book PHP: Basics to Advance. Every program was run on Apache with PHP 8 and MariaDB, and the output shown is the real result of that run. The works listed below were consulted and are cited for attribution and further reading. Product names and trademarks belong to their respective owners.

  1. The PHP Group. (n.d.). PHP Manual – PCRE Patterns. https://www.php.net/manual/en/reference.pcre.pattern.syntax.php
  2. The PHP Group. (n.d.). PHP Manual – preg_match. https://www.php.net/manual/en/function.preg-match.php
  3. The PHP Group. (n.d.). PHP Manual – preg_replace_callback. https://www.php.net/manual/en/function.preg-replace-callback.php
  4. The PHP Group. (n.d.). PHP Manual – preg_split. https://www.php.net/manual/en/function.preg-split.php
  5. MDN contributors. (n.d.). Client-side form validation. MDN Web Docs. https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation