Какова роль интерфейсов в ООП?
Приведу пример на коленке. Хотим, например, написать абстрактную файловую систему. Для начала, определим интерфейс, для ФС:interface FileSystemInterface {
  public function write($file, $data);
  public function read($file);
}
Затем, хочу реализацию интерфейса ФС для работы с файликами:class OSFileSystem implements FileSystemInterface {
  public function write($file, $data) {
     // открываем файлик, пишем данные
  }

  public function read($file) {
    // открываем файлик, возвращаем данные
  }
}
Вдруг, кому-то захотелось файловую систему в облаке. Окей, не проблема, реализуем это:class CloudFileSystem implements FileSystemInterface {
  public function write($file, $data) {
     // открываем соединение с облаком, пишем данные
  }

  public function read($file) {
    // открываем соединение с облаком, возвращаем данные
  }
}
Пусть у нас есть кой-то код, работающий с файловой системой, назовем его "Хранилище файлов". Пусть он выглядит примерно так:class FileStorage {
  protected $Fs;
  
  public function __construct(FileSystemInterface $Fs) {
    $this->Fs = $Fs;
  }  

  public function saveFile() {
    $this->Fs->write('file.txt', 'file data');
  }

  public function getFile() {
    return $this->Fs->read('file.txt', 'file data');
  }
}
Отлично! Теперь мы можем хранилищу файлов отдать любой объект с реализованным интерфейсом FileSystemInterface. Пример:// Хранилище файлов работает с файловой системой ОС:
$FS = new OSFileSystem();
$FileStorage = new FileStorage($Fs);
$FileStorage->getFile();

// Хранилище файлов работает с файловой системой в облаке:
$FS = new CloudFileSystem();
$FileStorage = new FileStorage($Fs);
$FileStorage->getFile();
Использование интерфейса, в данном случае. позволяет нам писать только реализацию работы файловой системы, а бизнес-логика, работающая с файловой системой никак не меняется, она знает, что в любом случае файловая система реализует интерфейс FileSystemInterface и может без опаски использовать методы этого интерфейса.
Во фреймворках, больших проектах интерфейсы описывают набор обязательных методов, которые в дальнейшем в коде стопроцентно используются. Таким образом, если разработчик Вася захочет написать какой-то свой плагин, например, для работы с юзерами, то он будет точно знать, какие методы он должен в своем классе реализовать. Если же не будет интерфейса, то Вася не будет знать, какие именно методы нужно реализовать, забудет про один маленький метод, а на продакшне потом выскочит исключение.
Абстрактные методы в абстрактном классе играют ту же роль, что и интерфейс - описывают набор обязательных методов. Но при этом в абстрактном классе можно реализовать некоторые методы, которые в скорее всего будут одинаковы для многих потомков (хотя бы для 2х). Таким образом избегаем копипаста в коде