Se afișează postările cu eticheta Java. Afișați toate postările
Se afișează postările cu eticheta Java. Afișați toate postările

marți, 13 noiembrie 2012

Câteva cuvinte despre algoritmi în Java şi PHP (introducere)

O binecunoscută butadă relevă că în spatele oricărui „succes peste noapte” stau zece, cincisprezece sau chiar douăzeci de ani de muncă asiduă. Şi, deşi oricine cunoaşte acest lucru, foarte mulţi continuă să creadă în existenţa unor minuni care îi vor face să dobândească succesul instantaneu. Fac acest lucru din două motive: primul, fiindcă partea cu „munca asiduă” este extrem de neplăcută – deşi „nemunca” eîncă şi mai neplăcută, iar al doilea, fiindcă minunile evocate chiar se întâmplă! În domeniul material, de exemplu, prin intermediul loteriilor, al sistemelor de pariuri, al jocurilor de noroc, mulţi, foarte mulţi oameni au realizat câştiguri fabuloase! Cu adevărat fabuloase! Şi ... mulţi, cu adevărat mulţi: cam unul la zece, sau o sută de milioane de „ghinionişti”. 

Într-un celebru eseu din 2001, Peter Norvig, director de cercetare la Google Inc. îşi exprima speranţa că un om talentat şi interesat ar putea învăţa să programeze bine în doar zece ani! Şi încă singur! Articolul e plin de idei bune, dar trebuie tratat cu o oarecare circumspecţie, fiindcă, de atunci au trecut, totuşi, zece ani, şi lucrurile s-au mai schimbat, pe ici, pe colo, în sensul că „materialul de studiu” a devenit mult mai bogat, mai stufos, dar şi prin faptul că, pe alocuri, autorul pare a se contrazice: „lucrează la proiecte cu alţi programatori”. În alte cuvinte, învăţarea „de unul singur” trebuie să se producă, totuşi, „împreună”. 

Limba şi cultura în care ne-am născut au o influenţă majoră asupra formării noastre ca oameni, asupra „Weltanschauung”-ului nostru. Desigur, în timpul vieţii putem invăţa o nouă limbă, sau mai multe, putem să ne apropiem de o altă cultură, sau de mai multe, iar dintr-o astfel de experienţă putem realiza că paralelismul limbilor naturale este imperfect, că, pentru a exprima o anumită idee, sau concept, o limbă este mai potrivită decât alta şi că, pentru a crea o literatură, o bogată şi valoroasă literatură, este nevoie de o limbă evoluată şi puternică. 

Nu altfel stă treaba şi în ceea ce priveşte limbajele de programare, acestea constituind interfeţele prin care programatorul şi calculatorul pot comunica. Aşa cum nu toate limbile sunt la fel de potrivite pentru a exprima un concept, o idee, sau un termen concret – spre exemplu, ne putem imagina că, pentru un zulus este, în general, mult mai greu, decât pentru un yakut, să înţeleagă, să definească, să „poetizeze” termeni precum „ger pătrunzător” sau „permafrost” – tot aşa limbajele de programare, chiar cele de cel mai general uz, prezintă unele caracateristici care le fac mai potrivite sau mai puţin potrivite pentru o anumită aplicaţie sau/şi un anumit programator. 

În fapt, limbajele de programare, şi, mai general, softurile de creare de obiecte şi aplicaţii sunt orientate spre un anumit aspect, cum ar fi: creare de documente, imagini, sunete etc., sau creare de aplicaţii de uz ştiinţific, de uz economic, de uz didactic, pentru realizarea de comunicaţii etc. De asemenea, limbajele respective sunt create în legătură cu o paradigmă de programare. 

În spatele oricărui program de calculator stă un anumit algoritm. Sau, mă rog, mai mulţi algoritmi. Ce e un algoritm? 

Cuvântul provine din limba persană şi reprezintă varianta latinizată a numelui unui matematician foarte important, din secolul IX. De fapt, reproduce acea parte din nume care face referire la localitatea de baştină a savantului: Muḥammad ibn Mūsā al-Khwārizmī (particula „ibn” înseamnă „fiul lui”, iar „al” face referire la locul de baştină) –azi, localitatea se numeşte Khiva şi este un oraş cu vreo 50.000 de locuitori, situat în oaza Khwarezm, provincia Xorazm, Uzbekistan. 

La modul cel mai general cu putinţă, un algoritm reprezintă o metodă, o modalitate de rezolvare a unei probleme. Iar activitatea de programare are exact acest obiectiv, şi anume rezolvarea de probleme cu ajutorul calculatorului. 

Prin urmare, aşa cum învăţarea unei limbi ne poate facilita comunicarea, transmiterea unor idei, dar nu ne este de niciun folos în absenţa acestor idei, tot aşa, un limbaj de programare este foarte util pentru a „înfăşura” un anumit algoritm într-un „ambalaj” care să poată fi înţeles de către calculator, dar nu ne este de niciun folos dacă nu avem ce algoritm să „înfăşurăm”. 

Algoritmica este, aşadar, ştiinţa pe care trebuie să o studiem şi să o înţelegem înainte de a ne apuca, propriu-zis, de programare, şi fără de care nu se poate vorbi de niciun fel de programare. Pentru că, până la urmă, un program nu este o succesiune de linii de cod, corecte din punct de vedere sintactic, dar care nu rezolvă, de fapt, nicio problemă, ci transpunerea, într-un limbaj de programare, a unui algoritm de rezolvare a unei probleme. Iar asta înseamnă, de fapt, 99% matematică. 

La începuturile ştiinţei calculatoarelor, se cunoşteau nenumărate exemple de diverşi algoritmi, dar o ştiinţă sistematizată a algoritmilor nu exista, propriu-zis, şi, din acest motiv, fiecar programator îşi crea programele mai mult intuitiv, în funcţie de „fler”, intuiţie şi experienţă. Fiecare program păstra „amprenta” creatorului său, şi, din acest motiv, erau greu de înţeles şi depanat de către un alt programator. 

 Acest „stil” de programare a condus destul de repede la o criză a acestei activităţi. Pe măsura creşterii dimensiunii lor, programele au devenit din ce în ce mai greu lizibile şi, ca urmare, costurile legate de depanarea şi actualizarea lor sau/şi de creare a noi programe au crescut exponenţial. Trebuia făcut ceva, iar acest „ceva” s-a numit programarea structurată. 

Ideea de bază a programării structurate este cea exprimată în celebra teoremă de structură a lui Böhm-Jacopini, din 1966, care afirmă, grosso-modo, că, pentru a crea orice program de calculator, sunt suficiente doar trei structuri, şi anume cea secvenţială, cea de decizie şi cea de ciclare. Şi, de asemenea, că orice program poate fi rescris în aşa fel încat să nu mai utilizeze instrucţiunide salt, cum ar fi „goto” (sau „jump”, în alte limbaje). 

În acele vremuri, un program era, de fapt, un text compus dintr-un anumit număr de linii (instrucţiuni, propoziţii, enunţuri). Fiecare linie instruia calculatorul asupra unei acţiuni pe care o avea de efectuat, îi furniza o anumită informaţie etc. – aici, lucrurile nu sunt fundamental diferite nici astăzi. Toate aceste linii, sau măcar o parte din ele erau etichetate, ceea ce înseamnă că li se asocia un anumit simbol, de obicei un număr natural, dar şi un text scurt, în alte situaţii, iar programul conţinea linii care forţau calculatorul să continue nu cu execuţia următoarei linii, ci cu o alta, cu o etichetă precizată. 

Programarea structurată a impus eliminarea acestor instructiuni de salt, iar efectul imediat a fost acela de creare a unor programe mult mai simple, mai clare şi mai uşor de depanat. Entuziasmul şi beneficiile create de această paradigmă au fost atât de mari, încât profesorul Niklaus Wirth, de la Universitatea din Zürich, Elveţia a creat, în 1972, un limbaj de programare special pentru învăţarea programării structurate, pe care l-a denumit Pascal, in cinstea marelui savant şi filosof Blaise Pascal. Extrem de popular, la un moment dat, în mediile educaţionale, limbajul Pascal şi-a atins, cu prisosinţă, obiectivul pentru care a fost creat, dar, din păcate (sau nu), nu a devenit foarte popular în industria de software, datorită, în principal, multiplelor sale limitări „didactice”. Oricum, datorită simplităţii sale şi modului în care introduce elevul în învăţarea principiilor programării structurate, limbajul Pascal este utilizat, în continuare, în mediile şcolare şi chiar universitare din România.

 „Biblia” programării structurate este, până în zilele noastre, lucrarea din 1972, a profesorilor O.J. Dahl (Oslo), E.W. Dijkstra (Eindhoven) şi C.A.R. Hoare (Belfast), intitulată Structured Programming. 

Un alt progres a fost reprezentat de apariţia programării procedurale. Ce este programarea procedurală? 

Programatorii au observat relativ curând că, în decursul unui program erau siliţi să repete, identic sau aproape identic, anumite secvenţe de cod, corespunzând unor operaţiuni efectuate de mai multe ori, cum ar fi, de exemplu, ordonarea unui şir, aşa după cum, de exemplu, se repetă refrenul în textul unui cântec. Şi atunci, soluţia firească găsită a fost aceea a creării unor programe de dimensiuni mai reduse, care să îndeplinească anumite operaţiuni frecvente, şi care să poată fi apelate (invocate) folosindu-le doar numele, şi nu rescriind tot codul. Astfel de progrămele – de fapt, unele, deosebit de complexe – au fost denumite - în funcţie de faptul că returnau un rezultat, sau efectuau doar o prelucrare, precum şi în funcţie de limbajul respectiv – funcţii, proceduri, metode, (sub)rutine etc. au uşurat foarte mult munca programatorilor. Mai mult decât atât, limbajele de programare au început să conţină, în ele însele, biblioteci sau module de funcţii (metode) de dimensiuni absolut impresionante – e vorba de mii si mii de funcţii, care „fac” o mulţime de lucruri deosebit de utile şi necesare. 

Apariţia programării procedurale uşurează enorm munca programatorilor (ori de câte ori nu o complică într-un mod inacceptabil), dar mai realizează un lucru foarte important: permite reutilizarea unor componente ale unor programe. În alte cuvinte, până la programarea procedurală, regula era: o problemă = un program, altă problemă = alt program. De la programarea procedurală încoace, regula este, de fapt, alt problemă = acelaşi program, uşor modificat. 

Următorul pas a fost reprezentat de programarea pe obiecte. Ideea programării pe obiecte a pornit din observarea naturii înconjurătoare, în care nu există „date” sau informmareaţii „pure”, ci numai asociate unui anumit obiect. În alte cuvinte, în natură nu întâlnim lungimi, pur şi simplu, ci lungimi ale unor obiecte, mase, ci mase ale unor obiecte, şi aşa mai departe. Acestea nu mai sunt, aşadar, noţiuni abstracte, ci devin atribute concrete ale unor obiecte. Apoi, orice obiect „ştie să facă” ceva, ceva ce un alt obiect „ştie” sau nu: de exemplu iepurele „sare”, pisica „miaună”, iar brânza „pute”; acestea sunt metodele obiectelor respective. În plus, obiectele pot fi clasificate după modul cum derivă unele din altele, clasificare inspirată din taxonimia inventată de botanistul suedez Carl Linnaeus (von Linné). Astfel, de exemplu, iepurele este un „rozător”, „rozătoarele” sunt, la rândul lor, „mamifere”, „mamiferele”, „vertebrate”, şi aşa mai departe, iar unele din „metodele” iepurelui, de exemplu aceea de a „roade” nu este o „invenţie” proprie, ci este comună întregului grup, pe când metoda „este drăguţ” îi aparţine în totalitate, celorlalte rozătoare (şoareci, şobolani etc.) aplicându-li-se, mai degrabă „metoda” „este respingător”. 

Dar, mai pe larg despre programarea pe obiecte vom mai avea ocazia să vorbim. 

Toate aceste paradigme ţin de programarea imperativă, sau programarea cu reguli. Este vorba de acea programare în care calculatorului „i se spune”, in mod clar, ce trebuie să „facă”. În fapt, într-o astfel de perspectivă, calculatorul este „luat de prost”, în sensul că i se comunică destul de puţine lucruri sau chiar deloc, despre problema pe care urmează să o rezolve, accentul căzând pe ceea ce urmează să facă, în mod efectiv. Sunt, însă, şi abordări care „umanizează” foarte mult calculatorul, privindu-l (aproape) ca pe o fiinţă umană, cu gânduri, sentimente, preferinţe şi abordări proprii. Dintr-o astfel de perspectivă, calculatorului nu i se mai „dau ordine”, ci i se comunică „probleme”, şi el însuşi construieşte/alege o metodă de rezolvare etc. Practic, însă, o asemenea abordare mi se pare forţată. Calculatorul nu este, nu poate să fie „creativ”, în mod „natural”, dar poate deveni astfel dacă i se „impune” acest lucru. În alte cuvinte, tot la programare imperativă ajungem. 

Spuneam, în cele de mai sus că, pentru a rezolva o anumită problemă, algoritmul este determinant, iar limbajul de programare în care decidem să implementăm acel algoritm contează, dar nu chiar aşa mult. 

Îmi propun ca, în continuare, să exemplific modul de funcţionare al algoritmilor, folosind, preponderent, două limbaje extrem de cunoscute, şi anume Java şi HP. 

Java este un limbaj de programare extrem de popular, dezvoltat la inceputul anilor ’90, de către James Gosling şi echipa sa de la Sun Microsystems (între timp compania a fost achiziţionată de Oracle). De la bun început, Java a fost concepută ca un limbaj universal, ceea ce înseamnă că poate fi rulat fără niciun fel de probleme pe tot felul de calculatoare, telefoane mobile, alte dispozitive inteligente etc., cu condiţia ca acestea să aibă instalată un program specific, numit Java Virtual Machine (JVM). Cum, practic, toate sistemele de operare conţin această maşină virtuală, rezultă că un program scris în Java poate fi rulat pe aproape toate maşinile de calcul. 

Java este un limbaj puternic, robust, tipizat, complet orientat pe obiecte şi cu o colecţie impresionantă de biblioteci de metode. Este conceput în trei variante principale („ediţii”), şi anume Standard Edition, care permite crearea de aplicaţii standalone (menite a rula pe un singur calculator), dar şi de appleturi, care sunt programe ce pot fi incorporate în diverse situri web, majoritatea browserelor moderne fiind capabile să opereze cu astfel de programe. Java Enterprise Edition este destinată creării de software industrial, adică de aplicatii distribuite, rulând pe mai multe calculatoare si accesând baze mari de date. În fine, Java Mobile Edition este destinată creării aplicaţiilor menite a rula, îndeosebi, pe dispozitive de tipul telefoanelor mobile etc. Popularul sistem de operare Android, dezvoltat de Google, este scris în Java şi menit să lucreze împreună cu aceasta. 

Şi, poate cel mai important aspect, este open-source, aceasta însemnând că poate fi descărcat oricând de pe internet, în mod gratuit, împreună cu toate bibliotecile sale, cu IDE-ul propriu NetBeans – deşi la fel de bine se poate folosi Eclipse, sau orice altceva. 

Limbajul Java este compilat şi interpretat. Aceasta înseamnă că programele scrise în Java sunt compilate de programul specific javac, din orice distribuţie de JVM, şi transformat în programe aşa-numite bytecode, care sunt independente de maşina de calcul, putând fi, apoi, mai departe, interpretate. Această caracteristică are o oarecare influenţă negativă asupra performanţelor de viteză ale programelor scrise în Java, influenţă pe deplin compensată de alţi factori cum ar fi, de exemplu, portabilitatea. 

Prima variantă a limbajului PHP a fost lansată în 1994 de programatorul suedez Rasmus Lerdorf. Iniţial, a fost vorba de un set de scripturi Perl, create pentru a-şi întreţine pagina personală de web. De aceea şi denumirea a fost, la început „Personal Home Page”. Ulterior, programatorii evrei Zeev Juravski şi Andi Gutmans, au rescris în intregime codul iniţial şi au redenumit limbajul în „Hypertext Preprocessor”, aşa cum este cunoscut până în prezent. Ulterior, cei doi au fondat compania Zend Company, care a dezvoltat următoarele variante ale limbajului. 

Dintru început, PHP a fost conceput ca un limbaj destinat, în special, creării şi dezvoltării de situri web. PHP este, alături de alte limbaje, „responsabil” de apariţia şi dezvoltarea siturilor web dinamice, adică a acelor situri car permit un mare grad de interactivitate cu utilizatorul, aceasta presupunând existenţa unor opţiuni ale utilizatorilor precum înr postarea de conţinuturi înrgistrarea, postarea de conţinuturi, transmiterea de mesaje, in timp real, altor utilizatori etc. Pentru a realiza toate aceste operaţiuni, PHP are nevoie să interacţioneze cu un mediu de gestiune a bazelor de date, aşa cum este, de exemplu MySQL. 

Cuplul PHP & MySQL este folosit pe scară largă la crearea şi întreţinerea unor situri precum Wikipedia, Facebook, Google sau Youtube. 

 Pentru a „vedea” un site web, un utilizator are nevoie, in primul rând, de un program numit browser (ex: Internet Explorer, Google Chrome, Firefox, Opera, Safari, Sea Monkey etc.), care îi permite să identifice şi să manipuleze fişiere aflate pe alte calculatoare, în conformitate cu URL-ul acestora (Uniform Resource Locator), acesta fiind un cod care „descrie” browserului adresa/locul unde se află fişierul respectiv şi cum poate fi el accesat. Pe baza acestor informaţii, browserul încearcă să identifice şi să afişeze conţinutul fişierului precizat. Dacă acel fişier conţine doar text sau cod nativ HTML, atunci el este recunoscut şi afişat ca atare de browser, altfel el trebuie interpretat în prealabil, iar de acest lucru este responsabil un program special numit server, care trebuie să existe în calculatorul apelat. Sunt mai multe tipuri de servere, dintre care cel mai popular este Apache, dezvoltat de o comunitate deschisă de programatori sub emblema Apache Software Foundation.

Apache, MysQL şi PHP sunt programe open-source, ceea ce înseamnă că, pentru a le descărca şi utiliza nu trebuie plătit niciun ban! De aceea, cele mai populare medii pentru crearea / dezvoltarea / întreţinerea siturilor web sunt LAMP, WAMP sau/şi MAMP (unde prima literă înseamnă Linux, Windows sau MacOS, A corespunde serverului Apache, M vine de al MySQL, iar P de la PHP – dar poate însemna, la fel de bine, şi Python sau Perl). 

PHP nu a fost conceput, de la bun început, ca un limbaj de programare universal, şi nici ca un limbaj orientat pe obiecte, dar a dobândit, intre timp, aceste caracteristici. Oricum, în PHP este posibilă atât abordarea stilului de programare structurat, cât şi a celui orientat pe obiecte. PHP este un limbaj interpretat, ceea ce permite o mare versatilitate a tipurilor de date. 

În cele ce urmează, îmi propun să prezint o serie de rezultate legate de proiectarea algoritmilor, urmând ca implementările corespunzătoare să fie efectuate, aproape in totalitate, intr-unul din cele două limbaje de mai sus, Java şi PHP.

vineri, 26 octombrie 2012

Emularea polimorfismului în PHP


        Supraîncarcarea (overloading) si redefinirea/suprascrierea (overridding) reprezinta cele doua forme de manifestare ale polimorfismului, unul din cele trei concepte-cheie ale paradigmei obiectuale în programare.
        Supraîncarcarea reprezinta capacitatea unei functii/metode sau a unui operator de a se adapta dinamic unui anumit context. Pentru a întelege mai bine acest concept, voi folosi o metafora din lumea înconjuratoare. Sa presupunem ca un „obiect medical” este înzestrat cu „metoda” „acorda primul ajutor”. În functie de contextul în care se aplica, aceasta metoda poate însemna cu totul si cu totul altceva, de exemplu masaj cardiac, respiratie artificiala, oprirea hemoragiei, sau multe alte lucruri. În viata de toate zilele spunem ca expresia sau cuvântul care defineste „metoda” noastra este polisemantic – are mai multe întelesuri, care depind, evident, de context. Tot astfel, în programarea orientata pe obiecte, o functie – voi folosi, în continuare, acest termen, deoarece el este uzitat în PHP – este supraîncarcata daca „stie” sa faca mai multe lucruri, în functie de context. Aceasta presupune fie faptul ca poate primi parametri de diferite tipuri, fie ca poate primi un numar variabil de parametri, fie amândoua. Sa presupunem, de exemplu, ca avem o functie scurteaza() care, daca primeste un parametru numeric real, extrage partea sa întreaga (deci scurteaza(3.14) == 3), iar daca primeste un string, elimina spatiile goale (deci scurteaza( ”un text oarecare”) == ”untextoarecare”) – acesta este un exemplu de functie care „stie” sa faca lucruri diferite în functie de tipul parametrului primit. Un exemplu de functie care accepta un numar variabil de parametri este usor de imaginat, de exemplu o functie aduna() ar putea fi conceputa astfel încât sa accepte oricâti termeni.
        Suprascrierea reprezinta capacitatea unei functii mostenite a unui obiect de a face altceva decât functia similara din clasa parinte. De exemplu, considerând clasa ”lumea vie”, vom constata ca trebuie sa îi implementam o functie de „hranire”, fiindca orice instanta a ei – adica orice organism viu – are aceasta functie. Presupunând ca am alcatuit o ierarhie de clase în care clasa lumea_vie a fost extinsa succesiv, astfel încât a rezultat o ierarhie de clase precum lumea_vie => lumea_animala => vertebrate => mamifere => hominide => om, vom constata ca functia „hranire” a clasei „om”, chiar daca „mosteneste” anumite caracteristici (=comportament) functia similara a clasei parinte, trebuie, totusi, sa îl modifice, pentru a implementa „abilitatea” omului de a se hrani cu alimente procesate, ca si pentru a renunta la unele deprinderi precum a ceea de a scuipa cojile semintelor pe jos. În acest sens, în functie de modul în care este conceputa, functia „hranire” a omului va prelua o parte din „codul” functiei „hranire” a hominidelor, va renunta la alta parte, si va modifica o alta parte. Putem sa ne imaginam, de asemenea, ca din clasa „lumea_vie” deriva si clasa „lumea_vegetala”, care, bineînteles, trebuie sa aiba definita si ea functia „hranire”. Numai ca diferentele între ceea ce se întelege între a „hrani” o planta (de ex. a o stropi, de a o orienta spre soare etc.) si a „hrani” un animal sunt atât de mari, încât, poate ca nu exista niciun element comun, care sa poata fi mostenit atât de clasa „lumea_animala”, cât si de clasa „lumea_vegetala”. În acest caz, solutia este aceea a definirii unei functii abstracte (si implicit, declararea ca abstracta a clasei „lumea_vie”), fara niciun fel de cod, lasând claselor derivate responsabilitatea de a defini, în mod concret, functia „hranire”. În alte cuvinte, functia „hranire” nu spune si nu face nimic despre ea însasi în clasa „lumea_vie”, dar obliga orice „obiect” de acest fel sa aiba o astfel de functie, care sa fie (re)definita în clasa de care apartine: toate organismele vii (= instante ale unor clase concrete derivate din clasa „lumea_vie”) sunt „obligate” sa se „hraneasca”, dar, ce înseamna, la modul concret, pentru fiecare organism viu în parte, este definit la nivelul clasei de care apartine.
        Dupa cum se stie, PHP nu a fost conceput, de la bun început, ca un limbaj obiectual, ci ca unul structural, adica unul in care codul este scris respectând paradigma programarii structurate Böhm-Jacopini. De aceea, creatorii limbajului PHP au ales sa îl faca loosely-typed, pentru un spor de versatilitate si eficienta. Pe de alta parte, aceasta optiune a facut, practic, imposibila, supraîncarcarea functiilor, cel putin pe baza tipului parametrilor.
        Sa consideram urmatorul exemplu:
<?php
class test {
    public function afiseazaMesaj1($un_parametru) {
        echo "Apelul functiei pentru un parametru: $un_parametru! <br/>";
    }

    public function afiseazaMesaj2($par1, $par2) {
        echo "Apelul functiei pentru doi parametri: $par1 $par2";
    }
}

$obj1 = new test;
$obj1->afiseazaMesaj1('Salut');
$obj1->afiseazaMesaj2('Salut,','lume!');
?>
        Dupa cum, probabil va asteptati, acest exemplu functioneaza perfect în PHP, producând iesirea:
Apelul functiei pentru un parametru: Salut! 
Apelul functiei pentru doi parametri: Salut, lume!
        Desigur, în acest exemplu nu este vorba de niciun polimorfism, ci, pur si simplu, de doua functii diferite, afiseazaMesaj1() si afiseazaMesaj2(), prima primind un parametru, cealalta doi parametri.
        Modificând putin acest exemplu, în sensul redenumirii functiilor afiseazaMesaj1() si afiseazaMesaj2() cu acelasi nume, afiseazaMesaj(), ca în exemplul de mai jos:
<?php
class Test {
    public function afiseazaMesaj($un_parametru) {
        echo "Apelul functiei pentru un parametru: $un_parametru! <br/>";
    }

    public function afiseazaMesaj($par1, $par2) {
        echo "Apelul functiei pentru doi parametri: $par1 $par2";
    }
}

$obj = new test;
$obj->afiseazaMesaj('Salut');
$obj->afiseazaMesaj('Salut,','lume!');
?>
primim eroarea fatala:
Fatal error: Cannot redeclare test::afiseazaMesaj() in ....
asta în timp ce codul similar din din Java:
public class transpusPHP {
 public static void main(String[] args) {
  transpusPHP obiect = new transpusPHP();
  obiect.afiseazaMesaj("Salut!");
  obiect.afiseazaMesaj("Salut, ","lume!");
 }
 public void afiseazaMesaj(String par){
  System.out.println(par);
 }
 public void afiseazaMesaj(String par1, String par2){
  System.out.print(par1);
  System.out.println(par2);
 }
}
functioneaza fara cusur si returneaza:
Salut!
Salut, lume!
        Cum putem rezolva aceasta problema?
        O prima idee este aceea de a folosi o functie cu parametri impliciti. PHP permite utilizarea functiilor cu parametri impliciti, al caror antet arata astfel:
function ($par_1, ..., $par_i, $par_j = val_j, ... , par_n = var_n){
//cod PHP
}
        Apelul unei astfel de functii se face pentru cel putin i parametri ($par_i este ultimul parametru care nu este definit implicit) , si pentru cel mult n. Daca parametrii care urmeaza dupa par_i (par_j si celialti) nu sunt definiti în mod explicit, atunci se folosesc valorile implicite corespunzatoare, iar daca, la un moment dat, un parametru este definit implicit, toti parametrii care urmeaza (pâna la n) sunt definiti implicit
        Cu ajutorul parametrilor impliciti, am putea implementa supraîncarcarea astfel:
<?php
class Test { 
    public function afiseazaMesaj($par1='', $par2='', $par3='') {
  if ($par3){
   echo "Apelul functiei pentru trei paramentri: 
/**/ $par1 $par2 $par3 <br/>";
  } elseif ($par2) {
   echo "Apelul functiei pentru doi parametri: 
/**/ $par1 $par2<br/>";
  } elseif ($par1) {
   echo "Apelul functiei pentru un parametru: 
/**/ $par1 <br/>";
  } else {
   echo "Nu a fost transmis niciun parametru <br/>";
  }
 }
}

$obj = new Test;
$obj->afiseazaMesaj('Salut');
$obj->afiseazaMesaj('Salut,','lume!');
$obj->afiseazaMesaj('Primul parametru, ', 'Al doilea, ', 'Al treilea');
$obj->afiseazaMesaj();
?>
        Efectul executiei programului este urmatorul:
Apelul functiei pentru un parametru: Salut 
Apelul functiei pentru doi parametri: Salut, lume!
Apelul functiei pentru trei paramentri: Primul parametru,  Al doilea, Al treilea 
Nu a fost transmis niciun parametru
        Principalele probleme pe care le ridica o astfel de abordare sunt urmatoarele:
  • Daca o un parametru nu este definit, sau are o valoare egala cu cea implicita, atunci toti parametrii care îi urmeaza primesc valorile implicite.
  • Nu putem, în acest fel, sa implementam functii care accepta oricât de multi parametri.
        O alta abordarea poate viza utilizarea functiei magice __call(), ca în exemplul urmator:
<?php
class Test
{
    public function __call($nume, $parametri)
    {
  $nr_parametri = sizeof($parametri);
  if ($nr_parametri){
   if ($nr_parametri == 1) {
    echo "Apelul functiei $nume pentru un 
/**/singur parametru: ";
   } elseif ($nr_parametri == 2) {
    echo "Apelul functiei $nume pentru 
/**/doi parametri: ";
   } else {
    echo "Apelul functiei $nume pentru 
/**/$nr_parametri parametri: ";
   } 
  } else {
   echo "Nu ati introdus niciun parametru.";
  }
        
  foreach ($parametri as $cheie => $valoare){
    echo $valoare;
  }
  echo "<br/>";
    }   
}
$obj = new Test;
$obj->afiseazaMesaj('Salut, ','lume!');
$obj->afiseazaMesaj('Salut!');
$obj->afiseazaMesaj('Salut, lume!');
$obj ->afiseazaMesaj('Primul parametru ', 'Al doilea parametru ', 
/**/'Al treilea parametru');
$obj->afiseazaMesaj();
?>
        Programul va afisa:
Apelul functiei afiseazaMesaj pentru doi parametri: Salut, lume!
Apelul functiei afiseazaMesaj pentru un singur parametru: Salut!
Apelul functiei afiseazaMesaj pentru un singur parametru: Salut, lume!
Apelul functiei afiseazaMesaj pentru 3 parametri: Primul parametru Al doilea parametru Al treilea parametru
Nu ati introdus niciun parametru.
        Functia __call() nu este o functie sistem, si prin urmare nu poate fi apelata precum functiile sistem. Dimpotriva, __call este un identificator care permite programatorului sa construiasca o functie proprie, în care sa indice ceea ce trebuie sa se întâmple în cazul în care programul încearca sa acceseze, în mod nepermis, o functie-membra a unei clase, respectiv o functie care nu exista (nu este definita).
        În exemplul de mai sus, functia afiseazaMesaj() nu exista (nu este definita) pentru un obiect de tip Test. Încercarea de a o apela transmite functiei __call() cei doi parametri solicitati: primul parametru (case-sensitive) fiind chiar numele functiei apelante, iar al doilea, un sir indexat numeric, ale carui componente sunt chiar parametrii cu care a fost apelata functia apelanta.
        De exemplu, apelul:
$obj->afiseazaMesaj('Salut, ','lume!');
determina transmiterea catre functia __call() a doi parametri si anume stringul $nume = ’afiseazaMesaj’ si array-ul $parametri([0]=>’Salut, ’, [1]=’lume!’). Dupa cum se vede, functia __call() nu utilizeaza, în acest caz, parametrul $nume, dar, în cazul în care sunt mai multe functii al caror comportament trebuie astfel descris, functia __call() va utiliza si acest parametru.
        În ceea ce priveste suprascrierea functiilor, aceasta functioneaza normal, faraniciun fel de probleme, ca în exemplul de mai jos:
<?php
class Bunic{
    public function afiseaza() {
        echo "Afisare din clasa Bunic<br/>";
    }
}


class Parinte extends Bunic{
    public function afiseaza() {
        echo "Afisare din clasa Parinte<br/>";
    }
}

class Copil extends Parinte { 

    public function afiseaza() {
  echo"<br/> Afisare din toate clasele<br/>";
  Bunic::afiseaza();
  Parinte::afiseaza();
        echo "Afisare din clasa Copil<br/>";
    }
}


$obj0 = new Bunic;
$obj1 = new Parinte;
$obj2 = new Copil;

$obj0->afiseaza();
$obj1->afiseaza();
$obj2->afiseaza();
?>
        Iesirea este urmatoarea:
Afisare din clasa Bunic
Afisare din clasa Parinte

Afisare din toate clasele
Afisare din clasa Bunic
Afisare din clasa Parinte
Afisare din clasa Copil
        Avem, asadar, o clasa Bunic, care este extinsa succesiv de clasa Parinte si clasa Copil. În clasa Bunic avem definita o functie afiseaza(), care afiseaza un mesaj corespunzator clasei respective. În clasa Parinte, redefinim aceasta functie, astfel incât sa afiseze un mesaj diferit. De remarcat ca functia afiseaza() din clasa Parinte este o functie complet noua, care nu refoloseste nimic din codul corespunzator din clasa Bunic. Pe de alta parte, functia afiseaza() din Copil reutilizeaza atât codul corespunzator din Bunic, cât si cel din Parinte.