Search This Blog

Monday, August 29, 2011

What is PEAR?

PEAR—the PHP Extension and Application Repository—was set up in 1999 by one of the PHP project's most respected and longest-serving contributors, Norwegian Stig Sæther Bakken. Its principal purpose is to provide an officially sanctioned library of open-source components contributed by PHP architects from all over the world, as well as an integrated means for installing those components into installations of PHP.

Those with experience of PERL may well draw similarities to the Comprehensive Perl Archive Network (CPAN), and would be right to do so. PEAR borrows many of the excellent features offered by CPAN, including the detection and automatic installation of dependent components when installing modules.

The components—or packages—within the PEAR network cover a myriad of functionality, including database connectivity, support for reading and writing unusual file formats, and widgets for generating complex HTML. Many components feature XML support, meaning you can put to use the skills you learned in Chapter 11.

In addition, PEAR provides a set of coding standards to which its contributors must adhere, which we examine shortly.

How is PEAR Structured?

The PEAR Project is divided into a number of missions:
  • A library of packages, each representing a discrete area of functionality.
  • The PEAR Package Manager, used for installing and removing these packages from an installation of PHP.
  • The PHP Foundation Classes (PFC), a subset of the PEAR repository consisting of modules of a highly stable and thoroughly tested nature, which are included in a standard PHP installation.
  • The PHP Extension Community Library—PECL—a library of components written in C (rather than native PHP), which has just recently become a project in its own right (see http://pear.php.net for more information).
  • The PEAR Coding Standards, which we'll explore later in the chapter.
Any single package found in the PEAR repository is almost always distributed as a class, a concept to which you've been introduced in the preceding two chapters. Because the packages are designed as reusable, modular components, an object-oriented methodology makes a great deal of sense.

This does mean that if you haven't yet read Chapters 12 and 13, now would be a good time to do so.

The PHP Foundation Classes

The PHP Foundation Classes (PFC), which are included by default in a normal installation of PHP, offer a number of essential toolkit routines beyond the basic intrinsic functionality of PHP. Although still, strictly speaking, unofficial, their common use in PHP applications is considered standard and accepted.

Packages within PFC generally have large scopes of use, rather than concerning themselves with, for example, obscure database platforms or file formats. Furthermore, such packages are always considered stable. At the time of this writing, some of the foundation classes distributed with PHP5 include the Mail class, an OS Guess class for determining the host operating system in PHP, and classes for making socket connections to other network hosts. Some of these classes may seem a little obscure in their own right, but nonbundled PEAR classes often make substantial use of them. By distributing them with PHP, the Package Manager does not have to install them on your behalf when you install a PEAR class that requires them. The PFC classes are always optimized for the version of PHP with which they are bundled; were it left to the Package Manager to download a copy, obscure incompatibilities between versions of PHP and versions of the PFC classes might start to cause problems. Explicitly tying PFC classes to distributions provides a common interface to the core of PHP such that non-PFC PEAR classes do not have to concern themselves with any differences between PHP versions.

If you are curious to know which modules are included as part of the Foundation Classes, check out the /usr/local/lib/php (Unix) or C:\PHP\PEAR (Windows) folder on your fresh PHP installation. Documentation for usage of these classes resides, like any other PEAR class, on the PEAR Web site at http://pear.php.net.

As these packages are included in a standard installation of PHP, you should not need to install them by hand, unless you explicitly requested they not be installed when you set up your development environment.

In theory, you can keep them up-to-date by using the PEAR Package Manager (more on this shortly), but in practice they are upgraded as and when new versions of PHP become available.

The PHP Extension Community Library

The PHP Extension Community Library or PECL (pronounced pickle) was originally a subset of the PEAR repository consisting of components written in C, rather than in native PHP. The rationale behind such components is usually the need for speed; compiled C often works out a lot quicker than PHP, particularly for mathematically intensive operations. PECL offers a means for component developers to avail themselves of the speed and power of C, whilst continuing to provide an interface to those components through standard PHP syntax.
PECL has recently evolved into a project in its own right, completely separate from PEAR—although it does retain the layout and politics of PEAR.

PECL is outside the scope of this chapter; however, familiarity with PEAR should leave you in good stead to tackle PECL should you want to do so. A good starting point is the project's Web site: http://pecl.pear.net.

The PEAR Package Manager

Many of the packages in the PEAR repository have dependencies. A dependency is the existence of a requirement for one class to be installed in order for another to function successfully. For example, a PEAR component that provides the capability to perform HTTP POST requests may depend on the existence of a generic HTTP functionality package. That package may in turn depend on a TCP/IP abstraction package, and so on.

As you can tell, starting with a fresh PHP installation and installing all necessary dependencies to make your class of choice function correctly could be immensely difficult to do by hand. The process is made much easier by means of the PEAR Package Manager.
The Package Manager provides a wealth of functionality for managing the insurgence of PEAR into your PHP setup, but its principal purpose is to allow you to install and remove PEAR packages with ease.

It is dependency-aware, which means that when you ask it to install a particular package that depends on one or more other packages, it kindly installs those dependent packages for you. It's also aware of the hierarchy of dependency, so it ensures that those packages are installed in the correct order.

Installing packages using the PEAR Package Manager is covered later in this chapter.

Recapping PEAR Standards

We discussed coding standards in Chapter 6, but let's just review the stylistic conventions used in this book's sample code.

The "correct" way to write code is something that's been debated for the last 30 years or so, without any particular common consensus having been reached. PHP is somewhat fortunate in that as a relatively youthful language, there has not been time for the kind of stylistic divergence witnessed in other languages. In this book, all of the authors decided that any example code we write would follow the standards set out by PEAR—known as the PEAR coding standards (PCS); that is to say, we'd stick to the same standards in our code as that to which authors of packages submitted to PEAR are required to adhere.

Indeed, should you ever want to try your hand at submitting your own work to PEAR so that others might benefit from it, you will need to adhere to them, too, so it's not a bad thing to get into the habit straightaway.

The PEAR Web site goes into great detail on the subject of coding standards, so we'll just explore some of those here. The following sections should get you into the right habits, in case you aren't already in them.

Control Structures, Comments, and Indenting

PEAR dictates that all code should be indented appropriately according to its context by using a suitable number of spaces (not tabs). In the case of PEAR, it's four spaces.
You may be able to configure your editor of choice such that when the tab key is pressed, four spaces are rendered instead of an actual tab character.
As well as being indented, PEAR requires that constructs err on the side of verbosity. For example, if a parenthesis is optional but its inclusion improves clarity, then its inclusion is required. White space should be used only to separate out distinct lines and thoughts, and where it is included for reason of clarity, it should be kept as minimal as possible.

Curly braces should always be used in control structures (while/if/switch/for/foreach) even when there is only a single line within the block, and that's why the use of braces is not required by the PHP interpreter. This improves clarity immeasurably. The opening brace should always begin on the same line as the start of the control block. The closing brace should fall on a new line. PEAR does not comment on whether it is appropriate to suffix the closing brace with a semicolon.

Starting and finishing blocks of PHP by means of the <?php and ?> operators should always include the optional letters php for reasons of clarity.

Comments are encouraged wherever it would improve readability to include them. Avoid using the # comment predicate, even though PHP allows it; instead, opt for C-style // (for a single-line comment) or /* and */ (to begin and end a multiline comment).

Here is an example of a PEAR-compliant control structure:

<?php
if ((($x == 14) && ($y == 19)) || $z = 24) {
    print($strMyVal. "\n");
      // This is a meaningful comment
};
?>

Function Calls and Definitions

When calling a function, there should be no space placed between the name of the function and its opening parenthesis prior to its argument list. If there are no arguments to be passed, a blank list should be used by opening and then immediately closing parentheses, as this example shows:

<?php
   $strMyVar = myFunction($strA, $strB, $strC);
   $strAnotherVar = anotherFunctionWithNoParameters();
?>

When declaring your own functions, the rules for braces in control structures are changed slightly; the starting brace for the block should begin on a new line, immediately after the function name and argument list definition. Here's an example of that syntax:

<?php
    function myFunction($strArg1, $strArg2)
    {
        return($strMyResult);
    };
?>

In the argument list, arguments that are optional should be designated by using the $strArg = " methodology. Optional arguments should always be last in the argument lists.

Finally, functions should always return a value if it is meaningful to do so. At the very least, a function should return whether or not it was successful in its designed endeavors.

Naming Conventions

Naming of variables, classes, and functions in languages that have weak typing (are loosely typed), such as PHP, is of immense importance.

PEAR conventions require the following:
  • Classes should begin with a capital letter, such as Publisher. If the class has hierarchical placement within PEAR, this should be reflected in the name, such as HTML_TreeMenu, where TreeMenu is a component within the HTML hierarchy of PEAR.
  • Functions should be named using the name of their parent package followed by an underscore (if applicable), and then a descriptive name without underscores; the descriptive name should have its first word or syllable lowercase, with the first letter of each subsequent word or syllable (as appropriate) capitalized (XML_RPC_serializeData) Private member functions or variables of classes accessed only from inside other methods of that class should be prefixed with an underscore ($this->_currentStatus).
  • Constants should be capitalized, with underscores used to separate words, such as $HOME_DIRECTORY.
PEAR does not voice an opinion about naming convention for working variables. Many PEAR authors have adopted Hungarian Notation whereby the expected contents of a variable are alluded to in its name; for example, $strMyVariable is designed to hold a string, and $intMyNumber is for holding a number.

The authors strongly suggest you adopt this method, except where it is awkward to do so; for example, counter variables for for loops might reasonably make use of $x, $y, and $z, but these should never be used for variables holding meaningful integers.

Monday, August 22, 2011

Chapter 14: PEAR

There'll be times when, as an aspiring PHP developer faced with a new project, you'll think that the mountain ahead of you seems almost impossible to climb.

Finding the right psychological starting point can be tricky for the best of us, but one of the most satisfying things to discover at the start of the project is that half of what you figured you'd have to write has already been written.

As your portfolio of PHP projects matures, you'll find that you can reuse much of your own code from previous projects (yet another reason why taking a modular, object-oriented approach to your work is highly recommended). So-called "search-and-replace" development has saved thousand of engineer hours over the years.

There will be times, of course, when you simply have nothing to fall back on. Some components can be immensely time-consuming to develop without actually contributing to the core ethos of your project. That is when the "blank canvas" syndrome can kick in, and the urge to make just one more cup of coffee before you get started takes hold.

Thankfully, there is an answer: a repository of thousands of useful, fully tested, re-usable PHP components, all of which are broadly free to use and include in your own application.
In this chapter, you explore this repository; and discover why it exists, and how it can help you. You find out when—and when not—to use it. Most importantly, you learn how to identify the components most relevant to your project, and how to integrate them into your application.

Monday, August 15, 2011

Putting it all Together

You'll recall from the encapsulation discussion in Chapter 12 that it's always a good idea to protect your data in private member variables and use functions to access all of the properties of your classes. You also looked at using __get and __set to make this process a bit easier. You can see just by looking at the class diagrams shown earlier that all of your classes have fairly simple property requirements and that most of them could benefit from the approach you took in implementing the Entity class earlier.

Because you know about inheritance and how to use it to your advantage in PHP, let's remove all that clever functionality from being tied to just the Entity class and create a class that will enable you to reuse the code in other classes. You originally called this class PropertyObject.

The PropertyObject Class

The original PropertyObject class and the Entity class that followed it had no unified facility for data validation, so you need an interface for anything that can be validated. Here's the code:

<?php
  interface Validator {
    abstract function validate();
  }
?>

Name the file interface.Validator.php. Now put it to some use with the new and improved PropertyObject class. The code that follows shows how to integrate the Validator interface with the PropertyObject class. Enter the code in a file called class.PropertyObject.php.

<?php
  require_once('interface.Validator.php');

  abstract class PropertyObject implements Validator {

     protected $propertyTable = array();    //stores name/value pairs
                                            //that hook properties to
                                            //database field names

    protected $changedProperties = array(); //List of properties that:
                                            //have been modified

    protected $data;                        //Actual data from
                                            //the database

    protected $errors = array();            //Any validation errors
                                            //that might have occurred

    public function __construct($arData) {
      $this->data = $arData;
    }

    function __get($propertyName) {
      if(!array_key_exists($propertyName, $this- propertyTable))
        throw new Exception("Invalid property\"$propertyName\"!");

      if(method_exists($this, 'get' . $propertyName)) {
        return call_user_func(array($this, 'get' . $propertyName));
      } else {
        return $this->data[$this->propertyTable[$propertyName]];
      }
   }

     function __set($propertyName, $value) {
       if(!array__key_exists ($propertyName, $this->propertyTable) )
          throw new Exception("Invalid property\"$propertyName\"!");

       if(method_exists ($this, 'set'.$propertyName) ) {
          return call_user_func(
                    array($this, 'set'.$propertyName),
                    $value
                        );
       } else {

         //If the value of the property really has changed
         //and it's not already in the changedProperties array,
         //add it.
         if($this->propertyTable[$propertyName] != $value &&
            !in_array ($propertyName, $this->changedPropertirs)) {
            $this->ChangedProperties [ ] = $propertyName;
         }

          //Now set the new value
         $this->data[$this->propertyTable[$propertyName]] = $value;

       }
     }

     function validate()
     {}

   }
?>

Take a closer look at what's happening here. You've created four protected member variables. Protected member variables are only visible to subclasses of a class; they aren't visible to the code that uses those objects.

$propertyTable will contain a mapping of real property names to field names in your database. Often the names of database fields have been prefixed with a naming convention to indicate their data type. For example, entities.sname1 might be a field in the table entities of type string, containing the first name. However, sname1 isn't a terribly friendly name for an object property, so you need to provide a mechanism to translate database field naming conventions to friendly property names.

$changedProperties serves the same purpose as in previous implementations of this class. It's an array that stores a list of the names of the properties that have been modified.

$data will be an associative array of database field names and values. The array will be supplied to the constructor with the data structure coming directly from pg_fetch_assoc() (or mysql_fetch_assoc(), or any of the other [x] _fetch_assoc() database functions). This makes constructing a useful object directly from a database query a lot easier, as you'll see shortly.
The last member variable, $errors, will contain an array of field names and error messages in the event that the validate method (required by the Validate interface) should fail.

The class is declared abstract for two reasons. The first is that the PropertyObject class on its own is not very useful. The classes that extend PropertyObject still have some work to do before you can use them. The second is that you have not provided an implementation of the required method validate(). Because you are still labeling that method abstract in PropertyObject, you must also label the class itself abstract, forcing all inheriting classes to implement that function. Any attempts to use classes that extend PropertyObject but not implement that function will cause a runtime error.

Next, you see the greatly simplified constructor. All the constructor does is accept the associative array that will most likely be populated from a database query and assign it to the protected member variable $data. Most subclasses of PropertyObject will need to override the constructor and do something a little more interesting.

The other major change is in the internals of the_get() and_set() functions. Because you are storing data in the $data member, you need to be able to map property names to the actual field names in the database. The lines that contain the code $this->data [ $this->propertyTable [ $ propertyName ] ] are doing just that. By fetching and assigning values to the $data member using their database field names, rather than their property names you can easily implement the sort of automatic database persistence that you saw previously in the Entity class.

If the workings of the $data and $propertyTable members aren't clear to you yet, don't worry. They will be as soon as you see an example.

The Contact Type Classes

Now that you have the handy-dandy PropertyObject class, you can start putting it to some use. The files that follow are for the Address, EmailAddress, and PhoneNumber classes.
In the code you'll see a reference to a class called DataManager, which is going to be a wrapper class for all the database functions you need to use. That enables you to have one central place for all your data-interaction code. You'll examine that class in just a bit.
Enter the code that follows (the Address class) into a file called class. Address.php:

<?php
  require_once('class.PropertyObject_php');

  class Address extends ProportyObject {

    function__construct($addressid) {
      $arData = DataManager: :get.AddressData ($addressid);

      parent::__construct($arData);

      $this->propertyTable['addressid'] ='addressid';
      $this->proportyTable['id'] ='addressid';
      $this-->propertyTable['entityid'] = 'entityid';
      $this->propertyTable['address1'] ='aaddress1';
      $this->propertyTable['address2'] = 'saddress2';
      $this->propertyTable['city'] = 'scity';
      $this->propertyTable['state'] ='cstate';
      $this->proportyTable['zipcode'] = 'spostalcode';
      $this->proportyTable['type'] = 'stype';
    }

    function validate() {
      if(strlen($this->state)!=2) {
        $this->errors['state'] = 'Please choose a valid state.';
      }

      if(strlen($this->zipcode) < 5 ||
        strlen($this->zipcode)>10){
        $this->errors['zipcode'] = 'Please enter a 5 or 9 digit zipcode';
     }

     if(!$this->address1) {
           $this->errors['address1'] = 'Address 1 is a required field.';
     }

     if(!$this->city) {
           $this->errors['city'] = 'City is a required field.';
     }

     if(sizeof (this->errors)) {
       return false;
     } else {
       return true;
     }
   }

   function __toString() {
     return $this->address1 . ', ' .
            $this->address2 . ', ' .
            $this->city . ', ' .
            $this->state . ' ' . $this->zipcode;
   }
  }
?>

Because the PropertyObject class took care of so much of the work, only two methods in the Address class needed to be implemented (a __toString() implementation was thrown in just for fun). In the constructor, you see for the first time how the $propertyTable array works. The list of properties required in the class was specified in the UML diagram created during the initial architecture of the application (in the beginning of this chapter). Based on the properties this object has, you can also make some decisions about the structure of the database table. Generally, you need one field for each property and because this class has to relate back to the Entity class, you also need to store some reference to the parent Entity. Let's assume that you have the following SQL statement to create the Address table:

CREATE TABLE entityaddress (
  addressid int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  entityid int,
  saddress1 varchar(255),
  saddress2 varchar(255),
  scity varchar(255),
  cstate char(2),
  spostalcode varchar(10),
  stype varchar(50),
  CONSTRAINT fk__entityaddress_entityid
    FOREIGN KEY (entityid) REFERENCES entity(entityid)
);

A properly named database field indicates its data type by using a one-character prefix, letting you know what kind of data goes in the field. Having naming conventions is just as important for database design as it is for your code.

The propertyTable array is set up in the Address class to map friendly property names (like city, state, and ZIP code) to the less friendly database field names (like scity, estate, and spostalcode). Note that you can map multiple property names to the same database field name in propertyTable. This enables you to refer to the primary key of the address by either $objAddress->addressid or $objAddress->id.

What's incredibly exciting about the Address class is that the overwhelming majority of the code is spent implementing business logic and data validation. There's almost no extraneous code here. Its sole responsibility is to populate itself and validate its own contents. Everything else is left up to the DataManager class (which you'll see in detail shortly) and the PropertyObject.

The code for the Email class, which looks very similar, follows. Enter it into a file called class.EmailAddress.php.

<?php
  require_once('class.PropertyObject.php');

  class EmailAddress extends PropertyObject {

    function __construct($emailid) {
      $arData = DataManager::getEmailData($emailid);

      parent::__construct($arData);

      $this->propertyTable['emailid'] = 'emailid';
      $this->propertyTable('id'] ='emailid';
      $this->propertyTable['entityid'] ='entityid';
      $this->propertyTable['email'] = 'semail';
      $this->propertyTable ['type' ] = 'stype';
    }

    function validate() {
      if(!$this->email) {
            $this->errors['email']='You must set an email address.';
      }

      if(sizeof($this-->errors)) {
         return false;
      } else {
        return true;
      }
    }

    function __toString() (
      return $this->email;
    }
  }
?>

There's very little ancillary code here, just fetching the code from the database and setting up the propertyTable. Everything else is data validation. Again, the UML diagram was the guide to deciding on the properties of the Email class and the structure of the corresponding database table.

The database table for the entityemail table looks like this:

CREATE TABLE entityemail   (
  emailid int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  entityid int,
  semail varchar(255),
  stype varchar(50),
  CONSTRAINT fk_entityemail_entityid
    FOREIGN KEY (entityid) REFERENCES entity(entityid)
);

The PhoneNumber class works very much like Address and Email. Here's the code to enter the code into class.PhoneNumber.php:

<?php
  require_once('class.PropertyObject.php');

  class PhoneNumber extends PropertyObject {

    function__construct($phoneid) {
      $arData = DataManager::getPhoneNumberData($phoneid);

      parent::__construct($arData);

      $this->propertyTable['phoneid'] = 'phoneid';
      $this->propertyTable['id'] = 'phoneid';
      $this->propertyTable['entityid'] = 'entityid';
      $this->propertyTable['number'] = 'snumber';
      $this->propertyTable['extension'] = 'sextension';
      $this->propertyTable['type'] = 'stype';
    }

    function validate() {
      if(!$this->number) {
          $this-->errors['number'] = 'You must supply a phone number.';
      }

      if(sizeof($this->errors)) {
        return false;
      } else {
        return true;
      }
    }

    function__toString() {
      return $this-->number .
             ($this->extension ? ' x' . $this->extension : '');
    }
  }
?>

And here's the SQL statement to create the entityphone table:

CREATE TABLE entityphone (
  phoneid int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  entityid int,
  snumber varchar(20) ,
  sextension varchar(20),
  stype varchar(50),
  CONSTRAINT   fk_entityemail_entityid
    FOREIGN KEY (entityid) REFFRENCES entity (entityid)
);

The DataManager is used to self-populate the data, set up the propertyTable, and define some validation rules. A__toString implementation is created to make it easier to print out complete phone numbers with their extensions later on.

The DataManager Class

Let's have a look at that DataManager class. It and the other database code samples in this chapter have been using MySQL, although a class like it would work just as well with PostgresSQL, Oracle, or any other RDBMS. The primary responsibility of the DataManager class is to put all the data access code into a single location, making it much easier to change the database type or connection parameters later on. All of the class's methods have been declared static because the class doesn't rely on any member variables. Note the use of the static function variable in getConnection(). This is done to ensure that only one database connection is open during a single page request. There's a lot of overhead associated with establishing a database connection so eliminating unnecessary connections helps improve performance. Create a file class.DataManager.php: and enter the following class code:

<?php
require_once('clasa.Entity.php');//this will be needed later
require_once('class.Individual.php');
require__once('class.Organisation.php');

class DataManager {
  private static function_getConnection(){
    static $hDB;

    if(isset ($hDB)) {
      return $hDB;
    }

    $hDB = mysql_connect('localhost', 'phpuser', 'phppass')
      or die("Failure connecting to the database!");
$Link = mysqI select_db('sample_db') or die('Failure selecting the
database!');
    return $hDB;
  }

  public static   function getAddresaData($addressID) {
    $sql = "SELECT * FROM entityaddress WHERE addressid = $addressID" ;
    $res = mysql_query($spl, DataManager::_getConnection());
    if(! ($res && mysql_num_rows($res)}) {
      die("Failed getting address data for address $addressID");
    }

    return mysql_fetch_assoc($res);
  }

  public static function getEmailData($emailID) {
    $sql = "SELECT * FROM entityemail WHERE emailid = $emailID";
    $res = mysql_query($sql, DataManager::_getConnection());

    if(! ($res && mysql_num rows($res))) {
      die("Failed getting email data for *email $emailID");
    }

    return mysql_fetch_assoc($res);
  }
}
?>

The DataManager class provides the data structures used to populate the $data member of your PropertyObject subclasses. There are separate functions to return the data for each of the types. You'll be adding a few new functions to this class a bit later.

All of the methods of this class are declared to be static. Remember, static methods are those requiring all member variables to be static. You don't need to instantiate static classes to use their methods. There are several cases where this makes sense. Consider a class called Math that exposes methods such as squareRoot(), power(), and cosine(), and has properties including the mathematical constants e and pi. All instances of this class perform the same math. The square root of 2 doesn't change, 4 raised to the 3rd power is always going to be 64, and the two constants are, well, constant. There's no need to create separate instances of this class because its state and properties never change. A class called Math implemented in this manner should allow for all its functions to be called statically.

The DataManager class is much the same. All of the functions are self-contained, and all of the member variables they interact with are static. The class exposes no properties. You can invoke the methods of the class using the static method operator :: as a result. Because all the methods you've created are static, you never need to instantiate an object with $obj = new DataManager(); you can use the syntax DataManager::getEmail() instead.
Note the use of a static function variable in the private method_getConnection(). The use of static function variables was discussed in Chapter 6.

The Entity, Individual, and Organization Classes

With all the supporting classes in place, you can move on to the core of the application: the Entity class and its subclasses.

First, make sure you're updating your UML diagram as you make changes to the object hierarchy. You've created the PropertyObject class and made all of your classes subclasses of it with one exception—the new DataManager class, which does not inherit from anything. The PropertyObject implements an abstract interface called Validator

Now you can begin to develop the Entity, Individual, and Organization classes. The following code shows the fully implemented Entity class:

<?php

require_once ('class.PropertyObject.php');
require_once ('class.PhoneNumber.php');
require_Once('class.Address.php');
require_once('class.Email.Address.php');

abstract class Entity extends PropertyObject {

  private $_emails;
  private $_addresses;
  private $_phonenumbers;
  public function__construct($entityID) {
    $arData = DataManager::get,EntityData ($entity.ID);

    parent::__construct($arData);

    $this->propertyTable['entityid'] = 'entityid';
    $this->propertyTablet'id'] = 'entityid';
    $this->propertyTable['name1'] = 'sname1';
    $this->propertyTable['name2'] = 'sname2';
    $this->propertyTable['type'] = 'ctype';

    $this->_emails = DataManager::getEmailObjectsForEntity($entityID);
    $this->_addresses = DataManager::getAddressObjectsForEntity($entityID);
    $this->_phonenumbers = DataManager::getPhoneNumberObjectsForEntity
    ($entity!D);

  }

    function setID($val) {
      throw new Exception('You may not alter the value of the ID field!');
    }

    function setEntityID($val) {
    $this->setID($val);
    }

    function phonenumbers{$index) {
      if(!isset ($this->_phonenumbers[$index])){
        throw new Exception('Invalid phone number specified!');
    } else {
        return $this->_phonenumbers[$index];
      }
    }

    function getNumberOfPhoneNumbers() {
      return sizeof ($this->_phonenumbers);
    }

    function addPhoneNumber(PhoneNumber $phone) {
      $this->_phonenumbers[] = $phone;
    }

    function addresses($index) {
      if(!isset($this->__addresses [$index]) ) {
        throw new Exception('Invalid address specified!');
      } else {
        return $this-->,_addresses[$index];
      }
    }

    function getNumberOfAddresses() {
      return sizeof($this->_addresses);
    }
    function addAddress(Address $addresa) {
      $this->_addresses[] = $address;
    }

    function emails($index) {
      if(!inset($this->_emails[$index])} {
        throw new Exception('Invalid email specified!');
      } else {
         return $this->_emails[$index];
      }
    }

    function get.NumberOfEmails() {
      return sizeof($this->_emails);
    }

    function addEmail(Email $email) {
      $this->_emails[]=$email;
    }

    public function, validate() {
      //Add common validation routines
    }

  }
?>

By moving all of the property getting and setting functionality to the parent PropertyObject class, you simplify the Entity class and ensure that it is focused only on the code required to implement an entity.

The Entity class is declared abstract because it isn't useful on its own. All entities are either Individuals or Organizations. You do not want to be able to instantiate objects of class Entity. Declaring it abstract prevents the class from being instantiable.

You've added requests to a few new functions of the DataManager:getEntityData() and get[x]ObjectsForEntity.getEntityData() returns the data required to instantiate an entity, just like functions you've already seen for the contact types. The following shows the code for the new functions in class.DataManager.php:

// top of file omitted for brevity
   ...
      die("Failed getting phone number data for phone $phoneID");
   }

   return pg_fetch_assoc($res);
 }

public static function getEntityData($entityID) {
    $sql = "SELECT * FROM entities WHERE entityid = $entityID";
    $res = mysql_query($sql, DataManager::_getConnection());
    if(!($res && mysql_num_rows ($res))) {
      die("Failed getting entity $entityID");
    }
    return mysql_fetch_assoc($res);
 }
?>
To add the get[x]ObjectsForEntity functions, place the following code at the end of class.DataManager.php, just after the getEntityData function:
public static function getAddressObjectsForEntity($entityID) {
  $sql = "SELECT addressid from entityaddress WHERE entityid = $entityID";
  $res = mysql_query($sql, DataManager::_getConnection());
  if(!$res) {
    die("Failed getting address data for entity SentityID");
  }

  if(mysql_num_rows($rea)) {
    $objs = array();
    while($rec = mysql_fetch_assoc($res)) {
      $objs[] = new Address($rect'addressid']};
    }
    return $objs;
  } else {
    return array();
  }
}

public static function getEmailObjectsForEntity($entityID) {
  $sql = "SELECT emailid from entityemail WHERE entityid = $entityID";
  $res = mysql_query($sql, DataManager::_getConnection());
    if(!$res) {
    die("Failed getting email data for entity $entityID");
  }

  if (mysql_num_rows ($res)) {
     $objs = array();
     while($rec = mysql_fetch_assoc($res)) {
       $objs[] = new EmailAddress($rec['emailid']);
    }
    return $objs;
  } else {
    return array();
  }
}

public static function getPhoneNumberObjectsForEntity($entityID) {
  $sql = "SELECT phoneid from entityphone WHERE entityid = $entityID";
  $res' = mysql_query($sql, DataManager::_getConnection());
  if(!$res) {
    die("Failed getting phone data for entity $entityID");
  }
  If(mysql_num_rows($res)) {
    $objs = array();
    while($rec = mysql_fetch_assoc($res)) {
      $objs[] = new PhoneNumber($rec['phoneid']);
    }
    return $objs;
  } else {
    return array();
  }
}

These functions take an entity ID value. They query the database to determine if any e-mails, addresses, or phone numbers exist for the entity in question. If so, they build an array of EmailAddress, Address, or PhoneNumber objects by passing each id to the constructor for the appropriate object type. This array is then passed back to the Entity object where it is stored in the appropriate private member variable.

With the Entity class doing all the heavy lifting, the remaining work is fairly simple. You just need to implement the Individual and Organization classes. Create a file called class.Individual.php and enter the following:

<?php
  require.once('class.Entity.php');
  require_Once('class.Organization.php');

  class Individual extends Entity {

    public function _construct($userID) {
      parent:: __eonstruct($userID);

      $this->propertyTable['firstname']='name1';
      $this->propertyTable['lastname']='name2';

    }

    public function __toString() {
      return $this->firstname . ' ' . $this->lastname;
    }

    public function.get'Employer(){
      return DataManager::getEmployer($this->Id);
    }

    public function validate() {
      parent::validate();

      //add individual-specific validation

    }

  }
?>

Short and sweet. Inheritance makes this easy. The Individual class sets up a few new properties that make it easier to access the first and last name of the individual, instead of having to use the rather ugly name1 and name2 properties defined in the Entity class. It also defines a new method, getEmployer(), which requires a new function in the DataManager. You get to that function as soon as you have your Organization class, which is shown in the following code. Create a file called class.Organization.php, and enter this code into it:

<?php
  require_once('class.Entity.php');
require_once ('class.Individual.php');

  class Organization extends Entity {

    public function __construct ($userID) {
      parent: __Construct($userID);

      $this->propertyTable['name'] = 'name1';

    }

    public function __toString() {
      return $this->name;
    }

    public function getEmployees() {
      return DataManager::get.Employees($this->id);
    }

    public function validate() {
      parent::validate();
      //do organization-specific validation
    }

  }

?>

Again, a fairly simple class, thanks to the power of inheritance. You declare a property called name that makes it easier to obtain the one and only name that an organization has (the sname2 property goes unused for organizations).

To add the functions getEmployer() and getEmployee() to the DataManager class, append the following code to the end of class.DataManager.php:

public static function getEmployer($individualID) {
    $sql = "SELECT organizationid FROM entityemployee " .
           "WHERE individualid = $individualID";
    $res = mysql_query($sql, DataManager::_getConnection());
    if(! ($res; && mysq1_num_rows($res))) {
       die("Failed getting employer info for individual $individualID");
    }

    $row = mysql_fetch_assoc($res);
    if($row) {
      return new Organization($row['organizationid']);
    } else {
      return null;
    }
}

  public static function getEmployees ($orgID) {
    $sql = "SELECT individualid FROM entityemployee" .
           "WHERE organizationid = $orgID";
    $res = mysql_query($sql, DataManager::_getConnection());
    if(! ($res && mysql__num_rows($res))) {
      die("Failed getting employee info for org $orgID");
    }

    if(py_num_rows($res)) {
       $objs = array();
       while($row = mysql_fetch_assoc($res)) {
         $objs[] = now Individual($rowt'individualid']);
       }
       return $objs;
     } else {
       return array();
     }
  }
These two functions rely on the presence of a table entityemployee, shown here:
CREATE TABLE entityemployee (
  individualid int NOT NULL,
  organizationid int NOT NULL,
  CONSTRAINT fk_entityemployee_individualid
    FOREIGN KEY (individualid) REFERENCES entity (entityid),
  CONSTRAINT fk_entityemployee_organizationid
    FOREIGN KEY (organizationid ) REFERENCES entity(entityid)
);

The table relates individuals to the organizations by which they are employed. The DataManager functions are similar to code you've already seen.

There's one last function needed to make the entire system workthe DataManager method for listing all the entities in the database. It's called getAllEntitiesAsObjects() and it finishes all the work you need to do on your objects:

public static function getAllEntitiesAsObjects() {
    $sql = "select entityid, type from entities";
    $res = mysql_query ($sql; DataManager: :_getConnection() );
    if(!$res) {
      die("Failed getting all entities");
    }
    if(mysql_num_rows($res)} {
      $objs = array();
      while($row = mysql__fetch_assoc($res)) {
        if($row['type'] == 'I') {
          $objs[] = new Individual($row['entityid']);
        } elseif ($row['type'] == '0') {
          $objs[] = new Organization($row['entityid']);
        } else {
          die("Unknown entity type {$row['type']} encountered!");
        }
      }
      return $objs;
    } else {
      return array();
    }
}
DataManager enables you to enumerate over all of the contacts in your system. It examines the value of the ctype field in the entity table to determine if the entry is an Individual or an Organization, and then instantiates an object of the appropriate type and adds it to the array that the function returns.

Making Use of the System

By now you can see the real power of an OOP approach. The following code (test.php) will display a view, albeit a rather ugly view, of all of the contacts in your database with all of their contact details:

<?php
  require_once('class.DataManager.php');//everything gets included by it

function println($data) {
  print $data . "<br>\n";
}

$arContacts = DataManager::getAllEntitiesAsObjects();
foreacb.($arContacts as $objEntity) {

  if(get_class($objEntity) == 'individual') {
    print "<h1>Individual - ($objEntity->_toString()}</h1>";
  } else {
    print "<h1>0rganization - {$objEntity->_toString() }</h1>";
  }

  if($objEntity->getNumberOfEmails()) {
    //We have emails! Print a header
    print "<h2>Emails</h2>";

    for($x=0; $x < $objEntity->get.NumberOfEmails(); $x++) {
      println($objEntity->emails($x) ->_toString());
    }
  }

  if($objEntity->getNumberofAddresses()) {
    //We have addresses!
    print "<h2>Addresses</h2>";
    for($x=0; $x < $objEntity->getNumberOfAddresses(); $x++) {
      println($objEntity->addresses($x)->__toString());
    }
  }

  if ($objEntity->getNumberOfPhoneNumbers()) {
     //We have phone numbers!
     print "<h2>Phones</h2>";

     for($x=0; $x < $objEntity->getNumberOfPhoneNumberS(); $x++) {
       println($objEntity->phonenumbers($x)->__toString());
     }
  }
  print "<hr>\n";

}
?>

In only 36 lines of code you can display nearly everything there is to show about the entities in your system. Employer and employees aren't shown here, but are left as an exercise for the reader. The line that calls the get_class function should give you some ideas for figuring out which class you're dealing with so you'll know whether to call getEmployer() on an Individual, or getEmployees() on an Organization.