Showing posts with label php. Show all posts
Showing posts with label php. Show all posts

Monday, 30 January 2017

PHP Traits to ease dependency injection

In proramming larger applications, the DIP (Dependency inversion Pattern) is an important tool in keeping maintenance to a minimum; Munir Hassan for Codeproject has a great article that covers the topic Dependency Inversion Principle and the Dependency Injection Pattern. When we couple modules too tightly without abstraction layers or interfaces, maintenance costs go up greatly. The Codeproject article above has saved me a lot of time, and when I read about PHP Traits a while back I realised that it can be used to make this even easier.

What is a PHP Trait?

A trait is a class-like structure in PHP that many call "language assisted copy and paste". It basically means that when you import a Trait into a Class all the properties and methods defined in the Trait get copy/pasted into your Class at runtime by the PHP interpreter. Take the following example:

Without traits

<?php
  class User {

    private $connection;

    public function __construct(ConnectionInterface $connection) {
      $this->setConnection($connection);
    }

    private function setConnection(ConnectionInterface $connection) {
      $this->connection = $connection;
    }

    public function getConnection(): ConnectionInterface {
      return $this->connection;
    }

  };
?>
The above seems fine (except for the possibility of getConnection() failing if it's not set, we'll worry about that at another time), it uses a form of constructor injection to make sure the User class knows as little as possible about the Connection class. This does however create the issue where, let's say, another class (Product) now also needs a connection, now we'll have the following code too:
<?php
  class Product {

    private $connection;

    public function __construct(ConnectionInterface $connection) {
      $this->setConnection($connection);
    }

    private function setConnection(ConnectionInterface $connection) {
      $this->connection = $connection;
    }

    public function getConnection(): ConnectionInterface {
      return $this->connection;
    }

  };
?>

With traits

These two classes also have other methods and properties, but the imprtant thing to note here is that both of these now use the exact same code to set and get it's connection implementations. We can simplify this process by using a connection trait. Below is the trait we are going to use in both classes:
<?php
  trait connectionTrait {

    private $connection;

    private function setConnection(ConnectionInterface $connection) {
      $this->connection = $connection;
    }

    public function getConnection(): ConnectionInterface {
      return $this->connection;
    }

  };
?>
Now we can use our new trait in both our classes instead of re-defining the code in both classes, let's have a look at the new User class with the Trait imported/applied:
<?php
  use Connection\ConnectionInterface;
  class User {

    use \Connection\ConnectionTrait;

    public function __construct(ConnectionInterface $connection) {
      $this->setConnection($connection);
    }

  };
?>

Conclusion

In the last example our User class now imports the Trait which adds the private $connection property to our class. It also adds the setconnection() and getConnection() methods to our User class at runtime. This way we both save lines of code and we ensure that if the connection trait ever needs to change we can easily apply this change through all classes that use it.

Wednesday, 9 November 2016

PHP Interfaces

When building multiple PHP classes that are interchangeable - for instance two user classes where one reads from a MySql database and one from a JSON file - it is crucial that these classes do not diverge in functionality; If you add a 'getUserInfo' method to the one, you have to add it to the other, chances are you are using a factory to instantiate your objects and would thus expect to be able to use them in exactly the same manner. An interface solves this problem.
An interface provides a set of rules that determine the shape of the class that implements this interface. In the above example the interface will enforce the existence of a 'getUserInfo' method in both the UserMySql and UserJSON classes.

Building an interface

Let's build an interface for the example above. A user class that fetches it's data from a MySql database, and a user class reads from a JSON file. These must both contain public 'create', 'read', 'update' and 'delete' methods.
interface.account.php
<?php
interface Account {
  public function create(string $name, string $email);
  public function delete();
  public function read(int $id);
  public function update(string $name, string $email);
}
?>
class.user.json.php
<?php
class UserJSON implements Account {
  public $id;
  public $name;
  public $email;
  public function create(string $name, string $email) {
    // insert into JSON file
  }
  public function delete() {
    // delete user with {$this->id}
  }
  public function read(int $id) {
    // read user from {$id}.json
  }
  public function update(string $name, string $email) {
    // update user with {$this->id}
  }
  public function doesSomethingElse () {
    // do something else
  }
  private function doesSomethingDifferent () {
    // do something different (and private)
  }
}
?>
class.user.mysql.php
<?php
class UserJSON implements Account {
  public $id;
  public $name;
  public $email;
  public function create(string $name, string $email) {
    // insert into JSON file
  }
  public function delete() {
    // delete user with {$this->id}
  }
  public function read(int $id) {
    // read user from {$id}.json
  }
  public function update(string $name, string $email) {
    // update user with {$this->id}
  }
}
?>
The interface and two classes above will work together well and the interface will keep the classes aligned properly. If either of these classes do no conform to the shape provided in the interface PHP will throw an error thus indicating that the class does not conform.

Saturday, 15 October 2016

PHP Factory pattern

What is a Factory?

A factory is in essence a function that returns an instance of an object of the desired type when the class type that you want is unknown. For instance, let's say you have a Staff class with Teacher and Janitor sub-classes. When you fetch the list of all user's from the database and you want to loop through them and create the correct object type for each user without having to write an if-block each time you want to do this. This is where a Factory comes in.

A simple Factory

Again, assuming we have a Staff class with Teacher and Janitor sub-classes:

class Staff {
  public $staffType = 'Staff';
}

class Teacher extends Staff {
  public $staffType = 'Teacher';
}

class Janitor extends Staff {
  public $staffType = 'Janitor';
}
We now want to create a new staff-member object without writing an if block each time, let's build a Factory.
class StaffFactory {

  public static function buildStaffMemberObject (string $type) {
    if ($type === 'teacher') {
      return new Teacher();
    } elseif ($type === 'janitor') {
      return new Janitor();
    } else {
      return new Staff();
    }
  }

}
When we want to create an instance of a new staff-member object we can use:
$teacher = StaffFactory::buildStaffMemberObject('teacher');
$janitor = StaffFactory::buildStaffMemberObject('janitor');
That's it, you have a working Factory that builds objects of the correct type for you.

A more complex Factory

The above example works, but it's lacking in flexibility. Every time you want to add a new type you are going to have to add another condition inside the if/else if block. What if we had a Factory that could dynamically figure out which class type to load, let's take a look:
class StaffFactory {

  public static function buildStaffMemberObject (string $type) {
    $className = ucfirst($type);
    $className = class_exists($className) ? $className : 'Staff';
    return new $className();
  }

}
This time we are checking whether a class by the given name exists in the current namespace, otherwise we default back to 'Staff'. This way when you create a new staff member type, for example, "Management", all you have to do is create and include the class in you script and you can immediately create it via your Factory.