Source for file phpSniff.class.php
Documentation is available at phpSniff.class.php
* PHP Client Sniffer (phpsniff)
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* @author Roger Raymond <epsilon7@users.sourceforge.net>
* @version $Id: phpSniff.class.php 20884 2006-12-22 10:28:34Z markwest $
* @copyright Copyright © 2002-2004 Roger Raymond
* @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
* Used to determine the browser and other associated properies
* using nothing other than the HTTP_USER_AGENT value supplied by a
* @author Roger Raymond <epsilon7@users.sourceforge.net>
* desc : directory writable by the server to store cookie check files.
* : trailing slash is needed. only used if you use the check cookie routine
* desc : Allow for the script to redirect the browser in order
* : to check for cookies. In order for this to work, this
* : class must be instantiated before any headers are sent.
* desc : language to report as if no languages are found
* Allow for browser to Masquerade as another. (ie: Opera identifies as MSIE 5.0)
* 2D Array of browsers we wish to search for in key => value pairs.
* key = browser to search for [as in HTTP_USER_AGENT]
* value = value to return as 'browser' property
'microsoft internet explorer' => 'IE',
'mozilla firebird' => 'FB',
* desc : 2D Array of javascript version supported by which browser
* : in key => value pairs.
* : key = javascript version
* : value = search parameter for browsers that support the
* : javascript version listed in the key (comma delimited)
* : note: the search parameters rely on the values
* : set in the $_browsers array
'1.5' => 'NS5+,MZ,PX,FB,FX,GA,CH,CA,SF,KQ3+,KM,EP', // browsers that support JavaScript 1.5
'1.3' => 'NS4.05+,OP5+,IE5+',
* desc : 2D Array of browser features supported by which browser
* : in key => value pairs.
* : value = search parameter for browsers that support the
* : feature listed in the key (comma delimited)
* : note: the search parameters rely on the values
* : set in the $_browsers array
* the following are true by default
* (see phpSniff.core.php $_feature_set array)
* browsers listed here will be set to false
'java' => 'OP3,LI,LX,NS1,MO,IE1,IE2',
'plugins' => 'IE1,IE2,LI,LX',
* the following are false by default
* (see phpSniff.core.php $_feature_set array)
* browsers listed here will be set to true
'css2' => 'NS5+,IE5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ3+,OP7+,KM,EP',
'css1' => 'NS4+,IE4+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP',
'iframes' => 'LI,IE3+,NS5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP',
'xml' => 'IE5+,NS5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP',
'dom' => 'IE5+,NS5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP',
* desc : 2D Array of browser quirks present in which browser
* : in key => value pairs.
* : value = search parameter for browsers that feature the
* : quirk listed in the key (comma delimited)
* : note: the search parameters rely on the values
* : set in the $_browsers array
'must_cache_forms' => 'NS,MZ,FB,PX,FX',
'avoid_popup_windows' => 'IE3,LI,LX',
'cache_ssl_downloads' => 'IE',
'break_disposition_header' => 'IE5.5',
'empty_file_input_value' => 'KQ',
'scrollbar_in_way' => 'IE6'
var $_browser_info = array(
'cookies' => 'Unknown', // remains for backwards compatability
'ss_cookies' => 'Unknown',
'st_cookies' => 'Unknown',
var $_feature_set = array(
'must_cache_forms' => false,
'avoid_popup_windows' => false,
'cache_ssl_downloads' => false,
'break_disposition_header' => false,
'empty_file_input_value' => false,
'scrollbar_in_way' => false
var $_get_languages_ran_once = false;
var $_browser_search_regex = '([a-z]+)([0-9]*)([0-9.]*)(up|dn|\+|\-)?';
var $_language_search_regex = '([a-z-]{2,})';
* Performs some basic initialization and returns and object
* @param string User Agent to parse
* @param mixed array of settings
* [check_cookies, default_language, allow_masqeurading]
* @return object phpSniff object
function phpSniff($UA= '',$settings = true)
{ // populate the HTTP_USER_AGENT string
// routine for easier configuration of the client at runtime
// for backwards compatibility with 2.0.x series
// if the user agent is empty, see if it exists somewhere
if (isset ($HTTP_SERVER_VARS['HTTP_USER_AGENT'])) {
$UA = $HTTP_SERVER_VARS['HTTP_USER_AGENT'];
} elseif (isset ($_SERVER['HTTP_USER_AGENT'])) {
$UA = $_SERVER['HTTP_USER_AGENT'];
// try to use the getenv function as a last resort
$UA = getenv('HTTP_USER_AGENT');
// if it's still empty, just return false as there is nothing to do
if (empty($UA)) return false;
$this->_set_browser('ua',$UA);
// run the cookie check routine first
// [note: method only runs if allowed]
// rip the user agent to pieces
$this->_get_browser_info();
// look for other languages
// establish the operating platform
// determine javascript version
$this->_get_javascript();
// determine current feature set
* turn the cookie check routine on or off
* @param bool true or false
* allow browser masquerading
* @param bool true or false
* set the default browser language
* @param string valid language (ex: en-us)
* @param string property to return . optional (null returns entire array)
* @return mixed array/string entire array or value of property
{ return $this->_browser_info;
* get_property is an alias for property
* @param string property to return . optional (null returns entire array)
* @return mixed array/string entire array or value of property
* @param string search phrase format = l:lang;b:browser
* @return bool true on success
* ex: $client->is('b:OP5Up');
{ // perform language search
if (preg_match('/l:'. $this->_language_search_regex. '/i',$s,$match))
{ if ($match) return $this->_perform_language_search($match);
// perform browser search
elseif (preg_match('/b:'. $this->_browser_search_regex. '/i',$s,$match))
{ if ($match) return $this->_perform_browser_search($match);
* @param string search phrase for browser
* @return bool true on success
* ex: $client->browser_is('OP5Up');
{ preg_match('/'. $this->_browser_search_regex. '/i',$s,$match);
if ($match) return $this->_perform_browser_search($match);
* @param string search phrase for language
* @return bool true on success
* ex: $client->language_is('en-US');
{ preg_match('/'. $this->_language_search_regex. '/i',$s,$match);
if ($match) return $this->_perform_language_search($match);
* checks to see if the browser supports any of the following features:
* <code>ex: $client->has_feature('html');</code>
* @param string feature we're checking on
* @return bool true on success
{ return $this->_feature_set[$s];
* checks to see if the browser has any of the following quirks:
* <li>avoid_popup_windows
* <li>cache_ssl_downloads
* <li>break_disposition_header
* <li>empty_file_input_value
* <code>ex: $client->has_quirk('avoid_popup_windows');</code>
* @param string quirk we're looking for
* @return bool true on success
{ return $this->_quirks[$s];
* _perform_browser_search
* @param string what we're searching for
* @return bool true on success
function _perform_browser_search ($data)
$search['phrase'] = isset ($data[0]) ? $data[0] : '';
$search['name'] = isset ($data[1]) ? strtolower($data[1]) : '';
$search['maj_ver'] = isset ($data[2]) ? $data[2] : '';
$search['min_ver'] = isset ($data[3]) ? $data[3] : '';
$search['direction'] = isset ($data[4]) ? strtolower($data[4]) : '';
$looking_for = $search['maj_ver']. $search['min_ver'];
if ($search['name'] == 'aol' || $search['name'] == 'webtv') {
return stristr($this->_browser_info['ua'],$search['name']);
} elseif ($this->_browser_info['browser'] == $search['name'] || $search['name'] == 'gecko') {
$what_we_are = $this->_browser_info['gecko_ver'];
$majv = $search['maj_ver'] ? $this->_browser_info['maj_ver'] : '';
$minv = $search['min_ver'] ? $this->_browser_info['min_ver'] : '';
$what_we_are = $majv. $minv;
if (($search['direction'] == 'up' || $search['direction'] == '+')
&& ($what_we_are >= $looking_for))
elseif (($search['direction'] == 'dn' || $search['direction'] == '-')
&& ($what_we_are <= $looking_for))
elseif ($what_we_are == $looking_for)
function _perform_language_search ($data)
{ // if we've not grabbed the languages, then do so.
return stristr($this->_browser_info['language'],$data[1]);
function _get_languages ()
{ // capture available languages and insert into container
if (!$this->_get_languages_ran_once)
{ if ($languages = getenv('HTTP_ACCEPT_LANGUAGE'))
{ $languages = preg_replace('/(;q=[0-9]+.[0-9]+)/i','',$languages);
$this->_set_browser('language',$languages);
$this->_get_languages_ran_once = true;
$regex_windows = '/([^dar]win[dows]*)[\s]?([0-9a-z]*)[\w\s]?([a-z0-9.]*)/i';
$regex_mac = '/(68[k0]{1,3})|(ppc mac os x)|([p\S]{1,5}pc)|(darwin)/i';
$regex_os2 = '/os\/2|ibm-webexplorer/i';
$regex_sunos = '/(sun|i86)[os\s]*([0-9]*)/i';
$regex_irix = '/(irix)[\s]*([0-9]*)/i';
$regex_hpux = '/(hp-ux)[\s]*([0-9]*)/i';
$regex_aix = '/aix([0-9]*)/i';
$regex_dec = '/dec|osfl|alphaserver|ultrix|alphastation/i';
$regex_vms = '/vax|openvms/i';
$regex_sco = '/sco|unix_sv/i';
$regex_linux = '/x11|inux/i';
$regex_bsd = '/(free)?(bsd)/i';
$regex_amiga = '/amiga[os]?/i';
{ /** Windows has some of the most ridiculous HTTP_USER_AGENT strings */
//$match[1][count($match[0])-1];
$v = $match[2][count($match[0])- 1];
$v2 = $match[3][count($match[0])- 1];
// Establish NT 5.1 as Windows XP
if (stristr($v,'NT') && $v2 == 5.1) $v = 'xp';
// Establish NT 5.0 and Windows 2000 as win2k
elseif ($v == '2000') $v = '2k';
elseif (stristr($v,'NT') && $v2 == 5.0) $v = '2k';
// Establish 9x 4.90 as Windows 98
elseif (stristr($v,'9x') && $v2 == 4.9) $v = '98';
// See if we're running windows 3.1
elseif ($v. $v2 == '16bit') $v = '31';
// otherwise display as is (31,95,98,NT,ME,XP)
// update browser info container array
if (empty($v)) $v = 'win';
$this->_set_browser('platform','win');
elseif (preg_match($regex_amiga,$this->_browser_info['ua'],$match))
{ $this->_set_browser('platform','amiga');
if (stristr($this->_browser_info['ua'],'morphos')) {
$this->_set_browser('os','morphos');
} elseif (stristr($this->_browser_info['ua'],'mc680x0')) {
$this->_set_browser('os','mc680x0');
} elseif (stristr($this->_browser_info['ua'],'ppc')) {
$this->_set_browser('os','ppc');
} elseif (preg_match('/(AmigaOS [\.1-9]?)/i',$this->_browser_info['ua'],$match)) {
// checking for AmigaOS version string
$this->_set_browser('os',$match[1]);
elseif ( preg_match($regex_os2,$this->_browser_info['ua']))
{ $this->_set_browser('os','os2');
$this->_set_browser('platform','os2');
// sets: platform = mac ; os = 68k or ppc
elseif ( preg_match($regex_mac,$this->_browser_info['ua'],$match) )
{ $this->_set_browser('platform','mac');
$os = !empty($match[1]) ? '68k' : '';
$os = !empty($match[2]) ? 'osx' : $os;
$os = !empty($match[3]) ? 'ppc' : $os;
$os = !empty($match[4]) ? 'osx' : $os;
$this->_set_browser('os',$os);
// sunos sets: platform = *nix ; os = sun|sun4|sun5|suni86
elseif (preg_match($regex_sunos,$this->_browser_info['ua'],$match))
{ $this->_set_browser('platform','*nix');
if (!stristr('sun',$match[1])) $match[1] = 'sun'. $match[1];
$this->_set_browser('os',$match[1]. $match[2]);
// irix sets: platform = *nix ; os = irix|irix5|irix6|...
elseif (preg_match($regex_irix,$this->_browser_info['ua'],$match))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os',$match[1]. $match[2]);
// hp-ux sets: platform = *nix ; os = hpux9|hpux10|...
elseif (preg_match($regex_hpux,$this->_browser_info['ua'],$match))
{ $this->_set_browser('platform','*nix');
$match[2] = (int) $match[2];
$this->_set_browser('os',$match[1]. $match[2]);
// aix sets: platform = *nix ; os = aix|aix1|aix2|aix3|...
elseif (preg_match($regex_aix,$this->_browser_info['ua'],$match))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os','aix'. $match[1]);
// dec sets: platform = *nix ; os = dec
elseif (preg_match($regex_dec,$this->_browser_info['ua'],$match))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os','dec');
// vms sets: platform = *nix ; os = vms
elseif (preg_match($regex_vms,$this->_browser_info['ua'],$match))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os','vms');
// sco sets: platform = *nix ; os = sco
elseif (preg_match($regex_sco,$this->_browser_info['ua'],$match))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os','sco');
// unixware sets: platform = *nix ; os = unixware
elseif (stristr($this->_browser_info['ua'],'unix_system_v'))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os','unixware');
// mpras sets: platform = *nix ; os = mpras
elseif (stristr($this->_browser_info['ua'],'ncr'))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os','mpras');
// reliant sets: platform = *nix ; os = reliant
elseif (stristr($this->_browser_info['ua'],'reliantunix'))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os','reliant');
// sinix sets: platform = *nix ; os = sinix
elseif (stristr($this->_browser_info['ua'],'sinix'))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os','sinix');
// bsd sets: platform = *nix ; os = bsd|freebsd
elseif (preg_match($regex_bsd,$this->_browser_info['ua'],$match))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os',$match[1]. $match[2]);
// linux sets: platform = *nix ; os = linux
elseif (preg_match($regex_linux,$this->_browser_info['ua'],$match))
{ $this->_set_browser('platform','*nix');
$this->_set_browser('os','linux');
function _get_browser_info ()
if (preg_match_all($this->_browser_regex,$this->_browser_info['ua'],$results))
{ // get the position of the last browser found
$count = count($results[0])- 1;
// if we're allowing masquerading, revert to the next to last browser found
// if possible, otherwise stay put
// insert findings into the container
$this->_set_browser('browser',$this->_get_short_name($results[1][$count]));
$this->_set_browser('long_name',$results[1][$count]);
$this->_set_browser('maj_ver',$results[2][$count]);
// parse the minor version string and look for alpha chars
preg_match('/([.\0-9]+)?([\.a-z0-9]+)?/i',$results[3][$count],$match);
$this->_set_browser('min_ver',$match[1]);
$this->_set_browser('min_ver','.0');
if (isset ($match[2])) $this->_set_browser('letter_ver',$match[2]);
// insert findings into container
$this->_set_browser('version',$this->_browser_info['maj_ver']. $this->property('min_ver'));
{ if (getenv('HTTP_CLIENT_IP'))
{ $ip = getenv('HTTP_CLIENT_IP');
{ $ip = getenv('REMOTE_ADDR');
$this->_set_browser('ip',$ip);
{ if (!empty($browsers)) $browsers .= "|";
$version_string = "[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?";
$this->_browser_regex = "/($browsers)$version_string/i";
function _get_short_name ($long_name)
// medianes :: new test cookie routine
{ global $HTTP_COOKIE_VARS;
} elseif (isset ($HTTP_COOKIE_VARS)) {
$cookies = $HTTP_COOKIE_VARS;
// make sure we have a valid file pointer
$location= 'http://'. getenv('SERVER_NAME'). $script_path. ($QS== ''? '': '?'. $QS);
header("Location: $location");
// we only want to proceed if we have a file pointer
$this->_set_browser('ss_cookies',isset ($cookies['phpSniff_session'])? 'true': 'false');
$this->_set_browser('st_cookies',isset ($cookies['phpSniff_stored'])? 'true': 'false');
// delete the old cookies
function _get_javascript()
// see if we have any matches
{ $browser = explode(',',$browser);
while(list (,$search) = each($browser))
{ if ($this->is('b:'. $search))
{ $this->_set_browser('javascript',$version);
function _get_features ()
{ $browser = explode(',',$browser);
while(list (,$search) = each($browser))
{ $this->_set_feature($feature);
{ $browser = explode(',',$browser);
while(list (,$search) = each($browser))
{ $this->_set_quirk($quirk);
{ $this->_set_browser('gecko',$match[1]);
$this->_set_browser('gecko_ver',$mozv[1]);
// mozilla milestone version
$this->_set_browser('gecko_ver',$mozv[1]);
// if this is a mozilla browser, get the rv: information
if ($this->browser_is($this->_get_short_name('mozilla'))) {
if (preg_match('/([0-9]+)([\.0-9]+)([a-z0-9+]?)/i',$mozv[1],$match)) {
$this->_set_browser('version',$mozv[1]);
$this->_set_browser('maj_ver',$match[1]);
$this->_set_browser('min_ver',$match[2]);
$this->_set_browser('letter_ver',$match[3]);
} elseif ($this->is('b:'. $this->_get_short_name('mozilla'))) {
// this is probably a netscape browser or compatible
$this->_set_browser('long_name','netscape');
$this->_set_browser('browser',$this->_get_short_name('netscape'));
function _set_browser ($k,$v)
function _set_feature ($k)
|