|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Http_UserAgent |
|
17 * @subpackage UserAgent |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
20 */ |
|
21 |
|
22 /** |
|
23 * Lists of User Agent chains for testing : |
|
24 * |
|
25 * - http://www.useragentstring.com/layout/useragentstring.php |
|
26 * - http://user-agent-string.info/list-of-ua |
|
27 * - http://www.user-agents.org/allagents.xml |
|
28 * - http://en.wikipedia.org/wiki/List_of_user_agents_for_mobile_phones |
|
29 * - http://www.mobilemultimedia.be/fr/ |
|
30 * |
|
31 * @category Zend |
|
32 * @package Zend_Http_UserAgent |
|
33 * @subpackage UserAgent |
|
34 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
35 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
36 */ |
|
37 class Zend_Http_UserAgent implements Serializable |
|
38 { |
|
39 /** |
|
40 * 'desktop' by default if the sequence return false for each item or is empty |
|
41 */ |
|
42 const DEFAULT_IDENTIFICATION_SEQUENCE = 'mobile,desktop'; |
|
43 |
|
44 /** |
|
45 * Default persitent storage adapter : Session or NonPersitent |
|
46 */ |
|
47 const DEFAULT_PERSISTENT_STORAGE_ADAPTER = 'Session'; |
|
48 |
|
49 /** |
|
50 * 'desktop' by default if the sequence return false for each item |
|
51 */ |
|
52 const DEFAULT_BROWSER_TYPE = 'desktop'; |
|
53 |
|
54 /** |
|
55 * Default User Agent chain to prevent empty value |
|
56 */ |
|
57 const DEFAULT_HTTP_USER_AGENT = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)'; |
|
58 |
|
59 /** |
|
60 * Default Http Accept param to prevent empty value |
|
61 */ |
|
62 const DEFAULT_HTTP_ACCEPT = "application/xhtml+xml"; |
|
63 |
|
64 /** |
|
65 * Default markup language |
|
66 */ |
|
67 const DEFAULT_MARKUP_LANGUAGE = "xhtml"; |
|
68 |
|
69 /** |
|
70 * Browser type |
|
71 * |
|
72 * @var string |
|
73 */ |
|
74 protected $_browserType; |
|
75 |
|
76 /** |
|
77 * Browser type class |
|
78 * |
|
79 * Map of browser types to classes. |
|
80 * |
|
81 * @var array |
|
82 */ |
|
83 protected $_browserTypeClass = array(); |
|
84 |
|
85 /** |
|
86 * Array to store config |
|
87 * |
|
88 * Default values are provided to ensure specific keys are present at |
|
89 * instantiation. |
|
90 * |
|
91 * @var array |
|
92 */ |
|
93 protected $_config = array( |
|
94 'identification_sequence' => self::DEFAULT_IDENTIFICATION_SEQUENCE, |
|
95 'storage' => array( |
|
96 'adapter' => self::DEFAULT_PERSISTENT_STORAGE_ADAPTER, |
|
97 ), |
|
98 ); |
|
99 |
|
100 /** |
|
101 * Identified device |
|
102 * |
|
103 * @var Zend_Http_UserAgent_Device |
|
104 */ |
|
105 protected $_device; |
|
106 |
|
107 /** |
|
108 * Whether or not this instance is immutable. |
|
109 * |
|
110 * If true, none of the following may be modified: |
|
111 * - $_server |
|
112 * - $_browserType |
|
113 * - User-Agent (defined in $_server) |
|
114 * - HTTP Accept value (defined in $_server) |
|
115 * - $_storage |
|
116 * |
|
117 * @var bool |
|
118 */ |
|
119 protected $_immutable = false; |
|
120 |
|
121 /** |
|
122 * Plugin loaders |
|
123 * @var array |
|
124 */ |
|
125 protected $_loaders = array(); |
|
126 |
|
127 /** |
|
128 * Valid plugin loader types |
|
129 * @var array |
|
130 */ |
|
131 protected $_loaderTypes = array('storage', 'device'); |
|
132 |
|
133 /** |
|
134 * Trace of items matched to identify the browser type |
|
135 * |
|
136 * @var array |
|
137 */ |
|
138 protected $_matchLog = array(); |
|
139 |
|
140 /** |
|
141 * Server variable |
|
142 * |
|
143 * @var array |
|
144 */ |
|
145 protected $_server; |
|
146 |
|
147 /** |
|
148 * Persistent storage handler |
|
149 * |
|
150 * @var Zend_Http_UserAgent_Storage |
|
151 */ |
|
152 protected $_storage; |
|
153 |
|
154 /** |
|
155 * Constructor |
|
156 * |
|
157 * @param null|array|Zend_Config|ArrayAccess $options |
|
158 * @return void |
|
159 */ |
|
160 public function __construct($options = null) |
|
161 { |
|
162 if (null !== $options) { |
|
163 $this->setOptions($options); |
|
164 } |
|
165 } |
|
166 |
|
167 /** |
|
168 * Serialized representation of the object |
|
169 * |
|
170 * @return string |
|
171 */ |
|
172 public function serialize() |
|
173 { |
|
174 $spec = array( |
|
175 'browser_type' => $this->_browserType, |
|
176 'config' => $this->_config, |
|
177 'device_class' => get_class($this->_device), |
|
178 'device' => $this->_device->serialize(), |
|
179 'user_agent' => $this->getServerValue('http_user_agent'), |
|
180 'http_accept' => $this->getServerValue('http_accept'), |
|
181 ); |
|
182 return serialize($spec); |
|
183 } |
|
184 |
|
185 /** |
|
186 * Unserialize a previous representation of the object |
|
187 * |
|
188 * @param string $serialized |
|
189 * @return void |
|
190 */ |
|
191 public function unserialize($serialized) |
|
192 { |
|
193 $spec = unserialize($serialized); |
|
194 |
|
195 $this->setOptions($spec); |
|
196 |
|
197 // Determine device class and ensure the class is loaded |
|
198 $deviceClass = $spec['device_class']; |
|
199 if (!class_exists($deviceClass)) { |
|
200 $this->_getUserAgentDevice($this->getBrowserType()); |
|
201 } |
|
202 |
|
203 // Get device specification and instantiate |
|
204 $deviceSpec = unserialize($spec['device']); |
|
205 $deviceSpec['_config'] = $this->getConfig(); |
|
206 $deviceSpec['_server'] = $this->getServer(); |
|
207 $this->_device = new $deviceClass($deviceSpec); |
|
208 } |
|
209 |
|
210 /** |
|
211 * Configure instance |
|
212 * |
|
213 * @param array|Zend_Config|ArrayAccess $options |
|
214 * @return Zend_Http_UserAgent |
|
215 */ |
|
216 public function setOptions($options) |
|
217 { |
|
218 if ($options instanceof Zend_Config) { |
|
219 $options = $options->toArray(); |
|
220 } |
|
221 |
|
222 if (!is_array($options) |
|
223 && !$options instanceof ArrayAccess |
|
224 && !$options instanceof Traversable |
|
225 ) { |
|
226 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
227 throw new Zend_Http_UserAgent_Exception(sprintf( |
|
228 'Invalid argument; expected array, Zend_Config object, or object implementing ArrayAccess and Traversable; received %s', |
|
229 (is_object($options) ? get_class($options) : gettype($options)) |
|
230 )); |
|
231 } |
|
232 |
|
233 // Set $_SERVER first |
|
234 if (isset($options['server'])) { |
|
235 $this->setServer($options['server']); |
|
236 unset($options['server']); |
|
237 } |
|
238 |
|
239 // Get plugin loaders sorted |
|
240 if (isset($options['plugin_loader'])) { |
|
241 $plConfig = $options['plugin_loader']; |
|
242 if (is_array($plConfig) || $plConfig instanceof Traversable) { |
|
243 foreach ($plConfig as $type => $class) { |
|
244 $this->setPluginLoader($type, $class); |
|
245 } |
|
246 } |
|
247 unset($plConfig, $options['plugin_loader']); |
|
248 } |
|
249 |
|
250 // And then loop through the remaining options |
|
251 $config = array(); |
|
252 foreach ($options as $key => $value) { |
|
253 switch (strtolower($key)) { |
|
254 case 'browser_type': |
|
255 $this->setBrowserType($value); |
|
256 break; |
|
257 case 'http_accept': |
|
258 $this->setHttpAccept($value); |
|
259 break; |
|
260 case 'user_agent': |
|
261 $this->setUserAgent($value); |
|
262 break; |
|
263 default: |
|
264 // Cache remaining options for $_config |
|
265 $config[$key] = $value; |
|
266 break; |
|
267 } |
|
268 } |
|
269 $this->setConfig($config); |
|
270 |
|
271 return $this; |
|
272 } |
|
273 |
|
274 /** |
|
275 * Comparison of the UserAgent chain and browser signatures. |
|
276 * |
|
277 * The comparison is case-insensitive : the browser signatures must be in lower |
|
278 * case |
|
279 * |
|
280 * @param string $deviceClass Name of class against which a match will be attempted |
|
281 * @return bool |
|
282 */ |
|
283 protected function _match($deviceClass) |
|
284 { |
|
285 // Validate device class |
|
286 $r = new ReflectionClass($deviceClass); |
|
287 if (!$r->implementsInterface('Zend_Http_UserAgent_Device')) { |
|
288 throw new Zend_Http_UserAgent_Exception(sprintf( |
|
289 'Invalid device class provided ("%s"); must implement Zend_Http_UserAgent_Device', |
|
290 $deviceClass |
|
291 )); |
|
292 } |
|
293 |
|
294 $userAgent = $this->getUserAgent(); |
|
295 |
|
296 // Call match method on device class |
|
297 return call_user_func( |
|
298 array($deviceClass, 'match'), |
|
299 $userAgent, |
|
300 $this->getServer() |
|
301 ); |
|
302 } |
|
303 |
|
304 /** |
|
305 * Loads class for a user agent device |
|
306 * |
|
307 * @param string $browserType Browser type |
|
308 * @return string |
|
309 * @throws Zend_Loader_PluginLoader_Exception if unable to load UA device |
|
310 */ |
|
311 protected function _getUserAgentDevice($browserType) |
|
312 { |
|
313 $browserType = strtolower($browserType); |
|
314 if (isset($this->_browserTypeClass[$browserType])) { |
|
315 return $this->_browserTypeClass[$browserType]; |
|
316 } |
|
317 |
|
318 if (isset($this->_config[$browserType]) |
|
319 && isset($this->_config[$browserType]['device']) |
|
320 ) { |
|
321 $deviceConfig = $this->_config[$browserType]['device']; |
|
322 if (is_array($deviceConfig) && isset($deviceConfig['classname'])) { |
|
323 $device = (string) $deviceConfig['classname']; |
|
324 if (!class_exists($device)) { |
|
325 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
326 throw new Zend_Http_UserAgent_Exception(sprintf( |
|
327 'Invalid classname "%s" provided in device configuration for browser type "%s"', |
|
328 $device, |
|
329 $browserType |
|
330 )); |
|
331 } |
|
332 } elseif (is_array($deviceConfig) && isset($deviceConfig['path'])) { |
|
333 $loader = $this->getPluginLoader('device'); |
|
334 $path = $deviceConfig['path']; |
|
335 $prefix = isset($deviceConfig['prefix']) ? $deviceConfig['prefix'] : 'Zend_Http_UserAgent'; |
|
336 $loader->addPrefixPath($prefix, $path); |
|
337 |
|
338 $device = $loader->load($browserType); |
|
339 } else { |
|
340 $loader = $this->getPluginLoader('device'); |
|
341 $device = $loader->load($browserType); |
|
342 } |
|
343 } else { |
|
344 $loader = $this->getPluginLoader('device'); |
|
345 $device = $loader->load($browserType); |
|
346 } |
|
347 |
|
348 $this->_browserTypeClass[$browserType] = $device; |
|
349 |
|
350 return $device; |
|
351 } |
|
352 |
|
353 /** |
|
354 * Returns the User Agent value |
|
355 * |
|
356 * If $userAgent param is null, the value of $_server['HTTP_USER_AGENT'] is |
|
357 * returned. |
|
358 * |
|
359 * @return string |
|
360 */ |
|
361 public function getUserAgent() |
|
362 { |
|
363 if (null === ($ua = $this->getServerValue('http_user_agent'))) { |
|
364 $ua = self::DEFAULT_HTTP_USER_AGENT; |
|
365 $this->setUserAgent($ua); |
|
366 } |
|
367 |
|
368 return $ua; |
|
369 } |
|
370 |
|
371 /** |
|
372 * Force or replace the UA chain in $_server variable |
|
373 * |
|
374 * @param string $userAgent Forced UserAgent chain |
|
375 * @return Zend_Http_UserAgent |
|
376 */ |
|
377 public function setUserAgent($userAgent) |
|
378 { |
|
379 $this->setServerValue('http_user_agent', $userAgent); |
|
380 return $this; |
|
381 } |
|
382 |
|
383 /** |
|
384 * Returns the HTTP Accept server param |
|
385 * |
|
386 * @param string $httpAccept (option) forced HTTP Accept chain |
|
387 * @return string |
|
388 */ |
|
389 public function getHttpAccept($httpAccept = null) |
|
390 { |
|
391 if (null === ($accept = $this->getServerValue('http_accept'))) { |
|
392 $accept = self::DEFAULT_HTTP_ACCEPT; |
|
393 $this->setHttpAccept($accept); |
|
394 } |
|
395 return $accept; |
|
396 } |
|
397 |
|
398 /** |
|
399 * Force or replace the HTTP_ACCEPT chain in self::$_server variable |
|
400 * |
|
401 * @param string $httpAccept Forced HTTP Accept chain |
|
402 * @return Zend_Http_UserAgent |
|
403 */ |
|
404 public function setHttpAccept($httpAccept) |
|
405 { |
|
406 $this->setServerValue('http_accept', $httpAccept); |
|
407 return $this; |
|
408 } |
|
409 |
|
410 /** |
|
411 * Returns the persistent storage handler |
|
412 * |
|
413 * Session storage is used by default unless a different storage adapter |
|
414 * has been set via the "persistent_storage_adapter" key. That key should |
|
415 * contain either a fully qualified class name, or a short name that |
|
416 * resolves via the plugin loader. |
|
417 * |
|
418 * @param string $browser Browser identifier (User Agent chain) |
|
419 * @return Zend_Http_UserAgent_Storage |
|
420 */ |
|
421 public function getStorage($browser = null) |
|
422 { |
|
423 if (null === $browser) { |
|
424 $browser = $this->getUserAgent(); |
|
425 } |
|
426 if (null === $this->_storage) { |
|
427 $config = $this->_config['storage']; |
|
428 $adapter = $config['adapter']; |
|
429 if (!class_exists($adapter)) { |
|
430 $loader = $this->getPluginLoader('storage'); |
|
431 $adapter = $loader->load($adapter); |
|
432 $loader = $this->getPluginLoader('storage'); |
|
433 } |
|
434 $options = array('browser_type' => $browser); |
|
435 if (isset($config['options'])) { |
|
436 $options = array_merge($options, $config['options']); |
|
437 } |
|
438 $this->setStorage(new $adapter($options)); |
|
439 } |
|
440 return $this->_storage; |
|
441 } |
|
442 |
|
443 /** |
|
444 * Sets the persistent storage handler |
|
445 * |
|
446 * @param Zend_Http_UserAgent_Storage $storage |
|
447 * @return Zend_Http_UserAgent |
|
448 */ |
|
449 public function setStorage(Zend_Http_UserAgent_Storage $storage) |
|
450 { |
|
451 if ($this->_immutable) { |
|
452 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
453 throw new Zend_Http_UserAgent_Exception( |
|
454 'The User-Agent device object has already been retrieved; the storage object is now immutable' |
|
455 ); |
|
456 } |
|
457 |
|
458 $this->_storage = $storage; |
|
459 return $this; |
|
460 } |
|
461 |
|
462 /** |
|
463 * Clean the persistent storage |
|
464 * |
|
465 * @param string $browser Browser identifier (User Agent chain) |
|
466 * @return void |
|
467 */ |
|
468 public function clearStorage($browser = null) |
|
469 { |
|
470 $this->getStorage($browser)->clear(); |
|
471 } |
|
472 |
|
473 /** |
|
474 * Get user configuration |
|
475 * |
|
476 * @return array |
|
477 */ |
|
478 public function getConfig() |
|
479 { |
|
480 return $this->_config; |
|
481 } |
|
482 |
|
483 /** |
|
484 * Config parameters is an Array or a Zend_Config object |
|
485 * |
|
486 * The allowed parameters are : |
|
487 * - the identification sequence (can be empty) => desktop browser type is the |
|
488 * default browser type returned |
|
489 * $config['identification_sequence'] : ',' separated browser types |
|
490 * - the persistent storage adapter |
|
491 * $config['persistent_storage_adapter'] = "Session" or "NonPersistent" |
|
492 * - to add or replace a browser type device |
|
493 * $config[(type)]['device']['path'] |
|
494 * $config[(type)]['device']['classname'] |
|
495 * - to add or replace a browser type features adapter |
|
496 * $config[(type)]['features']['path'] |
|
497 * $config[(type)]['features']['classname'] |
|
498 * |
|
499 * @param mixed $config (option) Config array |
|
500 * @return Zend_Http_UserAgent |
|
501 */ |
|
502 public function setConfig($config = array()) |
|
503 { |
|
504 if ($config instanceof Zend_Config) { |
|
505 $config = $config->toArray(); |
|
506 } |
|
507 |
|
508 // Verify that Config parameters are in an array. |
|
509 if (!is_array($config) && !$config instanceof Traversable) { |
|
510 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
511 throw new Zend_Http_UserAgent_Exception(sprintf( |
|
512 'Config parameters must be in an array or a Traversable object; received "%s"', |
|
513 (is_object($config) ? get_class($config) : gettype($config)) |
|
514 )); |
|
515 } |
|
516 |
|
517 if ($config instanceof Traversable) { |
|
518 $tmp = array(); |
|
519 foreach ($config as $key => $value) { |
|
520 $tmp[$key] = $value; |
|
521 } |
|
522 $config = $tmp; |
|
523 unset($tmp); |
|
524 } |
|
525 |
|
526 $this->_config = array_merge($this->_config, $config); |
|
527 return $this; |
|
528 } |
|
529 |
|
530 /** |
|
531 * @return the $device |
|
532 */ |
|
533 public function getDevice() |
|
534 { |
|
535 if (null !== $this->_device) { |
|
536 return $this->_device; |
|
537 } |
|
538 |
|
539 $userAgent = $this->getUserAgent(); |
|
540 |
|
541 // search an existing identification in the session |
|
542 $storage = $this->getStorage($userAgent); |
|
543 |
|
544 if (!$storage->isEmpty()) { |
|
545 // If the user agent and features are already existing, the |
|
546 // Zend_Http_UserAgent object is serialized in the session |
|
547 $object = $storage->read(); |
|
548 $this->unserialize($object); |
|
549 } else { |
|
550 // Otherwise, the identification is made and stored in the session. |
|
551 // Find the browser type: |
|
552 $this->setBrowserType($this->_matchUserAgent()); |
|
553 $this->_createDevice(); |
|
554 |
|
555 // put the result in storage: |
|
556 $this->getStorage($userAgent) |
|
557 ->write($this->serialize()); |
|
558 } |
|
559 |
|
560 // Mark the object as immutable |
|
561 $this->_immutable = true; |
|
562 |
|
563 // Return the device instance |
|
564 return $this->_device; |
|
565 } |
|
566 |
|
567 /** |
|
568 * Retrieve the browser type |
|
569 * |
|
570 * @return string $browserType |
|
571 */ |
|
572 public function getBrowserType() |
|
573 { |
|
574 return $this->_browserType; |
|
575 } |
|
576 |
|
577 /** |
|
578 * Set the browser "type" |
|
579 * |
|
580 * @param string $browserType |
|
581 * @return Zend_Http_UserAgent |
|
582 */ |
|
583 public function setBrowserType($browserType) |
|
584 { |
|
585 if ($this->_immutable) { |
|
586 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
587 throw new Zend_Http_UserAgent_Exception( |
|
588 'The User-Agent device object has already been retrieved; the browser type is now immutable' |
|
589 ); |
|
590 } |
|
591 |
|
592 $this->_browserType = $browserType; |
|
593 return $this; |
|
594 } |
|
595 |
|
596 /** |
|
597 * Retrieve the "$_SERVER" array |
|
598 * |
|
599 * Basically, the $_SERVER array or an equivalent container storing the |
|
600 * data that will be introspected. |
|
601 * |
|
602 * If the value has not been previously set, it sets itself from the |
|
603 * $_SERVER superglobal. |
|
604 * |
|
605 * @return array |
|
606 */ |
|
607 public function getServer() |
|
608 { |
|
609 if (null === $this->_server) { |
|
610 $this->setServer($_SERVER); |
|
611 } |
|
612 return $this->_server; |
|
613 } |
|
614 |
|
615 /** |
|
616 * Retrieve the "$_SERVER" array |
|
617 * |
|
618 * Basically, the $_SERVER array or an equivalent container storing the |
|
619 * data that will be introspected. |
|
620 * |
|
621 * @param array|ArrayAccess $server |
|
622 * @return void |
|
623 * @throws Zend_Http_UserAgent_Exception on invalid parameter |
|
624 */ |
|
625 public function setServer($server) |
|
626 { |
|
627 if ($this->_immutable) { |
|
628 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
629 throw new Zend_Http_UserAgent_Exception( |
|
630 'The User-Agent device object has already been retrieved; the server array is now immutable' |
|
631 ); |
|
632 } |
|
633 |
|
634 if (!is_array($server) && !$server instanceof Traversable) { |
|
635 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
636 throw new Zend_Http_UserAgent_Exception(sprintf( |
|
637 'Expected an array or object implementing Traversable; received %s', |
|
638 (is_object($server) ? get_class($server) : gettype($server)) |
|
639 )); |
|
640 } |
|
641 |
|
642 // Get an array if we don't have one |
|
643 if ($server instanceof ArrayObject) { |
|
644 $server = $server->getArrayCopy(); |
|
645 } elseif ($server instanceof Traversable) { |
|
646 $tmp = array(); |
|
647 foreach ($server as $key => $value) { |
|
648 $tmp[$key] = $value; |
|
649 } |
|
650 $server = $tmp; |
|
651 unset($tmp); |
|
652 } |
|
653 |
|
654 // Normalize key case |
|
655 $server = array_change_key_case($server, CASE_LOWER); |
|
656 |
|
657 $this->_server = $server; |
|
658 return $this; |
|
659 } |
|
660 |
|
661 /** |
|
662 * Retrieve a server value |
|
663 * |
|
664 * @param string $key |
|
665 * @return mixed |
|
666 */ |
|
667 public function getServerValue($key) |
|
668 { |
|
669 $key = strtolower($key); |
|
670 $server = $this->getServer(); |
|
671 $return = null; |
|
672 if (isset($server[$key])) { |
|
673 $return = $server[$key]; |
|
674 } |
|
675 unset($server); |
|
676 return $return; |
|
677 } |
|
678 |
|
679 /** |
|
680 * Set a server value |
|
681 * |
|
682 * @param string|int|float $key |
|
683 * @param mixed $value |
|
684 * @return void |
|
685 */ |
|
686 public function setServerValue($key, $value) |
|
687 { |
|
688 if ($this->_immutable) { |
|
689 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
690 throw new Zend_Http_UserAgent_Exception( |
|
691 'The User-Agent device object has already been retrieved; the server array is now immutable' |
|
692 ); |
|
693 } |
|
694 |
|
695 $server = $this->getServer(); // ensure it's been initialized |
|
696 $key = strtolower($key); |
|
697 $this->_server[$key] = $value; |
|
698 return $this; |
|
699 } |
|
700 |
|
701 /** |
|
702 * Set plugin loader |
|
703 * |
|
704 * @param string $type Type of plugin loader; one of 'storage', (?) |
|
705 * @param string|Zend_Loader_PluginLoader $loader |
|
706 * @return Zend_Http_UserAgent |
|
707 */ |
|
708 public function setPluginLoader($type, $loader) |
|
709 { |
|
710 $type = $this->_validateLoaderType($type); |
|
711 |
|
712 if (is_string($loader)) { |
|
713 if (!class_exists($loader)) { |
|
714 require_once 'Zend/Loader.php'; |
|
715 Zend_Loader::loadClass($loader); |
|
716 } |
|
717 $loader = new $loader(); |
|
718 } elseif (!is_object($loader)) { |
|
719 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
720 throw new Zend_Http_UserAgent_Exception(sprintf( |
|
721 'Expected a plugin loader class or object; received %s', |
|
722 gettype($loader) |
|
723 )); |
|
724 } |
|
725 if (!$loader instanceof Zend_Loader_PluginLoader) { |
|
726 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
727 throw new Zend_Http_UserAgent_Exception(sprintf( |
|
728 'Expected an object extending Zend_Loader_PluginLoader; received %s', |
|
729 get_class($loader) |
|
730 )); |
|
731 } |
|
732 |
|
733 $basePrefix = 'Zend_Http_UserAgent_'; |
|
734 $basePath = 'Zend/Http/UserAgent/'; |
|
735 switch ($type) { |
|
736 case 'storage': |
|
737 $prefix = $basePrefix . 'Storage'; |
|
738 $path = $basePath . 'Storage'; |
|
739 break; |
|
740 case 'device': |
|
741 $prefix = $basePrefix; |
|
742 $path = $basePath; |
|
743 break; |
|
744 } |
|
745 $loader->addPrefixPath($prefix, $path); |
|
746 $this->_loaders[$type] = $loader; |
|
747 return $this; |
|
748 } |
|
749 |
|
750 /** |
|
751 * Get a plugin loader |
|
752 * |
|
753 * @param string $type A valid plugin loader type; see {@link $_loaderTypes} |
|
754 * @return Zend_Loader_PluginLoader |
|
755 */ |
|
756 public function getPluginLoader($type) |
|
757 { |
|
758 $type = $this->_validateLoaderType($type); |
|
759 if (!isset($this->_loaders[$type])) { |
|
760 require_once 'Zend/Loader/PluginLoader.php'; |
|
761 $this->setPluginLoader($type, new Zend_Loader_PluginLoader()); |
|
762 } |
|
763 return $this->_loaders[$type]; |
|
764 } |
|
765 |
|
766 /** |
|
767 * Validate a plugin loader type |
|
768 * |
|
769 * Verifies that it is in {@link $_loaderTypes}, and returns a normalized |
|
770 * version of the type. |
|
771 * |
|
772 * @param string $type |
|
773 * @return string |
|
774 * @throws Zend_Http_UserAgent_Exception on invalid type |
|
775 */ |
|
776 protected function _validateLoaderType($type) |
|
777 { |
|
778 $type = strtolower($type); |
|
779 if (!in_array($type, $this->_loaderTypes)) { |
|
780 $types = implode(', ', $this->_loaderTypes); |
|
781 |
|
782 require_once 'Zend/Http/UserAgent/Exception.php'; |
|
783 throw new Zend_Http_UserAgent_Exception(sprintf( |
|
784 'Expected one of "%s" for plugin loader type; received "%s"', |
|
785 $types, |
|
786 (string) $type |
|
787 )); |
|
788 } |
|
789 return $type; |
|
790 } |
|
791 |
|
792 /** |
|
793 * Run the identification sequence to match the right browser type according to the |
|
794 * user agent |
|
795 * |
|
796 * @return Zend_Http_UserAgent_Result |
|
797 */ |
|
798 protected function _matchUserAgent() |
|
799 { |
|
800 $type = self::DEFAULT_BROWSER_TYPE; |
|
801 |
|
802 // If we have no identification sequence, just return the default type |
|
803 if (empty($this->_config['identification_sequence'])) { |
|
804 return $type; |
|
805 } |
|
806 |
|
807 // Get sequence against which to match |
|
808 $sequence = explode(',', $this->_config['identification_sequence']); |
|
809 |
|
810 // If a browser type is already configured, push that to the front of the list |
|
811 if (null !== ($browserType = $this->getBrowserType())) { |
|
812 array_unshift($sequence, $browserType); |
|
813 } |
|
814 |
|
815 // Append the default browser type to the list if not alread in the list |
|
816 if (!in_array($type, $sequence)) { |
|
817 $sequence[] = $type; |
|
818 } |
|
819 |
|
820 // Test each type until we find a match |
|
821 foreach ($sequence as $browserType) { |
|
822 $browserType = trim($browserType); |
|
823 $className = $this->_getUserAgentDevice($browserType); |
|
824 |
|
825 // Attempt to match this device class |
|
826 if ($this->_match($className)) { |
|
827 $type = $browserType; |
|
828 $this->_browserTypeClass[$type] = $className; |
|
829 break; |
|
830 } |
|
831 } |
|
832 |
|
833 return $type; |
|
834 } |
|
835 |
|
836 /** |
|
837 * Creates device object instance |
|
838 * |
|
839 * @return void |
|
840 */ |
|
841 protected function _createDevice() |
|
842 { |
|
843 $browserType = $this->getBrowserType(); |
|
844 $classname = $this->_getUserAgentDevice($browserType); |
|
845 $this->_device = new $classname($this->getUserAgent(), $this->getServer(), $this->getConfig()); |
|
846 } |
|
847 } |