Cum sa extragi parametrii unei functii in PHP?
6O problema destul de interesanta atunci cand vine vorba de interpretarea codului, este analiza parametrilor. In acest articol voi prezenta o modalitate, simpla, dar eficienta de a interpreta parametrii unei functii, clase initializate samd. Pentru a usura mai mult subiectul tratatat, am decis ca toti parametrii trebuie sa fie de tipul array(), de dimensiune nedefinita iar numarul acestora sa fie nelimitat.
Problemele intampinate
Evident, ca orice idee, aceasta poate avea „buguri”. Cel mai usor ar fi dupa unii sa rezolvam problema folosind o expresie regulata, sau un split dupa „,” samd. Nici una dintre aceste probleme nu poate fi abordata eficient pentru ca in primul caz, expresia regulata nu poate fi generalizata, iar in al doilea caz (problema care se aplica si la primul), virgula poate fi prezenta si in interiorul array-ului (daca acesta este unidimensional).
Abordarea corecta
Ideea care in aceasta situatie mi se pare cea mai eficienta, atat din punctul de vedere al complexitatii – O(n), cat si din punctul de de vedere al dificultatii de implementare este o abordare ce deriva dintr-un algoritm destul de simplu – evaluarea unei expresii in vederea corectitudinii ei.
Una din abordarile acestei probleme presupune numararea parantezelor deschise (adunarea lor) si eliminarea celor inchise (scaderea lor) atat timp cat acest lucru este posibil (valoarea respectiva depaseste zero). Modificand putin problema, vom avea urmatoarea abordare :
function computeParams($params) { //O(n) $np = array(); $open = 0; $content = ''; $i = 0; $n = strlen($params); $k = 0; while($i < $n) //cat timp indicele nu a depasit lungimea sirului { //daca avem nevoie de un nou parametru, il cautam if($open <= 0 && stripos($params,'array(', $i) !== FALSE) { //daca exista mutam indicele pe el, il initializam $i = stripos($params,'array(', $i)+5; $np[$k] = array(); $open = 1; $content = 'array('; } else //in caz contrar suntem intr-un indice ce incercam sa-l parcurgem { $content .= $params{$i}; // adaugam caracterul curent la parametrul actual(continutul acestuia) if($params{$i}=='(') $open++; // numaram parentezele deschise if($params{$i}==')') $open--; // scadem parantezele inchise if($open <= 0) $np[$k++] = trim($content); // daca valoarea parantezelor este mai mica ca zero am terminat parametrul } $i++; } return $np; }
Ok, avem parametrii acum, intr-un array de forma $ARR[x] = tot continutul parametrului al x-lea. (indicele incepand de la zero).
Urmeaza interpretarea acestor parametri. Spre exemplu dorim sa avem $ARR[x][setare] = valoare. Din nou, pentru a simplifica codul, am decis ca toate valorile parametrilor sa fie de forma cheie => valoare. Vom avea nevoie de o noua functie.
function computeParamsArgs($args) { //O(k + q) //incercam sa gasim toti parametrii, folosind expresia regulata de mai jos preg_match_all('/([\'"].*?[\'"]).*=>.*(array\(.*?\)|[\'"].*?[\'"]|true|false|0-9*)/is', $args, $args); //sizeof() = q $keys = $args[1]; //k = sizeof($keys), avem cheile parametrilor $vals = $args[2]; // avem valorile parametrilor $args = array(); //parcurgem fiecare cheie for($i = 0; $i < sizeof($keys); $i++) if(isset($vals[$i])) //daca o gasim valoarea cheii respective $args[trim($keys[$i],'\'" ')] = trim($vals[$i],'\'" '); // o parsam (fara ghilimele) si o adaugam intr-un vector return $args; }
Evident, abordarea de mai sus se refera doar la primul nivel al array-ului. Nu trebuie sa uitam faptul ca aceasta functie va fi folosita doar pentru a parsa unul dintre cei n parametrii extrasi. Interesanta poate fi si modificarea functiei, facand-o recursiva pentru a parsa un array in adancime. Oricum, editarea ei e o chestie de vreo 2-5 randuri, deci poate fi usor rezolvata daca esti interesat.
Ok, dar ce ne facem daca dorim sa restauram codul in forma lui originala? Pentru asta vom avea nevoie de o alta functie ce se va folosi de vectorul nostru unidimensional pentru a restaura parametrii intr-un singur sir.
private function restoreParamsArgs($args) { //O(2n) $params = 'array('; foreach($args as $key => $value) //luam fiecare setare cu cheie/valoare { if(strtolower(substr($value, 0, 6))!='array(' && strpos($value,'\'') !== false) //daca nu e un array valoarea $value = '\''.str_replace('\'', '\\\'', $value[$i]).'\''; //ii adaugam ghilimele, avand grija sa le parsam pe cele interioare $args[$key] = '\''.$key.'\' => '.$value; //adaugam argumentele intr-un vector sub forma de sir de caractere } $params .= implode(',', array_values($args)); /lipim argumentele create $params.= ')'; return $params; }
Abordarea este simpla, doar ca si aceasta va necesita putina atentie din partea dumneavoastra in cazul in care veti dori sa parcurgeti array-urile in adancime pana la ultimul nivel.
Cum punem totul in miscare?
Simplu!!!
$params = "array('cheie' => 'valoare', 'cheie2' => 1000, 'cheie3' => array(valoare)), array('cheie2' => TRUE, 'cheie3' => FALSE)"; $ARR = computeParams($params); //construim parametrii (parsam) foreach($ARR as $key => $value) //interpretam cheile si valorile din parametrii $ARR[$key] = computeParamsArgs($value); print_r($ARR); $ARR[0]['cheie'] = 'valoare_modificata'; //modificam o valoare foreach($ARR as $key => $value) //restauram $ARR[$key] = restoreParams($value); $params = implode(','$ARR);//construim textul initial avand un parametru modificat echo $params;
Aceasta idee poate fi dusa la un nivel foarte mare, ajungand sa interpreteze adevarate blocuri de cod in acest mod. Totul tine de imaginatia ta. Enjoy! 🙂
Buna,
Iti recomand sa citesti documentatia de PHP referitoare la functiile eval(), func_num_args() si func_get_args(), iar pentru lucrul cu clase, informatiile legate de Reflection – se poate face mult mai usor cu ajutorul lor ceea ce acoperi tu in acest articol.
Salut! Sa vedem daca ai dreptate. 🙂
Documentatia despre recomandarile oferite de tine le voi sari pentru ca sunt familiar cu ce fac. Problema abordata aici nu cred ca ai inteles-o in totalitate, pentru ca eu m-am referit la extragerea parametrilor din codul unui fisier astfel : Tu ai un fisier sursa.php in care incepi sa iti extragi diverse definitii de forma :
$x = new $y(parametru1, parametru2, parametru3 samd);
Pentru asta, evaluand codul si extragand parametri asa cum ai sugerat tu vom avea de pierdut pentru ca in final va duce la evaluarea codului si posibilitatea alterarii unor informatii – de exemplu poate in momentul in care am evaluat linia de cod data exemplu mai sus, ea automat a facut niste modificari in fisiere, a actualizat o baza de date, a trimis niste informatii la un alt server samd, in principiu chestii care nu le doreai.
Articolul a prezentat o modalitate eficienta de a intepreta codul citindu-l efectiv, si nu evaluandu-l, treaba care ar duce la probleme. 🙂
Sper ca ai inteles la ce m-am referit. 🙂
nu am inteles in practica la ce ar putea sa-mi foloseasca functiile prezentate… mi-ar prinde bine un exemplu concret… multumesc!
Redneckcode detected.
Indus?
@Nicolae Vartolomei Cui i se adreseaza si ce vrea sa insemne? 🙂
@ant Exemplu concret in practica (unde am avut eu nevoie de o astfel de abordare).
Sa presupunem ca tu vrei sa intepretezi anumite parti de cod. Situatia de fata va cauta doar parametrii. Evident, ce am dat eu aici nu e tot codul, pentru ca asa ar fi floare la ureche.
Sa presupunem ca dorim sa modificam fisierul x.php in mod dinamic(actualizand informatiile din el).
Undeva, avem :
Cautand linia asta si folosindu-ne de prima functie pusa la dispozitie + a doua vom ajunge la un vector de forma :
$ARR[0][‘x’] = y;
Eu acum de exemplu, voi putea adauga un alt parametru : $ARR[0][‘p’] = 10 si voi restaura linia respectiva avand in fisier de acum
Problema e o chestie incipienta dintr-un viitor intepretor care il poti dezvolta chiar tu. 🙂
[…] editare Afla cand cineva ti-a citit mailul Introducere in Design Patterns – MVC (1, 2 si 3) Cum sa extrageti parametrii unei functii in PHP? Cum sa extrageti numele unei variabile in PHP? Memcached – o solutie NoSQL Curl Multithread […]