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