PHP OOP Software Architecture

Understanding object oriented programming

This article is based upon an OOP bootcamp taught by Jeffrey Way from Laracast. It comprises my course notes along with personal code examples. 

Terminology

Let's dive into it and create a football player. In object oriented programming terms we use a different terminology: We create an object of the class FootballPlayer.

class FootballPlayer
{
    public $name;
    public $position;
public function __constructor($name, $position)
{
    $this->name = $name;
    $this->position = $position;
}

}

$playerA = new FootballPlayer('Marco Reus', 'Striker'); $playerB = new FootballPLayer('Axel Witsel', 'Midfield');

var_dump($playerA->name); // Marco Reus var_dump($playerB); // Axel Witsel, Midfield

Classes

What is a class? Refer to a class as a blueprint for an object that defines certain characteristics and behavior. In our example we defined the class FootballPlayer.

Objects, Instances, and $this

An object is a representation of a defined class. When we talk about a specific object we refer to it as an instance. Referring to our our example,  playerA is an instance of FootballPlayer. In more technical terms: An instance is an object in memory. It can be addressed with the keyword $this.

Variables

Variables are containers to store information. From a class perspective variables ar called attributes. In PHP variables always start with the $ sign, are case sensitive and must either start with a letter (A-Z, a-z) or an `_` (underscore). 

Methods

A method is a function within a class. It defines certain behavior of a class. A constructor is a special method that is trigged whenever a new object of a class will be initiated. 

Scope

Scope refers to the visibility of variables and methods. We differentiate among three different types of visibility: 

  • public: makes a variable/method available from everywhere, thus also other classes and instances of objects
  • protected: makes a variable/method available in the current class and all its sub-classes, including the parent class
  • private: makes a variable/method only available within it's own class

Protection and Security

Imagine the following business requirement: The email address of $user is only available if the profile is set to public. We implement this with the help of a getter function and private variables.  

<?php

class User { public $name; private $email; private $public = false;

public function __construct($name, $email)
{
    $this-&gt;name = $name;
    $this-&gt;email = $email;
}

public function makeProfilePublic()
{
    $this-&gt;public = true;
}

public function getEmail()
{
    if ($this-&gt;public) {
        return $this-&gt;email;
    }

    throw new Exception('Profile is not public');
}

}

$user = new User('Jan', 'info@example.org'); $user->makeProfilePublic(); var_dump($user->getEmail()); // info@example.org

Getters and Setters

If you want to provide more protection and security to your variables you can make use of getters() and setters().  In contrast to manipulating or retrieving variables directly, you access those variables through a setter function (for manipulating purpose) or a getter function (for retrieving purpose). Those functions enable you to add additional behavior, e.g. make the $email only available if $public is true. 

Encapsulation

The term sounds more complicated as it is. The idea behind it is hiding variables and methods that should not be accessed from the public interface. 

  • protected: protected variables and methods can still be accessed from class extensions 
  • private: even class extensions have no access to private variables and methods

Inheritance

The example introduces an abstract class called Shape that defines that every sub-class gets a default color green if no other one is defined. All sub-classes may retrieve the color attribute via getColor(). Moreover, through the abstract method getArea() the parent-class Shape demands that each subclass introduces a behavior to calculate the area of the respective body.

<?php

new abstract Shape { protected $color;

public function __constructor($color = 'green')
{
   $color = $this-&gt;color;
}

public function getColor()
{
    return $this-color;
}

abstract protected function getArea();

}

new Square extends Shape { protected $length = 5;

public function getArea($length)
{
    return pow ($this-&gt;length, 2);
}

}

new Triangle extends Shape { protected $base = 7; protected $height = 4;

public function getArea($base, $height)
{
    return .5 $this-&gt;base * $this-&gt;height;
}

}

Override behavior and attributes

If a sub-class should get different attribute values and / or behavior compared to its parent class you may simply override variables and functions within the subclass. 

new Shape 
{
    public $color = 'black';
}

new Circle extends Shape { public $color = 'green'; }

Abstract classes

Use an abstract class if you want to introduce common attributes and behavior that should be inhibited by all sub-classes. However, an abstract class can not be instantiated by itself.

$myshape = new Shape; // error because abstract class can not be instantiated 

Abstract methods

If you want that each subclass defines it individual behavior for e.g. area calculation, you make use of a template method design pattern

new Shape 
{
    abstract protected function getArea();
}

This example illustrates, that each subclass of Shape needs to define its own getArea() function. 

NOTE: A similar result can be achieved by making use of interfaces. Indeed, from a design pattern perspective there exists the claim: Favor composition over inheritance. Please refer to the resources for further information. 

Messages

This is about sending data among objects. Consider the following example. A Club hires a Player.

class Club
{
    public $name;
    protected $team = [];
public function __construct($name)
{
    $this-&gt;name = $name;
}

public function hire(Player $player)
{
   $this-&gt;team[] = $player;
}

public function getTeam()
{
    return $this-&gt;team;
}

}

class Player { public $name;

public function __construct($name)
{
    $this-&gt;name = $name;
}

}

// create a new club $bvb = new Club('BvB');

// create some players $sammer = new Player('Matthias Sammer'); $klos = new Player('Stefan Klos'); $ricken = new Player('Lars Ricken');

// club hires player $bvb->hire($sammer); $bvb->hire($klos); $bvb->hire($ricken);

// get team roster of club var_dump($bvb->getTeam()); // Sammer, Klos, Ricken

Type declaration

Type declaration, also known as type hinting is a security mechanism that proofs whether an argument for a method is of the demanded type. If this is not the case, an exception will be thrown. For instance, the function hire() expects an argument of type Player

public function hire(Player $player)
{
    $this->team[] = $player;
}

Dependencies

In general, we talk about dependencies when the instantiation of an object (or usage of a method) depends on other object instances. 

class Flower
{
   public function __constructor(Water $water, Sun $sun)
}

Managing dependencies, is a major topic in the design of your web application. In general, the fewer dependencies a class has, the better will be its handling, refactoring and testing. Concepts in this field are tight- and loose coupling. However, there explanation is beyond this rather basic introduction to OOP. 

Statics and Constants

Static methods and properties are not based upon an instance of an object, instead they are globally accessible and thus can not be changed on an instance basis. In other words, a static method or property of an object will share throughout all instances the same value or behavior.  

NOTE: Static methods and properties should be applied with caution, as they can easily brake your application. Only apply it, if you are certain about a proper use case. 

Static method

Example of static methods are Laravel's helper functions.

public static function contains($haystack, $needles)
    {
        foreach ((array) $needles as $needle) {
            if ($needle !== '' && mb_strpos($haystack, $needle) !== false) {
                return true;
            }
        }
        return false;
    }

Static Property

This example stresses the fact that static variables can not be changed on a instance basis but only globally. 

class Chair
{
    static $size = 'medium';
}

$mychair = new Chair(); $yourchair = new Chair();

var_dump(Chair::$size); // medium

$yourchair::$size = 'small';

var_dump($yourchair::$size); // small var_dump($mychair::$size); // small

Constant Property

A constant property can not be changed. 

const TAX = 0.19;
var_dump(TAX); // 0.19

const TAX = 0.10; // PHP notice const TAX already defined var_dump(TAX); // 0.19

Interfaces

What are interfaces? A list of public methods that must be defined by all objects that implement the interface. Consider the following example of a mailer interface. Each function that implements this interface need to provide a send() method with the arguments $from, $to, and $message.

interface Mailer
{
    public function send($from, $to, $message);
}

class WelcomeMailer implements Mailer { public function send () { // } }

class ForgotPasswortMailer implements Mailer { public function send () { // } }

class SuggestedContentMailer implements Mailer { public function send () { // } }

Contract for implementations

Think of an interface as the contract for your implementations. It is the place were you define the public interface (= no private and protected methods) that you are going to use in your implementations. You do not define any logic in interfaces. A class can implement multiple interfaces. 

class Example implements InterfaceA, InterfaceB
{
    // methods 
}

Why are interfaces relevant? They offer the following advantages:

  • Flexible: Your methods become interchangeable because they are developed upon the same interface
  • Maintainable: If a new feature is requested, a new implementation can be easily developed and integrated within the existing interface

Interface vs abstract class

We should look at it from two perspectives - methods and variables. 

Methods:

  • Interfaces: Methods are defined without a body, and therewith without any logic. Classes that implement this interface, must have a method with this name, despite of the implemented logic.
  • Abstract classes: A method can be defined with a body. If the body wont be overwritten by the respective sub class, it is going to implement the same logic. 

Variables:

  • Interfaces: You can not define any variables within interfaces
  • Abstract classes: You can define private, public, and protected variables.

Further resources