4

Using Object-Oriented Programming in PHP

PHP is an open source scripting programming language that has supported Object-Oriented Programming (OOP) features since PHP 5. PHP is straightforward to learn, and it is very flexible. No wonder it’s prevalent. There are a lot of open source PHP learning materials available online, as well as open source libraries and frameworks. If you’re planning to build a web application, the chances are that there will be some downloadable PHP libraries and frameworks that will pretty much suit your needs. If you require PHP developers for your project, you’d be glad to know that there are indeed a lot of PHP developers out there.

As a PHP developer, I have worked on many PHP projects ranging from tiny web applications to enterprise applications. I’ve seen many PHP applications developed by different companies and teams. Some applications I’ve seen and worked on personally were properly built following best practices and industry standards, but some were made of spaghetti mess glued together by duct tape. There’s one thing in common, though; it doesn’t matter if the software is well-written or poorly written; successful software will require updates. New features and bug fixes will be required. The more successful the software gets, the more users utilize the software, the more feature requests get submitted, and the more bugs get discovered. It’s a cycle, but it’s a great problem to have to begin with.

No one wants to see regressions introduced by new features and bug fixes, but it happens. And sometimes, if there are no proper development or release processes involved, regressions happen a lot. New bugs and problems can be introduced after a release, and this is very demoralizing. The business also loses its confidence in releasing more bug fixes or new features. For the developers and the business, releasing codes should be a positive experience, not a cause for anxiety.

Using OOP and Test-Driven Development (TDD) together helps to improve code quality by making sure that most of the functions and objects are testable, maintainable, reusable, and mockable. Mocks will be discussed more in Chapter 8, Using TDD with SOLID Principles.

In this chapter, we’ll go through the definition and meaning of OOP in PHP. We’ll go through the Four Pillars of OOP: abstraction, encapsulation, inheritance, and polymorphism. We will try to use example codes to explain the OOP concepts, and these concepts will serve as the foundations for the TDD codes we will be writing for our example project later.

In this chapter, we will be going through the following:

  • Understanding OOP in PHP
  • Classes versus objects
  • Abstraction in OOP
  • Encapsulation in OOP
  • Inheritance in OOP
  • Polymorphism in OOP
  • PHP Standards Recommendations (PSRs)

Technical requirements

The user is expected to have at least basic knowledge of PHP or other OOP languages such as Java or C#. I’m also using the PHPStorm Integrated Development Environment (IDE) throughout the book, which can be seen in the screenshots.

Understanding OOP in PHP

Can I use PHP with TDD? Absolutely – and PHP, thanks to its OOP capabilities, works well with TDD. We have previously explained that TDD is a process; it’s not a piece of software that you can install. You can install or download tools to implement TDD, and there are a lot of different tools available for other programming languages as well.

Since PHP 5 was released in the early 2000s, classes and objects were supported, allowing OOP to be used for PHP. It would be an advantage if the reader has a good understanding of OOP and has worked on PHP OOP projects, but if not, I’ll do my best to introduce OOP to you, as it is an effective and efficient way to write software. We will also be using OOP in our example project, so we need to make sure that the reader understands OOP.

What is OOP, anyway?

OOP is a programming style based on the concept of classes and objects. One of its goals is to let software developers write reusable and extensible codes.

OOP focuses on packaging behaviors, logic, and properties into reusable objects. These objects are instances of classes, and these classes are the files that we software developers have to write to contain logic, routines, and properties. Since objects are based on classes, we can create many instances of objects using a single class. We can also use OOP’s inheritance feature to create an object that will also have the capabilities of its parent. Later in this chapter, we will discuss the difference between a class and an object in OOP.

Back when I was a junior developer, my friend, who was a JavaScript developer, told me that he struggled to understand OOP because he had read some OOP explanations that used vehicle and animal metaphors. He said he didn’t understand how it related to the actual software he was writing, so for the sake of our younger selves, if I ever travel back in time, I’ll try to explain OOP to junior developers such as myself and my friend as if I had never used OOP before. I realized that, at least for me, if I understand the problem and the purpose of a solution, I can understand the concept easier.

I’ve seen many developers struggle with this concept and fail to take advantage of its incredible power. I learned OOP back in university while using Java and C++ and when I worked professionally as a C# developer after graduating. Back then, I thought OOP was everywhere and was the usual way of writing code in the professional software development world, but when I started my first professional PHP position, I worked on a PHP application with no OOP codes in the code base. With PHP, a developer can create a PHP file and start writing functions and codes directly executed from a CLI or a web browser. It’s so easy! However, it’s a slippery slope.

I remember telling myself, “I hate OOP, and it’s so complicated; why did I waste my time with OOP in Java and C# when I have to write so much code just to return text on a web page?” Sure enough, I ended up writing many files with lots of database queries and lots of business logic intertwined. I basically wrote the user interface, business logic, and persistence codes in a single file. Does that sound familiar? If you’re one of the poor souls who may have inherited my spaghetti codes from more than a decade ago, I sincerely apologize. I wrote spaghetti codes with OOP and without OOP in different languages. OOP is not a magic bullet solution to stop messy spaghetti codes, but OOP surely does help. It’s not the tool; it’s how you use it.

The excitement I felt did not last very long. As a junior PHP developer back then, I ended up writing so many PHP files with so many random functions. It was a nightmare to maintain, and I couldn’t even reuse my own codes elsewhere in the application! I told myself that if I only had used OOP, I could at least borrow or reuse some existing codes easily even if I had spaghetti codes. Now, imagine working with a team where you collectively build software and you cannot easily reuse each other’s codes. I then hated PHP, but as it turned out, I was not using PHP properly. I did not know that I could use PHP to implement OOP as well. I think the same thing can be said of any other programming language. It doesn’t matter how good the language is; the project’s success depends on the software engineers, developers, and architects themselves. You can use OOP in any language and still produce spaghetti codes. In this book, we will use TDD and software development best practices to help us write better and less tangled code. We will be talking about some software development principles that will help us a lot in writing better code in Chapter 8, Using TDD with SOLID Principles.

It took me a while before I truly understood the real benefits of OOP. Reading OOP explanations with vehicles and animals online are helpful but working on projects and experiencing the pains of not using OOP is what really helped me see the light.

With OOP, a developer can write codes that can be easily borrowed or used by other developers, including yourself.

In the coming sections, we’ll go through the fundamental concepts of OOP – abstraction, encapsulation, inheritance, and polymorphism – but before that, we will need to start to define what a class and an object are, as well as their differences and how to distinguish them.

In the following examples, we will not be using TDD yet. We will just be focusing on OOP itself. We will be focusing on the PHP-specific implementation of OOP.

Classes versus objects

A PHP class is a file that contains codes. Your class is a file that physically exists on your drive. If you turn your computer off and turn it on again, the class file is still there. The code it contains is a template to create an object during execution. The class file can contain properties and behaviors. The properties will be able to hold data in memory once the class has been instantiated. The behaviors will be handled by methods or functions. You can use the accessors and mutator methods to change the values of a class’s properties.

Dog.php class file

<?php
namespace Animal;
class Dog
{
    public function returnSound(): string
    {
        return "Bark";
    }
}

The preceding example class is a PHP file containing the namespace declaration, the class name, and a single method or function called returnSound(), which returns a "Bark" string.

On the other hand, an object is an instance of a class file. The object physically exists in the computer’s RAM, which is volatile memory. That means if you turn off your computer or stop the program, you lose the object. The object will only be created if you run your program.

In PHP, when you execute your program, the class file will be loaded by PHP from your hard disk to create an instance of an object that will temporarily exist in your RAM. The class literally is the template for PHP to create the object while your program is running.

We’ll use a consumer class that will utilize or consume the Dog.php class, and use a variable to hold an instance of a class, which is an object:

Display.php Class File

<?php
namespace Animal;
class Display
{
    public function outputSound()
    {
        $dog = new Dog();
        echo $dog->returnSound();
    }
}

The Display class is another class; you can think of this as a consumer class or the starting point of the example program. In this class, we have an outputSound() method that echoes the value from the object’s returnSound() method. Inside the outputSound() method, we have written instructions for PHP to create an instance of the Dog class using the new keyword:

Figure 4.1 – Assigning the Dog object to the $dog variable

Figure 4.1 – Assigning the Dog object to the $dog variable

When PHP executes the outputSound() method, it will create an object that is based on the Dog.php class file that is stored in your computer’s drive and then it will temporarily store the instance or object inside your computer’s memory. The $dog variable will be mapped with the Dog class instance or object. Whenever you use an object’s method or properties, you are basically accessing the object from your computer’s memory, and not from the Dog.php class file. To understand this further, we will need to talk about References in PHP, which we will cover in the next subsection.

Now, since we have created a new instance of the Dog.php class file and assigned it to the $dog variable, we will be able to access the Dog object’s methods or functions, or properties, depending on their visibility. We will talk about visibility in this chapter in the Encapsulation in OOP section. Since in our example, we have defined the Dog.php class’s returnSound() method as public, we can now access this method from the Display.php class’s outputSound() method with the following: $dog->returnSound();.

References and objects in PHP

What is a reference anyway? Well, in PHP, it’s an alias or a way for one or more variables to point to specific content. From the Dog object example earlier, we have created an instance of the Dog.php class and assigned it to the $dog variable. The $dog variable itself does not really contain the Dog object or instance’s memory address; it simply contains an identifier so that it can point to the Dog object that is stored in the memory. That means that you can have $dog1 and $dog2 variables that point to the same object. Let’s modify the Dog.php and Display.php classes to demonstrate the concept.

We will modify the Dog.php class like so:

<?php
namespace Animal;
class Dog
{
    private string $sound;
    public function __construct()
    {
        $this->setSound("Bark");
    }
    public function returnSound(): string
    {
        return $this->getSound();
    }
    /**
     * @return string
     */
    public function getSound(): string
    {
        return $this->sound;
    }
    /**
     * @param string $sound
     */
    public function setSound(string $sound): void
    {
        $this->sound = $sound;
    }
}

And we’ll modify the Display.php class as follows:

<?php
namespace Animal;
class Display
{
    public function outputSound()
    {
        $dog1 = new Dog();
        $dog2 = $dog1;
        $dog1->setSound("Barky Bark");
        // Will return "Barky Bark" which was set to $dog1.
        echo $dog2->returnSound();
    }
}

We’ve modified the Dog.php class so that we can change the sound it returns after we instantiate it. In the Display.php class, you’ll notice that we have introduced a new variable, $dog2, and assigned $dog1 to it. We only have one instance of the Dog object, and both the $dog1 and $dog2 variables have the same identifier and are referencing the same thing. Here’s a diagram representing this concept:

Figure 4.2 – What happens to $dog1’s property also happens to $dog2

Figure 4.2 – What happens to $dog1’s property also happens to $dog2

So, if we run $dog2->returnSound(), it will return the updated string that we have set in $dog1 even if we have mutated the $sound property after we have assigned $dog1 to $dog2.

Well, what if you don’t want $dog2 to be affected by what happens to $dog1’s properties, but still want to create a copy or a duplicate of that object? You can use PHP’s clone keyword like so:

Display.php Class

<?php
namespace Animal;
class Display
{
    public function outputSound()
    {
        $dog1 = new Dog();
        $dog2 = clone $dog1;
        $dog1->setSound("Barky Bark");
        // Will return "Bark".
        echo $dog2->returnSound();
    }
}

This time, $dog2 will return the original Bark string value assigned to the $sound property of $dog1 by its constructor. Here’s a diagram for your reference to understand this:

Figure 4.3 – Clone object

Figure 4.3 – Clone object

Since $dog2 has been cloned before the actor has mutated the $sound property of $dog1, $dog2 will retain the old value. Whatever happens to $dog1 will no longer automatically happen to $dog2, as they are no longer referencing the same object in the memory:

Figure 4.4 – Class versus object

Figure 4.4 – Class versus object

In summary, a PHP class is a file that contains a template for PHP to be able to create an object. When the new keyword is used and executed, PHP takes the class file and generates an instance of the class file, and stores it in the computer’s memory, this is what we call an object.

Now that we have clarified and explained the difference between an object and a class in PHP, we can now discuss the Four Pillars of OOP, starting with abstraction.

Abstraction in OOP

Abstraction in OOP is the concept of hiding complexities from the application, user, or developers. You can take a set of complex codes or instructions and wrap them inside a function. That function should use a verb for its name, which will make it easier to understand exactly what the complex instructions inside the function do.

For example, you can have a function called computeTotal($a, $b, $c) that contains logic or steps to compute the total based on the requirements. As a developer, you can just use the computeTotal method and not think about all the complex operations that are involved in the actual computation of the total, but if you need to fix a bug or understand what’s going on, then you can check what’s going on inside that computeTotal function:

public function computeTotal(int $a, int $b, int $c): int
{
    if ($c > 1) {
        $total = $a + $b;
    } else if ($c < 1) {
        $total = $a - $b;
    } else {
        $total = 0;
    }
    return $total;
}

What’s the benefit of using this concept? Using the preceding example, the developer won’t need to worry about the exact sequence of steps inside the function to get the total. The developer only needs to know that there is a computeTotal function available to be used, along with hundreds or thousands of other functions each with complex steps in instructions inside them. The developer can focus on the solution and not worry about the fine details inside each function.

An abstract class is a way to implement class abstraction and is a type of class that cannot be instantiated and needs to have at least one method declared as abstract. An abstract class is meant to be extended by other related classes:

<?php
abstract class AbstractPrinter
{
    abstract protected function print(string $message): 
        bool;
}
class ConsolePrinter extends AbstractPrinter
{
    protected function print(string $message): bool
    {
        // TODO: Implement print() method.
    }
}
class PdfPrinter extends AbstractPrinter
{
    protected function print(string $message): bool
    {
        // TODO: Implement print() method.
    }
}

The method declared as abstract in AbstractPrinter must also exist in the classes that extend this method. Each class that extends the AbstractPrinter abstract class can now have its own specific operations for the print method. A method declared as abstract in an abstract class can only declare the method’s visibility, parameters, and return value. It cannot have its own implementation.

Encapsulation in OOP

Encapsulation is a concept where data and methods that access and mutate this data are enclosed in a single unit like a capsule. In PHP, this capsule is the object or class.

The capsule or the object will have the ability to keep its data safe from being read or manipulated, using the concept of visibility.

Visibility in PHP

To be able to control what data or functions the developer can access or use in an object in PHP, the public, protected, and private keywords can be prefixed before the function keyword in the function declaration, or before the property name declaration:

  • private – Only the codes within the object can access this function or property
  • protected – Any object extending this class will be allowed access to the function or property
  • public – Any object user is allowed to access the property or method

What benefits do we developers get from this, then? We’ll get to know this in a bit.

Let’s modify the Dog.php class from the earlier example like so:

<?php
namespace Animal;
class Dog
{
    private string $sound;
    private string $color;
    public function __construct()
    {
        $this->setSound("Bark");
        $this->setColor("Black");
    }
    public function makeSound(): string
    {
        $prefix = "Hello ";
        $suffix = " World";
        return $prefix . $this->getSound() . $suffix;
    }
    /**
     * @return string
     */
    private function getSound(): string
    {
        return $this->sound;
    }
    /**
     * @param string $sound
     */
    public function setSound(string $sound): void
    {
        $this->sound = $sound . ", my color is: " . 
            $this->getColor();
    }
    /**
     * @return string
     */
    protected function getColor(): string
    {
        return $this->color;
    }
    /**
     * @param string $color
     */
    protected function setColor(string $color): void
    {
        $this->color = $color;
    }
}

Create a Cavoodle.php class :

<?php
namespace AnimalDogs;
use AnimalDog;
class Cavoodle extends Dog
{
    public function __construct()
    {
        parent::__construct();
        // Using the protected method from the Dog class.
        $this->setColor("Chocolate");
    }
}

Modify the Consumer.php class like so:

<?php
namespace Animal;
use AnimalDogsCavoodle;
class Consumer
{
    public function sayHello()
    {
        $dog = new Dog();
        $dog->setSound("Wooooof!");
        // Will output Hello Wooooof!, my color is: Black
        $dog->makeSound();
    }
    public function sayHelloCavoodle()
    {
        $cavoodle = new Cavoodle();
        $cavoodle->setSound("Bark Bark!");
        // Will output Hello Bark Bark!!, my color is: 
            Chocolate
        $cavoodle->makeSound();
    }
}

In this Dog.php example class, we have declared the following:

  • Private:
    • $sound
    • $color
  • Protected:
    • getColor()
    • setColor()
  • Public:
    • makeSound()
    • setSound($sound)

By doing so, we have protected the values of the Dog object’s $sound and $color properties from being modified directly by the object consumer. Only the Dog object can modify these values directly. The value stored inside the $sound property can be modified from the object consumer side by using the $dog->setSound($sound) method, but whatever the object consumer sets in the $dog->setSound($sound) method, the data that will be stored in the Dog object’s $sound property will always be suffixed with the value of the $color property. There’s nothing the object consumer can do to change that; only the object itself can change its own property’s value.

The following is a screenshot of the Consumer.php class and as I modify it, my PHPStorm IDE automatically suggests the available methods for the Cavoodle object:

Figure 4.5 – Public functions available for Dog

Figure 4.5 – Public functions available for Dog

You will notice that in the Consumer class, there are only two available functions for us. The setSound() and makeSound() functions are the functions that we have declared as publicly visible. We have successfully limited or protected the Cavoodle (which is an instance of a Dog class) object’s other functions and properties.

The following screenshot shows that as we are inside the Cavoodle.php class, my IDE automatically suggests the available methods for the Cavoodle class itself by using the $this key:

Figure 4.6 – Cavoodle itself can access more functions than the Consumer class

Figure 4.6 – Cavoodle itself can access more functions than the Consumer class

Inside the Cavoodle.php class itself, you will notice that this Cavoodle object can access the getColor() and setColor() methods. Why is that? It’s because the Cavoodle class has extended the Dog.php class, inheriting the Dog.php class’s non-private method – and since we have declared the getColor and setColor functions as having protected visibility, these methods are available for the Cavoodle class.

Accessors and mutators

Since we have set the $sound and $color properties to private, how do we let consumers access these properties? For reading data, we can write functions called accessors that return the data stored in the property. To change the value of the property, we can create functions called mutators to mutate the data for the property.

To access the $sound and $color properties in the Dog.php class, we need the following accessors:

  • getSound
  • getColor

To change the values of the $sound and $color properties in the Dog.php class, we need the following mutators:

  • setSound
  • setColor

These functions are declared in the Dog.php class – since these are functions, you can add extra validation or logic change the value before storing it into the property or returning it to the user.

When writing properties or functions, it’s a good practice to declare their visibility to be as restrictive as possible. Start with private and then if you think the child objects need to access the function or property, then set the visibility to protected. That way, you can end up having fewer publicly available methods and properties.

This will only allow you and the other developers that use your classes to see the functions that are supposed to be available to the consumers. I’ve written and seen classes with a lot of methods, only to find out that they are not intended to be used by other objects apart from the main object itself. This will also help preserve the data integrity of the object by preventing consumer objects from modifying properties directly. If you need to let the consumer manipulate the data stored in the property of the object, the user can use mutator methods. To let them read the data from the property, they can use the accessors.

Inheritance in OOP

Inheritance in OOP is a concept in which an object can acquire the methods and properties of another object.

Inheritance can help developers reuse functions for very related objects. You probably heard of the Don’t Repeat Yourself (DRY) principle; inheritance can help with writing less code and fewer repetitions too to help you to reuse codes.

Objects such as Cavoodle and Dog are related – we know that Cavoodle is a type of Dog. The functions available for Dog and Cavoodle should be focused on what Dog and Cavoodle should be able to do. If you have a Dog object and it has a computeTax function, for example, that function is not related to the Dog object and you’re probably doing something wrong – it has low cohesion. Having high cohesion means that your class is focused on doing what your class should really be doing. By having high cohesion, it’s easier to decide if an object should be an object that can be inherited, as with the Dog and the Cavoodle objects. It won’t make sense if the Cavoodle class extends a JetEngine class, but it makes perfect sense for the Cavoodle class to extend the Dog class:

Cavoodle.php

<?php
namespace AnimalDogs;
use AnimalDog;
class Cavoodle extends Dog
{
    public function __construct()
    {
        parent::__construct();
        // Using the protected method from the Dog class.
        $this->setColor("Chocolate");
    }
}

To use the Cavoodle class’s methods in a consumer class, create a new instance of the Cavoodle class:

public function sayHelloCavoodle()
{
    $cavoodle = new Cavoodle();
    $cavoodle->setSound("Bark Bark!");
    // Will output Hello Bark Bark!!, my color is: 
           Chocolate
    $cavoodle->makeSound();
}

The Cavoodle object has inherited the Dog object using the extends keyword. That means that any public or protected function from Dog is now available to the Cavoodle object. You will notice that there is no makeSound function declared in the Cavoodle.php class, but we are still able to use the $cavoodle->makeSound(); method simply because the Cavoodle object has inherited the makeSound function from the Dog object.

Polymorphism in OOP

Polymorphism means many shapes or many forms. Polymorphism is achieved through the inheritance of a PHP abstract class, as well as by implementing PHP interfaces.

Polymorphism helps you create a format or a standard for solving a specific problem programmatically, instead of just focusing on a single implementation of a solution.

How do we apply this in PHP and what benefit do we get in using this feature? Let’s take the example codes in the following subsections as an example, starting with a PHP abstract class.

Polymorphism with a PHP abstract class

When using abstract classes in PHP, we can implement polymorphism by using abstract functions. The following example is of a PHP abstract class:

AbstractAnimal.php

<?php
namespace AnimalsPolymorphismAbstractExample;
abstract class AbstractAnimal
{
    abstract public function makeSound();
}

Every PHP abstract class ideally should start with the Abstract prefix, followed by the desired name of the abstract class as suggested by the PSR standards. The PSR standards will be discussed later in this chapter in the PHP Standards Recommendations (PSR) section.

The abstract class needs at least one function declared as abstract. This can be achieved by adding the abstract keyword before the access modifier or visibility declaration of a function, such as abstract public function makeSound(). Now, we may notice that there is no actual action or logic inside the makeSound() method of the abstract class, and as we have explained earlier, we cannot instantiate abstract classes. We will need child classes to extend the abstract class, where we can declare the specific action or logic to be performed by that child class.

The following is an example of a child Cat.php class:

Cat.php

<?php
namespace AnimalsPolymorphismAbstractExample;
class Cat extends AbstractAnimal
{
    public function makeSound(): string
    {
        return "meow!";
    }
}

The following is an example of a child Cow.php class:

Cow.php

<?php
namespace AnimalsPolymorphismAbstractExample;
class Cow extends AbstractAnimal
{
    public function makeSound(): string
    {
        return "mooo!";
    }
}

Both of these classes have inherited the AbstractAnimal.php class, and since we have declared the makeSound() function as an abstract method, the Cat.php and Cow.php classes are required to have those same methods as well but without the abstract keyword. You will notice that the Cat object’s makeSound function returns a meow string, and the Cow object’s similar makeSound function returns a moo string. Here, we have achieved polymorphism by having one function signature, and having that function signature implemented uniquely by the child classes.

Next, we will look at implementing polymorphism using a PHP interface.

Polymorphism with a PHP interface

A PHP interface is a simpler version of a PHP abstract class. An interface cannot have properties like a normal class can, and it can only contain publicly visible functions. Each method in an interface must be implemented by any class that uses the interface but without the need to add the abstract keyword. Therefore, we must be very careful when declaring functions to an interface. It’s very easy to end up having an interface with too many functions that it doesn’t make sense for each implementing class to use. This is where the Interface Segregation Principle comes in to help, and this will be discussed more in Chapter 8, Using TDD with SOLID Principles.

Imagine that you need a program to return results in different formats, and you’d also like to be able to isolate the logic and dependencies to come up with the desired results. You can use an interface to set a contract that will be followed by your objects. For example, there are different ways and formats in which to return output and in the following example, we will return an XML and a JSON.

We will create a PHP interface, which will serve as a contract that both the JSON and XML classes will implement. The interface will have a single generic print function that accepts a string parameter, and returns a string:

PrinterInterface.php

<?php
namespace PolymorphismInterfaceExample;
interface PrinterInterface
{
    public function print(string $message): string;
}

We will then create the first concrete implementation of PrinterInterface and it will have to have a concrete implementation of the print function to return a JSON-formatted string:

Json.php

<?php
namespace PolymorphismInterfaceExample;
class Json implements PrinterInterface
{
    public function print(string $message): string
    {
        return json_encode(['hello' => $message]);
    }
}

The second concrete implementation of PrinterInterface is the Xml class – it will also have to include a print function that returns a string, but the string will be formatted as an XML this time:

Xml.php

<?php
namespace PolymorphismInterfaceExample;
class Xml implements PrinterInterface
{
    public function print(string $message): string
    {
        return "<message>Hello " . $message . "</message>";
    }
}

We have declared a public print(string $message): string method signature in PrinterInterface, and since the Xml.php and Json.php classes have implemented PrinterInterface using the implements keyword after the class name declaration, both Xml.php and Json.php are now required to follow the contract. They must have public print(string $message): string functions as well. Each implementing class will have its own unique way of returning an output. One returns an XML and the other returns a JSON – one method, and different forms or shapes. This is how polymorphism is achieved using PHP interfaces.

However, what’s the advantage of using polymorphism in the first place? Let’s take this consumer class for example:

Display.php

<?php
namespace PolymorphismInterfaceExample;
class Display
{
    /**
     * @var PrinterInterface
     */
    private $printer;
    public function __construct(PrinterInterface $printer)
    {
        $this->setPrinter($printer);
    }
    /**
     * @param string $message
     * @return string
     */
    public function displayOutput(string $message): string
    {
        // Do some additional logic if needed:
        $printerOutput = $this->getPrinter()->print
            ($message);
        $displayOutput = "My Output is: " . $printerOutput;
        return $displayOutput;
    }
    /**
     * @return PrinterInterface
     */
    public function getPrinter(): PrinterInterface
    {
        return $this->printer;
    }
    /**
     * @param PrinterInterface $printer
     */
    public function setPrinter(PrinterInterface $printer): 
        void
    {
        $this->printer = $printer;
    }
}

In the Display.php class, we have a displayOutput method that uses an object that must implement PrinterInterface. The displayOutput method gets the result from the PrinterInterface-implementing object (we don’t know what object that is) and appends it as a suffix to a string before returning it.

Now, this is the important bit – the Display.php class does not know how the PrinterInterface-implementing object comes up with the actual XML or JSON formatting. The Display.php class does not care and does not worry about that. We have handballed the responsibility to the PrinterInterface-implementing object. Therefore, instead of having one god class that contains all of the logic for returning a JSON or an XML output, resulting in a spaghetti mess, we just use other objects that implement the interface that we need them to implement. The Display.php class does not even know what class is being used – it only knows that it is using an object that has implemented PrinterInterface. We have now successfully decoupled the Display.php class from the job of formatting XML or JSON or any other format to other objects.

Now that we have gone through the fundamentals of OOP in PHP, let’s go through some guidelines or standards on how to write PHP codes. These guidelines are not required to build or run a PHP program, but they will help developers write better, more readable, and more shareable codes. The following standards on how to write PHP codes are important when building enterprise-level applications, especially as you are expected to develop codes with a lot of other developers, and your codes can be expected to be used and last for years. Future developers who will eventually take over your project should be able to understand, read, and reuse your codes easily as well. The following standards will help you and your team achieve that

PHP Standards Recommendations (PSRs)

As mentioned earlier, there are a lot of open source libraries and frameworks built for PHP. Each individual developer will have their own preferences in their style of writing codes, and each framework or library can have its own standard or way of doing things. This can start to become problematic for PHP developers, as we tend to use a lot of different libraries and frameworks.

For example, it’s no fun transitioning from one framework to another only to end up having different types of service containers, which will require you to change the way you organize the dependencies for your application, and therefore PSR-11 was introduced. Service containers are applications that manage the instantiation of objects including their dependencies—they are very handy when implementing Dependency Injection or DI, which is discussed in Chapter 8, Using TDD with SOLID Principles. This is one of the examples of why it is important, although not required, to follow some specific guidelines or standards, and where PSR comes in.

What is a PSR?

A PSR is recommended by the PHP Framework Interoperability Group (PHP-FIG). They are a group of very kind developers who help us make our PHP coding life way more organized. You can find out more about the PHP-FIG at https://www.php-fig.org/.

The following are the currently accepted PSRs:

  • PSR-1: Basic Coding Standard
  • PSR-3: Logger Interface
  • PSR-4: Autoloading Standard
  • PSR-6: Caching Interface
  • PSR-7: HTTP Message Interface
  • PSR-11: Container Interface
  • PSR-12: Extending Coding Style Guide (has deprecated PSR-2)
  • PSR-13: Hypermedia Links
  • PSR-14: Event Dispatcher
  • PSR-15: HTTP Handlers
  • PSR-16: Simple Cache
  • PSR-17: HTTP Factories
  • PSR18: HTTP Client

You can find all the currently accepted PSRs at https://www.php-fig.org/psr/. Initially, the most important PSRs to get familiar with are PSR-1, PSR-12, and PSR-4. This helps us write codes in a more consistent style, especially when transitioning from one framework to another framework. I used to have a “favorite” PHP MVC framework thinking that I’d use that framework until I grew old – and as usual, I was wrong. I ended up using so many different frameworks that I no longer cared which framework I was using. I now have favorites for each specific job.

The PSRs are just “recommendations.” They are not like actual laws that you need to follow, but if you are serious about writing PHP and improving the quality of your own code, then I highly suggest that you follow them. Many people have already experienced the pain of not following standards. I wrote my own dependency injection container once, only to end up having other developers in our team get confused down the road on how to use it. I just reinvented the wheel, the bad way. How I wish there was a standard I could follow! Oh yeah, there is now a PSR-11.

Summary

In this chapter, we defined what OOP is and why we’d want to take advantage of it. Then, we clearly defined what classes and objects are in PHP. We then went through some examples for each of the Four Pillars of OOP. We learned what abstraction, encapsulation, inheritance, and polymorphism are and how they work in PHP. We’ve also briefly gone through the PSRs because we don’t just want to go ahead and invent standards and start writing codes – we want to produce clean PHP code that is easy to understand and maintain, especially in an enterprise environment where you can expect to work with a lot of other developers, and where your codes will have to be very readable and maintainable for years to come.

This chapter should have prepared you to start writing actual object-oriented PHP code – and in our TDD example project, we will take advantage of the OOP capabilities of PHP.

In the next chapter, we will talk about unit testing. We will define what it is, and how unit testing is used in TDD. We will also go through different types of automated tests. After going through the definition of unit testing, we will start writing our first unit tests and start executing those unit tests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.237.77