Cuprins
- Setarea PHP și a serverului
- De unde să începi și cum să studiezi
- Note de optimizare și de stil
- Erori MySQL comune
- Sfaturi de securizare
- Thread-uri utile despre probleme comune
- Carti
- Bloguri
- Cursuri
I. Setarea PHP și a serverului
- Instalează Apache, PHP și MySQL manual (fiecare în parte) pe calculatorul tău personal
Mai întâi recomand instalarea manuală a unui daemon http (http://en.wikipedia....puter_software)) cum este Apache, apoi testarea acestuia. În mod standard, la vizitarea adresei http://localhost/, vei vedea "It works!". În interiorul directorului unde ai instalat Apache se află un subdirector "conf/" în care se află fișierul httpd.conf ("conf/httpd.conf"). Acest fișier conține setările daemonului http (in task manager httpd - ghici de unde vine "d"-ul). În acest moment, chiar și fără PHP, prietenii pot intra pe http://127.0.0.1 și-ți vedea "pagina" (prietenii trebuie să introducă adresa ta IP în loc de 127.0.0.1 - vezi http://www.ip-adress.com/ sau google "what is my ip address"). Într-adevăr, în acest moment calculatorul tău s-a transformat într-un server (un server http). Următorul pas este integrarea PHP-ului ca modul în apache (nu ca CGI). După ce urmezi pașii de instalare din manualul oficial PHP trebuie să-i dai restart lui apache pentru a prelua schimbările din httpd.conf.
Repet: recomand instalarea PHP ca modul!
Nu recomand folosirea WAMP sau alte "pachete" ce instalează apache,php,mysql dintr-un foc!
De ce nu? Repet: acum te afli pe partea profesională. Indiferent dacă ești sau nu unul, te întrepți (sper) către acea direcție! EȘTI UN PROFESIONIST, iar un profesionist trebuie să știe cum funcționează platforma pe care (în cazul de față) o programează: în cazul tău web-ul, daemonul http (apache), serverul mysql. Instalarea și setarea lor manuală te va ajuta să înțelegi mai bine ce se întâmplă și cum funcționează și interacționează aceste componente în fundal, iar WAMP (sau apachefriends) nu îți oferă acest lucru în mod nativ! Bineînțeles, poți învăța și cum funcționează WAMP, dar asta te va ține legat de acel software. Însă dacă înțelegi modul fundamental de funcționare vei vedea că WAMP nu este nimic altceva decât un bug (o mentalitate) ce provine din lumea unui gigant software cunoscut de noi toți în care nu e important decât mouse-ul și "pe cât posibil să nu înțelegi/vezi nimic din detalii". Însă PE UN PROFESIONIST CA TINE îl interesează detaliile!
Nu te lăsa doborât de ușurința aparentă oferită de WAMP. Accentuez: aparentă. De ce aparentă? Pentru că, după ce vei fi instalat WAMP, vei avea impresia că ești fericit, până în momentul în care vrei să setezi ceva mai obscur ce nu poate fi făcut decât dacă știi cum se fac setările în mod manual. Și în acest punct realizezi că te sfătuiesc bine: învață din start ce și cum. Nu te feri de cunoaștere!
Bineînțeles după ce ai învățat toate acestea și te consideri un guru în ce privește administrarea unui mediu (relativ) complet de dezvoltare web (web development), poți folosi iar WAMP, pentru că vei ști cum să configurezi mai orice aspect al fiecărei componente WAMP. ȘI TOTUȘI am îndoiala mea că dacă ajungi acolo sus te vei mai uita înapoi (de acolo de sus, în jos) la WAMP
Dacă ai probleme cu instalarea mediului de dezvoltare web întreabă pe Softpedia Forum > Professional Zone > Webmaster Corner > Technical Area / GENERALITATI (așa cum e descris și în regulamentul ariei PHP).
- Dupa ce instalezi Apache, pe Windows Vista merge doar 127.0.0.1 nu si localhost. Asta e deoarece in Vista, fisierul "C:\Windows\System32\Drivers\etc\hosts" este cam asa :
# tona de comentarii
#...
127.0.0.1 localhost
::1 localhost
Daca vrei sa iti mearga si localhost-ul (http://localhost/) trebuie sa stergi a doua linie ("::1 localhost"). M-am gandit ca pe unii i-ar ajuta aceasta informatie. Pe mine ma ajutat !
(autor: andreas-v)
- Setează PHP așa încât să raporteze cât mai multe erori și avertizări.
PHP nu este setat în mod standard pentru programatori, ci pentru servere publice pe Internet.
Tu ca programator trebuie să repari greșeli în codul scris de tine, deci VREI să vezi erorile și avertizările generate de PHP.
PHP te ajută în acest proces, dacă faci câteva setări în php.ini:
Cele mai importante setări sunt (ordonate subiectiv):
error_reporting = E_ALL|E_STRICT
display_errors = On
short_open_tag = Off
asp_tags = Off
display_startup_errors = On
Relativ importante, recomandate de mine personal:
output_buffering = Off
allow_call_time_pass_reference = Off
zlib.output_compression = Off
track_errors = On
register_globals = Off
date.timezone trebuie setată corespunzător. Citește http://www.php.net/manual/en/timezones.php în manual.
session.auto_start = 0
tidy.clean_output = Off
Opționale, dar semnificative:
implicit_flush = Off
log_errors = On
ignore_repeated_errors = On
report_memleaks = On
Gândește-te la posibilitatea că serverul public de pe Internet pe care va rula scriptul creat de tine ar putea avea setarea
safe_mode = On
și testează-ți scriptul atât în safe mode cât și normal.
Pe alt server unde nu ai acces la php.ini, dar poți crea fișiere .htaccess, se pot face configurațiile pe directoare/domenii/subdomenii separate:
- Pentru dezvoltare/programare:
php_flag display_startup_errors on
php_flag display_errors on
php_flag html_errors on
php_flag log_errors on
php_flag ignore_repeated_errors off
php_flag ignore_repeated_source off
php_flag report_memleaks on
php_flag track_errors on
php_value docref_root 0
php_value docref_ext 0
php_value error_log /calea/catre/public_html/domeniul-tau.ro/php_errors.log
php_value error_reporting 999999999
php_value log_errors_max_len 0
<Files /calea/catre/public_html/domeniul-tau.ro/php_errors.log>
Order allow,deny
Deny from all
Satisfy All
</Files>
- Pentru servere de producție:
php_flag display_startup_errors off
php_flag display_errors off
php_flag html_errors off
php_flag log_errors on
php_flag ignore_repeated_errors off
php_flag ignore_repeated_source off
php_flag report_memleaks on
php_flag track_errors on
php_value docref_root 0
php_value docref_ext 0
php_value error_log /calea/catre/public_html/domeniul-tau.ro/php_errors.log
php_value error_reporting 999999999
log_errors_max_len 0
<Files /calea/catre/public_html/domeniul-tau.ro/php_errors.log>
Order allow,deny
Deny from all
Satisfy All
</Files>
Am o eroare și nu știu unde!
Nu se poate să ai o eroare ȘI să nu știi unde este dacă ai setat PHP așa cum ți-am zis mai sus!
II. De unde să începi și cum să studiezi
- De oriunde si orice ai citi (carti, bloguri), nu lua absolut totul de buna. Fiecare programator care descrie ceva anume isi spune parerea, iar orice parere tine de subiectivitate. In schimb incearca sa tii minte parerea si argumentele pro/contra, iar cand ai strans mai multe argumente din surse diferite, formeaza-ti singur o parere bazata tot pe argumente logice pro/contra (nu pe "ce simti" ca e bine/corect). Deci:
- Cand intalnesti o afirmatie nebazata pe argumente pro/contra, sau fara explicatii, pune-o sub semnul intrebarii (tine-o minte, incearca pe parcurs sa ti-o justifici, si cand ai destule cunostinte iti vei da seama daca esti de acord sau nu cu acea afirmatie, sau sub ce conditii esti sau nu de acord).
- Nu in ultimul rand, nu uita ca fiecare metoda are avantajele si dezavantajele sale. La marile X vs. Y, de obicei intra prioritatile proiectului in joc! Pentru proiectul P1 aleg X deoarece Z1, pentru proiectul P2 aleg insa Y, pentru ca Z2 este foarte important pentru proiect.
- Programarea se invata programand, nu citind. Atunci cand dai peste o explicatie despre o metodologie, incearca sa o transcrii in cod!
- Cand intalnesti exemple cu cod concret, NU le copia ca sa le testezi, ci citeste explicatiile legate de el, ia linie cu linie codul si intelege-l. APOI inchide cartea si scrie un cod (poate fi la fel de bine alt cod) din minte care sa rezolve problema respectiva, folosind cat mai multe din notiunile nou invatate. Vei fi uimit cate greseli vei face, dar doar asa iti vor intra anumite lucruri in sange!
- A sti programare NU inseamna a sti nume de functii si ordinea parametrilor. Inseamna a detine cunostinte generale si a face conexiuni intre notiuni!
Ma consider un programator destul de bunicel in PHP, si totusi ma uit des in manual. De ce? Pai simplu, cand lucrezi la lucruri diferite, in limbaje diferite si cu framework-uri diferite ... e normal sa nu tii minte toate functiile! Dar stiu terminologia, stiu conceptele (care apropo, sunt comune intre mai multe limbaje/framework-uri), lucru care imi permite sa gasesc mai orice in maxim 1 minut.
- Recapituleaza ce ai asimilat, si incearca sa faci conexiuni intre notiuni! *
Nu rareori ti se va intampla sa citesti ceva, sa intelegi ce citesti, INSA sa nu poti lega si ordona aceste noi cunostintele de cele vechi, deja existente. In acest caz ai rabdare. Tine minte mot-a-mot ce ai citit, incearca sa faci o pauza la fiecare paragraf si sa judeci, si daca tot nu reusesti sa ordonezi acea cunoastere ... mai citeste! (ca solutie salvatoare, intreaba pe forum, insa atentie la punctul 8 de mai jos: terminologia!).
Se intampla sa trebuiasca sa citesti si cateva zeci de pagini pana sa pricepi ceva productiv (explicatie tot in linkul mentionat la punctul 8.).
*Altfel, nu vei fi nimic altceva decat un papagal scriitor de coduri - nicidecum un programator. Un programator INTELEGE ce face, si DE CE o face asa si nu altfel.
- Alte sfaturi pe care le-as mai da unui viitor programator sunt descrise in Lucruri pe care orice programator ar trebui sa le stie, Si pe care mi-ar fi placut sa mi le spuna si mie cineva la inceput - punctele 1, 3-10, 12. Daca stii C/C++ citeste-le pe toate.
- Apropo de C/C++: Un programator PHP (<sau pune aici orice alt limbaj>) bun stie sa programeze fluent in cel putin alte doua (<sau pune aici doua limbaje favorite> - si nu, CSS si HTML nu sunt limbaje de programare - vezi link de la punctul 8) limbaje.
- Citește cu atenție manualul! Manualul este necesar și suficient! (plecând de la premiza că îți folosești inteligența la maxim). Nu, nu se poate învăța PHP fără să citești. Și nu, nu se mai poate fără engleză (în lumea globalizată în care trăim, mai ales în domeniul IT) - la început este greu cu engleza, dar crede-mă că în câteva luni vei ajunge să înveți direct în engleză - fără să mai stai să traduci fiecare cuvințel, iar acesta este un câștig enorm pentru viitor!
După integrarea PHP-ului în apache ca modul poți trece la următorul capitol din manual. Nu sări peste pagini, ci citește-le penibil de exact pe fiecare, începând de la titlul lecției (mereu e important să înțelegi mai întâi ce studiezi) și terminând cu ultima "user contributed note". Atunci când te lovești de un termen pe care nu-l înțelegi încearcă să cauți pe google. wikipedia (http://en.wikipedia.org/wiki/) conține mereu articole destul de bune despre mai toate noțiunile.
De exemplu, nu înțelegi ce înseamnă "data type", atunci google "programming what is data type" și intră pe primul rezultat, sau caută pe wikipedia "data type". După ce te-ai documentat cum trebuie poți întreba pe forum, ca o ultimă soluție salvatoare. Oricum, pe web sunt atâtea articole bine scrise, încât e greu de imaginat ca cineva de pe forum să stea să-ți explice mai bine. Dacă ai început programare, atunci ar fi bine să te obișnuiești cu ideea: trebuie să-ți pui la treabă capacitatea de studiu individual!
- Ceea ce trebuie neapărat să faci este să te uiti peste lista categoriilor de funcții pe care le are PHP: http://www.php.net/m.../en/funcref.php
- Dacă ai nevoie de o funcție pentru manipularea unui string într-un anumit fel, atunci apasă CTRL+F în browserul tău și introdu "string functions"
similar: "http", "network", "array", "image", "flash", "mail", "mathematical", "mysql", "session", "filesystem" (pentru lucrul cu fișiere și directoare)
- Scenariu: vrei să obții o parte dintr-un string dar nu știi funcția.
Cert este: e o funcție de manipulare a unui string, deci urmând pasul 3. de mai sus, ajungi pe: http://www.php.net/m...ref.strings.php
Vrei o parte dintr-un string, deci CTRL+F și introdu "part of", "part of a" sau mai mult: "part of a string".
E într-adevăr o artă să găsești funcția potrivită; e o deprindere pe care începătorii o consideră frustrantă, dar nu uita: de îndată ce ai deprins acest lucru documentația oficială, așa comprimată cum e ea, îți va crește viteza cu care te poți ajuta singur și îți va scade frustrarea.
- Învață să folosești manualul oficial MySQL: http://dev.mysql.com...0/en/index.html
Folosește aceleași tehnici de la punctul 4
Ceea ce începătorii nu realizează este că mysql este un server de sine stătător și independent de apache(cu sau fără suport PHP). Poți folosi doar mysql "simplu".
După ce ai instalat MySQL, urmează tutorialul: http://dev.mysql.com...n/tutorial.html ca să afli cum poți face asta.
- Dacă nu vrei să dai bani, pe web găsești o grămadă de tutoriale gratis: google: php tutorial.
Cele mai bune cursuri PHP se pot obține direct de la sursă: Zend PHP 5 Certification Training sau Cursul PHP/MYSQL de la infoacademy.
- În multe locuri vei vedea exemple de coduri sursă. Unele sunt bune, altele nu. Oricare ar fi situația, renunță a cataloga calitatea unui cod în funcție de "merge" sau "nu merge". Un cod de calitate face mai mult decât "merge", un cod de calitate respectă cel puțin "notele de optimizare și de stil" de mai jos, arătând astfel că cine l-a scris știe ce face. În mod ideal, codul respectiv implementează și un algoritm inteligent (un exemplu ar fi "poligoane rotunjite". CTRL+F în acest thread după "florindinu").
Altfel spus: un cod trebuie să meargă pentru că e gândit să meargă, nu pentru că se întâmplă să meargă (dă impresia că ar fi bine scris, dar nu este).
- Nu te plânge când vezi un mesaj de eroare, ci fi fericit! A vedea eroarea este ca o mână cerească, în contrast cu a nu vedea niciun mesaj - dar "scriptul" tot să nu funcționeze - situație care e mult mai frustrantă. Însă dacă vezi o eroare sau avertizare, atunci măcar știi de unde să începi cu reparatul greșelilor pe care le-ai făcut.
III. Note de optimizare și de stil
- formatează-ți și documentează-ți codul
Asta nu înseamnă să exagerezi, comentarii ca:
$foo++;//increment $foo
sunt inutile. Formatează-ți codul conform unui standard. Eu personal obișnuiesc să-mi formatez codul ca în Pear Coding Standards, cu mici deviații de la acel standard. Orice ai alege, cel mai important e să respecți mereu formatul ales - fie el și invenție proprie.
Încearcă să-ți comentezi mereu codul în limba engleză deoarece e înțeleasă peste tot, de oricine e interesat de programare/PHP (și dacă te uiți atent, marile proiecte au avut succes datorită documentației de calitate în engleză)
Pentru documentarea codului recomand PHP Documentor.
- În PHP există doua mari tipuri de stringuri, interpretate și neinterpretate. Cele interpretate încep și se termină cu caracterul ". Se numesc interpretate (sau interpolate) deoarece orice variabilă întâlnită în ele este înlocuită cu valoarea sa. Din acest motiv, stringurile neinterpretate, marcate de caracterul ', sunt puțin mai rapide.
Atunci când folosești echo (vezi sfatul următor de ce echo și nu print), poți separa diferitele valori constante sau variabile prin virgulă. De exemplu, în loc de
echo "Salut $nume";
Poți folosi la fel de bine, dar puțin mai optim:
echo 'Salut ',$nume;
Optimizarea este minimală, ÎNSĂ adăugarea unui apel la o funcție în jurul variabilei $nume este mult mai naturală, de exemplu pentru a evita XSS sau alți vectori de atac trebuie să adaugi un apel la htmlentities() sau similar:
echo 'Salut ', htmlentities($nume);
ÎN PLUS, cu stringurile neinterpretate îti este mai ușor să generezi cod HTML valid. Nu uita că sintaxa generală în HTML este
<etichetă atribut="valoare">
Și nu este corect să folosești ' în loc de " sau să nu pui niciun fel de ghilimele. Astfel, în loc să te obosești să faci escaping într-un string interpretat, astfel:
echo "<a href=\"/index.php\">Home</a>";
mult mai simplu de citit, de scris, dar și mai performant este:
echo '<a href="/index.php">Home</a>';
Deasemenea, folosește stringuri neinterpretate și operatorul de concatenare (punctul - .) chiar și când nu vrei să afișezi ceva cu echo, ci să atribui o nouă valoare unei variabile - din același motive: nu numai că este mai optim (PHP execută puțin mai repede acel cod), dar și pentru că este mai ușor să adaugi apeluri la funcții în jurul acelei variabile. De exemplu,
în loc să declari o variabilă temporară (unde primești "o bilă neagră deja, vezi sfatul 11 de mai jos) și să folosești stringuri interpretate (unde primești a doua bilă neagră), astfel:
$temp = htmlentities($_GET['username']);
$message = "Hello $temp";
mult mai optim, dar și mai ușor de modificat în viitor este:
$message = 'Hello '.htmlentities($_GET['username']);
Dar și mai scurt de tastat, nu-i așa?
- folosește echo și nu print pentru a genera output.
Motiv: echo e mai rapid decât print
- În expresii logice (ceea ce scrii în interiorul parantezelor if,elseif,while,sau în bucla for), atunci când testezi dacă o variabilă are o anumită valoare constantă, nu testa asta așa:
if($foo == 4)
ci așa:
if(4 == $foo)
Explicație: de multe ori se poate întâmpla să scrii din greșeala "=" (operatorul de atribuire) în loc de "==" (operatorul logic de comparație). Ținând cont de faptul că o atribuire are loc de la dreapta la stânga, $foo = 4 ar însemna "atribuie-i valoarea 4 variabilei $foo", iar abia apoi este testată valoarea de adevăr a variabilei $foo. În cazul de față variabila va avea valoarea 4, deci valoarea logică de adevăr a acesteia va fi TRUE. Însă dacă te obișnuiești să scrii "4 == $foo", atunci în cazul în care din greșeală scrii "4 = $foo" PHP va afișa o eroare cum că nu poți atribui o valoare unei constante. Exemple de valori constante:
- 'foo' și "foo" (stringuri alocate static în script)
- 233.55 sau 0 - valori numerice
- TRUE, FALSE, NULL
deci pentru a programa în același stil mereu, așa e benefic:
while(TRUE == mysql_fetch_assoc($res)) {...} //scris în mod trivial pe scurt: while(mysql_fetch_assoc($res)) {...}
if(TRUE == $checked) { ... } //scris în mod trivial if($checked) { ... }
if(0 == $msg_count) { ... } //scris în mod trivial if(!$msg_count) { ... }
- într-o conjuncție logică, ordonează fiecare expresie în mod crescător după probabilitatea valorii de adevăr
Motiv: de îndată ce într-o expresie AND s-a întâlnit un FALSE, interpretarea valorilor următoare este întreruptă, căci expresia are oricum deja valoarea de adevăr FALSE
- Folosiți constante ca PHP_EOL, DIRECTORY_SEPARATOR, PATH_SEPARATOR, DATE_* etc acolo unde este posibil. O listă completă poate fi aflată cu
var_dump(get_defined_constants());
iar numele constantelor sunt destul de expresive. Folosirea constantelor asigură o portabilitate mult mai mare.
- Folosiți excepții acolo unde este posibil. Explicații sunt în manual, iar acesta este un thread cu alte explicații: http://forum.softped...howtopic=409028
- Apelează exit() după fiecare header('Location: ...');
Schimbarea locației implică comunicarea cu serverul HTTP cu ajutorul protocolului HTTP. Această comunicație între PHP și server poate fi întârziată din diferite motive, iar fluxul de execuție PHP nu are nimic de-a face cu "comenzile HTTP" date serverului.
Schimbarea locației nu înseamnă implicit și oprirea execuției scriptului PHP. Explicații și aici: Ce face header()?
- Array-urile sunt de obicei iterate cu bucla foreach (e mai rapid). Dacă totuși folosești bucla for, atunci nu apela funcții ca sizeof() sau count() în condiția buclei, deoarece acea funcție va fi apelată la fiecare iterație:
for($i=0;$i < count($array); $i++) {
//... operate on $array[$i]
}
ci așa:
$length = count($array);
for($i=0;$i < $length; $i++) {
//... operate on $array[$i]
}
- Folosește comparații stricte atunci când e posibil deoarece === sau !== sunt mai rapizi decât == sau !=.
Notă: statistic vorbind, cu aprox. 25% mai rapid.
- Nu declara variabile inutile.
Cel mai comun caz arată cam așa
$titlu = $_GET['titlu']; echo $titlu;
De ce nu direct
echo $_GET['titlu'];
?
Greșeala asta are ca sursă o intenție bună, și anume începătorul care a văzut că așa se face, și acum pur și simplu reproduce ce a văzut la alții, fără să reproducă complet ce a "furat".
Motivul pentru care alții făceau asta este cel mai probabil securitatea. Însă ei nu făceau doar atât, ci validau acel input, de exemplu, pentru a nu conține cod HTML. Acum, ca și începător, deja știi că pentru a șterge orice etichetă HTML dintr-un string poți folosi strip_tags(), și te vei grăbi să scrii un cod ca:
$titlu = $_GET['titlu'];
$titlu_curat = strip_tags($titlu);
echo $titlu_curat;
(Listare 11.1)
Ei bine, nu este efectiv ce faci deoarece ai declarat o variabilă (care ocupă RAM în plus, apropo ) fără să o folosești: $titlu. Și atunci te gândești să folosești direct valoarea returnată de strip_tags(), cam așa:
$titlu = strip_tags($_GET['titlu']);
echo $titlu;
(Listare 11.2)
Acum deja arată mai bine. Totuși, există o "problemă.
Ce este o variabilă defapt, și când introducem una nouă?
O variabilă este o modalitate de a da un nume (un identificator) unei regiuni de memorie RAM a cărei valoare vrem să o accesăm.
Deci unde este problema? Păi problema este următoarea: informații trebuiesc salvate în RAM-ul serverului atunci când vrei să accesezi acele valori în mod repetat, succesiv. Ori în exemplul nostru redus la un minim, nu vrem decât să afișăm valoarea titlului, fără taguri HTML, deci, ca programatori responsabili ce suntem:
echo strip_tags($_GET['titlu']);
(Listare 11.3)
- Listarea 11.1 este categoric felul în care nu trebuie să o faci, pentru că este inefectiv.
- Vei face totuși lucruri ca în 11.2 atunci când nu numai că vrei să afișezi valoarea unei variabile (o accesare RAM), ci și să o prelucrezi mai departe (de exemplu, să o salvezi într-un fișier sau bază de date - a doua accesare RAM - de aceea am spus "când vrei să accesezi acele valori în mod repetat").
- Pentru scriptul nostru simplu totuși, și pentru că efectiv nu ne mai interesează apoi ce este salvat în $_GET['titlu'], 11.3 este modul corect și curat de a ajunge la rezultatul dorit. Aici accesăm două regiuni de memorie: $_GET['titlu'] și return value a funcției strip_tags(). "Șmecheria" este că doar afișăm valoarea returnată de strip_tags(), fără a o salva în RAM.
Notă: în caz că nu te-ai prins, "return value" este tot o valoare, deci poate fi pasată la rândul ei altor funcții care acceptă tipul de date returnat de o funcție. Astfel, este posibil să înlănțuim apeluri de funcții în mod "recursiv":
$order = array_reverse(explode(' ',$order));
Notă la notă: exemplu similar cu o aplicație reală, unde $order este defapt un string care conține o listă de "acțiuni" separate prin spațiu provenită dintr-un fișier de configurare. Aceste acțiuni urmează să fie puse pe un stack (LIFO), însă nu mă aștept ca utilizatorul să introducă acțiunile în mod invers față de ce dorește defapt, de aceea apelul la array_reverse(). Acest lucru îl fac o singură dată, deci nu mă mai interesează apoi valoarea din fișierul de configurare, motiv pentru chiar o suprascriu (tot $order ia valoarea returnată). După cum vezi, teoria nu e doar de dragul de a fi, există și aplicații practice.
Concluzia: folosește variabile atunci când știi că vei avea nevoie de cel puțin două ori de valoarea salvată în ea. - Nu închide procesarea PHP la sfârșitul fișierului .php.
IV. Erori MySQL comune
- Dacă ai o problemă MySQL, folosește clientul mysql de la linia de comandă.
Cum poți face asta este descris în tutorialul oficial MySQL: http://dev.mysql.com...n/tutorial.html
Clientul mysql (fișierul mysql.exe) se află în subdirectorul bin al directorului unde ai instalat mysql, să zicem: "C:\program files\mysql\bin\".
Deci pentru a-l lansa în execuție, deschide ms-dos prompt (start -> run, introdu "cmd" în XP, "command" în versiuni anterioare) și schimbă
directorul de lucru curent (cu comanda cd, care e prescurtarea pentru "change directory"):
cd "C:\program files\mysql\bin\"
apoi lansează în execuție clientul oficial mysql, cu comanda:
mysql -u root -p
O descriere se află pe http://dev.mysql.com...connecting.html (tot în tutorialul oficial mysql)
Notă: descrierile de mai sus sunt "baby steps" pentru cei neinițiați care nu au prins "vremurile ms-dos".
Nu am mai menționat sisteme *NIX, se consideră că cine are un astfel de sistem știe să lucreze cum trebuie cu un shell.
Deseori apar erori în cereri MySQL datorită faptului că ai uitat un apostrof sau o ghilimea.
Fool-proof rule: numărul de ghilimele sau apostrofuri ne-escaped este mereu par! Asta înseamnă: dacă ai 13 (număr impar) ghilimele într-un string ce îl introduci în clientul mysql.exe, atunci ai o ghilimea în plus sau în minus. (regulă similară în Curly bracket programmin language: diferența dintre numărul parantezelor sau acoladelor deschise și cele închise dintr-un cod sursă sau un bloc de instrucțiuni este zero - adică: închizi o paranteză sau acoladă tot de atâtea ori cât ai deschis-o. Regula este la fel de valabilă pentru stringuri)
Clientul MySQL (poți folosi și phpMyAdmin, online la: www.phpmyadmin.net/documentation) îți spune și eroarea exactă. Citește-o.
De obicei îți raportează eroarea "unexpected X before Y", deci uită-te cu atenție ce ai scris în fața lui Y.
Dacă ai testat cererea SQL cu clientul mysql.exe, dar cu php ca client mysql problema încă persistă, atunci
- O altă metodă de a detecta greșeli în cereri SQL este să nu folosești clientul mysql.exe, ci PHP ca client mysql.
Scenariu: $res = mysql_query($sql);
În acest caz cererea salvată în variabila $sql poate conține erori, dar PHP nu le afișează pentru că ... nu i-ai spus să o facă! (e un principiu de bază în programare: mașina - adică calculatorul - face exact ceea ce-i spui tu să facă, nimic mai mult, nimic mai puțin. Dacă ceva "nu funcționează", atunci e vina omului, nu a mașinii).
Pentru a afișa eroarea, poți face cererea astfel:
$res = mysql_query($sql) or die('Cererea SQL:<pre>"'.htmlspecialchars($sql).'"</pre> a returnat eroarea:<pre>'.mysql_error().'</pre>');
Notă: eroarea ar fi avut același format dacă ai fi folosit clientul mysql nu PHP pentru a te conecta la serverul mysql, însă în scenariul de față poți folosi variabile din runtime-ul PHP, ceea ce nu ai putea face în mysql "pur".
- În cazul stupid în care încurci noțiuni elementare (ca în afisare dupa cautare) ca tipurile de date "STRING" și "COLUMN", cazuri în care MySQL sau alt RDBMS nu raportează erori, dar nici o eroare (pentru că nu este nici una), atunci afișează cererea SQL construită:
var_dump($sql)
Exemplu. Se dă un tabel construit și populat astfel:
CREATE TABLE `adrese` (
`id` int(11) NOT NULL auto_increment,
`site` char(30) NOT NULL,
`mail` char(30) NOT NULL,
`parola` char(30) NOT NULL,
`user` char(30) NOT NULL,
`data_ad` varchar(30) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=16;
INSERT INTO `adrese` (`id`, `site`, `mail`, `parola`, `user`, `data_ad`) VALUES
(11, 'sda', 'sad', 'sdasd', 'asd', '2008-11-04 16:52:46'),
(12, 'sda', 'sad', 'sdasd', 'asd', '2008-11-04 16:53:07'),
(13, 'sda', 'sad', 'sdasd', 'asd', '2008-11-04 16:53:41'),
(14, 'sda', 'sad', 'sdasd', 'asd', '2008-11-04 16:54:14'),
(15, 'sd', '', '', '', '2008-11-04 17:28:56');
Cel mai probabil nu este asta ceea ce vrei:
Quote SELECT * FROM `adrese` WHERE "id" = "13";
Ci asta:
Quote SELECT * FROM `adrese` WHERE `id` = "13";
Deoarece prima condiție este mereu falsă, deci nu se selectează niciun rând din tabel.
E ca și cum ai avea codul PHP:
if(13 == 'foo') {
echo 'Foo';
}
și te-ai mira de ce nu afișează "Foo".
- Dacă după ce ai urmat toți acești pași nu obții nimic, fie ea eroare, intrări modificate, adăugate, sortate sau nu obții niciun rezultat la o cerere SELECT, atunci verifică toate variabilele PHP pe care le folosești în construirea dinamică a cererii SQL.
De exemplu, dacă ai: $sql = 'SELECT * FROM `'.$table.'` WHERE `'.$column.'`="'.mysql_real_escape_string($criterion).'"';
și cererea SQL nu returnează niciun rezultat sau eroare, atunci cauza cea mai probabilă este că una sau mai multe dintre variabilele $table, $column sau $criterion nu are valoarea pe care te aștepți să o aibe. (Faptul acesta este un semn că ai pierdut conturul fluxului de execuție și/sau de date în scriptul tău, ar trebui să faci o pauză de cafea și aer proaspăt )
Pentru a verifica valoarea acestora poți folosi:
echo '<pre>';
var_dump($table,$column,$criterion);
echo '</pre>';
sau poți împacheta aceste trei linii într-o funcție și folosi function handling functions pentru a accepta un număr de parametri variabil (astfel de funcții se numesc variadice), așa cum o face var_dump (notă: poți deasemenea "coagula" toți parametrii funcției pe care o scrii într-un singur array)
Notă: aici variabilele $table și $column sunt sanitizate, deci nu am apelat mysql_real_escape_string() asupra lor
V. Sfaturi de securizare(autor: adormitu)
Aș dori să ofer unele sfaturi în legătură cu validarea datelor înainte de a fi printate, introduse în baze de date, salvate în fișiere ș.a.m.d.
Tehnici abordate: XSS (cross site scripting), SQL Injection, SQL Column Truncation, Remote/Local File Inclusion, Redirect (HTTP injection).
- Regula de aur
Validați orice input înainte de a-l folosi: $_POST/$_GET/$_COOKIE, $_SERVER['PHP_SELF'/'HTTP_REFERER'/'USER_AGENT'] ș.a.m.d.
- XSS (cross site scripting)
Definiție și informații aici, mai multe informații aici.
Pentru a preveni atacurile de tip XSS filtrăm datele primite de la utilizator prin intermediul funcțiilor htmlspecialchars() și strip_tags(). Acest articol explică de ce doar folosirea funcției strip_tags() nu este de ajuns.
PoC:
<?php
$validat = htmlspecialchars(
strip_tags($_GET['val']),
ENT_QUOTES,
"utf-8"
);
echo $validat;
echo '<a href="#" rel="'.$validat.'">link</a>';
Acum puteți vedea în exemplul prezentat validarea corectă a datelor, precum și un link.
De ce am pus linkul? Ca să prezint ce s-ar fi intamplat daca ați fi folosit doar funcția strip_tags() (puteți experimenta înlăturând apelul la htmlspecialchars()). Puteți testa atacul setând $_GET['val'] la valoarea:
" onmouseover="java script:alert(/xss/)
Un alt exemplu unde e necesară filtrarea:
<?php
echo $_SERVER['PHP_SELF'].PHP_EOL.
htmlspecialchars(strip_tags($_SERVER['PHP_SELF']), ENT_QUOTES, 'utf-8');
Ideea e că trebuie validat orice poate fi manipulat de utilizator. Un simplu:
/<script>alert(/xss/)</script>
în $_GET['val'] arată că scriptul este vulnerabil la XSS.
Notă: nu uitați că și $_POST poate fi populat cu date dăunătoare. La fel și $_SERVER['USER_AGENT'], dacă este folosit pentru a distribui conținut bazat pe browserul utilizatorului sau orice altă variabilă care este populată de client.
- SQL Injection
Definiție si informații aici.
[ http://imgs.xkcd.com/comics/exploits_of_a_mom.png - Pentru incarcare in pagina (embed) Click aici ]
Funcțiile utilizate pentru filtrare, verificare sunt (doar ca exemplu) addslashes() si get_magic_quotes_gpc().
Verificare, filtrare date:
<?php
$input = $_GET['val'];
if (!get_magic_quotes_gpc()) {
$valid = addslashes($input);
} else {
$valid = $input;
}
echo $valid. //cum va fi inserat in baza de date
PHP_EOL.
stripslashes($valid); //cum să-l printam 'curat'
După cum se vede în exemplu, aplicăm funcția addslashes() asupra datelor doar în cazul în care directiva magic_quotes = off în php.ini, deoarece dacă e activată, php aplică în mod automat filtrul addslashes() pe date.
Important de menționat ar fi că funcția addslashes() e recomandată DOAR atunci când nu aveți una specifică de tipul *_escape_string() pentru baza de date utilizată. Dacă sunteți interesați de ce addslashes() nu e de ajuns citiți următorul articol: addslashes versus mysql_real_escape_string.
Un alt sfat ar fi acela că în momentul în care știți că vă trebuie un număr, nu caracter sau string, fortați ca ceea ce primiți să fie ceea ce vreți.
<?php
$valid = (int) $_GET['val']; //(integer) aceași chestie
//...
$query = 'SELECT * FROM `tabel` WHERE id='.$valid;
- SQL Column Truncation
Este o vulnerabilitate descoperită recent, deși există de mult precum CSRF, și sunt multe aplicații care sunt vulnerabile la acest tip de atac. O explicație mai clară, dacă nu o stiți deja aici -> mysql and sql column truncation vulnerabilities.
Remedierea e mai simplă decât s-ar crede, doar trebuie să aducem datele la lungimea dorită.
<?php
$input = $_GET['val'];
$valid = substr($input, 0, 15); //doar de 16 caractere avem nevoie
echo $valid;
- Remote/Local File Inclusion
Definiție și exemple aici și pe web. Nu am găsit informații (în grabă) despre local file inclusion și log injection.
Cum ar fi bine să ne includem fișierele; nu că ar fi bine să folosim astfel de incluziuni pentru prezentarea conținutului, dar dacă chiar sunteți nevoiți folosiți următorul exemplu orientativ:
<?php
$id = (int) $_GET['id'];
$page = array(
'home.php',
'news.php',
'shop.php',
'contact.php'
);
if ($id > (count($page)-1) ) { //în caz că se solicita o pagină inexistentă
include($page[0]);
} else {
include($page[$id]);
}
- Redirect
De multe ori este necesară modificarea headerelor HTTP cu header(), cel mai adesea pentru a produce redirecționari (cod HTTP 302). Există două aspecte la care trebuie totuși să fim atenți când facem asta.
Primul este lipsa terminării execuției după header() - vezi sfatul III. 8. pentru a afla cum poți face cereri HTTP manuale.
<?php
if (!$_SESSION['auth']) { //în cazul în care nu e authentificat
header('Location: login.php');
}
echo 'micul meu secret';
Un browser "normal" cu siguranță nu va afișa "micul meu secret", dar o simplă cerere http făcută manual de un user cu un client tcp (telnet aici) o va face (vezi III. 8.), deoarece nu interpretează headerele http. Rezolvarea ar fi folosirea funcției exit sau punerea lui echo în ramura else a condiției:
<?php
if (!$_SESSION['auth']) { //în cazul în care nu e authentificat
header('Location: login.php');
exit();
}
echo 'micul meu secret';
A doua problemă cu folosirea funcției header() o constituie așa-numitul atac HTTP Header Injection. Împiedicarea lui e ușoara, iar începând cu PHP 5.1.2/4.4.2 acest tip de atac este blocat automat. În cazul în care folosiți o versiune PHP mai veche va trebui să vă verificați manual headerele, fie cu expresii regulare, fie cu funcțiile cType.
- PHP-IDS
O implementare facilă a protecției împotriva tuturor atacurilor menționate puteți avea prin intermediul aplicației PHP-IDS, care permite utilizatorului chiar și să trimită cod valid html fără niciun risc asupra aplicației/serverului, monitorizând de asemenea toate datele periculoase.
- AJAX
Aceste sfaturi trebuiesc urmate și în cazul tranzacțiilor HTTP asincrone. Gândiți-vă că acel client poate fi orice, inclusiv un client telnet, nu trebuie să fie neapărat un browser "de încredere".
- PoC
IFilter.php
<?php
class IFilter {
public $on, $sql;
public function __construct($on='_GET', $sql='mysql') {
$this->on = $on;
$this->sql = $sql;
}
public function xss($ids) {
global $ {$this->on};
foreach($ids as $id) {
$ {$this->on}[$id] = htmlspecialchars(
strip_tags(
$ {$this->on}[$id]
),
ENT_QUOTES,
"utf-8"
);
}
}
public function sqli($ids) {
global $ {$this->on};
foreach($ids as $id) {
if (function_exists($this->sql.'_real_escape_string')) {
$ {$this->on}[$id] = call_user_func($this->sql.'_real_escape_string', $ {$this->on}[$id]);
}
elseif(function_exists($this->sql.'_escape_string')) {
$ {$this->on}[$id] = call_user_func($this->sql.'_escape_string', $ {$this->on}[$id]);
}
else {
if (!get_magic_quotes_gpc()) {
$ {$this->on}[$id] = call_user_func('addslashes', $ {$this->on}[$id]);
}
}
}
}
public function tint($ids) {
global $ {$this->on};
foreach($ids as $id) {
$ {$this->on}[$id] = (int) $ {$this->on}[$id];
}
}
public function truncate($ids, $length=0) {
if ($length==0) {
return false;
}
global $ {$this->on};
foreach($ids as $id) {
$ {$this->on}[$id] = substr($ {$this->on}[$id], 0, $length);
}
}
}
?>
utilizare:
demo.php
<?php
$validator = new IFilter();
$validator->xss(array('id', 'cat')); //aplica functiile anti xss pe $_GET['id'] si $_GET['cat']
$validator->on = '_POST';
$validator->tint(array('sub')); //aplica typecast pe $_POST['sub'];
$validator->truncate(array('titlu'), 20); //se extrage substring de 20 caractere din $_POST['titlu'];
$validator->sqli(
array(
'autor',
'comment'
)
); //mysql_real_escape_string pe $_POST['autor'] si $_POST['comment']
$validator->sql = 'mssql';
$validator->sqli(
array(
'autor',
'comment'
)
); //addslashes
- Linkuri utile
VI. Thread-uri utile despre probleme comune
- Paginare: Start, Inapoi, Urmator, Final
- Why use Exceptions?
- cum aflu numele calculatorului si al utilizatorului - aflarea utilizatorului și al numelui calculatorului pe un site intranet (LAN) ce folosește windows și apache pe server
- poligoane rotunjite in php - (autor: florindinu)
- Ce face header()?
- Problema de securizare - despre cum să securizezi transmiterea datelor sensibile catre un client Flash.
- SQL injection - cum se evită corect?
- Seria "Code practices"
Oricine poate posta un "(better/worst) Code practices - Issue #<X>", cu condiția ca <X> să nu existe deja și să fie cât de cât un echilibru între numărul de "better" și de "worst". Dacă cineva găsește greșeli universal valabile într-un thread "better", atunci el devine autorul acelui număr din această serie
- (worst) Code practices - Issue #1 - Cum să nu generezi HTML static cu PHP și cum să nu validezi inputuri.
Autor: OriginalCopy
- (better) Code practices - Issue #2 - templating engines (smarty), unul "home-made", minimal, discuții aprinse despre MVC și avantaje/dezavantaje. Discuții la nivel înalt.
PS: "discuție la nivel înalt" înseamnă aici: există argumente bune și pro și contra, deci nu există o concluzie finală, ci este o discuție în care tot ce contează e înțelegerea argumentelor de o parte și de alta, pentru a putea decide mai târziu ce și cum faci în funcție de prioritățile proiectului respectiv.
Autori: OriginalCopy, păreri valoroase din partea (în ordinea cronologică a primului răspuns): MaxMax, urban, cobru
- (worst) Code practices - Issue #3 - un exemplu din categoria "AȘA NU", greu de descris ce conține, și exemple de bug-uri, SQL injection. Cod luat de pe un pastebin, deci unii chiar așa programează în PHP. Exemplifică ceea ce un utilizator @ softpedia / php nu ar trebui să facă niciodată.
Autor: OriginalCopy
- (better) Code practices - Issue #4 - Subiecte tratate:
- validarea formularelor și procesarea lor în același fișier PHP
- folosirea în mod efectiv de variabile intermediare și decizionale
- sesiuni
- separarea algoritmicii ("business logic") de afișare (output format)
- protejarea împotriva XSS
Autor: adormitu, l-a "detronat" pe OriginalCopy prin găsirea unor găuri de securitate - (worst) Code practices - Issue #5 - include() la mâna oricui
Autor: vim
- (better) Code practices - Issue #6 - validare în masă
Autor: OriginalCopy
- Concursuri
Oricine poate crea un concurs - totuși trebuie să fie demn de aria Programming, că nu vrem să dăm puncte ușor, nu?
Fiecare concurs are un punctaj acordat pentru fiecare soluție. O soluție bună constă în
- A: găsirea problemei
- B: formularea tehnic corectă a problemei
- C: rezolvarea problemei cu cod
Un concurs de punctaj 1 înseamnă că se acordă 1 punct pentru A, 1 pentru B, 1 pt. C
VII. Cărți
- The rough Guide to the Internet, Rough Guides, 14th ed.
Citeste cartea asta mai intai, daca nu ai o fundatie robusta in retelistica (si nu o ai daca nu ai citit o carte in domeniu).
De ce trebuie sa intelegi cum functioneaza Internetul? Motivul e simplu: cu PHP creezi aplicatii pentru web, web care este unul dintre serviciile Internet. Este inadmisibil sa programezi pe o platforma (Internetul in cazul de fata) fara sa o intelegi mai intai, si in plus PHP pune la dispozitie o gramada de extensii pentru alte servicii Internet (ftp, ssh, svn, s.a.m.d.).
Resurse alternative
Stiu ca unora dintre voi va este greu (din diferite motive) sa cumparati aceasta carte, de aceea am creat urmatoarea lista de articole pe care le consider bune.
- Trebuie sa va fie clar un lucru: nu am stat sa citesc cu atentie aceste articole. Am citit cuprinsul si cateva pasaje, si am ajuns doar pe baza acestui lucru la concluzia ca probabil sunt bine scrise.
- Inca un sfat: cel mai probabil aceste articole nu sunt complete. Ele iti vor oferi doar o imagine de ansamblu. Cand dai de un termen nou, te poti duce pe http://en.wikipedia.org si introduce termenul in casuta de cautare din stanga, pentru un articol mai detaliat.
- Si inca un sfat: probabil vei da de articole unde se vorbeste despre structura pachetelor TCP/IP (termen relevant: raw socket) sau alte lucruri "avansate". Este ok sa stii astfel de lucruri intr-adevar low-level, insa nu te vei lovi de ele in PHP decat daca vrei sa faci lucruri cu adevarat avansate (de ex. raw sockets sunt utili daca vrei sa scrii o aplicatie gen traceroute in PHP).
- Am incercat sa ordonez cat de cat articolele, deci ar trebui parcurse in ordine (nu strict, dar cat de cat)
- majoritatea articolelor (in afara de primele cateva) abordeaza detalii de nuanta despre Internet, Web, anumite "tehnologii", Legislatie, etc. Cu adevarat importante sunt primele articole, voi spune mai jos care.
- In PHP poti exersa lucrul cu Internetul folosind sockets: php.net/sockets (termen relevant: network programming)
- Articolele 1-10 explica in mare mai tot ce este fundamental. Toti incepatorii ar trebui sa le parcurga cel putin pe astea, INAINTE de a incepe programare web (in PHP sau nu). Inca o data: cand te lovesti de un nou termen, cauta-l pe wikipedia (dar nu in romana, ci in engleza).
- Articolele 11-21 intra in anumite detalii de care cel mai probabil te vei lovi in viata ta de programator web
- Articolele 22-24 sunt mai mult pentru amuzament
- Cred ca majoritatea articolelor sunt de pe "Howstuffworks" din motive melancolice: imi amintesc ca si eu le-am citit candva, demult (nu pe toate). Cred insa (si sper) ca s-au mai schimbat de atunci (in bine).
- How Does the Internet Work?
Un articol scurt care ofera o imagine de ansamblu buna
- How Internet Infrastructure Works
- Understanding how the Internet and the Web works, for PHP programmers
Ca exercitiu la articolele 1 si 2, probabil vei vrea sa faci experimentul practic pe care l-am explicat pe blogul meu
[va rog inchideti ochii pentru spam; dincolo de gluma, chiar il consider un exercitiu bun]
- What's the difference between the Internet and the World Wide Web?
- How Web Servers Work
- Who owns the Internet?
- What are the standard top-level domain names and who controls them?
- How Domain Name Servers Work
- Where are all the Internet domain names registered and maintained?
- Is it against the law to violate a Web site's terms of service?
- How Internet Search Engines work
Aici vreau doar sa mentionez ca "Internet Search Engine" este folosit colocvial, si pur tehnic vorbind, incorect (vezi articolul 4. de mai sus). Corect ar fi fost: "Web Search Engine".
- Why are there so many broken links in search engines?
- How Firefox Works
Nu stiu daca toti dintre voi stiu asta, dar Firefox ESTE cel mai bun browser pentru dezvoltarea de aplicatii web, deoarece:
- respecta standardele W3C (vezi wikipedia despre asta), iar standardele sunt bune, ne scapa de frustrare
- are cateva extensii indispensabile oricarui programator web: firebug, web developer, YSlow.
- How RSS Works
- Why is the Google algorithm so important?
- Why do some Web sites include www in the URL while others don't?
- Why does my Web browser display some book-marked sites with a custom icon?
- Why do some Web pages end in htm, html, asp, etc.?
- How Wikis Work
- How Spam Works
- How Streaming Video and Audio Work
- World Records: Internet
- Internet Quiz
Este chiar usor, deci nu te bucura prea mult chiar daca iei peste 70%
- Fact or Fiction: the Internet
- Dezvoltarea aplicatiilor web cu PHP si MySQL, Teora, ed. II (sau mai bine versiunea in engleza PHP and MySQL Web Development, 4th ed., Laura Thomson).
Poti citi in paralel cu primele capitole topicul: Sfaturi de programare în PHP
Cand ajungi la capitolele despre MySQL, poti citi in paralel cartea de la punctul 4.
- PHP in Action: Objects, Design, Agility, Manning Publications
Aici poti citi in paralel o carte despre UML.
- MySQL, 2 ed., Paul Dubois, Sams Publishing
Probabil nu sunt singurele "cele mai bune carti", dar le-am tinut in mana si garantez ca sunt bune.
Am sarit peste HTML, CSS si Javascript. Stie cineva o carte (sau mai multe) buna? Trebuie sa explice bine DOM-ul, iar la CSS sa explice box model.
VIII. Bloguri
Incearca sa citesti constant blogurile programatorilor dezvoltatori ale lui PHP insusi, programatori cu certificare Zend in PHP5, sau programatori care au publicat in reviste de specialitate, de exemplu in PHP Magazine sau PHP|Arch sau care participa la proiecte mari, cunoscute, scrise in PHP.
Multi programatori, chiar daca satisfac criteriile de mai sus, publica rareori explicatii sau metodologii practice (hands-on), eventual cu exemple de cod. Dar poti cauta oricand in arhiva sau sorta dupa taguri.
Voi incerca sa fac o lista de bloguri care contin articole interesante despre PHP, dar am nevoie de ajutorul vostru. Daca stiti un blog care contine mai multe articole interesante, in care sunt descrise lucruri de la simple la complexe, care se incadreaza in criteriile de mai sus, dar mai ales unde lucrurile sunt explicate foarte bine, fara a neglija terminologia, atunci:
pur si simplu posteaza link cu o descriere a autorului.
Totusi: asta nu este un loc unde sa-ti faci reclama la blog. - Sara Golemon, angajata Yahoo!, autoare a Extending and Embedding PHP, participa la dezvoltarea PHP, libssh, si cateva extensii in PECL.
- Ilia Alshanetsky, a fost release manager PHP, diferite publicatii in magazine cunoscute.
- Tobias Schlitt, dezvolta si menteneaza pachete PEAR, dezvolta ez Components.
- Stefan Esser, expert in securitate, a contribuit la PHP pana in 2006, lucreaza la Hardened PHP Project.
- Dagfinn Reiers?l, autor PHP in Action.
- Andrei Zmievski, dezvoltator principal al PHP, contribuie, menteneaza sau a inventat: apache, smarty, PHP-GTK.
- Brandon Savage, web developer, are cateva articole foarte interesante pentru incepatori si mediu-avansati
IX. Cursuri
Prin curs se înțelege: existența unor materiale educative, cât și a unor oameni experimentați care se uită permanent peste umărul cursanților și îi îndrumă.
- Gratuite
- Cu plată
- mă poate ajuta cineva cu sugestii?
Au contribuit: ecstrim, florindinu, adormitu, vim, luk4
și mulți alții prin întrebările lor care m-au făcut să mă gândesc la multe lucruri.
Completări, corecturi, întrebări, critică constructivă sau mulțumiri sunt ca de obicei binevenite.
După ce s-a ajuns la o concluzie finală, discuția va fi ștearsă și concluzia mutată în postul inițial. Așa se poate păstra totul într-un loc și este ușor de urmărit și de citit de oricine vizitează această arie aproape săptămânal, cât și de cei care citesc acest topic pentru prima oară.
|
|