Dynamic Web Content with PHP

This material from October 20th's lecture attempts to help you gain comfort with creating interactive forms for passing data back and forth from your Web server and your intended audience (or serendipidous audience if you aren't targeting one). Forms don't have to look and act like boring insurance questionnaires. Any data can be passed between forms that have been designed using everything you know about HTML and style sheets.

Lecture 7: Forms and Form Processing

Introduction to Forms

Web forms are bundles of interactive widgets placed together in a way to inform an information provider as to how to share data with the host Web page provider. Hopefully, you already have an introduction to writing or generating forms in HTML so you can focus on improving your layout and processing the forms with PHP. HTML forms have a benefit of being very reliable. HTML forms are asyncrhonous, meaning the act of filling out a form is not linked in time to the delivery nor processing of the form - delivery and processing both take place on demand by your Web audience's actions. To be sure, you can be an annoying pusher of forms - making them appear to your audience in PHP-coded pop-up windows. But, HTML does not have any method for you to stay connected, snooping on your audience as they contemplate your form. You deliver and HTML form from a server and then you wait for the recipient to fill it out. At that time, the form is shipped back to the server and the server pays attention again (without paying any attention to the delivered form between delivery and processing notification).

Review of HTML forms

There are many good HTML form tutorials out there for you to use to brush up on HTML forms. I'll point you to some and then show you some interesting ways to pretty up HTML forms with an intermediate skill base. Take a look at these references: Many of these HTML form processing examples use JavaScript to validate the form contents before the form content is shipped back to the server for processing. I like that technique, but you don't have to use JavaScript in this class unless you'd like to use it or are comfortable copying and pasting it without understanding the code completely. I like using JavaScript because it saves my server from having to get involved in correcting obvious typos that JavaScript can point out to your audience member just fine. But, with PHP, we have to get used to expecting the server to get very active in our information processing. PHP was built specifically to handle the load of heavy processing through partnership with powerful database engines. At some point, we need to change our behavior to rely on PHP's processing stature - otherwise we'll never use PHP comfortably as intended. If our PHP code gets bogged down at any point, we'll question if we have enough hardware, bandwidth, and other necessary software to deliver the applications we want to deliver on the Web - but we won't question PHP as software able to stay free from causing unnecessary processing bottlenecks.

Creating Forms and Form Elements

Let's take a look at a basic form that could work well for our Ocean Picture of the Day site:

Please choose your ocean picture file:

Your e-mail address:

Latitude taken from:

Longitude taken from:

Direction facing:

The Cascading Style Sheet (CSS) specification provides us with many useful style directives with which we can make our forms look more attractive. As you likely know already, each component within a form can have an internal style sheet (or you can externalize all your styles to reuse them on multiple forms). For example, look at the drop-down list box that asks us to inform the server as to which compass direction the camera was facing when the picture was taken. We can change how it looks significantly through an internal style attribute:

Direction facing:

The change in presentation occurs because the internal style attribute contains a style attribute:

style="color:blue;background-color:#CCFF66;font-family:Georgia, 'Times New Roman', Times, serif;font-size:12px"

Many other CSS style attributes have to do with form component layout. Do review those at http://www.websiteoptimization.com/speed/tweak/forms/ or http://themaninblue.com/writing/perspective/2004/03/24/ or http://www.sitepoint.com/article/fancy-form-design-css/ if you don't remember all the great style features you have within the style attribute that are relevant to form design.

Actions and Submission Methods

We are going to use PHP to process forms in this class. PHP has been built as a language with forms processing at the center of its context (along with all data processing and management in general). So, we expect to see all of our FORM ACTION attributes contain a value string that ends in .php. PHP will know what to do with our form data if we've set up our <FORM> begin and </FORM> end tags to encapsulate all our form data within the same form (or multiple FORM elements if we're processing more than one form on a Web page). Let's say you put all your PHP form processing directives into a code file that you have saved with name form_processor.php. You can put that file into our class Web server within the /var/www/html/dynphp/xxx/forms directory (where xxx is the initials you've set up for your PHP-accessible Web folder) and then access it from a URL: http://www.oworld.org/dynphp/xxx/forms/form_processor.php. Hopefully, that did not need to be said as you have mastered our Web server file system by now. You then refer to your PHP processing file in the ACTION attribute of your form. Something like this:

<form action="http://www.oworld.org/dynphp/xxx/forms/form_processor.php" method="post">
...
</form>

which then would work from any Web server you put the form on (since your reference to the form processor is absolute).

You have two choices as to the METHOD attribute value you put in your form. PHP is flexible enough to handle both methods without you having to tell PHP which one you are using. You just need to tell the form which one to use to send it to the PHP properly. If you use method="get" you will be able to see the CGI parameters being passed in the sending URL (look in the address text field of your Web browser after submitting). If you use method="post", the data will be passed in the background. Be careful about using a GET method with very long forms (or open ended fields like an essay textarea). With the GET method, your parameters will get cut off based on the maximum length your browser lets an address contain. For that reason alone, I usually use POST except when I am trying to debug an annoying CGI error and want to verify what is being sent without having to write PHP code to verify it. You can read a lot more about GET and POST in technical manuals on the Web. You'd have to understand them if you were going to write your own Web server (but I recommend saving that for your next computing class).

PHP's Pre-Defined Form Variables

Take a look again at the list of PHP pre-defined variables again (mentioned last class). We will get started learning about these here and continue until the end of our class together. The $_REQUEST variable is an extremely valuable variable for processing the HTTP Request variables. The HTTP request variables are those that are passed in from the form through the Common Gateway Interface (CGI). The CGI specifies a convention for passing variables: attribute name=value. Each attribute is separated from the next by an ampersand (&) and the whole CGI variable string is separated from the form processing URL by a question mark.

As an example, you can fill out the form above and click on the Upload button. I did that just now and watched as the URL in my Web brower address text field changed to:

http://www.oworld.org/risd/Oct20.php?uploaded=mypicture.jpg&essay=my+essay+here&e-mail=bogus%40bogus.net&lat=41.976&lon=-72.8798&direction=N

If you compare this to the source code of the form (use your browser to View-Page Source from its menu bar). You see some tie-ins between the form elements' HTML attributes and the URL that is generated (note you would not see the URL if the method was set to POST instead of GET in the opening form tag). Each of the different form elements that I generated have a name attribute and a given name as its value. The upload file INPUT element has a name=uploaded attribute-value pair which tells the CGI that I wish to pass the text entered in the text field as a variable named uploaded. Similarly:
  • The essay TEXTAREA element contains a name=essay attribute-value pair.
  • The e-mail INPUT element contains a name=e-mail attribute-value pair.
  • The lattitude INPUT element contains a name=lat attribute-value pair.
  • The longitude INPUT element contains a name=lon attribute-value pair.
  • The direction SELECT element contains a name=direction attribute-value pair.
These name attributes set up the ability for PHP to read their values using the pre-defined $_REQUEST PHP variable. The submit INPUT control is also critical - it must exist between the opening and closing FORM tags. The submit control sets up the form button that your Web audience can use to tell their browser they are ready to submit the form data. Clicking on the submit button initiates the sending of the CGI URL back to the server (to the ACTION address you specify). Hopefully, you have experience with HTML form processing so you can focus on the back-end process now.

Your PHP code is going to process your CGI data one variable at a time. You can access the e-mail address using one line of code:

$e-mail = $_REQUEST['e-mail']

You put the variable name you want to retrieve inside of the [''] syntax. Where have you seen that before? Oh, right. Last lecture we discussed associative arrays. A ha! The $_REQUEST variable is actually of type associative array. Everything we learned about associative arrays is then available to us in processing our form's data request returned to the server. You can embed the $_REQUEST['e-mail'] directly into code or save the array element into your own variable. The assigned variable will be set up as a variant at first - meaning if you then use the variable in a mathematical expression, it will behave as the appropriate numerical type (whole number or real number with decimal fraction). If you put it into a String context (like a substr() function), it will behave as a String. Pretty snazzy processing that simplifies things wonderfully (and has become a feature to older programming languages recently).

I want you to examine and discover form processing through the Save The Bay project example we are using in class. Go ahead and look at /var/www/html/dynphp/bdc/stb/process_trawl.php on our class server. Investigate how the trawl.php form communicates with the processing code.

The import_request_variables() function

bool import_request_variables ( string types [, string prefix] )

Imports GET/POST/Cookie variables into the global scope. It is useful if you want to see some of your form variables in global scope (remember that global scope means accessible anywhere on your page that has embedded PHP within).

Using the types parameter, you can specify which request variables to import. You can use 'G', 'P' and 'C' characters respectively for GET, POST and Cookie (we will look at Cookies processing next lecture). These characters are not case sensitive, so you can also use any combination of 'g', 'p' and 'c'. POST includes the POST uploaded file information. Note that the order of the letters matters, as when using "gp", the POST variables will overwrite GET variables with the same name. Any other letters than GPC are discarded.

The prefix parameter is used as a variable name prefix, prepended before all variable's name imported into the global scope. So if you have a GET value named "userid" (meaning you have a name="userid" input component on the submitted form), and provide a prefix pref_, then you'll get a global variable named $pref_userid. If you're interested in importing other variables into the global scope, such as SERVER, consider using the function extract().

Note: Although the prefix parameter is optional, you will get an E_NOTICE level error if you specify no prefix, or specify an empty string as a prefix. This is a possible security hazard. Notice level errors are not displayed using the default error reporting.

<?php
// This example will import all GET and POST variables from our form if you place it within your PHP form processing code
// if you use an "rvar_" prefix, you get "rvar_" prefixed to all your named variables
import_request_variables("gP", "rvar_");

// this next line prints out the latitude value filled into our form. You can use it in computational code once stored in the $rvar_lat variable.
echo
$rvar_lat ;
?>

Checking Input Values

Once we have retrieved our form variables, we should test them to see if they make sense given their context. For example, the terrestrial latitude system we use conventionally in the United States permits latitude to range from -90 degrees (the south pole) to 90 degrees (the north pole) with a useful computational value of 0 for the equator. We would want to check the lat submitted against this validation rule:

<?php
// Get our variables
import_request_variables("gP", "rvar_");

// this next line prints out the latitude value filled into our form. You can use it in computational code once stored in the $rvar_lat variable.
if (
$rvar_lat < -90.0 || $rvar_lat > 90.0 ) {
echo 'Latitude out of range - please return to your form, enter a value between -90.0 and 90.0, and resubmit your form.';
}
?>


Of course, as you get more sophisticated with your error processing, you can do a lot more than just ask your form submitting friend to hit the back button on her browser and repeat the form process. Lots of great reading on PHP error processing on the Web. But, since we and our friends don't make errors. we won't waste our time on that topic in class together.

The isset() function

Isset() is a very helpful PHP pre-defined function that lets you test to see whether a variable is set or not. If someone fills out your form, but skips over a field you've requested from them, that variable value will not be set in the form processing code.

If a variable has been unset with unset(), it will no longer be set. isset() will return FALSE if testing a variable that has been set to NULL. Also note that a NULL byte ("\0") is not equivalent to the PHP NULL constant.

If multiple parameters are supplied then isset() will return TRUE only if all of the parameters are set. Evaluation goes from left to right and stops as soon as an unset variable is encountered.

Parameters

var

The variable to be checked.

var

Another variable ..

...

Return Values

Returns TRUE if var exists; FALSE otherwise.

Examples

Example #1 isset() Examples

<?php

$var 
'';

// This will evaluate to TRUE so the text will be printed.
if (isset($var)) {
    echo 
"This var is set so I will print.";
}

// In the next examples we'll use var_dump to output
// the return value of isset().

$a "test";
$b "anothertest";

var_dump(isset($a));      // TRUE
var_dump(isset($a$b)); // TRUE

unset ($a);

var_dump(isset($a));     // FALSE
var_dump(isset($a$b)); // FALSE

$foo NULL;
var_dump(isset($foo));   // FALSE

?>

This also work for elements in arrays:

<?php

$a 
= array ('test' => 1'hello' => NULL);

var_dump(isset($a['test']));            // TRUE
var_dump(isset($a['foo']));             // FALSE
var_dump(isset($a['hello']));           // FALSE

// The key 'hello' equals NULL so is considered unset
// If you want to check for NULL key values then try: 
var_dump(array_key_exists('hello'$a)); // TRUE

?>

Type-casting

PHP will automatically convert data types as necessary across the board - you need not worry about it happening. If you specifically wish to override PHP's type conversion, you can perform what is called a type cast - you forcibly convert a variable of type A to type B. In PHP, type casting looks like this:

<?php
    $mystring
= "wombat";
    
$myinteger = (integer)$mystring
?>

At first, $mystring contains a string. However, we type cast it to be an integer, so PHP will convert it to an integer, and place the result into $myinteger. You can type cast as boolean using (bool), string using (string), and floating-point using (float).

Type casting is most often used to specifically enforce a type in order to provide extra security or just to make sure a set type of data is being used. For example, if your script absolutely requires an integer number, it's a smart move to typecast your variable with (integer) so that PHP will convert any other type to integer or do nothing if the type is already integer. Converting a float to an integer will automatically round the number down, and is actually faster than using the equivalent function.

Using the null Comparison

I tend to use the null comparison a lot in my code because it is so prevalent across programming languages - especially those languages with built-in HTML form processing features in their libraries or pre-defined function sets. Basically we can check to see if a request variable has been sent by checking to see if it is null. If it is, it has not been set (which may or may not be an error condition depending on your form design and function).

<?php
    if ($REQUEST_['lat']==null) {
// would evaluate to false (since the lat field was filled out in the form).;
    
if ($REQUEST_['lon']==null) { // would evaluate to true (since the lon field was not filled out in the form).
    }}
?>


Testing for null is a great way to verify that a variable has been send for processing before you attempt to use it in any of your PHP form processing routines.

Time to Practice Now

Now you can go to town designing the forms for the Ocean Picture of the Day and Save the Bay Citizen Science and make sure you have access to the form contents when it is submitted. Soon enough, we'll be tying those values to a database for storage and reporting on the values. Let's look at an example where someone fills out our form above without understanding the lonlat planetary geographical location system. Let's say they think they only need to fill out a latitude and not a longitude for where they took a scientific reading from a sensor device.