Communication as a Class Hierarchy

There are many possibilities to consider if you are to construct a class hierarchy. You need to understand that although communications have much in common, fundamental differences exist between one media form and another, as do totally different methods of addressing.

The Recipient Class: A Quick Test of OOP-Like Thinking

All messages have recipients (and often more than one). Given that these recipients are clearly more involved than strings, it would be nice to have a class to represent them with such useful methods as isValid() to determine whether the recipient is a valid one.

The difficulty is obvious. How are you to implement such a class effectively when so much variation in the data is required?

You could create a single Recipient class covering all possible scenarios, but this isn't particularly OOP-compliant because large numbers of member methods and variables are peculiar to one form of communication and irrelevant to others. The solution must be to have some kind of generic Recipient class, with each special case having a class in its own right. But how do you relate these special-case recipients to the master Recipient class?

First, reject the use of traditional class inheritance. Although technically feasible, it's still a misuse of OOP design. Superclasses classes that are extended to form subclasses must be classes in their own right. Ford is a subclass of superclass Car, but although Ford may contain additional or even overloaded methods or member variables, certain characteristics of Car indicate that it is (or once was) an object of use in its own right. It has its own methods (such as Drive and Shift) and its own member variables (such as Registration and Color), and these are not likely to be overloaded. A recipient of a particular ilk (such as that for an e-mail) could have member variables and methods of its own just like the Ford, but it would not inherit any of them from a parent generic recipient superclass. For reasons that a glance at the table in the previous section should make apparent, a generic recipient cannot have any useful member variables or methods,. Accordingly, we must reject the use of class inheritance.

The answer becomes apparent if you consider the bigger picture. What is your communications framework likely to want from these recipient objects? In fact, you can limit it to just two methods that will be accessed by the communications framework isValid() and getStringRepresentation(). These two would return a usable string representation of the framework, dependent on the context. Any other methods (setHouseNumber, for example) are for use by your application solely for populating the recipient. You can quite safely say that the communications framework will never touch them.

So the task can be summarized as follows. Create some means for a number of distinct classes representing recipients of different types of communications media to be universally accessed by an external framework of other classes, such that additional distinct classes may be added at a later date without any modification to that external framework.

The solution is to create an interface for the generic recipient class (not really a class at all). That interface provides definitions for these two key methods (isValid() and getStringRepresentation()) that the individual recipient objects must provide exactly as shown.

Consider the following code snippet, which provides just such a generic interface. You may wish to save this code as recipient.phpm at this stage:

   interface Recipient {       public function isValid();       public function getStringRepresentation();    } 

You might be tempted to implement an abstract recipient class to almost exactly the same effect, but this would be bad design rearing its ugly head once more. Again, remember that the recipient as you understand it has no useful properties and no useful methods. It would be fatuous to try to extend it in any way that infers class status when you merely want to provide a common means for other classes to interface with a collection of unrelated classes in some common way. If you have doubts as to the lack of commonality of the various recipient classes, refer to the table earlier in this section.

The EmailRecipient Class

Take a look at how to implement this. You won't reproduce every single type of recipient, so look at a simple one the EmailRecipient class. Start it by declaring a class that implements the Recipient interface we declared previously:

   class EmailRecipient implements Recipient { 

Add some member variables to use to store important properties of your e-mail recipient. You saw in the table appearing earlier in the chapter that an e-mail recipient has both a recipient name and a recipient address:

   private $recipient_name;    private $recipient_address; 

Now you must provide an implementation of every method declared in the Recipient interface. In fact, there are just two. The first will validate your recipient after it has been set up by whatever application is using it:

     public function isValid() {        if (preg_match("/[\<\>\r\n]{1,}/", $this->recipient_name)) {          return(false);        };        if (preg_match("/^([A-Z0-9._%-]+)()([A-Z0-9._%-]+)(\.)([A-Z0-9._%-    ]{2,4})$/i", $this->recipient_address)) {          return(true);        } else {          return(false);        };      } 

What's going on here is straightforward. A couple of regular expression matches are performed on the recipient. First, the recipient name is validated to ensure that it consists of one or more valid characters. Then, the e-mail address is checked against a regular expression to ensure its conformity to RFC 822 format.

Next you need to provide some means to get an intelligent (that is, readily usable) string representation of your recipient.

   public function getStringRepresentation() {      $strMyRepresentation = "";      if ($this->recipient_name) {        $strMyRepresentation .= $this->recipient_name."";      };      $strMyRepresentation .= "<" . $this->recipient_address . ">";      return($strMyRepresentation);    } 

As you might be aware, the readily accepted format for e-mail addresses is First Name Last Name "><>, so we stick to that format here.

The remainder of the class is dedicated to accessor methods that is, methods to get and set the various properties of the e-mail recipient. As you can see, we have provided an easy means to set all these within the constructor. For other recipients, such as with those of a snail mail letter, doing this may be cumbersome.

     public function __construct($strRecipientAddress, $strRecipientName = "") {        $this->recipient_name = $strRecipientName;        $this->recipient_address = $strRecipientAddress;      }      public function setRecipientName($strRecipientName) {        $this->recipient_name = $strRecipientName;      }      public function setRecipientAddress($strRecipientAddress) {        $this->recipient_address = $strRecipientAddress;      }      public function getRecipientName() {        return($this->recipient_name);      }      public function getRecipientAddress() {        return($this->recipient_address);      }    } 

That concludes the e-mail recipient class, an implementation of the generic Recipient interface. If you wanted to save this as emailrecipient.phpm at this stage, nobody would hold it against you.

Even though you haven't reached the communication framework yet, you can test this by using the following snippet of sample code. You need to use require() in both the Recipient interface and EmailCommunication class that you've saved so far.

   $objEmailRecipient = new EmailRecipient("", "Ed Lecky-    Thompson");    if ($objEmailRecipient->isValid()) {      print "Recipient is valid! ";      print "The string representation of this recipient would be: " .    htmlentities($objEmailRecipient->getStringRepresentation());    } else {      print "Recipient is not valid!";    }; 

This yields the following output:

   Recipient is valid! The string representation of this recipient would be: Ed    Lecky-Thompson <> 

If you take a quick look at the EmailRecipient class in more detail, you can see just how simple it is. The two private member variables are specific to this class, as are the constructor and the (somewhat superfluous) setRecipientName and setRecipientAddress accessor methods and their (less superfluous) get counterparts.

Because you have declared this class to implement the Recipient interface, you are obliged to provide the two methods so described in that interface. If you do not, PHP helpfully yields an error message.

In the getStringRepresentation method, you simply glue the pieces together to form a usable RFC822-compliant representation of the recipient's name and e-mail address. This is something you can drop straight into an SMTP session.

In the isValid method, you check the recipient's name for any bad characters that might confuse the SMTP server; then you check the e-mail address for basic syntactical validity against a regular expression.

Other Implementations

You have just implemented an EmailRecipient class to encapsulate neatly the concept of e-mail recipients. Other types of communication have their own types of recipient, and each will need an implementation of the Recipient interface, too.

You can follow a very similar procedure to implement such alternative recipient types. First, ensure that sufficient member variables exist to hold every property of that recipient type, and that you have suitable Get and Set accessor methods to read and write to those member variables. Second, implement the mandatory methods required by the Recipient interface: the validator method and the string representation method.

The Communication Class

Even though recipients are different by nature, with streamlined interface access to any and all recipient objects, you can greatly simplify your communication class.

A communication object is literally that a single communication by whatever method. We don't particularly mind which method, nor do we even allude to which ones are possible.

The communication class will never itself be instantiated. Instead, it will be extended by a class representing the particular form of communication to be used: EmailCommunication, SMSCommunication, and so forth.

Unlike the generic recipient construction, however, it will have useful methods of its own. For this reason, it really will be a genuine class and not just an interface. However, one method send will be declared as abstract so that it is implemented only in subclasses.

Subclasses of Communication may, of course, have methods and member variables of their own that supplement those of their superclass. A likely use for this are methods that somehow manipulate the message property of Communication in some way. An example is a method in SMSCommunication in which to load a bitmap (.BMP) file to send as a cellular phone operator logo in place of a standard text message.

Take a look at the following code. Note that it makes use of the Collection and Collection Iterator discussed in Chapters 5 and 6. If you want the following to compile, you'll need to require() that code somewhere along the way.

   abstract class Communication {      protected $arRecipientCollection;      private $strMessage;      protected $strErrorMessage;      protected $errorCode;      abstract public function send();      public function __construct() {        $this->strMessage = "";      }      public function addRecipient($objRecipient, $strIdentifier = "") {        $strRecipient = $objRecipient->getStringRepresentation();        if (!$strIdentifier) {          $strIdentifier = $strRecipient;        };        $this->arRecipientCollection->addItem($objRecipient, $strIdentifier);      }      public function removeRecipient($strIdentifier) {        $this->arRecipientCollection->removeItem($strIdentifier);      }      protected function _setMessage($strMessage) {        $this->strMessage = $strMessage;       }      protected function _getMessage() {        return($this->strMessage);      }      public function getErrorMessage() {        return($this->strErrorMessage);      }      public function getErrorCode() {        return($this->errorCode);      }    } 

As you can see, you declare the recipient collection as protected. This means that although it cannot be tinkered with from within your application's code, it can be tinkered with by subclasses of the Communication class which is a good thing.

You probably have noticed how we are brazenly adding recipient objects passed to addRecipient to the recipient collection, even though we have absolutely no idea of what type that object is, and equally no idea of what type of collection arRecipientCollection is. You may well think this is a recipe for runtime errors but, in fact, it is the subclass that will initialize the collection, and it will make it a collection of whatever it wants. As long as whatever is passed as a recipient object supports the interface developed earlier and is of the same type as the type required by the subclass, there will be no problems.

Use the string representation of the object as a suitable key for the collection. The message property remains private and can be manipulated using _setMessage and _getMessage, accessible only from the subclass. Don't worry about the error message and error code member variables yet. You'll meet those a bit later.

So how do you use this in practice? Quite simply, you extend it to form a useful subclass. Take a look at EmailCommunication, which, as the name suggests, is used for sending a message to one or more EmailRecipient objects.

E-mailing Your Users

Now look at EmailCommunication (the subclass to Communication) in detail. First, you need some simple way of enforcing the recipient collection of the superclass to contain only EmailRecipient objects. You can do this by reworking the Collection class slightly to form a subclass called EmailRecipientCollection. This may by definition consist only of EmailRecipient members.

   class EmailRecipientCollection extends Collection {      public function addItem(EmailRecipient $obj, $key = null) {        parent::addItem($obj, $key);      }    } 

Note that this offers no new functionality. It merely offers the enforcement through Class Type Hints as to the class type that new members must be.

With this class now understood by PHP, you can implement the EmailCommunication class itself.

Building a Test Version

It's a good idea to first build a reduced-functionality implementation to test the OOP interfaces. After all, if you can get that right, the rest should be fairly straightforward. By using some strategic printouts, you can see what's going on and check that the logic works.

After you are certain the logic and, hence, the OOP integration and interfaces work all right, you can concentrate on the implementation; that is, getting the e-mail out the door.

Start class in a fairly standard manner, declaring it and its two properties: one a visible address and one a collection of visible carbon copy addresses. You do not need to worry about blind carbon copy addresses; they are not visible and thus can be handled solely by the parent Communication class.

   class EmailCommunication extends Communication {     private $objApparentPrimaryRecipient;         // Visible To: address     private $arObjApparentSecondaryRecipients;    // Visible Cc: address(es) 

Next, provide a means to set the primary recipient of the e-mail. Note that the class adds to the main recipient collection of the parent class (generic), while also setting the primary visible recipient (specific to the EmailCommunication class).

     public function setPrimaryRecipient($objRecipient) {        if (!($this->arRecipientCollection->exists($objRecipient-    >getStringRepresentation()))) {          parent::addRecipient($objRecipient);        };        $this->objApparentPrimaryRecipient = $objRecipient->__clone();      } 

The methods for adding and removing carbon copy (Cc:) recipients are much the same. Again, you manipulate both the parent collection and the local apparent recipients array.

     public function addCarbonRecipient($objRecipient) {        if (!($this->arRecipientCollection->exists($objRecipient-    >getStringRepresentation()))) {          parent::addRecipient($objRecipient);        };        if (!($this->arObjApparentSecondaryRecipients->exists($objRecipient-    >getStringRepresentation()))) {          $this->arObjApparentSecondaryRecipients->addItem($objRecipient,    $objRecipient->getStringRepresentation());        };      }      public function removeCarbonRecipient($objRecipient) {        if ($this->arRecipientCollection->exists($objRecipient-    >getStringRepresentation())) {          parent::removeRecipient($objRecipient);        };        if ($this->arObjApparentSecondaryRecipients->exists($objRecipient-    >getStringRepresentation())) {          $this->arObjApparentSecondaryRecipients->removeItem($objRecipient-    >getStringRepresentation());        };      } 

The same principle applies for adding and removing blind recipients, except that you do not touch the apparent recipients list:

     public function addBlindRecipient($objRecipient) {        if (!($this->arRecipientCollection->exists($objRecipient-    >getStringRepresentation()))) {          parent::addRecipient($objRecipient);        };      }      public function removeBlindRecipient($objRecipient) {        if (!($this->arRecipientCollection->exists($objRecipient-    >getStringRepresentation()))) {          parent::removeRecipient($objRecipient->getStringRepresentation());        };      } 

Now turn your attention to the constructor, which must initialize the superclass collection to be a collection of the appropriate type, and the local collection of apparent recipients to be another collection of the same type (though not necessarily with the same contents). Note that you also call the parent class constructor when you're done.

   public function __construct() {      // Superclass collection      $this->arRecipientCollection = new EmailRecipientCollection();      // Local collection of visible (CC:) recipients      $this->arObjApparentSecondaryRecipients = new EmailRecipientCollection();      parent::__construct();    } 

Now comes the dummy send method, used to ensure that your class works correctly:

     public function send() {        print "ACTUAL RECIPIENTS<BR><BR>";        foreach ($this->arRecipientCollection as $strRecipientIdentifier =>    $objEmailRecipient) {          print "NAME: " . $objEmailRecipient->getRecipientName() . "<BR>";          print "EMAIL ADDRESS: " . $objEmailRecipient->getRecipientAddress() .    "<BR>";        };        print "<BR><BR>APPARENTLY TO RECIPIENT<BR><BR>";        print "NAME: " . $this->objApparentPrimaryRecipient-    >getRecipientName() . "<BR>";        print "EMAIL ADDRESS: " . $this->objApparentPrimaryRecipient-    >getRecipientAddress() . "<BR>";        print "<BR><BR>APPARENT SECONDARY RECIPIENTS<BR><BR>";        foreach ($this->arObjApparentSecondaryRecipients as    $strRecipientIdentifier => $objEmailRecipient) {          print "NAME: " . $objEmailRecipient->getRecipientName() . "<BR>";          print "EMAIL ADDRESS: " . $objEmailRecipient->getRecipientAddress() .    "<BR>";        };      }    } 

Let's examine the above in a bit more detail. First, notice the member properties:

   private $objApparentPrimaryRecipient;            // Visible To: address    private $arObjApparentSecondaryRecipients; // Visible Cc: address(es) 

These two member properties exist in addition to the master recipient list inherited from the Communication superclass. They represent a single EmailRecipient object representing to whom the e-mail is directly addressed (the "To:'' line) and a collection of EmailRecipients representing any Carbon Copy (Cc:) addresses. These two member properties are not, strictly speaking, important in determining who gets the message. That is still handled by the collection in the superclass. However, they exist for the purpose of formatting the e-mail so that the "master'' and "Cc:'' recipients appear in the right place in the message headers.

The constructor method concerns itself with the setup of these two additional private member variables, as well as calls the superclass constructor for good measure.

The various methods used for adding and removing the master recipient, carbon copy recipients, and blind carbon copy recipients, all work in much the same way. They check to see whether an object of that key (the key being the string representation of the recipient object) exists in the collection before performing the relevant operation. This is achieved using the exists method provided by the collection object.

Take a look at some test addresses just to prove that it works. You can then go back and look at the way we actually send the mail. To execute the following code, you will need to require() all the classes developed so far in this chapter.

   $objEmail = new EmailCommunication;    $objEmailRecipient = new EmailRecipient("", "Ed    Lecky-Thompson");    $objEmailCCRecipient = new EmailRecipient("", "Ted    Lecky-Thompson");    $objEmailBCCRecipient = new EmailRecipient("", "Zed    Lecky-Thompson");    $objEmail->setPrimaryRecipient($objEmailRecipient);    $objEmail->addCarbonRecipient($objEmailCCRecipient);    $objEmail->addBlindRecipient($objEmailBCCRecipient);    $objEmail->send(); 

Run the preceding code, and you get output like that shown in Figure 14-1.

Figure 14-1

This looks pretty healthy. As you can see from the code, Ed is the main recipient of this e-mail while Ted's been CC'd, and Zed's been BCC'd. When Ed receives the mail, he will be able to see his name on the To: line and Ted's on the Cc: line. Ed won't know Zed got the mail. When Ted receives the mail, he will see much the same. Again, he won't know Zed was BCC'd on this important message. When Zed receives the mail, he won't see his name anywhere, but he'll be able to deduce that he must have been BCC'd on the original message.

The output from our dummy test implementation shows that our code is correct. The list of actual recipients displays all three genuine recipients of the e-mail. The primary recipient is correct, and the CC'd recipient is listed correctly, too.

Getting the Message Across

You need to do a couple of things to make the EmailCommunication class function properly.

First, get the body of the e-mail built up and slotted into the message member variable. A simple, plain text e-mail consists of two parts the header and the body. Conveniently enough, the two are separated by nothing more complex than a blank line. The header contains the sender's address, recipient's address, subject, CC: addresses, and myriad other bits and pieces.

Now add two member variables one for the subject line and one for the sender. The sender can again be an EmailRecipient object because a sender has a name and an address, same as a recipient.

   private $objApparentSender;   // Visible From: address    private $strSubjectLine;      // Subject line 

The use of some basic methods will allow users of the class to set these properties.

For the time being, assume that you're dealing with purely plain text e-mails. You add a simple method to allow public setting of the message property. The message property is synonymous with the body of the e-mail, because the headers will be managed separately.

   public function setSubject($strSubject) {      $this->strSubjectLine = $strSubject;    }    public function setMessageBody($strMessageBody) {      $this->_setMessage($strMessageBody);    }    public function setSender($objSender) {      $this->objApparentSender = $objSender->__clone();    } 

All that remains to do now is to convert the send() method into something that assembles a header, assembles a body, glues them together, and spits them out to the Internet.

How do you go about doing that? The easiest way is with PEAR's Net_SMTP package, which gels quite neatly with our setup and once again provides a good example of why PEAR is so useful in avoiding reinventing the wheel in your application development. The Net_SMTP package may already be installed in your PHP setup. If not, install it in the usual way.

The basic syntax for using the Net_SMTP package is simple. You can feed it the assembled message as-is with no changes. Just tell it the operands to use when talking to the mail server. The minimum the mail server requires is a sender and a list of recipients. The server doesn't look at your headers. This is why blind carbon copies work. Although the server is told to deliver the message to that recipient, the recipient is never listed anywhere in the headers.

Here's some basic usage of Net_SMTP, step by step, assuming that the completed message is in $full_message_content and that an array of recipients exists in $rcpt:

   if (! ($smtp = new Net_SMTP("mail"))) {        die("Unable to instantiate Net_SMTP object\n");    } 

First, you instantiate a new instance of the Net_SMTP class with the name of your SMTP server, and die if you are unable to do so for some reason.

   if (PEAR::isError($e = $smtp->connect())) {        die($e->getMessage() . "\n");    } 

Next, you make sure that you can connect to the SMTP server you just specified OK and, if not, you die.

   if (PEAR::isError($smtp->mailFrom("''))) {        die("Unable to set sender\n");    } 

In this case, the sender was set to be

   foreach($rcpt as $recipient) {        if (PEAR::isError($res = $smtp->rcptTo($rcpt))) {            die("Unable to add recipient: " . $res->getMessage() . "\n");        }    } 

Next you loop through your array of recipients, adding each into the Net_SMTP class instance and dying if any single recipient proves impossible to add.

   if (PEAR::isError($smtp->data($full_message_content))) {        die("Unable to send data\n");    }    $smtp->disconnect(); 

Finally, you send your message and disconnect from the SMTP server.

It's worth noting that this example uses "mail'' as the hostname of the remote SMTP server. If you're running on Linux or something similar, you can probably stick a reference to the hostname mail in your /etc/hosts file pointing to and find that everything works fine out of the box. Otherwise, you need to locate the nearest friendly SMTP server to relay for you and use that instead.

Now, take a look at the finished EmailCommunication class, complete with an SMTP send through Net_SMTP:

   class EmailCommunication extends Communication {      private $objApparentSender;                   // Visible From: address      private $strSubjectLine;                      // Subject line      private $objApparentPrimaryRecipient;          // Visible To: address      private $arObjApparentSecondaryRecipients;    // Visible Cc: address(es)      public function __construct() {        // Superclass collection        $this->arRecipientCollection = new EmailRecipientCollection();        // Local collection of visible (CC:) recipients        $this->arObjApparentSecondaryRecipients = new EmailRecipientCollection();        parent::__construct();      }      public function setPrimaryRecipient($objRecipient) {        if (!($this->arRecipientCollection->exists(    $objRecipient->getStringRepresentation()))) {          parent::addRecipient($objRecipient);        };        $this->objApparentPrimaryRecipient = $objRecipient->__clone();      }      public function addCarbonRecipient($objRecipient) {        if (!($this->arRecipientCollection->exists(    $objRecipient->getStringRepresentation()))) {          parent::addRecipient($objRecipient);        };        if (!($this->arObjApparentSecondaryRecipients->exists(    $objRecipient->getStringRepresentation()))) {          $this->arObjApparentSecondaryRecipients->addItem(    $objRecipient, $objRecipient->getStringRepresentation());        };      }      public function removeCarbonRecipient($objRecipient) {        if ($this->arRecipientCollection->exists(    $objRecipient->getStringRepresentation())) {          parent::removeRecipient($objRecipient);        };        if ($this->arObjApparentSecondaryRecipients->exists(    $objRecipient->getStringRepresentation())) {          $this->arObjApparentSecondaryRecipients->removeItem(    $objRecipient->getStringRepresentation());        };      }      public function addBlindRecipient($objRecipient) {        if (!($this->arRecipientCollection->exists(    $objRecipient->getStringRepresentation()))) {          parent::addRecipient($objRecipient);        };      }      public function removeBlindRecipient($objRecipient) {        if (!($this->arRecipientCollection->exists(    $objRecipient->getStringRepresentation()))) {          parent::removeRecipient($objRecipient->getStringRepresentation());        };      }      public function setSubject($strSubject) {        $this->strSubjectLine = $strSubject;      }      public function setMessageBody($strMessageBody) {        $this->_setMessage($strMessageBody);      }      public function setSender($objSender) {        $this->objApparentSender = $objSender->__clone();      }      public function send() {        // Establish headers        $strHeaders .= "From: " . $this->objApparentSender    ->getStringRepresentation() . "\n";        $strHeaders .= "To: " .    $this->objApparentPrimaryRecipient->getStringRepresentation() . "\n";        foreach ($this->arObjApparentSecondaryRecipients as    $strRecipientIdentifier => $objEmailRecipient) {          $strHeaders .= "Cc: " . $objEmailRecipient->getStringRepresentation() .    "\n";        };        $strHeaders .= "Date: " . date("D, M j H:i:sTYO") ."\n";        // Establish body        $strBody = $this->_getMessage();        // Pull together to form complete email, correctly formatted        $strFullEmail = $strHeaders . "\n" . $strBody;        if (! ($smtp = new Net_SMTP("mail"))) {            $this->strErrorMessage = "Unable to instantiate Net_SMTP object";            $this->errorCode = 1;            return(false);        }        if (PEAR::isError($e = $smtp->connect())) {            $this->strErrorMessage = $e->getMessage();            $this->errorCode = 2;            $smtp->disconnect();            return(false);        }        if (PEAR::isError($smtp->mailFrom(    $this->objApparentSender->getStringRepresentation()))) {            $this->strErrorMessage = "Unable to set sender";            $this->errorCode = 3;            $smtp->disconnect();            return(false);        }        // Send to each recipient        foreach ($this->arRecipientCollection as $strRecipientIdentifier =>    $objEmailRecipient) {          $strThisAddress = $objEmailRecipient->getRecipientAddress();          if (PEAR::isError($res = $smtp->rcptTo($strThisAddress))) {            $this->strErrorMessage = "Unable to add recipient " . $strThisAddress;            $this->errorCode = 4;            $smtp->disconnect();            return(false);          };        };        if (PEAR::isError($smtp->data($strFullEmail))) {            $this->strErrorMessage = "Unable to send data to server";            $this->errorCode = 5;            $smtp->disconnect();            return(false);        }        $smtp->disconnect();        return(true);      }    }; 

There are a few things to note about our approach here. First, when a recipient is added as primary recipient or a member of the carbon copy (Cc:) list, you can add them to the collection held in the parent superclass and then clone the recipient object before adding to the list of apparent recipients. This step isn't vital but it means you can implement your own _clone() method in your EmailRecipient class, if you want, to modify the behavior taken when this copy is made.

Second, we've made use of the error code and error message member variables of the Communication superclass. These are accessible through public functions. You can use these to debug your applications. E-mail can be an unpredictable phenomenon at times, and failure can occur through any number of scenarios, including a failure to reach the SMTP server, being denied permission to send e-mail to your particular IP address, or having a recipient be rejected outright by the SMTP server.

Third, we've used the code date("D, M j H:i:s T Y 0") to construct our e-mail header's date property. A quick glance at the PHP documentation reminds us that this produces a timestamp in the format Tue, Jul 20 22:58:58 BST 2004 +0100, which is of the format required in e-mail headers.

To test the final working class, you can knock out some code like the following:

   $objEmail = new EmailCommunication;    $objEmailRecipient = new EmailRecipient("",    "Ed Lecky-Thompson");    $objEmailCCRecipient = new EmailRecipient("",    "Ted Lecky-Thompson");    $objEmailSender = new EmailRecipient("", "Test Sender");    $objEmail->setPrimaryRecipient($objEmailRecipient);    $objEmail->setSender($objEmailSender);    $objEmail->setMessageBody("Hello,\n\nThis is a short test email.\n\nGoodbye!");    $objEmail->setSubject("Test Subject");    $objEmail->addCarbonRecipient($objEmailCCRecipient);    $objEmail->addBlindRecipient($objEmailBCCRecipient);    if ($objEmail->send()) {     print "DONE! All went well! Mail sent successfully.";    } else {     print "Sorry, didn't send mail successfully.";    }; 

Being Smart with Templates

In Chapter 13 we started talking about how separating application and display logic from each other through the Model View Controller (MVC) philosophy was generally an excellent idea, one well worth pursuing in your own application architecture.

Chapter 13 also covers a package called Smarty, which helps you do just that, and you learned just how useful it could be in traditional Web application implementation. Smarty has another use that is often overlooked. It can produce template-driven e-mail output.

As you know, nine times out of ten the e-mails you send your users will be virtually the same. A few tags, or the first name after "Dear,'' or an account number might change, but not much else.

Splicing variables into strings is fine for smaller operations. But when those e-mails start to get bigger and more unwieldly, and even contain printed array structures, things will get tough. The solution is to use the TemplatedEmailCommunication class.

The TemplatedEmailCommunication class makes full use of Smarty. If you haven't read Chapter 13 yet, now would be an excellent time. You need to know how Smarty works in order to pull this off.

The class you're about to create actually extends EmailCommunication, so have that up and running first. Have Smarty installed and working, too.

   class TemplatedEmailCommunication extends EmailCommunication {      private $path_to_template_file;      private $objSmarty;      public function __construct($strPathToTemplateFile) {         $this->objSmarty = new Smarty;         $this->path_to_template_file = $strPathToTemplateFile;         parent::__construct();       }       public function setParameter($strParameter, $strValue) {         $this->objSmarty->assign($strParameter, $strValue);       }       public function parse() {         $this->setMessageBody($this->objSmarty->fetch($this     ->path_to_template_file));      }    } 

The preceding code above is simple but effective. Now try the following code. It needs to point to a simple Smarty template, so first create a template file named test.tpl as follows:

   Name: {$name}    Favorite Food: {$favefood} 

Next, enter the PHP code that follows. Make sure that it resides in the same directory as your Smarty template, or else modify the first line to point to the right path. You'll see it's very similar to the last time we took EmailCommunication for a drive, but the differences in the code are highlighted for you:

   $objEmail = new TemplatedEmailCommunication("test.tpl");    $objEmail->setParameter("name", "Ed");    $objEmail->setParameter("favefood", "Steak");    $objEmail->parse();    $objEmailRecipient = new EmailRecipient("", "Ed    Lecky-Thompson");    $objEmailSender = new EmailRecipient("", "Test Sender");    $objEmail->setPrimaryRecipient($objEmailRecipient);    $objEmail->setSender($objEmailSender);    $objEmail->setSubject("Test Subject");    if ($objEmail->send()) {     print "DONE! All went well! Mail sent successfully.";    } else {     print "Sorry, didn't send mail successfully.";    }; 

This gives the following output in the body of sent e-mail:

   Name: Ed    Favorite Food: Steak 

As you can see, the parse() method constructed the body of the e-mail for you. Smarty parsed the template according to the two parameters you gave it. But rather than use the traditional display() method, you used fetch() to capture its output into a string. You then set the message body to be that string and you're all set to send the e-mail.

Note that by extending the EmailCommunication class, you've not only avoided re-inventing the wheel with respect to the recipient management and SMTP connectivity but also kept the Templated EmailCommunication class down to just a few lines of code, making it much easier to tinker with.

Of course, what's been demonstrated here is but a subset of what is possible with Smarty, but it does go to show just how easy Smarty is to adapt for e-mail.

Using MIME

You've probably noticed that throughout this chapter we've dealt almost exclusively with simple, plain-text e-mails. These are easier to deal with, but inevitably, occasions will for arise for sending HTML e-mail, maybe even templated HTML e-mail. And what about attachments? How do you handle those? MIME is the answer, and it is an enormous topic.

Mercifully, PEAR has it largely covered. See for more details. The suggested approach is to extend EmailCommunication to form RichEmail Communication, which sports additonal methods such as setHTMLContent, setPlainText Content, addAttachment, and so forth.

We don't have the space to go into more detail here. However, if you've got the hang of this chapter, you can probably integrate MIME functionality into the classes you've already built without difficulty. Download and install the PEAR package from and read the documentation thoroughly. Constructing the relevant new RichEmailCommunication class should be straightforward enough.

You can find out more about MIME in the Usenet comp.mail.mime FAQ, the latest version of which is available in the newsgroup and is replicated across the Web, such as at

Professional PHP5 (Programmer to Programmer Series)
Professional PHP5 (Programmer to Programmer Series)
Year: 2003
Pages: 182
BUY ON AMAZON © 2008-2017.
If you may any questions please contact us: