Found at: http://publish.ez.no/article/articleprint/86/
|
A common way of accessing structures or objects uniformly is to use attributes access (also known as properties or metadata). Attributes allows code to read and write single values from an object in one general way.
PHP already has dynamic access to member variables in objects, the problem with this is that there is no access control and that you sometimes don't want to return the data unprocessed. A common way of doing this is creating set/get functions for all variables in the class. This is better than instant access of objects but has the problem that it's hard to differentiate functions which gets/sets attributes from other function types. The attribute system also has benefits when integrating with other systems such as templates and SOAP interfaces.
The solution is, as mentioned above, to create an attribute system. The attribute interface consists of four functions. Two functions exists for reading and writing while the last two allows for querying attribute existince and attribute listing. A typical attribute class looks like this.
class SimpleAttribute
{
/*!
Returns an array of attributes.
*/
function attributes()
{
return array( "id", "name" );
}
/*!
Returns true if the attribute $attr exists.
*/
function hasAttribute( $attr )
{
return in_array( $this->attributes(), $attr );
}
/*!
Returns the value of the attribute $attr or null if it doesn't exist.
*/
function attribute( $attr )
{
switch ( $attr );
{
case "id":
return $this->ID;
case "name":
return $this->Name;
}
return null;
}
/*!
Sets the attribute $attr to the value $val if it exists.
*/
function setAttribute( $attr, $val )
{
switch ( $attr );
{
case "id":
$this->ID = $val;
case "name":
$this->Name = $val;
}
return null;
}
var $ID;
var $Name;
}; |
You can now access the id attribute with
$attr_obj = new SimpleAttribute();
if ( $attr_obj->hasAttribute( "id" ) ) // Sanity checking
{
$id = $attr_obj->attribute( "id" );
} |
Sometimes you'll have lots of member variables which you want to expose as attributes but you find it tedious to write set/get code for all of them. To handle this more easely you write a base class for generic member variable access.
class MemberAttribute
{
/*!
*/
function &memberMapping()
{
return null; // Override this function to provide varible <-> attribute mapping
// The returned value is an array with two keys "attributes" and "members"
}
/*!
Initializes the member mapping with the "members" section,
this is taken from the "attributes" section by switching the place of keys/values.
*/
function initMapping()
{
$mapping =& $this->memberMapping();
$attrs =& $mapping["attributes"];
$members =& $mapping["members"];
$members = array();
foreach( $attrs as $attr => $member )
{
$members[$member] = $attr;
}
}
/*!
Returns an array of attributes.
*/
function attributes()
{
$mapping =& $this->memberMapping();
$attrs =& $mapping["members"]; // "members" contains member name to attribute name mapping.
if ( is_null( $attrs ) )
{
$attrs = array_keys( $mapping["attributes"] );
}
return $attrs;
}
/*!
Returns true if the attribute $attr exists.
*/
function hasAttribute( $attr )
{
$mapping =& $this->memberMapping();
$attrs =& $mapping["attributes"];
return isset( $attrs[$attr] );
}
/*!
Returns the value of the attribute $attr or null if it doesn't exist.
*/
function attribute( $attr )
{
$mapping =& $this->memberMapping();
$attrs =& $mapping["attributes"];
if ( !isset( $attrs[$attr] ) )
return null;
$member = $attrs[$attr];
return $this->$member;
}
/*!
Sets the attribute $attr to the value $val if it exists.
*/
function setAttribute( $attr, $val )
{
$mapping =& $this->memberMapping();
$attrs =& $mapping["attributes"];
if ( !isset( $attrs[$attr] ) )
return null;
$member = $attrs[$attr];
$this->$member = $val;
}
}; |
We then create the new class which creates the member mapping array and initializes it, after that we have a class with full attribute support.
class User extends MemberAttribute
{
function User()
{
$this->Map = array( "attributes" => array( "id" => "ID",
"name" => "Name",
"age" => "Age" ) );
$this->initMapping();
}
/*!
*/
function &memberMapping()
{
return $this->Map;
}
/// Unique user id
var $ID;
/// Name of user
var $Name;
/// Age of user
var $Age;
}; |
Uses
The usage are for attributes are wast, for instance in the next version of eZ publish (3.0) it's used for mapping from database tables to objects, used by the template system for accessing object data, used for automatic fetching of HTTP post data to objects and for accessing object data in the PHP code (instead of get/set functions).