File indexing completed on 2024-05-12 06:02:42

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Http_UserAgent
0017  * @subpackage UserAgent
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  */
0021 
0022 /**
0023  * Lists of User Agent chains for testing :
0024  *
0025  * - http://www.useragentstring.com/layout/useragentstring.php
0026  * - http://user-agent-string.info/list-of-ua
0027  * - http://www.user-agents.org/allagents.xml
0028  * - http://en.wikipedia.org/wiki/List_of_user_agents_for_mobile_phones
0029  * - http://www.mobilemultimedia.be/fr/
0030  *
0031  * @category   Zend
0032  * @package    Zend_Http_UserAgent
0033  * @subpackage UserAgent
0034  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0035  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0036  */
0037 class Zend_Http_UserAgent implements Serializable
0038 {
0039     /**
0040      * 'desktop' by default if the sequence return false for each item or is empty
0041      */
0042     const DEFAULT_IDENTIFICATION_SEQUENCE = 'mobile,desktop';
0043 
0044     /**
0045      * Default persitent storage adapter : Session or NonPersitent
0046      */
0047     const DEFAULT_PERSISTENT_STORAGE_ADAPTER = 'Session';
0048 
0049     /**
0050      * 'desktop' by default if the sequence return false for each item
0051      */
0052     const DEFAULT_BROWSER_TYPE = 'desktop';
0053 
0054     /**
0055      * Default User Agent chain to prevent empty value
0056      */
0057     const DEFAULT_HTTP_USER_AGENT = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)';
0058 
0059     /**
0060      * Default Http Accept param to prevent empty value
0061      */
0062     const DEFAULT_HTTP_ACCEPT = "application/xhtml+xml";
0063 
0064     /**
0065      * Default markup language
0066      */
0067     const DEFAULT_MARKUP_LANGUAGE = "xhtml";
0068 
0069     /**
0070      * Browser type
0071      *
0072      * @var string
0073      */
0074     protected $_browserType;
0075 
0076     /**
0077      * Browser type class
0078      *
0079      * Map of browser types to classes.
0080      *
0081      * @var array
0082      */
0083     protected $_browserTypeClass = array();
0084 
0085     /**
0086      * Array to store config
0087      *
0088      * Default values are provided to ensure specific keys are present at
0089      * instantiation.
0090      *
0091      * @var array
0092      */
0093     protected $_config = array(
0094         'identification_sequence' => self::DEFAULT_IDENTIFICATION_SEQUENCE,
0095         'storage'                 => array(
0096             'adapter'             => self::DEFAULT_PERSISTENT_STORAGE_ADAPTER,
0097         ),
0098     );
0099 
0100     /**
0101      * Identified device
0102      *
0103      * @var Zend_Http_UserAgent_Device
0104      */
0105     protected $_device;
0106 
0107     /**
0108      * Whether or not this instance is immutable.
0109      *
0110      * If true, none of the following may be modified:
0111      * - $_server
0112      * - $_browserType
0113      * - User-Agent (defined in $_server)
0114      * - HTTP Accept value (defined in $_server)
0115      * - $_storage
0116      *
0117      * @var bool
0118      */
0119     protected $_immutable = false;
0120 
0121     /**
0122      * Plugin loaders
0123      * @var array
0124      */
0125     protected $_loaders = array();
0126 
0127     /**
0128      * Valid plugin loader types
0129      * @var array
0130      */
0131     protected $_loaderTypes = array('storage', 'device');
0132 
0133     /**
0134      * Trace of items matched to identify the browser type
0135      *
0136      * @var array
0137      */
0138     protected $_matchLog = array();
0139 
0140     /**
0141      * Server variable
0142      *
0143      * @var array
0144      */
0145     protected $_server;
0146 
0147     /**
0148      * Persistent storage handler
0149      *
0150      * @var Zend_Http_UserAgent_Storage
0151      */
0152     protected $_storage;
0153 
0154     /**
0155      * Constructor
0156      *
0157      * @param  null|array|Zend_Config|ArrayAccess $options
0158      * @return void
0159      */
0160     public function __construct($options = null)
0161     {
0162         if (null !== $options) {
0163             $this->setOptions($options);
0164         }
0165     }
0166 
0167     /**
0168      * Serialized representation of the object
0169      *
0170      * @return string
0171      */
0172     public function serialize()
0173     {
0174         $device = $this->getDevice();
0175         $spec = array(
0176             'browser_type' => $this->_browserType,
0177             'config'       => $this->_config,
0178             'device_class' => get_class($device),
0179             'device'       => $device->serialize(),
0180             'user_agent'   => $this->getServerValue('http_user_agent'),
0181             'http_accept'  => $this->getServerValue('http_accept'),
0182         );
0183         return serialize($spec);
0184     }
0185 
0186     /**
0187      * Unserialize a previous representation of the object
0188      *
0189      * @param  string $serialized
0190      * @return void
0191      */
0192     public function unserialize($serialized)
0193     {
0194         $spec = unserialize($serialized);
0195 
0196         $this->setOptions($spec);
0197 
0198         // Determine device class and ensure the class is loaded
0199         $deviceClass          = $spec['device_class'];
0200         if (!class_exists($deviceClass)) {
0201             $this->_getUserAgentDevice($this->getBrowserType());
0202         }
0203 
0204         // Get device specification and instantiate
0205         $deviceSpec            = unserialize($spec['device']);
0206         $deviceSpec['_config'] = $this->getConfig();
0207         $deviceSpec['_server'] = $this->getServer();
0208         $this->_device = new $deviceClass($deviceSpec);
0209     }
0210 
0211     /**
0212      * Configure instance
0213      *
0214      * @param  array|Zend_Config|ArrayAccess $options
0215      * @return Zend_Http_UserAgent
0216      */
0217     public function setOptions($options)
0218     {
0219         if ($options instanceof Zend_Config) {
0220             $options = $options->toArray();
0221         }
0222 
0223         if (!is_array($options)
0224             && !$options instanceof ArrayAccess
0225             && !$options instanceof Traversable
0226         ) {
0227             // require_once 'Zend/Http/UserAgent/Exception.php';
0228             throw new Zend_Http_UserAgent_Exception(sprintf(
0229                 'Invalid argument; expected array, Zend_Config object, or object implementing ArrayAccess and Traversable; received %s',
0230                 (is_object($options) ? get_class($options) : gettype($options))
0231             ));
0232         }
0233 
0234         // Set $_SERVER first
0235         if (isset($options['server'])) {
0236             $this->setServer($options['server']);
0237             unset($options['server']);
0238         }
0239 
0240         // Get plugin loaders sorted
0241         if (isset($options['plugin_loader'])) {
0242             $plConfig = $options['plugin_loader'];
0243             if (is_array($plConfig) || $plConfig instanceof Traversable) {
0244                 foreach ($plConfig as $type => $class) {
0245                     $this->setPluginLoader($type, $class);
0246                 }
0247             }
0248             unset($plConfig, $options['plugin_loader']);
0249         }
0250 
0251         // And then loop through the remaining options
0252         $config = array();
0253         foreach ($options as $key => $value) {
0254             switch (strtolower($key)) {
0255                 case 'browser_type':
0256                     $this->setBrowserType($value);
0257                     break;
0258                 case 'http_accept':
0259                     $this->setHttpAccept($value);
0260                     break;
0261                 case 'user_agent':
0262                     $this->setUserAgent($value);
0263                     break;
0264                 default:
0265                     // Cache remaining options for $_config
0266                     $config[$key] = $value;
0267                     break;
0268             }
0269         }
0270         $this->setConfig($config);
0271 
0272         return $this;
0273     }
0274 
0275     /**
0276      * Comparison of the UserAgent chain and browser signatures.
0277      *
0278      * The comparison is case-insensitive : the browser signatures must be in lower
0279      * case
0280      *
0281      * @param  string $deviceClass Name of class against which a match will be attempted
0282      * @return bool
0283      */
0284     protected function _match($deviceClass)
0285     {
0286         // Validate device class
0287         $r = new ReflectionClass($deviceClass);
0288         if (!$r->implementsInterface('Zend_Http_UserAgent_Device')) {
0289             throw new Zend_Http_UserAgent_Exception(sprintf(
0290                 'Invalid device class provided ("%s"); must implement Zend_Http_UserAgent_Device',
0291                 $deviceClass
0292             ));
0293         }
0294 
0295         $userAgent = $this->getUserAgent();
0296 
0297         // Call match method on device class
0298         return call_user_func(
0299             array($deviceClass, 'match'),
0300             $userAgent,
0301             $this->getServer()
0302         );
0303     }
0304 
0305     /**
0306      * Loads class for a user agent device
0307      *
0308      * @param  string $browserType Browser type
0309      * @return string
0310      * @throws Zend_Loader_PluginLoader_Exception if unable to load UA device
0311      */
0312     protected function _getUserAgentDevice($browserType)
0313     {
0314         $browserType = strtolower($browserType);
0315         if (isset($this->_browserTypeClass[$browserType])) {
0316             return $this->_browserTypeClass[$browserType];
0317         }
0318 
0319         if (isset($this->_config[$browserType])
0320             && isset($this->_config[$browserType]['device'])
0321         ) {
0322             $deviceConfig = $this->_config[$browserType]['device'];
0323             if (is_array($deviceConfig) && isset($deviceConfig['classname'])) {
0324                 $device = (string) $deviceConfig['classname'];
0325                 if (!class_exists($device)) {
0326                     // require_once 'Zend/Http/UserAgent/Exception.php';
0327                     throw new Zend_Http_UserAgent_Exception(sprintf(
0328                         'Invalid classname "%s" provided in device configuration for browser type "%s"',
0329                         $device,
0330                         $browserType
0331                     ));
0332                 }
0333             } elseif (is_array($deviceConfig) && isset($deviceConfig['path'])) {
0334                 $loader = $this->getPluginLoader('device');
0335                 $path   = $deviceConfig['path'];
0336                 $prefix = isset($deviceConfig['prefix']) ? $deviceConfig['prefix'] : 'Zend_Http_UserAgent';
0337                 $loader->addPrefixPath($prefix, $path);
0338 
0339                 $device = $loader->load($browserType);
0340             } else {
0341                 $loader = $this->getPluginLoader('device');
0342                 $device = $loader->load($browserType);
0343             }
0344         } else {
0345             $loader = $this->getPluginLoader('device');
0346             $device = $loader->load($browserType);
0347         }
0348 
0349         $this->_browserTypeClass[$browserType] = $device;
0350 
0351         return $device;
0352     }
0353 
0354     /**
0355      * Returns the User Agent value
0356      *
0357      * If $userAgent param is null, the value of $_server['HTTP_USER_AGENT'] is
0358      * returned.
0359      *
0360      * @return string
0361      */
0362     public function getUserAgent()
0363     {
0364         if (null === ($ua = $this->getServerValue('http_user_agent'))) {
0365             $ua = self::DEFAULT_HTTP_USER_AGENT;
0366             $this->setUserAgent($ua);
0367         }
0368 
0369         return $ua;
0370     }
0371 
0372     /**
0373      * Force or replace the UA chain in $_server variable
0374      *
0375      * @param  string $userAgent Forced UserAgent chain
0376      * @return Zend_Http_UserAgent
0377      */
0378     public function setUserAgent($userAgent)
0379     {
0380         $this->setServerValue('http_user_agent', $userAgent);
0381         return $this;
0382     }
0383 
0384     /**
0385      * Returns the HTTP Accept server param
0386      *
0387      * @param  string $httpAccept (option) forced HTTP Accept chain
0388      * @return string
0389      */
0390     public function getHttpAccept($httpAccept = null)
0391     {
0392         if (null === ($accept = $this->getServerValue('http_accept'))) {
0393             $accept = self::DEFAULT_HTTP_ACCEPT;
0394             $this->setHttpAccept($accept);
0395         }
0396         return $accept;
0397     }
0398 
0399     /**
0400      * Force or replace the HTTP_ACCEPT chain in self::$_server variable
0401      *
0402      * @param  string $httpAccept Forced HTTP Accept chain
0403      * @return Zend_Http_UserAgent
0404      */
0405     public function setHttpAccept($httpAccept)
0406     {
0407         $this->setServerValue('http_accept', $httpAccept);
0408         return $this;
0409     }
0410 
0411     /**
0412      * Returns the persistent storage handler
0413      *
0414      * Session storage is used by default unless a different storage adapter
0415      * has been set via the "persistent_storage_adapter" key. That key should
0416      * contain either a fully qualified class name, or a short name that
0417      * resolves via the plugin loader.
0418      *
0419      * @param  string $browser Browser identifier (User Agent chain)
0420      * @return Zend_Http_UserAgent_Storage
0421      */
0422     public function getStorage($browser = null)
0423     {
0424         if (null === $browser) {
0425             $browser = $this->getUserAgent();
0426         }
0427         if (null === $this->_storage) {
0428             $config  = $this->_config['storage'];
0429             $adapter = $config['adapter'];
0430             if (!class_exists($adapter)) {
0431                 $loader = $this->getPluginLoader('storage');
0432                 $adapter = $loader->load($adapter);
0433                 $loader = $this->getPluginLoader('storage');
0434             }
0435             $options = array('browser_type' => $browser);
0436             if (isset($config['options'])) {
0437                 $options = array_merge($options, $config['options']);
0438             }
0439             $this->setStorage(new $adapter($options));
0440         }
0441         return $this->_storage;
0442     }
0443 
0444     /**
0445      * Sets the persistent storage handler
0446      *
0447      * @param  Zend_Http_UserAgent_Storage $storage
0448      * @return Zend_Http_UserAgent
0449      */
0450     public function setStorage(Zend_Http_UserAgent_Storage $storage)
0451     {
0452         if ($this->_immutable) {
0453             // require_once 'Zend/Http/UserAgent/Exception.php';
0454             throw new Zend_Http_UserAgent_Exception(
0455                 'The User-Agent device object has already been retrieved; the storage object is now immutable'
0456             );
0457         }
0458 
0459         $this->_storage = $storage;
0460         return $this;
0461     }
0462 
0463     /**
0464      * Clean the persistent storage
0465      *
0466      * @param  string $browser Browser identifier (User Agent chain)
0467      * @return void
0468      */
0469     public function clearStorage($browser = null)
0470     {
0471         $this->getStorage($browser)->clear();
0472     }
0473 
0474     /**
0475      * Get user configuration
0476      *
0477      * @return array
0478      */
0479     public function getConfig()
0480     {
0481         return $this->_config;
0482     }
0483 
0484     /**
0485      * Config parameters is an Array or a Zend_Config object
0486      *
0487      * The allowed parameters are :
0488      * - the identification sequence (can be empty) => desktop browser type is the
0489      * default browser type returned
0490      * $config['identification_sequence'] : ',' separated browser types
0491      * - the persistent storage adapter
0492      * $config['persistent_storage_adapter'] = "Session" or "NonPersistent"
0493      * - to add or replace a browser type device
0494      * $config[(type)]['device']['path']
0495      * $config[(type)]['device']['classname']
0496      * - to add or replace a browser type features adapter
0497      * $config[(type)]['features']['path']
0498      * $config[(type)]['features']['classname']
0499      *
0500      * @param  mixed $config (option) Config array
0501      * @return Zend_Http_UserAgent
0502      */
0503     public function setConfig($config = array())
0504     {
0505         if ($config instanceof Zend_Config) {
0506             $config = $config->toArray();
0507         }
0508 
0509         // Verify that Config parameters are in an array.
0510         if (!is_array($config) && !$config instanceof Traversable) {
0511             // require_once 'Zend/Http/UserAgent/Exception.php';
0512             throw new Zend_Http_UserAgent_Exception(sprintf(
0513                 'Config parameters must be in an array or a Traversable object; received "%s"',
0514                 (is_object($config) ? get_class($config) : gettype($config))
0515             ));
0516         }
0517 
0518         if ($config instanceof Traversable) {
0519             $tmp = array();
0520             foreach ($config as $key => $value) {
0521                 $tmp[$key] = $value;
0522             }
0523             $config = $tmp;
0524             unset($tmp);
0525         }
0526 
0527         $this->_config = array_merge($this->_config, $config);
0528         return $this;
0529     }
0530 
0531     /**
0532      * Returns the device object
0533      *
0534      * This is the object that will contain the various discovered device
0535      * capabilities.
0536      *
0537      * @return Zend_Http_UserAgent_Device $device
0538      */
0539     public function getDevice()
0540     {
0541         if (null !== $this->_device) {
0542             return $this->_device;
0543         }
0544 
0545         $userAgent = $this->getUserAgent();
0546 
0547         // search an existing identification in the session
0548         $storage = $this->getStorage($userAgent);
0549 
0550         if (!$storage->isEmpty()) {
0551             // If the user agent and features are already existing, the
0552             // Zend_Http_UserAgent object is serialized in the session
0553             $object = $storage->read();
0554             $this->unserialize($object);
0555         } else {
0556             // Otherwise, the identification is made and stored in the session.
0557             // Find the browser type:
0558             $this->setBrowserType($this->_matchUserAgent());
0559             $this->_createDevice();
0560 
0561             // put the result in storage:
0562             $this->getStorage($userAgent)
0563                  ->write($this->serialize());
0564         }
0565 
0566         // Mark the object as immutable
0567         $this->_immutable = true;
0568 
0569         // Return the device instance
0570         return $this->_device;
0571     }
0572 
0573     /**
0574      * Retrieve the browser type
0575      *
0576      * @return string $browserType
0577      */
0578     public function getBrowserType()
0579     {
0580         return $this->_browserType;
0581     }
0582 
0583     /**
0584      * Set the browser "type"
0585      *
0586      * @param string $browserType
0587      * @return Zend_Http_UserAgent
0588      */
0589     public function setBrowserType($browserType)
0590     {
0591         if ($this->_immutable) {
0592             // require_once 'Zend/Http/UserAgent/Exception.php';
0593             throw new Zend_Http_UserAgent_Exception(
0594                 'The User-Agent device object has already been retrieved; the browser type is now immutable'
0595             );
0596         }
0597 
0598         $this->_browserType = $browserType;
0599         return $this;
0600     }
0601 
0602     /**
0603      * Retrieve the "$_SERVER" array
0604      *
0605      * Basically, the $_SERVER array or an equivalent container storing the
0606      * data that will be introspected.
0607      *
0608      * If the value has not been previously set, it sets itself from the
0609      * $_SERVER superglobal.
0610      *
0611      * @return array
0612      */
0613     public function getServer()
0614     {
0615         if (null === $this->_server) {
0616             $this->setServer($_SERVER);
0617         }
0618         return $this->_server;
0619     }
0620 
0621     /**
0622      * Set the "$_SERVER" array
0623      *
0624      * Basically, the $_SERVER array or an equivalent container storing the
0625      * data that will be introspected.
0626      *
0627      * @param  array|ArrayAccess $server
0628      * @return void
0629      * @throws Zend_Http_UserAgent_Exception on invalid parameter
0630      */
0631     public function setServer($server)
0632     {
0633         if ($this->_immutable) {
0634             // require_once 'Zend/Http/UserAgent/Exception.php';
0635             throw new Zend_Http_UserAgent_Exception(
0636                 'The User-Agent device object has already been retrieved; the server array is now immutable'
0637             );
0638         }
0639 
0640         if (!is_array($server) && !$server instanceof Traversable) {
0641             // require_once 'Zend/Http/UserAgent/Exception.php';
0642             throw new Zend_Http_UserAgent_Exception(sprintf(
0643                 'Expected an array or object implementing Traversable; received %s',
0644                 (is_object($server) ? get_class($server) : gettype($server))
0645             ));
0646         }
0647 
0648         // Get an array if we don't have one
0649         if ($server instanceof ArrayObject) {
0650             $server = $server->getArrayCopy();
0651         } elseif ($server instanceof Traversable) {
0652             $tmp = array();
0653             foreach ($server as $key => $value) {
0654                 $tmp[$key] = $value;
0655             }
0656             $server = $tmp;
0657             unset($tmp);
0658         }
0659 
0660         // Normalize key case
0661         $server = array_change_key_case($server, CASE_LOWER);
0662 
0663         $this->_server = $server;
0664         return $this;
0665     }
0666 
0667     /**
0668      * Retrieve a server value
0669      *
0670      * @param  string $key
0671      * @return mixed
0672      */
0673     public function getServerValue($key)
0674     {
0675         $key    = strtolower($key);
0676         $server = $this->getServer();
0677         $return = null;
0678         if (isset($server[$key])) {
0679             $return = $server[$key];
0680         }
0681         unset($server);
0682         return $return;
0683     }
0684 
0685     /**
0686      * Set a server value
0687      *
0688      * @param  string|int|float $key
0689      * @param  mixed $value
0690      * @return void
0691      */
0692     public function setServerValue($key, $value)
0693     {
0694         if ($this->_immutable) {
0695             // require_once 'Zend/Http/UserAgent/Exception.php';
0696             throw new Zend_Http_UserAgent_Exception(
0697                 'The User-Agent device object has already been retrieved; the server array is now immutable'
0698             );
0699         }
0700 
0701         $server = $this->getServer(); // ensure it's been initialized
0702         $key    = strtolower($key);
0703         $this->_server[$key] = $value;
0704         return $this;
0705     }
0706 
0707     /**
0708      * Set plugin loader
0709      *
0710      * @param  string $type Type of plugin loader; one of 'storage', (?)
0711      * @param  string|Zend_Loader_PluginLoader $loader
0712      * @return Zend_Http_UserAgent
0713      */
0714     public function setPluginLoader($type, $loader)
0715     {
0716         $type       = $this->_validateLoaderType($type);
0717 
0718         if (is_string($loader)) {
0719             if (!class_exists($loader)) {
0720                 // require_once 'Zend/Loader.php';
0721                 Zend_Loader::loadClass($loader);
0722             }
0723             $loader = new $loader();
0724         } elseif (!is_object($loader)) {
0725             // require_once 'Zend/Http/UserAgent/Exception.php';
0726             throw new Zend_Http_UserAgent_Exception(sprintf(
0727                 'Expected a plugin loader class or object; received %s',
0728                 gettype($loader)
0729             ));
0730         }
0731         if (!$loader instanceof Zend_Loader_PluginLoader) {
0732             // require_once 'Zend/Http/UserAgent/Exception.php';
0733             throw new Zend_Http_UserAgent_Exception(sprintf(
0734                 'Expected an object extending Zend_Loader_PluginLoader; received %s',
0735                 get_class($loader)
0736             ));
0737         }
0738 
0739         $basePrefix = 'Zend_Http_UserAgent_';
0740         $basePath   = 'Zend/Http/UserAgent/';
0741         switch ($type) {
0742             case 'storage':
0743                 $prefix = $basePrefix . 'Storage';
0744                 $path   = $basePath   . 'Storage';
0745                 break;
0746             case 'device':
0747                 $prefix = $basePrefix;
0748                 $path   = $basePath;
0749                 break;
0750         }
0751         $loader->addPrefixPath($prefix, $path);
0752         $this->_loaders[$type] = $loader;
0753         return $this;
0754     }
0755 
0756     /**
0757      * Get a plugin loader
0758      *
0759      * @param  string $type A valid plugin loader type; see {@link $_loaderTypes}
0760      * @return Zend_Loader_PluginLoader
0761      */
0762     public function getPluginLoader($type)
0763     {
0764         $type = $this->_validateLoaderType($type);
0765         if (!isset($this->_loaders[$type])) {
0766             // require_once 'Zend/Loader/PluginLoader.php';
0767             $this->setPluginLoader($type, new Zend_Loader_PluginLoader());
0768         }
0769         return $this->_loaders[$type];
0770     }
0771 
0772     /**
0773      * Validate a plugin loader type
0774      *
0775      * Verifies that it is in {@link $_loaderTypes}, and returns a normalized
0776      * version of the type.
0777      *
0778      * @param  string $type
0779      * @return string
0780      * @throws Zend_Http_UserAgent_Exception on invalid type
0781      */
0782     protected function _validateLoaderType($type)
0783     {
0784         $type = strtolower($type);
0785         if (!in_array($type, $this->_loaderTypes)) {
0786             $types = implode(', ', $this->_loaderTypes);
0787 
0788             // require_once 'Zend/Http/UserAgent/Exception.php';
0789             throw new Zend_Http_UserAgent_Exception(sprintf(
0790                 'Expected one of "%s" for plugin loader type; received "%s"',
0791                 $types,
0792                 (string) $type
0793             ));
0794         }
0795         return $type;
0796     }
0797 
0798     /**
0799      * Run the identification sequence to match the right browser type according to the
0800      * user agent
0801      *
0802      * @return Zend_Http_UserAgent_Result
0803      */
0804     protected function _matchUserAgent()
0805     {
0806         $type = self::DEFAULT_BROWSER_TYPE;
0807 
0808         // If we have no identification sequence, just return the default type
0809         if (empty($this->_config['identification_sequence'])) {
0810             return $type;
0811         }
0812 
0813         // Get sequence against which to match
0814         $sequence = explode(',', $this->_config['identification_sequence']);
0815 
0816         // If a browser type is already configured, push that to the front of the list
0817         if (null !== ($browserType = $this->getBrowserType())) {
0818             array_unshift($sequence, $browserType);
0819         }
0820 
0821         // Append the default browser type to the list if not alread in the list
0822         if (!in_array($type, $sequence)) {
0823             $sequence[] = $type;
0824         }
0825 
0826         // Test each type until we find a match
0827         foreach ($sequence as $browserType) {
0828             $browserType = trim($browserType);
0829             $className   = $this->_getUserAgentDevice($browserType);
0830 
0831             // Attempt to match this device class
0832             if ($this->_match($className)) {
0833                 $type = $browserType;
0834                 $this->_browserTypeClass[$type] = $className;
0835                 break;
0836             }
0837         }
0838 
0839         return $type;
0840     }
0841 
0842     /**
0843      * Creates device object instance
0844      *
0845      * @return void
0846      */
0847     protected function _createDevice()
0848     {
0849         $browserType = $this->getBrowserType();
0850         $classname   = $this->_getUserAgentDevice($browserType);
0851         $this->_device = new $classname($this->getUserAgent(), $this->getServer(), $this->getConfig());
0852     }
0853 }