It's your responsibility as a developer to ensure that your users' data is useful to your app, so you need to ensure that critical information is validated before storing it in your database.
In the case of the calendar application, the date format is critical: if the format isn't correct, the app will fail in several places. To verify that only valid dates are allowed into the database, you'll use regular expressions (regexes), which are powerful pattern-matching tools that allow developers much more control over data than a strict string comparison search.
Before you can get started with adding validation to your application, you need to get comfortable using regular expressions. In the first section of this chapter, you'll learn how to use the basic syntax of regexes. Then you'll put regexes to work doing server-side and client-side validation.
Regular expressions are often perceived as intimidating, difficult tools. In fact, regexes have such a bad reputation among programmers that discussions about them are often peppered with this quote:
Some people, when confronted with a problem, think, "I know, I'll use regular expressions." Now they have two problems. | ||
--—Jamie Zawinski |
This sentiment is not entirely unfounded because regular expressions come with a complex syntax and little margin for error. However, after overcoming the initial learning curve, regexes are an incredibly powerful tool with myriad applications in day-to-day programming.
In this book, you'll learn Perl-Compatible Regular Expression (PCRE) syntax. This syntax is compatible with PHP and JavaScript, as well as most other programming languages.
You can read more about PCRE at http://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions
.
To learn how to use regexes, you'll need a file to use for testing. In the public
folder, create a new file called regex.php
and place the following code inside it:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <title>Regular Expression Demo</title> <style type="text/css"> em {background-color: #FF0;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
} </style> </head> <body> <?php /* * Store the sample set of text to use for the examples of regex */ $string = <<<TEST_DATA <h2>Regular Expression Testing</h2> <p> In this document, there is a lot of text that can be matched using regex. The benefit of using a regular expression is much more flexible — albeit complex — syntax for text pattern matching. </p> <p> After you get the hang of regular expressions, also called regexes, they will become a powerful tool for pattern matching. </p> <hr /> TEST_DATA;
/* * Start by simply outputting the data */ echo $string; ?> </body> </html>
Save this file, then load http://localhost/regex.php
in your browser to view the sample script (see Figure 9-1).
To test regular expressions, you'll wrap matched patterns with <em>
tags, which are styled in the test document to have top and bottom borders, as well as a yellow background.
Accomplishing this with regexes is similar using str_replace()
in PHP with the preg_replace()
function. A pattern to match is passed, followed by a string (or pattern) to replace the matched pattern with. Finally, the string within which the search is to be performed is passed:
preg_replace($pattern, $replacement, $string);
The p in preg_replace()
signifies the use of PCRE. PHP also has ereg_replace()
, which uses the slightly different POSIX regular expression syntax; however, the ereg
family of functions has been deprecated as of PHP 5.3.0.
The only difference between str_replace()
and preg_replace()
on a basic level is that the element passed to preg_replace()
for the pattern must use delimiters, which let the function know which part of the regex is the pattern and which part consists of modifiers, or flags that affect how the pattern matches. You'll learn more about modifiers a little later in this section.
The delimiters for regex patterns in preg_replace()
can be any non-alphanumeric, non-backslash, and non-whitespace characters placed at the beginning and end of the pattern. Most commonly, forward slashes (/
) or hash signs (#
) are used. For instance, if you want to search for the letters cat in a string, the pattern would be /cat/
(or #cat#, %cat%, @cat@
, and so on).
To explore the differences between str_replace()
and preg_replace()
, try using both functions to wrap any occurrence of the word regular with <em>
tags. Make the following modifications to regex.php
:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <title>Regular Expression Demo</title> <style type="text/css"> em { background-color: #FF0; border-top: 1px solid #000; border-bottom: 1px solid #000; } </style> </head> <body> <?php /* * Store the sample set of text to use for the examples of regex */ $string = <<<TEST_DATA <h2>Regular Expression Testing</h2> <p> In this document, there is a lot of text that can be matched using regex. The benefit of using a regular expression is much more flexible — albeit complex — syntax for text pattern matching. </p> <p> After you get the hang of regular expressions, also called
regexes, they will become a powerful tool for pattern matching. </p> <hr /> TEST_DATA;/*
* Use str_replace() to highlight any occurrence of the word
* "regular"
*/
echo str_replace("regular", "<em>regular</em>", $string);
/*
* Use preg_replace() to highlight any occurrence of the word
* "regular"
*/
echo preg_replace("/regular/", "<em>regular</em>", $string);
?> </body> </html>
Executing this script in your browser outputs the test information twice, with identical results (see Figure 9-2).
You may have noticed that the word regular in the title is not highlighted. This is because the previous example is case sensitive.
To solve this problem with simple string replacement, you can opt to use the str_ireplace()
function, which is nearly identical to str_replace()
, except that it is case insensitive.
With regular expressions, you will still use preg_replace()
, but you'll need a modifier to signify case insensitivity. A modifier is a letter that follows the pattern delimiter, providing additional information to the regex about how it should handle patterns. For case insensitivity, the modifier i
should be applied.
Modify regex.php
to use case-insensitive replacement functions by making the modifications shown in bold:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <title>Regular Expression Demo</title> <style type="text/css"> em { background-color: #FF0; border-top: 1px solid #000; border-bottom: 1px solid #000; } </style> </head> <body> <?php /* * Store the sample set of text to use for the examples of regex */ $string = <<<TEST_DATA <h2>Regular Expression Testing</h2> <p> In this document, there is a lot of text that can be matched using regex. The benefit of using a regular expression is much more flexible — albeit complex — syntax for text pattern matching. </p> <p> After you get the hang of regular expressions, also called regexes, they will become a powerful tool for pattern matching. </p> <hr />
TEST_DATA;/*
* Use str_ireplace() to highlight any occurrence of the word
* "regular"
*/
echo str_ireplace("regular", "<em>regular</em>", $string);
/* * Use preg_replace() to highlight any occurrence of the word * "regular" */echo preg_replace("/regular/i", "<em>regular</em>", $string);
?> </body> </html>
Now loading the file in your browser will highlight all occurrences of the word regular, regardless of case (see Figure 9-3).
As you can see, this approach has a drawback: the capitalized regular in the title is changed to lowercase when it is replaced. In the next section, you'll learn how to avoid this issue by using groups in regexes.
The power of regexes starts to appear when you apply one of their most useful features: grouping and backreferences. A group is any part of a pattern that is enclosed in parentheses. A group can be used in the replacement string (or later in the pattern) with a backreference, a numbered reference to a named group.
This all sounds confusing, but in practice it's quite simple. Each set of parentheses from left to right in a regex is stored with a numeric backreference, which can be accessed using a backslash and the number of the backreference (1
) or by using a dollar sign and the number of the backreference ($1
).
The benefit of this is that it gives regexes the ability to use the matched value in the replacement, instead of a predetermined value as in str_replace()
and its ilk.
To keep the replacement contents in your previous example in the proper case, you need to use two occurrences of str_replace()
; however, you can achieve the same effect by using a backreference in preg_replace()
with just one function call.
Make the following modifications to regex.php
to see the power of backreferences in regexes:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <title>Regular Expression Demo</title> <style type="text/css"> em { background-color: #FF0; border-top: 1px solid #000; border-bottom: 1px solid #000; } </style> </head> <body> <?php /* * Store the sample set of text to use for the examples of regex */ $string = <<<TEST_DATA <h2>Regular Expression Testing</h2> <p> In this document, there is a lot of text that can be matched using regex. The benefit of using a regular expression is much more flexible — albeit complex — syntax for text pattern matching. </p> <p>
After you get the hang of regular expressions, also called regexes, they will become a powerful tool for pattern matching. </p> <hr /> TEST_DATA;/*
* Use str_replace() to highlight any occurrence of the word
* "regular"
*/
$check1 = str_replace("regular", "<em>regular</em>", $string);
/*
* Use str_replace() again to highlight any capitalized occurrence
* of the word "Regular"
*/
echo str_replace("Regular", "<em>Regular</em>", $check1);
/* * Use preg_replace() to highlight any occurrence of the word * "regular", case-insensitive */echo preg_replace("/(regular)/i", "<em>$1</em>", $string);
?> </body> </html>
As the preceding code illustrates, it's already becoming cumbersome to use str_replace()
for any kind of complex string matching. After saving the preceding changes and reloading your browser, however, you can achieve the desired outcome using both regexes and standard string replacement (see Figure 9-4).
The remaining examples in this section will use only regexes.
In some cases, it's desirable to match more than just a word. For instance, sometimes you want to verify that only a certain range of characters was used (i.e., to make sure only numbers were supplied for a phone number or that no special characters were used in a username field).
Regexes allow you to specify a character class, which is a set of characters enclosed in square brackets. For instance, to match any character between the letter a and the letter c, you would use [a-c]
in your pattern.
You can modify regex.php
to highlight any character from A-C. Additionally, you can move the pattern into a variable and output it at the bottom of the sample data; this helps you see what pattern is being used when the script is loaded. Add the code shown in bold to accomplish this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <title>Regular Expression Demo</title>
<style type="text/css"> em { background-color: #FF0; border-top: 1px solid #000; border-bottom: 1px solid #000; } </style> </head> <body> <?php /* * Store the sample set of text to use for the examples of regex */ $string = <<<TEST_DATA <h2>Regular Expression Testing</h2> <p> In this document, there is a lot of text that can be matched using regex. The benefit of using a regular expression is much more flexible — albeit complex — syntax for text pattern matching. </p> <p> After you get the hang of regular expressions, also called regexes, they will become a powerful tool for pattern matching. </p> <hr /> TEST_DATA;/*
* Use regex to highlight any occurence of the letters a-c
*/
$pattern = "/([a-c])/i";
echo preg_replace($pattern, "<em>$1</em>", $string);
/*
* Output the pattern you just used
*/
echo " <p>Pattern used: <strong>$pattern</strong></p>";
?> </body> </html>
After reloading the page, you'll see the characters highlighted (see Figure 9-5). You can achieve identical results using [abc], [bac]
, or any other combination of the characters because the class will match any one character from the class. Also, because you're using the case-insensitive modifier (i
), you don't need to include both uppercase and lowercase versions of the letters. Without the modifier, you would need to use [A-Ca-c]
to match either case of the three letters.
To match any character except those in a class, prefix the character class with a caret (^
). To highlight any characters except A-C, you would use the pattern /([^a-c])/i
(see Figure 9-6).
It's important to mention that the preceding patterns enclose the character class within parentheses. Character classes do not store backreferences, so parentheses still must be used to reference the matched text later.
Certain character classes have a shorthand character. For example, there is a shorthand class for every word, digit, or space character:
Word character class shorthand (w
): Matches patterns like [A-Za-z0-9_]
Digit character class shorthand (d
): Matches patterns like [0-9]
Whitespace character class shorthand (s
): Matches patterns like [
]
Using these three shorthand classes can improve the readability of your regexes, which is extremely convenient when you're dealing with more complex patterns.
You can exclude a particular type of character by capitalizing the shorthand character:
Non-word character class shorthand (W
): Matches patterns like [^A-Za-z0-9_]
Non-digit character class shorthand (D
): Matches patterns like [^0-9]
Non-whitespace character class shorthand (S
): Matches patterns like [^
]
,
, and
are special characters that represent tabs and newlines; a space is represented by a regular space character ( ).
Another special symbol to be aware of is the word boundary symbol (). By placing this before and/or after a pattern, you can ensure that the pattern isn't contained within another word. For instance, if you want to match the word stat, but not thermostat, statistic, or ecstatic, you would use this pattern:
/stat/
.
When you use character classes, only one character out of the set is matched, unless the pattern specifies a different number of characters. Regular expressions give you several ways to specify a number of characters to match:
The star operator (*
) matches zero or more occurrences of a character.
The plus operator (+
) matches one or more occurrences of a character.
The special repetition operator ({min,max}
) allows you to specify a range of character matches.
Matching zero or more characters is useful when using a string that may or may not have a certain piece of a pattern in it. For example, if you want to match all occurrences of either John or John Doe, you can use this pattern to match both instances: /John( Doe)*/
.
Matching one or more characters is good for verifying that at least one character was entered. For instance, if you want to verify that a user enters at least one character into a form input and that the character is a valid word character, you can use this pattern to validate the input: /w+/
.
Finally, matching a specific range of characters is especially useful when matching numeric ranges. For instance, you can use this pattern to ensure a value is between 0
and 99
: /d{1,2}/
.
In your example file, you use this regex pattern to find any words consisting of exactly four letters: /(w{4})/
(see Figure 9-7).
Additionally, you can force the pattern to match from the beginning or end of the string (or both). If the pattern starts with a caret (^
), the regex will only match if the pattern starts with a matching character. If it ends with a dollar sign ($
), the regex will match only if the string ends with the preceding matching character.
You can combine these different symbols to make sure an entire string matches a pattern. This is useful when validating input because you can verify that the user only submitted valid information. For instance, you can you can use this regex pattern to verify that a username contains only the letters A-Z, the numbers 0-9, and the underscore character: /^w+$/
.
In some cases, it's desirable to use either one pattern or another. This is called alternation, and it's accomplished using a pipe character (|
). This approach allows you to define two or more possibilities for a match. For instance, you can use this pattern to match either three-, six-, or seven-letter words in regex.php
: /(w{3}|w{6,7})/
(see Figure 9-8).
In some cases, it becomes necessary to allow certain items to be optional. For instance, to match both single and plural forms of a word like expression, you need to make the s optional.
To do this, place a question mark (?
) after the optional item. If the optional part of the pattern is longer than one character, it needs to be captured in a group (you'll use this technique in the next section).
For now, use this pattern to highlight all occurrences of the word expression or expressions: /(expressions?)/i
(see Figure 9-9).
Now that you've got a general understanding of regular expressions, it's time to use your new knowledge to write a regex pattern that will match any occurrence of the phrases regular expression or regex, including the plural forms.
To start, look for the phrase regex: /(regex)/i
(see Figure 9-10).
Next, add the ability for the phrase to be plural by inserting an optional es at the end: /(regex(es)?)/i
(see Figure 9-11).
Next, you will add to the pattern so that it also matches the word regular with a space after it; you will also make the match optional: /(reg(ulars)?ex(es)?)/i
(see Figure 9-12).
Now expand the pattern to match the word expression as an alternative to es: /(reg(ulars)?ex(pression|es)?)/i
(see Figure 9-13).
Finally, add an optional s to the end of the match for expression: /(reg(ulars)?ex(pressions?|es)?)/i
(see Figure 9-14).
The examples in this chapter go over the most common features of regular expressions, but they don't cover everything that regexes have to offer. Jan Goyvaerts has put together a fantastic resource for learning all of the ins-and-outs of regexes, as well as some tools for testing them, at http://www.regular-expressions.info/
.
Now that you have a basic understanding of regexes, you're ready to start validating user input. For this app, you need to ensure that the date format is correct, so that the app doesn't crash by attempting to parse a date that it can't understand.
You'll begin by adding server-side validation. This is more of a fallback because later you'll add validation with jQuery. However, you should never rely solely on JavaScript to validate user input because the user can easily turn off JavaScript support and therefore completely disable your JavaScript validation efforts.
The first step toward implementing date validation is to define a regex pattern to match the desired format. The format the calendar app uses is YYYY-MM-DD HH:MM:SS
.
You need to modify regex.php
with a valid date format and a few invalid formats, so you can test your pattern. Start by matching zero or more numeric characters with your regex pattern. Do this by making the following changes shown in bold:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <title>Regular Expression Demo</title> <style type="text/css"> em { background-color: #FF0; border-top: 1px solid #000; border-bottom: 1px solid #000; } </style> </head> <body> <?php/*
* Set up several test date strings to ensure validation is working
*/
$date[] = '2010-01-14 12:00:00';
$date[] = 'Saturday, May 14th at 7pm';
$date[] = '02/03/10 10:00pm';
$date[] = '2010-01-14 102:00:00';
/*
* Date validation pattern
*/
$pattern = "/(d*)/";
foreach ( $date as $d )
{
echo "<p>", preg_replace($pattern, "<em>$1</em>", $d), "</p>";
}
/* * Output the pattern you just used */ echo " <p>Pattern used: <strong>$pattern</strong></p>"; ?> </body> </html>
After saving the preceding code, reload http://localhost/regex.php
in your browser to see all numeric characters highlighted (see Figure 9-15).
To match the date format, start by matching exactly four digits at the beginning of the string to validate the year: /^(d{4})/
(see Figure 9-16).
Next, you need to validate the month by matching the hyphen and two more digits: /^(d{4}(-d{2}))/
(see Figure 9-17).
Notice that the month and date sections are identical: a hyphen followed by two digits. This means you can simply repeat the month-matching pattern to validate the day using a repetition operator after the group: /^(d{4}(-d{2}){2})/
(see Figure 9-18).
Now match a single space and the hour section: /^(d{4}(-d{2}){2} (d{2}))/
(see Figure 9-19).
Make sure that you include the space character. The shorthand class (s
) shouldn't be used because new lines and tabs should not match in this instance.
To validate the minutes, you match a colon and exactly two digits: /^(d{4}(-d{2}){2} (d{2})(:d{2}))/
(see Figure 9-20).
Finally, repeat the pattern for the minutes to match the seconds, and then use the dollar sign modifier to match the end of the string: /^(d{4}(-d{2}){2} (d{2})(:d{2}){2})$/
(see Figure 9-21).
Armed with this regex pattern, you can now validate the date input in your application.
To validate the date string, you will add a new private method to the Calendar
class called _validDate()
.
This method will accept the date string to be validated, then compare it to the validation pattern using preg_match()
, which returns the number of matches found in the given string. Because this particular pattern will only match if the entire string conforms to the pattern, a valid date will return 1
, while an invalid date will return 0
.
If the date is valid, the method will return TRUE
; otherwise, it will return FALSE
.
Add this method to the Calendar
class by inserting the following bold code into class.calendar.inc.php
:
<?php class Calendar extends DB_Connect { private $_useDate; private $_m; private $_y; private $_daysInMonth; private $_startDay; public function __construct($dbo=NULL, $useDate=NULL) {...} public function buildCalendar() {...} public function displayEvent($id) {...} public function displayForm() {...} public function processForm() {...} public function confirmDelete($id) {...}/**
* Validates a date string
*
* @param string $date the date string to validate
* @return bool TRUE on success, FALSE on failure
*/
private function _validDate($date)
{
/*
* Define a regex pattern to check the date format
*/
$pattern = '/^(d{4}(-d{2}){2} (d{2})(:d{2}){2})$/';
/*
* If a match is found, return TRUE. FALSE otherwise.
*/
return preg_match($pattern, $date)==1 ? TRUE : FALSE;
}
private function _loadEventData($id=NULL) {...} private function _createEventObj() {...} private function _loadEventById($id) {...} private function _adminGeneralOptions() {...} private function _adminEntryOptions($id) {...} } ?>
Your next step is to modify the processForm()
method so it calls the _validDate()
method on both the start and end times for new entries. If the validation fails, simply return an error message.
Add the following bold code to processForm()
to implement the validation:
<?php class Calendar extends DB_Connect { private $_useDate; private $_m; private $_y; private $_daysInMonth; private $_startDay; public function __construct($dbo=NULL, $useDate=NULL) {...} public function buildCalendar() {...} public function displayEvent($id) {...}
public function displayForm() {...} /** * Validates the form and saves/edits the event * * @return mixed TRUE on success, an error message on failure */ public function processForm() { /* * Exit if the action isn't set properly */ if ( $_POST['action']!='event_edit' ) { return "The method processForm was accessed incorrectly"; } /* * Escape data from the form */ $title = htmlentities($_POST['event_title'], ENT_QUOTES); $desc = htmlentities($_POST['event_description'], ENT_QUOTES); $start = htmlentities($_POST['event_start'], ENT_QUOTES); $end = htmlentities($_POST['event_end'], ENT_QUOTES);/*
* If the start or end dates aren't in a valid format, exit
* the script with an error
*/
if ( !$this->_validDate($start)
|| !$this->_validDate($end) )
{
return "Invalid date format! Use YYYY-MM-DD HH:MM:SS";
}
/* * If no event ID passed, create a new event */ if ( empty($_POST['event_id']) ) { $sql = "INSERT INTO `events` (`event_title`, `event_desc`, `event_start`, `event_end`) VALUES (:title, :description, :start, :end)"; } /* * Update the event if it's being edited */ else
{ /* * Cast the event ID as an integer for security */ $id = (int) $_POST['event_id']; $sql = "UPDATE `events` SET `event_title`=:title, `event_desc`=:description, `event_start`=:start, `event_end`=:end WHERE `event_id`=$id"; } /* * Execute the create or edit query after binding the data */ try { $stmt = $this->db->prepare($sql); $stmt->bindParam(":title", $title, PDO::PARAM_STR); $stmt->bindParam(":description", $desc, PDO::PARAM_STR); $stmt->bindParam(":start", $start, PDO::PARAM_STR); $stmt->bindParam(":end", $end, PDO::PARAM_STR); $stmt->execute(); $stmt->closeCursor(); /* * Returns the ID of the event */ return $this->db->lastInsertId(); } catch ( Exception $e ) { return $e->getMessage(); } } public function confirmDelete($id) {...} private function _validDate($date) {...} private function _loadEventData($id=NULL) {...} private function _createEventObj() {...} private function _loadEventById($id) {...} private function _adminGeneralOptions() {...} private function _adminEntryOptions($id) {...}
} ?>
You can test the validation by entering a bad entry into the form at http://localhost/admin.php
(see Figure 9-22).
You use http://localhost/admin.php
because the only reason your server-side validation will be invoked is if the user has JavaScript disabled. In that case, the modal windows would not function, and the user would be brought to this form. In situations where JavaScript is enabled, the server-side acts as a double-check and an additional security measure against mischievous users.
After this form is submitted, the app will simply output the error message and die (see Figure 9-23). The calendar application is designed for users with JavaScript enabled; you use this approach to prevent the app from displaying errors.
For most users, JavaScript will be enabled. It's far more convenient as a user to get instant feedback on the form, so you will add new jQuery functionality to validate date strings on the client side.
Because you're going to continue to work with this script in the next chapter, you should put it in a separate file in the js
folder called valid-date.js
. This file will contain a function that is functionally equivalent to the _validDate()
method in the Calendar
class.
It will accept a date to validate, check it against the date-matching regex pattern you wrote previously using match()
, and then return true
if a match is found or false
if match()
returns null.
You build this function by inserting the following code into valid-date.js
:
// Checks for a valid date string (YYYY-MM-DD HH:MM:SS)
function validDate(date)
{
// Define the regex pattern to validate the format
var pattern = /^(d{4}(-d{2}){2} (d{2})(:d{2}){2})$/;
// Returns true if the date matches, false if it doesn't
return date.match(pattern)!=null;
}
The regex pattern is not enclosed in quotes. If you used quotes, the pattern would be stored as a string and interpreted accordingly—this would result in the script looking for an exact character match, rather than interpreting the regex pattern properly.
To use the validDate()
function, you'll need to include the new JavaScript file before init.js
, so that the function is available to be called. Do this by opening footer.inc.php
in the common
folder and inserting the following bold code:
<script type="text/javascript" src="http://www.google.com/jsapi"></script> <script type="text/javascript"> google.load("jquery", "1"); </script><script type="text/javascript"
src="assets/js/valid-date.js"></script>
<script type="text/javascript" src="assets/js/init.js"></script> </body> </html>
Now that validDate()
is available in init.js
, you need to add date validation before the form can be submitted. Store the start and end dates in variables (start
and end
, respectively), then check them using validDate()
before allowing the form to be submitted.
Next, modify the click
handler to the Submit button on the form that edits or creates events, and then trigger an alert with a helpful error message if either date input has an invalid value. You need to prevent the form from being submitted as well, so the user doesn't have to repopulate the other form fields.
You accomplish this by inserting the following bold code into init.js
:
// Makes sure the document is ready before executing scripts jQuery(function($){ var processFile = "assets/inc/ajax.inc.php", fx = {...} $("li a").live("click", function(event){...}); $(".admin-options form,.admin") .live("click", function(event){...}); // Edits events without reloading $(".edit-form input[type=submit]").live("click", function(event){ // Prevents the default form action from executing event.preventDefault(); // Serializes the form data for use with $.ajax() var formData = $(this).parents("form").serialize(),
// Stores the value of the submit button submitVal = $(this).val(), // Determines if the event should be removed remove = false,// Saves the start date input string
start = $(this).siblings("[name=event_start]").val(),
// Saves the end date input string
end = $(this).siblings("[name=event_end]").val();
// If this is the deletion form, appends an action if ( $(this).attr("name")=="confirm_delete" ) { // Adds necessary info to the query string formData += "&action=confirm_delete" + "&confirm_delete="+submitVal; // If the event is really being deleted, sets // a flag to remove it from the markup if ( submitVal=="Yes, Delete It" ) { remove = true; } }// If creating/editing an event, checks for valid dates
if ( $(this).siblings("[name=action]").val()=="event_edit" )
{
if ( !validDate(start) || !validDate(end) )
{
alert("Valid dates only! (YYYY-MM-DD HH:MM:SS)");
return false;
}
}
// Sends the data to the processing file $.ajax({ type: "POST", url: processFile, data: formData, success: function(data) { // If this is a deleted event, removes // it from the markup if ( remove===true ) { fx.removeevent(); }
// Fades out the modal window fx.boxout(); // If this is a new event, adds it to // the calendar if ( $("[name=event_id]").val().length==0 && remove===false ) { fx.addevent(data, formData); } }, error: function(msg) { alert(msg); } }); }); $(".edit-form a:contains(cancel)") .live("click", function(event){...}); });
Now save these changes, load http://localhost/
in your browser, and then create a new event with bad parameters using the modal window form (see Figure 9-24).
If you click the Submit button at this point, the validation will fail, and the app will show an alert box with the error message about the date format (see Figure 9-25).
After clicking the OK button in the alert box, the user will be able to edit her entry without having to repopulate any fields.
In this chapter, you tackled using regular expressions for form validation. The concepts you learned can be applied to validating any type of data, and they will greatly aid you in making sure the information supplied in forms is usable by your applications.
In the next chapter, you'll learn how to extend the jQuery object, both by directly extending the jQuery core and by developing a custom plugin for jQuery.