2
0

UserAgent.php 25 KB

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