Супроводження PHP MVC сайтів дуже витратне

15 хв. читання

Вирішив перекласти статтю, яка трапилася мені після роботи над огидним сайтом на огидному Yii, в якому логіка роботи програми розмазалась по всім трьом літерам із абревіатури MVC, в якому було багато схожого коду, який робив майже однакові речі, в якому було страшно міняти HTML-код будь-якої форми, бо потім могло виявитися, що форма використовується ще в трьох місцях (хоча, на мою думку, не повинна була). А також в якому всі моделі - це просто зборище функцій для роботи з БД, які відносяться до певної частини сайту.

Постійно доводилося перемикатися між багатьма відкритим файлами і тримати в голові що, де, і на якому етапі додається в результуючий HTML документ. Хоча нічого космічного там не відбувалося, і всі ці додаткові сутності виступали як зайвий багаж.

Робота з таким сайтом у всіма улюбленому Sublime text додавала ще більше роздратування.


Я в основному працюю над web програмами, написаними на PHP і MySQL, і написаними давно. Зазвичай початковий розробник недоступний, тому я мушу зрозуміти код, для того щоб виправити вади чи додати нові можливості.

Протягом тривалого часу, більшість PHP програм над якими мені доводилося працювати були написані в класичному PHP стилі:

  • URL для кожної сторінки;
  • PHP і HTML (і JavaScript) перемішані між собою;
  • бізнес логіка і представлення змішані в одному й тому ж файлі.

Декілька років тому я почав бачити більше коду в MVC стилі, написаного на таких фреймворках як: Zend, Symfony, Laravel, CodeIgniter тощо. Я думав, що це добре, і що супроводжувати PHP код, написаний на MVC фреймворках буде легше. Проте все виявилося якраз навпаки. Особисто для мене, програму в класичному PHP стилі легше зрозуміти і реструктуризовувати, навіть якщо вона деградувала в спагеті–код з PHP і HTML. Легше працювати над класичним PHP, навіть над погано написаним, тому що все що тобі потрібно знати, для того щоб слідувати потоку запит–відповідь, - знаходиться в одному місці і читається зверху донизу. Що ж до MVC, то зрозуміти логіку роботи, яка стоїть за ним з усім цим багажем ООП, який MVC зазвичай тягне за собою, на порядок важче; і MVC/OOP код не обов'язково вищої якості, або ж його легше обслуговувати чи він більш надійний.

MVC і web-додатки

MVC (модель–представлення–контролер) — це шаблон або стиль архітектури комп'ютерних програм, який веде відлік свого існування від 1970–х і 80–х. Ідея полягає в тому, щоб забезпечити поділ сфер відповідальності, відділяючи користувацький інтерфейс від бізнес логіки і представлення даних. MVC не вимагає ООП, але на практиці більшість MVC фреймворків реалізовані саме в цій парадигмі, бо ООП також може підтримувати строгий поділ сфер відповідальностей. Існували PHP MVC фреймворки й до появи Rails, але дивлячись на нині популярні PHP фреймворки, стає очевидним, що Rails вплинув на багатьох з них.

MVC походить з доінтернетних часів мейнфреймів, німих (dumb) терміналів і довгоживучих серверних процесів, які могли оновити інтерфейс користувача в будь–який час. Web модель запит/відповідь (мається на увазі те, як працюють сайти, коли браузер обмінюється HTTP запитами з HTTP сервером) без збереження стану між запитами означає відмову від постійно запущеного серверного процесу і динамічного оновлення інтерфейсу. Втім PHP працює не так.

Так звані MVC фреймворки для web програм загалом не є MVC. Натомість вони використовують стиль URL–обробник–шаблон–модель, який на перший погляд виглядає як MVC, але не має деяких важливих особливостей MVC. Наприклад "моделі" в web фреймворках - це зазвичай лиш прошарки над базою даних для того, щоб виконувати CRUD (Create, Read, Update, Delete) SQL запити. Бізнес логіка, яка напевне належить моделі, зазвичай, виноситься в контролер і моделі зазвичай не сповіщають контролер або представлення коли щось змінюється в них. Це не відповідає CRUD парадигмі або тому, як PHP програма запит–віповідь працює.

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

Мій досвід з PHP фреймворками

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

Приклад з реального життя: в якому саме місці я повинен розмістити бізнес правило, яке каже що клієнт не приймає картки "American Express"? В шаблоні де є JavaScript функція яка перевірить контрольну суму номера картки і визначить її тип? В шаблоні відбуваються перевірки на кшталт того в яких країнах клієнт веде справи, заповнені потрібні поля вводу чи ні; то чому б заодно не перевіряти і те які кредитні карти вони приймають? Чи цю перевірку годилося б додати до контролеру, який приймає номера кредитних карток введених користувачем? Функція отримання уповноваження від торгового процесора через зовнішній сервісний API належить контролеру чи моделі? Що якщо через деякий час мій клієнт вирішить що вони також будуть приймати і "AMEX" для покупок? Код для обробки кожного такого простого випадку з реального світу не завжди має очевидно правильне місце. Або він має бути розділений на декілька частин через те, що так буде зручніше (або для уникнення занадто частої відправки даних).

Інший приклад: мені дісталася програма, яка відображала сторінку з дописами з Instagram і Twitter згідно відповідності купі хештегів і авторів. Попередній розробник (якого мій клієнт звільнив через те, що програма була випущена занадто пізно і до того ж з вадами) використав Zend framework 2 для того, що видавалося як доволі просте завдання. Код являв собою класичне ОО спагеті: багато класів з методами, які роблять дуже гранульовані операції. Було важко з'ясувати де все відбувається, тому що весь потік виконання був неочевидний і стан був розсіяний серед багатьох об'єктів які мали неявні взаємозв'язки. Все це було написано на ZF 2 без очевидної на це причини. Програма являла собою десятки тисяч рядків мутного незрозумілого коду, який не працював. Я замінив все меншою за розміром сторінкою яка робила саме те що вона і повинна була робити: викликати API Twitter і Instagram, надавати просте кешування і генерувати сторінку дописів. Додавання авторів або оновлення хештегу звелося до простої зміни одного рядку в файлі налаштувань. Результатом моєї роботи була програма, яка займала менше 1000 рядків коду включно з коментарями.

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

Всі роблять це неправильно?

Перед тим як я почав працювати над веб-додатками, я провів більше десяти років, працюючи над корпоративними і освітніми програмами, написаним в ОО стилі. В основному на C++. Коли ці проекти зазнавали невдачі або з'ясовувалося, що їх важко обслуговувати і розширяти, то програмісти завжди звинувачували в цьому погано спроектовану структуру класів. Якби ми (або ті хто були до нас) могли б вірно змоделювати модель області роботи програми в класах і об'єктах, то все було б чудово. Парадигма ООП ніколи не піддавалася сумніву; вважалося, що просто більшість програмістів або занадто недосвідчені, або ліниві для того, щоб зробити все правильно.

Почитай обговорення в Мережі в яких хтось питає як зробити щось "коректно" в їхньому PHP фреймворку. Якщо ти розмістиш будь-яку бізнес логіку в шаблоні (або представленні), то ти робиш це неправильно. Якщо ти відсилаєш HTML з контролеру, то ти робиш це неправильно. Якщо модель "знає" про взаємодії з користувачем, то ти робиш це неправильно. Бізнес правила, які впливають на представлення (такі як варіанти доставки, які залежать від загальної вартості замовлення і місця призначення) мають бути в представленні? Такі зовнішні сервіси як обробка кредитних карток чи розрахунок вартості доставки розміщуються в моделі чи контролері? Звідки мені знати чи роблю я все правильно, коли немає жодних конвенцій як робити це правильно?

Програмування - це предмет моди і релігії. Воно засноване на поглядах, вірі і страху виглядати дурним або недосвідченим. Вибери яку–небудь програмістську тему і ти можеш провести цілісінький день читаючи про правильний спосіб робити це, або критику ймовірно неправильного способу. Ці аргументи рідко коли мають якесь підґрунтя у вигляді фактів або досліджень — вони анекдотичні і упереджені. Єдиний випадок, коли я сприймаю подібні обговорення про правильно-неправильно серйозно - це дискусії на тему алгоритмів або реляційних баз даних, тому що вони справді мають теоретичні і математичні обґрунтування.

Моє повернення до вільного стилю

Нещодавно я взяв на розробку проект з нуля, те що я рідко роблю. З початку це була робота по виправленню помилок, але сайт на Wordpress представляв з себе такий безлад, що я мусив сказати своєму клієнту, що краще було б почати все заново (а я не кажу це занадто часто). Я вирішив переглянути свій простий PHP MVC фреймворк і вдосконалити його, беручи до уваги нові можливості PHP. Я мав одну точку входу в програму для обробки глобальних налаштувань, автозавантаження класів, безпеки, управління сесіями і маршрутизації запитів. Я мав моделі, які насправді містили бізнес логіку, а не тільки CRUD. Я мав контролери які обробляли отримані дані, оновлювали моделі і передавали змінні шаблонам. І у мене були шаблони, які могли містити тільки прості інструкції if/then/else і цикли. Я намагався працювати з фреймоврком і дотримуватися правил, які я сам для себе придумав. Я реструктуризовував фреймворк два чи три рази для того, щоб розв'язати ті проблеми, з якими мені доводилося стикатися. Я мав більше 7000 рядків PHP коду. Все це працювало, але я був нещасний.

Я поглянув на інші фреймворки допускаючи що я роблю все неправильно, проте архітектура мого творіння була дуже подібна до архітектури інших, найбільш популярних, фреймворків. Я взяв на озброєння декілька хороших ідей, але в мене ніколи не виникало відчуття, що я занадто сильно стараюся втиснути програму в фреймворк, і що я проводжу багато часу дотримуючись того, що, як я думав, було правильним шляхом (або принаймні не дуже неправильним, за який мене не будуть лаяти на StackOverflow і Github).

Що врешті–решт змусило мене полишити такий спосіб організації коду - так це розуміння того, що мені доводилося перемикатися між занадто великою кількістю файлів для того, щоб слідувати за потоком виконання; і крім цього занадто багато коду мною було написано лише для дотримання розділення відповідальності. Мати бізнес логіку розкидану в багатьох файлах і класах - тяжко, тому що я працюю за 13 дюймовим портативним комп'ютером в Vim. Можливо з MVC фреймворками простіше працювати маючи кілька 23 дюймових моніторів, але на маленькому екрані зміни буферу і розділення екрану призводять до занадто частої зміни контексту. Я хотів бачити все на одній сторінці щоб я міг тримати все це в голові.

Тепер в мене є менше 4000 рядків PHP коду написаному в стилі більш, як я гадаю, придатному для обслуговування і аналізу. URL співвідносяться з індивідуальними файлами (з простим перевизначенням адрес за допомогою Apache, для того щоб отримати більш читабельні посилання на зразок /emai/inbox, які насправді переадресовуються на email_inbox.php). Кожен PHP файл, який пов'язаний з певним HTTP запитом, написано в типовому для PHP стилі: спочатку йде бізнес логіка, а потім все інше. Обробка запиту і бізнес логіка включно з запитами до бази даних розміщується на початку файлу з PHP сценарієм, а потім іде HTML, в якому можна використовувати лише змінні, визначені в тому ж самому файлі і жменю глобальних службових функцій. Немає вбудованих SQL запитів або операцій з базою даних. Все це знаходиться в моделях, і як тільки справа доходить до виводу HTML, то не залишається жодних посилань на функції моделі і жодних вбудованих блоків PHP коду. При такій організації стає легко з'ясувати, де саме кожен запит обробляється і прослідкувати за ним від кінцевого результату (виводу HTML коду), до окремого файлу. Спільні для програми PHP функції і шматки HTML коду (шапки сторінок, підвали, файли з CSS і JavaScript) розміщуються в окремих файлах. Більше немає шаблону, більше немає довгих блоків коду, які просто копіюють перевірені дані отримані ззовні або з результату запиту до БД, в якусь структуру для того щоб потім передати їх шаблонізатору.

Звичайно, це все лише моя думка заснована на моєму досвіді, моїх упередженнях і моєму власному стилі роботи. Якщо MVC і PHP працюють для тебе, чудово, але ти можеш бути здивованим, виявивши що ця зв'язка не така вже й крута і придатна для подальшого супроводу як ти думаєш. Все ще немає ніякої срібної кулі, коли мова заходить про розробку ПЗ.

P.S.

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

Ти мусиш захистити сайт від найбільш розповсюджених видів атак: SQL ін'єкцій, міжсайтового скриптингу, підробки міжсайтових запитів, викрадення сесій, підробки "коржиків", ненадійних програмних інтерфейсів і AJAX функції і т.і.

Вивчи все про ці види атак і використовуй вбудовані PHP функції або бібліотеки яким ти довіряєш для того, щоб захистити свою програму. Розглядай всі вхідні дані як небезпечні, незалежно від того отримав ти їх за допомогою HTTP запиту, з БД, з "коржиків" чи зовнішнього сервісу. Використовуй бібліотеку для роботи з БД (таку як, наприклад, PDO), ніколи не вставляй змінні в SQL запити. Очищай все що приходить в вигляді HTML або JSON. Переконайся що твій web–сервер блокує спроби доступитися до Git або SVN репозиторіїв, або до чогось іншого в кореневому каталозі web-серверу, до чого ніхто доступу мати не повинен. Створюй систему звіту про помилки і систему ведення журналів з самого початку. Не використовуй глобальні змінні.

Будь обачним. Пий молоко. Користуйся Linux.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.6K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація