Cover  Copyright  Contents  Reference  Index  previous  next
Chapter 2. PHP BASICS
1. SAMPLES
2. CONCEPT
3. SYNTAX SUMMARY

1. SAMPLES

Contents:
1. SAMPLES
1.1 Extracting keywords from a document
1.1.1 Uploading a file to the server
1.1.2 Handling the uploaded file on the server side
1.1.3 Extracting the words
1.1.4 Client side selection of the words - Javascript objects
1.2 Static methods for handling strings (PHP 5)
1.3 Client-side confirmation of the keywords
1.3.1 The __autoload function
1.3.2 Retrieving information from the client
1.3.3 Data file access
1.3.4 Creating Javascript objects
1.3.5 Client side operations
1.3.6 Link to the file editing script
1.4 File editing
1.5 An order entry Javascript application
1.5.1 The master page
1.5.2 The permanent header page
1.5.3 The starting page
1.5.4 The catalog pages
HEAD section - Functions
Customer name and address panel
Update product family and detail list
Write out product selection list
The SHOW ORDER button
The CONFIRM button
The CHANGE button
The SEND button
Additional family list
Restore information
1.6 Database table creation
1.6.1 Table description form in HTML/Javascript
1.6.2 Table creation PHP script
1.6.3 Table display script
1.6.4 Table insert script
1.7 An application generator
1.7.0 Database tables
1.7.1 Template pages
1.7.2 The application generation script

Here are a number of sample scripts intended to introduce you to some of the most widely used functions of PHP. Presented also are the most needed Javascript functionalities, especially those dealing with the HTML elements. Those are executable scripts that you can run by clicking on their names, displayed on the left, above the codes.

For the scripts to be run, you have to install the PHP and the Web server systems as recommended. That is, in the Web server configuration file, the samples directory that comes with this document has to be declared as an alias, with the name of hpp.

In the requests used in the following, the scripts are considered set in the local host, the Internet name of which is localhost. To run the scripts you need not connect to anything.

As the problems to solve by these scripts are "realistic", they can't help but be long. A good approach to studying a script is:

1.Run the script by clicking on its name, displayed above its code
2.Read and try to understand the code
3.Look at the lengthy explanation on the right, if you do not understand the code

The first four scripts described in 1.1 to 1.4 below make up one application were words are selected from an HTML text to compose an index which holds the hyperlinks to these words. The first script (SampleGetKeywords.php) gets all the words from the document text. The second one (StringHandler.cla) holds a number of functions to be used in the subsequent scripts. The third one (SampleConfirmWords.php) is called from the first and lets you select the words to be retained into the index. The fourth one (SampleEditFile.php) is called from the former to create the index appended to the document.

This application is intended to illustrate some of the functions available with PHP, and also the use of Javascript. In an interactive application, HTML is the client-side component, which no less important that the server-side's, so that to devise a good application, a sound knowledge of HTML is required, along with a scripting language such as Javascript which is chosen here. HTML and Javascript are widely drawned on in the samples, but are not part of the book. A presentation of these topics is to be found elsewhere.

1.1 Extracting keywords from a document

This is a script which extracts all the words contained in a document, and lets the user decide which ones to retain as 'key words'. The retained words are sent to a script described in 1.3 below for further processing.

When the script is first requested, it displays a file selection box to let the user select the document from which to extract the words.

Please, let the mouse pointer hover above the script name 'SampleGetKeywords.php' below (under the 1.1.1 title), to see the request message displayed in the message bar at the bottom of the screen (that is, if your browser offers this amenity, which it must). You can click on this name to have the file selection box displayed. By clicking on the button to the right of the input field, you will open the directory tree of your computer. Navigating through this tree lets you select the document to process.

A good idea is to click now, to run the application before reading further. You may select the document doc/WaldenPond.html, in the doc directory, for this test.

Clicking on the 'GO' button will upload the selected document to the server, along with a new request for the present script. Uploading a document means sending it from the client side to the server, as opposed to downloading which is sending from the server to the client.

When requested this time, the script sends back the words it has been retrieved from the document, set in an HTML page which contains a Javascript code to display each of the words in turn, for you to decide whether to accept it as a keyword or not. A distinction is made between 'discarding' and 'rejecting' a word. 'Discard' is intended to mean not to accept the word this time, 'reject', not to accept ever; to implement this distinction, the rejected words must be recorded somewhere for future use. This is not done in the present script, the idea is just a suggestion that you can implement.

The scripts are intended for illustration purpose. They have none of the features needed to make them run efficiently. So please do not use too long a document to test them. The 2 documents in the doc directory that comes with this document are intended for your use.

1.1.1 Uploading a file to the server

SampleGetKeywords.php
<?php
if (count($_FILES)==0){ //FIRST PHASE
  $script = $_SERVER['SCRIPT_NAME'];
?>
<HTML><HEAD>
<TITLE>KEYWORDS - FIRST: SELECT FILE</TITLE>
</HEAD>
<BODY>
<FORM action="<?php echo $script ?>"
      method="POST" 
      enctype="multipart/form-data">
Select the file to process:<br>
<INPUT type="file" name="upfile"
       size="96">
<br>
<BUTTON type="submit" name="sub" 
        value="go">GO</BUTTON>
</FORM>
<?php
}//end if count($_FILES)

(The instructions for use with follow this code have been omitted, not to overburden this presentation. To view the complete script, please click here.)

Here is a technique widely implemented in the following, to use two different parts of the same page in two successive phases. First, the page displays an entry FORM, then it is called back to process the data entered through the FORM. A condition is used to discriminate between the 2 phases.

The discriminating condition here is count($FILES)==0. The $_FILES array contains information on the files uploaded by the user. When the page is first scheduled, the array is empty, as no file is uploaded yet; the count($FILES) function returns 0. Then, the part of the PHP sequence comprised in the curly braces of the if structure is executed. Also the HTML code, even though outside the scope of the <?php ... ?> bracket, is controlled by the if statement. It is generated when the condition is true.

The item with the key 'SCRIPT_NAME' in the $_SERVER superglobal array (3rd line of the script), is the name (or more accurately, the URI) of the current script. It is retrieved into the variable $script and written out in place of the value of the FORM 'action' attribute, so that the script is called back when the 'submit' button (labelled 'GO') is clicked.

The HTML code defines a FORM element which contains an INPUT element with the type file. Most browsers generate a file explorer for this type of INPUT element, so that you can navigate through your directory tree, and select a file. When you click on the BUTTON of the 'submit' type, the contents of the selected file is sent to the server, along with the request for the script identified by the action attribute of the FORM element.

The PHP sequence resumes at the right of the action attribute, and the echo function (same as print) writes out the value of the variable $script, which was seen to identify the current script, at this location of the HTML page.

For the upload to perform correctly, the FORM element must have the enctype="multipart/form-data" attribute value.

When you click on the 'GO' button, the file is uploaded to the server, along with a new request for the current page.

else { //SECOND PHASE
  $file = $_FILES['upfile'];
  echo "File ".$file['name'];
  $size = $file['size'];
  echo " size: $size<br>";
  $tmp = $file['tmp_name'];  
  $s   = file_get_contents($tmp);
  echo str_word_count($s)." words";
  $upfile = "files/".$file['name'];
  copy ($tmp, $upfile)
    or die ("Unable to copy file");

1.1.2 Handling the uploaded file

As a result of your clicking on the 'GO' button, the script is called again. The information on the uploaded file is set by the PHP server in the $_FILES array, so that the item count of this array is not 0, therefore the else part of the if statement is executed. The INPUT element which defined the uploading is named 'upfile', so that the information is to be found at the key value 'upfile'. This information is an array which the script here retrieves in the $file variable. This is an associative array which contains predefined keys:
-'name' is associated with the name of the original file (at the user's site)
-'size' is associated with the file size
-'tmp_name' is associated with the name of the file as uploaded at the server side, which is temporary
-etc...
The script gets the contents of the uploaded file, using the file_get_contents function, then prints out the number of words contained in the file, obtained through the str_word_count function.

The uploaded file which is temporary is copied to the files directory in the server, under the same name as at the client site.

The expression "file/".$file['name'] is made of:

-2 terms, the constant string "file/" and the variable $file['name']
-the concatenation operator "." which appends the second term to the first
For example, if $file['name'] is "WaldenPond.html", the value of the expression is "file/WaldenPond.html". Note that the dot in "WaldenPond.html" is a data character, not an operator, because it is enclosed within the quotes.
//1. cut off left part up to <BODY>
  if ($pos = stripos ($s,"<body>"))
  {
     $s = substr($s,$pos+strlen("<body>"));
  }//end if $pos

//2. remove < ... > tags
  $p = $q = 0;
  $res='';  //result string

  while (!(($q=strpos($s,"<",$q))===false))
  {
    $res .= " ".substr($s,$p,($q-$p)); 
    $p = strpos($s,">",$q) //skip the <...>
    or die ("Bad document - Missing >");
    $q = ++$p; //go just beyond > 
  }//end while ------

  $res .= substr($s, $p); //right end
  $res  = strtolower($res);

//3. get words  
  $list = str_word_count ($res, 1);
//4. sort words
  sort ($list,SORT_STRING);
//5. discard multiplets
  for ($single[0]=$list[0], $i=1; 
       $i < count($list); $i++) {
     $a = $list[$i];
     $b = $list[$i - 1];
     if ($a != $b) $single[]=$a;
  }//end for

1.1.3 Extracting the words

1.The function stripos returns the position of the "<body>" sequence, if one exists disregarding case (the i in the name indicates case insensitivity), or false, otherwise. strlen returns the length of a string. The substr returns the substring starting from the character just beyond the "<body>" or "<BODY>" sequence, to the end of the original string, i.e. the part including this sequence and to its left is discarded.
2.The while loop removes the strings contained in < ... > tags, and returns the result in $res. The loop ends when no more '<' sign is found.
Let's look into the condition that controls the while loop execution:
-the expression ($q=strpos($s,"<",$p)) assigns the value returned by strpos, to $q, and on the other hand, has a value and a type, which are the ones assigned to $q. The === defines an expression which is true when the left and right hand sides agree in both value and type. When strpos returns 0, the expression has the value false, but is numeric; in this case, the condition ($q=strpos($s,"<",$p))===false is false, and its negation as indicated by the ! sign is true. This draws the distinction between the case where the < sign is found in the first position of the string, and that where it is not found.
At the start of each iteration:
.p points to the beginning of a sequence outiside the < ... > tags
.q points to the < sign which limits such a sequence on the right
This sequence is appended to the result $res by the ".=" assignment operator. Then the next sequence is determined.
After the loop, the remainder of the string is retrieved.
The strtolower function converts all characters into lower case.
3.The str_word_count function, with the value 1 as the second argument, returns an array containing the words found in the string
4.The sort function sorts the array passed to it, ranking the words in string order. The array passed as an argument is modified, contrarily to the usual situations.
5.This sequence compares each item to the preceding item in the list, and retains it only if it is different. The result is the $single array which contains each word only once.
The operations to achieve the result are carried out in the for loop where the script contained within the curly braces are repeatedly run, controlled by the expression within the parentheses. The for statement with the control expressions are:
for ($single[0]=$list[0], $i=1; $i < count($list); $i++)
Within the parentheses are 3 groups of expressions, separated by a semi-colon (;):
-the first group: $single[0]=$list[0], $i=1 is run once, as the initialization step of the statement. It is made of 2 expressions separated by a comma.
-the second group: $i < count($list) is the condition tested at the beginning of each iteration; if true, the iteration is started, i.e. script within the curly braces is run -- if false, the statement is terminated.
-the third group: $i++ is run at the end of each iteration
In the script, the current word as contained in the $list[$i] item is compared to the previous one. If different, it is put in the next available position in the $single array. The notation $single[] denotes the item in the $single array, at the next available numeric index.

1.1.4 Client side selection of the words - Javascript objects

This part of the procedure occurs on the client side, so it is handled by the HTML page, more precisely, the Javascript code found there. In an HTML page, Javascript sequences are to be found in:
-the contents of SCRIPT elements, framed within the <SCRIPT> and </SCRIPT> tags
-in an event control script, such as an onclick attribute of an HTML element like an INPUT or BUTTON element.

The Javascript language looks like PHP in some aspects, notably in the control statements such as if, for or while. It also deals with objects, but uses a different notation:

-the property prop of an object obj is denoted obj.prop, instead of obj->prop
-the method meth(...) of an object obj is denoted obj.meth(...), instead of obj->meth(...)
In the following samples, we are going to deal with objects from Javascript predefined classes:
-array objects
-string objects
-HTML objects

The HTML objects represent elements contained in an HTML page:

-the HTML page itself, represented by the document object (it always has this name)
-the elements contained in the HTML page, identified by their names as defined by their name attributes.
Please click here to have an overview of the Javascript HTML objects.

The attributes of an HTML element are properties of the objects that represent this element.

Elements contained in an other element are also properties of the latter. Thus, in the page we are dealing with, the element item is contained in the element box. The object item therefore is a property of the object box. Conforming to Javascript object usage, the notation box.item represents the element named item contained in the element named box.

One widely referenced attribute in our samples is the value attribute, especially of INPUT or TEXTAREA elements. This attribute represents the text displayed on the screen, in the element. It is also a property of its element. Conforming to Javascript usage, box.item.value, e.g., is the value property of the element item, contained in the element box.

The <SCRIPT> element in the <HEAD> section here, defines the variables used in the Javascript code, including the array of the extracted words, arr, and the user defined function processOver(), called when the process ends.

//6. Create array of words
?>
<HTML><HEAD>
<TITLE>KEYWORDS SECOND: SELECT</TITLE>
<SCRIPT language="JavaScript">
nbr = <?php echo count($single); ?>; //# of words
arr = new Array (); //array of extracted words

<?php
for ($i=0; $i < count($single); $i++){
  echo "arr[$i]=\"$single[$i]\";\n\r";
} 
?>;

idx = 0;  //index in arr array

function processOver () {
   box.item.value='';
   box.message.value='OVER !'; 
   box.message.value+='\nProcessed: '+nbr;
   box.message.value+='\nAccepted: '+box.acc.value;
   box.message.value+='\nDiscarded: '+box.dis.value;
   box.message.value+='\nRejected: '+box.rej.value;
   box.accword.disabled='true';
   box.disword.disabled='true';
   box.rejword.disabled='true';
}
</SCRIPT>
</HEAD>
The array of words extracted from the document is created in Javascript code by the notation:
arr = new Array();

PHP code is used to generate those parts of the Javascript code which involve variable data:

-$single is the PHP array which contains all of the words extracted from the document. The function count returns the number of items it contains. This value is written out onto the Javascript code. The symbol \" is the so-called escape sequence for the " which says that this is the " to print out, not to use as a string delimiter.
-the for loop writes out the Javascript statements which assign the extracted words as values to the items of the arr array:
arr[0]="word_0"
arr[1]="word_1" ...
where: word_0, word_1 are the extracted words contained in the $single array, to be transferred from PHP which is executes at the server site, to the Javascript page which is sent to the client. Note that the double-quote is to be used in arr[0]="word_0" and the like, not the single-quotes, because a word is allowed to contain a single-quote, which would interfere with the delimiting ones.
processOver is a user defined function. The keyword function introduces the function definition.
In the function processOver:
box.item.value=""clears the INPUT element named item, contained in the FORM element box
box.message.value='OVER !'assigns the string 'OVER !' to the value attribute of the TEXTAREA element named message
box.message.value+='\nProcessed:'+nbr;the + sign is the concatenation operator, in Javascript; "+=" is an assignment operator which appends the string within the single quotes, to the value displayed in the message element, followed by the value of the variable nbr. The '\n' causes a line feed in the displayed result
box.accword.disabled=truedisables the INPUT element named 'accword', making a click on this element ineffective (you may have inferred from the notation that disabled is a property of the accword element).
This is Javascript language.
<BODY>
<FORM action="SampleConfirmWord.php" 
      method="POST"
      name="box"
>
<INPUT type="text" size="32"
       name="item" value=<?php echo $single[0]; ?>
>This word ?
<TABLE>
<TR><TD>
<INPUT type="text" name="accword" value="ACCEPT"
   onclick="
     box.accept.value=arr[idx]+'/'+box.accept.value;
     box.acc.value++;   //accepted word counter 
     idx++;             //index of next word
     if (idx < nbr)           //if more words remain
        box.item.value=arr[idx]; //display new word
     else processOver();      //else - conclude
  ">
<TD>Please click here to accept word
<TR>
<TD><INPUT type="text" name="disword" value="DISCARD"
    onclick="
      box.discard.value=arr[idx]+'/'+box.discard.value;
      box.dis.value++;
      idx++;
      if (idx < nbr) 
         box.item.value = arr[idx];
      else processOver();
  ">
<TD>Please click here to discard
<TR>
<TD><INPUT type="text" name="rejword" value="REJECT"
    onclick="
      box.reject.value = arr[idx]+'/'+box.reject.value;
      box.rej.value++;
      idx++;
      if (idx < nbr) 
          box.item.value = arr[idx];
      else processOver();
    ">
<TD>Please click here to reject permanently
<TR><TD><INPUT type="hidden" name="acc" value="0">
<TR><TD><INPUT type="hidden" name="dis" value="0">
<TR><TD><INPUT type="hidden" name="rej" value="0">
<TR><TD><INPUT type="hidden" name="upfile" 
               value="<?php echo $upfile ?>">
<TR><TD>Accepted:
    <TD><INPUT type="text" name="accept" 
               value="" size="96">
<TR><TD>Discarded:
    <TD><INPUT type="text" name="discard" 
               value="" size="96">
<TR><TD>Rejected:
    <TD><INPUT type="text" name="reject" 
               value="" size="96">
</TABLE>
<BUTTON type="submit" name="sub" value="go">
  SUBMIT
</BUTTON>
<BR>
<TEXTAREA name="message" rows="5" cols="64">
</TEXTAREA>
</FORM>
<?php
}//end else count($_FILES)
?>
</BODY></HTML>
 
This is the sequel to the above code. It contains the BODY section of the HTML page. This part is largely HTML and Javascript, with just a very short PHP sequence to close the else block. The rest is the FORM block which occupies all of the BODY element. This is not a good design, since when submitted, the form will emit the values of all of the INPUT elements many of which are not needed.

The INPUT element item displays the word for the user to decide on. The first word displayed when the page is loaded is written out onto the HTML page by the PHP code, at the server site. The subsequent words will be put there by Javascript, as will be seen right now.

The INPUT element named accword has the value 'ACCEPT'. The 'onclick' attribute defines the routine to execute when the element is clicked. Here, it is made up with the following 6 lines which appends the accepted word to the value of the accept element and moves forward to the next word in the word array:
box.accept.value=arr[idx]+'/'+box.accept.value

- the current word, contained in the arr[idx] item, followed by the '/' sign, is added to the left of the old value of the accept element (not to the right, so that you can see the last added word).
box.acc.value++
- the value of the acc element, which is the counter of accepted words, is incremented by 1.
idx++
- the current index in the arr array of extracted words is moved forward
if (idx < nbr)
  box.item.value=arr[idx];
else processOver();
- if the end of the array of words is not passed, the new current word is displayed in the item INPUT element, else, the processOver program is called, to display the final statistics.

The disword and rejword INPUT elements, to be clicked to discard and (resp.) reject the current word, are treated likewise.

At the end of the process, the accepted, discarded and rejected words are in the strings that constitute the values of the INPUT elements named accept, discard and reject respectively. In these strings, the words are separated by a slash (/) sign. The script SampleConfirmWord.php, assigned to the action attribute in the FORM start tag, is called to process these data when the submit button is clicked.

A method for automatically link to another page will be seen later on.

1.2 Static methods for string handling (PHP 5)

The script SampleConfirmWord.php described in the next paragraph will need a number of functions of common occurrence. They are basically string handling functions. Functions of common occurrence can be defined as static methods of a class. A method is a function defined within a class. Static methods can be called directly from the class, without the need to create an object. This script show how static methods are defined. The script described in 1.3 below will show how they are called.

The class is defined in the first line in the script below. The keyword class introduces the class definition. StringHandler is the name of the class.

StringHandler.cla
<?php
class StringHandler {

public static function insert (&$s, $n, $x) {
//insert $x at position $n
  $left  = substr ($s, 0, $n); //0 to $n 
  $right = substr ($s, $n);    //$n to end
  $s = $left.$x.$right; //insert $x 
  return true;
}//end function insert





public static function getWordPos ($s, $x) {
//return positions of $x in $s, outside < ...>
$s .= "<>"; //exists  '<' after strpos
$p = 0;
while (($q = strpos($s, $x, $p))!== false) {
//1. is matching string part of bigger word?
if (self::in_word($s,$q+strlen($x))){
   $p = $q+strlen($x);
   continue;  //continue grand while loop
}//end if
elseif ($q > 0) {
  if (self::in_word($s,$q-1)){
   $p = $q+strlen($x);
   continue;  //continue grand while loop
  }
}//end elseif
//2. is matching string inside a < ... > bracket?
  $r = $p;
  while (($r=strpos($s,'<',$r)) < $q){
     if (($r=strpos($s,'>',$r)) > $q) {
        $p = ++$r;
        continue 2; //continue grand while loop
     }
     $r = $r++;
  }//end while $r < strpos
//
  $pos[] = $q; //put position in array $pos
  $p =$q + strlen($x); //advance $p behind $x
}//end while - $q < strpos

if (isset($pos)) return $pos;
else return false; 
}//end getWordPos

public static function in_word($s, $n) {
  $c = substr($s, $n, 1);
  if (((strcmp($c,"a")>=0)&&(strcmp($c,"z")<=0))||
      ((strcmp($c,"A")>=0)&&(strcmp($c,"Z")<=0))||
         (strcmp($c,"'")==0) ) return true;
  else return false;
}//end is_wordchar

function __construct ($x) {
  $this->value = $x;
}//end function construct

}//end class
?>
The keyword static makes a method static.

The function insert inserts a string identified as $x at position $n, into the string identified as &$s. This notation is said to pass the argument by reference, i.e. the original string $s is modified by the operations performed within the function.
The string handling functions from the PHP core, used in insert are:

-substr($s,0,$n) - returns substring of $s starting at position 0, with length $n.
-substr($s,$n) - returns substring of $s, starting at position $n (position 0 is first), with length extending to the end of the string.

The function getWordPos returns the positions of the word $x in the string $s, outside of < ... > brackets. The result is an array. The string functions from the core PHP, used in getWordPos are:

-strpos($s, $x, $p) returns the position in the string $s, of the first occurrence of $x, found at or after position $p
-strlen($x) returns the number of characters contained in the string $x
The notation of the form self::in_word($str,$p) standing out in red, designates the method in_word($str,$p), with self:: meaning the class where the notation is used. This function, defined below, returns true if the character at position $p in the string $str is part of a word, i.e. is an alphabetic character (upper or lower case), or a single quote.

To start with, the '<>' characters are added to the end of the $s string, to make the detection of the < ... > groups simpler; $p, the position from where the $x string is searched for, is initialized to 0.

The grand while(($q=strpos($s,$x,$p))!==false) { ... } loop obtains successive positions of $x in $s, using the strpos($s,$x,$p) function, with $p advancing beyond the last $x position after each iteration. The loop ends when no more occurrence of $x is to be found, which is when strpos is false. In the loop:

1.a test is first performed to determine whether the sequence which matches the word $x is part of a more extensive word (e.g. noun matched with pronounce). This is done by using the in_word function to test whether the position just after ($q+strlen($x)) or just before ($q-1), the word $x is still part of a word. If either is, the position is rejected
2.then a test is performed to determine whether the matching string is comprised within the < and > signs. If it is, the position is rejected.
When not rejected after these tests, the position is put into the $pos array. The notation $pos[], with nothing between the square brackets, designates the item at the next available numeric index value in the $pos array.

The function in_word returns true if the character at position $p in the $s string is alphabetic or a single quote. It uses the strcmp function from the PHP core.

Here are some further comments on the above code:
-The continue statement used when a position is rejected has the following meaning:
.when used alone, it terminates the current iteration of the innermost loop, and causes the next iteration to start
.when used with the value 2, it terminates the current iteration of the 2nd loop encountered when moving outward from where it is
In the present example, all the continue statements terminate the current grand while loop
-The expression ($q=strpos($s,$x,$p)) assigns the value returned from the strpos function to $q, and also has a value, which is the value assigned to $q. This value can be interpreted as a true or false in the condition that controls the while loop (you see that an expression in PHP has manifold facettes).
A problem arises when the $x string is found at the beginning of $s: then the strpos function returns 0 a the first iteration. As 0 has the boolean value of false, this can be confused with the real false value returned when $x is not found (we saw this earlier in a similar connection). An expression using the !== symbol is true when the expressions on its right and left sides disagree either in value, or in type. Therefore ($q=str($s,$x,$p))!==false) is true when strpos returns 0 which differs from the boolean false by being of the integer type.

1.3 Client-side confirming of the keywords

This script is called by the SampleGetKeywords.php described earlier. It displays each occurrence of each of the retained words, in its context, i.e. with a number of the characters preceding and following the word (here, 128 characters before and 128 behind), and lets the user decide if an anchor (<A name=...> HTML element) is to be placed at this occurrence.

The script is called as a sequel to the word extracting process described in 1.1 above. In uses the static methods defined in 1.2 above.

This script is a mix of PHP and Javascript.

SampleConfirmWord.php - not to be called directly

<HTML><HEAD>
<TITLE>CONFIRM KEYWORD</TITLE>
<SCRIPT>
<?php
function __autoload ($x) {
  require_once $x . '.cla';
}//end __autoload 

//1. Retrieve data
$accepted  = $_POST['accept'];  //accepted words
$discarded = $_POST['discard']; //discarded words
$rejected  = $_POST['reject'];  //rejected words

//2. Get keywords into array
//2.1 Remove / characters
$wordstr = str_replace("/"," ",$accepted,$nbr);
//2.2 Get words into 'words' array
$words = str_word_count($wordstr,1);

1.3.1 The __autoload function

The function __autoload (2 underscores) provides for the automatic loading of needed classes. Here, the needed class is StringHandler. In the function contents, .cla is the extension assigned to class file names. The rest of the function is to be "as is".

To use this technique, each class description must be contained in a file having the same name as the class and the extension .cla. This file can be set in the same directory as the script. Another eligible location for a class definition file is the include directory, as defined by the PHP configuration file (usually called includes, under the PHP home directory.)

1.3.2 Retrieving information from the client

$accepted, $discarded, $rejected respectively contain the accepted, discarded and rejected words separated by a / sign. $upfile contain the path of the uploaded file.

The function str_replace("/"," ",$accepted,$nbr) replaces all occurences of the / sign by " ", thus separating the words by a space. The number of replacements is recorded in the variable $nbr, accessible from the script, after the return from the function. This is a further example of a variable passed by reference. The result is returned in the $wordstr variable (the $accept variable is unchanged)
The function str_word_count($wordstr,1) returns the array containing the words found in $wordstr.

//3. Read File to process
$upfile    = $_POST['upfile'];
$s = file_get_contents ($upfile)
or die ("Unable to read file");

1.3.3 Data file access

The $_POST['upfile'] item passed from the calling script contains the path of uploaded file that has been copied to a local location in the server. This information is retrieved into the $upfile variable. The function file_get_content($upload) returns the contents of the file
//4. Process file
$idx = 0;
foreach ($words as $w) {
//4.1 Get word positions in file
if ($pos=StringHandler::getWordPos($s,$w))
{
//4.2 List positions
  $win=128;
  for ($n=0; $n < count($pos); $n++) {
    $p = $pos[$n]; //word position in text
    $a = ($p-$win<0) ? 0: ($p-$win); //left
    $b = ($p+$win>strlen($s)) ? strlen($s): $p+$win;
    $b -= $a; //length
    $u = substr($s,$a,$b); //extract -$win to +$win
    $u = strtr($u,"\"\n\r","'  "); //translate
    $key = $pos[$n];
    $val = "$w: $u";
    echo "A$idx = {"; //object name
    echo "wrd:\"$w\",pos:\"$key\",val:\"$u\"}";
    echo "\n\r"; //line feed, carriage return
    if ($idx==0) {  //save first word and context
        $wz = $w;   //- first word
        $vaz=$val;  //- first context
    }
    $idx++;
  }//end for $n
}//end if $pos
else {
  continue;
}//end else $pos
}//end foreach $words
echo "idm = $idx;\n";
echo "val = \"$vaz\"\n";
?>
idx = 0;
function terminate () {
   box.zone.value="GO!";
   box.word.value="";
   box.accept.disabled="true";
   box.reject.disabled="true";
}
</SCRIPT>
</HEAD>



<BODY>
<?php echo count($words)." words"; ?>
?>
<FORM name="box" action="SampleEditFile.php" 
      method="POST">
<INPUT type="text" name="accept" value="ACCEPT" 
   onclick="
   show.value+=eval('A'+idx+'.wrd')+'/';
   position.value+=eval('A'+idx+'.pos')+'/';
   idx++;
   if (idx < idm) {
     word.value=eval('A'+idx+'.wrd');
     zone.value=eval('A'+idx+'.val');
   }
   else {
     terminate ();
   }"
>
Click here to accept 
<BR><INPUT type="text" name="reject" 
                       value="REJECT"
   onclick="idx++;
   if (idx < idm) {
     word.value=eval('A'+idx+'.wrd');
     zone.value=eval('A'+idx+'.val');
   }
   else {
     terminate ();
   }"
>
Click here to reject 
<BR>Accept this word here? 
<INPUT type="text" name="word" 
       value="<?php echo $wz ?>" size="32">
<BR><TEXTAREA name="zone" row="16" col="128"
          style="width:64em;height:16em;"
>
<?php echo "\"$vaz\""; ?>
</TEXTAREA>
<BR>Words accepted: 
<INPUT type="text" name="show" value="" size="96">
<BR><BUTTON type="submit" value="sub" name="go">
  GO
</BUTTON>
<INPUT type="hidden" name="position" value="">
<INPUT type="hidden" name="upfile" 
       value="<?php echo $upfile; ?>">
</FORM>
</BODY></HTML>

1.3.4 Creating Javascript objects

The foreach ($words as $w) statement retrieves each of the item contained in the array $words into the variable $w, and have it processed in the sequence enclosed within the ensuing curly braces.
The notation StringHandler::getWordPos($s,$w) is a call to the static method getWordPos defined in the class String::Handler. This class is automatically loaded as per the use of the __autoload function.
Le function getWordPos returns the positions of all of the occurrences of the word $w within the string $s, which contains the whole document to process.

The 3 calls to the echo function generate a Javascript object in the form:

A$idx = {wrd:"$w", pos:"$key", val:"$u"}
 
where:
$idxis the rank of the object in the series: A0, A1, A2, etc.
$wis the word associated with this object
$keyis the position of this occurrence of the word
$uis the word occurrence context composed of the word $w framed in the 128 character sequences preceding and following it.
The \" symbol is the escape sequence for the double-quote sign (").
The functions from the PHP core used in the sequence are:
-strlen($s) returns the length of the $s string
-substr($s,$a,$b) returns the substring of $s, starting at $a, with length $b.
-strtr($u, "\"\n\r", "' ") -- the function has 3 arguments:
1-$uis the string to translate
2-\"\n\ris the characters to translate, consisting of the double-quote (\"), the line-feed (\n) and the carriage return (\r) characters
3-'the single-quote followed by 2 spaces, are the characters to replace those of the same rank in the second argument
This function translates a double-quote (") found in the string $u, into a single-quote, and the \n and \r characters into a space.

1.3.5 Client side operations

The HTML page has 2 INPUT elements named 'accept' and 'reject', with values 'ACCEPT' and 'REJECT'. Down the screen, there are
-an INPUT element named 'word' which displays the current word
-a TEXTAREA element named 'zone' which displays the current word context, i.e. the word framed between 128 preceding and 128 following characters.
The informations to process are in the word occurrence objects:
A$idx = {wrd:"$w", pos:"$key", val:"$u"}
At each stage of the process, one of the objects, A0, A1, A2, etc... is used. Each stage is identified by the value of the index idx which sets the number 0, 1, 2, etc. in the object names A0, A1, A2, ...
The 2 INPUT elements above control the accepting and rejecting of the word occurrence at each stage. They have similar definitions. Let's look at the 'accept' element. Its onclick attribute defines the script to be run when the element is clicked:
-first, the current word, held in property wrd of the current word occurrence object, is appended to the value of a hidden INPUT element named 'show', followed by a '/' sign. The name of the object is composed of the letter A and the value of the variable 'idx'. If you wrote show.value+='A'+idx+'.wrd' (+ is the concatenation operator in Javascript), a string like 'A0.wrd' would be appended to show.value. To have the value of A0.wrd, you have to evaluate this expression, using the eval function, as shown.
-similarly, the word position, held in property pos of the object, is appended to the value of the position hidden INPUT element
-the index idx is then incremented, to proceed on to the next word
-if there is still an object at number idx (idx < idm), the related word and context are displayed:
.the word (property wrd) is assigned to the value of the INPUT element 'word',
.the word context (property 'val') is assigned to the value of the TEXTAREA element 'zone'.
Otherwise, the function terminate is called to display some statistics.
The onclick attribute of the other INPUT elements, 'discard' and 'reject' have similar contents.

1.3.6 Link to the file editing script

When you click on the 'GO' button, a request for the SampleFileEdit.php script is sent to the server along with the lists of the words and their positions, as contained in the 'show' and 'position' INPUT elements, and also the path of the file being processed, that is saved in the 'upfile' INPUT element (in further samples we are going to see a better way to save information, using the $_SESSION superglobal array).

Javascript objects were used to hold the information on the word occurrences. Obviously, an array can be used here. Objects were used to show how to use Javascript objects. They are very similar to PHP objects, as they partake in the same oo essence (oo is for object oriented). Note however the differences between the Javascript and the PHP notations, in object handling, and elsewhere. For example, concatenation in noted by the + sign in Javascript, whereas the dot . sign is used in PHP.

1.4 File editing

SampleFileEdit.php - not to be called directly
<HTML><HEAD>
<TITLE></TITLE>
</HEAD>
<BODY>
<?php
function __autoload ($x) {
  require_once $x . '.cla';
}//end __autoload 

//1. Retrieve data
$upfile   = $_POST['upfile'];   //file URI
$words    = $_POST['show'];     //words
$position = $_POST['position']; //positions
 
//2. Get keywords into array
//2.1 Remove / characters
$posstr=trim(str_replace("/"," ",$position));
$worstr=trim(str_replace("/"," ",$words));
 
//2.2 Get words into 'wor', positions into 'pos'
$wor = str_word_count($worstr,1);
$pos = explode(" ",$posstr);
$words = array_combine($pos,$wor);
$vv = "";
foreach ($words as $k=>$v) {
  if (strcmp($v, $vv)==0) $n++;
  else $n = 0;
  $vv = $v;
  $nn = "00".$n;        //0 fill on left
  $nn = substr($nn,-3); //3 digit with 0 fill
  $words[$k] = $v."_$nn";
}//end foreach

krsort ($words);

//3. Read File to process
$s = file_get_contents ($upfile)
or die ("Unable to read file");

//4. Process file

//4.1 Insert anchors
 foreach ($words as $p => $w) {
 $left = "<a name=\"$w\">";
 $right= "</a>";
 StringHandler::insert($s,$p+strlen($w)-4,$right);
 StringHandler::insert($s,$p,$left);
 //create index elements
 $iarray["$w</a>"]="<a href=\"#$w\">";
}//end foreach $words

//4.2 Create index
$index = "";
ksort($iarray); //sort index elements
foreach ($iarray as $k => $v) { //compose index
  $index .= "<br>\n".$v.$k; 
}//end foreach $iarray

//4.3 Append index to document
$p = stripos($s,"</body>"); //remove </body>
$s = substr($s,0,$p);
$s .="<p>\n\n<b>INDEX</b><br>\n".$index;
$s .="</body></html>";

//4.4 Print out document (sent to client side)
print $s."\n";

//4.5 Create new file at server side
$fname=basename($upfile);
file_put_contents("files/new-$fname",$s);
?>
</BODY></HTML>
The function __autoload provides for the automatic loading of needed modules.

The script starts with retrieving the URI of the file to process and the lists of the retained keywords and their positions in the file.

The PHP core function str_replace is used to replace the '/' signs that separate the words (in the string processing sense) in the $words and $position strings, with spaces. The PHP core function trim cuts out the spaces at both ends of the strings.

The PHP core function str_word_count($wordstr,1) retrieves the words contained in the $wordstr string and returns them in an array. The PHP core function explode(" ",$posstr) cuts up the $posstr string at the positions containing the " " character, leaving the latter out, and returns the resulting chunks in an array. In the present case, both the str_word_count and the explode functions achieve the same result.

The PHP core function array_combine($pos,$wor) creates an array where the keys are the values of the $pos array, and the values, the values of the $wor array. A key/value pair of the created array is composed with values of the same index from the component arrays $pos and $wor.

The foreach($words $k=>$w) loop modifies the words contained in the $words array by appending to them a 3-digit number _000, _001, ... which distinguishes between different occurrences of the same word. This is how:
At each iteration, a key which is a position, is extracted into $k and the corresponding value which is a word, into $w. The PHP core function strcmp($v,$vv) compares the strings $v (new word) and $vv (previous word) and returns 0 if they are equal. The PHP core function substr($nn,-3) returns the last 3 digits of the transformed number $nn.

The PHP core function krsort($words) sorts the $words array on the keys, in reverse order. The keys in $words are the positions of the word occurences. The insertion of the anchors into the original text must be done the farthest first, in order not to modify the positions to be processed yet.

The PHP core function file_get_contents($upfile) returns the contents of the file defined by its URI, relative to the directory where the script is.

The insertion of the anchors <a name="word_xxx"> into the text occurs in the foreach ($words $p=>$w) loop. The static method insert of the class StringHandler is called. The notation StringHandler::insert is used. In passing the link to each anchor is created into the $iarray array.

The PHP core function ksort($iarray) sorts the $iarray array on the keys, in normal order. In the foreach ($iarray as $k=>$v) loop, the hyperlink elements are extracted from the sorted $iarray, and concatenated together to compose the index.

The index is appended to the original document, and the result is written out to the document to be sent to the user.

The transformed document is also written out to a file:
-The PHP core function basename($upfile) returns the file name proper, i.e. the rightmost component of the path name, as contained in the URI $upfile
-The returned name prefixed by "new-" is assigned to the file to be created, and the file is to be in the path files relative to the script location.
-The PHP core function file_put_contents($fpath,$s) write out the file with the contents $s, to the path $fpath

1.5 An order entry Javascript application

This application displays a catalog of products and lets a user choose the items she or he wishes to order. The list of ordered items (the shopping cart) is displayed by the click of a button. The catalog can be downloaded piecewise, thus saving on download time (and the user's impatience).

At application start, the user is requested to select a product family, then click on a field to confirm. The click on the confirmation field displays an order entry sheet composed of:

-a panel for entering the user's name and address
-a product list where the user can choose the desired items
-an order list where the selected items and their detailed and total prices are displayed
The user can modify her or his order, or call in a new product family.

The scripts to run the application consist of:

-an HTML master page which is a FRAMESET (no panicking, just look and learn what it is) which divides the screen into 2 frames, the upper frame occupied by a permanent header page, the lower frame by the order entry page
-a permanent HTML header page which holds the permanent information of the order entry session: user's name and address, current catalog extract and user choices, and displays buttons to navigate in the order entry page
-an HTML starter page displayed in the lower frame at application start
-a number of catalog pages, one for each of the product families in the catalog, to be displayed in the lower frame during the order entering process
-a final page which processes the order
All of the pages but the last are HTML and Javascript. This is natural enough since the operations are first performed on the client side. This application shows the importance of having good HTML practice.

1.5.1 The master page

OrderTop.html
<FRAMESET rows="60px,*">
<FRAME name="one" src="OrderHead.html">
<FRAME name="two" src="OrderStart.html">
</FRAMESET>
</HTML>
The application starts with this HTML page which is a FRAMESET. It divides the screen into 2 horizontal (row like) frames: the upper is allocated 60 pixels, the lower extends over the rest. The space occupied by a frame on the screen is a window.
At application start, the upper frame, named one, is to be occupied by the OrderHead.html page, the lower, named two, by the OrderStart.html page.

1.5.2 The permanent header page

This page is the header page displayed in the upper window, in the frame named 'one'. It contains the informations that remain throughout the order entry session. These are:
-the product families list
-the description of the products from the families downloaded by the client
-the list of the products selected by the client, to be included in the order
These informations are recorded here because the page in the lower frame changes each time the client downloads a new product family. The $_SESSION superglobal array which can be used across page changes will be illustrated in a latter sample. The intent of the present example is also to show how to communicate between the frames of a frameset.

This page also contains the buttons to navigate through the list of products, displayed in the lower frame.

OrderHead.html
<HTML><HEAD>
<TITLE>OrderHead</TITLE>
<SCRIPT>
famlen=3; //number of family characters
famStr="001book/002flower/003hardware/004software/";
famArr=new Array(); //all families
famMrk=new Array(); //family download mark
famSel=new Array(); //downloaded families
proStr="";          //product strings
customStr="";  //user's name and address
choiceList=""; //quantity ordered for each product
orderStr=""; //products ordered: qty,productId,price
firsttime=true; //first loading of catalog
</SCRIPT>
</HEAD>
<BODY>
<SCRIPT>
 p=0;
 q=0;
 i=0;
 while (p < famStr.length){
   q = famStr.indexOf('/', p);
   famArr[i]   = famStr.substring(p,q);
   famMrk[i++] = '-';
   p = ++q;   
 }//end while p < famStr.length
</SCRIPT>
<FORM name="family">
<BUTTON name="gotop" 
 onclick="top.two.box.cap.focus();">
 Back to top</BUTTON>
<BR>
<SCRIPT>
for (i=0; i < famArr.length; i++){
 document.writeln("<BUTTON type='push' ");
 document.writeln("name='a"+famArr[i]+"'");
 document.writeln(" value='"+famArr[i]+"'");
 document.writeln(" disabled ");
 document.writeln("style='width:100;' ");
 document.writeln("onclick='top.two.choiceTable");
 document.writeln(".a"+famArr[i]+".focus()'");
 document.writeln(">");
 document.writeln(famArr[i]);
 document.writeln("</BUTTON>");
}
</SCRIPT>
</FORM>
</BODY></HTML>
famStr is the list of family codes and names, separated by a / sign. As indicated by the famlen variable, the family code is 3 characters long, to its right is the family name (this store sells books, flowers, hardwares and sofwares). The family codes and names are to be retrieved into the famArr array. The famMrk array contains the marks (- or +) which indicate whether a family has been downloaded to the client side.

Javascript is similar to PHP, with some differences that you may observe.
The while loop decodes the famStr string and puts the family codes and names: 001book, 002flower, ... into the famArr array. Here are some samples of Javascript object notation:

-famStr.length is the number of characters contained in the string famStr
-famStr.indexOf('/',p)is the position of the first occurrence of / at or after p.
-famStr.substr(p,q) is the substring of famStr extending from position p inclusively to position q exclusively.
Note the use of the dot (.) in expressions like famStr.length (length property) and famStr.indexOf(..) (indexOf method) instead of the -> sign used in PHP.
All the marks in the famMrk array are initially set to "-" indicating that the family has not been downloaded to the client side.

In the BUTTON named gotop, the onclick method sets the focus on the top.two.box.cap element. This notation describes the hierarchy of elements in the HTML page we use (you have to have a minimum perception of what an HTML page is). Please click here to visualize this hierachy.

In this application, the screen is divided into 2 frames, each of which is in a window (see supra). The keyword 'top' represents the FRAMSET element which tops both frames. The other components of the notation: 'two', 'box', 'cap', are element names, as assigned by the name attribute (you do not see them here, as they are in another page). In object notation the name on the right of the dot (.) identifies an element contained in the element named on the left. 'focus' is a method defined for objects that represent certain elements of the HTML page. Focusing makes an element visible in the screen, with the page scrolled if needed. So you control the scrolling of the page in frame 'two', from this BUTTON here.

The for loop dynamically creates the buttons you can click to set the focus on the start of each product family in the product list.

Elements of the HTML page are to be generated dynamically when they contain variable data. The Javascript language provides for dynamic generation of the HTML text, through the writeln method. The writeln method pertains to the document object, as the notation in the sample script above implies. Please click here to display the hierarchy of the objects related to an HTML page. On top is the window object which represents the window in which the HTML page is displayed. The document object represents the HTML page. The writeln method writes to the document object, i.e. to the HTML page.

The expression famArr.length which controls the loop execution is the number of items contained in the array famArr (family codes and names).

The document.writeln("name='a"+famArr[i]+"'") statement writes out the string "name='a"+famArr[i]+"'" which is composed of 3 parts, concatenated by the + operator, which is the concatenation operator in Javascript:

-"name='" is a constant string, as shown by the enclosing double-quotes -- note that within the double-quotes, the single-quote is a data character
-famArr[i] is a variable
-"'" is a constant string
If the famArr[i] variable e.g. has the value "002Flower", the string would be name='a002Flower'. And that would be written out to the HTML page. For this value of the variable, the complete result written out to the HTML page is:

<BUTTON type='push' name='a002Flower' value='002FLower' disabled style='width:100' onclick='top.two.choiceTable.a002Flower.focus()'>002Flower</BUTTON>

in one line, since no new line character was provided in the written strings.

In the 'onclick' script, top.two.choiceTable.a002Flower.focus( ) references the element a002Flower contained in the 'choiceTable' element, in the 'two' frame. Obviously, such an element is to exist, at least when the BUTTON is enabled. This element is to be at the beginning of the 002Flower family in the product list

1.5.3 The starting page

This is the starter page to be loaded into the lower frame when the application starts, as requested by the master page which initiates the process.

It lets the user select a product family to download first.

OrderStart.html not to be called directly
<HTML><HEAD>
<TITLE>ORDER ENTRY</TITLE>
<SCRIPT>
 famArr=new Array();
</SCRIPT>
</HEAD>
<BODY>
<SCRIPT>
document.writeln("<b style='color:green'>");
document.writeln("ORDER ENTRY</b><p>");
 famlen=top.one.famlen;
 for (i=0; i < top.one.famArr.length;i++){
   famArr[i]=top.one.famArr[i];
 }//end for i < famArr.length
</SCRIPT>
<SCRIPT>
if (famArr.length > 0){
  document.writeln("<FORM name='famBox'>");
  document.writeln("<TABLE><TR>");

  document.writeln("<TD>Select a product family:");
  document.writeln("<TD><SELECT name='select'>");
  for (i=0; i < famArr.length; i++){
    document.writeln("<OPTION name='"+famArr[i]+"'>");
    document.writeln(famArr[i].substring(famlen));
    document.writeln("</OPTION>");
  }//end for
  document.writeln("</SELECT>");

  document.writeln("<TR><TD>");
  document.writeln("Click here to confirm choice:");
  document.writeln("<TD>");
  document.writeln("<INPUT type='text' name='xxx' ");
  document.writeln("value='CONFIRM' ");
  document.writeln("onclick='");
  document.writeln("idx=this.form.select.");
  document.writeln("selectedIndex;");
  document.writeln("vvv=famArr[idx].");
  document.writeln("substring(famlen);");
  document.writeln("location.replace");
  document.writeln("(\"Order.\"+vvv+\".html\")'>");
  document.writeln("</TABLE></FORM>");
}//end if famArr.length
</SCRIPT>
</BODY></HTML>
The famArr array is to accommodate the product family codes and names. It is defined in the HEAD section de the HTML page.

The product family codes and names are contained in the famArr array of the header page OrderHead.html loaded in the upper frame named "one", as seen in 1.5.2 above. The for loop for (i=0; i < famArr.length; i++) { ... } copies this array into the local famArr array, for processing.

In the top.one.famArr notation: "one" is the name of the upper frame. It also designates the HTML page loaded in this frame. From the present script loaded in frame "two", only the "top" element which tops both frames is directly visible. Frame "one" is referenced through the top element as "top.one". The "famArr" array contained in frame "one", is referenced as "top.one.famArr".

From the list of available product families contained in the famArr array, a SELECT element is created, with an OPTION element for each family. The family code and name, e.g. 001book, as held in the famArr array is assigned to the OPTION element name attribute. The family name, without the code, e.g. book, is set as the element contents, i.e. between the <OPTION> and </OPTION> tags; this is what shows in the list. The substring method is used to retrieve the name part of the code+name compound:
famArr[i].substring(famlen)
returns the substring of famArr[i] extending from position famlen (inclusively) to the end (famlen is the number of characters that compose the family code).

The script in the onclick attribute of INPUT element named 'xxx', as written out by the writeln function is:

idx = this.form.select.selectedIndex;
vvv = famArr[idx].substring(famlen);
location.replace("Order."+vvv+".html");
In the first line, this.form represents the FORM element where the line occurs, select is the name of the SELECT element, selectedIndex is a property of the SELECT element which represents the currently selected index (the corresponding OPTION contents shows in the element display field). So, idx is the currently selected index.
vvv is the selected family name.

location is an object associated with the HTML page, and represents its location in the Web network. The method replace replaces the current location with the one defined by the URI passed as argument, effectively replacing the current page by the one located by the new URI. Here the new URI has the form Order.vvv.html, where vvv is the selected family name, e.g. Order.book.html. This is a 'call' to a new page.

In the form as seen, the URI is relative to the directory containing the current script. If you run the application by clicking on its link above, the directory is identified as http://localhost/hpp/ so that the new absolute URI is: http://localhost/hpp/Order.vvv.html

1.5.4 The catalog pages

One catalog page is set up for each product family. Such a page contains the list of the products in the family, set in a single string where the data are separated by a / sign. In the product list, each product is represented by these data:
-a selection mark which is - (minus) when the product is not currently selected, + (plus) when it is.
-the product ID which starts with the family code. The product ID of all of the products have the same length. However, to save on space and transmission bandwidth, they are recorded in an abbreviated form as follows: the list is sorted on the product ID values, and from each product ID, only the right most part which differs from the previous ID is recorded. For example, if 2 consecutive IDs are 123456 and 123776, only the value 776 is recorded for the second ID.
-the product name
-the product unit price

Each product in the list is displayed on the page as a checkbox so that the client can select the product by clicking on it. Next to the check box is an INPUT field where the client can specify the quantity to be put on the order list.
Please click here, then select a family product, to view the application window again.

The user can add a product family to the ones she or he has already downloaded. When an additional product family is requested, a new catalog page in loaded into the lower frame, replacing the old one, all the information of which are lost. These informations are:

-the name and address of the client
-the quantity ordered of each product
Before the new page is loaded, a script in the old page records these informations into the header page held in the upper frame. When the new page is loaded:
-a script at the beginning of the page appends the product list of the page to the list of products already loaded by previous requests and recorded in the header page permanently set in the upper frame, and retrieves the product list so updated.
-a script at the end of the page restores the client name and address and ordered quantities saved in the header page, onto its own fields; this script has to be put at the end of the page to be sure that all of the concerned fields have been loaded.
These operations make use of the functions defined in the <HEAD> section of the page.

Order.book.html - not to be called directly
<HTML><HEAD><TITLE>ORDER ENTRY</TITLE>
<SCRIPT>
 family="001book";
 proStr="-/0010123A/HTML 4/5.50/";
 proStr+="-/43B/Javascript/3.25/";
 proStr+="-/0211C/JAVA/12.32/-/0312D/CSS/2.12/";   
 famArr=new Array(); //available families list
 famSel=new Array(); //downloaded families list
 proSele =new Array(); //product selection indicators
 proCode =new Array(); //product ID codes
 proName =new Array(); //product names
 proPrice=new Array(); //product prices
 choiceList=""; //quantity ordered for each product

 function saveAddress(){
   top.one.customStr="";
   for (i=0; i < customer.length; i++){
     top.one.customStr+=customer[i].value+"/";
   }//end for
   customOrder.customStr.value=top.one.customStr;
 }//end saveAddress

 function setAddress(){ 
   customOrder.customStr.value=top.one.customStr; 
   i=0;
   p=0;
   q=p;
   while (p < window.top.one.customStr.length) {
     q = top.one.customStr.indexOf("/",p);
     customer[i++].value 
             = top.one.customStr.substring(p,q);
     p = ++q;
   }//end while
 }//end setAddress

HEAD section - Functions

A number of functions are defined in the HEAD section of the HTML page (they are in Javascript language).

The saveAddress function saves the client name and address into the customStr string in the header page.

These informations come from the INPUT fields contained in the FORM element named 'customer' of the current page.

Instead of naming each of the INPUT fields, you can treat the containing FORM element as an array and the INPUT fields as items of this array:

customer[0] represents the firstname INPUT element
customer[1] represents the lastname INPUT element
etc.
customer.length is the number of elements in the customer array.

top.one.customStr represents the customStr string in the upper frame, by the same logic as seen above. Finally, the string of client name and address is also saved in the INPUT element named 'customStr', contained in the FORM named 'customOrder'.

The setAddress function is the reverse of saveAddress. It uses the client name and address stored in the 'customStr' string of the header page to restore the values of the INPUT fields of the 'customer' FORM element.

The data in the customStr are separated by the / sign. The 'indexOf' method is applied to the customStr string to find the successive positions of this sign. The 'substring' function as used here returns each single datum delimited by the / sign.

function saveChoice(){
  choiceList="";
  for (i=0; i < choiceTable.length; i++){
    choice="";
    if (choiceTable[i].type=="checkbox"){
       if (choiceTable[i].checked)
           choice=choiceTable[i+1].value;
       choiceList+=choice+"/";
    }
  }
  top.one.choiceList=choiceList;
}//end function saveChoice












function setChoice(){
  p=0;
  for (i=0; i < choiceTable.length; i++){
    q = choiceList.indexOf("/",p)
    if (choiceTable[i].type=="checkbox"){
      choice=choiceList.substring(p,q);
      if(choice) {
        choiceTable[i].checked=true;
        choiceTable[i+1].value=choice;
      }
      p = ++q;
      if (p >= choiceList.length) break;
    }//end if type=="checkbox"
  }//end for
}//end function setChoice
The saveChoice function saves the quantity to put into the shopping cart of each of the products, first into the 'choiceList' local string, then into the 'choiceList' in the header page. A sample choiceList string is: 3///7/2/// -- this string shows the ordered quantities to be: 3 for the 1st product, 7 for the 4th, 2 for the 5th, none for the others.
In the HTML page each product is represented by 2 consecutive INPUT elements, the first is a checkbox (type='checkbox'), the value attribute of the second is the quantity of the product for the shopping cart. These INPUT elements are contained in the choiceTable FORM element, which is treated as an array, with the INPUT elements, the items of the array.

In the saveChoice function, the for loop walks through all of the input elements contained in the FORM choiceTable element, and retains those which have the checkbox type. The element next to it contains the quantity to put into the shopping cart. This quantity is to be taken into account only if the corresponding checkbox is checked (from the notation, you may have inferred that 'type' and 'checked' are properties of a checkbox object).

The SetChoice function is the reverse of SaveChoice. If the quantity of a product for the shopping cart, as saved in the choiceList string, is not empty or 0 (both interpreted as false), it restores this quantity to the appropriate INPUT element, and the checked property of the related checkbox to the value true.
The for loop runs through all of the input elements contained in the choiceTable element. But may be it is not necessary to go that far. There are no more elements to examine when the end of the choiceList string is reached. In this event, the break statement causes the for loop to be exited.

function disableInput(){
  for (i=0; i < customer.length; i++) {
    customer[i].disabled=true;
  }
  for (i=0; i < choiceTable.length; i++) {
    if(choiceTable[i].type=="checkbox"){
       choiceTable[i].disabled=true;
       choiceTable[i+1].disabled=true;
    }//end if
  }//for i < choiceTable.length
}//end function disable input

function enableInput() {
  for (i=0; i < customer.length; i++) {
    customer[i].disabled=false;
  }
  for (i=0; i < choiceTable.length; i++) {
    if(choiceTable[i].type=="checkbox"){
       choiceTable[i].disabled=false;
       choiceTable[i+1].disabled=false;
    }//end if
  }
}//end function enable input
The disableInput function disables:
-the INPUT elements that hold the customer name and address
-the checkbox and the quantity INPUT elements associated with the products.
An INPUT element has a disabled property. When this is set to true the element is disabled, the checked property of a checkbox and the value of a text type INPUT element can no longer be changed.

The ensableInput function enables:

-the INPUT elements that hold the customer name and address
-the checkbox and the quantity INPUT elements associated with the products.
An INPUT element is enabled when its disabled property is set to false. Then, the checked property of a checkbox and the value of a text type INPUT element recover their capability of being changed.
function saveOrder(){
  order="";
  i = 0;
  for (j=0; j < choiceTable.length; j++){
  if (choiceTable[j].type=="checkbox") {
     if (choiceTable[j].checked) {
       order += choiceTable[j+1].value+"/";
       order += proCode[i]+"/"+proPrice[i]+"/";
     }//end if checked
     i++;
  }//end if type=="checkbox"
  }//end for j
  customOrder.orderStr.value=order;
  top.one.orderStr=order;
}//end function saveOrder


function showOrder () {
  orderName=new Array();
  orderQty =new Array();
  orderVal =new Array();
  i=0;
  k=0;
  qtyLen=0;
  namLen=0;
  spc="                                ";
  dot="................................";
  valTot=0;
  bbb=0.;
  for (i=0; i < proName.length; i++){  
  if (eval("choiceTable.p"+proCode[i]+".checked")){
      qty = "choiceTable.q"+proCode[i]+".value"
      orderQty[k]=eval(qty);
      orderName[k]=proName[i];
      orderVal[k]=orderQty[k]*proPrice[i];
      if (qtyLen < orderQty[k].length) 
          qtyLen=orderQty[k].length;
      if (namLen < orderName[k].length) 
          namLen=orderName[k].length ;
      valTot+=orderVal[k];
      orderVal[k]=orderVal[k].toFixed(2); 
      k++;
  }//end if proCode.checked
  }//end for i < proName.length
  valFmt = valTot.toFixed(2); 
  valLen = valFmt.length;


  txt="";
  for (k=0; k < orderQty.length; k++){
    str=spc+orderQty[k];
    txt+=str.substring(str.length-qtyLen)+" ";
    str=orderName[k]+dot;
    txt+=str.substring(0,namLen+2)+" ";
    str=spc+orderVal[k];
    txt+=str.substring(str.length-valLen);
    txt+="\n";
  }//end for k < orderQty.length
  txt+="\nTotal ";
  for (i=0;i< qtyLen+1+namLen+2-"Total".length;i++)
  {txt += ".";}//end for i
  txt += valFmt;
  resultTable.cart.value=txt;
}//end function showOrder
</SCRIPT>
<STYLE>
  TD {vertical-align:baseline;}
</STYLE>
</HEAD>
The function saveOrder saves the current order into
-the value of the orderStr element contained in the customOrder FORM,
-and the string orderStr of the header page.
An order consists of these data from each of the ordered product:
-quantity (choiceTable[j+1].value),
-product ID (proCode[i]),
-unit price (proPrice[i]).
In the function, j is the index of the checkbox in the FORM element, i is the index of the product in the list of selected products. The different data are separated by a / character.
top.one.orderStr denotes the orderStr variable contained in the header page, by the logic as explained above.

The showOrder function displays the list of the products that the customer has currently selected, with their quantities and values.
The product list is contained in the choiceTable FORM element. For each product, a checkbox INPUT element indicates whether the product has been selected, and a text INPUT element next to it shows the quantity to put in the shopping cart -- these elements have names in the following form, where code is the product ID code:

-pcode for the checkbox element
-qcode for the text element
The proCode[i] array item contains a product ID code. If this code is, e.g. 12345, the expression "choiceTable.p"+proCode[i]+"checked" is the string "choiceTable.p12345.checked". To have the value of the checked property of the element choiceTable.p12345, you have to evaluate the expression using the eval function. This value, when true means that the product has been selected.
Similarly, the expression choiceTable.q"+proCode[i]+".value" has to be evaluated to yield the value of the value property of the element choiceTable.q12345, which is the quantity of product.

The for (i=0; ..) loop retrieves the quantity, name, and value of each selected product, into the orderQty, orderName and orderVal arrays, and determine the maximum number of characters to be displayed for the quantities (qtyLen) and names (namLen). This is to prepare for a well aligned display.
The toFixed(2) function formats the numbers in a fixed point decimal representation, with 2 digits after the decimal point.

The for (k=0; ...) creates the order to display, as a string (txt) which is then assigned to the value property of the TEXTAREA element cart contained in the resultTable FORM element.
To represent a quantity (orderQty[i]) right justified in its field, a sufficent number of spaces (spc) is added to its left, then the last qtyLen characters are retained. A similar technique is used to represent a name (orderName[i]) left justified in its field padded to the right with dots.
In the TEXTAREA display space, the new line character is the \n sign, as used in text documents.

<BODY style="background-color:#55FFFF">

<SCRIPT>
 top.one.proStr+=proStr; //add local product list
 proStr=top.one.proStr;  //update local list
 famlen=top.one.famlen;  //# of chars in family code
 famnbr=top.one.famSel.length; //# of items in famSel
 top.one.famSel[famnbr]=family;//add new family
 famSel=top.one.famSel; //update local array
 choiceList=top.one.choiceList; //product quantities

 //Create available family list (not downloaded yet)
 j = 0;
 for (i=0; 
      i < top.one.famArr.length; 
      i++){
   fam = top.one.famArr[i];
   if (fam == family) {
       //mark selected
       top.one.famMrk[i]="+"; //is downloaded
       top.one.family[1+i].disabled=false;
   }//end if fam==family
   else {
     if (top.one.famMrk[i]=="-") { 
        //if not selected
        famArr[j++]=top.one.famArr[i];
     }//end if "-"
   }//end else
 }//end for i < famArr.length

//Retrieve products into arrays
 p=0;
 q=0;
 i=0;
 while (p < proStr.length){
   q = proStr.indexOf("/",p);
   proSele[i]=proStr.substring(p,q);
   p = ++q;
   q = proStr.indexOf("/",p);
   proCode[i]=proStr.substring(p,q);
   p = ++q;
   q = proStr.indexOf("/",p);
   proName[i]=proStr.substring(p,q);
   p = ++q;
   q = proStr.indexOf("/",p);
   proPrice[i]=proStr.substring(p,q);
   p = ++q;
   i++;
 }//end while
</SCRIPT>

<FORM name="customer">
<TABLE cellspacing="0">
<TR><TD colspan="3" style="color:red;">
    <b>Please fill in the form</b>
<TR><TD style="text-align:left;">First Name<TD>
    <INPUT TYPE="text" NAME="firstname" SIZE="64">
<TR><TD style="text-align:left;">Last Name<TD>
    <INPUT TYPE="text" NAME="lastname" SIZE="64">
<TR><TD style="text-align:left;">e-mail<TD>
    <INPUT TYPE="text" NAME="email" SIZE="64">
<TR><TD style="text-align:left;">Street address<TD>
    <INPUT TYPE="text" NAME="address" SIZE="64">
<TR><TD style="text-align:left;">City<TD>
    <INPUT TYPE="text" NAME="city" SIZE="64">
<TR><TD style="text-align:left;">State<TD>
    <INPUT TYPE="text" NAME="state" SIZE="64">
<TR><TD style="text-align:left;">Zip code<TD>
    <INPUT TYPE="text" NAME="zipcode" SIZE="64">
<TR><TD style="text-align:left;">Country<TD>
    <INPUT TYPE="text" NAME="country" SIZE="64">
</TABLE>
</FORM>

Update product family and detail list

The current page contains the product list of a family. The family is to be added to the families and the products to the products already downloaded to the client side. This is done in the script contained in the SCRIPT element set at the start of the BODY section. As it is placed here, the script is run when the page is loaded.
The product list of the current page comes in the string proStr. This list is added to the existing list held in the header page and the result replaces the initial list of the current catalog page. The new family is added to the famSel array of downloaded families, first in the header page, then in the current catalog page. The choiceList string contains the quantities ordered for each product; it was saved in the header page, and is now restored to the current page.

The famArr array in the header page contains the codes and names of all of the existing families. The famArr array in the current page contains those of the families still eligible for selection, i.e. not downloaded yet.

The header page contains navigation buttons in a FORM element named family. Each of these buttons is associated with a family. When clicked, such a button sets the focus on the starting point of its family, in the product list. In the script on the left, these buttons are identified as items of the family array. Initially, all of the buttons are disabled. The button associated with the family described by the current page is enabled, i.e. its disabled property is set to false (the buttons of the previously selected families were enabled when each of these is selected, and retain their status).

The informations on the products are contained in the string proStr, as updated by the initial script. They are decoded into the arrays:

proSeleselection indicator (+ if the product has been selected, - otherwise)
proCodeproduct ID code
proNameproduct name
proPriceproduct unit price

Customer name and address panel

The name and address of the customer are the value properties of the INPUT elements contained in the customer FORM element. This is ordinary HTML code.
<FORM name="box">
<BUTTON name="cap">
<b style="color:red">SELECTION LIST</b>
</BUTTON></FORM>
<!-- ORDER ENTRY TABLE -->
<TABLE style="background:white;">
<!-- left column: price list-->
<TR><TD>
<FORM name="choiceTable" style="width:100%;">
<TABLE style="margin-right:1em;">
<TR><TD><TD><TD><TD>Quantity
<SCRIPT>
//create product list
 lastFam = "";
 iFam    = 0;
 lastCod = "";
 for (i=0; i < proSele.length; i++) {
 //1. expand product code
   currCod = proCode[i];
   diff = lastCod.length - currCod.length;
   if (diff > 0) {
    currCod 
       = lastCod.substring(0,diff)+currCod; 
   }
   lastCod = currCod;
   proCode[i] = currCod;
 //2. display family label 
   currFam = currCod.substring(0,famlen);
   if (currFam != lastFam) { //new family
   //family heading
     document.writeln("<TR><TD colspan='2'>");
     document.writeln("<BUTTON ");
     document.writeln("name='a"+famSel[iFam]+"'>");
     document.writeln("</BUTTON>");
     document.writeln("<b>"+famSel[iFam++]+"</b>");
   }
   lastFam = currFam;
 //3. table row describing product
   iProd = iFam+2*i+1; //quantity INPUT index
   document.writeln("<TR>");
   document.writeln("<TD><INPUT type='checkbox' ");
   document.writeln("onclick='if "); 
   document.write("(choiceTable["+iProd+"]"); 
   document.writeln(".value==0)"); 
   document.write("choiceTable["+iProd+"]"); 
   document.writeln(".value=1"); 
   document.writeln("else"); 
   document.write("choiceTable["+iProd+"]"); 
   document.writeln(".value=0"); 
   document.writeln("choiceTable["+iProd+"]"); 
   document.writeln(".focus()'"); 
   document.writeln("name='p"+proCode[i]+"'"); 
   document.writeln("value='"+proName[i]+"'>"); 
   document.writeln("<TD>"+proName[i]); 
   document.writeln("<TD style='text-align:right;'>"); 
   document.writeln(proPrice[i]+" "); 
   document.writeln("<TD style='text-align:right;'>"); 
   document.writeln("<INPUT type='text' "); 
   document.writeln("name='q"+proCode[i]+"'>"); 
} //end for i < proSele.length
</SCRIPT>
</TABLE></FORM>

Write out product SELECTION LIST

The SELECTION LIST is the list of products contained in the choiceTable FORM element. The list is presented in a table, the rows of which are generated in the for (i=0; i < proSel.length; i++) loop.
The 4 arrays, preSele, preCode, preName, prePrice all have the same number of items, which is the number of products that have been downloaded. Each row of the table is dedicated to one product, with the following design:
<TR><TD><INPUT type='checkbox'><TD>name<TD>price
<TD><INPUT type='text' ... >

where name is the product name and price is the product unit price.

As the product code is recorded in an abbreviated form, the first operation is to expand it to its real size: if a code is shorter than its normal size, it is completed on the left by the first characters of the previous code.

When a new family is encountered, which is detected by the changing of the family code at the beginning of the product code, the family label is written out, to the right of a BUTTON named "afamcode", where famcode is the code of the family. This BUTTON is to be given focus when the corresponding navigation BUTTON in the header page is clicked.

Finally, the table row with the design shown above is written out.
The 'onclick' script of the checkbox element is:

if (choiceTable[iProd].value==0) choiceTable[iProd].value=1;
else choiceTable[iProd].value=0;
choiceTable[iProd].focus();

where iProd is the index that identifies the INPUT element containing the quantity of product to be put in the shopping cart. The effect of the script is to switch the quantity to 1 when the checkbox is selected, and to 0 when it is de-selected, and in both cases, to set the focus on the element containing the quantity so that the customer can enter a quantity other than 1. (There is a flaw in this design which is left for you to correct: there is no need to set the focus on the quantity when it is 0, since the product is then de-selected.)

Note that choiceTable[iProd].value==0 is true when the value attribute is not defined.

<!-- right column-->
<FORM name='resultTable'>
<TD style="vertical-align:top">
<BUTTON type='push'      
 onclick='
   confirm.disabled=false;
   showOrder();
 '>
SHOW ORDER
</BUTTON> show order, then CONFIRM
<BR>
<TEXTAREA name='cart' cols='32' rows='24'>
</TEXTAREA>
<BR>
<BUTTON name='confirm' disabled
  onclick='
    saveAddress();
    saveChoice();
    saveOrder();
    showOrder();
    disableInput();
    customOrder.send.disabled=false;
  '>CONFIRM</BUTTON>
<BUTTON
  onclick='
    enableInput();
    customOrder.send.disabled=true;
  '>CHANGE</BUTTON>
</FORM>

<FORM name="customOrder" method="POST"
      action="OrderReceive.php">
  <BUTTON name="send" disabled type="submit">
    <b style="color:true">SEND</b>
  </BUTTON> CONFIRM before you can send
  <INPUT name="customStr"  type="hidden">
  <INPUT name="orderStr"   type="hidden">
</FORM>
</TABLE>

The SHOW ORDER button

A click on the SHOW ORDER button:
-calls the showOrder function to display the user's order in the cart TEXTAREA
-enables the CONFIRM button (you cannot confirm an order before you are shown it).

The CONFIRM button

The CONFIRM button:
-calls the saveAddress, saveChoice, saveOrder functions to save the customer's name and address, the products desired quantities, the list of selected products with code, quantity and unit price, onto the header page
-calls the showOrder function to show the lattest state of the order
-calls the disableInput function to disable the customer's name and address INPUT elements and the product checkbox and quantity INPUT elements
-enables the SEND button
Disabling the INPUT elements makes sure that what you send to the server is what you see on the screen.

The CHANGE button

The CHANGE button cancels the effect of CONFIRM. It enables the INPUT elements, and disables the SEND button.

The SEND button

The SEND button sends the customer's name and address (string customStr) and the order list (string orderStr), along with a request for the OrderReceive.php page, to the server
if (famArr.length > 0){
  document.writeln("<FORM name='famBox'>");
  document.writeln("<TABLE><TR>");
  document.writeln("<TD>Select a product family:");
  document.writeln("<TD><SELECT name='select'>");
  for (i=0; i < famArr.length; i++){
    document.writeln("<OPTION name='"+famArr[i]+"'>");
    document.writeln(famArr[i].substring(famlen));
    document.writeln("</OPTION>");
  }//end for
  document.writeln("</SELECT>");
  document.writeln("<TR><TD style=");
  document.writeln("'vertical-align:baseline;'>");
  document.writeln("Click here to add to list:");
  document.writeln("<TD>");
  document.writeln("<INPUT type='text' name='xxx' ");
  document.writeln("value='CONFIRM' ");
  document.writeln("onclick='");
  document.writeln("idx=this.form.select.");
  document.writeln("selectedIndex;");
  document.writeln("vvv=famArr[idx].");
  document.writeln("substring(famlen);");
  document.writeln("saveAddress();");
  document.writeln("saveChoice();");
  document.writeln("location.replace");
  document.writeln("(\"Order.\"+vvv+\".html\")'>");
  document.writeln("</TABLE></FORM>");
}//end if famArr.length
</SCRIPT>

Additional families list

The product families that have not been selected are set in the famArr array of the current page. The document.writeln method puts them in the OPTION items of a SELECT element. An INPUT element which displays the word CONFIRM is associated with the SELECT element. The 'onclick' attibute of this element is:
idx=this.form.select.selectedIndex;
vvv=famArr[idx].substring(famlen);
saveAddress();
saveChoice();
location.replace("Order.vvv.html");

selectedIndex is the index of the currently selected item from the select element. The famArr array contains the compounds code+name of the product families. The code part has a length of famlen. The substring(famlen) function returns the right part of the string, starting at famlen, so that vvv is the name of the selected family.
The functions saveAddress and saveChoice save current the name and address and ordered quantities.

The location.replace statement replaces the current page with the one defined by the Order.vvv.html URI.

<SCRIPT language="Javascript">
   if (top.one.firsttime){
     top.one.firsttime=false;
   }
   else {
     setAddress();
     setChoice();
   }
</SCRIPT>
</BODY></HTML>

Restoring the customer's and selection information

As a new catalog page is loaded, its user's name and address fields are empty and its product selection checkboxes are unselected. The final script calls the setAddress and setChoice functions to restore the informations previously saved in the header page. The script is placed here, at the end of the HTML page to make sure that the fields to be restored are readily loaded.

1.6 Database table creation

The script presented in the following can be used to create a database table. The first script is an HTML page with Javascript code. The second is the PHP script which processes the table description sent in by the first, and connects to the database server to request table creation.

1.6.1 A table description form in HTML/Javascript

DBCreateTable.html
<HTML><HEAD><TITLE>CREATE TABLE</TITLE>
<META http-equiv="Content-Type" content="text/html">
<SCRIPT>
datatype ="BOOLEAN:/TINYINT: 1 byte/";
datatype+="SMALLINT: 2 bytes/MEDIUMINT: 3 bytes/";
datatype+="INTEGER: 4 bytes/BIGINT: 8 bytes/";
datatype+="FLOAT: single-precision/";
datatype+="DOUBLE: double-precision/DECIMAL:/";
datatype+="DATE:YYYY-MM-DD/";
datatype+="DATETIME:date + HH-MM-SS/YEAR:/";
datatype+="TIMESTAMP:/TIME:/";
datatype+="CHAR:0-255/"; 
datatype+="VARCHAR:0-255/"; 
datatype+="TINYBLOB: 255 char/";
datatype+="BLOB: 65535 char/";
datatype+="TEXT: 65535 char/";
datatype+="MEDIUMBLOB: 16777214 char/";
datatype+="MEDIUMTEXT: 16777214 char/";
datatype+="LONGBLOB: 4 Gigabytes/";
datatype+="LONGTEXT: 4 Gigabytes/";
datatype+="ENUM:list-65535/SET:list-64/"; 
The script contained in the SCRIPT element in the HEAD section defines the datatype variable which contains the possible types of the data stored in the database, and a number of functions to handle the operations on the page.

+ is the string concatenation operator with Javascript. The += operator appends the string on its right side to the variable on its left.
Each data type is described with:

-the keyword to be used in an SQL statement,
-the : sign to terminate the keyword
-an optional comment
-the / ending the type description
Example: TINYINT:1 byte/
In this, the keyword is TINYINT, the comment is 1 byte that hints to the fact that TINYINT is a number of 1 byte long.
colElem = new Array();
function retrieveEntry (){
//1. Field name
  i=0;
  if (kolumn.fieldName.value=='') {
     kolumn.fieldName.focus();
     return;
  }//end if fieldName empty
  colElem[i++]=kolumn.fieldName.value; 
The function retrieveEntry retrieves the data that make up the definition of one column. The fields that contain these data are in the kolumn FORM element. colElem is the array into which these data are retrieved.
The function first tests that the column name (in INPUT field fieldName) is not empty. If it is, the focus is set on that field for the user to enter a value, and the function terminates. Otherwise, the column name is retrieved into the colElem array.
//2. Field type
//2.1 Basic type
  idx = kolumn.seltype.selectedIndex;
  p   = kolumn.seltype[idx];
  typ = kolumn.seltype[idx].value;
  colElem[i++]=" "+typ;
The possible field types are the options of the seltype SELECT element. selectedIndex is a property of a SELECT element that indicates the currently selected OPTION. The type is retrieved in to the colElem array (all of the data, except the first, are preceded by a space, to make it convenient to compose the SQL statement).
//2.2 Type qualification
//2.2.1 Characters
  if ((typ=="CHAR")||(typ=="VARCHAR")){       
    val = parseInt(kolumn.varsize.value);
    val = val.toString();                     
    if ((val=="NaN") || (val <= 0)){
      kolumn.varsize.focus();
      return;
    }
    else {
      colElem[i++] = "("+kolumn.varsize.value+")";
    }
    idc = kolumn.charcode.selectedIndex;
    if (typ=="CHAR"){
      if (idc > 0) 
          colElem[i++]=" "+kolumn.charcode[idc].text;
    }
    else {
      if (idc == 1) 
          colElem[i++]=" "+kolumn.charcode[idc].text;
    }
  }//end typ CHAR || VARCHAR
The data types CHAR and VARCHAR require the number of characters to be specified. This number is retrieved from the varsize field.

The function parseInt returns the integer value found in the string passed as an argument. The symbol "NaN" (Not a Number) is returned if the string does not represent a number. To detect this value, the variable has to be converted into a string, using the toString method. If the data is not a number, focus is set on the field, and the function terminates; otherwise, the value is set within parentheses and retrieved into colElem.

A CHAR data can further be qualified as BINARY, ASCII or UNICODE. A VARCHAR can be qualified as BINARY. These are options selected from the SELECT element charcode. The selectedIndex property of the SELECT element is the index of the selected option. Here the text property of the selected OPTION element is retrieved. This is the contents of the element, enclosed between the <OPTION> and the </OPTION> tags.
The 1st option (index 0) is blank (no qualification). The option with index 1 is BINARY, the only allowed for the VARCHAR type.

//2.2.2 Number
  if((kolumn.seltype.selectedIndex > 0)        
   &&(kolumn.seltype.selectedIndex < 9)){
     m = parseInt(kolumn.m.value);
     m = m.toString();                          
     if ((m=="NaN")||(m <= 0)){                 
        if (kolumn.seltype.selectedIndex > 5) { 
            kolumn.m.focus();                   
            return;
        }//end selectedIndex > 5
     }//end NaN || <= 0
     else {                                     
        if (kolumn.seltype.selectedIndex < 6) { 
           colElem[i++] = "("+m+")";
        }//end selectedIndex < 6 (integer)
     }//end else 
     if (kolumn.seltype.selectedIndex > 5) {    
        d = parseInt(kolumn.d.value);
        d = d.toString();                       
        if ((d=="NaN")||(d <= 0)){              
           kolumn.d.value=0;                    
           d = 0;
        }//end if NaN || <= 0
        colElem[i++] = "("+m+","+d+")";
     }//end selectedIndex > 5
//2.2.3 Unsigned, zerofill properties 
     if (kolumn.unsigned.value.length > 0){      
     colElem[i++]=" "+kolumn.unsigned.value;}   
     if (kolumn.zerofill.value.length > 0){      
     colElem[i++]=" "+kolumn.zerofill.value;} 
}//if type numeric
The numeric types are the options from 1 to 8, in the seltype SELECT list.

For the integer types (options 1 to 5), you can optionally specify the number of digit to display. This is in the field named m. When this field contains an integer, the latter is retrieved and put in the colElm, enclosed within parentheses.

For the floating point types (options 6 to 8), the total number of digits (field m) and the number of decimal digits (field d) must be specified. If the total number of digits (in m) is not numeric, the focus is set on the field, for correction, and the function terminates. If the decimal part (d) is not numeric, it is defaulted to 0.

The parseInt method is used here, as with varsize above, to return the numeric value of the field. This value must be converted to a string to test for the NaN (Not a Number) situation.

Data of a numeric type can also have the UNSIGNED and/or ZEROFILL properties. These properties are assigned to the column, and retrieved into the colElem array when they are found in the unsigned and zerofill fields, respectively.

//2.2.4 Character options                        
if ((typ=="CHAR")||(typ=="VARCHAR")){
  if (kolumn.charoptions.value.length > 0){
     colElem[i++]=" "+kolumn.charoptions.value;
  }
}//end if typ CHAR || VARCHAR  
//2.2.5 Enum and set                             
if ((typ=="ENUM")||(typ=="SET")){
  if (kolumn.enumlst.value.length > 0){
     colElem[i++]=kolumn.enumlst.value;
  }
  else {
     kolumn.enumlst.focus();
     return
  }
}//end if typ ENUM || SET  
The CHAR and VARCHAR types can have further options such as CHARSET, COLLATED, etc... These are to be manually entered into the charoptions field. When present, they are retrieved into the colElem array.

When the column is of the ENUM or SET data type, the list of allowable values is to be manually entered into the enumlst field.


//2.3 NULL option                                
idn = kolumn.nullfield.selectedIndex;
colElem[i++] = " "+kolumn.nullfield[idn].text;
//2.4 Default value                              
if (kolumn.defaultValue.value.length>0){
  defa = kolumn.defaultValue.value;
  if (kolumn.seltype.selectedIndex>8){
    defa = "'"+defa+"'";
  }//end selectedIndex >8
  colElem[i++] = " DEFAULT "+defa;
}//End defaultValue.value.length > 0
//2.5 Extras                                      
for (j=0; j < kolumn.columnextra.length; j++){
  if (kolumn.columnextra.options[j].selected==true){
    opt = kolumn.columnextra.options[j].text;
    p = opt.indexOf(":");
    colElem[i++]=" "+opt.substring(0,p);
  }//end selected==true
}//end for j < columnextra

//3. Show result                                  
kolumn.show.value="";
for (j=0; j<colElem.length; j++){
 kolumn.show.value +=colElem[j];
 colElem[j]="";
}
kolumn.confirmButton.disabled=false;
}//end function retrieveEntry      
The NULL option of a column can have one of the 2 values 'NULL' meaning that the column may be NULL, or 'NOT NULL' meaning the column must have a non-NULL value. These values are defined in the nullfield SELECT element. Here the text property of the selected OPTION is retrieved, i.e. its contents enclosed between the <OPTION> and the </OPTION> tags. The text property is that part of the option that you see.

The column default value, if any is entered in the defaultValue INPUT field. The data of the types from 9 on must be enclosed within single quotes. The word DEFAULT is added in front of the data, as it is in the SQL statement.

Extra informations are defined in the columnextra multiple SELECT box. The OPTION items in the SELECT element are represented by the options array which is a property of the SELECT element ("options" is a reserved name). selected is a property of an options item, which is true when the item is selected. As the options are displayed with a colon (:) and a comment, the part from the colon on is cut off using the substring method.

After having retrieved all of the data that make up the definition of the current column, the function puts them together in the show element where they are assembled into a the SQL definition of the column. The confirmButton BUTTON which adds the column defnition to the SQL statement being built is enabled.

 
function confirm(){                                   
if (result.tableDefn.value.length!=0){
 result.tableDefn.value=result.tableDefn.value+", ";  
}
result.tableDefn.value+=kolumn.show.value;            
dispform.defn.value
         +=kolumn.show.value.toString()+"\n";
kolumn.show.value="";
kolumn.fieldName.value="";
kolumn.varsize.value="24";
kolumn.unsigned.value="";
kolumn.zerofill.value="";
kolumn.m.value="";
kolumn.d.value="";
kolumn.charoptions.value="";
kolumn.enumlst.value="";
kolumn.defaultValue.value="";
kolumn.constraints.value="";
kolumn.fieldName.focus();
}//end function confirm                           
The confirm function adds the current definition of a column to the SQL statement being built for creating the table, and resets the INPUT fields.

The SQL statement is created in the hidden INPUT element tableDefn contained in the FROM element result.

The line which defines a new column is assembled in the show INPUT element, by the retrieveEntry function. The function 'confirm' appends it to the value of the tableDefn element. Before doing so, it appends a comma to the last line in tableDefn, if there is one, as indicated by the value of this element not having a 0 length. The new column definition line is also appended to the value of the defn TEXTAREA element, and terminated by the newline sequence \n.

The function ends with setting the focus on the fieldName field, for a new column definition.

function clearResult (){             
  result.tableDefn.value="";
  dispform.defn.value="";
  kolumn.show.value="";
  kolumn.fieldName.value="";
  kolumn.varsize.value="24";
  kolumn.unsigned.value="";
  kolumn.zerofill.value="";
  kolumn.m.value="";
  kolumn.d.value="";
  kolumn.charoptions.value="";
  kolumn.enumlst.value="";
  kolumn.defaultValue.value="";
  kolumn.constraints.value="";
  kolumn.fieldName.focus();
}//end function clear
</SCRIPT>
</HEAD>
The function clearResult clears the elements tableDefn, defn and show which contain the results of the current session, and resets the INPUT fields, then sets the focus on the fieldName element for a newentrey. This is presently the only way to correct a bad entry.
<BODY><a name="top"></a>
<FORM name='kolumn'>
<TABLE style="width:100%;">
<TR><TD style="vertical-align:top;">Table name: 
    <TD style="vertical-align:top;width:200">
    <INPUT name='tableName'><BR>
<TR><TD style="vertical-align:top;">Column name: 
    <TD colspan="3" style="vertical-align:top;">
    <INPUT name='fieldName'>
           Enter column properties below, 
           then click on 'VIEW', then 'CONFIRM'
<TR><TD style="vertical-align:top">
     Column data type:
    <TD><SELECT name='seltype'>
<SCRIPT>
p=0;
q=p;
while (p < datatype.length) {
  q  = datatype.indexOf("/", p);
  vv = datatype.substring(p, q);
  ix = vv.indexOf(":");
  xx = vv.substring (0, ix);
  document.writeln("<OPTION value="+xx+">")
  document.writeln(vv);
  document.writeln("</OPTION>");
  p = ++q;
}//end while p < datatype.length
</SCRIPT>
</SELECT>
The data that define a table are entered through the elements contained in the FORM element named kolumn (this saves the checking whether 'column' is a reserved word -- Javascript would fail if you use a reserved name, such as 'clear').
The tableName field receives the name of the table to create. The fieldName field receives the name of the column being defined.
The seltype SELECT element lets the user select one of the possible data types for the column being defined.

The OPTION elements are generated by a Javascript script.
The data types are contained in the datatype string as decribed above.
Each data type description, in the datatype string, ends with a / sign. It is retrieved into the vv variable. It includes a comment separated from the data type identifier by a colon. This identifier is retrieved into the xx variable.

The identifier is assigned to the value property of the OPTION element.
The description, complete with the comment, is assigned to text property which is the contents of the element, i.e. the part contained between the <OPTION> and the </OPTION> tags. This is the part visible by the user.

CHAR/VARCHAR size: 
<BUTTON onclick='varsize.value++'>+</BUTTON>
<INPUT name='varsize'>
<BUTTON onclick='varsize.value--'>-</BUTTON>
<SELECT name='charcode'>
<OPTION></OPTION>
<OPTION>BINARY</OPTION>
<OPTION>ASCII</OPTION>
<OPTION>UNICODE</OPTION>
</SELECT>
<TR><TD><TD style="background:inherit;">
<table style="width:100%;margin:0;padding:0;">
<tr>
<td style="width:10%"><BUTTON onclick='
    if((seltype.selectedIndex>0)
    &&(seltype.selectedIndex<9)){
      if (unsigned.value) {unsigned.value=""}
      else {unsigned.value="UNSIGNED"}
    }//end if selectedIndex
    '>
    </BUTTON>SIGN:
    <td style="width:20%"><INPUT name='unsigned'>
<td style="width:20%">
    <BUTTON onclick='m.value++;'>+</BUTTON>
    Total digits:<td><INPUT name='m'>
    <BUTTON onclick='m.value="";'></BUTTON>
<tr>
<td style="width:10%">
<BUTTON onclick='
   if((seltype.selectedIndex>0)
   &&(seltype.selectedIndex<9)){
      if (zerofill.value){
         zerofill.value=""}
      else {
         zerofill.value="ZEROFILL"}
   }//end if selectedIndex
   '>
   </BUTTON>FILL:<td><INPUT name='zerofill'>
<td>
   <BUTTON onclick='d.value++;'>+</BUTTON>
    Decimal digits:<td>
   <INPUT name='d'>
   <BUTTON onclick='d.value="";'></BUTTON>
<tr>
<td colspan="4"><INPUT name='charoptions'>
    optional: CHARACTER SET, COLLATED, 
    etc.. as needed
</table><TR><TD>Enum / Set list:
<TD><INPUT name='enumlst' size='96'>
</TABLE>
The value of the varsize INPUT element is the number of characters assigned to the column, when the latter is of the CHAR or VARCHAR type. This element is preceded by a BUTTON with an "onclick" script which increments its value, and followed by a BUTTON with an "onclick" script which decrements its value.
The charcode SELECT element has the options: blank, BINARY, ASCII, UNICODE, as shown.

The "onclick" script of the unnamed BUTTON on the left is:
if((seltype.selectedIndex>0)
&&(seltype.selectedIndex<9)){
  if (unsigned.value){unsigned.value=""}
  else {unsigned.value="UNSIGNED"} }

This is a flip/flop button which controls the value of the unsigned INPUT element, switching it from blank to "UNSIGNED" and vice versa, when the column being defined is of a numeric type.

The m INPUT element contains the total number of digits displayed in the column when it is a number. It is preceded by a BUTTON which increments its value, and followed by a BUTTON which wipes it out.

The "onclick" script of the unnamed BUTTON on the left is:
if((seltype.selectedIndex>0)
&&(seltype.selectedIndex<9)){
  if (zerofill.value){zerofill.value=""}
  else {zerofill.value="ZEROFILL"} }

This is a flip/flop button which controls the value of the zerofill INPUT element, switching it from blank to "ZEROFILL" and vice versa, when the column being defined is of a numeric type.

The d INPUT element defines the number of decimal digits in a decimal type number. It is preceded by a BUTTON which increments its value, and followed by a BUTTON which wipes it out.

The charoptions INPUT element receives the manually entered options for a character type column.

The enumlst INPUT element receives the manually entered list of allowable values for a SET or ENUM type column.

<TABLE>
<TR>
<TD style="vertical-align:top">
    Null authorized:
<TD>
   <SELECT name='nullfield'>
     <OPTION>NULL</OPTION>
     <OPTION>NOT NULL</OPTION>
   </SELECT>
<TD style="vertical-align:top">
    Default value:
<TD><INPUT name='defaultValue'> NO quotes 
</TABLE>

<TABLE>
<TR>
<TD style='vertical-align:top'>Extra:
<TD style:'vertical-align:top'>
   <SELECT name='columnextra' multiple>
   <SCRIPT>
   extradata =":/AUTO_INCREMENT:/KEY:/PRIMARY KEY:/";
   p=0;
   q=p;
   while (p < extradata.length) {
     q = extradata.indexOf("/", p);
     document.writeln("<OPTION>")
     document.writeln(extradata.substring(p, q));
     document.writeln("</OPTION>");
      p = ++q;
   }//end while p < extradata.length
   </SCRIPT>
   </SELECT>
    Depress CTRL for multiple selection
<TR>
<TD style="vertical-align:top">
<BUTTON type="push" name='enterConstraint'
  onclick='kolumn.show.value=kolumn.constraints.value;
  confirmButton.disabled=false;'>
CONSTRAINT
</BUTTON>
<TD><INPUT name='constraints' size='128'>
The nullfield SELECT elements offers the options "NULL" and "NOT NULL" as hard coded in its OPTION items.

The defaultValue INPUT field receives the manually entered default value of the column.

The columnextra SELECT element allows multiple choice, as indicated by its multiple attribute. Its OPTION elements are generated by a script, from the extradata string where the option values are recorded using the same technique as used with the data types.

The while (p < extradata.length) { ... } loop can be used as a model of how to decode a list of data separated by a given character (here a / sign).

The OPTION elements are dynamically generated by the document.writeln statements.

The option values (extradata.substring(p,q)) are set in the contents of the OPTION elements to make them visible to the user.

The enterConstraint BUTTON directly adds the code element from the constraints to the SQL statement being constructed in the show element (the other code elements are held in the colElem array and transferred to the SQL statement by the retrieveEntry function).

<TR>
<TD style="vertical-align:top;text-align:right;">
<BUTTON type="push" onclick='retrieveEntry();'>
VIEW</BUTTON>
<TD><INPUT name='show' size='128'><BR>
<BUTTON type="push" 
        onclick='kolumn.show.value="";
                kolumn.fieldName.focus()
        '>
MODIFY</BUTTON>
<BUTTON type="push" name='confirmButton' 
        disabled
        onclick='disabled;
                 confirm()'>
CONFIRM
</BUTTON>View before you can confirm
  
</TABLE>
</FORM> <!--end kolumn -->

<TABLE style="float:left"><TR><TD>
<FORM name='result' 
      action='DBCreateTable.php' 
      method='POST'>
  <INPUT name='tableName' value='' 
         type='hidden'>
  <INPUT name='tableDefn' value='' 
         type='hidden'>
  <INPUT name='tableCons' value='' 
         type='hidden'>
  <BUTTON type='submit' name='sub' 
    onclick='
       tableName.value=kolumn.tableName.value
       dispform.defn.value="";
  '>
  SUBMIT
  </BUTTON>
</FORM>
The "onclick" script of the BUTTON labelled "VIEW" (i.e with this word in its contents) calls the retrieveEntry function which retrieves the data from the data entry fields of the page, and composes the description of a column into the value of the show element.

The "onclick" script of the BUTTON labelled "MODIFY" clears the show element value, which contains the description of a column in SQL syntax, and sends the cursor to the fieldName INPUT field, for a new data entry process.

The "onclick" script of the BUTTON labelled "CONFIRM" disables itself and call the confirm function which adds the new column definition to the SQL statement being created and the display panel, and clears the entry FORM.

The result FORM contains the data and the controls to send them to the server:

-the action attribute of the FORM element points to the DBCreateTable.php page, which is scheduled when the submit button of the FORM is clicked
-the tableName element which contains the name of the table to create
-the tableDefn element which contains the column definition list to be used as part of the CREATE TABLE SQL statement
-the tableCons element which contains the constraints to complement the definition of the column being defined
<TR><TD>
<FORM>
<BUTTON type='push' name='clear'
    onclick='clearResult()'>CLEAR</BUTTON>
</FORM>
</TABLE>

<FORM name='dispform'>
 <TEXTAREA name='defn' cols='96' rows='20'>
 </TEXTAREA>
</FORM>
</BODY></HTML>
The clear button calls the clearResult function to clear the entry FORM for a new entry.

The defn TEXTAREA area is where the SQL statement being created is displayed.

1.6.2 The table creation PHP script

This script is called from the DBCreateTable.html page which sends it the data to create a table. The script first displays an entry form for the user to enter the needed identification informations: host name, database name, user name, password. When called again, it connects to the database server to submit the table creation request. Upon successful execution of the request, it automatically calls the DBDescribe.php page which displays a description of the newly created table.

DBCreateTable.php
<HTML><HEAD><TITLE>CREATE TABLE</TITLE>
</HEAD><BODY>
<p style="font-weight:bold;font-size:150%;
          color:#DD0000;text-align:center;
          padding-bottom:1em;">CREATE TABLE</p>
<?php if (!isset($_POST['hostname'])){
  $script = $_SERVER['SCRIPT_NAME'];
?>
<FORM action='<?php echo $script ?>' method='POST'
      name='box'>
<TABLE>
  <TR><TD>Host name:
      <TD><INPUT name='hostname' 
                 value='localhost:3306'>
  <TR><TD>Database name:
      <TD><INPUT name='dbname' value='mydatabase'>
  <TR><TD>User name:
      <TD><INPUT name='usrname' value='phong'>
  <TR><TD>Password:
      <TD><INPUT name='passwrd' type='password'>
  <TR><TD>Table name:
      <TD><INPUT name='tblname' 
      value="<?php echo ($_POST['tableName']); ?>">
  <TR><TD><INPUT name='table' type='hidden' 
      value="<?php echo ($_POST['tableDefn']); ?>">
</TABLE>
<BUTTON type='submit' name='sub'>SUBMIT</BUTTON>
</FORM>
<SCRIPT>
   box.passwrd.focus();
</SCRIPT>
<?php 
}//end if !$_POST
This is a two-phase script like some we have seen in the above. The condition used to discriminate between the two phases is:
!isset($POST['hostname'])
The present script is first requested by the DBCreateTable.html script which does not transmit a data named "hostname", leaving therefore the $_POST['hostname'] item undefined. In this case, the isset function returns false, and !isset is true. The PHP statement and the HTML text comprised within the curly braces of the if structure is scheduled.

The current page URI is retrieved from the $_SERVER array and written out to the value of the action attribute of the FORM element, so that a click on the submit button will call the page again.

The HTML text defines the entry form for the user to enter the host name, database name, user name and password, needed to access the database. The value attributes of the elements assign default values to them. Notably, the hostname element is assigned the value of localhost:3306, which identifies the local host, where the 3306 is the usual port used by the MySQL database server.

The table name and the column definition part of the SQL statement to create the table come from the calling page, in the data respectively named tableName and tableDefn. They are retrieved from the $_POST array into the value of the INPUT elements tblname and table, so as to be furthered to the page when it is called again.

else {
$host  = $_POST['hostname'];
$dbase = $_POST['dbname'];
$user  = $_POST['usrname'];
$pass  = $_POST['passwrd'];
$table = $_POST['tblname'];
$defn  = $_POST['table'];
print "$defn<BR>";
$link = mysql_connect($host, $user, $pass)
or die ("Unable to connect to DB server");
//create table
mysql_select_db ($dbase)
or die ("Unable to open ".$dbase);
print "Database opened!<br>";
$query = "CREATE TABLE $table ($defn)";
mysql_query($query) 
  or die (mysql_error());
//report success
print "Table created!";
mysql_close($link);
When the page is called from the entry FORM defined above, the host name is received in the $_POST array, at the key hostname, so that the discriminating expression !isset($_POST['hostname']) is false. The else part of the if ... else structure is implemented.

It retrieves the information sent in from the entry FORM, then calls the needed MySQL functions to create the table:

-the mysql_connect function connects the script to the MySQL database server and return the resource type data which represents the connection
-the mysql_select_db function connects to a database
-the mysql_query function submits the SQL statement contained in its argument to the MySQL database server
-the mysql_close function closes the connection
If an error occurs when executing the mysql_query function, the mysql_error is called. It writes out the error message to the HTML page.
session_start(); //or session.auto_start=1
$_SESSION['hostname']= $host;
$_SESSION['usrname'] = $user;
$_SESSION['passwrd'] = $pass;
$_SESSION['dbname']  = $dbase;
$_SESSION['tblname'] = $table;
?>
<FORM name='formname'>
  <INPUT name='linkcall'
   onfocus='location.href="DBDescribe.php"'
  >
</FORM>
<SCRIPT>
  formname.linkcall.focus();
</SCRIPT>
<?php
}//end else $_POST
?>
</b>
</BODY></HTML>
The session_start function starts a session which will persist when the current page is replaced by a new one. Creating a session also creates the $_SESSION superglobal array. The information needed to connect to the database and access the table that was created are saved into the $_SESSION array.

The "onfocus" script of the linkcall INPUT element is executed when the element get the focus; this script is:
uri="http://localhost/hpp/DBDescribe.php";
location.href=uri;

location is an object pertaining to an HTML page, which identifies the page location. href is a property of the location object; its value is the URI of the page. Changing it replaces the page with the one defined by the new URI. In the above we saw how this is done through the replace method. Here this property is directly changed by an assignment (=) operator.
Focus is automatically set on the element, as seen below.

The script contained in the SCRIPT element:
formname.linkcall.focus( );
is executed when this part of the page is loaded and sets the focus on the linkcall element so that its "onfocus" script is run. This causes the current page to be replaced by the new page at location DBDescribe.php. This is the new page URI, relative to the directory that contains the script. If you run the application by clicking on the link above, the absolute URI of the new page is http://localhost/hpp/DBDescribe.php.

1.6.3 Table description script

This script is called by the DBCreateTable.php and displays a description of the table created there.

In fact, this script needs only to be passed the table name and the information needed to access the database: host name, database name, user name and password.

DBDescribe.php - not to be called directly

<HTML><HEAD><TITLE>DESCRIBE TABLE</TITLE>
<STYLE>
   TD {padding:0.25em;}
   TT {color:#0000AA;}
</STYLE>
</HEAD>
<BODY>
<?php 
  session_start();
  $name  = $_SESSION['tblname'];
  $host  = $_SESSION['hostname'];
  $user  = $_SESSION['usrname'];
  $pass  = $_SESSION['passwrd'];
  $dbase = $_SESSION['dbname'];
?>
The start_session function starts a session, and makes the $_SESSION superglobal array available.

The table name and the information needed to access the database were put in the $_SESSION array by the calling script DBCreateTable.php, with the indices as follows:

-table nameindex: 'tblname'
-host nameindex: 'hostname'
-user nameindex: 'usrname'
-passwordindex: 'passwrd'
-database nameindex: 'dbname'
Table: 
<?php print ("<b>$dbase.$name</b><p>"); 
$link = mysql_connect($host, $user, $pass)
or die ("Unable to connect to DB server");
  mysql_select_db ($dbase)
   or die ("Unable to open".$dbase);
  $query = "DESCRIBE ".$name;
  print ("QUERY: <tt>$query</tt><br>");
  $desc  = mysql_query($query);
  $col   = mysql_num_fields($desc);
?>
The table description is the result of the query of the form: DESCRIBE table_name.
The MySQL functions used here are:
-mysql_connect -- connects to the database server
-mysql_select_db -- connects to the database
-mysql_query -- submits the SQL query, which is the DESCRIBE query shown above
-mysql_num_fields -- returns the number of columns in the table
The table description is returned in the resource variable $desc.
Table description<br>
<TABLE>
<THEAD style='color:red;
              background:black;'>
<TR><TD>Field<TD>Type<TD>Null
    <TD>Key<TD>Default<TD>Extra
</THEAD>
<TBODY style='background:#000088;
              color:white;'>
<?php
while($row=mysql_fetch_array($desc)){
    print ("<TR>");
    for ($i=0; $i < $col; $i++){
         print ("<TD>$row[$i] ");
    }//end for $col
    print ("\n");
}//end while $row
print ("</TBODY></TABLE><p>\n");
mysql_free_result($desc);
mysql_close($link);
?>
</BODY></HTML>
The table description is displayed in a table, with the following columns that represent the different properties of a row: Field (column name), Type (data type), Null (NULL authorization), Key (key property), Default (default value), Extras (other properties).

Each column of the table is described by a row in the $desc resource. The mysql_fetch_array returns the next row of the resource, into the $row array. The properties of the related column are in the order enumerated above: column name, data type, NULL authorization, etc. These properties are displayed in a row of the display table.

The mysql_fetch_array returns false when no more row exists, ending the while loop.

The mysql_free_result frees the memory associated with the $desc resource.
The mysql_close closes the connection to the database server.

1.6.4 Table insert scripts

DBEntry.php
<HTML><HEAD><TITLE>INSERT</TITLE></HEAD>
<STYLE>
   TD {padding:0.25em;}
   TT {color:#0000AA;}
</STYLE>
<BODY>
<?php 
session_start();
if (count($_SESSION)==0){
  $_SESSION['on']="yes";
  $script = $_SERVER['SCRIPT_NAME'];
?>
<FORM name='box' method='POST'
      action='<?php echo $script; ?>'>
<TABLE>
  <TR><TD>Host name<TD><INPUT name='hostname'>
  <TR><TD>User name<TD><INPUT name='usrname'>
  <TR><TD>Password <TD><INPUT name='passwrd' 
                        type='password'>
  <TR><TD>Database <TD><INPUT name='dbname'>
  <TR><TD>Table    <TD><INPUT name='tblname'>
</TABLE>
<BUTTON type='submit'>SUBMIT</BUTTON>
</FORM>
<?php
}//end if count($_SESSION)==0
The session_start function starts a session, making the $_SESSION superglobal array available.
When the script is first requested, the $_SESSION array contains no data; the count function returns the number of items contained in it, which is 0. Therefore the text contained within the curly braces of the if structure is scheduled.
First a value is put into the $_SESSION array so that at the next call, the value returned by the count function is no longer 0. Then the URI of the current page, retrieved in the $_SERVER array, at key "SCRIPT_NAME", is written out as the value of the action attribute of the FORM element, so that the page is called back when the submit button is clicked.
The FORM element contains the INPUT fields for the user to enter the name of the table and the information to access it.
else {
if (!isset($_SESSION['nrow'])){
  $host  = $_POST['hostname'];
  $user  = $_POST['usrname'];
  $pass  = $_POST['passwrd'];
  $dbase = $_POST['dbname'];
  $table = $_POST['tblname'];
  $_SESSION['hostname']=$host;
  $_SESSION['usrname']=$user;
  $_SESSION['passwrd']=$pass;
  $_SESSION['dbname']=$dbase;
  $_SESSION['tblname']=$table;

When the script is called back, the $_SESSION has one item, therefore, as a result of testing the count($_SESSION) function, the text in the else structure is scheduled.

At the first call back, the description of the table where data are to be inserted is not available yet; the item with key "nrow" which represents the number of columns in the table is not set, so that isset($_SESSION['nrow']) is false and !isset is true. The script seen on the left is scheduled. It retrieves the table name and database access information from the $_POST array and puts them into the $_SESSION array.

  $link = mysql_connect($host, $user, $pass)
  or die ("Unable to connect to DB server");
  mysql_select_db ($dbase)
   or die ("Unable to open".$dbase);
The mysql_connect function connects to the database server. The mysql_select_db function accesses the database.
  $query = "DESCRIBE ".$table;
  $desc  = mysql_query($query);
  $r=0;
  while($row=mysql_fetch_array($desc,MYSQL_NUM)){
    $_SESSION["rww$r"]=$row[0]; //column name
    $p = strpos($row[1],"("); 
    if($p) $typ = substr($row[1],0,$p);//type
    else  $typ = $row[1];
    $_SESSION[$row[0]]=$typ;    //column type
    $r++;
  }//end while $row
  mysql_free_result($desc);
  mysql_close($link);
  $_SESSION['nrow']=$r;         //number of rows
}//end !isset($_SESSION['nrow'])
?>
The sequence on the left retrieves the description of the table where data are to be inserted, using the "DESCRIBE table" SQL statement, and puts this description into the $_SESSION array:
-the column names at keys "rwwr" where r is the rank of the column in the described table ("rww0", "rww1", "rww2", etc.)
-the column data types at keys equal to the column names.
-the number of rows at key "nrow"

The mysql_query function here returns a description table each row of which contains the properties of one of the columns of the queried table. The mysql_fetch_array function returns the next unretrieved row of the description table, or false when no more rows are available. Here the row is returned in the $row array. The information of interest to us are:

-the column name (at index 0)
-the column type (at index 1)
Here is an example of how a column type looks like:
int(3) UNSIGNED ZEROFILL
In a case such as this, the strpos function is used to locate the ( sign (left parenthesis), and the substr cuts off the right part of the value at the ( so as to retain only the data type code int. The data type code ($typ) is assigned as the value to an item in the $_SESSION array, with the column name ($row[0]) as the key.
<FORM name='entryForm' action='DBInsert.php' 
      method='POST'>
<TABLE><TBODY>
<?php
  $dbase = $_SESSION['dbname'];
  $table = $_SESSION['tblname'];
  print ("<b style='color:red'>$dbase.$table</b><p>"); 
  print ("<b style='color:blue'>ENTER DATA:</b><BR>");

  $nrow = $_SESSION['nrow'];
  for ($r = 0; $r < $nrow; $r++){
    $colname = $_SESSION["rww$r"];
    print ("<TR><TD>$colname<TD><INPUT type='text' 
            name=$colname
            onblur='report.message.value=\"\"
                    report.query.value=\"\"
                   '>");
    print ("\n");
  }//end while $row
  print ("</TBODY></TABLE><p>\n");

  if (isset($_SESSION['return'])) {
      $message=$_SESSION['return'];
      $query  =$_SESSION['query'];
  }
  else {$message = "";
        $query   = "";
  }
?>
<BUTTON type='submit' name='send_'>SEND</BUTTON>
</FORM>
The sequence starting here is run every time the script is called back. It creates the INPUT elements for the user to enter the data to be inserted.

The database and table names are retrieved from the $_SESSION table and displayed for the user information.

The INPUT elements are generated by the for loop seen on the left. $_SESSION['nrow'] contains the number of columns. The column names as defined in the table, are in the items $_SESSION['rww0'], $_SESSION['rww1'], etc. An INPUT element has an "onblur" script which is called when the element loses focus, e.g. when after entering data in the element, the user leaves it to enter data into another element. The "onblur" script erases the elements message and query contained in the report FORM. These elements receive messages from the previous data insert run. They are erased after the user has a chance to see them.

If there was a previous run, it set:

-a return message in the $_SESSION['return'] item, reporting on its insert operation
-the insert statement in the $_SESSION['query'] item
These data are retrieved here in the $message and $query variables. They will be set in INPUT elements to be displayed, below.
<SCRIPT>
  entryForm[0].focus();
</SCRIPT>

<FORM name="report" action="TheEnd.html">
  <BUTTON type='submit'>QUIT</BUTTON><BR>
  <INPUT name='message' size='128' 
         value="<?php print $message ?>">
  <INPUT name='query' size='128' 
         value="<?php print $query ?>">
</FORM>
<?php
}//end else count($_POST)
?>
</BODY></HTML>
entryForm[0].focus() is a Javascript script. It sets the focus to the first data entry field to start the data entry process.

The report FORM contains the BUTTON to terminate the data insert process, and the INPUT elements message and query to receive the report message from the previous run. The messages were retrieved into the variables $message and $query variables and assigned to the values of the INPUT elements by the PHP scripts.

The DBInsert.php script is called to handle the insert operation proper, using the data entered by the user on the DBEntry.php page.

DBInsert.php - not to be called directly

<?php
session_start();
$table = $_SESSION['tblname'];
$host  = $_SESSION['hostname'];
$user  = $_SESSION['usrname'];
$pass  = $_SESSION['passwrd'];
$dbase = $_SESSION['dbname'];

$numerics ="BOOLEAN/TINYINT/SMALLINT/MEDIUMINT/";
$numerics.="INTEGER/INT/BIGINT/FLOAT/DOUBLE/";
$numerics.="DECIMAL/YEAR/TIMESTAMP";

$link = mysql_connect($host, $user, $pass)
   or die ("Unable to connect to DB server");
mysql_select_db ($dbase)
   or die ("Unable to open".$dbase);
The session_start function is called to start a session and make the $_SESSION superglobal array available. The name of the table where data are to be inserted and the information to access it are retrieved from this array. The mysql_connect and mysql_select_db connect to the database server, then access the datbase.

The $numerics string variable contains the codes of the numeric data types. It is used to test whether the data of a column is of a numeric type, in order to put it within single-quotes when it is not.

"." is the concatenation operator in PHP. The ".=" assignement operator appends the string on the right handside to the one on the left.

$list  ="";
$values="";

foreach ($_POST as $key=>$val) {
if ($key!='send_'){ //exclude BUTTON
  $typ = $_SESSION[$key];
The foreach loop extracts the items of the $_POST array, and for each item, sets the key in $key and the value in $val, and runs the script within the curly braces.

The data received in the $_POST array come from the data entry FORM of the calling script, which contains the submit BUTTON and the INPUT elements. The names of the latter are those of the table columns and their values, the values to be inserted.

The submit BUTTON, named "send_", is to be disregarded.

The remaining keys and values are the column names and the corresponding values to be inserted. At each iteration, they are set in the $key and $val variables.

The data type of a column has been put into the $_SESSION array, with the column name as the key. It is retrieved into the $typ variable.

  //populate TIMESTAMP column
  if (strcasecmp($typ,"TIMESTAMP")==0){
     if ($val==0) $val = time();
  }//end if $type==TIMESTAMP

  //enclose non-numeric between quotes
  if (stripos($numerics,$typ)===false) 
     {$val = "'$val'"; }

  //add definition to list
  if (strlen($values)==0){
    $list  ="$key";
    $values="$val";
  }
  else{
    $list  .=",$key";
    $values.=",$val";
  }
}//end if $key!='send_'
}//end foreach
If the column is of the TIMESTAMP type, and a 0 was entered for it, the current time stamp as returned by the time() function is assigned to it (the function strcasecmp compares the 2 strings passed as arguments, and returns 0 if they are equal, disregarding lower or uppercase.)

The stripos function here searches the $numerics string for a match with the $typ string, disregarding case. It returns the boolean false if no match is found, meaning that the column is not of a numeric type. In this case, the value is enclosed within single-quotes. The identity sign === is satisfied if the function returns the boolean false indicating a no match situation, not when it returns a 0 which occurs when the match is found with the first word in $numeric.

The variable $list is to contain the comma separated list of column names, the variable $values the comma separated list of values, to be assembled into an INSERT SQL statement.

$query = "INSERT INTO ".$table." ($list)";
$query.= " VALUES ($values)";
$_SESSION['query']=$values;
$ok  = mysql_query($query);
if ($ok) {     //OK
 $_SESSION['return']="Insert successful";
}//end if $ok
else {         //ERROR
   $_SESSION['return']=mysql_error();
}//end else $ok
mysql_close($link);
?>
<FORM name='getback'>
  <INPUT name='callEntry' 
   onfocus='location.href="DBEntry.php"'>
</FORM>
<SCRIPT>
  getback.callEntry.focus();
</SCRIPT>
The INSERT statement is created, in the form:
INSERT INTO table (name_list) VALUES (value_list)
The value list is saved in the $_SESSION array at the key "query". The statement is submitted through the mysql_query function.

Upon return from the mysql_query function, a completion message is put in the $_SESSION array, with the key "return". This message is "Insert successful" if the function is successfully executed. Otherwise, it is the MySQL error message returned by the mysql_error function.

The "onfocus" script of the callEntry element links to the page "DBEntry.php" for new data to be entered.
The getback.callEntry.focus( ) Javascript statement sets the focus on the callEntry element, so that its "onfocus" script is run, and links to the data entry page.

1.7 An application generator

This section describes the tools that can be used to generate the scripts as used in 1.5 above.

In each script used in that applicaion, apart from the script name, the data specific to the application are concentrated in very few data strings:

-a list the families of products, in the header page
-a family identification and the list of the products of the family, in the catalog pages
The rest is common code that can be used for any application of the same design.

The specific data can be stored in database tables. Only 2 are needed:

-a family description table
-a product description table

The script code are held in model scripts which are templates that can be transformed into application scripts by completing them with application specific data.

In the following is a description of the database tables, the template pages and the processing script to generate the application from these.

1.7.0 Database tables

An application is generated from the data contained in 2 tables:
-a family table
-a product table

The family table must have the columns with the following names and other properties:

NameData typeOther propertiesComment
idnumberinteger typeZEROFILL UNSIGNEDit is advisable to limit the number of digits
shortnameVARCHAR  

The product table must have the columns with the following names and other properties:

NameData typeOther propertiesComment
familyinteger typeZEROFILL UNSIGNED PRIMARY KEYsame as idnumber in the family table
subfamilyinteger typeZEROFILL UNSIGNED PRIMARY KEYit is advisable to limit the number of digits
numbreinteger typeZEROFILL UNSIGNED PRIMARY KEYit is advisable to limit the number of digits
nameVARCHAR  
priceDECIMAL or
floating point
  

SQL statements that can be used to create the tables are:
Family description table

CREATE TABLE family (idnumber SMALLINT(3) UNSIGNED ZEROFILL NOT NULL PRIMARY KEY,
shortname VARCHAR(8) NOT NULL, fullname VARCHAR(24) NULL)
Product description table:
CREATE TABLE product 
(family SMALLINT(3) UNSIGNED ZEROFILL NOT NULL CONSTRAINT PRIMARY KEY (family,subfamily,numbre),
subfamily TINYINT(2) UNSIGNED ZEROFILL NOT NULL, numbre INTEGER(5) UNSIGNED ZEROFILL NOT NULL, 
name VARCHAR(16) NOT NULL, price FLOAT(10.2) UNSIGNED NOT NULL)

1.7.1 The template pages

A application uses 4 sorts of pages:
1.The top page, a FRAMSET page which divides the display space into 2 frames, the upper frame where a permanent page is loaded, the lower frame which accomodates the order entry page
2.The header page, to loaded in the upper frame of the top page
3.The starter page, to be loaded in the lower frame
4.A number of product description pages, each containing the description of one family of product.

A model is set up for each of these catagories. They are derived from the pages used in the application presented in 1.5 above, by replacing the data specific to this application with place holders composed of asterisks.

The model for the top page is as follows:
OrderTop.model

<HTML><HEAD><TITLE></TITLE><HEAD>
<FRAMESET rows="60px,*">
<FRAME name="one" src="****Head.html">
<FRAME name="two" src="****Start.html">
</FRAMESET>
</HTML>
To view the other pages, please click on the appropriate link below:
Model header pageOrderHead.model
Model start pageOrderStart.model
Model product list pageOrder.product.model

You can see that the place holders to be replaced (standing out in red) in the display, are very few. The rest of the code in each page has been copied from the corresponding page used in 1.5 above.

1.7.2 The application generation script

DBCreateAppl.php
<HTML><HEAD>
<TITLE></TITLE>
<META http-equiv="Content-Type" content="text/html">
</HEAD>
<BODY>
<?php
if (count($_POST)==0){
  $script = $_SERVER['SCRIPT_NAME'];
?>
  <FORM name='box' 
        action='<?php echo $script; ?>'
        method='POST'>
  <TABLE>
    <TR><TD>Application code:
        <TD><INPUT name='appl' type='text'>
             NO SPACE PLEASE
    <TR><TD>Family table:
        <TD><INPUT name='family' type='text'>
    <TR><TD>Product table:
        <TD><INPUT name='product' type='text'>
    <TR><TD>Host:
        <TD><INPUT name='host' type='text'>
    <TR><TD>User:
        <TD><INPUT name='user' type='text'>
    <TR><TD>Password:
        <TD><INPUT name='pass' type='password'>
    <TR><TD>Database:
        <TD><INPUT name='dbase' type='text'>
    <TR><TD>
        <TD><BUTTON type='submit' 
                    name='sub' type='text'>
    SUBMIT</BUTTON>
  </TABLE>
  </FORM>
<?php
}//end if count($_POST)==0
This is a 2 phase script as already seen in the applications above.

The script is first called from a user's browser, without any data passed to it, so that the $_POST superglobal array in its environment is empty. The count function here returns 0. The text within the curly braces of the if structure is scheduled. First, the URI of the current page at the key 'SCRIPT_NAME', from the $_SERVER array, and writes it out as the value of the FORM action attribute, so that a click on the submit button will request the page again.

The HTML code defines an entry FORM element where the user can enter the identification of the database tables that contain the application specific data, and the information needed to access them:

-application abbreviated name
-family table name
-product table name
-host name
-user name
-password
-database name
else {
   $appl    = $_POST['appl'];//application name
   $family  = $_POST['family']; //family table
   $product = $_POST['product'];//product table
   $host    = $_POST['host'];
   $user    = $_POST['user'];
   $pass    = $_POST['pass'];
   $dbase   = $_POST['dbase'];
//connect to server
  $link = mysql_connect($host, $user, $pass)
  or die ("Unable to connect to DB server");
//connect to database
  mysql_select_db ($dbase)
    or die ("Unable to open $dbase");
When the page is called back from the first phase, information is passed in the $_POST superglobal array. The count function is no longer 0, and the text within the curly braces of the else structure is scheduled. The script retrieves the transmitted data from the $_POST array into PHP variables.

The mysql_connect function connects the script to the database server.

The mysql_select_db function connects the script to the database.

For more on the MySQL functions, please click here

//access family table
  $query="SELECT idnumber, shortname FROM ";
  $query.=$family;
  $famresult = mysql_query ($query)
   or die ("Unable to access table $family");
//family properties
  $meta = mysql_fetch_field($famresult); 
  $fldlen  = $meta->max_length;
  echo ($fldname = $meta->name." - ");
  echo ($fldtype = $meta->type);
 
The ".=" assignment operator appends the $family variable value to the $query value. The query to submit is: SELECT idnumner, shortname FROM family
where family is the name of the family table.
The mysql_query function submits the SELECT query, and returns the table that contains the family ID codes (idnumber) and short names (shortname).

The function mysql_fetch_field returns the informations on the different columns contained in the resource result returned by a query. As is called here, without specifying the column number, the function returns informations on the next column (here: the first, that holds the family code, idnumber) in the form of an object. The properties of the object queried here are:

-the maximum data length in the column ($meta->max_length) which is the family code length
-the column name ($meta->name, echoed for you to check)
-the column data type ($meta->type, echoed for you to check)
Note the -> notation to denote object properties.

//process families
  $famStr="";
  while ($fam = mysql_fetch_array($famresult)){
    $str   = "";
    $lastcode = "         ";
    $famid = $fam['idnumber'];
    $famlb = $fam['shortname'];
    $famStr.=$famid.$famlb."/";
The product families are processed in the grand loop while ($fam = ...) { ... }
At each run, the mysql_fetch_array function returns the next row from the $famresult resource, which contains the informations on one of the available product families. This row is an array where the keys are the names of the queried columns from the family table:
-idnumber is the family ID code -- in the database table definition it must be declared with a ZEROFILL option so as to be filled with 0's to the left if it does not fill the maximum size
-shortname is a short name assigned to the family, for exemple, 'flower' for a complete name of 'Garden flowers and potted plants'
These 2 pieces of data are contatenated and terminated with a / sign to give a compound like 002flower/, and the result is appended to the $famStr string, which constitutes the family list.

    $query = "SELECT family, subfamily,";
    $query.= " numbre, name, price";
    $query.= " FROM $product WHERE family=$famid";
    $query.= " ORDER by family, subfamily, numbre";
    $proresult = mysql_query($query)
     or die ("<br>Unable to access table $product");
    while ($pro = mysql_fetch_array($proresult)){
      $profam = $pro['family'];
      $prosub = $pro['subfamily'];
      $pronum = $pro['numbre'];
      $prolab = $pro['name'];
      $propri = $pro['price'];
      //compose code
      $code="$profam$prosub$pronum"; //product code
      $p = 0;
      while ($p < strlen($code)){ 
        $a = substr($lastcode,$p,1);
        $b = substr($code,$p,1);
        if (strcasecmp($a, $b)!=0) break;
        $p++;
      }
      $lastcode = $code;
      $code = substr($code, $p); //shorten code
      //add product to string
      $str.="-/$code/$prolab/$propri/"; //append
    }//end while $pro=mysql_fetch_array($proresult)
The "product" table is next queried to retrieve all the products of the current family. The query SQL statement constructed into the $query variable, is:
SELECT family, subfmily, numbre, name, price FROM product WHERE family=familyCode ORDER BY family, subfamily, numbre
The mysql_query function returns the resulting table in the $proresult variable.
The while ($pro = ..) { ... } loop processes the result rows returned into the $pro array. Each row represents one of the products. The keys in the $pro array are the names of the columns in the product table.
The product ID code as used in the application, is composed of the family code (family column), the subfamily code (subfamily column) and the product code within its subfamily (numbre column). These data are concatenated and assigned to the $code string.
The while (p < strlen($code)) { ... } loop derives a shortened representation of the code by removing the part on the left which is the same as found in the previous code. The function substring($code,$p,1) returns the one character found at position $p in the $code string. The current code ($code) and the previous one ($lastcode) are scanned and compared from left to right. The first position where a difference is found is where the retained substring starts. The data describing the product: the - mark indicating a "not selected" initial status, the product code ($procode), product name ($prolab) and product price ($propri), put together, each terminated by a / sign, compose the string description of the product. This string is appended to the $str string which is to hold the product list.

  $filename="$appl.$famlb.html";
   $s = file_get_contents("Order.product.model");
   $s = str_replace('********',$famid.$famlb,$s);
   $s = str_replace('******',$str,$s);
   $s = str_replace('****',$appl,$s);
   file_put_contents($filename,$s);
 }//end while $fam=my_sql_fetch_array($famresult)
 
At the end of the while ($pro=mysql_fetch_array...) loop, the product list of the current family is completed. The page that holds the family product descriptions is to be created. This is an HTML page with a file name of the form appl.family.html where appl is the application name and family is the family name. Note that in the expression "$appl.$famlab.html", the dots are data characters, not the concatenation operators, as they are enclosed within the quotes.
The file_get_contents function from the PHP core reads in the contents of the model for a catalog page. In this, the place holders for the application specific data are composed with asterisks:
-str_replace('********',$famid.$famlb,$s) -- replaces the string '********' with the family code ($famid) and name ($famlb) compound, in the contents of the file ($s)
-str_replace('******',$str,$s) -- replaces the string '******' with the product list ($str), in the contents of the file ($s)
-str_replace('****',$appl,$s) -- replaces the string '****' with the application name ($appl), in the contents of the file ($s)
The file_put_contents function from the PHP core creates the file that holds the family product description, with $s as its contents.

 $s = file_get_contents ("OrderTop.model");
 $s = str_replace("****",$appl, $s);
 file_put_contents($appl."Top.html",$s);

 $s = file_get_contents ("OrderHead.model");
 $s = str_replace("****",$famStr,$s);
 $s = str_replace("**",$fldlen,$s);
 file_put_contents($appl."Head.html", $s);

 $s = file_get_contents ("OrderStart.model");
 $s = str_replace("****",$appl, $s);
 file_put_contents($appl."Start.html", $s);
 flush();
}//end else count($_POST)==0
?>
</BODY></HTML>
At the end of the while ($fam=my_sql_fetch_array ... loop, the processing of the product families is complete. The pages that control the applications are to be created, from the model files:
-the FRAMESET master page which contains the other pages (applTop.html page) -- derived from OrderTop.model
-the header page contained in the upper frame (applHead.html page) -- derived from OrderHead.model
-the starter page contained in the lower frame at application start (applStart.html page) -- detived from OrderStart.model
where appl is the application name.
In these files, the place holder **** are replaced by the application name. In the header page, the "**" is place holder for the length of the family code. It is replaced by the value retrieved from the family table.

Again, the file_put_contents function is used to create the files. The flush function makes sure that no data intended for output is stuck in the buffers.


top Cover  Copyright  Contents  Reference  Index  previous  next