Location: PHPKode > scripts > Request Info User Agent > request-info-user-agent/UserAgent.php
<?php
/**
 * Classe FredT_Request_Info_UserAgent
 *
 * Roles:
 * HTTP_USER_AGENT identification class
 * - System detection (OS, device type TV, Console, Mobile ...)
 * - "Agent" detection (Browser, Bot, Undesirable)
 * - Many convenience function (isIE(), isBot(), isMobilSystem() ...)
 * - Simple usage : new FredT_Request_Info_UserAgent() for detection of the current HTTP_USER_AGENT
 * - or more advanced  new FredT_Request_Info_UserAgent(userAgentParam) to stat usage for exemple
 * - External file for perennity, facility update of the search data
 * - Performance:
 * 	    . File cache for detection data
 * 	    . Dection mode (Fast, light, full)
 *
 * @author      FredT
 * @category    FredT Library
 * @package     FredT Request Info
 * @version     10/01/2009
 *
 * @todo (later), @see loadReference() function :
 * 	- create xml (or/and ini) file : validation capicity for data and syntax
 *  - parsing code for ini/xml format
 *  - sort detection data
 *
 * @example see Tests PHPUnit
 *
 *
 * Source:
 * Many data from @see http://www.phpclasses.org/browse/package/4117.html (Class client_info 2007/09)
 *
 */


/** FredT_Exception */
require_once(Exception.php');


class FredT_Request_Info_UserAgent
{
    const BROWSER_IE = 'Internet Explorer';
    const BROWSER_FIREFOX = 'Firefox';
    const BROWSER_OPERA = 'Opera';

    const BOT_GOOGLE = 'Googlebot';

    const SYSTEM_UNKNOW = 'unknow';
    const SYSTEM_MOBIL = 'mobil';
    const SYSTEM_CONSOLE = 'console';
    const SYSTEM_TV = 'tv';

    const TYPE_BROWSER='browser';
    const TYPE_BOT='bot';
    const TYPE_UNDESIRABLE='undesirable';
    const TYPE_UNKNOW = 'unknow';

    const MODE_FULL_DETECT=3;
    const MODE_LIGHT_DETECT=2;
    const MODE_FAST_DETECT=1;

    protected static $_refFile = '/Data/refUA.php';
    /**
     * Cache file
     * @var string
     */
    protected static $_fileCache;
    /**
     * @var array
     */
    protected static $_reference=array();

    protected static $_detectMode=self::MODE_FAST_DETECT;
    /**
     * User Agent
     * @var string
     */
    protected $_ua;
    /**
     * .NET Common Language Runtime
     * @var bool
     */
    protected $_NetClr;
    /**
     * @var string
     */
    protected $_name;
    /**
     * @var string
     */
    protected $_version;
    /**
     * @var string
     */
    protected $_system;
    /**
     * @var string
     */
    protected $_systemVersion;
    /**
     * @var string
     */
    protected $_systemType;
    /**
     * @var string
     */
    protected $_systemSubType;
    /**
     * @var string
     */
    protected $_systemDescription;
    /**
     * @var string
     */
    protected $_agentType;
    /**
     * @var string
     */
    protected $_agentSubType;
    /**
     * @var string
     */
    protected $_description;


    /**
     * constructor, can be use for define a userAgent string other than the actual,
     * @see setDetectMode() function for performance
     *
     * @param string $userAgent defaut null for use the current userAgent
     * @param unknown_type $mode
     */
    public function __construct($userAgent=null, $mode=null)
    {
        self::setDetectMode($mode);
        $this->setUserAgent($userAgent);
    }

    /**
     * Set detection mode for performance
     *
     * @param MODE_*_DETECT $mode self::const default is MODE_FAST_DETECT
     * @return void
     */
    public static function setDetectMode($mode=null)
    {
    	if (! is_null(self::$_detectMode) && is_null($mode)) {
    	    return;
    	}
    	self::unloadReference();
    	if (is_null($mode) || ($mode!=self::MODE_FULL_DETECT && $mode!=self::MODE_LIGHT_DETECT) ) {
    	    $mode=self::MODE_FAST_DETECT;
    	}
        self::$_detectMode=$mode;
    }

    /**
     * return detection mode
     * @return const MODE_*_DETECT
     */
    public static function getDetectMode()
    {
    	return self::$_detectMode;
    }

    /**
     * Definele user agent, no param or null for current user agent
     *
     * @param string $userAgent
     * @return FredT_Request_Info_UserAgent
     */
    public function setUserAgent($userAgent=null)
    {
        if (empty(self::$_reference)) {
            self::loadReference();
        }
        if (null===$userAgent && isset($_SERVER['HTTP_USER_AGENT'])) {
            $userAgent = $_SERVER['HTTP_USER_AGENT'];
        }
        elseif (null===$userAgent) {
            $userAgent = getenv('HTTP_USER_AGENT');
        }
        elseif (! is_string($userAgent)) {
            throw new FredT_Request_Exception('Invalid format for parameter userAgent!');
        }

        $this->_ua = $userAgent;
        $this->_NetClr=null;
        $this->_detectAgent(self::$_reference['agent']);
        $this->_detectSystem(self::$_reference['system']);
        return $this;
    }

    /**
     * return the current used UserAgent
     * @return String
     */
    public function getUserAgent()
    {
    	return $this->_ua;
    }

    /**
     * return system name (win, linux ... for OS, Sony, nokia ... for mobil ...)
     *
     * @return string
     */
    public function getSystem()
    {
    	return $this->_system;
    }

    /**
     * os, mobil, tv, consoles ...  version
     * @return string|null
     */
    public function getSystemVersion()
    {
    	return $this->_systemVersion;
    }

    /**
     * return system type (const SYSTEM_*)
     * @return string
     */
    public function getSystemType()
    {
    	return $this->_systemType;
    }

    /**
     * Return system sub-type
     * @return string|nul
     */
    public function getSystemSubType()
    {
    	return $this->_systemSubType;
    }

    /**
     * Return system description
     * @return string|null
     */
    public function getSystemDescription()
    {
    	return $this->_systemDescription;
    }

    /**
     * Return agent type (const TYPE_* or other string)
     * @return string
     */
    public function getType()
    {
    	return $this->_agentType;
    }

    /**
     * Retourne s'il est défini le sous type du user agent détecté
     * @return string|null
     */
    public function getSubType()
    {
    	return $this->_agentSubType;
    }

    /**
     * Retourne le nom du user agent détecté, ou null si le type n'est pas détecté
     * Peut renvoyer une des constantes FredT_Request_Info_UserAgent::BROWSER_*, ou BOT_* ...
     * @return string|null
     */
    public function getName()
    {
    	return $this->_name;
    }

    /**
     * Retourne la version du user agent détecté, ou null
     * @return string|null
     */
    public function getVersion()
    {
    	return $this->_version;
    }

    /**
     * Retourne l'éventuelle description si elle existe pour le user agent détecté
     * @return string|null
     */
    public function getDescription()
    {
    	return $this->_description;
    }

    /**
     * Get all infos in array
     *
     * @return array
     */
    public function getInfos()
    {
    	return array(
    	            'user agent'=>$this->getUserAgent(),
					'agent type'=>$this->getType(),
    	            'agent sub-type'=>$this->getSubType(),
					'agent name'=>$this->getName(),
    	            'agent version'=>$this->getVersion(),
    	            'agent description'=>$this->getDescription(),
    	            'system name'=>$this->getSystem(),
    	            'system version'=>$this->getSystemVersion(),
    	            'system type'=>$this->getSystemType(),
	                'system sub-type'=>$this->getSystemSubType(),
    	            'system description'=>$this->getSystemDescription(),
    	            'NetClr'=>$this->isNET_CLR()
        	    );
    }


    /**
     * Retourne true si le user agent est de type bot
     * @return bool
     */
    public function isBot()
    {
    	return (bool) ($this->getType()==self::TYPE_BOT);
    }

    /**
     * Retourne true si le user agent est de type browser
     * @return bool
     */
    public function isBrowser()
    {
    	return (bool) ($this->getType()==self::TYPE_BROWSER);
    }

    /**
     * Retourne true si l'agent est de type inconnu et n'a pas été détecté
     * @return bool
     */
    public function isUnknow()
    {
    	return (bool) ($this->getType()==self::TYPE_UNKNOW);
    }

    /**
     * Retourne true si le system est de type inconnu et n'a pas été détecté
     * @return bool
     */
    public function isUnknowSystem()
    {
    	return (bool) ($this->getSystemType()==self::SYSTEM_UNKNOW);
    }
    public function isMobilSystem()
    {
    	return (bool) ($this->getSystemType()==self::SYSTEM_MOBIL);
    }
    public function isConsoleSystem()
    {
    	return (bool) ($this->getSystemType()==self::SYSTEM_CONSOLE);
    }
    public function isTvSystem()
    {
    	return (bool) ($this->getSystemType()==self::SYSTEM_TV);
    }

    /**
     * Retourne true si le user agent est de type indésirable
     * @return bool
     */
    public function isUndesirable()
    {
    	return (bool) ($this->getType()==self::TYPE_UNDESIRABLE);
    }

    /**
     * Retourne true si le browser est IE
     * @return bool
     */
    public function isIE()
    {
    	return ($this->getName()===self::BROWSER_IE);
    }

    /**
     * Retourne true si le browser est Firefox
     * @return bool
     */
    public function isFirefox()
    {
    	return ($this->getName()===self::BROWSER_FIREFOX);
    }

    /**
     * Retourne true si le browser est Opera
     * @return bool
     */
    public function isOpera()
    {
    	return ($this->getName()===self::BROWSER_OPERA);
    }

    /**
     * Retourne true si est un bot google
     * @return bool
     */
    public function isGoogleBot()
    {
    	return ($this->getName()===self::BOT_GOOGLE);
    }

    /**
     * Return True if User Agent is .NET Common Language Runtime
     * @return bool
     */
    public function isNET_CLR() {
    	if (null === $this->_NetClr) {
    	    $this->_NetClr = (bool) eregi('NET CLR',$this->_ua);
    	}
    	return $this->_NetClr;
    }

    /**
     * System detection
     * @param array $ref
     */
    protected function _detectSystem($ref)
    {
        $this->_system=null;
        $this->_systemVersion=null;
        $this->_systemType=null;
        $this->_systemSubType=null;
        $this->_systemDescription=null;

        $tmp_array = array();
        foreach ($ref as $name => $elements) {
            $exprReg=$elements['search'];
            foreach ($exprReg as $expr) {
                if(preg_match($expr, $this->getUserAgent(), $tmp_array)) {
                    $this->_system = $name;

                    if (isset($tmp_array) && isset($tmp_array[1])) {
                        if (isset($elements['version_subSearch'])) {
                            // sous-recherche de version version exemple Win
                            foreach ($elements['version_subSearch'] as $version => $expr) {
                                if(preg_match($expr, $tmp_array[1])) {
                                    $this->_systemVersion = $version;
                                }
                            }
                        }
                        if($this->_systemVersion===null) {
                            $this->_systemVersion = (string) $tmp_array[1];
                        }
                    }
                    elseif (isset($elements['version_addSearch'])) {
                        // Pour version recherche additionnellle voir linux et bsd par exemple
                        foreach ($elements['version_addSearch'] as $version => $expr) {
                            if(preg_match($expr, $this->getUserAgent())) {
                                $this->_systemVersion = $version;
                            }
                        }
                    }
                    $this->_systemType=$elements['type'];
                    if (isset($elements['subType'])) {
                        $this->_systemSubType = $elements['subType'];
                    }
                    if (isset($elements['description'])) {
                        $this->_systemDescription = $elements['description'];
                    }
                    return ;
                }
            }
        }
        $this->_systemType = self::SYSTEM_UNKNOW;
    }

    /**
     * Agent detection
     * @param array $ref
     */
    protected function _detectAgent($ref)
    {
        $this->_name=null;
        $this->_version=null;
        $this->_agentType=null;
        $this->_agentSubType=null;
        $this->_description=null;

        $tmp_array = array();
        foreach ($ref as $name => $elements) {
            $exprReg=$elements['search'];
            foreach ($exprReg as $expr) {
                if(preg_match($expr, $this->getUserAgent(), $tmp_array)) {
                    $this->_name = $name;
                    if (isset($tmp_array) && isset($tmp_array[1])) {
                        $this->_version = (string) $tmp_array[1];
                    }
                    $this->_agentType=$elements['type'];
                    if (isset($elements['subType'])) {
                        $this->_agentSubType = $elements['subType'];
                    }
                    if (isset($elements['description'])) {
                        $this->_description = $elements['description'];
                    }
                    return ;
                }
            }
        }
        $this->_agentType = self::TYPE_UNKNOW;
    }

    /**
     * Load data of the external file for detection
     * - Use cache if his path is set
     * - parse data detection for performance
     * - Other file possibility (not tested)
     *
     * @param string $file|null
     * @return void
     */
    public static function loadReference($file=null)
    {
        if ( !is_null(self::getFileCache()) && is_null($file) ) {
            $file= self::getFileCache();
        }
        elseif (is_null($file)) {
            $file= dirname(__FILE__) . self::$_refFile;
        }
        elseif ( ! is_file($file) ) {
            throw new FredT_Request_Exception('Reference file "'.$file.'" is not found!');
        }
        $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION ));
        switch ($ext) {
        	case 'php':
        	    self::$_reference = include($file);
        	break;
        	case 'xml':
        	case 'ini':
        	    // xxx créer ref par default au format xml ou ini pour modif plus facile
        	    // Parsage du fichier ici
    	    break;
        	default:
        		throw new FredT_Request_Exception('Invalid file format of "'.$file.'" for reference user agent!');
        	break;
        }
        while (list($detection, $detectionData) = each(self::$_reference)) {
            while (list($name, $elements) = each($detectionData)) {
                $mode=isset($elements['mode']) ? $elements['mode'] : self::MODE_FULL_DETECT;
                if ( $mode>self::getDetectMode() ) {
                    unset(self::$_reference[$detection][$name]);
                }
            }
        }
        if ( !is_null(self::getFileCache()) ) return;
        $ref = self::$_reference;
        while (list($detection, $detectionData) = each($ref)) {
            while (list($name, $elements) = each($detectionData)) {
                $elements['search']= (array) $elements['search'];
                while (list($i, $expr) = each($elements['search'])) {
                    if(!preg_match('/^\/.*\/si$/si', $expr)){
                        // si chaine simple, transforme en expr reguliere
                        $elements['search'][$i]='/' . $expr . '/si';
                    }
                }
                $detectionData[$name]=$elements;
            }
            $ref[$detection]=$detectionData;
            /* xxx sort here
            1 trie $exprReg ??
            2 trie par type order undesirable->bot->browser
            3 move 'Internet Explorer', 'Netscape Navigator', et 'Mozilla compatible' at the end?
			*/
        }
        self::$_reference = $ref;
    }

    /**
     * Convenience public function for test cache
     * @return void
     */
    public static function unloadReference() {
    	self::$_reference = array();
    	self::setFileCache(null);
    }

    /**
     * Return reference detection array or a php string if $export = true
     *
     * @param bool $export
     * @return array|string
     */
    public static function getReference($export=false)
    {
        if (empty(self::$_reference)) {
            self::loadReference();
        }
        $ret = self::$_reference;
        if ($export) {
            $ret=var_export($ret,true);
            $ret=str_replace('\\\\','\\',$ret);
        }
        return $ret;
    }

    /**
     * Set path to class file cache
     * Specify a path to a file that will add the parsing refeference into php array.
     * This is an opt-in feature for performance purposes.
     *
     * @param  string $file
     * @throws FredT_Request_Exception if file is not writeable or path does not exist
     */
    public static function setFileCache($file)
    {
        if (null === $file) {
            self::$_fileCache = null;
            return ;
        }

        if (!file_exists($file) && !file_exists(dirname($file))) {
            throw new FredT_Request_Exception('Specified file and his directory does not exist (' . $file . ')!');
        }
        if (file_exists($file) && !is_writable($file)) {
            throw new FredT_Request_Exception('Specified file is not writeable (' . $file . ')');
        }
        if (!file_exists($file) && file_exists(dirname($file)) && !is_writable(dirname($file))) {
            throw new FredT_Request_Exception('Specified directory is not writeable (' . $file . ')');
        }
        // create file
        if (!file_exists($file)) {
            self::unloadReference();
            $oldMode= self::getDetectMode();
            self::setDetectMode(self::MODE_FULL_DETECT);
            $content = '<?php' . "\n" .
                      'return ' . self::getReference(true) . ';';
            file_put_contents($file, $content);
            self::setDetectMode($oldMode);
        }

        self::$_fileCache = $file;
    }

    /**
     * Retrieve class file cache path
     * @return string|null
     */
    public static function getFileCache()
    {
        return self::$_fileCache;
    }

}
Return current item: Request Info User Agent