Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 18

Implementing Interfaces for Contract Enforcement

Recommended resources

Hello! Welcome to the third lesson in our "Advanced OOP Concepts" module.

In our last lesson, we mastered method overriding, learning how a child class can specialize or extend behavior inherited from a parent using the extends and parent:: keywords. This focused on the "is-a" relationship (e.g., an Article is a Post).

Today, we shift our perspective to the "can-do" relationship. We will explore interfaces, which are a cornerstone of modern, flexible PHP applications. You will learn how to define an interface as a formal "contract" and how to implement it in your classes. This forces different, unrelated classes to share a common set of behaviors, which is a powerful way to write interchangeable and maintainable code—a crucial skill for your goal of building professional web applications.

1. What is an Interface?

At its core, an interface is a blueprint of methods that a class must implement. Think of it as a contract. If a class agrees to the contract (by "implementing" the interface), it is obligated to provide its own implementation for all the methods defined in that contract.

The interface only specifies what methods must exist (their names, parameters, and return types); it says nothing about how they should work.

Let's begin with a short video that uses a simple, non-technical analogy to explain this core idea.

PHP Interfaces & Polymorphism - Interfaces Explained - Full PHP 8 Tutorial

This clip from Program With Gio uses the analogy of a debt collector to introduce the concept of an interface as a set of instructions on 'what' to do, not 'how' to do it.

Watch the first minute of the video (from 00:05 to 01:04) to get a clear, high-level understanding of what an interface represents.


2. Defining and Implementing an Interface

Now, let's see how this concept translates into PHP code. You define an interface using the interface keyword and a class agrees to the contract using the implements keyword.

PHP Interfaces & Polymorphism - Interfaces Explained - Full PHP 8 Tutorial

Continuing with the same video, let's see the syntax in action. Notice what happens when a class implements an interface but fails to fulfill the contract.

Watch from 01:04 to 02:36. Observe how the DebtCollector interface is created and then implemented by the CollectionAgency class. Your code editor or IDE will immediately flag an error until the required collect method is added to the class, enforcing the contract.

Here is the basic structure in code:

// 1. The Contract
interface Loggable {
    public function log(string $message): void;
}

// 2. A class that agrees to the contract
class FileLogger implements Loggable {
    // 3. Fulfilling the contract by implementing the method
    public function log(string $message): void {
        // Implementation: write the message to a file
        file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND);
    }
}

// Another class that also agrees to the same contract
class DatabaseLogger implements Loggable {
    public function log(string $message): void {
        // A completely different implementation: save the message to a database
        echo "Saving '{$message}' to the database.\n";
    }
}

If a class implements an interface, it must provide an implementation for every method declared in that interface, or PHP will raise a fatal error.


3. The Power of Interfaces: Writing Flexible Code

So, why is this useful? The primary benefit of interfaces is writing flexible, loosely-coupled code. By relying on an interface "contract" instead of a specific class "implementation," you can easily swap out components.

This is often achieved by using an interface as a type declaration (or "type hint") in a function or method parameter. This tells PHP, "I don't care what specific class you give me, as long as the object you provide implements this interface."

This is a key OOP principle called polymorphism (meaning "many forms"), where objects of different classes can be treated as objects of a common supertype (the interface).

PHP Interfaces & Polymorphism - Interfaces Explained - Full PHP 8 Tutorial

This next segment is crucial. It will first demonstrate a common problem: code that is 'tightly coupled' to a specific class, making it rigid. It then shows how using an interface as a type hint solves this problem, making the code flexible and adaptable.

Watch from 07:03 to 12:52. Focus on these key points: The Problem: The collectDebt method initially requires a CollectionAgency object, locking it into one implementation. The Error: When a new Rocky object is passed, the code breaks because it's the wrong class type. The Solution: The type hint is changed from the class (CollectionAgency) to the interface (DebtCollector). Now, the method accepts any object that fulfills the DebtCollector contract, making it polymorphic.

This ability to "program to an interface, not an implementation" is fundamental to building scalable applications. You could have a Checkout class that accepts a PaymentGateway interface, allowing you to process payments via PayPal, Stripe, or any other gateway without changing the Checkout class itself.

Let's reinforce this powerful idea with a short article that focuses specifically on using interfaces in type declarations.

Type Declarations using Interfaces in PHP

This article by Alain Schlesser clearly explains the value of using interfaces as contracts in your method arguments. It's a great summary of the 'why' behind interfaces.

Read the sections 'Using Type Declarations with Interfaces' and 'Splitting up the Implementations'. The key takeaway is encapsulated in this sentence: 'I need some kind of object that is able to fulfil the WatcherInterface contract.' This is the mental model you should adopt.


4. Key Rules and Features of Interfaces

Interfaces have a few simple but strict rules.

PHP Interfaces & Polymorphism - Interfaces Explained - Full PHP 8 Tutorial

Let's quickly cover the specific rules governing interfaces in PHP.

Watch the segment from 02:36 to 05:32. It covers the following key rules: All interface methods must be public. A class can implement multiple interfaces. An interface can extend another interface. Interfaces cannot have properties, but they can have constants.

Here's a summary of those rules:

  • Visibility: All methods declared in an interface must be public. This is because they are meant to be the public-facing contract.
  • No Implementation: Interface methods cannot have a body (no {...}).
  • No Properties: Interfaces cannot contain properties (variables), but they can define public constants.
  • Multiple Interfaces: Unlike class inheritance where you can only extend one parent, a class can implement multiple interfaces by listing them, separated by commas.
    class SuperLogger implements Loggable, Formattable {
        // Must implement all methods from BOTH Loggable and Formattable
    }
    
  • Interface Inheritance: An interface can extend one or more other interfaces.
    interface PremiumLoggable extends Loggable {
        public function logWithPriority(string $message, int $priority): void;
    }
    
    A class implementing PremiumLoggable would need to define both log() and logWithPriority().

5. Interfaces vs. Abstract Classes (A Preview)

If you're thinking that an interface sounds similar to an abstract class (which we'll cover next), you're not alone. They both enforce a contract. However, they have key differences and are used for different purposes.

  • An interface is a pure contract; it contains no implementation at all. A class implements an interface to promise it can do something.
  • An abstract class is a mix; it can contain both implemented methods and abstract (unimplemented) methods. A class extends an abstract class because it is a specialized version of that class, inheriting shared code.

The video you've been watching has a great segment comparing the two.

PHP Interfaces & Polymorphism - Interfaces Explained - Full PHP 8 Tutorial

Let's briefly compare interfaces to abstract classes to clarify their distinct roles before we dive deep into abstract classes in the next lesson.

Watch from 12:52 to 15:00. The video explains the key differences and, importantly, shows how an abstract class can itself implement an interface, combining the benefits of both approaches.

This comparison chart provides a useful at-a-glance reference for the differences. We will revisit this in the next lesson.

Interface Vs Abstract Class (PHP) Comparison
This chart summarizes 14 key differences between Interfaces and Abstract Classes in PHP. While an interface is a pure contract (`implements`), an abstract class can provide partial implementation (`extends`).

6. Hands-on Practice

Let's put this into practice with a scenario relevant to web development: a system that can export data in different formats.

  1. Define the Interface:

    • Create an interface named Exportable.
    • It should define one public method called export() that takes no arguments and is declared to return a string.
  2. Create Concrete Implementations:

    • Create a class named JsonExporter that implements the Exportable interface. Its export() method should take an array of data (passed in the constructor) and return a JSON-encoded string.
    • Create a class named CsvExporter that also implements the Exportable interface. Its export() method should take an array of data (passed in the constructor) and return a simple CSV-formatted string. For simplicity, you can just implode() the values on each line.
  3. Create a Consumer Class:

    • Create a class named DataExporter.
    • Give it a public method called performExport(). This method should accept one argument type-hinted with the Exportable interface (e.g., public function performExport(Exportable $exporter)).
    • Inside this method, call the export() method on the provided $exporter object and echo the result.
  4. Instantiate and Test:

    • Create some sample data, like an array of user arrays.
    • Create an instance of DataExporter.
    • Call performExport(), passing it a new JsonExporter object.
    • Call performExport() again, this time passing it a new CsvExporter object.
    • Observe how the DataExporter works with both objects without any changes.
Click to see a possible solution
<?php

// 1. The Contract
interface Exportable
{
    public function export(): string;
}

// 2. Concrete Implementations
class JsonExporter implements Exportable
{
    private array $data;

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function export(): string
    {
        return json_encode($this->data);
    }
}

class CsvExporter implements Exportable
{
    private array $data;

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function export(): string
    {
        $csv = '';
        // Add header
        if (!empty($this->data)) {
            $csv .= implode(',', array_keys($this->data[0])) . PHP_EOL;
        }
        // Add rows
        foreach ($this->data as $row) {
            $csv .= implode(',', $row) . PHP_EOL;
        }
        return $csv;
    }
}

// 3. The Consumer Class
class DataExporter
{
    // This method accepts ANY object that fulfills the Exportable contract
    public function performExport(Exportable $exporter)
    {
        echo "--- Exporting Data ---\n";
        echo $exporter->export();
        echo "\n----------------------\n\n";
    }
}

// 4. Instantiate and Test
$users = [
    ['id' => 1, 'name' => 'Alice', 'email' => 'alice@example.com'],
    ['id' => 2, 'name' => 'Bob', 'email' => 'bob@example.com'],
];

$exporter = new DataExporter();

// Use the JSON exporter
$jsonExporter = new JsonExporter($users);
$exporter->performExport($jsonExporter);

// Use the CSV exporter
$csvExporter = new CsvExporter($users);
$exporter->performExport($csvExporter);

?>

Expected Output:

--- Exporting Data ---
[{"id":1,"name":"Alice","email":"alice@example.com"},{"id":2,"name":"Bob","email":"bob@example.com"}]
----------------------

--- Exporting Data ---
id,name,email
1,Alice,alice@example.com
2,Bob,bob@example.com

----------------------

Conclusion

Great work today! You have learned one of the most powerful concepts in object-oriented programming. By defining and implementing interfaces, you can create robust and flexible systems where components can be swapped easily, making your code easier to maintain, test, and extend.

Key Takeaways:

  • An interface is a contract that defines what methods a class must implement, but not how.
  • A class uses the implements keyword to agree to an interface's contract.
  • The real power comes from using an interface as a type declaration, which enables polymorphism and creates loosely-coupled code.
  • A class can implement multiple interfaces, allowing it to fulfill multiple contracts.
  • Interfaces are different from abstract classes: interfaces are pure contracts, while abstract classes can provide shared code.

Preview of the Next Lesson:
We've had a glimpse of abstract classes. In our next lesson, we will dive deep into them. You'll learn how to create abstract classes and methods to build base classes that provide some shared implementation while forcing child classes to provide the rest. This will complete our trio of advanced OOP relationship tools: extends, implements, and abstract.