PHP examples

Examples include:


"Image submit button"

Image input form-fields, which act as "submit" controls, can serve as effective tools for queries that involve specifying locality. The text example uses this mechanism to identify fast-food outlets that are closest to selected points on a map.

The form is:

<html>
<head>
<title>Hunger!</title>
</head>
<body bgcolor=white>
<form action=hunger.php method=get>
<table frame=border rules=all align=center>
<caption>Desperate for a Big Mac?</caption>
<tr>

<td rowspan=2><input type="image" src="iwol.jpg" name="map"></td>

<td><img src="mac.jpg"></td>
</tr>
<tr><th>Click on map</th></tr>
</table>
</form>
</body>
</html>

The <input type="image"> control identifies the file with its image and of course has a name so that its input values can be identified. Another image file provides an iconic representation of the fast-food outlet. (You will have to provide your own image with a map of your area and icon for your preferred food store chain.)

When the map is clicked, the browser will submit a "get" request to the hunger.php program on the server; the request will include the coordinates of the mouse click point as a query string (e.g. ?map.x=87&map.y=121).

The processing code is:

<html>
<head>
<title>Your nearest MacDonalds ... </title>
</head>
<body>
<h1 align=center>Your nearest MacDonalds</h1>
<p align=center>
<img src=mac.jpg>
<p>
<?

function distance($x1,$y1, $x2, $y2) 
{
	$dx = $x1 - $x2;
	$dy = $y1 - $y2;
	return sqrt($dx*$d + $dy*$dy);
}

// Data defined as simple arrays with
// an address as a text string, and a location
// defined in terms of the coordinates used in the map image
$mac1 = array (
	"address" => "street-1, suburb-1",
	"x" => 130,
	"y" => 310
);

// Would need a few more locations defined to make
// it all worthwhile

$mac6 = array (
	"address" => "street6, suburb6",
	"x" => 145,
	"y" => 383
);

$macs = array ($mac1,  $mac6 );

// Form data will be recieved as map_x, map_y
// (Submitted names map.x, and map.y are changed automatically)

$mindist = 1.5E6; // a long way to go for a mac
foreach($macs as $ff) {
	$dist = distance($map_x, $map_y, $ff["x"], $ff["y"]);
	if($dist < $mindist) {
		$mindist = $dist;
		$best = $ff["address"];
		} 
}

print "<h2 align=center><em><font size=+2 color=red>$best</font></em></h2>";

?>
<p align=center>
<img src=mac.jpg>
<p>
</body>
</html>

Often, this approach is just an alternative to fixed HTML pages with image maps that have predefined page links. But it is more general in scope as the processing of locality data can be as complex as you wish.


Hidden fields in forms used to store state

Hidden fields are one of the standard ways in which state data can be saved. But they are limited. They really only work when interaction between browser and web server involves a sequence of forms that must all be completed and returned. Further, the data in the "hidden" fields are actually viewable on the browser (try the "View Source" option in the browser controls), and are editable by even the most junior grade of hacker. Consequently, the hidden fields mechanism is not really viable.

The example has:

The example code has the company merchandise defined as a set of simple structures; of course, a real application would obtain these data from a database. The final reporting code illustrates how to iterate through the set of all returned name/value data, including how to recognize "arrays" of data such as come from multi-choice selection form fields or, as in this example, sets of checkboxes.


File upload

The text's example for file upload uses a form for uploading code and documentation for a programming task. A browser displaying this web page will open a file selection dialog that allows a user to select a file whose contents are to be sent to the script running in the web server.

The processing script illustrates a variety of system calls for tasks such as creating directories, copying files, and reading the contents of files.


Database

All versions of PHP will have support for MySQL built-in; Windows versions of PHP also have ODBC. Many other database systems are supported; you have to rebuild your Unix/Linux PHP with appropriate configuration options.

Each database has a slightly different API, but there are strong similarities. Code connects to a database, it then prepares statements with SQL, data values are bound to parameters before SQL update and selection statements are executed. (Some databases only support simple statements with fixed SQL strings, others have parameterized statements.) An update will typically return a count of rows affected; a selection will return some form of "cursor" that can be used to access successive rows of the returned data. Field access functions return data in specific fields of a returned row (some APIs work in terms of index numbers for fields, others use column names, some support both styles).

The introductory example in the text illustrates a PHP script that can return selected data from a table of soccer results. The table is in a Microsoft Access database; the ODBC library functions are used to retrieve data.

This example illustrates a very common PHP style where the same script handles both "GET" and "POST" queries. A "GET" request results in the display of a form with options (list drawn games, list home wins etc). The submission of this form results in a "POST" style invocation of the same script. A "POST" is handled by code that generates a response page with data take from the data table.

The main function of the script checks for posted data. If there are no data, this is a "get" request and the script generates the form page.

The functions to list all games, list drawn games etc are very similar; they vary only with regard to the actual SQL query that must be run and the title that is to be placed on the generated table.

The processing of a search involves Connection to the database, execution of the query, and generation of a tabular report. The report generation step involves iterating through the rows accessed via the "database cursor", extraction of data values and embedding of these values in rows of a HTML table.

The addition of data first involves display of an extra input form. If a Post request is received with no value for the "choice" parameter (radio button in original form) then it is assumed to be new posted data that are to be added to the table. The function to add data again needs a connection to a database, daa must be extracted from the submitted form and checked, and then an update (insert values into table) must be run.

A datatable definition for this example would be trivial:

create table Teams (
	Name1	varchar(32),
	Name2	varchar(32),
	Score1	integer,
	Socre2	integer
);

The actual table used was defined visually in Microsoft Access.

As was the case with Perl, you may find it necessary to have your code set some environment variables before it attempts to open a database connection (some database systems obtain the equivalents of database URLs from environment variables). For example, code using Oracle might have a set environment and connect functions like the following:


function setOracleEnvironment() {
// Obviously, database locations and component names will vary.
	putenv("ORACLE_HOME=/packages/oracle8/u01/app/oracle/product/8.1.6");
	putenv("ORACLE_SID=csci8");
	putenv("TWO_TASK=csci8");
}

function connect() {
	$user = “HSimpson";	$password = “Duh";
	$db = Ora_Logon(  $user, $password ); 
	if (!$db) die( "The Oracle refuses to admit you"  ); 
	// switch on auto-commit mode
	Ora_CommitOn($db);    
	return $db;   
}

There will also be differences in the functions used to perform datbase operations, e.g.:

function report($caption, $comment, $cursor) {
	$count = 0;
	while( Ora_Fetch( $cursor) ) { 
		if($count==0) tableheader($caption); $count++;
  		$Team1 = ora_getColumn($cursor, 0);
 		$Score1 = ora_getColumn($cursor, 1);
  		$Team2 = ora_getColumn($cursor, 2);
 		$Score2 = ora_getColumn($cursor, 3);
		print "<tr><td>$Team1</td><td>$Score1</td><td>$Team2</td><td>$Score2</td></tr>";
	}
	if($count>0) print "</table>";
	else print "<h3 align=center>$comment</h3>";
}


"Big Brother" example

Dynamically generated graphic pages (or graphic images embedded in larger pages) are an effective way of presenting many forms of data. The text uses an example where the response page incorporates a histogram generated dynamically and reflecting the input of the post request that caused its generation.

The application allowed students to run a voting campaign against academics, in much the same way that TV stations running the "Big Brother" shows allowed viewers to vote for the contestant that was next to be dismissed. (The students' verdict was not binding on the school; and in any case the election process was declared void after one student ran scripts to cast tens of thousands of votes favoring expulsion of particular academics!) The system involved a static HTML page for vote casting, and a dynamically generated page with a histogram summarizing the current voting patterns.

Voting form Histogram

The form and code are supplied in generic form; you need to add names and images of candidates.

The form is simply a table submission buttons; either "image" buttons (for faculty where a photo was available) or ordinary "submit" buttons:

<tr>
	<td>
		<input type="submit" name="Person17" value="Person17">
	</td>
	<td>
		<input type="image" src="images/Person18.jpg" name="Person18" width="100" height="150">
	</td>
</tr>

(Images must be held in an images subdirectory of the directory containing the HTML page and should be in a 2:3 (width:height) ratio; image formats other than jpg are equally usable.)

Submission (by clicking on a submit button or image) sends the name of the input field to the "bigbrother.php" server side script.

The processing script supplied works with an Oracle database; the database login sequence and all invocations of database operations will need adjustment to use another database. The script works with two tables. The first holds names and votes (a varchar and an integer field); there are entries in this table for each person in your school's faculty. The second table holds just one data item - the IP address of the last voter. This provides the basis for a very crude throttle on scripted client voting. If a vote is received with a client IP address the same as that last recorded, then this new vote is not counted and there is a delay of several seconds before a response is sent. This won't deter serious hackers (who will run their client side scripts on several machines with different IP addresses or, if more sophisticated, fake their client's IP address making it something random). If you want an idea for a good way to deal with problems of scripted client access, go to Yahoo and check out the mechanism that they use to eliminate script access in the acquisition of Yahoo mail accounts.

The main function of the script starts by setting up its environment and obtaining a database connection. Then the simple check on scripted voting is enforced.

The main program continues with code to pick up the selected candidate. The input will either be the name of a submit button or the name of an image button with "_x" (and then "_y") appended. Once the candidate is identified the update function can be run to increment the appropriate vote count. Data summarizing all recorded votes are obtained with the getdata function; this runs a simple "select * from table" SQL query. The data are returned as an associative array with candidate name as index and vote count as value.

The makeImage function generates a PNG image of a histogram that represents the voting patterns. This image is written to a temporary file. The generated HTML page will contain an <img> link referencing the temporary file. You have to arrange for a regularly scheduled job to run to delete image files that are left. The returned HTML page has an expires HTTP header with an expired date (caches etc won't save the HTML page so links to a soon to be destroyed picture won't stay around).


Cookies and session state

The final example illustrates how to maintain state with the help of cookies. Two versions are provided, one using explicit code for cookie and session state maintenance and a second using functions from PHP's session library. The explicit style provides a clearer view of the underlying operations.

The example uses a small web of pages, almost all generated by scripts though there are a couple of static HTML pages. It represents a commercial site (a micro-Amazon) that has divisions selling books, computers, and office furniture. Customers are supposed to enter via the Welcome page where a new session gets allocated. They can then browse the pages with the books etc, use the purchase page to add items to a shopping cart, and either continue browsing or proceed to checkout and final pages. There is an extraneous jobs page; it is there simply to illustrate how diversion into other pages does not affect the cookie based session. The pages that offer goods simply use some static HTML text with a couple of pre-defined items; naturally, a real site would obtain the data from a database and generate the product listings dynamically.

Document web

Explicit cookie code

This version uses the following elements:

The code to allocate a randomized session cookie (this one named "SessionData") is standard:

<?
$existingSession=$HTTP_COOKIE_VARS["SessionData"];
if(!isset($existingSession)){
	srand((double)microtime()*100000);
	$token = md5(uniqid(rand()));
	setcookie("SessionData",$token,0);
}
?>

This code runs at the start of the Welcome script (because it is setting header data, it must run before any other output is generated). The expiry time (third argument in setcookie is zero, identifying this as a session cookie that is to be discarded when the browser exits). The other pages share a code fragment that checks for this cookie and redirects cookie-less clients:

<?
$existingSession=$HTTP_COOKIE_VARS["SessionData"];
if(!isset($existingSession)){
	header("Location: getwithit.html");
	exit();
}
?>

The purchases script creates or appends to a file, named to correspond to the cookie's sessionid. Data written to the file are run through the urlencode (and decode) functions - this is simply to avoid any problems with input data that includes characters such as newlines that might confuse the script that reads the file contents.

include("chkrdir.inc");
// chkrdir.inc will either
//    set global variable $existingSession to our sessionkey
//    or, if no session key found, redirect the user.
function recordorder($item) {
	global $fp;
	$coded = urlencode($item) . "\n";
	fwrite($fp,$coded);
}
function process($data) {
	if(!isset($data)) return;
	if(!is_array($data)) return;
	if(empty($data)) return;
	foreach($data as $val)
		recordorder($val);
}

$computers = $HTTP_POST_VARS["computers"];
$books = $HTTP_POST_VARS["books"];
$furniture = $HTTP_POST_VARS["furniture"];
$filename = "./orders/" . $existingSession;
$fp = fopen($filename, "a");
process($computers);
process($books);
process($furniture);
fclose($fp);
?>
<!-- Rest of page in static HTML -->
...

Similar file processing occurs in the final reporting page.

It is possible to adapt to cookie rejecting clients and use URL encoding. Examples in the servlet section illustrate this approach. Basically, the allocated session identifier must be embedded in all links included in pages that are returned to clients. The simplest mechanism is to add it as a query string. When subsequent requests are received, the session id is obtained from the query string. The following code fragments illustrate the mechanisms. First, there is the code that checks for a query string with a session id; an id is generated if none was found.

<?
unset($token);

$qstr=$HTTP_SERVER_VARS['QUERY_STRING'];
if(preg_match("/SessionData=([0123456789abcdef]{32})/",$qstr,$matches)) {
	$token = $matches[1];
}
if(!isset($token)){
	srand((double)microtime()*100000);
	$token = md5(uniqid(rand()));
}
$tag = "?SessionData=" . $token;
?>

Elsewhere, code must modify all URLs by adding the query string tag, e.g. :

<p>Our departments:
<ul>
<li><a href=furniture.php<? print $tag ?> >Furniture</a>
<li><a href=computers.php<? print $tag ?> >Computers</a>
<li><a href=books.php<? print $tag ?> >Books</a>
</ul>

PHP sessions

The PHP Sessions library provides a higher level API for programming these state maintenance tasks. You can "register" variables with a session, and the library code will presist the values assigned to these variables. Normally, the library uses files for persistence, but there is a module for in-memory persistence (much like Session variables in the later servlet examples).

A recoded version of the same document web would start with a Welcome.php page that establishes the session and registers variables. This example uses a very simple representation of a "shopping cart" - it is going to be a single string variable to which are appended the product codes of all items that are selected. Consequently, there is just the one session variable, $stuff, that has to be registered. The new PHP script at the start of the Welcome page is:

<?
session_start();
session_register("stuff");
session_save_path("orders");
$stuff= "";
?>

Other pages start with <? session_start(); ?>. The code in the purchases page that adds new items to the "shopping cart" is:

session_start(); 
$stuff = $HTTP_SESSION_VARS["stuff"];

function recordorder($item) {
	global $stuff;
	// Append new product code to string that represent "Shopping cart"
	if(isset($stuff)) $stuff = $stuff . ", " . $item;
	else $stuff = $item;
}

The final page should destroy the session data:

print "$stuff 
"; session_destroy();