UserAgent.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php
  2. namespace App\Services;
  3. const PLATFORM = 'platform';
  4. const BROWSER = 'browser';
  5. const BROWSER_VERSION = 'version';
  6. class UserAgent
  7. {
  8. const PLATFORM = 'platform';
  9. const BROWSER = 'browser';
  10. const BROWSER_VERSION = 'version';
  11. /**
  12. * Parses a user agent string into its important parts
  13. *
  14. * @param string|null $u_agent User agent string to parse or null. Uses $_SERVER['HTTP_USER_AGENT'] on NULL
  15. * @return string[] an array with 'browser', 'version' and 'platform' keys
  16. * @throws \InvalidArgumentException on not having a proper user agent to parse.
  17. */
  18. static function parse_user_agent( $u_agent = null ) {
  19. if( $u_agent === null && isset($_SERVER['HTTP_USER_AGENT']) ) {
  20. $u_agent = (string)$_SERVER['HTTP_USER_AGENT'];
  21. }
  22. if( $u_agent === null ) {
  23. throw new \InvalidArgumentException('parse_user_agent requires a user agent');
  24. }
  25. $platform = null;
  26. $browser = null;
  27. $version = null;
  28. $return = [ PLATFORM => &$platform, BROWSER => &$browser, BROWSER_VERSION => &$version ];
  29. if( !$u_agent ) {
  30. return $return;
  31. }
  32. if( preg_match('/\((.*?)\)/m', $u_agent, $parent_matches) ) {
  33. preg_match_all(<<<'REGEX'
  34. /(?P<platform>BB\d+;|Android|Adr|Symbian|Sailfish|CrOS|Tizen|iPhone|iPad|iPod|Linux|(?:Open|Net|Free)BSD|Macintosh|
  35. Windows(?:\ Phone)?|Silk|linux-gnu|BlackBerry|PlayBook|X11|(?:New\ )?Nintendo\ (?:WiiU?|3?DS|Switch)|Xbox(?:\ One)?)
  36. (?:\ [^;]*)?
  37. (?:;|$)/imx
  38. REGEX
  39. , $parent_matches[1], $result);
  40. $priority = [ 'Xbox One', 'Xbox', 'Windows Phone', 'Tizen', 'Android', 'FreeBSD', 'NetBSD', 'OpenBSD', 'CrOS', 'X11', 'Sailfish' ];
  41. $result[PLATFORM] = array_unique($result[PLATFORM]);
  42. if( count($result[PLATFORM]) > 1 ) {
  43. if( $keys = array_intersect($priority, $result[PLATFORM]) ) {
  44. $platform = reset($keys);
  45. } else {
  46. $platform = $result[PLATFORM][0];
  47. }
  48. } elseif( isset($result[PLATFORM][0]) ) {
  49. $platform = $result[PLATFORM][0];
  50. }
  51. }
  52. if( $platform == 'linux-gnu' || $platform == 'X11' ) {
  53. $platform = 'Linux';
  54. } elseif( $platform == 'CrOS' ) {
  55. $platform = 'Chrome OS';
  56. } elseif( $platform == 'Adr' ) {
  57. $platform = 'Android';
  58. } elseif( $platform === null ) {
  59. if(preg_match_all('%(?P<platform>Android)[:/ ]%ix', $u_agent, $result)) {
  60. $platform = $result[PLATFORM][0];
  61. }
  62. }
  63. preg_match_all(<<<'REGEX'
  64. %(?P<browser>Camino|Kindle(\ Fire)?|Firefox|Iceweasel|IceCat|Safari|MSIE|Trident|AppleWebKit|
  65. TizenBrowser|(?:Headless)?Chrome|YaBrowser|Vivaldi|IEMobile|Opera|OPR|Silk|Midori|(?-i:Edge)|EdgA?|CriOS|UCBrowser|Puffin|
  66. OculusBrowser|SamsungBrowser|SailfishBrowser|XiaoMi/MiuiBrowser|YaApp_Android|
  67. Baiduspider|Applebot|Facebot|Googlebot|YandexBot|bingbot|Lynx|Version|Wget|curl|
  68. Valve\ Steam\ Tenfoot|
  69. NintendoBrowser|PLAYSTATION\ (?:\d|Vita)+)
  70. \)?;?
  71. (?:[:/ ](?P<version>[0-9A-Z.]+)|/[A-Z]*)%ix
  72. REGEX
  73. , $u_agent, $result);
  74. // If nothing matched, return null (to avoid undefined index errors)
  75. if( !isset($result[BROWSER][0], $result[BROWSER_VERSION][0]) ) {
  76. if( preg_match('%^(?!Mozilla)(?P<browser>[A-Z0-9\-]+)(/(?P<version>[0-9A-Z.]+))?%ix', $u_agent, $result) ) {
  77. return [ PLATFORM => $platform ?: null, BROWSER => $result[BROWSER], BROWSER_VERSION => empty($result[BROWSER_VERSION]) ? null : $result[BROWSER_VERSION] ];
  78. }
  79. return $return;
  80. }
  81. if( preg_match('/rv:(?P<version>[0-9A-Z.]+)/i', $u_agent, $rv_result) ) {
  82. $rv_result = $rv_result[BROWSER_VERSION];
  83. }
  84. $browser = $result[BROWSER][0];
  85. $version = $result[BROWSER_VERSION][0];
  86. $lowerBrowser = array_map('strtolower', $result[BROWSER]);
  87. $find = function ( $search, &$key = null, &$value = null ) use ( $lowerBrowser ) {
  88. $search = (array)$search;
  89. foreach( $search as $val ) {
  90. $xkey = array_search(strtolower($val), $lowerBrowser);
  91. if( $xkey !== false ) {
  92. $value = $val;
  93. $key = $xkey;
  94. return true;
  95. }
  96. }
  97. return false;
  98. };
  99. $findT = function ( array $search, &$key = null, &$value = null ) use ( $find ) {
  100. $value2 = null;
  101. if( $find(array_keys($search), $key, $value2) ) {
  102. $value = $search[$value2];
  103. return true;
  104. }
  105. return false;
  106. };
  107. $key = 0;
  108. $val = '';
  109. if( $findT([ 'OPR' => 'Opera', 'Facebot' => 'iMessageBot', 'UCBrowser' => 'UC Browser', 'YaBrowser' => 'Yandex', 'YaApp_Android' => 'Yandex', 'Iceweasel' => 'Firefox', 'Icecat' => 'Firefox', 'CriOS' => 'Chrome', 'Edg' => 'Edge', 'EdgA' => 'Edge', 'XiaoMi/MiuiBrowser' => 'MiuiBrowser' ], $key, $browser) ) {
  110. $version = is_numeric(substr($result[BROWSER_VERSION][$key], 0, 1)) ? $result[BROWSER_VERSION][$key] : null;
  111. } elseif( $find('Playstation Vita', $key, $platform) ) {
  112. $platform = 'PlayStation Vita';
  113. $browser = 'Browser';
  114. } elseif( $find([ 'Kindle Fire', 'Silk' ], $key, $val) ) {
  115. $browser = $val == 'Silk' ? 'Silk' : 'Kindle';
  116. $platform = 'Kindle Fire';
  117. if( !($version = $result[BROWSER_VERSION][$key]) || !is_numeric($version[0]) ) {
  118. $version = $result[BROWSER_VERSION][array_search('Version', $result[BROWSER])];
  119. }
  120. } elseif( $find('NintendoBrowser', $key) || $platform == 'Nintendo 3DS' ) {
  121. $browser = 'NintendoBrowser';
  122. $version = $result[BROWSER_VERSION][$key];
  123. } elseif( $find('Kindle', $key, $platform) ) {
  124. $browser = $result[BROWSER][$key];
  125. $version = $result[BROWSER_VERSION][$key];
  126. } elseif( $find('Opera', $key, $browser) ) {
  127. $find('Version', $key);
  128. $version = $result[BROWSER_VERSION][$key];
  129. } elseif( $find('Puffin', $key, $browser) ) {
  130. $version = $result[BROWSER_VERSION][$key];
  131. if( strlen($version) > 3 ) {
  132. $part = substr($version, -2);
  133. if( ctype_upper($part) ) {
  134. $version = substr($version, 0, -2);
  135. $flags = [ 'IP' => 'iPhone', 'IT' => 'iPad', 'AP' => 'Android', 'AT' => 'Android', 'WP' => 'Windows Phone', 'WT' => 'Windows' ];
  136. if( isset($flags[$part]) ) {
  137. $platform = $flags[$part];
  138. }
  139. }
  140. }
  141. } elseif( $find([ 'Applebot', 'IEMobile', 'Edge', 'Midori', 'Vivaldi', 'OculusBrowser', 'SamsungBrowser', 'Valve Steam Tenfoot', 'Chrome', 'HeadlessChrome', 'SailfishBrowser' ], $key, $browser) ) {
  142. $version = $result[BROWSER_VERSION][$key];
  143. } elseif( $rv_result && $find('Trident') ) {
  144. $browser = 'MSIE';
  145. $version = $rv_result;
  146. } elseif( $browser == 'AppleWebKit' ) {
  147. if( $platform == 'Android' ) {
  148. $browser = 'Android Browser';
  149. } elseif( strpos((string)$platform, 'BB') === 0 ) {
  150. $browser = 'BlackBerry Browser';
  151. $platform = 'BlackBerry';
  152. } elseif( $platform == 'BlackBerry' || $platform == 'PlayBook' ) {
  153. $browser = 'BlackBerry Browser';
  154. } else {
  155. $find('Safari', $key, $browser) || $find('TizenBrowser', $key, $browser);
  156. }
  157. $find('Version', $key);
  158. $version = $result[BROWSER_VERSION][$key];
  159. } elseif( $pKey = preg_grep('/playstation \d/i', $result[BROWSER]) ) {
  160. $pKey = reset($pKey);
  161. $platform = 'PlayStation ' . preg_replace('/\D/', '', $pKey);
  162. $browser = 'NetFront';
  163. }
  164. return $return;
  165. }
  166. }