RU beehive logo ITEC dept promo banner
ITEC 325
2017spring
ibarland

homelectshws
D2Lbreeze (snow day)

form-handle-test-sanitize
testing forms; sanitizing html

We've been talking about input forms (html elements), and then php scripts to process a submitted form:

testing a form-handler

Video: easily testing a form-handler (18m38s)

Our functions have test cases, and this helps gives us confidence before we ever deploy to the web. But what about form-handlers? The result from form-handler is a long page that includes various bits from the form. It's too tedious to make this a check/expect sort of test, since the details of the html part of the output aren't relevant (and might change). Moreover, generating the page requires the POST data, that were entered by hand back in the form. But we would like to be able to check that the form-handler without going through the web, and also eyeball-check the result on certain inputs w/o having to continually re-type the inputs and call up the form.

Solution: Make a php program which simply initializes $_POST, and then require's the form-handler. Recall the sample form from last lecture (and recall what happens when it submits). Then, inspect:

Awesome sauce: We can run these handler-tests locally, or over the web. The former is useful when the latter isn't showing up as expected.
Although we use these to manually test our handler, they are not unit tests (they're not automated). Admittedly, the filename topic-handle-sample-input-N.php might be better a more descriptive name than just topic-handle-test-N.php, but we'll concede to a shorter, more convenient name.

Sanitizing input

Video: sanitizing html 1: htmlspecialchars (25m18s)

When creating a page which includes something typed in by the user (from a previous text-input), we must be careful. What characters could the user type that our browser would not interpret as data, but instead as part of the html page's structure? (Here's our sample form.)

We have three levels of html to think about:

Here is an improved form which sanitizes its input before printing. Much better! However, there is still a minor issue: consider a multi-line message: How does it render? We realize that for the browser to render a newline, its html-source must contain “<br/>”, and so our code-to-print-that-html must look for any newlines in the user's input, and replace that character with the br tag. Here's a fully-working, sanitizing form.

Video: sanitizing html 2: nl2br (6m59s)
Optional but good to know: Video: magic-quotes; array_map (24m01s)

Some functions to consider:

Quick q: suppose a user types:

  hi
<3
in a textarea whose name is msg. What is


We have seen arrays, and mentioned that if they have all-numeric indices (keys) then we can process them with a for loop or a while loop, using the same syntax that Java and Javascript happen to use.

Then we saw that if an array has keys which aren't all numeric, we can use a foreach loop to process them:

  $myData = array( 'hi' => 'hallo', 'good day' => 'guten Tag', 'see you later' => 'auf wiedersehen' );

  foreach ($mydata as $german) {
    echo $german, "\n";
    }

  foreach ($mydata as $english => $german) {
    echo "You say '$english', I say '$german'.\n";
    }
The foreach loop is of course one way to process each element of the $_POST array (if you didn't want code specific/different for each input form).


directory-processing

Look at the documentation for scandir.
Since it returns an array of filenames, it's a natural match to use with other functions that want an array of strings: For example, echo htmlLines( scandir( '/ibarland/Tmp' ) );


array_map

Suppose we wanted an English list of hyperlinks, separated by commas, with the word "and" before the last item. This decomposes into two orthogonal parts:

And you just wrote a function for making an English list, and you called that. The array you pass in must be an array of URLs.

To create the array, hopefully you also used your function hyperlink, written from hw02. (If you wrote the same long HTML a tag over and over, that's a sign that a function would be better.) So you might have a loop:

  $URLsAsText = array( "http://d20srd.org", "http://www.radford.edu", "http://google.com" );

  $URLsAsHTML = array();
  foreach ($URLsAsText AS $url) {
     $URLsAsHTML[] = hyperlink($url);
     }

  echo "It should appeal to users of ", commaSeparatedList( $URLsAsHTML ), "."
  

Any other repeated stuff? Hmm, the “http:” prefix was kinda annoying, but writing a loop for that seems definite overkill.
(Design Question: Should hyperlink be prepending a http:? How does this limit what it can do? Does it violate the principle of least surprise?)

It's kinda annoying to keep writing loops that make a new array of updated values. Most of the loop is very rote — the only part that differs is the particular rule to transform the individual element to the new element. (In the example above, the answer is “the function hyperlink”.)

There is a handy function, array_map: You pass it an array of data, and you pass it the rule (function) on how to transform each individual datum, and it gives you back the entire transformed array. So our loop above gets turned in to:

  $URLsAsText = array( "http://d20srd.org", "http://www.radford.edu", "http://google.com" );
  $URLsAsHTML = array_map( "hyperlink", $URLsAsText );
  echo "It should appeal to users of ", commaSeparatedList( $URLsAsHTML ), ".";
or we could even get rid of the intermediate variable, if we don't want to save the result1:
  $URLsAsText = array( "http://d20srd.org", "http://www.radford.edu", "http://google.com" );
  echo "It should appeal to users of ", 
       commaSeparatedList( array_map( "hyperlink", $URLsAsText ) ),
       ".";

Finally, note that we can also handle the “prepend “http://” to each item” issue. We could make a separate function and pass that to array_map, or we could use an anonymous function:

  $URLsAsText = array_map( function ($domain) { return "http://" . $domain; }2,
                           array( "d20srd.org", "www.radford.edu", "google.com" ) );
  

video from distance lecture (breeze), 2017-feb-14 (1h23m), REVIEWING this info

1 Heck, if you don't even want to name the original array, you could inline that. This is arguably in-lining too much, but that can be an issue of taste (and requires taking care with indentation):
  echo "It should appeal to users of ", 
       commaSeparatedList( array_map( "hyperlink",
                                      array( "http://d20srd.org",
                                             "http://www.radford.edu",
                                             "http://google.com" ) ) ),
       ".";
     
2 This is a function that we declare in the middle of the line; note it doesn't even have a name. We don't call this function ourselves: we are giving the function to array_map, and they'll use the function as they see fit — in this case, calling the function on every element of the array we're also handing them. In truth, the implementation of array_map is potentially very simple:
function arraymap( $arr, $func ) {
  $result = array();
  foreach ($arr AS $k => $v) {
    $result[$k] = $func($v);    // call the function we were handed, and store the answer in our array $result.
    }
  return $result;
  }
.      

homelectshws
D2Lbreeze (snow day)


©2017, Ian Barland, Radford University
Last modified 2017.Feb.21 (Tue)
Please mail any suggestions
(incl. typos, broken links)
to ibarlandradford.edu
Rendered by Racket.