JOOMLA中国
  • Joomla中国首页
  • 社区
  • 教程
  • 应用市场
  • B计划
Joomla! Framework TM
  • Namespace
  • Class
  • Tree
  • Deprecated

Namespaces

  • Composer
    • Autoload
  • Joomla
    • Application
      • Cli
        • Output
          • Processor
      • Web
    • Data
    • DI
      • Exception
    • Event
    • Filter
    • Input
    • Ldap
    • Registry
      • Format
    • Session
      • Storage
    • String
    • Uri
    • Utilities
  • None
  • PasswordCompat
    • binary
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Yaml
        • Exception
    • Polyfill
      • Util

Classes

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