University of Washington CSE 154

Section 4: Forms; Regular Expressions

Except where otherwise noted, the contents of this document are Copyright © Marty Stepp, Jessica Miller, and Victoria Kirst. All rights reserved. Any redistribution, reproduction, transmission, or storage of part or all of the contents in any form is prohibited without the author's expressed written permission.

Valid HTML5 Valid CSS

Exercise : Buggy HTML Form

The following HTML form has several mistakes that causes it not to submit its data correctly, as well as some poor design choices that make it unpleasant to the user. Look at the form, find the bug(s), and correct the problems.

<form action="wherever.php">
	UW NetID: <input type="text" id="uwnetid" size="8" /> <br />
	Year: <input type="checkbox" name="frosh"> Freshman</input>
	<label>
		<input type="checkbox" name="soph" /> Sophomore
		<input type="checkbox" name="junior" /> Junior
		<input type="checkbox" name="senior" /> Senior
	</label> <br />
	Student ID number:
	<!-- don't allow the user to type more than 7 characters -->
	<input type="text" name="studentid" size="7" /> <br /><br />
	Personal statement: Please type a roughly 500-word essay. <br />
	<input type="text" name="essay" size="500" />
</form>

Exercise Solution

<form action="http://webster.cs.washington.edu/params.php">
	UW NetID: <input type="text" id="uwnetid" size="8" maxlength="8" name="uwnetid" /> <br />
	Year:
	<label><input type="radio" name="year" /> Freshman</label> <br />
	<label><input type="radio" name="year" /> Sophomore </label> <br />
	<label><input type="radio" name="year" /> Junior </label> <br />
	<label><input type="radio" name="year" /> Senior </label> <br />
	Student ID number:
	<input type="text" name="studentid" size="7" maxlength="7"/> <br /><br />
	Personal statement: <br />
	<textarea name="essay" rows="10" cols="80">
		Please type a roughly 500-word essay.
	</textarea>
</form>

Exercise : Animal Gallery (by Alex Miller)

Given a directory of animal pictures (images.zip), write a PHP webpage animals.php which displays these pictures.

If no query parameter is set, then the page should simply display all of the images in the images directory. The user can set an animal query parameter to choose whether to display only puppy pictures, only pony pictures, or only kitty pictures. For example, if the user entered the following URL:

animals.php?animal=puppy

Then the page should only display puppy pictures. The animal parameters that do have photos are kitty, puppy and pony If the user types in a different query parameter that does not have any photos, you should display a message saying that there are no photos for that animal.

Exercise Solution

<!DOCTYPE html PUBLIC>
<html>
  <head>
     <title>Animal Gallery</title>
  </head>
  <body>
    <div>
      <?php
      $animal = "";
      if (isset($_GET["animal"])) {
          $animal = $_GET["animal"];
      }
      $files = glob("images/{$animal}*.jpeg");
      if(count($files) == 0) {?>
         <p> Sorry, looks like we don't have any photos of <?= $animal ?>s.</p>
      <?php } else {
         foreach ($files as $image) {
            ?>
            <img src="<?= $image ?>" alt="animal picture" />
            <?php
         }
      }
      ?>
    </div>
  </body>
</html>

Exercise : Chat-It (by Katlyn Edwards)

chatpic

Given this basic skeleton page, add in the necessary form components and PHP to make a minimalistic chat page. You can see a completed version here.

The form needs a place for the user to type in their name, a place for the user to type their message, and a submit button.

The PHP part has two phases. If the page has post data to the form, it should save the data to chat.txt in the following format: name:message\n Remember to set the correct permissions on chat.txt so that the server can write to it.

The other part of the PHP should display the text file into the div with an id of "chatlog". The names should be bold then followed by " says: " and the message. Each message should be it's own paragraph.

Exercise Solution

  <body>
    <?php  
    if(isset($_POST["name"]) && isset($_POST["message"])) {
      file_put_contents("chat.txt", $_POST["name"] . ":" . $_POST["message"] . "\n", FILE_APPEND);
    } ?>
    <div id="chatlog">
       <?php
          $messages = file("chat.txt", FILE_IGNORE_NEW_LINES);
          foreach($messages as $message) {
             $parts = explode(":", $message); ?>
             <p><strong><?= $parts[0] ?></strong> says: &quot;<?= $parts[1] ?>&quot;</p>
          <?php } ?>
    </div>
    <form action="chat.php" method="post">
       Name: <input type="text" name="name" /><br/>
       Message: <input type="text" name="message" /><br/>
       <input type="submit" value="Send!" /> 
    </form>
  </body>

Exercise : Show Links

Write a PHP function show_links in showlinks.php that accepts two parameters: an array of URL strings, and a substring to search for. Display each case-insensitively matching link in a div with a bolded numbering in front.

$links = array("http://www.cs.washington.edu/142/", "http://...");
show_links($links, "CS.Washington.Edu");

The call generates this output (see next slide for screenshot):

<h1>Links to CS.Washington.Edu:</h1>
<div>
	<strong>Site #1:</strong>
	<a href="http://www.cs.washington.edu/142/">http://www.cs.washington.edu/142/</a>
</div>
<div> <strong>Site #2:</strong>...</div>

Exercise Example Output

screenshot
show_links($links,
	"CS.Washington.Edu");

Exercise Solution

<?php
function show_links($links, $site) {  # Displays all URLs from the given
	?>                                  # array that match the given site.
	<h1>Links to <?= $site ?>:</h1>
	<?php
	$site = strtolower($site);
	$count = 0;
	foreach ($links as $url) {
		if (strstr($url, $site)) {
			$count++;
			?>
			<div>
				<strong>Site #<?= $count ?>:</strong>
				<a href="<?= $url ?>"> <?= $url ?> </a>
			</div>
			<?php
		}
	}
}
?>

Exercise : Grades

screenshot

Write a PHP page grades.php that takes a query parameter student and computes total homework points earned by that student. For example, grades.php?student=Marty will read marty.txt. The student input files consist of a single line of scores separated by spaces:

15 14 22 19 13

Print a heading, a bullet list of scores on each assignment, and the total at the bottom. If there is no text file for that student, print "no scores found."

Exercise Solution

<!DOCTYPE html>
<html>
	<head>
		<title>Grades</title>
	</head>
	<body>
		<?php
		$student = "";
		if (isset($_GET["student"])) {
			$student = $_GET["student"];
		}
		?>
		<h1>Grades for <?= $student ?></h1>
		<ul>
			<?php
			$filename = strtolower($student) . ".txt";
			...

Exercise Solution, continued

			if (file_exists($filename)) {
				$total = 0;
				$text = file_get_contents($filename);
				$scores = explode(" ", $text);
				foreach ($scores as $score) {
					$total += $score;
					?>
					<li><?= $score ?> points</li>
				<?php } ?>
				<li>TOTAL: <?= $total ?></li>
			<?php } else { ?>
				<li>no scores found. :-(</li>
			<?php } ?>
		</ul>
	</body>
</html>

Exercise : Calculations (by Alex Miller)

Given an input file input.txt, Write a PHP page calculations.php that reads in the file and calculates the result for each line in the file. For example, the result for the line:

divide:8 2 2

would be:

8 / 2 / 2 = 2

Exercise Solution

<ul><?php
	$lines = file("input.txt");
	foreach ($lines as $line) {
		$split = split(":", $line);
		$numbers = split(" ", $split[1]);
		$sum = $numbers[0];
		for ($i = 1; $i < sizeof($numbers); $i++) {
			if ($split[0] == "add") {
				$sum += $numbers[$i];
			} else if ($split[0] == "multiply") {
				$sum *= $numbers[$i];
			} else if ($split[0] == "subtract") {
				$sum -= $numbers[$i];
			} else if ($split[0] == "divide") {
				$sum /= $numbers[$i];
			}
		} ?>
	<li><?= $line ?> = <?= $sum ?></li>
<?php } ?></ul>
	

Exercise : Captions (by Morgan Doocy)

Write a form, caption.html, which asks the user to select a photo to display and two lines of caption. Then write a script, caption.php, that will accept the submitted information and display the photo and captions on-screen:

Provide several photo URLs to select from in the form of a dropdown menu. Give the options in the dropdown user-friendly labels, but make the user's final selection result in an absolute URL being submitted to the server script. For the img's alt text, use a combination of both caption lines.

Exercise : Captions (by Morgan Doocy)

screenshot of caption.html filled out screenshot of caption.php

Exercise Solution

<form action="caption.php" method="post">
	<dl>
		<dt>Image:</dt>
		<dd>
			<select name="image">
				<option value="http://tinyurl.com/8x4ouqu">Cupcake</option>
				<option value="http://tinyurl.com/6uqgufv">Leo Strutting</option>
			</select>
		</dd>
		<dt>Line 1:</dt>
		<dd><input type="text" name="line1" /></dd>
		<dt>Line 2:</dt>
		<dd><textarea name="line2" rows="2" cols="30"></textarea></dd>
	</dl>
	<p><input type="submit" value="Captionate!" /></p>
</form>

Exercise Solution

<?php
   $image = $_POST["image"];
   $line1 = $_POST["line1"];
   $line2 = $_POST["line2"];
?>
<p>
	<img id="image" src="<?= $image ?>" alt="<?= "$line1. $line2" ?>" />
</p>
<h1 id="line1"><span><?= $line1 ?></span></h1>
<p id="line2"><?= $line2 ?></p>

Exercise : Captions (optional validation)

Optional: Modify your PHP code to ensure all expected parameters are passed and non-empty. If any parameters aren’t, display an error message instead of the expected output.

Exercise Solution (optional validation)

<?php
	$valid = true;
	foreach (array('image', 'line1', 'line2') as $param) {
		if (!isset($_POST[$param]) || !$_POST[$param]) {
			$valid = false;
			break;
		} else {
			$$param = $_POST[$param]; // $image = $_POST['image'], etc.
		}
	}
	if (!$valid) {
		?>
		<p>ERROR: You submitted invalid values.</p>
		<?php
	} else {
		?>
		<p><img id="image" src="<?= $image ?>" alt="<?= "$line1. $line2" ?>" /></p>
		<h1 id="line1"><span><?= $line1 ?></span></h1>
		<p id="line2"><?= $line2 ?></p>
		<?php
	}
?>

Exercise : Show Twos (by Alex Miller)

Write a PHP function show_twos that takes an integer parameter and outputs its factors of 2 with HTML. For example:

show_twos(68);
show_twos(18);
show_twos(68);
show_twos(120);

should produce the following output:

<strong>68</strong> = 2 * 2 * 17<br/>
<strong>18</strong> = 2 * 9<br/>
<strong>68</strong> = 2 * 2 * 17<br/>
<strong>120</strong> = 2 * 2 * 2 * 15<br/>

If you have time, wrap the code in a page that accepts a query parameter num and passes that parameter to the function.

Exercise Solution

<?php
function show_twos($n) { ?>
   <strong><?= $n ?></strong> =
   <?php
   while ($n % 2 == 0) {
      print "2 *";
      $n = $n / 2;
   }
   ?>
   <?= $n ?><br />
   <?php
}

show_twos($_GET["num"]);
?>

Exercise : Complaint Generator

Write an HTML form, complaint.html, that allows the user to generate complaint letters. The user will specify the first/last name of the person to complain about, the person's gender, and how many sentences of complaints to generate. Test your form by having it initially submit its data to params.php. The following should be the appearance of your form:
expected form output

Exercise : Complaint Generator

Once your form submits properly, write a PHP page letter.php on the server to process this form data. On the server there is a file sentence.txt containing a bunch of complaint sentences. Your PHP code should read this file, randomly pick sentences from it, and turn these into a complaint letter. The file has one sentence per line. You will need to personalize the letter by inserting the person's name and other information into it. The following are the patterns you will need to replace:

Exercise : Complaint Generator

You can use the str_replace function to help you replace the above patterns in the text. If you write the code correctly, you can replace each placeholder with a single call. When you're finished with your page, it should look like the following:

expected form output

Exercise Solution

Your finished code might look like the following sample solution, written by TA Stefanie Hatcher:

Recall: Regex Syntax Reference

| or
() grouping
^ start
$ end
special chars
* 0 or more
+ 1 or more
? 0 or 1
{min,max} between min and max
quantifiers
[abcde] one of those characters
[a-z] a character from a through z
\d digit
\s whitespace
character sets

Exercise : Regular Expressions (by Zack Cava)

Write a regular expression (slides) that would match the following kinds of patterns. You can use the site Rubular to test your regex.

  1. a student's letter grade such as A, B+, or D- (try it) (data)
  2. a DNA sequence (string of any combination of A, C, G, T) (try it) (data)
  3. a US ZIP code (try it) (data)
  4. a credit card number, with optional dashes every 4 numbers (try it) (data)
  5. a real number such as 3.14 or -42.8775 (try it) (data)

Regular Expressions, continued

  1. a dollar amount of at least $100.00 (try it) (data)
  2. a word with two or more vowels (A, E, I, O, or U) (try it) (data)
  3. a string that starts with either 'q' or 's', and that also contains a double z ('zz') later within the same word (try it) (data)
  4. a string that contains at least 5 vowels (a, e, i, o, or u) in a row (try it) (data)
  5. a string that is at least 25 characters long (try it) (data)

Regular Expressions, continued

  1. a string that ends with two or more occurrences of "?!", as in, "Huh?!?!" (try it) (data)
  2. a valid amount for a US coin: 1, 5, 10, 25, or 50 cents (try it) (data)
  3. an IP address, of the format 128.208.3.88 (accept any 3-digit numbers between the dots) (try it) (data)
  4. a string that contains at least 3 words, each separated by one or more spaces (try it) (data)
  5. a string that contains a quotation with "quote marks" around it (try it) (data)

Exercise Solutions

  1. letter grade: /^[ABCDF][+\-]?$/
    • if F+/F- are not allowed: /^[ABCDF][+\-]|F$/
  2. DNA sequence: /^[ACGT]+$/
  3. US ZIP: /^\d{5}(-\d{4})?$/ (with optional “+4”)
  4. credit card with optional dashes: /^\d{4}(-?\d{4}){3}$/
  5. real number: /^[-]?\d+(\.\d+)?$/
    • to allow for no leading zero (e.g., .16667): /^[-]?(\d+(\.\d+)?|\.\d+)$/
    • why not just /^[-]?\d*(\.\d+)?$/?

Exercise Solutions, continued

  1. $100+: /^\$[1-9]\d{2,}\.\d{2}$/
  2. word with 2 or more vowels: /^[a-z]*[aeiou][a-z]*[aeiou][a-z]*$/i
    • or /^[a-z]*([aeiou][a-z]*[aeiou][a-z]*){2,}$/i
  3. string starting with q or s and zz within same word: /^[qs][^ ]*zz/ (not /^[qs].*zz/)
  4. 5 or more vowels in a row: /[aeiou]{5}/i (why don’t we need {5,}?)
  5. 25 or more characters long: /.{25}/ (why don’t we need {25,}?)

Exercise Solutions, continued

  1. string ending with 2 or more '?!': /(\?!){2}$/ (why don’t we need {2,}?)
  2. US coin amount: /^1|5|10|25|50$/ or /^10?|50?|25$/
  3. IP address: /^\d{1,3}(\.\d{1,3}){3}$/
  4. string with 3+ words: /[a-z]+( +[a-z]+){2}/i (why don’t we need {2,}?)
  5. string containing a quote: /"[^"]+"/

Regular expressions in PHP

function description
preg_match(regex, string) returns true if string matches regex
preg_replace(regex, replacement, string) returns a new string with all substrings that match regex replaced by replacement
preg_split(regex, string) returns an array of strings from given string broken apart using given regex as delimiter (like explode but more powerful)

Exercise : Regex Images (by Jamie Pell)

The code for images.php displays all icon JPG images in the images/ folder. Modify it using regular expressions so that it will display only image file namess that match each of the following patterns: (sample solution)

screenshot screenshot screenshot screenshot (example output)

Exercise Solution

<?php
$folder = "images";
$images = glob("$folder/*.jpg");

$regex = "/abbath/i";       // contain "abbath", case insensitive

foreach ($images as $image) {
	if (preg_match($regex, $image)) {
		?>
		<img src="<?= $image ?>" alt="an awesome picture" />
		<?php
	}
}

// begin with "abbath" and end with "cat", "dog", or "sheep"
// $regex = "/^$folder\/abbath(.*)(dog|cat|sheep).jpg$/";  
// $regex = "/[0-9].jpg$/";   // end in a number
// $regex = "/^[ab]{4}/i";    // start with 4 As/Bs
?>

Exercise : Regex validation (by Morgan Doocy)

Adapt the solution to Exercise 1 (Buggy HTML Form) to create a PHP script with form validation.

Exercise : Regex validation, continued

Exercise solution

<?php
	$valid = true;
	if ($_SERVER['REQUEST_METHOD'] == 'POST') {
		$patterns = array(
			'uwnetid' => '/^[a-z_\-]{8}$/i',
			'year' => '/^frosh|soph|junior|senior$/',
			'studentid' => '/^\d{7}$/',
			'essay' => '/^[a-z0-9]+([^a-z0-9]+[a-z0-9]+){449,549}$/'
		);
		foreach ($patterns as $param => $pattern) {
			if (!isset($_POST[$param]) || !preg_match($pattern, $_POST[$param])) {
				$valid = false;
				break;
			}
		}
	}
?>

Exercise solution, continued

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Essay Submission</title>
	</head>
	<body>
		<?php if ($_SERVER['REQUEST_METHOD'] == 'GET') { ?>
			<form action="" method="post">
				<!-- (form controls here) -->
			</form>
		<?php } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { ?>
			<?php if (!$valid) { ?>
				<p>ERROR: You submitted an invalid value.</p>
			<?php } else { ?>
				<p>Thank you for your submission, <?= $_POST['uwnetid'] ?>!</p>
			<?php } ?>
		<?php } ?>
	</body>
</html>