Chapter 4. Creating Clean Code with OOP

When applications start growing, representing more complex data structures becomes necessary. Primitive types like integers, strings, or arrays are not enough when you want to associate specific behavior to data. More than half a century ago, computer scientists started using the concept of objects to refer to the encapsulation of properties and functionality that represented an object in real life.

Nowadays, OOP is one of the most used programming paradigms, and you will be glad to know that PHP supports it. Knowing OOP is not just a matter of knowing the syntax of the language, but knowing when and how to use it. But do not worry, after this chapter and a bit of practice, you will become a confident OOP developer.

In this chapter, you will learn about the following:

  • Classes and objects
  • Visibility, static properties, and methods
  • Namespaces
  • Autoloading classes
  • Inheritance, interfaces, and traits
  • Handling exceptions
  • Design patterns
  • Anonymous functions

Classes and objects

Objects are representations of real-life elements. Each object has a set of attributes that differentiates it from the rest of the objects of the same class, and is capable of a set of actions. A class is the definition of what an object looks like and what it can do, like a pattern for objects.

Let's take our bookstore example, and think of the kind of real-life objects it contains. We store books, and let people take them if they are available. We could think of two types of objects: books and customers. We can define these two classes as follows:

<?php

class Book {
}

class Customer {
}

A class is defined by the keyword class followed by a valid class name—that follows the same rules as any other PHP label, like variable names—and a block of code. But if we want to have a specific book, that is, an object Book—or instance of the class Book—we have to instantiate it. To instantiate an object, we use the keyword new followed by the name of the class. We assign the instance to a variable, as if it was a primitive type:

$book = new Book();
$customer = new Customer();

You can create as many instances as you need, as long as you assign them to different variables:

$book1 = new Book();
$book2 = new Book();

Class properties

Let's think about the properties of books first: they have a title, an author, and an ISBN. They can also be available or unavailable. Write the following code inside Book.php:

<?php

class Book {
    public $isbn;
    public $title;
    public $author;
    public $available;
}

This preceding snippet defines a class that represents the properties that a book has. Do not bother about the word public; we will explain what it means when talking about visibility in the next section. For now, just think of properties as variables inside the class. We can use these variables in objects. Try adding this code at the end of the Book.php file:

$book = new Book();
$book->title = "1984";
$book->author = "George Orwell";
$book->available = true;
var_dump($book);

Printing the object shows the value of each of its properties, in a way similar to the way arrays do with their keys. You can see that properties have a type at the moment of printing, but we did not define this type explicitly; instead, the variable took the type of the value assigned. This works exactly the same way that normal variables do.

When creating multiple instances of an object and assigning values to their properties, each object will have their own values, so you will not override them. The next bit of code shows you how this works:

$book1 = new Book();
$book1->title = "1984";
$book2 = new Book();
$book2->title = "To Kill a Mockingbird";
var_dump($book1, $book2);

Class methods

Methods are functions defined inside a class. Like functions, methods get some arguments and perform some actions, optionally returning a value. The advantage of methods is that they can use the properties of the object that invoked them. Thus, calling the same method in two different objects might have two different results.

Even though it is usually a bad idea to mix HTML with PHP, for the sake of learning, let's add a method in our class Book that returns the book as in our already existing function printableTitle:

<?php

class Book {
    public $isbn;
    public $title;
    public $author;
    public $available;

    public function getPrintableTitle(): string {
        $result = '<i>' . $this->title
            . '</i> - ' . $this->author;
        if (!$this->available) {
            $result .= ' <b>Not available</b>';
        }
        return $result;
    }
}

As with properties, we add the keyword public at the beginning of the function, but other than that, the rest looks just as a normal function. The other special bit is the use of $this: it represents the object itself, and allows you to access the properties and methods of that same object. Note how we refer to the title, author, and available properties.

You can also update the values of the current object from one of its functions. Let's use the available property as an integer that shows the number of units available instead of just a Boolean. With that, we can allow multiple customers to borrow different copies of the same book. Let's add a method to give one copy of a book to a customer, updating the number of units available:

public function getCopy(): bool {
    if ($this->available < 1) {
        return false;
    } else {
        $this->available--;
        return true;
    }
}

In this preceding method, we first check if we have at least one available unit. If we do not, we return false to let them know that the operation was not successful. If we do have a unit for the customer, we decrease the number of available units, and then return true, letting them know that the operation was successful. Let's see how you can use this class:

<?php
$book = new Book();
$book->title = "1984";
$book->author = "George Orwell";
$book->isbn = 9785267006323;
$book->available = 12;

if ($book->getCopy()) {
    echo 'Here, your copy.';
} else {
    echo 'I am afraid that book is not available.';
}

What would this last piece of code print? Exactly, Here, your copy. But what would be the value of the property available? It would be 11, which is the result of the invocation of getCopy.

Class constructors

You might have noticed that it looks like a pain to instantiate the Book class, and set all its values each time. What if our class has 30 properties instead of four? Well, hopefully, you will never do that, as it is very bad practice. Still, there is a way to mitigate that pain: constructors.

Constructors are functions that are invoked when someone creates a new instance of the class. They look like normal methods, with the exception that their name is always __construct, and that they do not have a return statement, as they always have to return the new instance. Let's see an example:

public function __construct(int $isbn, string $title, string $author, int $available) {
    $this->isbn = $isbn;
    $this->title = $title;
    $this->author = $author;
    $this->available = $available;
}

The constructor takes four arguments, and then assigns the value of one of the arguments to each of the properties of the instance. To instantiate the Book class, we use the following:

$book = new Book("1984", "George Orwell", 9785267006323, 12);

This object is exactly the same as the object when we set the value to each of its properties manually. But this one looks cleaner, right? This does not mean you cannot set new values to this object manually, it just helps you in constructing new objects.

As a constructor is still a function, it can use default arguments. Imagine that the number of units will usually be 0 when creating the object, and later, the librarian will add units when available. We could set a default value to the $available argument of the constructor, so if we do not send the number of units when creating the object, the object will be instantiated with its default value:

public function __construct(
    int $isbn,
    string $title,
    string $author,
    int $available = 0
) {
    $this->isbn = $isbn;
    $this->title = $title;
    $this->author = $author;
    $this->available = $available;
}

We could use the preceding constructor in two different ways:

$book1 = new Book("1984", "George Orwell", 9785267006323, 12);
$book2 = new Book("1984", "George Orwell", 9785267006323);

$book1 will set the number of units available to 12, whereas $book2 will set it to the default value of 0. But do not trust me; try it by yourself!

Magic methods

There is a special group of methods that have a different behavior than the normal ones. Those methods are called magic methods, and they usually are triggered by the interaction of the class or object, and not by invocations. You have already seen one of them, the constructor of the class, __construct. This method is not invoked directly, but rather used when creating a new instance with new. You can easily identify magic methods, because they start with __. The following are some of the most used magic methods:

  • __toString: This method is invoked when we try to cast an object to a string. It takes no parameters, and it is expected to return a string.
  • __call: This is the method that PHP calls when you try to invoke a method on a class that does not exist. It gets the name of the method as a string and the list of parameters used in the invocation as an array, through the argument.
  • __get: This is a version of __call for properties. It gets the name of the property that the user was trying to access through parameters, and it can return anything.

You could use the __toString method to replace the current getPrintableTitle method in our Book class. To do that, just change the name of the method as follows:

public function __toString() {
    $result = '<i>' . $this->title . '</i> - ' . $this->author;
    if (!$this->available) {
        $result .= ' <b>Not available</b>';
    }
    return $result;
}

To try the preceding code, you can just add the following snippet that creates an object book and then casts it to a string, invoking the __toString method:

$book = new Book(1234, 'title', 'author');
$string = (string) $book; // title - author Not available

As the name suggests, those are magic methods, so most of the time their features will look like magic. For obvious reasons, we personally encourage developers to use constructors and maybe __toString, but be careful about when to use the rest, as you might make your code quite unpredictable for people not familiar with it.

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

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