September 1, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Learning WML -Scripting Tips And Integration With PHP

  • August 30, 2002
  • By Steve Schafer
  • Send Email »
  • More Articles »

The Cards

The following sections list the PHP code used for each function/card. Note that the WML and header code is taken care of by global statements-each card need only output the <card> tags and everything in-between. The $cmd variable controls what function is executed, and hence, which card is displayed.

Note: I've used "echo" commands in this script, but "print" commands would do just as well. Also, to increase the readability of the output I've added line feeds (via a variable, $lf, set to ASCII character 10) to most lines of output. I've chosen to append this variable to the "echo" statements for readability purposes--using "\n" in your "echo" commands would do just as well but tends to clutter the information between the quotes. Also note the use of the entity "&amp;" in the URLs instead of a straight ampersand ("&"). This is necessary to keep WML from assuming that the ampersand is the beginning of an entity name.

Menu

The menu function displays a simple select list, allowing the user to choose what function he or she wants to access:

  echo "<card id=\"Menu\">\n";
  echo "<p mode=\"nowrap\">".$lf;
  
  // Set up Select menu list 
  echo "<select name=\"Select\" title=\"Select:\">".$lf;

  // Go through results from Query, listing each as a CHOICE entry
  echo "<option onpick=\"?cmd=List\">";
  echo "List Contacts</option>".$lf;
  echo "<option onpick=\"?cmd=Search\">";
  echo "Search Contacts</option>".$lf;
  
  // Close select
  echo "</select>".$lf;

  // Close card
  echo "</p>".$lf."</card>".$lf;

The code is straightforward, defining a simple <select> list. Each <option> in the list calls the current script, passing the appropriate value of $cmd. Note that we need to handle the case when $cmd is empty, which it will be when the script is first called. In the body, near the beginning of the script, we add the following line:

  if (empty($cmd)) { $cmd = "Menu"; }

That ensures that if no command is given (via $cmd), the menu will be displayed.

List

This is the meat of the script, displaying both the raw list as well as search results, five records at a time. (For reference after the listing, each line has been numbered.)

1  // Construct appropriate *count* query
2:   $query = "select count(*) from Phone";
3:   if (!empty($search)) {
4:     $query = $query." where FirstName like \"%".$search."%\" or";
5:     $query = $query." LastName like \"%".$search."%\"";
6:   }

7:   $result = mysql_query($query,$link)
8:     or die("Query failed:$query");

9:   list($total_rows) = mysql_fetch_array($result);

10:   // Construct appropriate query
11:   $query = "select * from Phone";
12:   if (!empty($search)) {
13:     $query = $query." where FirstName like \"%".$search."%\" or";
14:     $query = $query." LastName like \"%".$search."%\"";
15:   }

16:   // Get first/next five records
17:   $query = $query." order by LastName limit ".$idx.",5";

18:   $result = mysql_query($query,$link)
19:     or die("Query failed:$query");

20:   // Advance DB index
21:   $next = $idx + 5;

22:   // Start card
23:   echo "<card id=\"Contacts\">\n";
24:   echo "<do type=\"accept\" label=\"View\"> <go href=\"\"/> </do>".$lf;

25:   // Display appropriate full/search heading
26:   if (empty($search)) {
27:     echo "<p mode=\"nowrap\"><b>Phone Book</b>".$lf;
28:   } else {
29:     echo "<p mode=\"nowrap\"><b>Search Results</b>".$lf;
30:   }

31:   // Set up Select list (list of five records)
32:   echo "<select name=\"View\" title=\"View:\">".$lf;

33:   // Go through results from Query, listing each as a CHOICE entry
34:   while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {

35:     $recordid = $line[Id]; 
36:     $Name = $line[LastName] . ", " . $line[FirstName];
37:     $Number = $line[Phone];
38:     $Prompt = $Name . " (" . $Number . ")";

39:   // Build URL for option, include DB index and search
40:     $option = "<option onpick=\"";
41:     $option = $option."?cmd=Display&amp;id=".$recordid."&amp";
42:     $option =
   $option.";idx=".$recordid."&amp;search=".$search."\">";
43:     $option = $option.$Prompt."</option>".$lf;
44:     echo $option.$lf;

45:   }

46:   // If there are more records to display, set up paging
47:   //   else mark end of list (to keep Home as same option)
48:   if ($total_rows >= $next) {

49:   // Link to next five
50:     echo "<option title=\"Next\" onpick=\"?cmd=List&amp;idx=$next";

51:   // Pass Search criteria if exists
52:     if (!empty($search)) {
53:       echo "&amp;search=".$search;
54:     }

55:     echo "\">[Next Records]".$lf."</option>".$lf;

56:   } else {

57:     echo "<option title=\"End of List\" onpick=\"?cmd=List&amp;idx=$idx";

58:   // Pass Search criteria if exists
59:     if (!empty($search)) {
60:     echo "&amp;search=".$search;
61:     }

62:   // Close tags
63:     echo "\">[End of List]".$lf."</option>".$lf;

64:   }          

65:   // Add option for Home
66:   echo "<option onpick=\"?cmd=Menu\" title=\"Home\">".$lf;
67:   echo "[Back to Home]".$lf;
68:   echo "</option>".$lf;

69:   // Close select
70:   echo "</select>".$lf;

71:   // Close card
72:   echo "</p>".$lf."</card>".$lf;

This code makes use of global variables ($idx, $search) to display the list of contacts. Both variables are passed as a name/value pair when the script is called with $cmd equal to "List." If $search is empty, the full list of contacts is displayed, else the text of $search is added to the query and records are returned only if FirstName or LastName contains the search text.

The $idx variable marks what results the script currently is listing. The record at location $idx is the first record on the current page.

This becomes more self-explanatory as we work through the code:

Lines 1-9 construct and execute a "count" query, storing the number of returned rows in the variable $total_rows. This value is used later (line 48) to determine whether there are more pages of data to display. Note that the search text is added if $search is not empty (hence contains search criteria).

Lines 10-19 construct a query to return the target dataset. Again, the search criterion is added to the query, if it exists. Line 17 appends a limit clause to the query, causing the query to return only five records (or less), starting at the record indicated by the value of $idx. If $idx is zero (which it will be the first time the script executes List), the first five records are returned. The variable $next is set to a value of $idx + 5 (line 21) and used to call the next iteration of List, causing the next five records to be displayed.

Note: This method is far from perfect. For example, if a record is added or modified that causes a record to be added or removed from the returned dataset, the next page will return different results than it would before the record was added or modified. If the list is being displayed while the records are being modified-which happens in most database applications-the displayed results can be somewhat unpredictable.

Lines 22-30 begin the display card definition, including the appropriate header-"Phone Book" if the search string is empty (raw list being displayed) or "Search Results" if the search string is not empty (search results being displayed). This helps guarantee that the result set returned will be the same, allowing consistent paging through the set (with the caveat explained above).

Line 32 begins the <select> list, with lines 34-70 building and displaying five items as <option>s. Lines 35-38 build the text for the <option> prompt, while lines 39-44 construct the <option> statement with an appropriate "onpick" parameter that recursively calls the script with $cmd equal to "Display" and the ID of the record to display. Note that $idx and $search are also passed to maintain their values through the recursive call, just in case we need them later.

Lines 49-64 build option number 6 in our select list. If there are more records to display ($total_rows >= $next) the script generates the option "[Next Records]" with an appropriate "onpick" parameter to recursively call the script, specifying the next starting record to display (via idx=$next). If there are no more records to display, the script generates an "[End of List]" option, which recursively calls the script with the same starting point as is currently displayed. Each option also includes the search criteria if it exists (lines 51-54 and 58-61).

Finally, a "Home" option is created (lines 65-68) to allow the user to return to the home menu from any page of the listing. The open tags are then closed.

Search

The search function is simply an input tag that accepts up to 10 characters and recursively calls the script supplying the text entered and sets $cmd equal to "List."

  echo "<card id=\"Search\">\n";
  echo "<do type=\"accept\" label=\"Go\">".$lf;
  echo "<go href=\"?cmd=List&amp;search=\$searchtext\">".$lf;
  echo "</go>".$lf;
  echo "</do>".$lf;

  echo "<p>".$lf;
  echo "<b>Phone Book Search</b><br/>Search for:".$lf;
  echo "<input name=\"searchtext\" title=\"Search\" type=\"text\"";
  echo "  format=\"10m\"/>";
  echo "</p>".$lf;
  echo "</card>".$lf;

Display

Display uses the record ID passed in $idx to select a record from the database and display all of its related information (name, address, phone, etc.).

  echo "<card>\n";
  
  // Get specific record
  $query = "select * from Phone where Id = \"".$idx."\"";
  
  $result = mysql_query($query,$link)
    or die("Query failed:$query");
  
  // Get data and display
  while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
    $recordid = $line[Idx];

    echo "<p mode=\"wrap\">".$lf;
    echo "$line[LastName], $line[FirstName]<br />".$lf;
    echo "$line[Address]<br />".$lf;
    echo "$line[City], $line[State]  $line[Zip]<br />".$lf;
    echo "<a href=\"wtai://wp/mc;$line[Phone]\" title=\"Dial\">";
    echo "$line[Phone]</a><br />".$lf;
    echo "<a href=\"?cmd=Menu\" title=\"Menu\">";
    echo "[Home Menu]</a><br /><br />".$lf;

    $Date = date("M j, Y", strtotime($line[LastUpdate]));
    echo "Record Updated:<br />$Date";
    
    // Close record display card
    echo $lf."</p></card>".$lf;
    
  }

Note that the card has a "Dial" option mapped to the Accept key and is displayed with the phone number highlighted. This allows the user to quickly dial the selected number on devices that support URL dialing. Other items of note include a "Home Menu" link and a more verbose format for the last update date. We do not need to provide any functionality to return to the last page of record listings-the user can do so by pressing the Back key on his or her device.

The Entire Script

Now that we've defined the various functions of the script, let's tie it all together with a "switch" statement and some additional initialization statements:

<?php

header("Content-type: text/vnd.wap.wml");  

header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");              
// expires in the past
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");   
// Last modified, right now
header("Cache-Control: no-cache, must-revalidate");              
// Prevent caching, HTTP/1.1
header("Pragma: no-cache");                                
// Prevent caching, HTTP/1.0

echo "<?xml version=\"1.0\"?>\n";  
echo "<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\""  
   . " \"http://www.wapforum.org/DTD/wml_1.1.xml\">\n"; 
echo "<wml>\n";

// Open link to DB
$link = mysql_connect("localhost", "webuser", "webby99")
  or die("Could not connect to database!");
mysql_select_db("customers")
  or die("Could not select database!");

// Line feed
$lf = chr(10);

// Make sure that index into DB has value
if (empty($idx)) { $idx = 0; }

if (empty($cmd)) { $cmd = "Menu"; }

switch ($cmd) {

   case "Menu";

      echo "<card id=\"Menu\">\n";
      echo "<p mode=\"nowrap\">".$lf;
      
      // Set up Select menu list 
      echo "<select name=\"Select\" title=\"Select:\">".$lf;

      // Go through results from Query, listing each as a CHOICE entry
      echo "<option onpick=\"?cmd=List\">";
      echo "List Contacts</option>".$lf;
      echo "<option onpick=\"?cmd=Search\">";
      echo "Search Contacts</option>".$lf;
      
      // Close select
      echo "</select>".$lf;

      // Close card
      echo "</p>".$lf."</card>".$lf;

   break;

   case "List";

      // Construct appropriate *count* query
      $query = "select count(*) from Phone";
      if (!empty($search)) {
         $query = $query." where FirstName like \"%".$search."%\" or";
         $query = $query." LastName like \"%".$search."%\"";
      }

      $result = mysql_query($query,$link)
        or die("Query failed:$query");

      list($total_rows) = mysql_fetch_array($result);

      // Construct appropriate query
      $query = "select * from Phone";
      if (!empty($search)) {
         $query = $query." where FirstName like \"%".$search."%\" or";
         $query = $query." LastName like \"%".$search."%\"";
      }

      // Get first/next five records
      $query = $query." order by LastName limit ".$idx.",5";

      $result = mysql_query($query,$link)
        or die("Query failed:$query");

      // Advance DB index
      $next = $idx + 5;
      
      // Start card
      echo "<card id=\"Contacts\">\n";
      echo "<do type=\"accept\" label=\"View\"> <go href=\"\"/> </do>".$lf;
      
      // Display appropriate full/search heading
      if (empty($search)) {
         echo "<p mode=\"nowrap\"><b>Phone Book</b>".$lf;
      } else {
         echo "<p mode=\"nowrap\"><b>Search Results</b>".$lf;
      }
      
      // Set up Select list (list of five records)
      echo "<select name=\"View\" title=\"View:\">".$lf;

      // Go through results from Query, listing each as a CHOICE entry
      while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {

        $recordid = $line[Id]; 
        $Name = $line[LastName] . ", " . $line[FirstName];
        $Number = $line[Phone];
        $Prompt = $Name . " (" . $Number . ")";

        // Build URL for option, include DB index and search
        $option = "<option onpick=\"";
        $option = $option."?cmd=Display&amp;id=".$recordid."&amp";
        $option = $option.";idx=".$recordid."&amp;search=".$search."\">";
        $option = $option.$Prompt."</option>".$lf;
        echo $option.$lf;

      }

      // If there are more records to display, set up paging
      //   else mark end of list (to keep Home as same option)
      if ($total_rows >= $next) {

         // Link to next five
         echo "<option title=\"Next\" onpick=\"?cmd=List&amp;idx=$next";

         // Pass Search criteria if exists
         if (!empty($search)) {
            echo "&amp;search=".$search;
         }

         echo "\">[Next Records]".$lf."</option>".$lf;
         
      } else {
      
         echo "<option title=\"End of List\" onpick=\"?cmd=List&amp;idx=$idx";
         
         // Pass Search criteria if exists
         if (!empty($search)) {
            echo "&amp;search=".$search;
         }

         // Close tags
         echo "\">[End of List]".$lf."</option>".$lf;
         
      }         

      // Add option for Home
      echo "<option onpick=\"?cmd=Menu\" title=\"Home\">".$lf;
      echo "[Back to Home]".$lf;
      echo "</option>".$lf;

      // Close select
      echo "</select>".$lf;

      // Close card
      echo "</p>".$lf."</card>".$lf;
      
   break;
   
   
   case "Search";
   
      echo "<card id=\"Search\">\n";
      echo "<do type=\"accept\" label=\"Go\">".$lf;
      echo "<go href=\"?cmd=List&amp;search=\$searchtext\">".$lf;
      echo "</go>".$lf;
      echo "</do>".$lf;

      echo "<p>".$lf;
      echo "<b>Phone Book Search</b><br/>Search for:".$lf;
      echo "<input name=\"searchtext\" title=\"Search\" type=\"text\"";
      echo "  format=\"10m\"/>";
      echo "</p>".$lf;
      echo "</card>".$lf;
   
   break;
   
   
   case "Display";
   
      echo "<card>\n";
      
      // Get specific record
      $query = "select * from Phone where Id = \"".$idx."\"";
      
      $result = mysql_query($query,$link)
        or die("Query failed:$query");
   
      // Get data and display
      while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
        $recordid = $line[Idx];

        echo "<p mode=\"wrap\">".$lf;
        echo "$line[LastName], $line[FirstName]<br />".$lf;
        echo "$line[Address]<br />".$lf;
        echo "$line[City], $line[State]  $line[Zip]<br />".$lf;
        echo "<a href=\"wtai://wp/mc;$line[Phone]\" title=\"Dial\">";
        echo "$line[Phone]</a><br />".$lf;
        echo "<a href=\"?cmd=Menu\" title=\"Menu\">";
        echo "[Home Menu]</a><br /><br />".$lf;

        $Date = date("M j, Y", strtotime($line[LastUpdate]));
        echo "Record Updated:<br />$Date";
        
        // Close record display card
        echo $lf."</p></card>".$lf;
        
      }

   
   break;
   
}

echo $lf."</wml>".$lf;

mysql_close($link);

?>

Note that we pass a handful of headers at the beginning of the script to inhibit caching. Since our database is frequently updated and correct/up-to-date information in the field is valuable, we do not want the device to display cached information instead of recently updated information. However, generally speaking, inhibiting the cache is a bad idea and should be done sparingly, if at all.

The Script in Action

Now let's see the script in action. The following figures demonstrate each function:

Note: All images courtesy Openwave Systems Inc. (Openwave, the Openwave logo, Openwave SDK, Openwave SDK Universal Edition, Openwave SDK WAP Edition are trademarks of Openwave Systems Inc. All rights reserved.)

Figure 1 - The menu.

Figure 2 - The list. Notice the [Next Records] option.

Figure 3 - The end of the list. Notice the [End of List] option.

Figure 4 - The search form.

Figure 5 - The results of a search (for "in"). Note the first result is "Biggins, Lisa" but the text has scrolled to the phone number due to the "nowrap."

Figure 6 - A record in the display card.

Room for Improvement

This script has plenty of room for improvement, including the following items:

  • Applying the cache inhibitor headings only to cards that could cause problems (such as Display), instead of globally.
  • Optimizing the output to avoiding duplicating code (such as the addition of search criteria to the URL(s)).
  • Adding more prompts for the user through card titles, etc.
  • Optimizing and standardizing variable naming and usage.
  • Providing means for the user to edit records. Although it can be tedious to enter data on most mobile devices, simple corrections or notes would be welcome. A "last called" field could also be entered automatically each time a contact is called.

This script represents only a small portion of what can be done with PHP and a database such as MySQL. This example could be expanded to offer group calendaring, scheduling, order placement, stock checking, etc. As long as you keep the target audience and the respective design goals in mind, the sky's the limit.

About the Author

Steve Schafer is president and CEO of Progeny Linux Systems, a Linux-based consulting company in Indianapolis, Indiana. He has written several technical books and articles and can be reached at sschafer@synergy-tech.com.

About the "Learning WML" series...

This series of articles describes how to provide Web content to mobile devices through WML (Wireless Markup Language). This article covers advanced integration with PHP.

These articles cover WML and WMLScript version 1.1, which are supported by the majority of mobile devices in use today. The articles assume a working knowledge of HTML and general Web technologies, and further assume that you have read the previous article(s) in this series.

# # #





Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel