An Introduction to Tera-WURFL

Posted in Mobile, PHP and Programming on Tuesday, the 11th of March, 2008.

Tagged: and

I recently added a post about Wurfl, a comprehensive open-source XML database of mobile device capabilities. I noted that actually querying Wurfl in a performant manner:

is going to be a non-trivial task, and is perhaps a topic for a further article.

Well, I guess this is that article. It's time to have a look at Tera-WURFL, which is perhaps the most popular tool for querying Wurfl programmatically - from PHP, at least.

Tera-WURFL

Tera-WURFL is a PHP library written by Steve Kamerman, and made freely available to the public. The developers claim querying Tera-WURFL to be five to ten times faster than querying Wurfl directly with PHP, but in practice the performance benefits tend to be much higher, not to mention the greatly improved convenience of having a PHP library already written for you.

The key features of Tera-WURFL can be summarised as follows:

Installation and Configuration

Installing Tera-WURFL is pretty painless. It comes with full installation instructions so I won't go into too much detail here. Suffice to say that you'll need to download the latest version (currently 1.5.2) from the site, and either unzip it into a directory which is accessible via the web, or unzip it elsewhere, and create a symlink to it from a web directory. This is so that you can later browse to Tera-WURFL's admin interface in order to import or update WURFL.

You will also need to create an empty MySQL database for Tera-WURFL, and make sure that you have a MySQL user account which has full permissions on that database. Place the details of the database and the user account into the relevant slots in tera_wurfl_config.php, which lives in the root of the unzipped folders, and you're ready to go.

Querying Tera-WURFL

Once everything is installed and configured, accessing Tera-WURFL from within a PHP application is trivially easy:

<?php
 
require_once '/path/to/tera_wurfl/tera_wurfl.php';
 
$device = new Tera_Wurfl(); 
$device->getDeviceCapabilitiesFromAgent(
                    $_SERVER['HTTP_USER_AGENT']);

That's really all there is to it, from a user's perspective. $device is now a large object with comprehensive information regarding the device and its capabilities.

Let's try a concrete example, that of Nokia's popular N95 handset, which identifies itself with the HTTP User-Agent string:

Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 NokiaN95/11.0.026; 
        Profile MIDP-2.0 Configuration/CLDC-1.1) 
        AppleWebKit/413 (KHTML, like Gecko) Safari/413

Passing that string into the getDeviceCapabilitiesFromAgent() method, and calling print_r() on the resulting object provides us with output similar to the following:

array (
  'id' => 'nokia_n95_ver1_sub_mozilla_b',
  'user_agent' => 'Mozilla/5.0 (SymbianOS/9.2; U;
    Series60/3.1 NokiaN95/11.0.026; Profile/MIDP-2.0
    Configuration/CLDC-1.1 )
    AppleWebKit/413 (KHTML, like Gecko) Safari/413',
  'fall_back' => 'nokia_n95_ver1',
  'product_info' => 
  array (
    'brand_name' => 'Nokia',
    'model_name' => 'N95',
    'unique' => true,
    'ununiqueness_handler' => '',
    'is_wireless_device' => true,
    'device_claims_web_support' => true,
    'has_pointing_device' => false,
    'has_qwerty_keyboard' => false,
    'can_skip_aligned_link_row' => true,
    'uaprof' =>
        'http://nds1.nds.nokia.com/uaprof/NN95-1r100.xml',
    'uaprof2' => '',
    'uaprof3' => '',
    'nokia_series' => 60,
    'nokia_edition' => 3,
    'device_os' => 'Symbian OS',
    'mobile_browser' => 'Nokia',
    'mobile_browser_version' => '',
  ),

  // snip
  
  'xhtml_ui' => 
  array (
    'xhtml_honors_bgcolor' => true,
    'xhtml_supports_forms_in_table' => false,
    'xhtml_support_wml2_namespace' => false,
    'xhtml_autoexpand_select' => false,
    'xhtml_select_as_dropdown' => false,
    'xhtml_select_as_radiobutton' => false,
    'xhtml_select_as_popup' => false,
    'xhtml_display_accesskey' => false,
    'xhtml_supports_invisible_text' => false,
    'xhtml_supports_inline_input' => false,
    'xhtml_supports_monospace_font' => false,
    'xhtml_supports_table_for_layout' => true,
    'xhtml_supports_css_cell_table_coloring' => false,
    'xhtml_format_as_css_property' => true,
    'xhtml_format_as_attribute' => false,
    'xhtml_nowrap_mode' => false,
    'xhtml_marquee_as_css_property' => false,
    'xhtml_readable_background_color1' => '#FFFFFF',
    'xhtml_readable_background_color2' => '#FFFFFF',
    'xhtml_allows_disabled_form_elements' => false,
    'xhtml_document_title_support' => true,
    'xhtml_preferred_charset' => 'utf8',
    'opwv_xhtml_extensions_support' => false,
    'xhtml_make_phone_call_string' => 'wtai://wp/mc;',
    'xhtmlmp_preferred_mime_type' => 'application/xhtml+xml',
    'xhtml_table_support' => true,
    'xhtml_send_sms_string' => 'none',
    'xhtml_send_mms_string' => 'none',
    'xhtml_supports_file_upload' => true,
    'xhtml_file_upload' => 'supported',
  ),
  'ajax' => 
  array (
    'ajax_support_javascript' => true,
    'ajax_manipulate_css' => true,
    'ajax_support_getelementbyid' => true,
    'ajax_support_inner_html' => true,
    'ajax_xhr_type' => 'standard',
    'ajax_support_full_dom' => true,
  ),

  // snip
  
  'display' => 
  array (
    'resolution_width' => 240,
    'resolution_height' => 320,
    'columns' => 15,
    'max_image_width' => 229,
    'max_image_height' => 300,
    'rows' => 6,
  ),
  'image_format' => 
  array (
    'wbmp' => true,
    'bmp' => true,
    'epoc_bmp' => true,
    'gif_animated' => true,
    'jpg' => true,
    'png' => true,
    'tiff' => true,
    'transparent_png_alpha' => false,
    'transparent_png_index' => false,
    'svgt_1_1' => false,
    'svgt_1_1_plus' => false,
    'greyscale' => false,
    'gif' => true,
    'colors' => 262144,
  ),

  // snip
 
  'sound_format' => 
  array (
    'wav' => true,
    'mmf' => false,
    'smf' => false,
    'mld' => false,
    'midi_monophonic' => true,
    'midi_polyphonic' => true,
    'sp_midi' => true,
    'rmf' => true,
    'xmf' => true,
    'compactmidi' => false,
    'digiplug' => false,
    'nokia_ringtone' => true,
    'imelody' => false,
    'au' => true,
    'amr' => true,
    'awb' => true,
    'aac' => true,
    'mp3' => true,
    'voices' => 64,
    'qcelp' => false,
    'evrc' => false,
  ),
  
  // snip

 )

We can immediately see a lot of useful information there, such as the exact make and model of the handset, the screen dimensions, and the various sound and image formats which the device supports. Note that I've snipped the output considerably there, as the real object contains a great deal of data. Some notable items I've left out include:

To see the object in its entirety, feel free to query the database using the form I have hosted here. This is a very slightly modified version of a tool which ships with Tera-WURFL, and should give you a feel for the level of detail you can expect.

Performance

I mentioned earlier that actually querying Tera-WURFL is pretty quick and efficient. To see why, we'll need to look at what happens behind the scenes.

Here's some ad hoc performance stats for the Nokia N95 we looked at just now:

Time to load tera_wurfl_class.php:0.004951000213623
Time to initialize class:0.00052809715270996
Time to find the user agent:0.5135498046875
Total:0.51902890205383

Total Queries: 95

I know what you're thinking: half a second is a little sluggish. No wonder, when we're making ninety-five queries! But let's hit 'refresh' and try once more. The output for a subsequent query is as follows:

Time to load tera_wurfl_class.php:0.0053188800811768
Time to initialize class:0.00048208236694336
Time to find the user agent:0.00093793869018555
Total:0.0067389011383057

Total Queries: 1 (Found in cache)

That's more like it. As the initial generation of the Tera_Wurfl object is so query-intensive, Tera-WURFL (since version 1.5) caches it in a dedicated table as a serialised string. That means that subsequent requests for the same user-agent are reduced to one single-table query against a primary key, which is about the swiftest thing you can do with a database[1]. Combine that with MySQL's built-in query caching and we're really flying.

Applications

Of course, how you actually put Tera-WURFL to work for you is up to you. You may choose to use it to automatically tailor wallpapers and other graphics to screen sizes, to determine whether or not a user can support your J2ME app, or adapt markup to specific mobile browsers. In fact, a future post may look at Wall4PHP, a tag library which, handily enough, comes bundled with Tera-WURFL, and can be used to automatically adapt mobile web pages to the browser on which they are being viewed.

Still, for the time being, I hope this has given a reasonable introduction to what Tera-WURFL can offer the mobile developer.

[1] It will be even faster if we change the table storage engine to InnoDB, as opposed to the default MyISAM. This is because InnoDB's use of clustered indexes makes lookups against primary keys extremely efficient.

Comments

Posted by George on Monday, the 27th of April, 2009.

Hi great article

could you please let me know how you got the performance statistics from the script:

Time to load tera_wurfl_class.php:0.0053188800811768
Time to initialize class:0.00048208236694336
Time to find the user agent:0.00093793869018555
Total:0.0067389011383057

Total Queries: 1 (Found in cache)

I am running my own installation and I am trying to make some tuning

Posted by Simon Harris on Wednesday, the 29th of April, 2009.

Hi George -

Those numbers are all generated by the Web interface that ships with Tera-WURFL. As mentioned in the post, I have a copy hosted here.

Posted by Steve Kamerman on Tuesday, the 27th of October, 2009.

Hey guys, you should check out the new Tera-WURFL 2.0 - I released RC4 last night and it's very good! It includes the same logic as the new Java WURFL API (maybe the same as the PHP API as well) but it is almost completely backwards compatible with the previous versions of Tera-WURFL. Check it out on www.Tera-WURFL.com

Enter your comment: