Chapter 15
Chapter 15
How to use
regular expressions,
handle exceptions,
and validate data
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 1
Objectives
Applied
1. Create and use regular expressions.
2. Create and throw exceptions.
3. Catch and handle exceptions.
Knowledge
1. Describe the creation of a regular expression, and the processing
that’s done by the preg_match function.
2. Describe the use of case-insensitive, multiline, and global regular
expressions.
3. Describe the use of the preg_replace and preg_split functions that
work with regular expressions.
4. Describe how regular expressions can be used for data validation
such as validating a social security number.
5. Describe the way exceptions are created, thrown, and handled.
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 2
Terms
regular expression:
a coded pattern that is used to search for matching patterns in
text strings
commonly used for data validation
pattern:
a string contained within single quotes and forward slashes
example:
$pattern = ˈ/NC/ˈ;
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 3
A function for matching a regular expression
preg_match($pattern, $string);
//returns 1 if the pattern is found, 0 if not, FALSE
//if there is an error in the pattern
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 4
How to test for errors in a regular expression
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 5
By default a search is case-sensitive
$pattern = '/murach/i';
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 6
Patterns for special characters
Pattern Matches
\\ Backslash character
\/ Forward slash
\t Tab
\n New line
\r Carriage return
\f Form feed
\xhh The Latin-1 character whose value is the two
hexadecimal digits
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 7
Matching special characters
$string =
"© 2010 Mike's Music. \ All rights reserved (5/2010).";
preg_match('/\xA9/', $string)
// Matches © and returns 1
preg_match('///', $string)
// Returns FALSE and issues a warning\
preg_match('/\//', $string)
// Matches / and returns 1
preg_match('/\\\\/', $string)
// Matches \ and returns 1
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 8
Patterns for character types
Pattern Matches
. Any single character except a new line character
(use \. to match a period)
\w Any letter, number, or the underscore
\W Any character that’s not a letter, number or the
underscore
\d Any digit
\D Any character that’s not a digit
\s Any whitespace character (space, tab, new line,
carriage return, form feed, or vertical tab)
\S Any character that’s not whitespace
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 9
Matching character types
$string = 'The product code is MBT-3461.';
preg_match('/MB./', $string)
// Matches MBT and returns 1 because MB is followed by a
//character
preg_match('/MB\d/', $string)
// Matches nothing and returns 0 because MB is not
//followed by any digit
preg_match('/MBT-\d/', $string)
// Matches MBT-3 and returns 1
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 10
Using the character class: a list of characters to
match against a single character
$string = 'The product code is MBT-3461.';
preg_match('/MB[TF]/', $string)
// Matches MBT and returns 1
preg_match('/[.]/', $string)
// Matches . and returns 1
// Equivalent to preg_match('/\./', $string)
preg_match('/[13579]/', $string)
// Matches 3 and returns 1
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 11
Metacharacters
• characters which have special meanings in patterns such as / \ . [ ] $ ^ ( ) –
• most metacharacters lose their special meanings inside a character class
• the exceptions:
• ^ (caret) negation: match any character except)
• - (dash): represents a range of characters between the ones on either side
preg_match('/MB[^TF]/', $string)
// Matches nothing and returns 0
preg_match('/MBT[^^]/', $string)
// Matches MBT- and returns 1
preg_match('/MBT-[1-5]/', $string)
// Matches MBT-3 and returns 1
preg_match('/MBT[_*-]/', $string)
// Matches MBT- and returns 1
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 12
Using bracket expressions (complete list on pp. 467)
preg_match('/MBT[[:punct:]]/', $string)
// Matches MBT- and returns 1
preg_match('/MBT[[:digit:]]/', $string)
// Matches nothing and returns 0
preg_match('/MB[[:upper:]]/', $string)
// Matches MBT and returns 1
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 13
Patterns for string positions
Pattern Matches
^ The beginning of the string (use \^ to match a caret)
$ The end of the string (use \$ to match a dollar sign)
\b The beginning or end of a word (must not be inside
brackets)
\B A position other than the beginning or end of a word
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 14
Matching string positions
$author = 'Ray Harris';
preg_match('/^Ray/', $author)
// Returns 1: 'Ray' is the beginning of the string
preg_match('/Harris$/', $author)
// Returns 1:'Harris' is the end of the string
preg_match('/^Harris/', $author)
// Returns 0
preg_match('/Ann\b/', $editor)
// Returns 0
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 15
Matching subpatterns
$name = 'Rob Robertson';
preg_match('/^(Rob)|(Bob)\b/', $name)
// Returns 1
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 16
Matching repeating patterns
$phone = '559-555-6627';
preg_match('/^\d{3}-\d{3}-\d{4}$/', $phone)
// Returns 1
$phone_pattern =
'/^(\d{3}-)|(\(\d{3}\) ?)\d{3}-\d{4}$/';
preg_match($phone_pattern, $phone)
// Returns 1
preg_match($phone_pattern, $fax)
// Returns 1
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 17
Look-ahead assertion: a condition on the
characters that follow
(?=pattern)
Look-ahead assertions
(?=[[:digit:]])
// The next character in the pattern must be a digit
(?=.*[[:digit:]])
// The pattern must contain at least one digit (0 or more
characters (.*) must be followed by a digit)
A look-ahead assertion
$pattern = '/^(?=.*[[:digit:]])[[:alnum:]]{6}$/';
preg_match($pattern, 'Harris')
// Assertion fails and returns 0
preg_match($pattern, 'Harri5')
// Matches and returns 1
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 18
A pattern to enforce password complexity
$pw_pattern =
'/^(?=.*[[:digit:]])(?=.*[[:punct:]])[[:print:]]{6,}$/';
preg_match($pw_pattern, $password1)
// Assertion fails and returns 0
preg_match($pw_pattern, $password2)
// Matches and returns 1
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 19
A global regular expression finds all matches
preg_match_all($pattern, $string, $matches);
returns the number of matches in the string and stores
all matching substrings in an array (3rd parameter)
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 20
How to use the preg_replace function
to replace a pattern with a string
$items = 'MBT-6745 MBS-5729';
$items = preg_replace('/MB[ST]/', 'ITEM', $items);
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 21
The preg_split function returns an array of strings
that is created by splitting the string on the
pattern. (This is similar to the explode function.)
// $items contains:
// 'MBT-6745', 'MBS-5729', 'MBT-6824', 'MBS-5214'
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 22
Regular expressions for testing validity
Phone numbers as: 999-999-9999
/^[[:digit:]]{3}-[[:digit:]]{3}-[[:digit:]]{4}$/
Note: that this may still match some invalid dates such
as 02/30/2012. Additional validation is necessary.
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 23
Testing a phone number for validity
$phone = '559-555-6624';
$phone_pattern =
'/^[[:digit:]]{3}-[[:digit:]]{3}-[[:digit:]]{4}$/';
// Returns 1
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 24
Testing a date for a valid format,
but not for a valid month, day, and year
$date = '8/10/209'; // invalid date
$date_pattern = '/^(0?[1-9]|1[0-2])\/'
. '(0?[1-9]|[12][[:digit:]]|3[01])\/'
. '[[:digit:]]{4}$/';
// Returns 0
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 25
Complete email address validation
function valid_email ($email) {
$parts = explode("@", $email); //split into 2 parts
if (count($parts) != 2 ) return false;
if (strlen($parts[0]) > 64) return false;
if (strlen($parts[1]) > 255) return false;
$atom = '[[:alnum:]_!#$%&\'*+\/=?^`{|}~-]+';
$dotatom = '(\.' . $atom . ')*';
$address = '(^' . $atom . $dotatom . '$)';
$char = '([^\\\\"])';
$esc = '(\\\\[\\\\"])';
$text = '(' . $char . '|' . $esc . ')+';
$quoted = '(^"' . $text . '"$)';
$local_part = '/' . $address . '|' . $quoted . '/';
$local_match = preg_match($local_part, $parts[0]);
if ($local_match === false
|| $local_match != 1) return false;
continued…
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 26
Complete email address validation (continued)
$hostname =
'([[:alnum:]]([-[:alnum:]]{0,62}[[:alnum:]])?)';
$hostnames = '(' . $hostname .
'(\.' . $hostname . ')*)';
$top = '\.[[:alnum:]]{2,6}';
$domain_part = '/^' . $hostnames . $top . '$/';
$domain_match = preg_match($domain_part, $parts[1]);
if ($domain_match === false
|| $domain_match != 1) return false;
return true;
}
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 27
Exceptions: runtime errors due to unexpected
conditions
You can also throw exceptions and write code to
handle them
The syntax for creating new Exception objects
new Exception($message [, $code])
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 28
A function that may throw an Exception
function calculate_future_value(
$investment, $interest_rate, $years) {
if ($investment <= 0 ||
$interest_rate <= 0 ||
$years <= 0 ) {
throw new Exception("Please check all entries.");
} //function ends and control is passed back
$future_value = $investment;
for ($i = 1; $i <= $years; $i++) {
$future_value =
($future_value +
($future_value * $interest_rate * .01));
}
return round($futureValue, 2);
}
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 29
When an Exception object is thrown, the
application needs to catch it and handle it. This is
called exception handling.
A try-catch statement catches any thrown
exceptions using a try block and at least one
catch block.
The syntax for a try-catch statement
try { statements }
catch (ExceptionClass $exceptionName) { statements }
[ catch (ExceptionClass $exceptionName) { statements } ]
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 31
The user interface
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 32
model/fields.php
<?php
class Field {
private $name;
private $message = '';
private $hasError = false;
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 33
model/fields.php (continued)
public function setErrorMessage($message) {
$this->message = $message;
$this->hasError = true;
}
public function clearErrorMessage() {
$this->message = '';
$this->hasError = false;
}
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 34
model/fields.php (continued)
class Fields {
private $fields = array();
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 35
model/validate.php
<?php
class Validate {
private $fields;
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 36
model/validate.php (continued)
// Validate a generic text field
public function text($name, $value, $required = true,
$min = 1, $max = 255) {
// Get Field object
$field = $this->fields->getField($name);
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 37
model/validate.php (continued)
// Validate a field with a generic pattern
public function pattern($name, $value, $pattern,
$message, $required = true) {
// Get Field object
$field = $this->fields->getField($name);
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 38
model/validate.php (continued)
public function phone($name, $value,
$required = false) {
$field = $this->fields->getField($name);
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 39
model/validate.php (continued)
public function email($name, $value, $required = true) {
$field = $this->fields->getField($name);
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 40
model/validate.php (continued)
// Split email address on @ sign and check parts
$parts = explode('@', $value);
if (count($parts) < 2) {
$field->setErrorMessage('At sign required.');
return;
}
if (count($parts) > 2) {
$field->setErrorMessage(
'Only one at sign allowed.');
return;
}
$local = $parts[0];
$domain = $parts[1];
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 41
model/validate.php (continued)
// Check lengths of local and domain parts
if (strlen($local) > 64) {
$field->setErrorMessage('Username too long.');
return;
}
if (strlen($domain) > 255) {
$field->setErrorMessage(
'Domain name part too long.');
return;
}
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 42
model/validate.php (continued)
// Patterns for address formatted local part
$atom = '[[:alnum:]_!#$%&\'*+\/=?^`{|}~-]+';
$dotatom = '(\.' . $atom . ')*';
$address = '(^' . $atom . $dotatom . '$)';
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 43
model/validate.php (continued)
// Patterns for domain part
$hostname =
'([[:alnum:]]([-[:alnum:]]{0,62}[[:alnum:]])?)';
$hostnames =
'(' . $hostname . '(\.' . $hostname . ')*)';
$top = '\.[[:alnum:]]{2,6}';
$domainPattern = '/^' . $hostnames . $top . '$/';
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 44
The controller (index.php)
<?php
require_once('model/fields.php');
require_once('model/validate.php');
if (isset($_POST['action'])) {
$action = $_POST['action'];
} else {
$action = 'reset';
}
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 45
The controller (index.php) (continued)
$action = strtolower($action);
switch ($action) {
case 'reset':
include 'view/register.php';
break;
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 46
The controller (index.php) (continued)
case 'register':
// Copy form values to local variables
$first_name = trim($_POST['first_name']);
$last_name = trim($_POST['last_name']);
$phone = trim($_POST['phone']);
$email = trim($_POST['email']);
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 47
The view (view/register.php)
<?php include 'header.php'; ?>
<div id="content">
<form action="." method="post">
<fieldset>
<legend>User Information</legend>
<label>First Name:</label>
<input type="text" name="first_name"
value=
"<?php echo htmlspecialchars($first_name);?>"/>
<?php echo
$fields->getField('first_name')->getHTML(); ?>
<br />
<label>Last Name:</label>
<input type="text" name="last_name"
value="<?php echo htmlspecialchars($last_name);?>"/>
<?php echo
$fields->getField('last_name')->getHTML(); ?>
<br />
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 48
The view (view/register.php) (continued)
<label>Phone:</label>
<input type="text" name="phone"
value="<?php echo htmlspecialchars($phone);?>"/>
<?php echo $fields->getField('phone')->getHTML(); ?>
<br />
<label>E-Mail:</label>
<input type="text" name="email"
value="<?php echo htmlspecialchars($email);?>"/>
<?php echo $fields->getField('email')->getHTML(); ?>
<br />
</fieldset>
<fieldset>
<legend>Submit Registration</legend>
<label> </label>
<input type="submit" name="action" value="Register"/>
<input type="submit" name="action" value="Reset" />
<br />
</fieldset>
</form>
</div>
<?php include 'footer.php'; ?>
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 49
A long version of the Registration application
Murach's PHP and MySQL, C15 © 2010, Mike Murach & Associates, Inc. Slide 50