SmartObject
SmartObject протягом багатьох років покращував поведінку об'єктів у PHP. Починаючи з версії PHP 8.4, усі його функції стали вбудованими частинами самого PHP, чим завершив свою історичну місію бути піонером сучасного об'єктно-орієнтованого підходу в PHP.
Встановлення:
SmartObject з'явився у 2007 році як революційне рішення недоліків тодішньої об'єктної моделі PHP. У час, коли PHP страждав від численних проблем з об'єктно-орієнтованим дизайном, він приніс значні покращення та спростив роботу розробників. Він став легендарною частиною фреймворку Nette. Він пропонував функціональність, яку PHP отримав лише через багато років – від валідації доступу до властивостей об'єктів до складної обробки помилок. З появою PHP 8.4 він завершив свою історичну місію, оскільки всі його функції стали вбудованими частинами мови. Він випередив розвиток PHP на вражаючих 17 років.
SmartObject пройшов цікавий технічний розвиток. Спочатку він був
реалізований як клас Nette\Object
, від якого інші класи успадковували
потрібну функціональність. Значна зміна прийшла з PHP 5.4, який
представив підтримку trait. Це дозволило трансформацію у trait
Nette\SmartObject
, що принесло більшу гнучкість – розробники могли
використовувати функціональність навіть у класах, які вже
успадковувалися від іншого класу. У той час як оригінальний клас
Nette\Object
припинив існування з появою PHP 7.2 (який заборонив
називати класи словом ‚Object‘), trait Nette\SmartObject
продовжує жити.
Розглянемо функції, які колись пропонували Nette\Object
і пізніше
Nette\SmartObject
. Кожна з цих функцій у свій час представляла значний
крок вперед у області об'єктно-орієнтованого програмування в PHP.
Послідовна обробка помилок
Однією з найгостріших проблем раннього PHP була непослідовна
поведінка при роботі з об'єктами. Nette\Object
вніс порядок і
передбачуваність у цей хаос. Подивімося, як поводилось PHP спочатку:
Fatal error завершував роботу програми без можливості якось відреагувати.
Тихий запис у неіснуючі члени без попередження міг призвести до
серйозних помилок, які було важко виявити. Nette\Object
перехоплював
усі ці випадки і викидав виняток MemberAccessException
, що дозволяло
програмістам реагувати на помилки та обробляти їх:
Починаючи з PHP 7.0, мова більше не викликає неперехоплювані fatal error, а з PHP 8.2 доступ до неоголошених членів вважається помилкою.
Підказка „Did you mean?“
Nette\Object
з'явився з дуже зручною функцією: інтелектуальними
підказками при опечатках. Коли розробник робив помилку в назві методу
чи змінної, він не лише повідомляв про помилку, але й пропонував
допомогу у вигляді підказки правильної назви. Це іконічне
повідомлення, відоме як „did you mean?“, зекономило програмістам години
пошуку опечаток:
Сучасний PHP хоч і не має жодної форми „did you mean?“, але цю функцію тепер забезпечує Tracy. Він навіть може автоматично виправляти такі помилки.
Properties з контрольованим доступом
Значною інновацією, яку SmartObject приніс у PHP, були properties з контрольованим доступом. Ця концепція, поширена в таких мовах як C# чи Python, дозволила розробникам елегантно контролювати доступ до даних об'єкта та забезпечувати їх консистентність. Properties є потужним інструментом об'єктно-орієнтованого програмування. Вони функціонують як змінні, але насправді представлені методами (getters і setters). Це дозволяє валідувати вхідні дані або генерувати значення в момент читання.
Для використання properties потрібно:
- Додати анотацію класу у форматі
@property <type> $xyz
- Створити getter з назвою
getXyz()
абоisXyz()
, setter з назвоюsetXyz()
- Забезпечити, щоб getter і setter були public або protected. Вони опціональні – тобто можуть існувати як read-only або write-only properties
Розглянемо практичний приклад на класі Circle, де properties
використовуються для забезпечення того, що радіус завжди буде
невід'ємним числом. Замінимо public $radius
на property:
Починаючи з PHP 8.4, тієї ж функціональності можна досягти за допомогою property hooks, які пропонують набагато елегантніший і коротший синтаксис:
Extension methods
Nette\Object
приніс у PHP ще одну цікаву концепцію, натхненну
сучасними мовами програмування – extension methods. Ця функція, запозичена з
C#, дозволила розробникам елегантно розширювати існуючі класи новими
методами без необхідності їх модифікації чи успадкування. Наприклад,
ви могли додати до форми метод addDateTime()
, який додає власний
DateTimePicker:
Extension methods виявилися непрактичними, оскільки редактори не підказували їх назви, навпаки, повідомляли, що метод не існує. Тому їх підтримку було припинено. Сьогодні більш поширеним є використання композиції або успадкування для розширення функціональності класів.
Отримання назви класу
Для отримання назви класу SmartObject пропонував простий метод:
Доступ до рефлексії та анотацій
Nette\Object
надавав доступ до рефлексії та анотацій через методи
getReflection()
та getAnnotation()
. Цей підхід значно спростив роботу з
метаінформацією класів:
Починаючи з PHP 8.0, можна отримувати доступ до метаінформації через атрибути, які пропонують ще більше можливостей та кращу перевірку типів:
Method getters
Nette\Object
пропонував елегантний спосіб передачі методів, ніби
вони були змінними:
Починаючи з PHP 8.1, можна використовувати так званий first-class callable syntax, який розвиває цю концепцію ще далі:
Події
SmartObject пропонує спрощений синтаксис для роботи з подіями. Події дозволяють об'єктам інформувати інші частини програми про зміни свого стану:
Код $this->onChange($this, $radius)
еквівалентний наступному циклу:
Для кращої зрозумілості рекомендуємо уникати магічного методу
$this->onChange()
. Практичною заміною є функція Nette\Utils\Arrays::invoke: