Next Previous Contents

5. HTML Widgets Classes

5.1 Sql_Query

Sql_Query will generate a query form for simple table queries: A list of field names, comparision operators and input fields is presented. The user may search for any values in any of the presented columns using SQL standard operators. Multiple query conditions are possible and these conditions can be joined using AND and OR operations.

The number of query conditions can be made variable. If so, the user may shrink and grow the query widget using the appropriate buttons.

All button labels and other messages of the interface are variable and held in language dictionaries. Currently, de and en dictionaries are provided.

Instance variables


classname
Serialization helper: The name of this class.
persistent_slotsSerialization helper: Names of all persistent slots
conditionsNumber of query conditions
input_sizeVisible width of input fields
input_maxUseable width of input fields
methodForm method to GET or POST the form
langLanguage dictionary to use
translateFlag: translate column names
containerFlag: create a master container
variableFlag: create resize buttons
Accessible instance variables.


dict
The GUI language dictionary.
compareSQL comparison function dictionary.
Internal instance variables.

Instance methods

Accessible instance methods

start()

Initialization function. Currently empty.

form($base, $option, $class, $target)

The function will generate and return HTML for the SQL Query selection form. All variables in the form will start with the prefix $base and have numeric indices appended after an underline character. It is possible to have multiple Sql_Query instances on a single page, if they use different base characters.

The function must know the field names of the SQL table that is to be queried. $option can be either a simple array of these field names ($translate set empty) or a hash field name to long name ($translate set to on).

All tags in the generated form are tagged with a CSS stylesheet class, if $class is set to a CSS classname. $class is optional and if it is left empty, no class attributes are generated. $target is the URL of the SQL Query form target. It is optional and if it is left empty, a self referencing form is generated (recommended).

The function returns a string containing the HTML to render the SQL Query selection form.

where($base, $incr)

When the form() generated page is submitted, a lot of parameters have to be evaluated and transformed into a SQL where condition matching the user selections. The where() function takes care of all this; it just needs to be told which $base prefix has been used in the form() call.

The $incr parameter is optional and determines how many query condition rows are added or subtracted when the "More" and "Fewer" buttons are used. The default value is 1.

The function returns a string which can be successfully used behind a "where" keyword in a SQL query.

Internal instance methods

plain_where($base)

This function does all the work for where(), but does not resize the query condition window.

Example

The Sql_Query class can be used directly. It is more useful when made persistent, so it is recommended that you add the line require("sqlquery.inc") to your prepend.php3 file where indicated in that file.

See the Table class in this section for a nice method to display and format query results. See the DB_Sql class (a core class) for a nice method to connect to databases.

The following code fragment is quite large, but contains a complete and working example using the Sql_Query, DB_Sql and Table classes to query a database table.


<?php
  // We require() sqlquery.inc and table.inc in prepend.inc
  // to make this example work!
  page_open(array("sess" => "Example_Session"));

  $db = new DB_Example;   // That's a DB_Sql subclass.
  $t  = new Table;    // For formatting results
  $t->heading = "on"; // We want table headings..
?>
<html>
<head><title>Testseite</title>
<style type="text/css"><!--
h1          { font-family: arial, helvetica, sans-serif; color: #d33e30 }
table.test  { background-color: #eeeeee }
th.test     { font-family: arial, helvetica, sans-serif  }
td.test     { font-family: arial, helvetica, sans-serif }
table.query { background-color: #cccccc }
td.query    { font-face: arial, helvetica, sans-serif }
--></style>
</head>
<body bgcolor="#ffffff">
<h1>Testpage</h1>
<?php
  // the following fields are selectable
  $field = array(
    "username"   => "Login Name",
    "password"   => "Password",
    "perms"      => "Permissions"
  );

  // When we hit this page the first time,
  // there is no $q.
  if (!isset($q)) {
    $q = new Sql_Query;     // We make one
    $q->conditions = 1;     // ... with a single condition (at first)
    $q->translate  = "on";  // ... column names are to be translated
    $q->container  = "on";  // ... with a nice container table
    $q->variable   = "on";  // ... # of conditions is variable
    $q->lang       = "en";  // ... in English, please

    $sess->register("q");   // and don't forget this!
  }

  // When we hit that page a second time, the array named
  // by $base will be set and we must generate the $query.
  // Ah, and don't set $base to "q" when $q is your Sql_Query
  // object... :-)
  if (isset($x)) {
    $query = $q->where("x", 1);
  }

  // In any case we must display that form now. Note that the
  // "x" here and in the call to $q->where must match.
  // Tag everything as a CSS "query" class.
  printf($q->form("x", $field, "query));
  printf("<hr>");

  // Do we have a valid query string?
  if ($query) {
    // Show that condition
    printf("Query Condition = %s<br>\n", $query);

    // Do that query
    $db->query("select * from auth_user where ". $query);

    // Dump the results (tagged as CSS class test)
    printf("Query Results = %s<br>\n", $db->num_rows());
    $t->show_result($db, "test");
  }

  page_close();
?>
</body>
</html>

5.2 Table and CSV_Table

The Table class is a neat way to format two-dimensional associative arrays of data or the results of a database query into a table. Table and its subclasses allow you to simply pass them either an array or a query result and they spit out the proper HTML for a table containing all the values. Table has some primitive filtering capabilities making it useful even without subclassing, but for the full power of Table you have to write your own subclass.

When used with the check option, it is assumed that the table is part of a HTML FORM element. Code is generated to create an INPUT TYPE=CHECKBOX element before each table row. The checkboxes will form an array indexed by row number. The name of the array will whatever you set the check instance variable to.

Exactly one of two types of possible column filtering take place when each table row is generated. If the fields instance variable is set, only the columns keyed by the named fields in that array are shown in that order. That is, if you fill in the fields instance variable with array("a", "c", "e"), only the columns a, c and e become part of the generated table.

If fields has not been set, all data columns are traversed with each() and all columns whose names match the regexp in filter are shown in the table. By default, this regular expression lets through all column names that start with an alphabetic character and continue with either alphanumeric characters or "_" (underscore). This default has been chosen, because the DB_Sql database class uses mysql_fetch_array() internally to get data from the database and this function returns all columns twice with both a numeric index and proper column names. The default filter will make all data show up only once and with proper column names.

Additionally, the map_cols instance variable provides column name remapping. If map_cols is set, it will remap the name of the found column with a new name.

For instance, a table with the following columns, fname, lname, and mydate can be remapped to First Name, Last Name, and Date using the following code (where $t is your instantiated Table class object):


$t->map_cols = array("fname"  => "First Name",
                     "lname"  => "Last Name",
                     "mydate" => "Date");

The map_cols instance variable also allows you to map column names to different languages using this same technique.

For derived classes, the instance variable add_extra has been added. If this variable is set, then the functions table_heading_row_add_extra() and table_row_add_extra() are called. In the Table class, these functions do nothing, but in derived classes override these functions to provide additional functionality that may be needed. For instance, hyperlinks to provide edit, delete, or view capabilities for that row could be easily added into these functions (in your derived Table class) allowing greater customization.

A subclass of Table, CSV_Table, is being provided to allow to create CSV representations of your data with minimal effort. CSV (comma separated values) can be imported by MySQL's LOAD DATA INFILE statement and many spreadsheet import functions.

The Table class now provides both high-level, mid-level and low-level functions through modularization. This allows programmers to use either the simplified high-level functionality or, depending on the degree of complexity needed, the power of the mid- or low-level functions. Every effort to maintain backwards compatibility has been applied. However, it would be a good idea to become familiar with the new functions if you use the Table class extensively. Typically, the high- and mid-level support functions begin with show_ while the low-level functions do not.

Instance variables


classname
Serialization helper: The name of this class.
checkIf set, the check option is active.
filterA regular expression selecting the columns that are shown.
fieldsA list of colum names that are shown.
headingA flag; if set, a heading is being created.
map_colsA list of colum names that remap (replace) the static column names.
add_extraA flag; if set, the the extra functions for heading and rows are called.
Accessible instance variables.

Instance methods

High-level instance methods

show($ary, $class = "")

Will format and print the two dimensional array (or hash) $ary as a table according to the filtering rules explained above. If $class is set, each HTML element will be tagged as belonging to the named class; this is useful with cascading style sheets.

show_page($ary, $start, $num, $class = "")

Just as show(), but will show only num elements starting at start.

show_result($db, $class = "")

Will format and print the result set of $db. $db is exspected to be a subclass of DB_Sql that has just been sent a query. Table will grab all available results from the result set of that query by calling $db->next_record() repeatedly and format them into a table.

show_result_page($db, $start, $num, $class = "")

Just as show_result(), but will show only num elements starting at start.

Mid-level instance methods

show_table_rows($ary, $class="")

Walks the passed array displaying each row of data as an HTML table row.

show_table_rows_result($db, $class="")

Walks the passed database object displaying each record as an HTML table row.

show_table_page_rows($ary, $start, $num, $class="")

Walks the passed array displaying each row of data as an HTML table row. However, data does not start displaying until $start element and end after $num rows.

show_table_page_rows_result($db, $start, $num, $class="")

Walks the passed database object displaying each record as an HTML table row. However, data does not start displaying until $start record and ends after $num records have been displayed.

show_table_heading_row($ary, $class="")

Uses the passed array to create an HTML header row.

show_table_heading_row_result($db, $class="")

Uses the passed database object to create an HTML header row.

show_table_heading_cells($data, $class="")

Walks the passed array and displays each item in an HTML table header cell.

show_table_cells($row, $row_key, $data, $class="")

Walks the passed array and displays each item in an HTML table cell.

Low-level instance methods

table_open($class = "")

This function can be overridden by a subclass of Table. It is called as the very first step in table creation and should output HTML that opens a table (for example printf("<table%s>\n", $class?" class=$class":"");).

table_close()

This function can be overridden by a subclass of Table. It is called as the very last step in table creation and should output HTML that closes a table (for example printf("<table>\n");/).

select_colnames($data)

Internal function to generate a list of column names.

table_heading_row($data, $class = "")

Internal driver function to generate a table heading row.

table_heading_cell($col, $val, $class)

This function can be overridden by a subclass of Table. It is called each time a table heading cell is to be generated.

$col is the current column number, $val is the name of the column. $class is the HTML CSS class of the element that is to be generated.

table_heading_cell_open($class="")

Starts a header cell.

table_heading_cell_close($class="")

Ends a header cell.

table_heading_row_add_extra($data, $class="")

Virtual function for derived classes. This function is called after all header cells have been created. It allows the programmer to add additional HTML code to the header row before it is closed.

table_row($data, $class = "")

Internal driver function to generate a table row.

table_row_open($row, $data, $class = "")

This function can be overridden by a subclass of Table. It is called as the very first step in row creation and should output HTML that opens a table row.

$row is the current row number. $data is a hash of column name/value pairs for that row and $class is an optional HTML CSS class name for all generated elements.

table_row_close()

This function can be overridden by a subclass of Table. It is called as the very last step in row creation and should output HTML that closes a table row.

table_cell($row, $cell, $key, $val, $class)

This function can be overridden by a subclass of Table. It is called each time a table cell is to be generated.

$row is the current row number, $cell is the current cell number. $key is the current column name, $val is the value of the cell. $class is the HTML CSS class of the element that is to be generated.

table_cell_open($class="")

Starts a cell.

table_cell_close($class="")

Ends a cell.

set_checkbox_heading($class="")

This function creates an empty header cell to coincide with the checkbox option for that column.

table_checkbox_cell($row, $row_key, $data, $class="")

Outputs HTML code to display a checkbox. This function runs if the member variable $check has been set. $check should be set to some key within the $data array (ex: if $data["myKey"], then set $check="myKey").

set_checkbox($row, $row_key, $data, $class="")

Creates an HTML checkbox based on the passed data, only if the instance variable $check is set.

Example

Table is not automatically included or prepended into each page. Include the table class into the pages that are to use Table. Then create an instance of Table:


<?php
  // Include Table
  require("table.inc");
  
  // make a Table instance
  $t = new Table;
  
  // We want table headings to be printed.
  $t->heading = "on";

Now create a two dimensional array or prepare a database query and have table print it.


  // Create a database object
  $db = new DB_Session;
  
  // create a twodim array called $tab
  $tab = $db->metadata("active_sessions");
  
  // print that array
  $t->show($tab, "metadata");
  
  // prepare a database query
  $db->query("select * from active_sessions");
  
  // print that result
  $t->show_result($db, "data");

5.3 Form

The form class (sometimes called OOH Forms) is a convenience library for dealing with html forms. It provides Javascript and server-side form validation, and is customizable and extensible.

Using OOH Forms

The OOH Forms library consists of five files: oohforms.inc of_checkbox.inc of_radio.inc of_select.inc of_text.inc of_textarea.inc. oohforms.inc automatically includes the others. You may wish to modify this so you can manually include the files for just the form elements you use. Or you may wish to cut and paste the contents of the element files into oohforms.inc to save the overhead of multiple includes. Determining the appropriate configuration of the files for your site is left an exercise for the reader; for most purposes require("oohforms.inc") will suffice.

In general, the structure of a page that uses oohforms is as follows:


require("oohforms.inc");         // include the library

$f = new form;                   // create a form object

$f->add_element(...);     // set up form elements
$f->add_element(...);    
$f->add_element(...);    

if ($submitname)                 // Is there data to process?
  if ($err = $f->validate()) {   // Is the data valid?
    echo $err;                   // No; Display error
    $f->load_defaults();  // Load form with submitted data
  else {
    /* Process data */           // Data ok; Do something with it
  }

$f->start(...);                  // Start displaying form
$f->show_element(...);    // Show elements
$f->show_element(...);
$f->show_element(...);
$->finish();                     // Finish form

There are obviously many variations on this theme, but that covers the basics. Specific methods are documented below.

start($jvsname,$method,$action, $target)

Outputs the initial <form> tag and sets up some initial state needed by the class. All of the arguments are optional, though you'll usually want to use at least one in order to get Javascript validation. $jvsname is an arbitrary string used to link the Javascript to the form; if it is empty (the default), no Javascript validation is provided. $method is the HTTP method used to submit the form; default is "POST". $action is the URL that receives the form submission; default is $PHP_SELF. $target is the frame target for the form results; default is _self.

finish($after,$before)

Outputs the any hidden fields that were added to the form, the final </form> tag, then the Javascript validation function (if necessary). $after and $before are both optional; if either is a nonempty string it is output as additional Javascript to be run on submission of the form, either before or after the validation code. Though the order of the arguments may seem counterintuitive, it makes the common case easier to type; in general you'll want to wait until after the validation has taken place to do anything fancy with Javascript. Note that unlike with validation, OOH Forms has no way of giving you server side functionality equivalent to the Javascript you use here.

add_element($element)

add_element is used to define the attributes of a particular form element so that the other class methods can use and manipulate it properly. add_element takes exactly one argument: an associate array whose key value pairs are used to define the form element type and it's various attributes. Some of these attributes correspond to html attributes, while others are needed for the value added features of oohforms. The attributes and the syntax and semantics of the values they take are documented below; note that not all element types use all of the attributes

type

The type of element this is; can be "submit", "hidden", "text", "textarea", "select", "radio", "checkbox", or "file".

name

A string naming this element. This name will be used as an argument to other methods and will be the name="" value in the generated html (and hence the variable name in PHP). Do not append [] to the name if you want an array valued element; set the multiple attribute instead.

value

The default value of this form element. If the form element has the multiple attribute set, value can be an array. If the this is a select element, value can refer to either the textual name (label in the options array) or the submission value (value in options).

multiple

A flag to tell oohforms to assume this element is array valued. The use of this flag is most straightforward with select elements, but it can be use with text and checkbox elements as well. See the show_element documentation for more information about how oohforms deals with such elements.

extrahtml

Extra html code that is inserted verbatim into the opening form tag. For select elements, the extra html goes into the select tag, not the option tags.

size

For text elements, used to set the html size attribute that gives the width in characters of the text entry box. For select elements, the size (number of options visible at once) of the selection box. Validation is only performed on select elements if size is set to 1, since select validation doesn't make much sense if you can see multiple options at once. For file elements, the maximum size file upload to accept.

pass

If set for a text element, renders the html as a password element, i.e. entry displays as asterisks.

src

If set for a submit element, convert to an image element and use the value of src as the source URL for the image.

maxlength

Used verbatim as the maxlength html attribute in text elements.

minlength

If length_e is set, this is the minimum length text element entry accepted by the validator.

length_e

If set, validate the text element to assure it has at least minlength characters. The value of length_e is the error string to be used in the event of failed validation.

valid_e

If set, perform validation on a text, radio, or select element. For a text element, validation assures a match with valid_ regex. radio element validation assures that one of the options in the group has been chosen. select validation only works for select elements with multiple unset and size equal to 1; the validator will not accept the first option of accept menu, assuming that it is some sort of prompt (e.g. "Please select an item"). In all cases, the value of valid_e is the error string used for failed validations.

valid_regex

Regular expression used to validate entry into a test field if valid_e is set. Note that if you must use ^...$ if you want the regex to match the whole entry.

icase

If set, regex matching is case insensitive.

checked

Only used for a checkbox element that does not have multiple set. If checked is set, the element will display as checked.

rows

Used verbatim as the rows= element in a textarea element.

cols

Used verbatim as the cols= element in a textarea element.

wrap

Used verbatim as the wrap= element in a textarea element.

options

Array of options to be displayed in a select element. If the elements of the array are simple values (strings or numbers), they are simply displayed verbatim and used as the value for that particular option. The elements may themselves be associate arrays with keys "label" and "value". In that case, the value of "label" is displayed and the value of "value" is used on submission.

Examples:


$f->add_element(array("type"=>"text",
                             "name"=>"foo",
                             "valid_regex"=>"^[a-z]*$",
                             "valid_e"=>"Letters only",
                             "icase"=>1,
                             "value"=>"bar"));
$f->add_element(array("type"=>"checkbox",
                             "name"=>"compress",
                             "multiple"=>1));
$f->add_element(array("type"=>"textarea",
                             "name"=>"comment",
                             "rows"=>6,
                             "cols"=>40,
                             "value"=>""));
$o = array(array("label"=>"Please Select","value"=>0),
           array("label"=>"Apple","value"=>1),
           array("label"=>"Orange","value"=>2),
           array("label"=>"Pear","value"=>3),
           array("label"=>"Grape","value"=>4));
$f->add_element(array("type"=>"select",
                             "name"=>"menu",
                             "options"=>$o,
                             "size"=>1,
                             "valid_e"=>"Please select a fruit",
                             "value"=>"apple"));

show_element($name,$value)

Outputs the form element named $name. Usually, the second argument is not used. It is always necessary for radio elements and checkbox elements with the multiple attribute set, since many of these may have the same name. It also must be used for submit elements to label the submission button; the value attribute is not used for submit elements. For other elements that may be array valued (notably text elements) multiple calls to show_element will show successive values.

load_defaults($element_list)

Sets the default value of form elements to the value of the PHP variables with the same name. This is generally used to redisplay a form with the same values which were submitted. The argument is optional; if given it is an array of element names; only these elements ares affected.

validate($result,$element_list)

Validates a form submission. If all of the elements are valid, return $result, otherwise return the relevant error message (the valid_e or length_e attribute of some form element). $result defaults to false. The second argument is also optional; it is an array of names of elements to validate.

freeze($element_list)

Freezes the form elements whose names are given in the array passed as the argument. If no argument is given, freeze all of the elements. Frozen elements are rendered as plain, static html rather than form widgets. This static rendering is accompanied by appropriate hidden elements to simulate the affect of using the unfrozen version of the element.

Customizing OOH Forms

Since OOH Forms is object oriented, it can be easily customized by extending the classes that define the element types. In general, you must make sure your derived class has a constructor and you may override any of the self_* functions of of_element. The source for the existing elements is the best documentation for how to do this properly, but a few general notes follow.

self_show($val,$which)

Display an instance of this element unfrozen. $val is the $value argument of show_element if there was one; $which can be used as an index for array valued elements; it is equal to the number of times show_element has been called for this element previously. This function must return the number of hidden tags output.

self_show_frozen($val,$which)

Display an instance of this element frozen. In addition to the html to show the frozen element, you must output tags for hidden fields to duplicate the effect of submitting an unfrozen element of this type. The function must return the number of hidden tags output;

self_validate($val)

Validate $val for this element. If it is valid, return false, otherwise return the relevant error string.

self_print_js($ndx_array)

Print out Javascript code to validate this element. $ndx_array is an array of the indices of the elements to be validated as used in the Javascript form.element[] array. This is needed since the extra [] in array valued element names confuses Javascript.

self_load_defaults($val)

Set the default value for this element to $val. Usually the default definition of this function, which just copies $val to $this->value is all that is needed, but there may be special cases that need to do something else. See the implementation of the checkbox element for an example.

5.4 tpl_form

The tpl_form class is intended to provide a general framework for HTML form deployment. It heavily depends on OOH Forms library, so it is required that you read and understand the relative documentation.

The main idea is that you create a black box by sub-classing tpl_form, provide some HTML code mixed with OOH Forms calls to actually render the form itself. Your application will then use that black box to obtain some input from the user. Your application doesn't have to know how to handle user input, nor how to validate input data, since internal methods will take care of that.

This approach is very similar (I think) to OOH Forms one, only at a higher level. OOH Forms elements have no way to communicate with each other, and are only able to perform "simple" checks on data integrity, while tpl_form adds a consistent interface for complex data evaluation and processing.

Furthermore, the get_default_values and set_default_values methods can be used to maintain user input between sessions, without worrying about serialization of form variables (a BAD THING(tm)), using an hash array containing field names and values.

You'll note that an array is used to share data with the application. You may object this is kinda futile, since all user input data may be found in global vars and HTTP_POST or HTTP_GET global hashes. This is true, and in the general case you'll pass back and forth an empty array. The values variable is intended for use in very complex data-entry setup, where a form behaviour may depend on previous data entered by the user. In this case, if all forms cooperate reading and writing to values hash array, final result may be constructed step by step across multiple HTML pages.

Instance variables


classname
Name of the class. Used for serialization AND in display to determine the filename of template containing HTML/php code needed to actually render the form.
errorContains error messages generated by validate and validate_input methods.valuesThis is a sort of "shared memory area" between the form and the application. Is read in init method and passed back in get_values method.
form dataHolds form info (Form object).
has defaultsFlag, form default values were passed via set_default_values method. Should not be tampered with by the user.
Internal instance variables.

Instance methods

Accessible instance methods

init($values)

This is a sort of a constructor for the class. $values is an hash array intended to store form values to be passed back to the application via get_values method.

get_default_values()

Returns an array containing all data submitted by the user for the form. This array is intended to be passed to set_defaults_values some time later.

set_default_values($fv)

Restore form defaults from an array as returned by get_default_values.

display()

Actually display form fields. This method should not be overridden in descendants. User should instead provide a file named as the derived class and with ".ihtml" extension which will be automatically included.

get_values()

This method should not be overridden. It is intended as the main interface between the application and the form. Once the form has been properly derived to suit designer's needs, application calls get_values and receives back the array passed to init, eventually modified by process_input method, or false if user input is invalid. In that latter case, the application should call display to (re)present the form to the user, eventually filled with proper default values.

clear()

Sort of a "destructor". There should no real need to call it, except maybe freeing some memory. May be called from the application, otherwise is not executed. Returns true.

Internal instance methods

setup()

Init the Form object, which will contain all fields info. The hidden field form_name, automatically added by this routine, is used by other methods to determine if form has already been submitted by the user. You shouldn't override this in descendants, use setup_fields instead. Returns true.

setup_fields()

Override this method in order to provide form fields definition that suit your needs.

validate()

Validates user input. This method should not be overridden in descendants. See validate_input instead. Returns false on error and sets error variable accordingly.

validate_input()

This method should be overridden in descendants, in order to provided complex validation methods (i.e. field2 should not be empty IF field1 == "other"). Should return false on error and set error variable with a sensible error message.

process()

Process user data. This method should not be overridden by descendants. See process_input and process_default instead. Returns true on success, false otherwise.

process_input()

This method should be overridden in descendants. It is executed after validation has taken place. The data passed to the form could be used to fill values array.

process_default()

This method should be overridden in descendants. It is executed when form validation fails or before presenting the form for the first time. Should be used to bypass form displaying if data can be extracted from previous actions, divination, penguin fly watching or whatever.

Example

Suppose you have a form that the user should fill with her (eheh) name and e-mail. You want to check wether this e-mail is valid, or your blind date setup is lost. A... er... simple regular expression for validating syntactically the e-mail is presented in the example code below.


 $this->form_data->add_element(array(
  "type"=>"text",
  "name"=>"email",
  "valid_e"=>"Syntax error in E-Mail address.",
  "valid_regex"=>"^([-a-zA-Z0-9.]+@[-a-zA-Z0-9]+(\.[-a-zA-Z0-9]+)+)*$"
 ));

Now, this piece of code should do the job, but since you're feeling very paranoid today, you'd also like to validate the host name part of the address with DNS. So, you put together some code which takes an hostname in input and reports true on valid hostname, false otherwise (HINT: on PHP Code Exchange you should find a procedure for "active" email validation).

Now that you have your shining new code, you can check the address. The user fills out the form, you parse user input, no syntax errors, time to call your mycheckhost from the application. If the function is ok update your database, else load defaults into the form, display again, close the page, goodbye.

I've done something similar for MANY forms, some of them with very complex validation procedures, and I found that was too easy producing very bad and unreadable code (well, I actually realized that the first time I had to change some logic in data validation...).

tpl_form should provide a solid framework to build your forms with, and all the code will be self-contained and separated from main application logic. Hope you'll like it.

Time to see some code. First of all, class declaration, sub-classing tpl_form:


class myform extends tpl_form {
  var $classname = "myform";
  
  function setup_fields() {
    $this->form_data->add_element(array(
     "name"=>"email",
     ..., // See previous code snippet
    ));
    $this->form_data->add_element(array(
     "name"=>"submit",
     "type"=>"submit",
     "value"=>"submit"
    ));
  }
  
  function validate_input() {
    global $email;
    list($uname, $hostname) = split("@", $email);
    if (! mycheckhost($hostname)) {
      $this->error = sprintf("Sorry, unknown host %s, try again.", $hostname);
      return false;
    }
    // Additional checks here...
    return true;
  }
}

You shuld provide a file myform.ihtml with HTML and php code to render the form. Minimalistic example:


<html>
<body>
<?php
 $this->form_data->start_form($this->classname, "POST", $sess->self_url(), "");
 printf("%s<br>\n", $this->error);
 $this->form_data->show_element("email");
 printf("<br>\n");
 $this->form_data->show_element("submit");
 $this->form_data->finish();
?>
</body>
</html>

Your tpl_form class is complete, and will only need a little work on the artistic side... 8-) To use your brand new class, include class definition code into your application, then...


$mf = new myform;
$mf->init(array()); // This is not strictly required, AT PRESENT,
                    // but I recommend it
if ($rv = $mf->getdata()) {
  $mf->clear(); // This is not strictly required, anyway it should free
                // some memory...
  global $email;
  // process your data at will
} else {
  $mf->display();
}

Hope this very little example does help in understanding the real power of tpl_form class, at least in terms of rapid designing and code partitioning.

5.5 Tree

The Tree class can render tree structures such as directory hierarchies and menu structures as HTML. The structure must be given to Tree as an nested array of arrays of arbitrary depth.

The idea of Tree is, that there are several mathematical models a tree could be viewed: One model is a data structure like nested arrays or a pointer structure from where you can print multidimensional graphics and can do other neat things like deleting one part of the tree or inserting a whole subtree. But you can also imagine a tree as a one dimensional string or as a sequence of function calls (which is nearly the same in the mathematical sense).

To generate HTML-code from a tree-structure it is like this: You need at the end a one-dimensional string, which tells the browser what to do. The Tree class assists you in generating this string in this way, that it will go through the whole tree and call several functions on every stage trough the way. It will be your task to change the functions, so that a nice layout will be generated.

Instance variables


classname
Serialization helper: The name of this class.
delimitera char for truncating the "path"
treean array of an array of an array
outpthe "output"
prfx, sufx, flaginternal - some helpers to create outp
Accessible instance variables.

Instance methods

Accessible instance methods

build_tree()

This function is completely user driven! You have to create an array with the structure described below. See the example for details.

Don't be shy to create some own functions which are called by build_tree() - e.g. for recursive calls.

go_trough_tree($key="",$path="",$depth=0,$lcount=0,$pcount=0)

This is the most important function of this class. It will call the output functions in the right order with the correct parameters.

All variables are optional. The parameters are perhaps useful, if you want to display only partial trees, but this is not supported now.

path_to_index (&$path,$key="")

This function is mostly used internally, but could be useful for you to generate $this->tree. This function generates a PHP3 associate array-index string from a path, which is also a string but truncated by $this->delimiter. If $key is given, it will be added to $path (minds empty path and so on).

Example:


  $t->delimiter="/";
  $path= "usr/local/lib";
  ## $path must be given as a var, because it is called by reference!
  $bla = $t->path_to_index($path,"etc");

  ## $path is now "usr/local/lib/etc"
  ## $bla is now ["usr"]["local"]["lib"]["etc"]

path_to_parent (&$path)

This function isn't used internally, but could be useful for you during generating the output tree. It will remove one from the depth of the path.

Example:


  $t->delimiter="/";
  $path= "usr/local/lib";
  $bla = $t->path_to_parent($path);

  ## $path is now "usr/local"
  ## $bla is now ["usr"]["local"]

path_add ($path,$key)

This function is the 'non-call-by-reference-version' of path_to_index. It will add the $key to the path and return it.

path_sub ($path)

This function is the 'non-call-by-reference-version' of path_to_parent. It will find the parent of path and return it.

path_index ($path)

This function is the 'non-call-by-reference-version' of path_to_index(). It will return the associate key to the tree described by path.

starttree ()

This function is called by go_trough_tree() at the beginning of the output of a tree.

All *tree-functions are called by go_trough_tree(), but it's your turn, to give them a nice layout. I think it is possible to generate nearly every kind of tree-layout with this. Have a look at the variables: E.g. $depth makes it possible to handle every "level" in another manner.

growtree ($key,$value,$path,$depth,$count,$pcount)

This function is called by go_trough_tree() at the beginning of the output of a tree.

It is called every time, when go_trough_tree() will call itself recursively. You could also say it is called, when the current item has a successor.

leaftree ($key,$value,$path,$depth,$count,$pcount)

This function is called, when the current item has no successor.

shrinktree ($key,$depth)

This function is the "opposite" of growtree(). It is called every time, when the current item was the last item in this sub-list.

endtree()

Called when leaving tree.

The Tree Array

As said above, before you call go_trough_tree(), first $tree must be generated.

$tree consists of nested arrays of arbitrary depth. An example:


$t= new Tree;
$t->tree = array(
                "usr" => array(
                  0       => "allowed",
                  "lib"   => "forbidden",
                  "local" => "allowed",
                  "bin"   => "forbidden",
                  "etc"   => array(
                     0       => "allowed",
                    "hosts"  => "forbidden",
                    "mailcap"=> "allowed"
                  ),
                  "var"   => "allowed",
                  "tmp"   => "allowed"
                ),
                "root" =>"forbidden"
              );
$t->go_through_tree();
print $t->outp;

This is a completely recursive structure and I think, it is clear, how to create it with a recursive call of a function. If not, see the example below.

One little quirk has to be explained, because it is a little bit confusing: the array name 0 (zero) is used for the value of the parent element. As shown in the example, an element with children (for example "etc") cannot have attributes (such as "allowed"). Instead the value of this element is stored in a pseudo-child named 0. If this element is not present, it will have the value "Array" (perhaps something that should be changed).

The output of this example if you don't change the output-functions will look like this:


/
^---- usr->'allowed' : 'usr' (1) [1/2]
|    ^---- lib->'forbidden' : 'usr^lib' (2) [2/7]
|    O---- local->'allowed' : 'usr^local' (2) [3/7]
|    O---- bin->'forbidden' : 'usr^bin' (2) [4/7]
|    O---- etc->'allowed' : 'usr^etc' (2) [5/7]
|    |    ^---- hosts->'forbidden' : 'usr^etc^hosts' (3) [2/3]
|    |     \--- mailcap->'allowed' : 'usr^etc^mailcap' (3) [3/3]
|    O---- var->'allowed' : 'usr^var' (2) [6/7]
|     \--- tmp->'allowed' : 'usr^tmp' (2) [7/7]
 \--- root->'forbidden' : 'root' (1) [2/2]

Looks a bit confusing. From left to right the fields are

Example

My example is just going trough the directory structure of your hard disk.

The following code could read it:


class dir_Tree extends Tree {
     var $classname = "dir_Tree";
     var $delimiter="/";

     var $tdat;

     function build_tree ($path=".") {
         $this->tree=$this->recurs_dir($path,0);
     }

     ## This example code can read and output 1000 directory entries with
     ## many subdirs in about 20 seconds on my system (P200, 64 MB);
     ## 220 dir entries with a maximum depth of 4 are read in 2 seconds.
     ## This is ok. :)

     function recurs_dir ($path,$depth) {
     GLOBAL $flap_out;
         $d=opendir($path);

         while ( $name=readdir($d) ) {
             $pathname=$path . $this->delimiter . $name;
             if (is_dir($pathname) && !ereg("\.\.?",$pathname)) {
                 if (isset($flap_out[$pathname])) {
                     $array[$name]=$this->recurs_dir($pathname,$depth+1);
                 }
                 # ATTENTION: It is IMPORTANT fill the [0] array 
                 # *after* filling the rest of the array!
                 $array[$name][0]=$pathname;
             } else {
                 $array[$name]=$pathname;
             }
         }
         closedir($d);
         return($array);

     }

     #################################################
     ## FLAPPING IN and OUT
     ## This is used to create an array which includes
     ## all sub-paths which should be showed
     ## 

     function flapping ($path) {
     GLOBAL $flap_out;
         if ($path) {
             if (is_dir($path)) {
                 if (isset($flap_out[$path])) {
                     unset($flap_out[$path]);
                 } else {
                     $flap_out[$path]=$name;
                 }
             }
         }
     }
}

$t= new dir_Tree;
$t->flapping($val); ## $val is given by GET-method, see *tree()-functions 
$t->build_tree();
$t->go_through_tree();
print $t->outp;

With this code it is very easy to flap in and out whole parts of the tree. Send the path via GET-method and put this path in flapping(). The whole $flap_out-array must be persistent (e.g. via session). Perhaps you can program a garbage collection, which will look into $flap_out and check for paths that already exist?

Known Bugs / Tips

There is one known bug: If a name of a subpath contains the $delimiter-string. This cannot be solved correctly and you have to look for it when you create the tree.

The same thing is with the value [0] (zero) of a sub-array. This element is always used as the attribute of the parent element.

A good tip: when you build your tree recursively then the [0]-index must be filled after the subtree is returned from recursive call. See in the example above what I mean. I think it's a PHP3 specialty.

Also it is possible that not every name could be inserted into the associate index-field (Control-chars etc.), but this is untested.

5.6 STRINGS2 function set

This is a set of functions, which are used very often by me.

They are so easy, that I now stop describing and simply insert the code. Perhaps the next revision of this set I will replace it with a better description:


<?php
##
## Strings2-Functions
##
## Copyright (c) 1998,1999 Alex 'SSilk' Aulbach
##
## These Functions are very practical and if I could program
## C a little bit better it will be placed directly in PHP3.
## But I can't... :-}
##


##
## Have you ever worried about such constructs like
##    echo ($faxnumber) ? sprintf("Fax: %s",$faxnumber) : "";
##
## This functionset could help you to replace those constructs by
##    p_iftrue($faxnumber,"Fax: %s");
## which is nearly the half of typing and looks more clear and solves
## an error if $faxnumber is unset.
##
function o_iftrue ($val,$str) {
        if (isset($val) && $val) {
                return(sprintf($str,$val));
        }
}
function p_iftrue ($val,$str) {
        print o_iftrue($val,$str);
}

##
## Output "One or More"
##
## This function is good if you want to differ a output by number:
##  e.g.  o_1or2($q->num_rows(),
##               "Found only one matching record",
##               "Found %s records");
## Will output if num_rows() is 1:  "Found only one matching record"
##                            200:  "Found 200 records"
##
## if $val is empty() or "" a blank string will be returned!
##
function o_1or2 ($val,$str1,$str2) {
        if (isset($val) && $val) {
                if (1==$val) {
                        return(sprintf($str1,$val));
                } else {
                        return(sprintf($str2,$val));
                }
        } else {
                return(false);
        }
}
function p_1or2 ($val,$str1,$str2) {
        print o_1or2 ($val,$str1,$str2);
}


##
## This is for the case, that you want to output something
## if $val is false e.g.
##
## p_0or1($faxnumber,"THERE IS NO FAXNUMBER","Faxnumber: %s");
## 
function o_0or1 ($val,$str1,$str2) {
        if (empty($val) || !$val) {
                if (isset($val)) {
                        return(sprintf($str1,$val));
                } else {
                        return($str1);
                }
        } else {
                return(sprintf($str2,$val));
        }
}
function p_0or1 ($val,$str1,$str2) {
        print o_0or1 ($val,$str1,$str2);
}

##
## Replaces all blank-chars with  
## This function is used, when you are not willing to let the browser
## break your lines an can be used instead of <NOBR>-Tag
## as very compatible replacement
##
##   can also be replaced by a true whitespace which has in
## ISO-latin-1 the code 160
##
function o_nonbsp ($val) {
        return(ereg_replace("[[:blank:]\n\r]"," ",$val));
}
function p_nonbsp ($val) {
        print o_nonbsp($val);
}
?>


Next Previous Contents

Banner.Novgorod.Ru