View Source

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{composition-setup}{composition-setup}]]></ac:plain-text-body></ac:macro>
<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{zone-template-instance:ZFPROP:Proposal Zone Template}

{zone-data:component-name}
Zend_Http_UserAgent (was Zend_Browser)
{zone-data}

{zone-data:proposer-list}
[Raphaƫl Carles|mailto:raphael.carles@interakting.com]
{zone-data}

{zone-data:liaison}

{zone-data}

{zone-data:revision}
1.0 - 03 August 2010: Initial Draft.
{zone-data}

{zone-data:overview}
This proposition covers several classes dedicated to the client browser/device detection and the available associated features and capabilities.
Its aim is to provide an interface to devices identification libraries like WURFL or DeviceAtlas and ease browsers differences handling, including mobile browsers.
This normalization of client environment detection can ease the management of multi-support development.{zone-data}

{zone-data:references}
* [WURFL|http://wurfl.sourceforge.net/]
* [Tera WURFL|http://www.tera-wurfl.com/wiki/index.php/Main_Page]
* [DeviceAtlas|http://deviceatlas.com/]
{zone-data}

{zone-data:requirements}
* This component *will* allow quick detection of browsers, using per-session storage of last identification data
* This component *will* ease the use of external device identification libraries
* This component *will* be lightweight by using singleton pattern
* This component *will* provide an easy way to include new browsers types to detect.
* This component *will not* provide content adaptation/content replacement mechanisms or helpers
{zone-data}

{zone-data:dependencies}
* Zend_Session (optional)
{zone-data}

{zone-data:operation}
The UserAgent component should be seen as an information provider to Zend Framework applications at any level (helpers, controllers...)

Detection relies on declared or forced user agent information from server vars. It allows to have a standard behavior in the case that user agent string is present, a default one otherwise, and a forced mode where user agent is given at call time.

The identification class has a declared list of browsers types, ordered by priority.
Priorities can be changed to reflect application orientation (eg. a mobile-oriented website should have a faster identification of mobile devices than desktop ones).
New browser types can be developed and added to the priority list (or to extend an existing one), allowing wider recognition (probes, text browsers, ...) for the application that uses it.

The identification function is not called directly, although this is also possible.
All calls should be done to the getInstance method to execute the full identification process only one time per-request, or if session is activated, one time per-session and user agent.

After a quick detection of browser type, Zend_UserAgent _can_ populate features by two ways :
- by responding directly to features checks (eg. return false to every request for a text browser, return true to every request for a desktop browser)
- by delegating to a Zend_UserAgent_Features_Adapter that will retrieve features.

The result is then stored as mentioned to bypass identification at next call (unless another user agent is forced).
{zone-data}

{zone-data:milestones}
Component already done for specific developments without ZF.

* Milestone 1: refactoring of existing code and adpatations to ZF design standards
* Milestone 2: Working prototype checked into the incubator supporting use cases #1, #2, ...
* Milestone 3: Working prototype checked into the incubator supporting use cases #3 and #4.
* Milestone 4: Unit tests exist, work, and are checked into SVN.
* Milestone 5: Initial documentation exists.
{zone-data}

{zone-data:class-list}
* Zend_UserAgent

* Zend_UserAgent_AbstractUserAgent
* Zend_UserAgent_Mobile
* Zend_UserAgent_Tablet
* Zend_UserAgent_Desktop (by default)
* Zend_UserAgent_Bot
* Zend_UserAgent_Text

* Zend_UserAgent_Features_Adapter
* Zend_UserAgent_Features_Adapter_WurflPhpApi (the first adapter to be provided)
* Zend_UserAgent_Features_Adapter_Wurfl
* Zend_UserAgent_Features_Adapter_TeraWurfl
* Zend_UserAgent_Features_Adapter_DeviceAtlas

* Zend_UserAgent_Storage
* Zend_UserAgent_Storage_NonPersistent
* Zend_UserAgent_Storage_Session

The first adapter provided will be Zend_UserAgent_Features_Adapter_WurflPhpApi [http://wurfl.sourceforge.net/nphp/]
{zone-data}

{zone-data:use-cases}
{deck:id=Use Cases}
{card:label=UC-01}
h3. Standard usage
{code:php}
/** config Wurfl API for mobile devices */
$config['wurflapi']['wurfl_lib_dir'] = '../../Wurfl/1.1/';
$config['wurflapi']['wurfl_config_file'] = '../../Wurfl/resources/wurfl-config.php';

/** init config for the first call */
Zend_UserAgent::setConfig($config);

/** Identification
* if the object is in session, it is unserialised
* otherwise, if it's in the static var, it's used (singleton pattern)
* ultimately it calls the constructor and makes the identification :
* - the first operation is to identify the browser type
* - then, the browser/device features are collected
*/
$oUserAgent = Zend_UserAgent::getInstance();

var_dump($oUserAgent->getType());
var_dump($oUserAgent->device->getFeature('browser_name'));
var_dump($oUserAgent->device->getFeature('browser_version'));
var_dump($oUserAgent->device->getFeature('device_os'));
var_dump($oUserAgent->device->getFeature('mobile_browser'));
var_dump($oUserAgent->device->getFeature('model_name'));
var_dump($oUserAgent->device->getFeature('device_os_version'));
var_dump($oUserAgent->device->getFeature('resolution_height'));
var_dump($oUserAgent->device->getFeature('resolution_width'));
{code}
{card}

{card:label=UC-02}
h3. Forced User Agent (for testing)

{code:php}
$config['wurflapi']['wurfl_lib_dir'] = '../../Wurfl/1.1/';
$config['wurflapi']['wurfl_config_file'] = '../../Wurfl/resources/wurfl-config.php';
Zend_UserAgent::setConfig($config);

$ua = 'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleW1ebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A102 Safari/419.3';

$oUserAgent = Zend_UserAgent::getInstance($ua);

/**
* the UA is defined in the first call, after, for the current script, getInstance (without parameter) will use the same UA
*/
$oUserAgent2 = Zend_UserAgent::getInstance();
{code}
{card}

{card:label=UC-03}
h3. Changing the identification sequence
By default, uses "mobile,desktop", but you may want to change it to test Bots, Tablets, etc.

{code:php}
/**
* NOTE :
* The following sequence will ignore Mobile devices
* Whatever is the sequence, if there is no identification, the 'desktop' browser type is used
* To don't have identification sequence, pu the 'identification_sequence' parameter to empty
*/
$config['identification_sequence'] = 'bot,tablet,email,validator,text';
Zend_UserAgent::setConfig($config);
$oUserAgent = Zend_UserAgent::getInstance();
{code}
{card}

{card:label=UC-04}
h3. Changing the persistent storage
Uses Session by default; NoPersistent can be used for testing.

{code:php}
$config['persistent_storage_adapter'] = 'NonPersistent';
Zend_UserAgent::setConfig($config);
$oUserAgent = Zend_UserAgent::getInstance();
{code}

*or*

{code:php}
Zend_UserAgent::setConfig(array('persistent_storage_adapter' => 'NonPersistent'));
Zend_UserAgent::setConfig($config);
$oUserAgent = Zend_UserAgent::getInstance();
{code}
{card}

{card:label=UC-05}
h3. To add or change a matcher

{code:php}
/** For a new one : */
$config['tablet']['matcher']['path'] = 'Project/Browser/Tablet.php';
$config['tablet']['matcher']['classname'] = 'Project_Browser_Tablet';

/** To replace an existing matcher : here 'mobile' */
$config['mobile']['matcher']['path'] = 'Project/Browser/Mobile.php';
$config['mobile']['matcher']['classname'] = 'Project_Browser_Mobile';

/** don't froget to redefine a new identification sequence to use the new matcher */
$config['identification_sequence'] = 'tablet,mobile,desktop';

Zend_UserAgent::setConfig($config);
$oUserAgent = Zend_UserAgent::getInstance();
{code}
{card}

{card:label=UC-06}
h3. To define a new adapter to collect browser/device features
Note for mobile: WurflApi is used by default.

{code:php}
$config['mobile']['features']['path'] = 'Project/Browser/Features/Adapter/DeviceTest.php';
$config['mobile']['features']['classname'] = 'Project_Browser_Features_Adapter_DeviceTest';
$ua = 'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleW1ebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A102 Safari/419.3';
Zend_UserAgent::setConfig($config);
$oUserAgent = Zend_UserAgent::getInstance($ua);
{code}
{card}
{deck}
{zone-data}

{zone-data:skeletons}
{deck:id=Class Skeletons}
{card:label=UserAgent}
{code:php}
class Zend_UserAgent
{
public static $config;
public $device;
public $browserType;
public static function setConfig ($config = array())
static public function defaultConfig ()
public static function getInstance ($userAgent = null)
public function getStorage ($browser)
public static function setStorage (Zend_UserAgent_Storage_Interface $storage)
public static function getClassName ($browserType)
static public function getUserAgent ($userAgent = null)
static public function getAcceptHeader ($httpAccept = null)
static public function initUserAgent ($userAgent = null)
static public function initHttpAccept ($httpAccept = null)
static public function setUserAgent ($userAgent)
static public function setHttpAccept ($httpAccept)
public static function match ($userAgent, $uaSignatures = null)
public function getType ()
}
{code}
{card}

{card:label=AbstractUserAgent}
{code:php}
class Zend_UserAgent_AbstractUserAgent
{
public $browser;
public $userAgent;
public function __construct ()
public function defineFeatures ()
abstract public function getType ();
public function hasFeature ($feature)
public function getFeature ($feature)
public function setFeature ($feature, $value = false, $group = '')
public function setGroup ($group, $feature)
public function getGroup ($group)
public function getAllFeatures ()
public function getAllGroups ()
static public function loadFeaturesAdapter ($browserType)
}
{code}
{card}

{card:label=Storage}
{code:php}
interface Zend_UserAgent_Storage
{
public function isEmpty();
public function read();
public function write($contents);
public function clear();
}
{code}
{card}

{card:label=Features Adapter}
{code:php}
interface Zend_UserAgent_Features_Adapter
{
public function getFromRequest($request);
}
{code}
{card}

{deck}
{zone-data}

{zone-template-instance}]]></ac:plain-text-body></ac:macro>