Автозавантаження класів в php додатку, dof

Передмова

Сучасні web-додатки, як правило, складаються з безлічі класів, кожен з яких вирішує певне завдання.

Отже, за замовчуванням, якщо в деякому нашому додатку нам необхідно використовувати якийсь клас, скажімо, MyClass. нам потрібно переконається, що файл, в якому описаний даний клас, був раніше підключений за допомогою операторів require або include:

Тобто кожен клас (а також інтерфейс і ін.), який ми збираємося використовувати в додатку ми повинні «підключити» заздалегідь або «по ходу справи». Одна з проблем полягає в тому, що найчастіше додатки мають аж ніяк не лінійною структурою і далеко не завжди ми можемо знати заздалегідь які з безлічі класів (які потенційно можуть бути використані в ході виконання скриптів), будуть дійсно задіяні, а які - ні. Підключення заздалегідь всіх потенційно використовуваних класів веде до нераціонального використання ресурсу пам'яті, а «ручне» підключення потрібних класів «по ходу справи» швидше за все призведе до використання разл. хитрувань у вигляді перевірок умов і операторів включення, і в наслідку - ускладнення коду, і погіршення його удобочитаемости.

В рамках даної статті мова піде саме про автоматичну завантаженні класів в PHP додатку.

засоби SPL

У PHP (починаючи приблизно з версії 5) з'явилася чудова можливість автоматичного завантаження що раніше не завантажених класів. Тобто коли PHP інтерпретатор зустрічає в коді згадка якогось класу, він теоретично може самостійно спробувати виконати завантаження зазначеного класу. Однак, щоб це стало можливим, нам все ж таки доведеться описати якусь функцію (метод), яка визначає, де саме знаходиться опис потрібного класу. Зазвичай вона називається функція-завантажувач.

Не будемо розглядати тут використання ф-ції __autoload (); т. к. цей шлях нині вважається дещо застарілим і практично повністю витіснений повсюдно засобами з набору SPL.

Описати і зареєструвати функцію-завантажувач можна кількома способами, наприклад так:

Стандартна функція spl_autoload_registered (); дозволяє зареєструвати функцію-завантажувач в чергу __autoload (). т. е. зареєструвати користувача функцію-завантажувач, як АВТО завантажувач. У теорії ми можемо зареєструвати і кілька функцій-завантажувачів, якщо це необхідно. У нашому ж прикладі ми реєструємо якусь анонімну функцію, яка буде виконувати завантаження класу по переданому їй імені класу.

Працює це приблизно так:
Коли PHP інтерпретатор зустріне в коді ім'я незнайомого класу, він спробує його завантажити послідовно за допомогою всіх таких зареєстрованих Автозавантажувач. Тобто кожної функції-завантажувачу по черзі в якості параметра (в нашому випадку - $ classname) буде передано повне * ім'я цього «незнайомого» класу, потім щоб функція-завантажувач змогла спробувати знайти соотв. файл з описом цього класу і включити його. Якщо жодна з зареєстрованих ф-цій-завантажувачів не змогла знайти і підключити клас, в результаті буде викинута помилка про те, що такий клас не був знайдений.

В даному прикладі, ми прийняли, що в нашому додатку файли з описом класів розташовані в якійсь папці / my_classes_folder /. а ще ім'я файлу класу зобов'язана збігатися з ім'ям самого класу. Тобто на нашу угодою, щоб знайти і завантажити скажімо, MyClass ми повинні додати до шляху / my_classes_folder / ім'я класу (MyClass), потім додати до нього розширення .php і включити з його допомогою require. Що, власне, і описано в прикладі вище. Всі перевірки на існування соотв файлів і шляхів та інше тут опущені простоти заради.

Однак і тут виникають нюанси, наприклад, а що якщо класи в додатку мають якусь складну ієрархічну структуру або НЕ зосереджені в одній папці або ж зовсім не підкоряються подібним угодам. Як варіант можна розглядати якийсь маппинг класів і шляхів до файлів з їх описом, але цей варіант має і свої недоліки і в окремих випадках може мати досить складну реалізацію.

Простори назв, PSR-0 і PSR-4

Починаючи з версії 5.3 PHP став ще трохи ближче до інших розвинених мов програмування, і разом з тим в ньому з'явилася підтримка просторів імен (namespaces). У контексті нашої задачі це перш за все означає, що для будь-якого класу (інтерфейсу, домішки) ми придбали також можливість вказувати його місце в загальній ієрархії класів.

Припустимо, що наш клас MyClass відноситься до якоїсь групи утилітарних (допоміжних) класів, тобто входить в підгрупу Utilities. яка в свою чергу входить в загальну підгрупу MyClasses:

Як такі нововведення можуть допомогти вирішити деякі із згаданих раніше проблем?

Розглянемо цей приклад класу MyClass, в контексті з анонімної функцією-завантажувачем, описаним вище. Почнемо з того, що повне ім'я класу в нашому теперішньому випадку буде складатися з імені простору + імені самого класу, т. Е. - \ MyClasses \ Utilities \ MyClass. І саме в такому вигляді воно буде передано в кач-ве фактичного параметра в функцію-завантажувач. Тобто використовуючи колишню схему без зміни ми отримаємо за фактом такий шлях до файлу з описом класу: /my_classes_folder/MyClasses\Utilities\MyСlass.php
А якщо замінимо в цьому рядку всі зворотні Слеш на звичайні? Отримаємо на вигляд типовий шлях, що складається з ієрархії папок і файл MyClass.php в кінці.

Модифікуємо соотв. чином код нашої ф-ції-завантажувач:

висновок:
Ми можемо розміщувати класи нашого застосування в соотв. папках і підпапках, точно повторюючи структуру просторів імен нашого застосування, і таким чином за допомогою простого перетворення повного імені класу ми зможемо отримувати частковий або повний шлях до файлу з описом цього класу.

Таким чином, ми прийшли до більш сучасного підходу до автозавантаженні класів, який базується на стандартах PSR-0 / PSR-4. Розглянемо приклад простого класу, який реалізує подібний механізм автозавантаження за стандартом PSR-0.

Нижче також наведемо приклад використання даного Автозавантажувач в коді програми:

Нижче пропонується короткий огляд призначення методів вищеописаного класу Autoloader. Клас використовує патерн "одинак". чому саме так? Кожен раз, коли створюється новий екземпляр класу, його метод load () буде зареєстрований як автозавантажувач. Нам немає необхідності реєструвати одну і ту ж функцію як завантажувач кілька разів, тому ми таким чином закриваємо цю можливість.