Где лучше объявлять код методов — в интерфейсе или в классе php?
В PHP есть несколько вариантов организации кода, и выбор между интерфейсом и классом зависит от того, что именно вы объявляете.
Ключевое различие
Интерфейс — это только контракт: сигнатуры методов без реализации (в классическом понимании).
Класс — это реализация: содержит тела методов.
1 2 3 4 5 6 7 8 9 10 | interface LoggerInterface { public function log(string $message): void; // только сигнатура } class FileLogger implements LoggerInterface { public function log(string $message): void // реализация { file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND); } } |
Что где объявлять
В интерфейсе объявляют:
Публичные сигнатуры методов — имя, параметры, тип возврата.
Константы (начиная с PHP 8.1 — и
final-константы).Начиная с PHP 8.0 — можно объявлять
public,static, а также использоватьprivate/protectedметоды (для внутреннего использования в интерфейсе).
1 2 3 4 | interface PaymentGateway { public function charge(float $amount): bool; public function refund(string $transactionId): bool; } |
В классе объявляют:
Тела (реализацию) методов.
Приватные и защищённые методы — вспомогательная логика, которую не нужно раскрывать наружу.
Свойства (в интерфейсах свойств нет).
Методы, не входящие в контракт.
1 2 3 4 5 6 7 8 9 10 | class StripeGateway implements PaymentGateway { private string $apiKey; public function __construct(string $apiKey) { $this->apiKey = $apiKey; } public function charge(float $amount): bool { /* ... */ } public function refund(string $transactionId): bool { /* ... */ } private function signRequest(array $data): string { /* внутренняя логика */ } } |
Правила выбора
| Ситуация | Где объявлять |
|---|---|
| Нужен общий контракт для нескольких классов | Интерфейс |
| Есть общая реализация для нескольких классов | Абстрактный класс |
| Логика специфична для одного класса | Обычный класс |
| Метод — деталь реализации | Класс (private/protected) |
| Метод — часть публичного API | Интерфейс + реализация в классе |
| Нужна гибкость (несколько реализаций, DI, тесты) | Интерфейс |
Почему не «всё в интерфейс»
Интерфейс — публичный контракт. Если положить туда всё, вы свяжете себя обязательством поддерживать эти методы во всех реализациях.
Нет свойств и состояния. Всё, что связано с данными объекта, живёт в классе.
Нарушение ISP (Interface Segregation Principle). Лучше несколько маленьких интерфейсов, чем один огромный.
Тестирование. Узкий интерфейс проще мокать.
Практический паттерн
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // Контракт interface UserRepositoryInterface { public function findById(int $id): ?User; public function save(User $user): void; } // Реализация class MySqlUserRepository implements UserRepositoryInterface { public function findById(int $id): ?User { /* SQL */ } public function save(User $user): void { /* SQL */ } private function hydrate(array $row): User { /* деталь реализации */ } } // Использование зависит от абстракции, а не от конкретики class UserService { public function __construct(private UserRepositoryInterface $repo) {} } |
Итог
Интерфейс = «что умеет объект» (контракт, публичный API).
Класс = «как именно он это делает» (реализация + приватные детали).
Объявляйте метод в интерфейсе, только если он является частью контракта, который должны соблюдать другие классы. Всё остальное — в классе.
Об авторе
Антон Банников
Дипломированный php-программист, linux и open source-энтузиаст. Разработчик запатентованной программы расчета физико-химических свойств солей. Опыт работы с языками программирования ~25 лет, опыт работы с сайтами ~ 21 год, опыт программирования на языке php ~17 лет. Опыт программирования на языке JS ~14 лет. Разработчик антивирусного сканера (php/jquery) и участник проекта быстрого интернет-магазина "F-Shop" (php/jquery).
Рекомендуемые статьи
Как скачать и установить дистрибутив Drupal 8?

Несомненные плюсы использования CMS Drupal
