Может ли нам понадобится когда-нибудь создавать объекты класса HTMLElement? Наверняка нет. Вся работа строится на базе конкретных элементов, а значит, на практике, всегда используются его наследники.

В PHP, классы, объекты которых не имеют смысла, принято помечать специальным ключевым словом abstract. Тогда никто не сможет создать объект этого класса напрямую. Только через наследников. В остальном, абстрактный класс такой же класс, со своими методами и свойствами.

<?php

abstract class HTMLElement
{
    public $attributes = [];

    public function __construct($attributes = [])
    {
        $this->attributes = $attributes;
    }

    public function getAttribute(string $key)
    {
        return $this->attributes[$key];
    }
}

Абстрактные классы имеют смысл только в связке с наследованием. В большинстве ситуаций, базовый класс не нуждается в объектах и его логично пометить как абстрактный.

Еще одна особенность абстрактных классов, связана с использованием интерфейсов. Абстрактный класс, в отличие от конкретного, не обязан реализовывать интерфейсы полностью. Все что не реализовывает абстрактный класс, должно быть реализовано в его наследнике. Например в нашей ситуации, можно добавить интерфейс Showable в абстрактный класс, но реализовывать его. В таком случае PHP сам проверит что все наследники HTMLElement реализуют метод __toString().

<?php

abstract class HTMLElement implements Showable // __toString
{
    // ...
}

Абстрактные классы могут иметь абстрактные методы, фактически это сигнатуры методов, а не сами методы. В этом смысле они становятся похожи на интерфейсы.

<?php

abstract class HTMLElement
{
    abstract public function __toString();
}

С появлением абстрактных классов возникает множество новых вопросов: когда они нужны а когда нет, можно ли использовать абстрактные классы вместо интерфейсов, как совмещать их с интерфейсами и так далее?

Абстрактные классы не фундаментальная концепция и не обязательная часть ООП. Более того, в большинстве ООП языках абстрактных классов нет. Однако нельзя сказать что код на них пишется хуже, скорее наоборот. Например в Ruby, JavaScript, Python их нет, но есть наследование и есть классы, которые можно было отметить как абстрактные, однако создатели этих языков не стали вводить это понятие в язык. Никогда нельзя забывать, что любые новые сущности, упрощая в одном месте, увеличивают общую сложность из-за возрастающего числа комбинаций этих сущностей друг с другом.

Общая рекомендация, не придавайте слишком много значения абстрактным классам. Используйте их тогда, когда класс имеет смысл пометить абстрактным, но не более того. Как вы увидите позже, абстрактные классы, вместе с наследованием, успешно заменяются трейтами. Причем последние гораздо более гибкий инструмент.

Для продолжения нужно перейти в курс и вступить в него.