10.2 The Person Class

The next step is creating Person, an object representation of the data. This class encapsulates the address book entry's information into an object you can manipulate. You need to be able to create a new object, get and set information, cycle through all the fields, and convert the object to XML.

10.2.1 Constructor

The class begins with its constructor, as shown in Example 10-2.

Example 10-2. Person::_ _construct( )
class Person implements IteratorAggregate {

    private $data;

    public function _ _construct($person = NULL) {

        $this->data = array('firstname' => '', 

                            'lastname'  => '',

                            'email'     => '',

                            'id'        =>  0);

        if (is_array($person)) {

            foreach ($person as $field => $value) {

                $this->$field = $value;




Example 10-2 initializes the $data property as an array of four elements: firstname, lastname, email, and id. The first three hold information about a person, and the last one is the record's unique database key.

When nothing's passed into the constructor, you're left with an empty Person object, which can then be defined. However, you can optionally pass an array of data that contains information about a person.

When this happens, the constructor loops through the array and converts each element into an object property. As you'll see, this class actually overloads property access with _ _get( ) and _ _set( ), so the information is really going inside $data.

You've probably noticed that none of the properties are set by name. For example, there's no call to $this->firstname. Instead, the code references $this->$field.

It's somewhat unusual to refer to a property name with a variable; however, this allows you to avoid hardcoding property names inside the foreach. In general, you want to write generic code like this, as it reduces headaches when you modify your code.

The foreach converts an array with any combination of keys into properties. Assume, for instance, that $person is array('firstname' => 'Rasmus'). Then inside the loop, $field is firstname and $value is Rasmus. Therefore, $this->$field = $value translates to $this->firstname = 'Rasmus'.

When $person holds additional elements, the foreach automatically takes care of every field in turn without any action on your part. Don't worry if someone tries to pass in additional array elements, such as address. These are filtered out inside of _ _set( ).

10.2.2 Encapsulating Property Access with _ _get( ) and _ _set( )

Public properties break encapsulation, so Person implements the property overload methods of _ _get( ) and _ _set( ). The methods in Example 10-3 actually retrieve and store data from the protected $data property.

Example 10-3. Person::_ _set( ) and Person::_ _get( )
    public function _ _set($property, $value) {

        if (isset($this->data[$property])) {

            $this->data[$property] = $value;



    public function _ _get($property) {

        if (isset($this->data[$property])) {

            return $this->data[$property];      

        } else {

            return false;



The _ _set( ) method doesn't add all properties to $data. It first uses isset( ) to check that $property already exists within the array. This will return true only for the elements assigned in the constructor. If $property fails this test, the method discards $value. This check prevents you from adding arbitrary data to the object, so you can be sure of the class's properties.

The _ _get( ) method behaves in a similar fashion. If the property isset( ), the method returns the property's value. If it's not set, the method returns false.

See Section 2.6.1 for more on property overloading.

10.2.3 Enabling Custom Object Iteration with getIterator( )

Since you're overloading property access, the object is no longer iterable using PHP's default iteration behavior. There's only one class property, $data, and its visibility is restricted from public view.

The fix is to implement the IteratorAggregate interface and to write a getIterator( ) method, as shown in Example 10-4.

Example 10-4. Person::getIterator( )
    public function getIterator( ) {

        return new ArrayObject($this->data);


This method must return an iterator. Instead of creating a custom iterator, it's better to use SPL's ArrayObject. This object takes an array, such as $this->data, and converts it into an iterator object that acts just like an array.

Chapter 6 covers iteration in detail. In particular, see Section 6.8 and Section 6.9.

10.2.4 Converting a Person Object to an XML Document Using DOM

Your final method is toDOM( ), shown in Example 10-5. This method converts Person into a DOM object for use with the XML functions.

Example 10-5. Person::toDOM( )
    public function toDOM( ) {

        $xml = new DOMDocument('1.0', 'UTF-8');

        $xml->formatOutput = true; // indent elements

        $person = $xml->appendChild(new DOMElement('person'));

        foreach ($this as $key => $value) {

            $person->appendChild(new DOMElement($key, $value));


        return $xml;


The goal of Example 10-5 is to create a DOM object and populate it with child elements for each piece of data stored in the object.

Your first step is instantiating a new DOMDocument. Set the XML version to 1.0 and the encoding to UTF-8. The second line indents the output with spaces. This makes the XML easier to read for humans.

Now you create the DOM elements. The root element is a new person. Next, a foreach loop creates elements for each of the current object's properties.

It's important to loop through $this instead of $this->data. Right now, you happen to store your data inside the $data property, but that could change. If you do loop through $this->data and then change this, you would have to update all your methods. Looping through $this invokes getIterator( ), so as long as you update getIterator( ), it's a seamless change every place else.

The code uses a feature of the DOMElement constructor. When a second parameter is passed, the object creates a text node containing $value and places it as a child of the new DOM element.

See Section 5.9 for more details on creating DOM objects.

10.2.5 Creating and Manipulating a Person Object

Example 10-6 shows the Person class in action.

Example 10-6. Creating a new Person
$rasmus = new Person;

$rasmus->firstname = 'Rasmus';

$rasmus->lastname  = 'Lerdorf';

$rasmus->email     = 'rasmus@php.net';

foreach($rasmus as $property => $value) {

    print "$value\n";


print $rasmus->toDOM( )->saveXML( );

Example 10-6 creates a new Person, assigns values to the object, and then iterates through it. It also prints the object's DOM representation. This is the result:

firstname: Rasmus

lastname: Lerdorf

email: rasmus@php.net

id: 0

<?xml version="1.0" encoding="UTF-8"?>







The properties all hold their assigned values; however, since id wasn't changed, it's still at the default value of 0.

Alternatively, you can pass an array to the constructor:

$info = array('firstname' => 'Zeev',

              'lastname'  => 'Suraski',

              'email'     => 'zeev@php.net');

$zeev = new Person($info);

print $zeev->toDOM( )->saveXML( );

<?xml version="1.0" encoding="UTF-8"?>







Passing an array is functionally equivalent to setting properties one by one, but it's more concise.