[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/classes/ -> useragent.php (source)

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Environment class to aid with the detection and establishment of the working environment.
  19   *
  20   * @package    core
  21   * @copyright  2013 Sam Hemelryk
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  /**
  26   * The user agent class.
  27   *
  28   * It's important to note that we do not like browser sniffing and its use in core code is highly discouraged.
  29   * No new uses of this API will be integrated unless there is absolutely no alternative.
  30   *
  31   * This API supports the few browser checks we do have in core, all of which one day will hopefully be removed.
  32   * The API will remain to support any third party use out there, however at some point like all code it will be deprecated.
  33   *
  34   * Use sparingly and only with good cause!
  35   *
  36   * @package    core
  37   * @copyright  2013 Sam Hemelryk
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class core_useragent {
  41  
  42      /**
  43       * The default for devices, think of as a computer.
  44       */
  45      const DEVICETYPE_DEFAULT = 'default';
  46      /**
  47       * Legacy devices, or at least legacy browsers. These are older devices/browsers
  48       * that don't support standards.
  49       */
  50      const DEVICETYPE_LEGACY = 'legacy';
  51      /**
  52       * Mobile devices like your cell phone or hand held gaming device.
  53       */
  54      const DEVICETYPE_MOBILE = 'mobile';
  55      /**
  56       * Tables, larger than hand held, but still easily portable and smaller than a laptop.
  57       */
  58      const DEVICETYPE_TABLET = 'tablet';
  59  
  60      /**
  61       * An instance of this class.
  62       * @var core_useragent
  63       */
  64      protected static $instance = null;
  65  
  66      /**
  67       * The device types we track.
  68       * @var array
  69       */
  70      public static $devicetypes = array(
  71          self::DEVICETYPE_DEFAULT,
  72          self::DEVICETYPE_LEGACY,
  73          self::DEVICETYPE_MOBILE,
  74          self::DEVICETYPE_TABLET
  75      );
  76  
  77      /**
  78       * The current requests user agent string if there was one.
  79       * @var string|bool|null Null until initialised, false if none available, or string when available.
  80       */
  81      protected $useragent = null;
  82  
  83      /**
  84       * The users device type, one of self::DEVICETYPE_*.
  85       * @var string null until initialised
  86       */
  87      protected $devicetype = null;
  88  
  89      /**
  90       * Custom device types entered into the admin interface.
  91       * @var array
  92       */
  93      protected $devicetypecustoms = array();
  94  
  95      /**
  96       * True if the user agent supports the display of svg images. False if not.
  97       * @var bool|null Null until initialised, then true or false.
  98       */
  99      protected $supportssvg = null;
 100  
 101      /**
 102       * Get an instance of the user agent object.
 103       *
 104       * @param bool $reload If set to true the user agent will be reset and all ascertations remade.
 105       * @param string $forceuseragent The string to force as the user agent, don't use unless absolutely unavoidable.
 106       * @return core_useragent
 107       */
 108      public static function instance($reload = false, $forceuseragent = null) {
 109          if (!self::$instance || $reload) {
 110              self::$instance = new core_useragent($forceuseragent);
 111          }
 112          return self::$instance;
 113      }
 114  
 115      /**
 116       * Constructs a new user agent object. Publically you must use the instance method above.
 117       *
 118       * @param string|null $forceuseragent Optional a user agent to force.
 119       */
 120      protected function __construct($forceuseragent = null) {
 121          global $CFG;
 122          if (!empty($CFG->devicedetectregex)) {
 123              $this->devicetypecustoms = json_decode($CFG->devicedetectregex, true);
 124          }
 125          if ($this->devicetypecustoms === null) {
 126              // This shouldn't happen unless you're hardcoding the config value.
 127              debugging('Config devicedetectregex is not valid JSON object');
 128              $this->devicetypecustoms = array();
 129          }
 130          if ($forceuseragent !== null) {
 131              $this->useragent = $forceuseragent;
 132          } else if (!empty($_SERVER['HTTP_USER_AGENT'])) {
 133              $this->useragent = $_SERVER['HTTP_USER_AGENT'];
 134          } else {
 135              $this->useragent = false;
 136              $this->devicetype = self::DEVICETYPE_DEFAULT;
 137          }
 138      }
 139  
 140      /**
 141       * Returns the user agent string.
 142       * @return bool|string The user agent string or false if one isn't available.
 143       */
 144      public static function get_user_agent_string() {
 145          $instance = self::instance();
 146          return $instance->useragent;
 147      }
 148  
 149      /**
 150       * Returns the device type we believe is being used.
 151       * @return string
 152       */
 153      public static function get_device_type() {
 154          $instance = self::instance();
 155          if ($instance->devicetype === null) {
 156              return $instance->guess_device_type();
 157          }
 158          return $instance->devicetype;
 159      }
 160  
 161      /**
 162       * Guesses the device type the user agent is running on.
 163       *
 164       * @return string
 165       */
 166      protected function guess_device_type() {
 167          global $CFG;
 168          if (empty($CFG->enabledevicedetection)) {
 169              $this->devicetype = self::DEVICETYPE_DEFAULT;
 170              return $this->devicetype;
 171          }
 172          foreach ($this->devicetypecustoms as $value => $regex) {
 173              if (preg_match($regex, $this->useragent)) {
 174                  $this->devicetype = $value;
 175                  return $this->devicetype;
 176              }
 177          }
 178          if ($this->is_useragent_mobile()) {
 179              $this->devicetype = 'mobile';
 180          } else if ($this->is_useragent_tablet()) {
 181              $this->devicetype = 'tablet';
 182          } else if (substr($this->useragent, 0, 34) === 'Mozilla/4.0 (compatible; MSIE 6.0;') {
 183              // Safe way to check for IE6 and not get false positives for some IE 7/8 users.
 184              $this->devicetype = 'legacy';
 185          } else {
 186              $this->devicetype = self::DEVICETYPE_DEFAULT;
 187          }
 188          return $this->devicetype;
 189      }
 190  
 191      /**
 192       * Returns true if the user appears to be on a mobile device.
 193       * @return bool
 194       */
 195      protected function is_useragent_mobile() {
 196          // Mobile detection PHP direct copy from open source detectmobilebrowser.com.
 197          $phonesregex = '/android .+ mobile|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i';
 198          $modelsregex = '/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i';
 199          return (preg_match($phonesregex, $this->useragent) || preg_match($modelsregex, substr($this->useragent, 0, 4)));
 200      }
 201  
 202      /**
 203       * Returns true if the user appears to be on a tablet.
 204       * @return int
 205       */
 206      protected function is_useragent_tablet() {
 207          $tabletregex = '/Tablet browser|android|iPad|iProd|GT-P1000|GT-I9000|SHW-M180S|SGH-T849|SCH-I800|Build\/ERE27|sholest/i';
 208          return (preg_match($tabletregex, $this->useragent));
 209      }
 210  
 211      /**
 212       * Gets a list of known device types.
 213       *
 214       * @param bool $includecustomtypes If set to true we'll include types that have been added by the admin.
 215       * @return array
 216       */
 217      public static function get_device_type_list($includecustomtypes = true) {
 218          $types = self::$devicetypes;
 219          if ($includecustomtypes) {
 220              $instance = self::instance();
 221              $types = array_merge($types, array_keys($instance->devicetypecustoms));
 222          }
 223          return $types;
 224      }
 225  
 226      /**
 227       * Returns the theme to use for the given device type.
 228       *
 229       * This used to be get_selected_theme_for_device_type.
 230       * @param null|string $devicetype The device type to find out for. Defaults to the device the user is using,
 231       * @return bool
 232       */
 233      public static function get_device_type_theme($devicetype = null) {
 234          global $CFG;
 235          if ($devicetype === null) {
 236              $devicetype = self::get_device_type();
 237          }
 238          $themevarname = self::get_device_type_cfg_var_name($devicetype);
 239          if (empty($CFG->$themevarname)) {
 240              return false;
 241          }
 242          return $CFG->$themevarname;
 243      }
 244  
 245      /**
 246       * Returns the CFG var used to find the theme to use for the given device.
 247       *
 248       * Used to be get_device_cfg_var_name.
 249       *
 250       * @param null|string $devicetype The device type to find out for. Defaults to the device the user is using,
 251       * @return string
 252       */
 253      public static function get_device_type_cfg_var_name($devicetype = null) {
 254          if ($devicetype == self::DEVICETYPE_DEFAULT || empty($devicetype)) {
 255              return 'theme';
 256          }
 257          return 'theme' . $devicetype;
 258      }
 259  
 260      /**
 261       * Gets the device type the user is currently using.
 262       * @return string
 263       */
 264      public static function get_user_device_type() {
 265          $device = self::get_device_type();
 266          $switched = get_user_preferences('switchdevice'.$device, false);
 267          if ($switched != false) {
 268              return $switched;
 269          }
 270          return $device;
 271      }
 272  
 273      /**
 274       * Switches the device type we think the user is using to what ever was given.
 275       * @param string $newdevice
 276       * @return bool
 277       * @throws coding_exception
 278       */
 279      public static function set_user_device_type($newdevice) {
 280          $devicetype = self::get_device_type();
 281          if ($newdevice == $devicetype) {
 282              unset_user_preference('switchdevice'.$devicetype);
 283              return true;
 284          } else {
 285              $devicetypes = self::get_device_type_list();
 286              if (in_array($newdevice, $devicetypes)) {
 287                  set_user_preference('switchdevice'.$devicetype, $newdevice);
 288                  return true;
 289              }
 290          }
 291          throw new coding_exception('Invalid device type provided to set_user_device_type');
 292      }
 293  
 294      /**
 295       * Returns true if the user agent matches the given brand and the version is equal to or greater than that specified.
 296       *
 297       * @param string $brand The branch to check for.
 298       * @param scalar $version The version if we need to find out if it is equal to or greater than that specified.
 299       * @return bool
 300       */
 301      public static function check_browser_version($brand, $version = null) {
 302          switch ($brand) {
 303  
 304              case 'MSIE':
 305                  // Internet Explorer.
 306                  return self::check_ie_version($version);
 307  
 308              case 'Firefox':
 309                  // Mozilla Firefox browsers.
 310                  return self::check_firefox_version($version);
 311  
 312              case 'Chrome':
 313                  return self::check_chrome_version($version);
 314  
 315              case 'Opera':
 316                  // Opera.
 317                  return self::check_opera_version($version);
 318  
 319              case 'Safari':
 320                  // Desktop version of Apple Safari browser - no mobile or touch devices.
 321                  return self::check_safari_version($version);
 322  
 323              case 'Safari iOS':
 324                  // Safari on iPhone, iPad and iPod touch.
 325                  return self::check_safari_ios_version($version);
 326  
 327              case 'WebKit':
 328                  // WebKit based browser - everything derived from it (Safari, Chrome, iOS, Android and other mobiles).
 329                  return self::check_webkit_version($version);
 330  
 331              case 'Gecko':
 332                  // Gecko based browsers.
 333                  return self::check_gecko_version($version);
 334  
 335              case 'WebKit Android':
 336                  // WebKit browser on Android.
 337                  return self::check_webkit_android_version($version);
 338  
 339              case 'Camino':
 340                  // OSX browser using Gecke engine.
 341                  return self::check_camino_version($version);
 342          }
 343          // Who knows?! doesn't pass anyway.
 344          return false;
 345      }
 346  
 347      /**
 348       * Checks the user agent is camino based and that the version is equal to or greater than that specified.
 349       *
 350       * Camino browser is at the end of its life, its no longer being developed or supported, just don't worry about it.
 351       *
 352       * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
 353       * @return bool
 354       */
 355      protected static function check_camino_version($version = null) {
 356          // OSX browser using Gecko engine.
 357          $useragent = self::get_user_agent_string();
 358          if ($useragent === false) {
 359              return false;
 360          }
 361          if (strpos($useragent, 'Camino') === false) {
 362              return false;
 363          }
 364          if (empty($version)) {
 365              return true; // No version specified.
 366          }
 367          if (preg_match("/Camino\/([0-9\.]+)/i", $useragent, $match)) {
 368              if (version_compare($match[1], $version) >= 0) {
 369                  return true;
 370              }
 371          }
 372          return false;
 373      }
 374  
 375      /**
 376       * Checks the user agent is Firefox (of any version).
 377       *
 378       * @return bool true if firefox
 379       */
 380      public static function is_firefox() {
 381          return self::check_firefox_version();
 382      }
 383  
 384      /**
 385       * Checks the user agent is Firefox based and that the version is equal to or greater than that specified.
 386       *
 387       * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
 388       * @return bool
 389       */
 390      public static function check_firefox_version($version = null) {
 391          // Mozilla Firefox browsers.
 392          $useragent = self::get_user_agent_string();
 393          if ($useragent === false) {
 394              return false;
 395          }
 396          if (strpos($useragent, 'Firefox') === false && strpos($useragent, 'Iceweasel') === false) {
 397              return false;
 398          }
 399          if (empty($version)) {
 400              return true; // No version specified..
 401          }
 402          if (preg_match("/(Iceweasel|Firefox)\/([0-9\.]+)/i", $useragent, $match)) {
 403              if (version_compare($match[2], $version) >= 0) {
 404                  return true;
 405              }
 406          }
 407          return false;
 408      }
 409  
 410      /**
 411       * Checks the user agent is Gecko based (of any version).
 412       *
 413       * @return bool true if Gecko based.
 414       */
 415      public static function is_gecko() {
 416          return self::check_gecko_version();
 417      }
 418  
 419      /**
 420       * Checks the user agent is Gecko based and that the version is equal to or greater than that specified.
 421       *
 422       * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
 423       * @return bool
 424       */
 425      public static function check_gecko_version($version = null) {
 426          // Gecko based browsers.
 427          // Do not look for dates any more, we expect real Firefox version here.
 428          $useragent = self::get_user_agent_string();
 429          if ($useragent === false) {
 430              return false;
 431          }
 432          if (empty($version)) {
 433              $version = 1;
 434          } else if ($version > 20000000) {
 435              // This is just a guess, it is not supposed to be 100% accurate!
 436              if (preg_match('/^201/', $version)) {
 437                  $version = 3.6;
 438              } else if (preg_match('/^200[7-9]/', $version)) {
 439                  $version = 3;
 440              } else if (preg_match('/^2006/', $version)) {
 441                  $version = 2;
 442              } else {
 443                  $version = 1.5;
 444              }
 445          }
 446          if (preg_match("/(Iceweasel|Firefox)\/([0-9\.]+)/i", $useragent, $match)) {
 447              // Use real Firefox version if specified in user agent string.
 448              if (version_compare($match[2], $version) >= 0) {
 449                  return true;
 450              }
 451          } else if (preg_match("/Gecko\/([0-9\.]+)/i", $useragent, $match)) {
 452              // Gecko might contain date or Firefox revision, let's just guess the Firefox version from the date.
 453              $browserver = $match[1];
 454              if ($browserver > 20000000) {
 455                  // This is just a guess, it is not supposed to be 100% accurate!
 456                  if (preg_match('/^201/', $browserver)) {
 457                      $browserver = 3.6;
 458                  } else if (preg_match('/^200[7-9]/', $browserver)) {
 459                      $browserver = 3;
 460                  } else if (preg_match('/^2006/', $version)) {
 461                      $browserver = 2;
 462                  } else {
 463                      $browserver = 1.5;
 464                  }
 465              }
 466              if (version_compare($browserver, $version) >= 0) {
 467                  return true;
 468              }
 469          }
 470          return false;
 471      }
 472  
 473      /**
 474       * Checks the user agent is IE (of any version).
 475       *
 476       * @return bool true if internet exporeer
 477       */
 478      public static function is_ie() {
 479          return self::check_ie_version();
 480      }
 481  
 482      /**
 483       * Checks the user agent is IE and returns its main properties:
 484       * - browser version;
 485       * - whether running in compatibility view.
 486       *
 487       * @return bool|array False if not IE, otherwise an associative array of properties.
 488       */
 489      public static function check_ie_properties() {
 490          // Internet Explorer.
 491          $useragent = self::get_user_agent_string();
 492          if ($useragent === false) {
 493              return false;
 494          }
 495          if (strpos($useragent, 'Opera') !== false) {
 496              // Reject Opera.
 497              return false;
 498          }
 499          // See: http://www.useragentstring.com/pages/Internet%20Explorer/.
 500          if (preg_match("/MSIE ([0-9\.]+)/", $useragent, $match)) {
 501              $browser = $match[1];
 502          // See: http://msdn.microsoft.com/en-us/library/ie/bg182625%28v=vs.85%29.aspx for IE11+ useragent details.
 503          } else if (preg_match("/Trident\/[0-9\.]+/", $useragent) && preg_match("/rv:([0-9\.]+)/", $useragent, $match)) {
 504              $browser = $match[1];
 505          } else {
 506              return false;
 507          }
 508          $compatview = false;
 509          // IE8 and later versions may pretend to be IE7 for intranet sites, use Trident version instead,
 510          // the Trident should always describe the capabilities of IE in any emulation mode.
 511          if ($browser === '7.0' and preg_match("/Trident\/([0-9\.]+)/", $useragent, $match)) {
 512              $compatview = true;
 513              $browser = $match[1] + 4; // NOTE: Hopefully this will work also for future IE versions.
 514          }
 515          $browser = round($browser, 1);
 516          return array(
 517              'version'    => $browser,
 518              'compatview' => $compatview
 519          );
 520      }
 521  
 522      /**
 523       * Checks the user agent is IE and that the version is equal to or greater than that specified.
 524       *
 525       * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
 526       * @return bool
 527       */
 528      public static function check_ie_version($version = null) {
 529          // Internet Explorer.
 530          $properties = self::check_ie_properties();
 531          if (!is_array($properties)) {
 532              return false;
 533          }
 534          // In case of IE we have to deal with BC of the version parameter.
 535          if (is_null($version)) {
 536              $version = 5.5; // Anything older is not considered a browser at all!
 537          }
 538          // IE uses simple versions, let's cast it to float to simplify the logic here.
 539          $version = round($version, 1);
 540          return ($properties['version'] >= $version);
 541      }
 542  
 543      /**
 544       * Checks the user agent is IE and that IE is running under Compatibility View setting.
 545       *
 546       * @return bool true if internet explorer runs in Compatibility View mode.
 547       */
 548      public static function check_ie_compatibility_view() {
 549          // IE User Agent string when in Compatibility View:
 550          // - IE  8: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; ...)".
 551          // - IE  9: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; ...)".
 552          // - IE 10: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0; ...)".
 553          // - IE 11: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; ...)".
 554          // Refs:
 555          // - http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx.
 556          // - http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx.
 557          // - http://blogs.msdn.com/b/ie/archive/2011/04/15/the-ie10-user-agent-string.aspx.
 558          // - http://msdn.microsoft.com/en-us/library/ie/hh869301%28v=vs.85%29.aspx.
 559          $properties = self::check_ie_properties();
 560          if (!is_array($properties)) {
 561              return false;
 562          }
 563          return $properties['compatview'];
 564      }
 565  
 566      /**
 567       * Checks the user agent is Opera (of any version).
 568       *
 569       * @return bool true if opera
 570       */
 571      public static function is_opera() {
 572          return self::check_opera_version();
 573      }
 574  
 575      /**
 576       * Checks the user agent is Opera and that the version is equal to or greater than that specified.
 577       *
 578       * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
 579       * @return bool
 580       */
 581      public static function check_opera_version($version = null) {
 582          // Opera.
 583          $useragent = self::get_user_agent_string();
 584          if ($useragent === false) {
 585              return false;
 586          }
 587          if (strpos($useragent, 'Opera') === false) {
 588              return false;
 589          }
 590          if (empty($version)) {
 591              return true; // No version specified.
 592          }
 593          // Recent Opera useragents have Version/ with the actual version, e.g.:
 594          // Opera/9.80 (Windows NT 6.1; WOW64; U; en) Presto/2.10.289 Version/12.01
 595          // That's Opera 12.01, not 9.8.
 596          if (preg_match("/Version\/([0-9\.]+)/i", $useragent, $match)) {
 597              if (version_compare($match[1], $version) >= 0) {
 598                  return true;
 599              }
 600          } else if (preg_match("/Opera\/([0-9\.]+)/i", $useragent, $match)) {
 601              if (version_compare($match[1], $version) >= 0) {
 602                  return true;
 603              }
 604          }
 605          return false;
 606      }
 607  
 608      /**
 609       * Checks the user agent is webkit based
 610       *
 611       * @return bool true if webkit
 612       */
 613      public static function is_webkit() {
 614          return self::check_webkit_version();
 615      }
 616  
 617      /**
 618       * Checks the user agent is Webkit based and that the version is equal to or greater than that specified.
 619       *
 620       * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
 621       * @return bool
 622       */
 623      public static function check_webkit_version($version = null) {
 624          // WebKit based browser - everything derived from it (Safari, Chrome, iOS, Android and other mobiles).
 625          $useragent = self::get_user_agent_string();
 626          if ($useragent === false) {
 627              return false;
 628          }
 629          if (strpos($useragent, 'AppleWebKit') === false) {
 630              return false;
 631          }
 632          if (empty($version)) {
 633              return true; // No version specified.
 634          }
 635          if (preg_match("/AppleWebKit\/([0-9.]+)/i", $useragent, $match)) {
 636              if (version_compare($match[1], $version) >= 0) {
 637                  return true;
 638              }
 639          }
 640          return false;
 641      }
 642  
 643      /**
 644       * Checks the user agent is Safari
 645       *
 646       * @return bool true if safari
 647       */
 648      public static function is_safari() {
 649          return self::check_safari_version();
 650      }
 651  
 652      /**
 653       * Checks the user agent is Safari based and that the version is equal to or greater than that specified.
 654       *
 655       * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
 656       * @return bool
 657       */
 658      public static function check_safari_version($version = null) {
 659          // Desktop version of Apple Safari browser - no mobile or touch devices.
 660          $useragent = self::get_user_agent_string();
 661          if ($useragent === false) {
 662              return false;
 663          }
 664          if (strpos($useragent, 'AppleWebKit') === false) {
 665              return false;
 666          }
 667          // Look for AppleWebKit, excluding strings with OmniWeb, Shiira and SymbianOS and any other mobile devices.
 668          if (strpos($useragent, 'OmniWeb')) {
 669              // Reject OmniWeb.
 670              return false;
 671          }
 672          if (strpos($useragent, 'Shiira')) {
 673              // Reject Shiira.
 674              return false;
 675          }
 676          if (strpos($useragent, 'SymbianOS')) {
 677              // Reject SymbianOS.
 678              return false;
 679          }
 680          if (strpos($useragent, 'Android')) {
 681              // Reject Androids too.
 682              return false;
 683          }
 684          if (strpos($useragent, 'iPhone') or strpos($useragent, 'iPad') or strpos($useragent, 'iPod')) {
 685              // No Apple mobile devices here - editor does not work, course ajax is not touch compatible, etc.
 686              return false;
 687          }
 688          if (strpos($useragent, 'Chrome')) { // Reject chrome browsers - it needs to be tested explicitly.
 689              return false;
 690          }
 691  
 692          if (empty($version)) {
 693              return true; // No version specified.
 694          }
 695          if (preg_match("/AppleWebKit\/([0-9.]+)/i", $useragent, $match)) {
 696              if (version_compare($match[1], $version) >= 0) {
 697                  return true;
 698              }
 699          }
 700          return false;
 701      }
 702  
 703      /**
 704       * Checks the user agent is Chrome
 705       *
 706       * @return bool true if chrome
 707       */
 708      public static function is_chrome() {
 709          return self::check_chrome_version();
 710      }
 711  
 712      /**
 713       * Checks the user agent is Chrome based and that the version is equal to or greater than that specified.
 714       *
 715       * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
 716       * @return bool
 717       */
 718      public static function check_chrome_version($version = null) {
 719          // Chrome.
 720          $useragent = self::get_user_agent_string();
 721          if ($useragent === false) {
 722              return false;
 723          }
 724          if (strpos($useragent, 'Chrome') === false) {
 725              return false;
 726          }
 727          if (empty($version)) {
 728              return true; // No version specified.
 729          }
 730          if (preg_match("/Chrome\/(.*)[ ]+/i", $useragent, $match)) {
 731              if (version_compare($match[1], $version) >= 0) {
 732                  return true;
 733              }
 734          }
 735          return false;
 736      }
 737  
 738      /**
 739       * Checks the user agent is webkit android based.
 740       *
 741       * @return bool true if webkit based and on Android
 742       */
 743      public static function is_webkit_android() {
 744          return self::check_webkit_android_version();
 745      }
 746  
 747      /**
 748       * Checks the user agent is Webkit based and on Android and that the version is equal to or greater than that specified.
 749       *
 750       * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
 751       * @return bool
 752       */
 753      public static function check_webkit_android_version($version = null) {
 754          // WebKit browser on Android.
 755          $useragent = self::get_user_agent_string();
 756          if ($useragent === false) {
 757              return false;
 758          }
 759          if (strpos($useragent, 'Linux; U; Android') === false) {
 760              return false;
 761          }
 762          if (empty($version)) {
 763              return true; // No version specified.
 764          }
 765          if (preg_match("/AppleWebKit\/([0-9]+)/i", $useragent, $match)) {
 766              if (version_compare($match[1], $version) >= 0) {
 767                  return true;
 768              }
 769          }
 770          return false;
 771      }
 772  
 773      /**
 774       * Checks the user agent is Safari on iOS
 775       *
 776       * @return bool true if Safari on iOS
 777       */
 778      public static function is_safari_ios() {
 779          return self::check_safari_ios_version();
 780      }
 781  
 782      /**
 783       * Checks the user agent is Safari on iOS and that the version is equal to or greater than that specified.
 784       *
 785       * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
 786       * @return bool
 787       */
 788      public static function check_safari_ios_version($version = null) {
 789          // Safari on iPhone, iPad and iPod touch.
 790          $useragent = self::get_user_agent_string();
 791          if ($useragent === false) {
 792              return false;
 793          }
 794          if (strpos($useragent, 'AppleWebKit') === false or strpos($useragent, 'Safari') === false) {
 795              return false;
 796          }
 797          if (!strpos($useragent, 'iPhone') and !strpos($useragent, 'iPad') and !strpos($useragent, 'iPod')) {
 798              return false;
 799          }
 800          if (empty($version)) {
 801              return true; // No version specified.
 802          }
 803          if (preg_match("/AppleWebKit\/([0-9]+)/i", $useragent, $match)) {
 804              if (version_compare($match[1], $version) >= 0) {
 805                  return true;
 806              }
 807          }
 808          return false;
 809      }
 810  
 811      /**
 812       * Check if the user agent matches a given brand.
 813       *
 814       * Known brand: 'Windows','Linux','Macintosh','SGI','SunOS','HP-UX'
 815       *
 816       * @param string $brand
 817       * @return bool
 818       */
 819      public static function check_browser_operating_system($brand) {
 820          $useragent = self::get_user_agent_string();
 821          return ($useragent !== false && preg_match("/$brand/i", $useragent));
 822      }
 823  
 824      /**
 825       * Gets an array of CSS classes to represent the user agent.
 826       * @return array
 827       */
 828      public static function get_browser_version_classes() {
 829          $classes = array();
 830          if (self::is_ie()) {
 831              $classes[] = 'ie';
 832              for ($i = 12; $i >= 6; $i--) {
 833                  if (self::check_ie_version($i)) {
 834                      $classes[] = 'ie'.$i;
 835                      break;
 836                  }
 837              }
 838          } else if (self::is_firefox() || self::is_gecko() || self::check_camino_version()) {
 839              $classes[] = 'gecko';
 840              if (preg_match('/rv\:([1-2])\.([0-9])/', self::get_user_agent_string(), $matches)) {
 841                  $classes[] = "gecko{$matches[1]}{$matches[2]}";
 842              }
 843          } else if (self::is_webkit()) {
 844              $classes[] = 'safari';
 845              if (self::is_safari_ios()) {
 846                  $classes[] = 'ios';
 847              } else if (self::is_webkit_android()) {
 848                  $classes[] = 'android';
 849              }
 850          } else if (self::is_opera()) {
 851              $classes[] = 'opera';
 852          }
 853          return $classes;
 854      }
 855  
 856      /**
 857       * Returns true if the user agent supports the display of SVG images.
 858       *
 859       * @return bool
 860       */
 861      public static function supports_svg() {
 862          // IE 5 - 8 don't support SVG at all.
 863          $instance = self::instance();
 864          if ($instance->supportssvg === null) {
 865              if ($instance->useragent === false) {
 866                  // Can't be sure, just say no.
 867                  $instance->supportssvg = false;
 868              } else if (self::is_ie() and !self::check_ie_version('9')) {
 869                  // IE < 9 doesn't support SVG. Say no.
 870                  $instance->supportssvg = false;
 871              } else if (self::is_ie() and !self::check_ie_version('10') and self::check_ie_compatibility_view()) {
 872                  // IE 9 Compatibility View doesn't support SVG. Say no.
 873                  $instance->supportssvg = false;
 874              } else if (preg_match('#Android +[0-2]\.#', $instance->useragent)) {
 875                  // Android < 3 doesn't support SVG. Say no.
 876                  $instance->supportssvg = false;
 877              } else if (self::is_opera()) {
 878                  // Opera 12 still does not support SVG well enough. Say no.
 879                  $instance->supportssvg = false;
 880              } else {
 881                  // Presumed fine.
 882                  $instance->supportssvg = true;
 883              }
 884          }
 885          return $instance->supportssvg;
 886      }
 887  
 888      /**
 889       * Returns true if the user agent supports the MIME media type for JSON text, as defined in RFC 4627.
 890       *
 891       * @return bool
 892       */
 893      public static function supports_json_contenttype() {
 894          // Modern browsers other than IE correctly supports 'application/json' media type.
 895          if (!self::is_ie()) {
 896              return true;
 897          }
 898  
 899          // IE8+ supports 'application/json' media type, when NOT in Compatibility View mode.
 900          // Refs:
 901          // - http://blogs.msdn.com/b/ie/archive/2008/09/10/native-json-in-ie8.aspx;
 902          // - MDL-39810: issues when using 'text/plain' in Compatibility View for the body of an HTTP POST response.
 903          if (self::check_ie_version(8) && !self::check_ie_compatibility_view()) {
 904              return true;
 905          }
 906  
 907          // This browser does not support json.
 908          return false;
 909      }
 910  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1