Book Image

Mastering PHP Design Patterns

By : Junade Ali
Book Image

Mastering PHP Design Patterns

By: Junade Ali

Overview of this book

Design patterns are a clever way to solve common architectural issues that arise during software development. With an increase in demand for enhanced programming techniques and the versatile nature of PHP, a deep understanding of PHP design patterns is critical to achieve efficiency while coding. This comprehensive guide will show you how to achieve better organization structure over your code through learning common methodologies to solve architectural problems. You’ll also learn about the new functionalities that PHP 7 has to offer. Starting with a brief introduction to design patterns, you quickly dive deep into the three main architectural patterns: Creational, Behavioral, and Structural popularly known as the Gang of Four patterns. Over the course of the book, you will get a deep understanding of object creation mechanisms, advanced techniques that address issues concerned with linking objects together, and improved methods to access your code. You will also learn about Anti-Patterns and the best methodologies to adopt when building a PHP 7 application. With a concluding chapter on best practices, this book is a complete guide that will equip you to utilize design patterns in PHP 7 to achieve maximum productivity, ensuring an enhanced software development experience.
Table of Contents (14 chapters)
Mastering PHP Design Patterns
Credits
About the Author
About the Reviewer
www.PacktPub.com
Preface

Revising object-oriented programming


Object-oriented programming is more than just classes and objects; it's a whole programming paradigm based around objects (data structures) that contain data fields and methods. It is essential to understand this; using classes to organize a bunch of unrelated methods together is not object orientation.

Assuming you're aware of classes (and how to instantiate them), allow me to remind you of a few different bits and pieces.

Polymorphism

Polymorphism is a fairly long word for a fairly simple concept. Essentially, polymorphism means the same interface is used with a different underlying code. So multiple classes could have a draw function, each accepting the same arguments, but at an underlying level, the code is implemented differently.

In this section, I would like to talk about Subtype Polymorphism in particular (also known as Subtyping or Inclusion Polymorphism).

Let's say we have animals as our supertype; our subtypes may well be cats, dogs, and sheep.

In PHP, interfaces allow you to define a set of functionality that a class that implements it must contain, as of PHP 7 you can also use scalar type hints to define the return types we expect.

So for example, suppose we defined the following interface:

interface Animal 
{ 
  public function eat(string $food) : bool; 
 
  public function talk(bool $shout) : string; 
} 

We could then implement this interface in our own class, as follows:

class Cat implements Animal { 
} 

If we were to run this code without defining the classes we would get an error message as follows:

Class Cat contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Animal::eat, Animal::talk) 

Essentially, we are required to implement the methods we defined in our interface, so now let's go ahead and create a class that implements these methods:

class Cat implements Animal 
{ 
  public function eat(string $food): bool 
  { 
    if ($food === "tuna") { 
      return true; 
    } else { 
      return false; 
    } 
  } 
 
  public function talk(bool $shout): string 
  { 
    if ($shout === true) { 
      return "MEOW!"; 
    } else { 
      return "Meow."; 
    } 
  } 
} 

Now that we've implemented these methods, we can then just instantiate the class we are after and use the functions contained in it:

$felix = new Cat();echo
$felix->talk(false);

So where does polymorphism come into this? Suppose we had another class for a dog:

class Dog implements Animal 
{ 
  public function eat(string $food): bool 
  { 
    if (($food === "dog food") || ($food === "meat")) { 
      return true; 
    } else { 
      return false; 
    } 
  } 
 
  public function talk(bool $shout): string 
  { 
    if ($shout === true) { 
      return "WOOF!"; 
    } else { 
      return "Woof woof."; 
    } 
  } 
} 

Now let's suppose we have multiple different types of animals in a pets array:

$pets = array( 
  'felix'     => new Cat(), 
  'oscar'     => new Dog(), 
  'snowflake' => new Cat() 
); 

We can now actually go ahead and loop through all these pets individually in order to run the talk function. We don't care about the type of pet because the talk method that is implemented in every class we get is by virtue of us having extended the Animals interface.

So let's suppose we wanted to have all our animals run the talk method. We could just use the following code:

foreach ($pets as $pet) { 
  echo $pet->talk(false); 
} 

No need for unnecessary switch/case blocks in order to wrap around our classes, we just use software design to make things easier for us in the long-term.

Abstract classes work in a similar way, except for the fact that abstract classes can contain functionality where interfaces cannot.

It is important to note that any class that defines one or more abstract classes must also be defined as abstract. You cannot have a normal class defining abstract methods, but you can have normal methods in abstract classes. Let's start off by refactoring our interface to be an abstract class:

abstract class Animal 
{ 
  abstract public function eat(string $food) : bool; 
 
  abstract public function talk(bool $shout) : string; 
 
  public function walk(int $speed): bool { 
    if ($speed > 0) { 
      return true; 
    } else { 
      return false; 
    } 
  } 
} 

You might have noticed that I have also added a walk method as an ordinary, non-abstract method; this is a standard method that can be used or extended by any classes that inherit the parent abstract class. They already have their implementation.

Note that it is impossible to instantiate an abstract class (much like it's not possible to instantiate an interface). Instead, we must extend it.

So, in our Cat class let's remove the following:

class Cat implements Animal 

We will replace it with the following code:

class Cat extends Animal 

That's all we need to refactor in order to get classes to extend the Animal abstract class. We must implement the abstract functions in the classes as we outlined for the interfaces, plus we can use the ordinary functions without needing to implement them:

$whiskers = new Cat();
$whiskers->walk(1);

As of PHP 5.4 it has also become possible to instantiate a class and access a property of it in one system. PHP.net advertised it as: Class member access on instantiation has been added, e.g. (new Foo)->bar(). You can also do it with individual properties, for example, (new Cat)->legs. In our example, we can use it as follows:

(new \IcyApril\ChapterOne\Cat())->walk(1); 

Just to recap a few other points about how PHP implemented OOP, the final keyword before a class declaration or indeed a function declaration means that you cannot override such classes or functions after they've been defined.

So, let's try extending a class we have named as final:

final class Animal 
{ 
  public function walk() 
  { 
    return "walking..."; 
  } 
} 
 
class Cat extends Animal 
{ 
} 

This results in the following output:

Fatal error: Class Cat may not inherit from final class (Animal) 

Similarly, let's do the same except at a function level:

class Animal 
{ 
  final public function walk() 
  { 
    return "walking..."; 
  } 
} 
 
class Cat extends Animal 
{ 
  public function walk () { 
    return "walking with tail wagging..."; 
  } 
} 

This results in the following output:

Fatal error: Cannot override final method Animal::walk() 

Traits (multiple inheritance)

Traits were introduced in PHP as a mechanism for introducing Horizontal Reuse. PHP conventionally acts as a single inheritance language, because of the fact that you can't inherit more than one class into a script.

Traditional multiple inheritance is a controversial process that is often looked down upon by software engineers.

Let me give you an example of using Traits first hand; let's define an abstract Animal class that we want to extend into another class:

class Animal 
{ 
  public function walk() 
  { 
    return "walking..."; 
  } 
} 
 
class Cat extends Animal 
{ 
  public function walk () { 
    return "walking with tail wagging..."; 
  } 
} 

So now let's suppose we have a function to name our class, but we don't want it to apply to all our classes that extend the Animal class, we want it to apply to certain classes irrespective of whether they inherit the properties of the abstract Animal class or not.

So we've defined our functions like so:

function setFirstName(string $name): bool 
{ 
  $this->firstName = $name; 
  return true; 
} 
 
function setLastName(string $name): bool 
{ 
  $this->lastName = $name; 
  return true; 
} 

The problem now is that there is no place we can put them without using Horizontal Reuse, apart from copying and pasting different bits of code or resorting to using conditional inheritance. This is where Traits come to the rescue; let's start off by wrapping these methods in a Trait called Name:

trait Name 
{ 
  function setFirstName(string $name): bool 
  { 
    $this->firstName = $name; 
    return true; 
  } 
 
  function setLastName(string $name): bool 
  { 
    $this->lastName = $name; 
    return true; 
  } 
} 

So now that we've defined our Trait, we can just tell PHP to use it in our Cat class:

class Cat extends Animal 
{ 
  use Name; 
 
  public function walk() 
  { 
    return "walking with tail wagging..."; 
  } 
} 

Notice the use of the Name statement? That's where the magic happens. Now you can call the functions in that Trait without any problems:

$whiskers = new Cat();
$whiskers->setFirstName('Paul');
echo $whiskers->firstName;

All put together, the new code block looks as follows:

trait Name 
{ 
  function setFirstName(string $name): bool 
  { 
    $this->firstName = $name; 
    return true; 
  } 
 
  function setLastName(string $name): bool 
  { 
    $this->lastName = $name; 
    return true; 
  } 
} 
 
class Animal 
{ 
  public function walk() 
  { 
    return "walking..."; 
  } 
} 
 
class Cat extends Animal 
{ 
  use Name; 
 
  public function walk() 
  { 
    return "walking with tail wagging..."; 
  } 
} 
 
$whiskers = new Cat(); 
$whiskers->setFirstName('Paul'); 
echo $whiskers->firstName;  

Scalar type hints

Let me take this opportunity to introduce you to a PHP 7 concept known as scalar type hinting; it allows you to define the return types (yes, I know this isn't strictly under the scope of OOP; deal with it).

Let's define a function, as follows:

function addNumbers (int $a, int $b): int 
{ 
  return $a + $b; 
} 

Let's take a look at this function; firstly you will notice that before each of the arguments we define the type of variable we want to receive; in this case, it's int (or integer). Next up, you'll notice there's a bit of code after the function definition : int, which defines our return type so our function can only receive an integer.

If you don't provide the right type of variable as a function argument or don't return the right type of the variable from the function; you will get a TypeError exception. In strict mode, PHP will also throw a TypeError exception in the event that strict mode is enabled and you also provide the incorrect number of arguments.

It is also possible in PHP to define strict_types; let me explain why you might want to do this. Without strict_types, PHP will attempt to automatically convert a variable to the defined type in very limited circumstances. For example, if you pass a string containing solely numbers it will be converted to an integer, a string that's non-numeric, however, will result in a TypeError exception. Once you enable strict_types this all changes, you can no longer have this automatic casting behavior.

Taking our previous example, without strict_types, you could do the following:

echo addNumbers(5, "5.0");

Trying it again after enabling strict_types, you will find that PHP throws a TypeError exception.

This configuration only applies on an individual file basis, putting it before you include other files will not result in this configuration being inherited to those files. There are multiple benefits of why PHP chose to go down this route; they are listed very clearly in version 0.5.3 of the RFC that implemented scalar type hints called PHP RFC: Scalar Type Declarations. You can read about it by going to http://www.wiki.php.net (the wiki, not the main PHP website) and searching for scalar_type_hints_v5.

In order to enable it, make sure you put this as the very first statement in your PHP script:

declare(strict_types=1); 

This will not work unless you define strict_types as the very first statement in a PHP script; no other usages of this definition are permitted. Indeed, if you try to define it later on, your script PHP will throw a fatal error.

Of course, in the interests of the rage-induced PHP core fanatic reading this book in its coffee stained form, I should mention that there are other valid types that can be used in type hinting. For example, PHP 5.1.0 introduced this with arrays and PHP 5.0.0 introduced the ability for a developer to do this with their own classes.

Let me give you a quick example of how this would work in practice, suppose we had an Address class:

class Address 
{ 
  public $firstLine; 
  public $postcode; 
  public $country; 
 
  public function __construct(string $firstLine, string $postcode, string $country) 
  { 
    $this->firstLine = $firstLine; 
    $this->postcode = $postcode; 
    $this->country = $country; 
  } 
} 

We can then type the hint of the Address class that we inject into a Customer class:

class Customer 
{ 
  public $name; 
  public $address; 
 
  public function __construct($name, Address $address) 
  { 
    $this->name = $name; 
    $this->address = $address; 
  } 
} 

And this is how it all can come together:

$address = new Address('10 Downing Street', 'SW1A 2AA', 'UK');
$customer = new Customer('Davey Cameron', $address);
var_dump($customer);

Limiting debug access to private/protected properties

If you define a class which contains private or protected variables, you will notice an odd behavior if you were to var_dump the object of that class. You will notice that when you wrap the object in a var_dump it reveals all variables; be they protected, private, or public.

PHP treats var_dump as an internal debugging function, meaning all data becomes visible.

Fortunately, there is a workaround for this. PHP 5.6 introduced the __debugInfo magic method. Functions in classes preceded by a double underscore represent magic methods and have special functionality associated with them. Every time you try to var_dump an object that has the __debugInfo magic method set, the var_dump will be overridden with the result of that function call instead.

Let me show you how this works in practice, let's start by defining a class:

class Bear { 
  private $hasPaws = true; 
} 

Let's instantiate this class:

$richard = new Bear();

Now, if we were to try and access the private variable that is hasPaws, we would get a fatal error:

echo $richard->hasPaws;

The preceding call would result in the following fatal error being thrown:

Fatal error: Cannot access private property Bear::$hasPaws

That is the expected output, we don't want a private property visible outside its object. That being said, if we wrap the object with a var_dump as follows:

var_dump($richard); 

We would then get the following output:

object(Bear)#1 (1) { 
   ["hasPaws":"Bear":private]=> 
   bool(true) 
} 

As you can see, our private property is marked as private, but nevertheless it is visible. So how would we go about preventing this?

So, let's redefine our class as follows:

class Bear { 
  private $hasPaws = true; 
  public function __debugInfo () { 
    return call_user_func('get_object_vars', $this); 
  } 
} 

Now, after we instantiate our class and var_dump the resulting object, we get the following output:

object(Bear)#1 (0) {  
} 

The script all put together looks like this now, you will notice I've added an extra public property called growls, which I have set to true:

<?php 
class Bear { 
  private $hasPaws = true; 
  public $growls = true; 
   
  public function __debugInfo () { 
    return call_user_func('get_object_vars', $this); 
  } 
} 
$richard = new Bear(); 
var_dump($richard); 

If we were to var_dump this script (with both public and private property to play with), we would get the following output:

object(Bear)#1 (1) { 
  ["growls"]=> 
  bool(true) 
} 

As you can see, only the public property is visible. So what is the moral of the story from this little experiment? Firstly, that var_dumps exposes private and protected properties inside objects, and secondly, that this behavior can be overridden.