# Exploring Inheritance in PHP

Hello!

Now you know the fundamentals of classes in PHP, it's time to move on to inheritance.

In case you missed the previous posts, you can [follow the series here](https://mastering-php.com/).

## What is Inheritance?

The concept of inheritance allows us to create a class that derives from another class. This new class is called a subclass and extends an existing class. For example, we may have a user class and want to add admin users to our app. Instead of creating a whole new AdminUser class with mostly the same functionality, we can instead use inheritance and create a subclass of the User class.

This new subclass inherits all functionality from the User class, but we can override and extend with functionality specific to admin users.

## Creating the Superclass

A superclass is a class that is inherited by subclasses. A superclass can be any class, so let's create one:

```php
class User {
    public function __construct(public string $name) {
        //
    }

    public function getDisplayName(): string {
        return $this->name;
    }
}

// Usage
$user = new User("John");
echo $user->getDisplayName(); // "John"
```

It's a simple user class where each user has a name and a display name that is displayed on the website.

If we want to introduce different types of users, we can create subclasses that inherit, or extend, the user class. We can do so using the `extend` keyword in the class definition:

```php
class VerifiedUser extends User {
    
}

// Usage
$verified_user = new VerifiedUser("Jane");
echo $verified_user->getDisplayName(); // "Jane"
```

As you can see, we haven't added anything to the class yet, but the constructor and `getDisplayName` methods still work! This is because our class inherits the functionality from the superclass, which in this example is the User class.

## Overriding Methods

We now effectively have two identical classes, which isn't very helpful. However, our verified users should have a cool verified badge next to their name!

To implement this, we can override the existing getDisplayName method (that is currently inherited from the User class) with new functionality:

```php
class VerifiedUser extends User {
    public function getDisplayName(): string {
        return $this->name . " ✔";
    }
}

// Usage
$verified_user = new VerifiedUser("Jane");
echo $verified_user->getDisplayName(); // "Jane ✔"
```

### The Override Attribute

Since PHP 8.3, we can explicitly tell PHP that we want to override an existing method using the `#[\Override]` attribute:

```php
class VerifiedUser extends User {
    #[\Override]
    public function getDisplayName(): string {
        return $this->name . " ✔";
    }
}
```

In case the function we want to override doesn't exist on the superclass, this will now throw an error:

```php
class VerifiedUser extends User {
    #[\Override]
    public function getName(): string {
        return $this->name . " ✔";
    }
}

// Fatal error: VerifiedUser::getName() has #[\Override] attribute,
// but no matching parent method exists
```

## Adding Methods

Besides overriding existing methods, we can add new methods and properties to our new subclass:

```php
class VerifiedUser extends User {
    public function getDisplayName(): string {
        return $this->name . " ✔";
    }

    public function isVerifiedSince(): DateTime {
        return new DateTime("1 month ago");
    }
}

// Usage
$verified_user = new VerifiedUser("Jane");
echo $verified_user->isVerifiedSince()->format("Y-m-d"); // "2024-04-19"
```

## Composition

Now you might ask, why not simply create a new class? Inheritance is cool, but if my class doesn't have that much functionality, can't I simply create a new class from scratch?

And of course, you could, but then you're missing out on a great feature:

Everywhere you can use the User class, you can now also use the VerifiedUser class, since it is a subclass.

This means, let's say we have a function that accepts a User instance:

```php
function sayHelloTo(User $user) {
    echo "Hello " . $user->getDisplayName();
}
```

This is not limited to the User class, but also accepts subclasses:

```php
$user = new User("John");
sayHelloTo($user); // "Hello John"

$verified_user = new VerifiedUser("Jane");
sayHelloTo($verified_user); // "Hello Jane ✔"
```

And we didn't have to change the code of the function, it just works!

However, this does not work in the other direction. If we have a function that accepts a VerifiedUser, we can't pass it an instance of the User class:

```php
function wasVerifiedThisYear(VerifiedUser $user) {
    return $user->isVerifiedSince()->format("Y") === date("Y");
}

$user = new User("John");
var_dump( wasVerifiedThisYear($user) );
// Fatal error: Uncaught TypeError: wasVerifiedThisYear():
// Argument #1 ($user) must be of type VerifiedUser, User given

$verified_user = new VerifiedUser("Jane");
var_dump( wasVerifiedThisYear($verified_user ) );
// bool(true)
```

## Instanceof Operator

The `instanceof` operator can be used to check if a given object is an instance of a specific class:

```php
$user = new User("John");

var_dump( $user instanceof User ); // bool(true)
var_dump( $user instanceof VerifiedUser ); // bool(false)
```

Once we know that an object is an instance of a certain class, we can safely call methods specific to this class:

```php
function getVerifiedInfo(User $user): string {
    if(! $user instanceof VerifiedUser) {
        return $user->getDisplayName() . " is not verified";
    }

    return $user->getDisplayName() . " is verified since " . $user->isVerifiedSince()->format("Y-m-d");
}

$user = new User("John");
echo getVerifiedInfo($user); // "John is not verified"

$verified_user = new VerifiedUser("Jane");
echo getVerifiedInfo($verified_user ); // "Jane ✔ is verified since 2024-04-19"
```

## Calling Methods from the Superclass

Let’s go back to our User example from the beginning:

```php
class User {
    public function __construct(public string $name) {
        //
    }

    public function getDisplayName(): string {
        return $this->name;
    }
}

class VerifiedUser extends User {
    public function getDisplayName(): string {
        return $this->name . " ✔";
    }
}

// Usage
$user = new User("John");
echo $user->getDisplayName(); // "John"

$verified_user = new VerifiedUser("Jane");
echo $verified_user->getDisplayName(); // "Jane ✔"
```

You can see, the `VerifiedUser` class overrides the `getDisplayName` method completely. But in this example, we only want to append a little checkmark symbol to the name.

Instead of copying the logic to get the display name from the superclass `User`, we can call the method we are overriding by prefixing it with `parent::`, like this:

```php
 class VerifiedUser extends User {
    public function getDisplayName(): string {
    	return parent::getDisplayName() . " ✔";
    }
}
```

The output is the same as before, but now, if we change how the default display name is generated in the superclass, these changes will also be reflected in the `VerfiedUser` class:

```php
class User {
    public function __construct(public string $name) {
        //
    }

    public function getDisplayName(): string {
        # Let's say we change how the name is displayed:
        return "@" . strtolower($this->name);
    }
}

class VerifiedUser extends User {
    public function getDisplayName(): string {
        # We're not changing anything here!
        return parent::getDisplayName() . " ✔";
    }
}

// Usage
$user = new User("John");
echo $user->getDisplayName(); // "@john"

$verified_user = new VerifiedUser("Jane");
# Notice how the new format is reflected here, even though
# we haven't touched the VerifiedUser class. And the
# checkmark is still added!
echo $verified_user->getDisplayName(); // "@jane ✔"
```

## Next Steps

Extending and overriding methods of existing classes is a very powerful feature. But it’s not only helpful when working with your own classes, it can also come in very handy when you need to change the functionality of a third-party library.

Whenever you’re ready, head over to the next part of this series.
