Amazon.com Widgets

PHP Example: The Strategy Pattern

Even though we liked the old color scheme of the phppatterns.com site better than the new one, we think the content is still aces. One of our favorite areas is the design section with it’s excellent examples of various design patterns written in PHP.

One pattern that we find ourselves using repeatedly is the Strategy Pattern. The Strategy Pattern is one of the original Gang of Four design patterns. The Strategy Pattern is grouped with other behavioral patterns because it’s best suited to solving problems that affect the behavior of your program.

You can define the Strategy Pattern like so:

Encapsulate various related algorithms in individual subclasses of a common superclass.*

You might wonder why this would offer a solution that affects the behavior of your programs. Here’s the beauty of this pattern - if you’ve ever found yourself writing lots of if-else if-else blocks, or if you’ve ever written loads of long switch or case statements, then you’re going to love this thing. I’m not saying you won’t have if-blocks, but you’ll find that your code will become much cleaner and more flexible if you start applying some Strategy. Read on for the example.

The Strategy Pattern example on the phppatterns.com site, described here, uses something pretty common in web application programming - form validation. That’s the example we’ll use - and extend here.

Web applications are often filled with HTML forms, and these forms can often have many fields. It’s pretty common to have the web application validate the data submitted in these fields on the server - and this is often in addition to client (or browser) based field validation. This is usually a response to the ability that users have to disable javascript within their browser, opening up the possibility to completely bypass client-based validation.

So - let’s consider a simple form for user registration. In this application, you only have three fields that you’re concerned with - the user name, the password, and a second password field that you’ll use to confirm the user typed their password correctly. Told you the form was simple. Now, let’s look at the requirements - which are anything but simple:

Let’s assume your customer’s requirements are to make all three fields required for new users. For existing users, only username is required - but if a user enters a password, then the confirmation password field will also be required. In all cases, user names and passwords must be between 6 and 20 characters, can only contain letters and numbers (no punctuation), are case sensitive, and of course, the password and confirmation passwords must match one another. Finally - user names must not already exist in your user database. That’s quite a bit of business logic for such a trivial form, but you’ll probably find that the business rules that are important to your clients often provide you with the most complex challenges.

So let’s assume you have already created the form, and the logic to write the data into the database, and you’re ready to apply these business requirements.

You might be tempted to start off thinking in terms of if-then or case statements. I won’t even bother with the sample code because I want you to stop thinking that way - so I won’t enable your addiction to long, buggy, complex if-else if blocks.

Instead, let’s look at how we can apply the Strategy Pattern. Remember, we defined the strategy pattern is about encapsulating related algorithms in subclasses of a common abstract class, and we clearly have several related algorthms. In the one case, we have an algorithm for validating the username field. In the other case, we have an algorithm for validating the password (and confirm password) field. We also have an algorithm for validating whether any arbitrary required field is present or not. So - by the Strategy pattern - three related algorithms would mean three related subclasses.

Field Validator StrategyWhat do these related classes have in common? In all cases, they encapsulate logic to validate a form field. So, we’ll start with with an abstract FieldValidator class - and create a simple interface: we’ll just have a validate() method that gets called during object construction. The validate method will check the business rules for the current subclass, and add any errors it encounters to the errorMsg list. We’ll also add an isValid() mathod that checks the error list and returns true if there are no errors. If validation fails, the validation error(s) for the current field will get recorded in an array of error messages that we can display back to the user with a getError() method. Once we have defined the superclass, we’ll then create three subclasses - one for the username, one for the password, and one for any required field. The UML for the FieldValidator strategy is presented here in the figure to the left.

Now that we’ve got the various classes defined, lets write the code. While this bit is almost identical to the PHPPatterns.com example, we’ll get to the extension shortly.

  1. <?php
  2. /**
  3. * FieldValidator
  4. *
  5. * "Abstract" superclass for form field validation
  6. * Example of the Strategy Pattern presented on
  7. * phppatterns.net
  8. */
  9. class FieldValidator {
  10. /**
  11. * Private
  12. * $errorMsg stores error messages if not valid
  13. */
  14. var $errors;
  15. //! A constructor.
  16. /**
  17. * Constucts a new FieldValidator object
  18. */
  19. function FieldValidator() {
  20. $this->errors=array();
  21. $this->validate();
  22. }
  23. //! A manipulator
  24. /**
  25. * @return void
  26. */
  27. function validate() {
  28. // Superclass method does nothing
  29. }
  30. //! A manipulator
  31. /**
  32. * Adds an error message to the array
  33. * @return void
  34. */
  35. function setError ($msg) {
  36. $this->errors[]=$msg;
  37. }
  38. //! An accessor
  39. /**
  40. * Returns true if string valid, false if not
  41. * @return boolean
  42. */
  43. function isValid () {
  44. if ( $this->errorMsg != null && sizeof($this->errorMsg ) > 0 ) {
  45. return false;
  46. } else {
  47. return true;
  48. }
  49. }
  50.  
  51. /**
  52. * Pops the last error message off the array
  53. * @return string
  54. */
  55. function getError () {
  56. return array_pop($this->errorMsg);
  57. }
  58. }
  59. ?>

And now, the three subclasses

  1. <?php
  2. /**
  3. * RequiredFieldValidator subclass of FieldValidator
  4. * Validates a that a required field is present
  5. */
  6. class RequiredFieldValidator extends FieldValidator {
  7. /**
  8. * Private
  9. * $user the fieldName to validate
  10. */
  11. var $fieldName;
  12. /**
  13. * Private
  14. * $user the fieldName to validate
  15. */
  16. var $fieldValue;
  17.  
  18. /**
  19. * Constucts a new Validator object
  20. * @param $fieldName the Name of the Field
  21. * @param $fieldValue The Value of the Field
  22. */
  23. function RequiredFieldValidator ($fieldName, $fieldValue) {
  24. $this->fieldName=$fieldName;
  25. $this->fieldValue=$fieldValue;
  26. FieldValidator::FieldValidator();
  27. }
  28. /**
  29. * Validates the field is present
  30. * @return void
  31. */
  32. function validate() {
  33. if ( $this->fieldValue == null || “”== $this->fieldValue )
  34. {
  35. $this->setError($this->fieldName . ‘ is a required field.’);
  36. }
  37. }
  38. }
  39. /**
  40. * UserNameValidator subclass of FieldValidator
  41. * Validates a username
  42. */
  43. class UserNameValidator extends FieldValidator {
  44. /**
  45. * Private
  46. * $user the username to validate
  47. */
  48. var $user;
  49. /**
  50. * Constucts a new ValidateUser object
  51. * @param $user the string to validate
  52. */
  53. function UserNameValidator ($user) {
  54. $this->user=$user;
  55. FieldValidator::FieldValidator();
  56. }
  57. /**
  58. * Validates a username
  59. * @return void
  60. */
  61. function validate() {
  62. // do we have OK characters?
  63. if (!preg_match(‘/^[a-zA-Z0-9]+$/’,$this->user )) {
  64. $this->setError(‘User Login is null or contains invalid characters’);
  65. }
  66. // do we meet minimum length requirement?
  67. if (strlen($this->user) < 6 ) {
  68. $this->setError(‘User Login mut be at least 6 characters’);
  69. }
  70. // do we meet the max length requirement?
  71. if (strlen($this->user) > 20 ) {
  72. $this->setError(‘User Login is too long’);
  73. }
  74. // does the current username already exist?
  75. if ( $this->user != null && $this->alreadyExists( $this->user ) )
  76. {
  77. $this->setError(‘The User Login, "’. $this->user .‘" is already in use’);
  78. }
  79. }
  80. // alreadyExists - return true if the user name is in the DB
  81. function alreadyExists( $userName )
  82. {
  83. // we’ll leave it to you to determine how implement this method as an exercise.
  84. }
  85. }
  86. /**
  87. * ValidatorPassword subclass of FieldValidator
  88. * Validates a password
  89. */
  90. class PasswordValidator extends FieldValidator {
  91. /**
  92. * Private
  93. * $pass the password to validate
  94. */
  95. var $pass;
  96. /**
  97. * Private
  98. * $conf to confirm the passwords match
  99. */
  100. var $conf;
  101. //! A constructor.
  102. /**
  103. * Constucts a new ValidatePassword object subclass or FieldValidator
  104. * @param $pass the string to validate
  105. * @param $conf to compare with $pass for confirmation
  106. */
  107. function PasswordValidator ($pass,$conf) {
  108. $this->pass=$pass;
  109. $this->conf=$conf;
  110. FieldValidator::FieldValidator();
  111. }
  112. //! A manipulator
  113. /**
  114. * Validates a password
  115. * @return void
  116. */
  117. function validate() {
  118. if ($this->pass!=$this->conf) {
  119. $this->setError(‘Passwords do not match’);
  120. }
  121. if (!preg_match(‘/^[a-zA-Z0-9_]+$/’,$this->pass )) {
  122. $this->setError(‘Password is null or contains invalid characters’);
  123. }
  124. if (strlen($this->pass) < 6 ) {
  125. $this->setError(‘Password must be at least 6 characters long’);
  126. }
  127. if (strlen($this->pass) > 20 ) {
  128. $this->setError(‘Password cannot be more than 20 characters’);
  129. }
  130. if (strlen($this->conf) < 6 ) {
  131. $this->setError(‘Confirmation Password must be at least 6 characters long’);
  132. }
  133. if (strlen($this->conf) > 20 ) {
  134. $this->setError(‘Confirmation Password cannot be more than 20 characters’);
  135. }
  136. }
  137. }
  138. ?>


This is roughly the point at which the example on the phppatterns.com ends. After introducing the Strategy Pattern, and then providing the field validator examples, you’re given a little bit of code that shows you how to call you new validator classes. What would have made this an even better example is the recognition that, in addition to the tasks of field validation, you could just as easily apply the Strategy pattern to the task of form validation.

Form Validator StrategyRemember our business requirements? Our requirements in this example say that some of the fields are required for new users, but different fields are required for existing users. Once again, we have related algorithms, which, in this case, are the individual form level validation requirements. In our example, the related “forms” are the new user form and an existing user form. Regardless of whether you actually use different HTML forms for these, you can think of their validation requirements distinctly - and can represent them as concrete implementations of a common FormValidator superclass.

Our new FormValidator class allows you to register new FieldValidator instances to it using the addValidator() method, and provides the abiity to call the validate() method on each one through the isValid() call. Let’s see how this code plays out:

  1. <?php
  2. /*
  3. * FormValidator
  4. *
  5. * Abstract class that performs field-level validation
  6. * for a given HTML form.
  7. *
  8. */
  9. class FormValidator
  10. {
  11. var $errors;
  12. var $validatorList;
  13. function FormValidator()
  14. {
  15. $this->validatorList = array();
  16. }
  17. /**
  18. * validate
  19. *
  20. * Main abstract method that needs to be implemented in
  21. * each subclass
  22. */
  23. function validate()
  24. {
  25. $returnVal = true;
  26. // Perform validation
  27. // against each validator in the list
  28. // every one must return true for this method
  29. // to return true
  30. foreach($this->getValidators() as $validator)
  31. {
  32. if (!$validator->isValid())
  33. {
  34. $returnVal = false;
  35. while ($error=$validator->getError())
  36. {
  37. $this->addError($error);
  38. }
  39. }
  40. }
  41. return $returnVal;
  42. }
  43. function getErrors()
  44. {
  45. return $this->errors;
  46. }
  47. function addError($message)
  48. {
  49. $this->errors[] = $message;
  50. }
  51. function addValidator(& $validatorObject)
  52. {
  53. $this->validatorList[] = $validatorObject;
  54. }
  55.  
  56. function getValidators()
  57. {
  58. return $this->validatorList;
  59. }
  60. }
  61.  
  62. ?>

And now, let’s examine the FormValidator subclasses - for the New and Existing user forms.

  1. <?php
  2. /*
  3. * NewUserFormValidator
  4. * Validates that the business rules for new user form submissions are followed
  5. */
  6. class NewUserFormValidator extends FormValidator
  7. {
  8. function NewUserFormValidator()
  9. {
  10. parent::FormValidator();
  11. $this->addValidator( new PasswordValidator($_REQUEST[‘passWord’],$_REQUEST[‘confirmPassword’]));
  12. $this->addValidator( new UserNameValidator($_REQUEST[‘userName’]));
  13. $this->addValidator( new RequiredFieldValidator(“Password”, $_REQUEST[‘passWord’]));
  14. $this->addValidator( new RequiredFieldValidator(“User Login”, $_REQUEST[‘userName’]));
  15. }
  16. }
  17. /**
  18. * ExistingUserValidator -
  19. * This is a formvalidator subclass that gets called when
  20. * a user record is updated
  21. */
  22. class ExistingUserFormValidator extends FormValidator
  23. {
  24. function ExistingUserFormValidator()
  25. {
  26. parent::FormValidator();
  27. $this->addValidator( new RequiredFieldValidator(“User Login”,$_REQUEST[‘userName’]));
  28.  
  29. // only validate password and the confirmation password if either one is present
  30. if( $_REQUEST[‘passWord’] != “” || $_REQUEST[‘confirmPassword’] != “”)
  31. {
  32. $this->addValidator(new RequiredFieldValidator(“Password”, $_REQUEST[‘passWord’]));
  33. $this->addValidator(new RequiredFieldValidator(“Confirmation Password”, $_REQUEST[‘confirmPassword’]));
  34. $this->addValidator(new PasswordValidator($_REQUEST[‘passWord’],$_REQUEST[‘confirmPassword’]));
  35. }
  36. }
  37. }
  38. ?>

Now that the various building blocks are in place, let’s see how we might tie this together in our page controller script. You’re probably using some kind of MVC pattern for your system, right? At the controller level - all we’re really concerned with is if we’re dealing with a new or existing user. For new users, we’ll utilize the NewUserFormValidator to do the validation - and if it’s an existing user, we use the ExistingUserFormValidator. Let’s say we have already implemented a method to decide which validator to use. Maybe the User BusinessObject could tell us based on the value of some inbound action parameter. Sure. That might look something like this:

  1.  
  2. . . .
  3. function saveUser()
  4. {
  5. // assume we’ve already populated a user object from the html post
  6. // see the blog entry about using introspection for an example of how to do this easily
  7. $theUser = $this->user;
  8.  
  9. // assume form validators are related to business objects
  10. // get the appropriate validator for inserts and updates
  11. // often these are the same, but not always - it depends on the
  12. // business object
  13. // here we’ll use "action" parameter - which may have been the
  14. // value of the user form submit button or a hidden field to help figure out
  15. // what form validator to use
  16.  
  17. $validator = null;
  18. if ($this->action == “NEW”)
  19. {
  20. $validator = new NewUserFormValidator();
  21. }
  22. else
  23. {
  24. $validator = new ExistingUserFormValidator();
  25. }
  26.  
  27. // in a real system, make sure $validator isn’t null here
  28.  
  29. if ( $validator->validate() )
  30. {
  31. $user->save();
  32. header(“Location: “ . $successPage);
  33. return;
  34. }
  35. else
  36. {
  37. // since there are errors, let’s get the error list from the validator
  38. // and pass that to back to our web form so we can display them
  39.  
  40. $errors = $validator->errors;
  41. include $editorPage;
  42. }
  43. }
  44. . . .

So that’s it. Looks like a lot of work for such a simple form, huh? I agree - except, that in real world applications, you rarely have just one form, you might have dozens or more - each with dozens of individual form fields. The beauty of this sort of application of the Strategy pattern is that for each new type of field validation task, you simply create a new FieldValidator subclass - and then register the new FieldValidator on the appropriate FormValidator where it’s needed. For example, what if we add email address to our user form? You could easily create an EmailFieldValidator - and then simply add a line to call the addValidator() method in the appriate FormValidator class to use it. What if the field validation rules change as a result of supporting different languages or countries? For example, say you have a need to validate postal codes in the US and Canada. You could simply create US and Canada PostalCodeFieldValidator classes that encapsulate the rules for valid postal formats in those countries. In addition, it would now be pretty easy to add any number of new forms that contain complex validation logic. Need to start writing Contacts or Leads or Invoice details in your web app? Simply create a new FormValidator for each one, and then you business logic will be neatly encapsulated and ready for use as well as extension.

* Note: I talk quite a bit here about abstract superclasses when I discuss the Strategy Pattern. There’s absolutely nothing wrong with using an interface instead. So, if you’re a PHP 5 or Java programmer, and your problem is better suited toward an interface, then by all means, use an interface. Since I’m writing this example in PHP 4 - there really isn’t even any “abstract” nature to these superclasses. Don’t let that put you off - the main point here is to code to a public interface provided by your superclass so you can enjoy the benefits in each concrete implementation.


Leave a Comment