Відображення в PHP
Російська (Українська) translation by Sergey Zhuk
Відображенням зазвичай називають здатність програми перевірити саму себе та змінювати свою логіку під час виконання. Якщо говорити не технічними термінами, то у відображенні ми просимо об'єкт розповісти нам про свої властивості та методи, а також маємо можливість їх змінювати (включаючи приватні). У цьому уроці ми глибше вивчимо, як це відбувається, і коли цим користуватися.
Невелика передісторія
Як і у випадку з будь-якою крутою іграшкою, можна використовувати відображення, але не зловживати ним.
З появою мов високого рівня (наприклад С), такі відображення стали зникати. Потім вони знову з'явилися на об'єктно-орієнтованих мовах.
Сьогодні більшість мов програмування використовують можливості відображення. Мови зі статичною типізацією, такі як Java, майже не мають проблем із відображенням. Однак найцікавіше те, що динамічно типізовані мови (наприклад, PHP або Ruby) сильно зав'язані на ньому. Без використання концепції відображення неможливо було б реалізувати (качину типізацію) duck-typing. Коли ми передаємо один об'єкт іншому (як параметр наприклад), одержуючий об'єкт поняття не має про структуру та тип цього об'єкта. Все що він може - це використати відображення, щоб визначити методи, які можна чи не можна викликати в отриманого об'єкта.
Простий приклад
Відображення у PHP. Насправді може бути кілька ситуацій, коли ми можемо використовувати відображення, навіть не підозрюючи про це. Наприклад:
Потім змінимо Nettuts наступним чином:
Тепер Nettuts не має абсолютно жодного відношення до класу Editor. Він не включає сам файл, не ініціалізує цей клас, і навіть незнає про його існування. Я можу передати об'єкт будь-якого типу метод publishNextArticle() і код буде працювати.

Як ви можете бачити на цій діаграмі класів, клас Nettuts має лише один прямий зв'язок у Manager . Manager створює його, і навіть Manager залежить від Nettuts . Але Nettuts більше не має жодних зв'язків з класом Editor, і Editor пов'язаний тільки з Manager.
Під час виконання Nettuts буде використовувати об'єкт Editor, проте слово > тут у нас під питанням. Під час виконання PHP переглядає отриманий об'єкт і перевіряє, що він реалізує методи setNextArticle() та publish().
Інформація про об'єкт
PHP може відобразити всі деталі об'єкта. Створимо PHPUnit тест для легкої та швидкої перевірки нашого коду:
Тепер додамо var_dump( ) до Nettuts :
Запусти тест і побачимо магію, яка відбувається у висновку:
Наш клас відображення має властивість name, встановлену у вихідний тип змінної $editor : Editor , але цієї інформації недостатньо. А що на рахунок методом класу Editor?
У цьому коді ми привласнюємо об'єкт класу відображення змінної $reflector , щоб можна було викликати його методи. ReflectionClass надає величезний набір методів, які можна використовувати для отримання інформації про об'єкт. Один з них - getMethods(), він повертає масив з інформацією про кожен метод об'єкта.
Інший метод getProperties() , повертає властивості об'єкта (включно з приватними):
Елементи масивів, які були отримані за допомогою getMethods() та getProperties() є типу даних ReflectionMethod та ReflectionProperty відповідно; ці об'єкти можуть бути досить корисними:
Це виявилося досить просто для нас, тому що у нас вже був Editorдля передачі в invoke() . У деяких ситуаціях ми можемо мати кілька об'єктів Editor , щоб мати можливість вибрати, який об'єкт ми хочемо використовувати. В іншій ситуації у нас навпаки може взагалі не бути об'єкта, з яким ми могли б працювати, у такому разі ми можемо отримати його за допомогою ReflectionClass.
Змінимо метод publish() у класі Editor, щоб продемонструвати подвійний виклик:
І ось наш новий висновок:
Управління даними об'єкта
Новий метод назвемо getEditorName() і він просто повертатиме значення приватної властивості $name . Властивість $name встановлюється під час створення об'єкта, і немає жодних публічних сеттерів, щоб змінити його. Але ми можемо отримати доступ до цієї змінної через відображення. Спочатку ви можете спробувати очевидніший підхід:
Незважаючи на те, що це виводить значення var_dump() , він видає помилку при спробі отримати значення за допомогою відображення:
Щоб виправити проблему, нам потрібно отримати доступ до приватних властивостей та методів об'єкта за допомогою ReflectionProperty.
Виклик setAccessible з параметром true зроблять це:
От і все. Цей код змінює "John Doe" на "Mark Twain".
Непряме використання відображення
Деякі вбудовані PHP функції неявно використовують відображення - одна з них call_user_func() .
Зворотній виклик
Функція call_user_func() може приймати масив, в якому перший елемент вказує на об'єкт, а другий на ім'я методу. Можна передати додатковий параметр, який потім буде переданий у метод, що викликається. Наприклад:
Цей висновок демонструє, що код набуває наступного значення:
Використання значення змінної
Інший приклад неявного використання відображення – виклик методу за допомогою значенняіншою змінною, замість прямого виклику цього методу. Розглянемо приклад:
Цей код виводить те саме, що й у попередньому прикладі. PHP просто підставляє рядок, який міститься в змінній та викликає метод. Це також працює, коли ви хочете створити об'єкти, використовуючи змінні як ім'я класу.
Коли потрібно використовувати відображення?
Тепер, коли ми залишили всі технічні деталі позаду, можна запитати, коли варто використовувати відображення? Ось кілька сценаріїв:
- Динамічна типізація практично неможлива без відображення.
- Аспектно-орієнтоване програмування прослуховує виклики методів і має код, навколо цих методів з використанням відображення.
- PHPUnit багато в чому покладається на відображення, як і інші тестові фреймворки.
- Web-фреймвкорки здебільшого часто використовують відображення для різних потреб. Деякі використовують для ініціалізації моделей, створення об'єктів для файлів відображення та інше. Laravel використовує відображення для реалізації ін'єкції залежностей.
- Metaprogramming, як наш останній приклад, теж є неявним відображенням.
- Фреймворки для аналізу коду використовують відображення, щоб зрозуміти ваш код.
Висновок
Як і з будь-якою іншою крутою іграшкою, використовуйте відображення, але не перестарайтеся. Відображення є цінним, коли вам потрібно перевірити багато об'єктів, але має всі можливості ускладнити архітектуру та дизайн вашого проекту. Я б рекомендував використовувати його тільки коли воно дійсно необхідне і дає перевагу або коли ніякого іншого вибору вже немає.
Особисто я використовував відображення лише в кількох сценаріях,здебільшого при використанні сторонніх модулів зі слабкою документацією. Я часто помічаю використання коду, як у останньому прикладі. Легко викликати потрібний метод, коли ваш MVC відповідає зі змінною, яка містить значення "add" або "remove".