Liskov Substitution Principle, LSP - Принцип подстановки Барбары Лисков

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

class Bird {
    public function fly() {
        $flySpeed = 10;
        return $flySpeed;
    }
}
 
/**
* Дочерний класс от Bird. 
* Не изменяет поведение, но дополняет.
* Не нарушает принцип LSP
*/
class Duck extends Bird {
 
    public function fly() {
        $flySpeed = 8;
        return $flySpeed;
    }
     
    public function swim() {
        $swimSpeed = 2;
        return $swimSpeed;
    }
}
 
/**
* Дочерний класс от Bird. 
* Изменяет поведение.
* Нарушает принцип LSP
*/
class Penguin extends Bird {
 
    public function fly() {
          //die('i can`t fly (((');  // не типичное поведение - die или exception
          return 'i can`t fly ((('; // не типичное поведение - возвращаем string, а не integer
    }
 
    public function swim() {
        $swimSpeed = 4;
        return $swimSpeed;
    }
}

Клас BirdRun - работает с классами Bird

class BirdRun {
 
    private $bird; 
    public function __construct(Bird $bird) {
        $this->bird = $bird;
    }
 
    public function run() {
        $flySpeed = $this->bird->fly();
    }
}

Использование:

$bird = new Bird();
//$bird = new Duck();
//$bird = new Penguin();
$birdRun = new BirdRun($bird);
$birdRun->run();

После замены использования Bird на Duck код будет работать как и прежде - принцип LSP соблюден.
После замены Bird на Penguin код меняет свое поведение, следовательно в данном случае принцип LSP нарушен.