[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Part of the Joomla Framework Application Package 4 * 5 * @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved. 6 * @license GNU General Public License version 2 or later; see LICENSE 7 */ 8 9 namespace Joomla\Application\Web; 10 11 /** 12 * Class to model a Web Client. 13 * 14 * @property-read integer $platform The detected platform on which the web client runs. 15 * @property-read boolean $mobile True if the web client is a mobile device. 16 * @property-read integer $engine The detected rendering engine used by the web client. 17 * @property-read integer $browser The detected browser used by the web client. 18 * @property-read string $browserVersion The detected browser version used by the web client. 19 * @property-read array $languages The priority order detected accepted languages for the client. 20 * @property-read array $encodings The priority order detected accepted encodings for the client. 21 * @property-read string $userAgent The web client's user agent string. 22 * @property-read string $acceptEncoding The web client's accepted encoding string. 23 * @property-read string $acceptLanguage The web client's accepted languages string. 24 * @property-read array $detection An array of flags determining whether or not a detection routine has been run. 25 * @property-read boolean $robot True if the web client is a robot 26 * @property-read array $headers An array of all headers sent by client 27 * 28 * @since 1.0 29 */ 30 class WebClient 31 { 32 const WINDOWS = 1; 33 const WINDOWS_PHONE = 2; 34 const WINDOWS_CE = 3; 35 const IPHONE = 4; 36 const IPAD = 5; 37 const IPOD = 6; 38 const MAC = 7; 39 const BLACKBERRY = 8; 40 const ANDROID = 9; 41 const LINUX = 10; 42 const TRIDENT = 11; 43 const WEBKIT = 12; 44 const GECKO = 13; 45 const PRESTO = 14; 46 const KHTML = 15; 47 const AMAYA = 16; 48 const IE = 17; 49 const FIREFOX = 18; 50 const CHROME = 19; 51 const SAFARI = 20; 52 const OPERA = 21; 53 const ANDROIDTABLET = 22; 54 const EDGE = 23; 55 const BLINK = 24; 56 const EDG = 25; 57 58 /** 59 * The detected platform on which the web client runs. 60 * 61 * @var integer 62 * @since 1.0 63 */ 64 protected $platform; 65 66 /** 67 * True if the web client is a mobile device. 68 * 69 * @var boolean 70 * @since 1.0 71 */ 72 protected $mobile = false; 73 74 /** 75 * The detected rendering engine used by the web client. 76 * 77 * @var integer 78 * @since 1.0 79 */ 80 protected $engine; 81 82 /** 83 * The detected browser used by the web client. 84 * 85 * @var integer 86 * @since 1.0 87 */ 88 protected $browser; 89 90 /** 91 * The detected browser version used by the web client. 92 * 93 * @var string 94 * @since 1.0 95 */ 96 protected $browserVersion; 97 98 /** 99 * The priority order detected accepted languages for the client. 100 * 101 * @var array 102 * @since 1.0 103 */ 104 protected $languages = []; 105 106 /** 107 * The priority order detected accepted encodings for the client. 108 * 109 * @var array 110 * @since 1.0 111 */ 112 protected $encodings = []; 113 114 /** 115 * The web client's user agent string. 116 * 117 * @var string 118 * @since 1.0 119 */ 120 protected $userAgent; 121 122 /** 123 * The web client's accepted encoding string. 124 * 125 * @var string 126 * @since 1.0 127 */ 128 protected $acceptEncoding; 129 130 /** 131 * The web client's accepted languages string. 132 * 133 * @var string 134 * @since 1.0 135 */ 136 protected $acceptLanguage; 137 138 /** 139 * True if the web client is a robot. 140 * 141 * @var boolean 142 * @since 1.0 143 */ 144 protected $robot = false; 145 146 /** 147 * An array of flags determining whether or not a detection routine has been run. 148 * 149 * @var array 150 * @since 1.0 151 */ 152 protected $detection = []; 153 154 /** 155 * An array of headers sent by client. 156 * 157 * @var array 158 * @since 1.3.0 159 */ 160 protected $headers; 161 162 /** 163 * Class constructor. 164 * 165 * @param string $userAgent The optional user-agent string to parse. 166 * @param string $acceptEncoding The optional client accept encoding string to parse. 167 * @param string $acceptLanguage The optional client accept language string to parse. 168 * 169 * @since 1.0 170 */ 171 public function __construct($userAgent = null, $acceptEncoding = null, $acceptLanguage = null) 172 { 173 // If no explicit user agent string was given attempt to use the implicit one from server environment. 174 if (empty($userAgent) && isset($_SERVER['HTTP_USER_AGENT'])) 175 { 176 $this->userAgent = $_SERVER['HTTP_USER_AGENT']; 177 } 178 else 179 { 180 $this->userAgent = $userAgent; 181 } 182 183 // If no explicit acceptable encoding string was given attempt to use the implicit one from server environment. 184 if (empty($acceptEncoding) && isset($_SERVER['HTTP_ACCEPT_ENCODING'])) 185 { 186 $this->acceptEncoding = $_SERVER['HTTP_ACCEPT_ENCODING']; 187 } 188 else 189 { 190 $this->acceptEncoding = $acceptEncoding; 191 } 192 193 // If no explicit acceptable languages string was given attempt to use the implicit one from server environment. 194 if (empty($acceptLanguage) && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) 195 { 196 $this->acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE']; 197 } 198 else 199 { 200 $this->acceptLanguage = $acceptLanguage; 201 } 202 } 203 204 /** 205 * Magic method to get an object property's value by name. 206 * 207 * @param string $name Name of the property for which to return a value. 208 * 209 * @return mixed The requested value if it exists. 210 * 211 * @since 1.0 212 */ 213 public function __get($name) 214 { 215 switch ($name) 216 { 217 case 'mobile': 218 case 'platform': 219 if (empty($this->detection['platform'])) 220 { 221 $this->detectPlatform($this->userAgent); 222 } 223 224 break; 225 226 case 'engine': 227 if (empty($this->detection['engine'])) 228 { 229 $this->detectEngine($this->userAgent); 230 } 231 232 break; 233 234 case 'browser': 235 case 'browserVersion': 236 if (empty($this->detection['browser'])) 237 { 238 $this->detectBrowser($this->userAgent); 239 } 240 241 break; 242 243 case 'languages': 244 if (empty($this->detection['acceptLanguage'])) 245 { 246 $this->detectLanguage($this->acceptLanguage); 247 } 248 249 break; 250 251 case 'encodings': 252 if (empty($this->detection['acceptEncoding'])) 253 { 254 $this->detectEncoding($this->acceptEncoding); 255 } 256 257 break; 258 259 case 'robot': 260 if (empty($this->detection['robot'])) 261 { 262 $this->detectRobot($this->userAgent); 263 } 264 265 break; 266 267 case 'headers': 268 if (empty($this->detection['headers'])) 269 { 270 $this->detectHeaders(); 271 } 272 273 break; 274 } 275 276 // Return the property if it exists. 277 if (property_exists($this, $name)) 278 { 279 return $this->$name; 280 } 281 282 $trace = debug_backtrace(); 283 trigger_error( 284 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], 285 E_USER_NOTICE 286 ); 287 } 288 289 /** 290 * Detects the client browser and version in a user agent string. 291 * 292 * @param string $userAgent The user-agent string to parse. 293 * 294 * @return void 295 * 296 * @since 1.0 297 */ 298 protected function detectBrowser($userAgent) 299 { 300 $patternBrowser = ''; 301 302 // Attempt to detect the browser type. Obviously we are only worried about major browsers. 303 if ((stripos($userAgent, 'MSIE') !== false) && (stripos($userAgent, 'Opera') === false)) 304 { 305 $this->browser = self::IE; 306 $patternBrowser = 'MSIE'; 307 } 308 elseif (stripos($userAgent, 'Trident') !== false) 309 { 310 $this->browser = self::IE; 311 $patternBrowser = ' rv'; 312 } 313 elseif (stripos($userAgent, 'Edge') !== false) 314 { 315 $this->browser = self::EDGE; 316 $patternBrowser = 'Edge'; 317 } 318 elseif (stripos($userAgent, 'Edg') !== false) 319 { 320 $this->browser = self::EDG; 321 $patternBrowser = 'Edg'; 322 } 323 elseif ((stripos($userAgent, 'Firefox') !== false) && (stripos($userAgent, 'like Firefox') === false)) 324 { 325 $this->browser = self::FIREFOX; 326 $patternBrowser = 'Firefox'; 327 } 328 elseif (stripos($userAgent, 'OPR') !== false) 329 { 330 $this->browser = self::OPERA; 331 $patternBrowser = 'OPR'; 332 } 333 elseif (stripos($userAgent, 'Chrome') !== false) 334 { 335 $this->browser = self::CHROME; 336 $patternBrowser = 'Chrome'; 337 } 338 elseif (stripos($userAgent, 'Safari') !== false) 339 { 340 $this->browser = self::SAFARI; 341 $patternBrowser = 'Safari'; 342 } 343 elseif (stripos($userAgent, 'Opera') !== false) 344 { 345 $this->browser = self::OPERA; 346 $patternBrowser = 'Opera'; 347 } 348 349 // If we detected a known browser let's attempt to determine the version. 350 if ($this->browser) 351 { 352 // Build the REGEX pattern to match the browser version string within the user agent string. 353 $pattern = '#(?<browser>Version|' . $patternBrowser . ')[/ :]+(?<version>[0-9.|a-zA-Z.]*)#'; 354 355 // Attempt to find version strings in the user agent string. 356 $matches = []; 357 358 if (preg_match_all($pattern, $userAgent, $matches)) 359 { 360 // Do we have both a Version and browser match? 361 if (\count($matches['browser']) == 2) 362 { 363 // See whether Version or browser came first, and use the number accordingly. 364 if (strripos($userAgent, 'Version') < strripos($userAgent, $patternBrowser)) 365 { 366 $this->browserVersion = $matches['version'][0]; 367 } 368 else 369 { 370 $this->browserVersion = $matches['version'][1]; 371 } 372 } 373 elseif (\count($matches['browser']) > 2) 374 { 375 $key = array_search('Version', $matches['browser']); 376 377 if ($key) 378 { 379 $this->browserVersion = $matches['version'][$key]; 380 } 381 } 382 else 383 { 384 // We only have a Version or a browser so use what we have. 385 $this->browserVersion = $matches['version'][0]; 386 } 387 } 388 } 389 390 // Mark this detection routine as run. 391 $this->detection['browser'] = true; 392 } 393 394 /** 395 * Method to detect the accepted response encoding by the client. 396 * 397 * @param string $acceptEncoding The client accept encoding string to parse. 398 * 399 * @return void 400 * 401 * @since 1.0 402 */ 403 protected function detectEncoding($acceptEncoding) 404 { 405 // Parse the accepted encodings. 406 $this->encodings = array_map('trim', (array) explode(',', $acceptEncoding)); 407 408 // Mark this detection routine as run. 409 $this->detection['acceptEncoding'] = true; 410 } 411 412 /** 413 * Detects the client rendering engine in a user agent string. 414 * 415 * @param string $userAgent The user-agent string to parse. 416 * 417 * @return void 418 * 419 * @since 1.0 420 */ 421 protected function detectEngine($userAgent) 422 { 423 if (stripos($userAgent, 'MSIE') !== false || stripos($userAgent, 'Trident') !== false) 424 { 425 // Attempt to detect the client engine -- starting with the most popular ... for now. 426 $this->engine = self::TRIDENT; 427 } 428 elseif (stripos($userAgent, 'Edge') !== false || stripos($userAgent, 'EdgeHTML') !== false) 429 { 430 $this->engine = self::EDGE; 431 } 432 elseif (stripos($userAgent, 'Edg') !== false) 433 { 434 $this->engine = self::BLINK; 435 } 436 elseif (stripos($userAgent, 'Chrome') !== false) 437 { 438 $result = explode('/', stristr($userAgent, 'Chrome')); 439 $version = explode(' ', $result[1]); 440 441 if ($version[0] >= 28) 442 { 443 $this->engine = self::BLINK; 444 } 445 else 446 { 447 $this->engine = self::WEBKIT; 448 } 449 } 450 elseif (stripos($userAgent, 'AppleWebKit') !== false || stripos($userAgent, 'blackberry') !== false) 451 { 452 if (stripos($userAgent, 'AppleWebKit') !== false) 453 { 454 $result = explode('/', stristr($userAgent, 'AppleWebKit')); 455 $version = explode(' ', $result[1]); 456 457 if ($version[0] === 537.36) 458 { 459 // AppleWebKit/537.36 is Blink engine specific, exception is Blink emulated IEMobile, Trident or Edge 460 $this->engine = self::BLINK; 461 } 462 } 463 464 // Evidently blackberry uses WebKit and doesn't necessarily report it. Bad RIM. 465 $this->engine = self::WEBKIT; 466 } 467 elseif (stripos($userAgent, 'Gecko') !== false && stripos($userAgent, 'like Gecko') === false) 468 { 469 // We have to check for like Gecko because some other browsers spoof Gecko. 470 $this->engine = self::GECKO; 471 } 472 elseif (stripos($userAgent, 'Opera') !== false || stripos($userAgent, 'Presto') !== false) 473 { 474 $version = false; 475 476 if (preg_match('/Opera[\/| ]?([0-9.]+)/u', $userAgent, $match)) 477 { 478 $version = (float) ($match[1]); 479 } 480 481 if (preg_match('/Version\/([0-9.]+)/u', $userAgent, $match)) 482 { 483 if ((float) ($match[1]) >= 10) 484 { 485 $version = (float) ($match[1]); 486 } 487 } 488 489 if ($version !== false && $version >= 15) 490 { 491 $this->engine = self::BLINK; 492 } 493 else 494 { 495 $this->engine = self::PRESTO; 496 } 497 } 498 elseif (stripos($userAgent, 'KHTML') !== false) 499 { 500 // *sigh* 501 $this->engine = self::KHTML; 502 } 503 elseif (stripos($userAgent, 'Amaya') !== false) 504 { 505 // Lesser known engine but it finishes off the major list from Wikipedia :-) 506 $this->engine = self::AMAYA; 507 } 508 509 // Mark this detection routine as run. 510 $this->detection['engine'] = true; 511 } 512 513 /** 514 * Method to detect the accepted languages by the client. 515 * 516 * @param mixed $acceptLanguage The client accept language string to parse. 517 * 518 * @return void 519 * 520 * @since 1.0 521 */ 522 protected function detectLanguage($acceptLanguage) 523 { 524 // Parse the accepted encodings. 525 $this->languages = array_map('trim', (array) explode(',', $acceptLanguage)); 526 527 // Mark this detection routine as run. 528 $this->detection['acceptLanguage'] = true; 529 } 530 531 /** 532 * Detects the client platform in a user agent string. 533 * 534 * @param string $userAgent The user-agent string to parse. 535 * 536 * @return void 537 * 538 * @since 1.0 539 */ 540 protected function detectPlatform($userAgent) 541 { 542 // Attempt to detect the client platform. 543 if (stripos($userAgent, 'Windows') !== false) 544 { 545 $this->platform = self::WINDOWS; 546 547 // Let's look at the specific mobile options in the Windows space. 548 if (stripos($userAgent, 'Windows Phone') !== false) 549 { 550 $this->mobile = true; 551 $this->platform = self::WINDOWS_PHONE; 552 } 553 elseif (stripos($userAgent, 'Windows CE') !== false) 554 { 555 $this->mobile = true; 556 $this->platform = self::WINDOWS_CE; 557 } 558 } 559 elseif (stripos($userAgent, 'iPhone') !== false) 560 { 561 // Interestingly 'iPhone' is present in all iOS devices so far including iPad and iPods. 562 $this->mobile = true; 563 $this->platform = self::IPHONE; 564 565 // Let's look at the specific mobile options in the iOS space. 566 if (stripos($userAgent, 'iPad') !== false) 567 { 568 $this->platform = self::IPAD; 569 } 570 elseif (stripos($userAgent, 'iPod') !== false) 571 { 572 $this->platform = self::IPOD; 573 } 574 } 575 elseif (stripos($userAgent, 'iPad') !== false) 576 { 577 // In case where iPhone is not mentioed in iPad user agent string 578 $this->mobile = true; 579 $this->platform = self::IPAD; 580 } 581 elseif (stripos($userAgent, 'iPod') !== false) 582 { 583 // In case where iPhone is not mentioed in iPod user agent string 584 $this->mobile = true; 585 $this->platform = self::IPOD; 586 } 587 elseif (preg_match('/macintosh|mac os x/i', $userAgent)) 588 { 589 // This has to come after the iPhone check because mac strings are also present in iOS devices. 590 $this->platform = self::MAC; 591 } 592 elseif (stripos($userAgent, 'Blackberry') !== false) 593 { 594 $this->mobile = true; 595 $this->platform = self::BLACKBERRY; 596 } 597 elseif (stripos($userAgent, 'Android') !== false) 598 { 599 $this->mobile = true; 600 $this->platform = self::ANDROID; 601 602 /* 603 * Attempt to distinguish between Android phones and tablets 604 * There is no totally foolproof method but certain rules almost always hold 605 * Android 3.x is only used for tablets 606 * Some devices and browsers encourage users to change their UA string to include Tablet. 607 * Google encourages manufacturers to exclude the string Mobile from tablet device UA strings. 608 * In some modes Kindle Android devices include the string Mobile but they include the string Silk. 609 */ 610 if (stripos($userAgent, 'Android 3') !== false || stripos($userAgent, 'Tablet') !== false 611 || stripos($userAgent, 'Mobile') === false || stripos($userAgent, 'Silk') !== false) 612 { 613 $this->platform = self::ANDROIDTABLET; 614 } 615 } 616 elseif (stripos($userAgent, 'Linux') !== false) 617 { 618 $this->platform = self::LINUX; 619 } 620 621 // Mark this detection routine as run. 622 $this->detection['platform'] = true; 623 } 624 625 /** 626 * Determines if the browser is a robot or not. 627 * 628 * @param string $userAgent The user-agent string to parse. 629 * 630 * @return void 631 * 632 * @since 1.0 633 */ 634 protected function detectRobot($userAgent) 635 { 636 $this->robot = (bool) preg_match('/http|bot|robot|spider|crawler|curl|^$/i', $userAgent); 637 638 $this->detection['robot'] = true; 639 } 640 641 /** 642 * Fills internal array of headers 643 * 644 * @return void 645 * 646 * @since 1.3.0 647 */ 648 protected function detectHeaders() 649 { 650 if (\function_exists('getallheaders')) 651 { 652 // If php is working under Apache, there is a special function 653 $this->headers = getallheaders(); 654 } 655 else 656 { 657 // Else we fill headers from $_SERVER variable 658 $this->headers = []; 659 660 foreach ($_SERVER as $name => $value) 661 { 662 if (substr($name, 0, 5) == 'HTTP_') 663 { 664 $this->headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; 665 } 666 } 667 } 668 669 // Mark this detection routine as run. 670 $this->detection['headers'] = true; 671 } 672 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |