SolutionsTools & SDKSupportForums Register



Quick Links
 
WURFL and PHP
 
 
by Andrea Trasatti

This month, we are pleased to host an article by Andrea Trasatti, the creator of the PHP middleware used to run WURFL-based applications. To learn more about Andrea, read this.

PHP is one of the most popular scripting languages for web application development. After so much talk about Java, I just had to roll up my sleeves and show that PHP is also up to the task.

To be able to use WURFL in your application, you will need to download the latest PHP library. It is accessible directly from the WURFL website: there is a direct link to the PHP section (labeled PHP Tools) where you will also find some more examples.

The PHP library is composed of groups of files, each with a well-defined functionality.
  • Parsing XML: this enables accessing WURFL from a PHP script
  • In-Memory Data Access: a script/class that makes it easy to access data stored in the associative array
  • Configuration
  • Cache Management
  • Samples
After downloading and extracting the files to a directory of your choice within your server document root, you should also get a copy of the latest wurfl.xml file (also available in zipped format for your convenience) and place it in the same directory where you extracted the PHP files.

At this point, you will need to configure your installation. Open the file named "wurfl_config.php". While there are many possible configurations here, we will choose a basic one that gets you started quickly. As you figure out your needs, you may refer to the info in the configuration file itself to adapt the installation.

First of all, define the "DATADIR" directory. PHP scripts will store all the local and temporary files in it as well as assuming that's the directory where wurfl.xml can be found.
define("DATADIR", './');

Of course, insert the name of the directory you chose for extracting the files in your actual configuration. This can be an absolute or relative path. Just make sure there's a closing slash. If you are on a UNIX-like system, you should allow the web server to write in this directory. (chmod -R 755 nobody, that's assuming your server runs as "nobody". If that's not the case and you are not sure what the user is, ask your system administrator).

For the sake of starting quickly, also make sure to apply these values to your configuration:
define ("WURFL_USE_CACHE", false);
define ("WURFL_AUTOLOAD", true);

This will disable internal caching (we will talk more about this later). Logging is enabled at the highest level by default. You will want to change this for production servers. "LOG_LEVEL" defines the detail you want in your logs. Use the default PHP constants for logging.

Finally, we can start with a first check to make sure everything is okay. Open your web browser and invoke the script "check_wurfl.php". If you have it running on your local machine, the URL should be something like:
http://localhost/wurfl/check_wurfl.php 

 

    1  set_time_limit(600);
    2    :
    3  require_once('./wurfl_config.php');
    4  require_once(WURFL_CLASS_FILE);
    5    :
    6    $wurflObj = new wurfl_class();
    8    :
    6  if ( isset($_GET['force_ua']) && strlen($_GET['force_ua'])>0 ) {
    9  	$wurflObj->GetDeviceCapabilitiesFromAgent($_GET['force_ua']);
    10 } else {
    11 	//Forcing a test agent
    12	$wurflObj->GetDeviceCapabilitiesFromAgent("MOT-c350");
    13 }
    14   : 
    15 echo "<pre>";
    16 var_export($wurflObj->capabilities);
    17 echo "</pre>";
    18  :


Listing 1: Snippets from check_wurfl.php

This script will initialize the WURFL and return the list of capabilities and values of the Motorola c350 device by querying the "MOT-c350" user-agent string (that's just a randomly-chosen default; you can override the user-agent through the 'force_ua' query string). This little script is useful when you want to check a device or, as in our case, make sure that everything is configured properly. If something went wrong, PHP errors on screen or in your logs will give you valuable hints about where to look.

Caching
 
Invoking the check_wurfl.php script takes a while, between 10 and 20 seconds depending on your system. Most of the time goes to parse the wurfl.xml file using Expat. Storing information in an associative array is more convenient for PHP and this is why a cache is useful.

The PHP library includes two caching systems and you may choose the one you like best. The first one stores the entire associative array in a single file called cache.php. Because of the WURFL size, the file cache.php also gets large (about 1.5Mb). Storing it is quick, but reading and interpreting it takes a long time. This is why I strongly suggest that you adopt a caching system at PHP-level. The Zend Accelerator is one example, but alternatives are available. This will be slow at the first hit, but really fast from the second and on.

Thanks to the community, we recently introduced a new caching system which offers even better performance. We called it "multicache", since we generate many small cache files instead of a big one. Each single device in WURFL has its own cache file. This means that it will be really fast to read. This approach will generate many files in your system. This may not be desirable, depending on how well your I/O is performing. You can read more about the cache and multicache and how to configure them on the website and in the included documentation (readme.txt).
 
PHP WURFL Functions
 
Let's get back to how you can use these libraries to make your wireless site better. check_wurfl.php (Listing 1) is handy to verify the capabilities of a device, but is also a good script to start using the PHP libraries. Unless you want to write a new engine to parse the XML, you need not know anything about wurfl_parser.php (except its location when you are configuring the scripts). What is really useful to you is the wurfl_class.php, instead.

With reference to Listing 1,
  • Lines 3 and 4 load everything that the libraries need to operate. Just include those two lines and wurfl_config.php will prepare everything for you.
  • Line 6 instantiates the wurfl object on which you can invoke all the methods.
  • Line 7 through 12: Now that we have the object we need to search for a user agent. check_wurfl.php is not a wireless application. We need to give it a user-agent to chew on another way. The script checks if a form posted a user agent string. If not, it will resort to a randomly chosen user agent (the Motorola c350). Typically, in your wireless application you get the user-agent string from the HTTP header:
    $wurflObj->GetDeviceCapabilitiesFromAgent($_SERVER['HTTP_USER_AGENT']);

    The wurfl object searches the WURFL for the user agent requested and fills the internal properties with the appropriate data. This operation is needed every time you want to search for information of a new device, in general you will do this once at every run.
  • Line 16 prints all the capabilities (and respective values) for the device.
Now that we have got the basics about initializing the WURFL object and using the User Agent string as a key, we need more. In real apps, we want to find out whether a device supports, for example, GIF or JPEG images (or any other formats for that matter). This is easy:
$is_gif_supported = $wurflObj->getDeviceCapability('gif');

Most capabilities in WURFL are Booleans. Checking what a device can do is simple. You just need to know the right capability. When it comes to non-Boolean capabilities the API is the same. This example will return the number of colors:
$max_colors = $wurflObj->getDeviceCapability('colors');


In addition to image formats and colors, screen dimensions are equally important when you want to render images on actual devices. The screen_width and screen_height capabilities will give you that information.

Another common requirement in today's wireless applications is to figure out the mark-up supported by each device. Devices on the market support WML, xHTML, iHTML and HTML (smartphones and PDAs). If you have to support multiple mark-ups, you may want to check out the "preferred_markup" capability or just check if a given mark-up is supported. The preferred_markup stores the name of the mark-up that the device renders better, according to the opinion of the community supporting WURFL.

In the following example, we use all the functions we have just described. This is basically all you need to know in order to use WURFL in your application.



  <?php
  require_once('./wurfl_config.php');
  require_once('./wurfl_class.php');
  // creating the WURFL object
  $myDevice = new wurfl_class();
  $myDevice->GetDeviceCapabilitiesFromAgent($_SERVER["HTTP_USER_AGENT"]);
  
  header("Content-Type: text/vnd.wap.wml");
  echo '<?xml version="1.0" encoding="ISO-8859-1"?>'."\n";
  ?>
  <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
            "http://www.wapforum.org//DTD//wml_1.1.xml">
  <wml>
   <card>
    <p mode="nowrap">
  
  <?php
    if ( $myDevice->getDeviceCapability('gif') ) {
    echo '<img src="logo.gif" alt="Global TEL" />'."\n";
    } else {
    echo '<img src="logo.wbmp" alt="Global TEL" />'."\n<br/>\n";
    }
  ?>
  
  <a href="index.php">Home</a><br/>
   </p>
   </card>
  </wml>


Listing 2: Simple But Self-Contained Use Of The PHP WURFL Library

What if you wanted to manage a site and deliver more than one markup? We are not going into the detail of how to build different templates or writing an engine to generate different markups from a single source, but we can give you an introduction on how to get the markup and prepare the headers for this, including detecting and redirecting web browsers.


    <?php
    require_once('./wurfl_config.php');
    require_once('./wurfl_class.php');
    // creating the WURFL object
    $myDevice = new wurfl_class($wurfl, $wurfl_agents);
    $myDevice->GetDeviceCapabilitiesFromAgent($_SERVER["HTTP_USER_AGENT"]);
    
    if ( $myDevice->GetDeviceCapability('is_wireless_device') ) {
      switch ( $myDevice->GetDeviceCapability('preferred_markup') ) {
        case 'wml_1_1':
        case 'wml_1_2':
        case 'wml_1_3':
          $ctype='text/vnd.wap.wml';
          $doctype = '<?xml version="1.0" encoding="ISO-8859-15"?>';
          $doctype .= "\n";
          $doctype .= '<!DOCTYPE wml 
                PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
                "http://www.wapforum.org/DTD/wml_1.1.xml">';
          break;
        case 'xhtml_basic':
        case 'html_wi_w3_xhtmlbasic':
          $ctype='application/xhtml+xml';
          $doctype = '<?xml version="1.0"?>';
          $doctype .= "\n";
          $doctype .= '<!DOCTYPE html 
                PUBLIC "-//W3C//DTD XHTML Basic 1.0//EN"
                "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">';
          break;
        case 'xhtml_mobileprofile':
        case 'html_wi_oma_xhtmlmp_1_0':
        case 'xhtml_mp_1.0':
          $ctype='application/vnd.wap.xhtml+xml';
          $doctype = '<?xml version="1.0" encoding="UTF-8"?>';
          $doctype .= "\n";
          $doctype .= '<!DOCTYPE html 
                PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN"
                "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">';
          break;
        case 'html_wi_imode_html_1':
        case 'html_wi_imode_html_2':
        case 'html_wi_imode_html_3':
        case 'html_wi_imode_html_4':
        case 'html_wi_imode_html_5':
        case 'html_wi_imode_compact_generic':
          $ctype='text/html';
          $doctype = '';
          break;
        case 'html_web_3_2':
          $ctype='text/html';
          $doctype='<!DOCTYPE HTML 
                PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">';
          $doctype .= "\n";
          break;
        case 'html_web_4_0':
          $ctype='text/html';
          $doctype='<!DOCTYPE HTML 
                PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                "http://www.w3.org/TR/html4/loose.dtd">';
          $doctype .= "\n";
          break;
      }
      header("Content-Type: $ctype");
      echo $doctype."\n"
    } else {
      // Redirecting web browsers to another URL
      header("Location: http://www.mysite.com/");
    }
    ?>


Listing 3: Redirect to a Web Site or Generate a Wireless Page in the Most Appropriate Mark-Up



The line:
if ( $myDevice->GetDeviceCapability('is_wireless_device') ) {

decides if the request comes from a wireless device or not (in which case it is redirected to a website). When a request comes from a wireless device, 'preferred_markup' is used to figure out the appropriate MIME types and headers for the device.

In addition to 'is_wireless_device', there is another special capability that helps you decide whether you want to direct an incoming request to a web site or to a site made for wireless devices (which uses HTML tailored for small screens, such as Microsoft smartphones or phones running the Opera browser). The capability is "device_claims_web_support". The reason for this is that developers disagree on what should be considered a web client and what should be considered a wireless client.

The two capabilities can be combined to achieve whatever need you have.
  • "is_wireless_device" makes a distinction between a browser installed on a computer (desktop, notebook, etc) and a browser installed on a portable device (WAP phone, i-mode, smartphone, PDA, etc).
  • "device_claims_web_support" tells you whether the browser "claims web support", i.e. whether the browser is supposed to be able to render arbitrary web pages on a small screen. Opera or Microsoft Explorer for smartphones are typical examples of this.
The combination of these two capabilities should give you all the freedom to decide what to do in your specific application.

Conclusions
 
This article introduced the PHP WURFL APIs and should be enough to get you started. You can find more information on the WURFL website. The wmlprogramming mailing list on Yahoo Groups is the forum where the WURFL community discusses issues about WURFL and the accompanying middleware.


 
References:
 
WURFL Repository
http://wurfl.sourceforge.net/wurfl.xml (About 2Megs)
http://wurfl.sourceforge.net/wurfl.zip (zipped, about 200k)

WURFL PHP:
http://wurfl.sourceforge.net/php/

Zend Accelerator for PHP:
http://www.zend.com/

 
 
Copyright © 2000-2008 Openwave Systems Inc.    About Us  |  Openwave  |  Terms & Conditions  |  Privacy Policy  |  Update Profile