HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>До сих пор мы работали с так называемой утиной типизацией: Если что-то ходит как утка и крякает как утка, то это и есть утка. Она проявляется в том, что в сигнатуре функции не указывается ожидаемый класс или интерфейс объекта. Мы просто ждём, что вызывающий код отдаст объект, имеющий необходимые методы:</p>
1 <p>До сих пор мы работали с так называемой утиной типизацией: Если что-то ходит как утка и крякает как утка, то это и есть утка. Она проявляется в том, что в сигнатуре функции не указывается ожидаемый класс или интерфейс объекта. Мы просто ждём, что вызывающий код отдаст объект, имеющий необходимые методы:</p>
2 <p>Утиная типизация автоматически вытекает из динамической типизации. В большинстве динамических языков понятия "интерфейс" даже не существует. PHP в этом смысле пошёл своим путём. Несмотря на его динамическую природу, классовая модель PHP взята из Java. И в PHP присутствуют почти все соответствующие атрибуты: интерфейсы, абстрактные классы и многое другое. Поэтому в PHP принято указывать интерфейсы ожидаемых объектов в сигнатурах функций.</p>
2 <p>Утиная типизация автоматически вытекает из динамической типизации. В большинстве динамических языков понятия "интерфейс" даже не существует. PHP в этом смысле пошёл своим путём. Несмотря на его динамическую природу, классовая модель PHP взята из Java. И в PHP присутствуют почти все соответствующие атрибуты: интерфейсы, абстрактные классы и многое другое. Поэтому в PHP принято указывать интерфейсы ожидаемых объектов в сигнатурах функций.</p>
3 <p>Теперь эта функция работает только с объектами тех классов, которые реализуют указанный интерфейс.</p>
3 <p>Теперь эта функция работает только с объектами тех классов, которые реализуют указанный интерфейс.</p>
4 <p>Как вы помните, в сигнатуре можно указывать и класс, и интерфейс. Что правильнее? Вооружившись принципом инверсии зависимостей, мы можем однозначно сказать, что правильнее использовать интерфейсы и завязываться на абстракцию вместо конкретной реализации. Только в этом случае появится возможность подменить реализацию. Ниже пример реального кода из фреймворка Symfony2, в котором активно используется полиморфизм подтипов с инъекцией через конструктор:</p>
4 <p>Как вы помните, в сигнатуре можно указывать и класс, и интерфейс. Что правильнее? Вооружившись принципом инверсии зависимостей, мы можем однозначно сказать, что правильнее использовать интерфейсы и завязываться на абстракцию вместо конкретной реализации. Только в этом случае появится возможность подменить реализацию. Ниже пример реального кода из фреймворка Symfony2, в котором активно используется полиморфизм подтипов с инъекцией через конструктор:</p>
5 <p><em>В очередной раз подчеркну что не нужно возводить эту технику в абсолют. Если создавать интерфейсы на всё подряд, то код очень быстро распухнет и станет сложным. Вспомните, в ruby/python/js вообще нет интерфейсов и никто не умер. Попробуйте побродить по исходникам популярного микрофреймворка<a>Lumen</a>. Внутри него крайне тяжело найти места, где в сигнатурах методов встречаются явные указания интерфейсов</em></p>
5 <p><em>В очередной раз подчеркну что не нужно возводить эту технику в абсолют. Если создавать интерфейсы на всё подряд, то код очень быстро распухнет и станет сложным. Вспомните, в ruby/python/js вообще нет интерфейсов и никто не умер. Попробуйте побродить по исходникам популярного микрофреймворка<a>Lumen</a>. Внутри него крайне тяжело найти места, где в сигнатурах методов встречаются явные указания интерфейсов</em></p>
6 <p>Для лучшей переносимости кода, в PHP вводятся стандартные интерфейсы для самых частых задач. Они описываются в<a>PSR</a>. В примере выше используется стандартный интерфейс Psr\Log\LoggerInterface. Со временем, всё большая часть библиотек начнёт его реализовывать, а это позволит безболезненно подменять логгер без необходимости менять свой код. Кроме логгера в PSR описаны множество других интерфейсов, среди них HTTP-клиент, HTTP-запрос, HTTP-ответ, кеш и другое.</p>
6 <p>Для лучшей переносимости кода, в PHP вводятся стандартные интерфейсы для самых частых задач. Они описываются в<a>PSR</a>. В примере выше используется стандартный интерфейс Psr\Log\LoggerInterface. Со временем, всё большая часть библиотек начнёт его реализовывать, а это позволит безболезненно подменять логгер без необходимости менять свой код. Кроме логгера в PSR описаны множество других интерфейсов, среди них HTTP-клиент, HTTP-запрос, HTTP-ответ, кеш и другое.</p>
7 <p>Теперь мы можем ответить на вопрос, почему этот полиморфизм называется полиморфизмом подтипов. Типом в ООП языках принято называть интерфейсы (хотя это<a>не совсем правда</a>) и отношение подтипов определяется отношением интерфейсов.</p>
7 <p>Теперь мы можем ответить на вопрос, почему этот полиморфизм называется полиморфизмом подтипов. Типом в ООП языках принято называть интерфейсы (хотя это<a>не совсем правда</a>) и отношение подтипов определяется отношением интерфейсов.</p>
8 <p>Интерфейсы можно расширять другими интерфейсами, для этого используется ключевое слово extends. Пример из библиотеки DS (той, где описаны структуры данных на PHP в объектном синтаксисе):</p>
8 <p>Интерфейсы можно расширять другими интерфейсами, для этого используется ключевое слово extends. Пример из библиотеки DS (той, где описаны структуры данных на PHP в объектном синтаксисе):</p>
9 <p>Теперь любой класс, реализующий интерфейс<em>Collection</em>, будет обязан реализовать все интерфейсы, которые расширяет<em>Collection</em>. Это касается каждого интерфейса в цепочке.</p>
9 <p>Теперь любой класс, реализующий интерфейс<em>Collection</em>, будет обязан реализовать все интерфейсы, которые расширяет<em>Collection</em>. Это касается каждого интерфейса в цепочке.</p>
10 <h2>Статические методы</h2>
10 <h2>Статические методы</h2>
11 <p>Статические методы жёстко завязаны на имя класса, поэтому полиморфизм подтипов в них невозможен. Но можно схитрить и использовать вместо класса переменную с его именем:</p>
11 <p>Статические методы жёстко завязаны на имя класса, поэтому полиморфизм подтипов в них невозможен. Но можно схитрить и использовать вместо класса переменную с его именем:</p>
12  
12