SOLID PHP : Laying Foundations for applications (Part 1)

This will be the first of (hopefully) a few articles explaining (with code and all the neat stuff) some basics on software design for those daring to do this job. PHP has been a language long cursed by many programmers, some say the problem is that is not a compiled language, others say that has weak typing, some talk about performance and there are many many talks about it, you even get to see people that say it sucks! and they just can’t stop wondering why the some big guys use PHP. Personally, I don’t believe in bad languages (with some exceptions, cough.. vb.. cough) but I rather think in bad programmers or to be less agressive in programmers who dislike different ways to do things (now that I think about it, I don’t know which one is worst…)

Anyways, I’ve chosen to do my examples using PHP because there is plenty of literature about the subject using C#, Java or even C++. When we talk about SOLID, the first thing we need to say is that SOLID is a list of 5 principles that encourage several good practices, they’re good, they’re pretty simple, however, they are not the only ones. There are many principles on software design, specially because software design is for one a young science and therefore there is many new stuff to find, and secondly because there are (obviously) more than 5 design principles to do good software, however these 5 are quite important and basic. Now, will these design principles make my code perfect? Well… nope, no way!! there is no such thing as perfect code (hello worlds not included) and mostly because when we write code, we will make mistakes (remember, we are humans…), however, following these principles will prevent a lot of these mistakes, will make your code more readable and will make your system (among other things) scalable.

Today I will start talking about the first principle, which is the Single Responsability Principle. This principle states one elementary truth and its that there should be one (and only one!) reason for a class to change, sadly I can’t make the one bolder than the rest of the principle, but I hope the parenthesis give the correct amount of dramatism. Now, when we say “reason for a class to change”, what’s that? With reason to change we mean reasons that will make you touch (edit) the code again, and using that definition we have to agree that touching (editing) working code has a problem : it introduces bugs…

Now, let’s stop being so chatty and see some code shall we?

  class UserManagement
  {
   public function LogUserIn($userName, $password)
   {
    $host = $dbConf["host"];
    $dbUsr = $dbConf["user"];
    $dbPwd = $dbConf["pass"];
    $link = mysql_connect($host, $dbUsr, $dbPwd);
    $logger = new FileLogger();
    
    if ( !$link ) {
     $logger->Log("Error opening connection to DB");
     return false;
    }
    
    if ( !mysql_select_db($dbConf["db"], $link)){
     $logger->Log("Error selecting DB");
     return false;
    }
    
    //Prevent some injection over here...
    $user = mysql_escape_string($userName);
    $pwd = mysql_escape_string($password);
    
    $query = "SELECT COUNT(*) FROM Users " .
       "WHERE user = '$user' AND Pass = '$pwd'";
    $rsc   = mysql_query($query, $link);
    
    if ( !$rsc ) {
     $logger->Log("Error querying DB");
     return false;
    }
    
    $row = mysql_fetch_array($rsc);
    mysql_close($link);
    
    return count($row) > 0 && $row[0] > 0;
   }
  }
 

Some may say, OK, it’s a perfectly working function, what’s the fuzz all about? Well, what this function does? The function itself does many things:

  • Connects to the database
  • Builds the Log system explicitly and logs errors
  • Handles domain logic code (the whole COUNT to figure out if the user exists..)

So, this function has several reasons to make it change. What happens if we want to change the database? We need to change this code. What happends if we change the Log system? We need to change this code. What happens if we also have to check some more stuff? We also have to touch this code. So, the code has many problems that may make it change. How do we solve this? Using Contracts(a.k.a Interfaces). If we are to connect to the database, we define how the connector has to be, if we are going to perform logging, we define how the Logger has to be and so on. If we do all that, then our class can manage only one thing, say managing the Login of a user. Imagine now we have these two interfaces:

 interface IDatabaseConnection
 {
  public function OpenConnection();
  public function BuildQuery($queryString);
  public function EscapeString($string);
  public function CloseConnection();
 }
 
 interface IDatabaseQuery
 {
  public function GetAsRowCollection();
  public function GetScalarValue();
  //many more...
 }
 
 interface ILogger
 {
  public function Log($message);
 }

With these interfaces, we define contracts. For instance, we’re saying that we want that any class implementing the ILogger interface needs to have a Log function receiving a message argument. Interfaces can even force types using type hinting and therefore providing even better defined contracts, which is always cool.

Once we have defined the contracts we need, then we could rewrite our code to something like this:

 class UserManagement
 {
  private $logger;
  private $dbConnector;
  
  public function __construct(IDatabaseConnection $connector, 
         ILogger $logger){
   $this->logger = $logger;
   $this->dbConnector = $connector;
  }
  
  public function LogUserIn($userName, $password)
  {
   //Prevent some injection over here...
   $user = $this->dbConnector->EscapeString($userName);
   $pwd = $this->dbConnector->EscapeString($password);
   
   $query = "SELECT COUNT(*) FROM Users " .
      "WHERE user = '$user' AND Pass = '$pwd'";
   $command   = $this->dbConnector->BuildQuery($query);
   
   if ( $command != null ) {
    $this->logger->Log("Error building DB command");
    return false;
   }
   
   $this->dbConnector->OpenConnection();
   $count = $command->GetScalarValue();
   $this->dbConnector->CloseConnection();
   
   return $count != 0;
  }
 }

We’ve written code far more readable, with no dependency in anything but domain logic, well, perhaps we’re building SQL code in here, and that’s not good, but to prevent having to do that, we could use Propel or Doctrine and instead of writing plain and cold SQL we could use an ORM framework to do the dirty work for us. I’m just putting a simple example here using traditional database connection, but if you have an elaborate application, then consider seriously using Propel or Doctrine.

Now we would need to implement the ILogger interface and the IDatabaseConnection interface to be able to perform the operations we need, however, if we need to change the way we connect to the database, then we’ll change the implementation (or add a new one!) of our IDatabaseConnection and that’s just about it!

However, do keep in mind that the issue with the Single Responsability Principle is that it will help you with dependencies, but it will make your design grow substantially. We started with a class and ended with 3 interfaces and also their implementations (3 more classes) and finally the same (but reduced) class. Bottom line is : these principles are to design a propper application, but if you’re making your own small tool, like some scrapper or a small reader for rss news, SOLID principles are not the thing you should be considering, they apply when you are facing an application that may grow.