Claudiu Persoiu

Blog-ul lui Claudiu Persoiu


Archive for the ‘PHP’ Category

De ce este un limbaj ca PHP asa popular?

without comments

Da, PHP este probabil cel mai popular limbaj de programare server side pentru Web in acest moment. Si probabil va ramane asa pentru o vreme indelungata.

Este probabil cel mai bun!

Nu tocmai, dar nu te opri din citit…

De ce este atat de usor de invatat?

In primul rand, datorita sintaxei. Dar motivul pentru care este asa de popular nu este pentru ca ar avea cea mai buna sintaxa! Eu am inteles aproape imediat sintaxa cand am vazut primul exemplu de “hello world”, de ce? Pentru ca am studiat C la scoala, iar persoanele care vin din lumea Java au aceeasi senzatie.

Dar sa vedem cateva limbaje de programare care folosesc aceasta sintaxa: Java, C#, JavaScript, Perl, Go, PHP si, bineinteles, C si C++, adica aproape toate platformele si cam toata piata.

Dar spre deosebire de (majoritatea) limbajelor listate mai sus, este cel mai simplu pentru incepatori! Nu trebuie sa creezi o functie “main”, o clasa sau altceva, incepi direct!

Partea buna este ca poate sa fie atat de complex pe cat vrei sau cat ai nevoie. Acum exista clase si interfete similare cu Java, mostenire orizontala cu “traits”, programare functionala cu closures si functii anonime, generatori, iar, daca esti cu adevarat hipster, pana si goto!

Dar PHP nu suna chiar asa de rau…

Dar trebuie sa amintim un detaliu foarte important: PHP nu a devenit popular datorita caracteristicilor de mai sus, in schimb caracteristicile au aparut datorita popularitatii, chiar si “goto”. Nu inteleg de ce cineva ar adauga goto intr-un limbaj dupa mai bine de 10 ani de existenta, dar asta nu este treaba mea si nici nu este relevant pentru acest subiect.

Limbajul a devenit foarte popular cu versiunile 3 si 4. Pana cand a aparut un model decent de OOP era deja foarte popular! In special acum, de cand cu versiunea PHP 7, cand arata mult mai mult ca un limbaj decent de programare, dar acum 10 ani nu era aceeasi poveste.

Dar node.js probabil il va depasi!

Sigur ca da… va amintiti de Ruby? Python? Si altele…

Daca te gandesti la PHP, are un mare avantaj si dezavantaj totodata, este cat de “stateless” se poate.

Cu PHP ai impresia ca nu stii nimic despre lume, trebuie sa faci bootstrap (initializezi) totul de fiecare data. Avantajul este ca in cazul unui memory leak, in majoritatea cazurilor, este ok. Cand requestul se termina totul se va curata/elibera si nimanui nu-i va mai pasa. Si PHP avea multe memory leak-uri la inceput, pana sa devina foarte popular si nimanui nu parea sa-i pese.

Cand au inceput sa apara cron job-urile si alte procese de lunga durata, abia atunci a devenit o mare problema si a aparut nevoia de garbage collection explicit. Acesta a fost adaugat in versiunea 5.3 care aparut abia in 2009.

In mod normal, un proces care ruleaza continuu nu ar trebui sa fie un dezavantaj, dar morala povestii este ca nu este neaparat nici avantaj.

Nimeni nu va construi urmatorul Google cu el, dar macar niste blog-uri? Sau, si mai bine, niste magazine virtuale?

Nu este capabil de ceva precum motorul de cautare Google, si e ok pentru ca nu a fost facut pentru asta.

Un alt exemplu pentru “nu neaparat cel mai bun castiga” este ca WordPress este cea mai populara platforma de blogging.

Chiar mai mult, lumea foloseste WordPress in tot felul de moduri bizare, practic poti face orice cu el! Si nu ar trebui sa reporosam asta oamenilor care il folosesc, nu e ca si cum te uiti la ce piese sunt in cuptorul cu microunde atunci cand il cumperi, nu-ti doresti decat sa incalzeasca mancarea. Oamenii care forteaza limitele platformei WordPress fac la fel, ei pot sa faca ce au nevoie si nu exista un motiv bun pentru care sa nu o faca.

Ar trebui sa-l folosesc?

Sa luam doua exemple pozitive: Yahoo! si Facebook.

Yahoo! il foloseste de foarte mult timp in unele din produsele sale si, uneori, intr-o combinatie cu alte limbaje de programare.

Facebook l-a folosit de la inceput pentru ca este usor de invatat. Ei au impins limitele limbajului in mai multe randuri prin: HipHop for PHP si, mai recent, Hack, care a reprezentat si cea mai importanta motivatie din spatele PHP 7.

De ce atata deranj pentru un limbaj de programare? Pentru ca este usor de invatat si utilizat!
De obicei, este mai important sa lansezi “acum” decat sa ai ceva “perfect” mai tarziu, mai ales in universul world wide web.

Este un motiv foarte bun pentru care un limbaj care nu este in mod sigur “cel mai bun”, dar este usor de folosit este atat de popular online, in timp ce un limbaj de programare ca C, care este mult mai dificil de invatat, este atat de popular offline.

Cu alte cuvinte, chiar daca PHP nu este cel mai bun, este in general suficient de bun.

Written by Claudiu Persoiu

19 October 2017 at 8:46 PM

Posted in PHP

Tagged with

PHP6, doar o alta poveste… (PHP6 si cartile)

without comments

Gata, razboiul s-a terminat, PHP7 s-a lansat, iar PHP6 va ramane doar o legenda, o poveste a versiunii care nu a existat.

Dupa o vreme nu cred ca se mai astepta nimeni sa apara, au trecut totusi aproape 10 ani, dintr-un total de 20 de ani de viata pentru PHP.

Dar cum se poate ca o versiune atat de asteptata sa nu apara de loc?

Sa incepem cu inceputul, cand, acum aproximativ 10 ani, PHP devenea un limbaj “serios”. Dupa versiunea PHP5, model obiectual avea in sfarsit vizibilitate specifica pentru metode si proprietati, obiectele nu mai erau copiate, ci trimise prin referinta. Aceste facilitati au generat o multitudine de framework-uri obiectuale, care acum erau destinate si mediului enterprise, nu doar pentru site-uri micute.

In tot acest context exista o problema, internationalizarea.

Asa a aparut PHP6, trebuia sa fie un limbaj care sa foloseasca nativ Unicode, mai specific UTF-16. Asa totul se va procesa intr-un format unic si international.

Proiectul a fost pornit si intretinut de Andrei Zmievski. Poate astazi nu multi au auzit de Andrei, dar acum 10 ani era foarte popular pentru proiecte precum Smarty si PHP-GTK.

Dupa cativa ani proiectul a ramas intepenit, iar atunci a aparut PHP5.3. Acesta, desi aducea schimbari importante, nu era o versiune majora pentru ca inca mai era speranta ca intr-o zi va fi un PHP6.

Existau totusi niste oameni plini de speranta! Si mai ales, care au fost gata sa monetizeze speranta!

Fie ca unii au fost autori, iar altii editori, ei stiau un lucru legat de carti (in special de cele tehnice) ca au niste lucruri clare: un public tinta si o perioada in care este relevanta.

Dar sa analizam si niste rezultate.

PHP_6_and_MySQL_5_for_Dynamic_Web_Sites_Visual_QuickPro_GuidePHP 6 and MySQL 5 for Dynamic Web Sites: Visual QuickPro Guide (3rd Edition) (Peachpit Press – 2008) – Larry Ullman

Larry a scris mai multe carti, printre care: “PHP and MySQL for Dynamic Web Sites: Visual QuickPro Guide (2nd Edition)” si evident “PHP and MySQL for Dynamic Web Sites: Visual QuickPro Guide”.

Intamplator am avut ocazia sa rasfoiesc toate cele 3 carti, dar nu in ordinea cronologica ci pur intamplator am inceput cu “PHP and MySQL for Dynamic Web Sites: Visual QuickPro Guide (2nd Edition)”. Ce e interesant este ca similitudinea este izbitoare, practic a pus un 6 in titlu si in rest aproape ca nu a schimbat cartea de loc.

In general toate cartile Visual QuickPro Guide sunt carti pentru incepatori, iar de la o editie la alta lucrurile nu se schimba prea mult, doar se adapteaza.

Professional_PHP6_Wrox_Programmer_to_ProgrammerProfessional PHP6 (Wrox 2009) – Ed Lecky-Thompson, Steven D. Nowicki (Thomas Myer)

Interesant este ca in imagine apar 3 persoane, dar pe Amazon.com si pe Wrox apar doar doua nume, de asta am tinut sa-l trec separat, este vorba de Thomas Myer.

Ma intreb oare daca Thomas Myer a cerut sa dispara, sau a fost scos? E bizar sa ai numele pe o carte dar sa nu apari in lista de autori pe site-ul editurii.

Nu intamplator exista si o “Professional PHP5” – Ed Lecky-Thompson, Heow Eide-Goodman, Steven D. Nowicki, Alec Cove.

PHP_6_MySQL_Programming_for_the_Absolute_BeginnerPHP 6/MySQL Programming for the Absolute Beginner (Cengage Learning PTR – 2008) – Andrew B. Harris

Este intradevar o carte bizara, nu exista un echivalent de PHP5, ba chiar pe site-ul scriitorului nu mai exista alta carte de PHP: .

Exista oare un motiv? In cazul lui Larry Ullman era un alt subiect atins, un alt public tinta, dar in acest caz e doar ciudat, un singur autor, o singura carte de PHP si intr-o versiune fantoma, publicata in 2008.

PHP_6_Fast_and_Easy_Web_DevelopmentPHP 6 Fast and Easy Web Development (Cengage Learning PTR – 2008) – Matt Telles, Julie C. Meloni

Fara prea multa dificultate am gasit si: “PHP Fast & Easy Web Development” – Julie C. Meloni

Ciudatenia acestei carti este ca Matt Telles nu mai are alta carte de PHP, desi are de Python, C# si C++.

Ceva imi spune ca poate el a fost adus doar ca sa actualizeze cartea originala la noul context. Am crezut ca este doar o parere presonala, doar ca la o cautare pe Google am gasit ca este si Technical Reviewer pentru “PHP 6/MySQL Programming for the Absolute Beginner (Cengage Learning PTR)”, carte care a fost prezentata anterior.

Beginning_PHP_6_Apache_MySQL_6_Web_DevelopmentBeginning PHP 6, Apache, MySQL 6 Web Development (Wrox – 2009) – Timothy Boronczyk, Elizabeth Naramore, Jason Gerner, Yann Le Scouarnec, Jeremy Stolz

Cartea originala a fost evident: “Beginning PHP, Apache, MySQL Web Development” – Michael K. Glass, Yann Le Scouarnec, Elizabeth Naramore, Gary Mailer, Jeremy Stolz, Jason Gerner

Au iesit Michael K. Glass si Gary Mailer, iar in locul lor a intrat Timothy Boronczyk.

PHP a fost un limbaj foarte la moda in perioada respectiva, iar PHP6 a fost foarte asteptat.

Cum se poate totusi asta? Cum se poate sa existe atatea carti de la edituri importante pentru un limbaj de programare care nu a existat? Raspunsul este simplu: lacomie.

Exista doua explicatii posibile: ori autorul a incercat sa acapareze piata cu o noua versiune, ori editura a vrut sa forteze nota, in speranta de a capta piata. Probabil speranta era ca in momentul cand ar fi aparut, ar fi avut cartile deja disponibile pentru livrare.

Acesta este un alt motiv pentru care nu era un plan bun ca versiunea PHP NG sa devina PHP6, ar fi insemnat sa apara o versiune de PHP si sa aiba deja o serie de carti scrise, ba chiar scrise de cativa ani.

Pun pariu ca atunci cand s-a votat intre PHP6 si PHP7 erau niste editori plini de speranta.

In continuare, va urez lectura placuta!

Written by Claudiu Persoiu

22 February 2016 at 9:11 PM

Posted in PHP

Tagged with ,

Yet another PHP 20 blog…

with 2 comments

PHP 20 party

Acum 20 de ani, Rasmus Lerdorf a anuntat aparitia unui nou tool, numit PHP.

Eu am intalnit PHP in 2004, dornic sa fac paginile mele HTML mai dinamice. Cineva mi-a recomandat PHP pentru ca era foarte simplu si usor de folosit. Pentru ca instalarea parea destul de complicata, am folosit PHP Triad. In cateva minute am fost gata sa-mi incep experimentele si am fost cucerit de feedbackul instant oferit.

Pe atunci eram student si cursurile erau bazate pe Pascal si C/C++. Nu-mi placea Pascal pentru ca era deja depasit, iar C avea un sistem complicat de lucru cu memoria si in loc sa ma preocup de gandirea logica, trebuia sa ma preocup de alocarea de resurse.

Acest limbaj nou pentru mine avea sintaxa asemanatoare cu C, dar fara bataia de cap a managementului de resurse. Eliminand acest impediment, am reusit sa-mi dezvolt gandirea logica si chiar sa devin un programator mai bun in general, nu doar in PHP.

In perioada respectiva, PHP nu era considerat un limbaj “serios” de programare, mai degraba unul pentru incepatori. La unul din primele interviuri la care am mers, angajatorul mi-a spus pe un ton cicalitor: “PHP este o jucarie, haide la noi si o sa inveti programare adevarata in ceva serios, cum este FoxPro!” Acela a fost unul din momentele care m-au ambitionat sa ma fac programator PHP.

11 ani mai tarziu, inca pot spune ca este limbajul meu de suflet si ca probabil nu as fi avut o cariera ca programator fara el.

Care a fost prima ta interactiune cu PHP?

Written by Claudiu Persoiu

8 July 2015 at 3:02 PM

Posted in PHP

Tagged with

Collecting Hack

without comments

Stamp-Collection

Ceva ce nu am abordat in blog-ul anterior au fost colectile. Hack vine cu o varietate de colectii pentru organizarea de date.

Structurile de date reprezinta o parte fundamentala a unui linbaj de programare, pentru ca acestea vor constitui modul in care informatia circula in aplicatie.

Pana la versiunea 5, PHP avea un singur tip de colectii de date, denumit “array”. Acest tip de date poate sa aiba trei moduri: array, hash table sau o combinatie intre cele doua.

In PHP 5 au fost introdusi o serie de iteratori pentru a facilita constructia de structuri. Din pacate si structurile rezultate aveau scopul sa ofere posibilitatea de a accesa obiectele intr-un mod similar cu array-urile.

Abea in PHP 5.3 au aparut structuri de date cu adevarat diferite, cum ar fi SplStack, dar si multe altele.

Cu toate astea, structuri cum ar fi vectori si tupluri nu au aparut niciodata in mod nativ. Se pot construi, dar nu este simplu sau intuitiv.

Hack din HHVM a venit cu o alta abordare, o serie de colectii native care sunt gata de folosire.

Tipurile de colectii

Lista de colectii este:

  • Vector – lista ordonata folosind un index
  • Map – hash table tip dictionar
  • Set – lista care stocheaza doar valori unice
  • Pair – un caz particular de Vector care are doar doua elemente.

Vector, Map si Set au si cate un echivalent immutable (inflexibil si read-only). Acestia sunt: ImmVector, ImmMap si ImmSet. Scopul acestor tipuri de date este sa expuna informatii pentru citire, fara sa permita modificarea acestora. O colectie immutable se poate genera direct, folosind constructorul, sau folosind metodele toImmVector, toImmMap si respectiv toImmSet.

Chiar si mai mult, exista si o serie de clase abstracte pentru a implementa cu usurinta structuri similare:

 

Vector

Avantajul unui vector este ca va avea tot timpul cheile in succesiune, iar ordinea elementelor nu se schimba. Cand vine vorba de array nu este nici o modalitate simpla de a verifica daca ar trebui sa se comporte ca un hash table sau ca un vector. La vector, spre deosebire de hash table, valoarea cheii nu este relevanta, doar succesiunea elementelor si numarul lor sunt importante.

Sa luam un exemplu:

<?hh

function listVector($vector) {
     echo 'Listing array: ' . PHP_EOL;
     for($i = 0; $i < count($vector); $i++) {
          echo $i . ' - ' . $vector[$i] . PHP_EOL;
     }
}

$array = array(1, 2, 3);

listVector($array);

// eliminarea unui element din array
unset($array[1]);

listVector($array);

Rezultatul va fi:

Listing array:
0 - 1
1 - 2
2 - 3
Listing array:
0 - 1

Notice: Undefined index: 1 in ../vector.hh on line 6
1 -

Motivul este foarte simplu: count intoarce intr-adevar numarul de elemente, dar index-ul nu este garantat secvential. Atunci cand al doilea element din array a fost eliminat, numarul de elemente s-a redus cu unul, dar index-ul 1 a ramas nealocat, iar ultimul index este egal cu marimea, asa ca nu se va mai ajunge la el.

Sa luam acelasi exemplu folosind un vector:

<?hh
… 
$vector = Vector{1, 2, 3}; 

listVector($vector); 

// eliminarea unui element din vector
$vector->removeKey(1);

listVector($vector);

Asa cum am anticipat, rezultatul este:

Listing array:
0 - 1
1 - 2
2 - 3
Listing array:
0 - 1
1 - 3

Este de remarcat ca nu se poate folosi unset, pentru ca nu este o cheie cea care se elimina, ci elementul respectiv, iar urmatoarea valoare din vector ii va lua locul.

Un alt lucru important este ca, daca un index nu exista si incercam sa-l modificam, va aparea o exceptie de tip “OutOfBoundsException”.

Cateva exemple care vor genera exceptia anterioara:

<?hh 

$vector = Vector{1,2,3,4}; 

// va functiona pentru ca cheia nr 1 exista 
$vector->set(1, 2);

// nu va functiona pentru ca inca nu exista cheia 4
$vector->set(4, 5);

// nu va functiona din acelasi motiv
$vector[4] = 5;

// pentru a adauga un element nou se pot folosi doar metode care nu specifica cheia
$vector[] = 5;

// sau
array_push($vector, 5);

Pentru accesare de elemente problema de “OutOfBoundsException” ramane la fel. De exemplu, daca indexul 10 nu exista:

var_dump($vector[$unsetKey]);

Un alt caz mai special este atunci cand elementul nu exista, dar se foloseste metoda “get”.

var_dump($vector->get($unsetKey));

Exemplul anterior nu va genera o eroare, in schimb rezultat va fi “null” atunci cand nu exista cheia. Mi se pare bizar un astfel de comportament, pentru ca poate exista un element null in vector, iar rezultatul va fi acelasi.

Pentru a evita confuzia intre elemente care nu sunt definite si elemente care sunt null, exista o metoda speciala pentru a vedea daca exista cheia:

var_dump($vector->containsKey($unsetKey));

Scoaterea elementelor din vector se face folosind:

$vector->remove($key);

Sau, pentru scoaterea ultimului element:

$vector->pop();

Map

Intr-un hash table, fata de un vector, ordinea si numarul elementelor nu sunt foarte relevante. In schimb, asocierea cheie-valoare este foarte importanta. Din acest motiv, un Map se mai numeste si “dictionar” – pentru ca poti ajunge usor de la o cheie la o valoare, pentru ca sunt “mapate”. De acolo si denumirea de “Map”.

Implementarea HHVM va retine si ordinea in care elementele au fost introduse.

In PHP, echivalentului unui Map era un array asociativ.

Fata de Vector, Map are nevoie de o cheie care va ramane permanent asociata cu elementul, indiferent daca se vor scoate sau adauga elemente in colectie.

Functile array_push sau array_shift nu vor functiona pentru Map, pentru ca acesta nu trimit o cheie, iar asocierea cheie-valoare nu ar fi controlata:

<?hh 

$map = Map{0 => 'a', 1 => 'b', 3 => 'c'};

array_push($map, 'd');

array_unshift($map, 'e');

var_dump($map);

Va genera urmatorul rezultat:

Warning: Invalid operand type was used: array_push expects array(s) or collection(s) in ../map.hh on line 5

Warning: array_unshift() expects parameter 1 to be an array, Vector, or Set in ../map.hh on line 7
object(HH\Map)#1 (3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [3]=>
  string(1) "c"
}

Dupa cum se poate vedea, elementele nu au fost adaugate si fiecare din cazuri a generat cate un Warning.

Adaugarea efectiva se poate face folosind:

<?hh 

$map = Map{0 => 'a', 1 => 'b', 3 => 'c'};

// adaugarea unui element folosind sintaxa de array
$map['new'] = 'd';

// adaugarea unui element folosind metoda structurii
$map->set('newer', 'e');

var_dump($map);

Rezultatul va fi:

object(HH\Map)#1 (5) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [3]=>
  string(1) "c"
  ["new"]=>
  string(1) "d"
  ["newer"]=>
  string(1) "e"
}

Spre deosebire de Vector, pentru ca elementul care se schimba este strans legat de cheie, unset este o metoda valida de a elimina un element:

unset($map[$key]);

Structura are si o metoda pentru a elimina elementul cu o anumita cheie:

$map->remove($key);

In acest caz, nici una din optiuni nu va genera o eroare daca nu exista cheia.

Exceptia de “OutOfBoundsException” se aplica si aici pentru chei care nu sunt definite, dar la fel ca la Vectori, exista o metoda pentru a testa daca exista cheia:

$map->contains($key);

Similar cu Vector, exista o metoda care intoarce cheia setata sau null daca aceasta nu exista:

$map->get($key);

Pentru a ne asigura ca nu se genereaza o exceptie “OutOfBoundsException”, un Map nu ar trebui parcurs cu “for”, ci doar cu “foreach”.

Pentru ca metoda “pop” de la vector nu se bazeaza pe o cheie, nu exista in structura Map.

Set

Seturile au scopul de a pastra unicitatea valorilor. Pentru aceasta structura, valorile sunt restrictionate doar la tipurile scalare: string si integer.

Interfata pentru aceasta structura este mult mai simpla decat la Vector si Map, pentru ca scopul este mult mai limitat.

Pentru Set cheia nu poate fi accesata, dar este relevanta din alt punct de vedere.

Sa luam un exemplu pentru a evidentia:

<?hh
$set = Set{'a', 'b', 'c'}; 

foreach($set as $key => $val) {
     echo $key . ' - ' . $val . PHP_EOL;
}

Rezultatul va fi:

a - a
b - b
c - c

Cheia si valoarea sunt identice, un mod ingenios de a pastra si unicitatea.

Cu toate astea, operatiunea este transparenta, lucru care permite adaugarea de elemente fara a referentia o cheie:

<?hh

$set = Set{'a', 'b', 'c'};

array_push($set, 'd');

array_unshift($set, 'e');

$set[] = 'f';

var_dump($set);

Vom avea un rezultat similar cu cel de la vectori:

object(HH\Set)#1 (6) {
  string(1) "e"
  string(1) "a"
  string(1) "b"
  string(1) "c"
  string(1) "d"
  string(1) "f"
}

Chiar daca se pot adauga noi valori folosind operatorul [], acestea nu pot fi referentiate folosind acest operator:

<?hh

$set = Set{'a', 'b', 'c'};

echo $set['a'];

Va genera eroarea:

Fatal error: Uncaught exception 'RuntimeException' with message '[] operator not supported for accessing elements of Sets' in ../set.hh:5
Stack trace:
#0 {main}

Pentru scoaterea de elemente se poate folosi doar metoda nativa (remove) si metode care nu presupun referentierea de chei:

<?hh 

$set = Set{'a', 'b', 'c', 'd'}; 

array_pop($set); 

array_shift($set); 

$set->remove('b');

var_dump($set);

Rezultatul va fi:

object(HH\Set)#1 (1) {
  string(1) "c"
}

Fata de Vector si Map, metoda “remove” va primi valoarea, nu cheia de acces.

Pentru Set nu exista nici un fel de cheie de acces, deci cam tot ce putem sa facem este sa verificam daca un element exista, folosind “contains”:

$set->contains($value);

Metoda va returna o valoarea booleana, care arata daca elementul exista sau nu.

Pair

O pereche este o colectie cu doua elemente. Nu poate avea mai multe sau mai putine. Elementele sunt indexate la fel ca si Vectorul, printr-o cheie care in acest caz poate avea doar valorile 0 si 1.

Nu sunt multe de spus despre aceasta structura de date, pentru ca elementele nu se pot scoate, adauga sau inlocui. Acesta este si motivul pentru care nu exista un echivalent immutable, deoarece structura in sine nu este flexibila:

<?hh 

$pair = Pair{'a', 'b'}; 

foreach($pair as $key => $val) {
     echo $key . ' - ' . $val . PHP_EOL;
}

Rezultatul va fi:

0 - a
1 - b

O structura foarte simpla cu un scop foarte simplu.

Notiuni comune

Aproape toate structurile prezentare anterior au cateva metode si comportamente comune. Spun aproape toate, pentru ca Set si mai ales Pair, prin natura lor mai restrictiva, nu dispun de unele functionalitati pe care Vector si Map le au.

Filter

Este o functie de filtrare care vine din programarea functionala. Scopul este de a filtra o structura de date si de a genera una noua de acelasi fel, exceptie facand Pair, ca urmare a restrictiei legate de numarul de elemente. In PHP, echivalentul este array_filter.

Vector si Map au doua metode: filter si filterWithKey. Acestea accepta un argument de tip “callable”, cu alte cuvinte o functie:

<?hh 

$vector = Vector{'a', 'b', 'c', 'd', 'e'}; 

// eliminarea elementului cu valoarea 'a' 
$result = $vector->filter($val ==> $val != 'a');

// eliminarea fiecarui al doilea element folosind cheia
$result2 = $vector->filterWithKey(($key, $val) ==> ($key % 2) == 0);

var_dump($vector);
var_dump($result);
var_dump($result2);

Rezultatul va fi:

object(HH\Vector)#1 (5) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "c"
  [3]=>
  string(1) "d"
  [4]=>
  string(1) "e"
}
object(HH\Vector)#3 (4) {
  [0]=>
  string(1) "b"
  [1]=>
  string(1) "c"
  [2]=>
  string(1) "d"
  [3]=>
  string(1) "e"
}
object(HH\Vector)#5 (3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "c"
  [2]=>
  string(1) "e"
}

Dupa cum se poate observa, rezultatul functiei “callable” este tratat ca un bool si, in functie de acesta, elemente sunt adaugate in structura rezultata.

Map are un comportament identic cu cel de la Vector, diferenta fiind doar in natura cheilor.

Un lucru interesant este ca o colectie poate sa fie si immutable, pentru ca operatiunea nu modifica structura de la care a plecat, dar colectia va fi si ea de tipul structurii initiale:

<?hh 

$vector = Vector{'a', 'b', 'c'}; 

$vector = $vector->toImmVector();

// eliminarea elementului cu valoarea 'a'
$result = $vector->filter($val ==> $val != 'a');

var_dump($vector);
var_dump($result);

Rezultatul va fi:

object(HH\ImmVector)#2 (3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "c"
}
object(HH\ImmVector)#4 (2) {
  [0]=>
  string(1) "b"
  [1]=>
  string(1) "c"
}

Pair are si el aceleasi functii ca Vector si Map, dar comportamentul nu este identic, din cauza faptului ca un Pair poate sa aiba doar 2 elemente, nici mai mult nici mai putin. Din acest motiv, cand se filtreaza un Pair, rezultatul va fi ImmVector, adica o structura similara cu Pair, dar care nu are un numar exact de elemente:

<?hh 

$pair = Pair{'a', 'b'}; 

// eliminarea elementului cu valoarea 'a' 
$result = $pair->filter($val ==> $val != 'a');

var_dump($result);

Structura rezultata va fi:

object(HH\ImmVector)#3 (1) {
  [0]=>
  string(1) "b"
}

Set nu are decat metoda “filter”, pentru ca, asa cum am demonstrat anterior, cheile sunt identice cu valorile. Daca ar fi existat si o valoare cu chei, ar fi functionat similar.

Map

O alta functie provenita din limbajele functionale este “Map”. Aceasta are scopul de a modifica valorile unei structuri, folosind o functie, rezultatul fiind o noua structura de tipul celei initiale. In PHP, echivalentul este array_map.

Similar cu filter, Vector si Map au metodele comune: “map” si “mapWithKey”. Si in acest caz, accepta un argument de tip “callable”:

<?hh 

$vector = Vector {'a', 'b', 'c'}; 

$result = $vector->map($val ==> $val . $val);

$result2 = $vector->mapWithKey(($key, $val) ==> str_repeat($val, 1 + $key));

var_dump($vector);
var_dump($result);
var_dump($result2);

Rezultatul va fi:

object(HH\Vector)#1 (3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "c"
}
object(HH\Vector)#3 (3) {
  [0]=>
  string(2) "aa"
  [1]=>
  string(2) "bb"
  [2]=>
  string(2) "cc"
}
object(HH\Vector)#5 (3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(2) "bb"
  [2]=>
  string(3) "ccc"
}

Rezultatul functiei “callable” este noua valoare a elementelor din structura.

La fel ca si in cazul “filter”, o colectie immutable are ca rezultat o colectie immutable.

Tot similar cu filter este si faptul ca functia map, aplicata pe un Pair, va avea ca rezultat un ImmVector:

<?hh 

$pair = Pair{'a', 'b'}; 

$result = $pair->map($val ==> $val . $val);

var_dump($result);

Va avea ca rezultat:

object(HH\ImmVector)#3 (2) {
  [0]=>
  string(2) "aa"
  [1]=>
  string(2) "bb"
}

Conversie

Unele elemente pot fi convertite in alte tipuri:

din \ catre Vector Map Set Pair Array
Vector x x x x
Map x x x x
Set x x x
Pair x x x x
Array x x x x

La tabelul de mai sus se mai adauga cateva restrictii de structura:

1. Orice structura, cand se converteste catre Set, trebuie sa contina doar valori scalare de tip int sau string:

(Map{})->add(Pair {'a', new stdClass()})
    ->toSet();

Va genera eroarea:

Fatal error: Uncaught exception 'InvalidArgumentException' with message 'Only integer values and string values may be used with Sets' in …

2. Un Map, cand este convertit la orice alta structura in afara de array, isi va pierde cheile in majoritatea cazurilor.
Conversia de la array catre altre structuri se face folosind:

$vector = new Vector ($array);

In afara de Pair, toate structurile de mai sus au ca unic parametru al constructorului un element care implementeaza Traversable.

Concluzii

Hack aduce o noua perspectiva asupra celui mai popular tip de date din PHP. Motivul celor de la Facebook este unul simplu, optimizarea. Daca ai un comportament consistent, poti optimiza pentru structura respectiva. In PHP acest lucru nu este tocmai posibil, din cauza faptului ca un array in PHP poate sa fie orice fel de colectie.

Din punct de vedere al structurilor de date, mi se pare interesant sa ai astfel de tipuri de date. In framework-uri, de obicei exista structuri care emuleaza comportamentul colectiilor introduse de Hack. Spre exemplu, intr-un ORM, o colectie de obiecte este reprezentata in general ca un vector, pentru ca are scopul de a se itera asupra valorilor ei. Un obiect care reprezinta valorile unor campuri dintr-o tabela o sa fie o structura de tip Map, pentru ca valoarea campului este legata de denumirea campului.

Mi se pare foarte interesant nu doar faptul ca exista aceste structuri, dar si faptul ca exista interfete pentru a implementa unele noi.

Sper ca Hack sa influenteze PHP, aducand stucturi cu un scop bine determinat in limbaj.

Written by Claudiu Persoiu

30 May 2014 at 11:50 AM

Posted in PHP

Tagged with , , , , , , ,

Limbajul de programare Hack, apocalipsa PHP?

without comments

you-have-been-hacked

Introducere

Facebook a lansat acum aproape o luna limbajul de programare Hack.

De atunci, peste tot au inceput sa apara articole apocaliptice legate de acest limbaj si cum acesta va inlocui PHP. Titlul acestui articol a fost inspirat din “Will Hack Kill PHP?“.

Ce mi se pare si mai ciudat este ca a urmat un val de aprecieri negative legate de PHP, aparent Hack “repara” limbajul mai sus mentionat. Parerea mea este ca, in primul rand, limbajul ar fi trebuit sa fie “stricat” ca sa poata fi “reparat”.

Evident ca si PHP are multe lipsuri, la fel ca orice alt limbaj de programare, dar trebuie sa fie si un motiv pentru care este cel mai popular limbaj pentru Web. Pana la urma, Facebook l-a folosit atata timp, iar acum nu il schimba, incearca sa-l imbunatateasca… nu?

Un lucru este sigur, este probabil cel mai neinspirat nume. Atunci cand cauti “Facebook hack” gasesti orice altceva decat acest limbaj de programare…

Despre Hack

Hack ruleaza pe HHVM. HHVM este incercarea Facebook de optimizare a limbajului PHP prin Just In Time complication, ultima abordare de optimizare a limbajului. Practic, Facebook incearca sa-si reduca din costuri, optimizand interpretatorul de limbaj, iar acum un alt limbaj cu totul. Pana la urma, daca ne gandim la infrastructura Facebook, este normal sa faca asta. Chiar si o optimizare relativ minora duce la o reducere consistenta a costurilor.

Initial am crezut ca este o versiune “imbunatatita”, dar se pare ca este alt limbaj de programare, practic este un PHP cu ceva in plus!

Un mic tutorial al limbajului este la: http://hacklang.org/tutorial/.

Tutorialul nu prinde chiar toate facilitatile limbajului, mai multe detalii sunt la: http://docs.hhvm.com/manual/en/hacklangref.php.

Practic cam toate facilitatile care deosebesc Hack de PHP sunt de fapt optionale. Aproape ca poti scrie PHP si va functiona. Contrar asteptarilor, nu este obligatorie nici macar specificarea tipului de input/output pentru variabile.

Pentru ca pana la urma este un limbaj de programare, nu voi intra in detaliu legat de tot ce aduce nou. Acesta este mai degraba rolul unui carti, nu al unui articol.

Doresc sa subiliniez cateva lucruri care mi se par interesante.

Typechecker

In mod bizar, cel putin pentru inceput, la runtime tipul rezultatului chiar daca este trimis, nu este neaparat interpretat.

Sa luam un exemplu:

<?hh 

function a($a): void {
     return true;
}

echo a('a');

Acest exemplu va avea output… 1.

Practic, la runtime nu se verifica decat tipul de input, nu si cel de output.

Pentru a rula Typechecker-ul se adauga in directorul curent un fisier gol:

$ touch .hhconfig

Apoi se ruleaza:

$ hh_client

Dupa cum se poate vedea, tipurile de date se testeaza intr-un pas separat fata de runtime.

La rularea manuala de Typechecker, acesta va identifica toate inconsistentele. Scopul este ca acesta sa identifice problemele inainte de runtime, de exemplu cand se editeaza un fisier, nu cand ruleaza efectiv aplicatia.

Output-ul de la Typechecker este:

../test.php:4:9,12: Invalid return type
../test.php:3:17,20: This is void
../test.php:4:9,12: It is incompatible with a bool

Din pacate mai este de lucru la aceste feature. Daca incercam sa validam doar la runtime, folosind “type hinting” la fel ca in PHP, functia devine:

function a(int $a): void {
     return true;
}

echo a('a');

Output-ul de la Typechecker nu se schimba, dar la rulare vom obtine:

Fatal error: Argument 1 passed to a() must be an instance of int, string given in ../test2.php on line 5

Practic, Typecheckerul face ce nu face type hinting, iar cel din urma poate sa primeasca acum si argumente de tip scalar.

Chiar daca ar fi fost vorba doar de adaugarea de  argumente scalare, tot mi se pare o inbunatatire importanta.

Lambda operator

Este o formula mai degraba cunoscuta in limbajele functionale.

Un exemplu:

<?hh 

$sqr = $x ==> $x * $x;

echo $sqr(5) . PHP_EOL;

Rezultatul o sa fie, evident, 25.

Mi se pare un mod foarte interesant si lizibil de a ingloba logica de mici dimensiuni.

Folosind noua sintaxa, o functie poate intoarce si alta functie:

$add = $x ==> $y ==> $x + $y;

$result = $add(1);

echo $result(2) . PHP_EOL;

Rezultatul o sa fie 3.

Daca o variabila din interiorul unei expresii lambda nu se intalneste in interiorul functiei care o defineste, atunci acesta va prelua variabila din mediul in care expresia este declarata:

// variabila in scope-ul curent
$z = 5;

$addZ = $x ==> $x + $z;
// schimbat variabila in scope-ul curent
$z = 6;

// efectuat adunare
echo $addZ(1) . PHP_EOL;

Rezultatul o sa fie… 6!

Echivalentul in PHP ar fi:

$addZ = function ($x) use ($z) {
     return $x + $z;
}

Valoarea lui $z se ia din mediul in care functia este definita, nu este o referinta la variabila.

Evident ca, in cazul in care variabila care este preluata din scope este un obiect, ea va fi preluata prin referinta:

<?hh 

class a {         
     public function __construct(public string $x) {}         
     public function __toString() { 
          return $this->x; 
     }
}

// variabila in scope-ul curent
$z = new a('Claudiu');

$addZ = $x ==> $x . ' ' . $z . '!';

// schimbat variabila care se va folosi la concatenare
$z->x = 'World';

// rulat concatenare
echo $addZ('Hello') . PHP_EOL;

Rezultatul va fi:

Hello World!

Shapes

Din nou o sintaxa mai des intalnita in limbaje functionale. Scopul este de a valida un tip de structura mai specific decat un array.

Motivul este foarte bun, valiadarea de structuri de date simple. Structurile care se verifica ar trebui sa contina elementele care au fost definite in shape.

<?hh 

// definirea unei structuri 
newtype Circle = shape('radius' => int, 'b' => int);

// functie care foloseste tipul structurii de mai sus
function areaCircle(Circle $param) {
     return M_PI * $param['radius'] * $param['radius'];
}

// o serie de shape-uri care folosesc structura
$circle = shape('radius' => 10);
$cilinder = shape('radius' => 10, 'height' => 15);

// o structura care nu ar trebui sa functioneze cu Circle
$sqr = shape('side' => 10);

echo areaCircle($circle) . PHP_EOL;
echo areaCircle($cilinder) . PHP_EOL;
echo areaCircle($sqr) . PHP_EOL;

Outputul este:

314.15926535898
314.15926535898

Notice: Undefined index: radius in /home/brand/test.hh on line 6

Notice: Undefined index: radius in /home/brand/test.hh on line 6
0

Un pic dezamagitor, speram ca parametrul care nu se potriveste cu structura sa genereze o eroare, dar se pare ca va trece mai departe.

Nici Typechecker-ul nu gaseste nimic in neregula.

Initiativa este foarte buna, acum trebuie doar sa asteptam varianta functionala.

Concluzie

Probabil Hack va influenta PHP, lucru normal pana la urma, se intampla asta tot timpul cu limbajele de programare.

Va inlocui PHP? Nu cred, probabil vor fi multi care il vor adopta pentru a reduce costurile si pentru o mai buna structura a codului.

Este foarte putin probabil ca acest limbaj sa aiba succes in urmatorii ani, in afara de proiecte de dimensiuni medii si mari. Pentru proiecte mici, in general se foloeste shared hosting, iar acesta de obicei nu are ultima versiune de PHP, putin probabil sa foloseasca ultima versiune de HHVM. Acesta poate nu este cel mai interesant argument, dar pana la urma cele mai multe site-uri de pe web sunt mici si foarte mici.

Un mod multi mai simplu de optimizare este sa folosesti HHVM si atat. Teoretic nu trebuie sa schimbi nimic si rezultatele se vor vedea imediat! Practic HHVM nu este 100% compatibil cu Zend Engine, dar probleme de compatiblitate se rezolva progresiv cu fiecare versiune. Una din prioritatile HHVM este sa intepreteze codul la fel ca Zend Engine, dar sa fie mult mai eficient!

Written by Claudiu Persoiu

29 April 2014 at 10:15 PM

Posted in PHP

Tagged with , ,

PHP 5.6 awesomeness!

with 2 comments

Nici nu a inceput bine anul si o noua versiune de PHP se pregateste de lansare. La data cand scriu acest blog, este in alpha 2. Se poate descarca de aici: snaps.php.net.

Sunt o serie de imbunatatiri interesante, dar astazi voi vorbi doar despre 3 dintre ele: constant scalar expressions, variadic functions si arguments unpacking.

Constant scalar expressions

Aceasta prima imbunatatire este si singura care nu avea un echivalent inainte de aceasta versiune. Iar, cand spun un echivalent, ma refer doar la clase.

Pana acum, constantele definite folosind cuvantul cheie “const” nu permiteau nici un fel de expresie, se putea folosi doar o valoare scalara, ex:

const INT_CONST = 1;

const STRING_CONST = "string";

const HEREDOC_CONST = <<<'EOT'
nowdoc
EOT;

Evident, mai este si varianta mai interesanta, care permite setarea de constante cu valori dinamice:

define('DYN_CONST', $variable);

Partea proasta este ca acest tip de constanta nu se poate defini pentru o clasa, deoarece pentru aceasta operatiune se poate folosi doar const.

Acum insa se poate folosi:

const ONE = 1;
const TWO = ONE * 2;
const STR = "string of one and two :" . ONE . TWO;

Si, daca asta nu este suficient, exista varianta:

define('FLAG', true);

const EXP = FLAG ? ONE : TWO;

Practic poti seta dinamic valori  pentru o constanta a unei clase! De exemplu, poti seta direct intr-o astfel de constanta tipul de mediu in care se afla utilizatorul, dev sau prod.

Variadic functions

Aceasta este imbunatatirea care imi place cel mai mult, desi are si un echivalent in versiunile mai vechi.

In prezent, daca vrei sa ai o functie cu un numar variabil de parametri, poti face:

function variableParameters () {
     $params = func_get_args();
     foreach ($params as $parameter) {
          echo $parameter . PHP_EOL;
     }
}

variableParameters(1);
variableParameters(1, 2);
variableParameters(1, 2, 3);

Sunt cateva probleme, mai mult sau mai putin evidente, la o astfel de abordare:
1. Din semnatura functiei nu este clar ca accepta mai multi parametri, dimpotriva, pare ca nu accepta nici un parametru;
2. func_get_args poate sa fie apelata oriunde in functie. Daca este pe prima linie, o sa fie evident scopul; daca este pe linia 20 din functie, o sa fie greu de reperat si va genera confuzie.

O alternativa este sa punem un numar de parametri, dar nici asta nu o sa evidentieze comportamentul variabil.

Aici intervine noua abordare:

function variableParameters (...$params) {
     foreach ($params as $parameter) {
          echo $parameter . PHP_EOL;
     }
}

In aceasta noua varianta, este evident ce se intampla cu parametrii functiei. Nu trebuie sa intelegi daca si cati parametri primeste functia, este clar ca acestia au un numar variabil!

In general, cand caut o functie, eu ma uit la definitia ei in IDE inainte sa ma uit la documentatia ei, mai ales ca documentatia de multe ori nici nu exista.

Arguments unpacking

Problema care incearca sa fie rezolvata in acest mod este: ai o functie cu parametri multiplii, iar acestia trebuie trimisi in mod dinamic.

Cum spuneam si mai sus, exista moduri alternative de a rezolva aceasta problema. Sa luam functia din exemplul trecut:

function variableParameters () {
     $params = func_get_args();
     foreach ($params as $parameter) {
          echo $parameter . PHP_EOL;
     }
}

Si acum sa zicem ca este un numar variabil de parametri, iar acestia sunt stocati intr-un array. Ca sa-i pasam nu avem foarte multe solutii:

$params = ['param1', 'param2', 'param3'];

call_user_func_array("variableParameters", $params);

Rezultatul va fi:

param1
param2
param3

In PHP 5.6 vom putea folosi cele 3 puncte, similar cu exemplul de la “Variadic functions”, doar ca aplicati invers. In loc de parametrii pe care ii primeste functia, sunt parametrii care se trimit catre aceasta:

variadicParameters(...$params);

Poate pentru un exemplu de acest fel nu este clar, dar sa zicem ca este un numar fix de parametri pentru functie:

function twoParams($a, $b) {
     echo $a . $b . PHP_EOL;
}

$params = ["Hello", "PHP 5.6!"];

twoParams(...$params);

Chiar daca nu este ceva ce nu se putea face in versiunile anterioare, acum este mult mai elegant.

Iar, ca sa concluzionam, cele doua exemple:

function variableParameters (...$params) {
     foreach ($params as $parameter) {
          echo $parameter . PHP_EOL;
     }
}

variableParamerers(...["cool", "parameter", "unpacking"]);

Cred ca prin aceste mici schimbari de nuanta, PHP face tranzitia de la “the PHP way”, la “the best way”, preluand din alte limbaje.

Written by Claudiu Persoiu

22 February 2014 at 10:17 PM

Posted in PHP

Tagged with ,

Finally finally in PHP 5.5

with one comment

Cea mai noua versiune de PHP este aproape gata. In momentul cand scriu acest blog, PHP 5.5 este in RC 1.

Cum am mai spus si in blogul anterior, lista de noutati este disponibila la: http://www.php.net/manual/en/migration55.new-features.php

Al doilea feature ca popularitate este “finally”: http://www.php.net/manual/en/language.exceptions.php

Care-i treaba cu “finally“?

Pare un pic confuz, pare un bloc care se executa dupa ce se termina blocul de try catch. Dar ce este nou?

Sa zicem ca avem un bloc try/catch:

PHP 5.x < 5.5:

// open resouce
$resource = new Resouce();
try {
    // do stuff with the resouce
    $resouce->doStuff();
} catch (Exception $e) {
    // log exception
    syslog(LOG_ERR, $e->getMessage());
}
// release resouce
unset($resouce);

PHP 5.5

// open resouce
$resource = new Resouce();
try {
    // do stuff with the resouce
    $resouce->doStuff();
} catch (Exception $e) {
    // log exception
    syslog(LOG_ERR, $e->getMessage());
} finally {
    // release resouce
    unset($resouce);
}

Pana aici nu exista nici un motiv pentru care este nevoie de un nou bloc. Am prins exceptia, am facut logging pe ea si am continuat.

Dar sa zicem ca este o resursa si vrem sa o eliberam, iar ulterior sa aruncam exceptia. O varianta ar fi sa eliberam resursa in catch.

Dar mai ramane cazul “fericit”, sa zicem ca trebuie sa o eliberam si atunci.

// open resouce
$resource = new Resouce();
try {
    // do stuff with the resouce
    $resouce->doStuff();
} catch (Exception $e) {
    // release resouce
    unset($resource);
    // perpetuate exception
    throw $e;
}

Sa complicam si mai mult, sa zicem ca avem n tipuri de exceptii. Vor rezulta n conditii de catch, plus 1 pentru cazul fericit, iar in toate trebuie sa eliberam resursa. Nu foarte eficient…

O alta varianta este sa stocam exceptia intr-o variabila si, dupa ce am eliberat resursa, sa aruncam si exceptia, daca este cazul.

// variable to store the exception
$exception = false;

// open resouce
$resource = new Resouce();
try {
    // do stuff with the resouce
    $resouce->doStuff();
} catch (Exception $e) {
    $exception = $e;
}

// release resouce
unset($resource);

if($exception) {
    throw $exception;
}

Acesta este unul din modurile in care se realizeaza in prezent. Functioneaza, dar nu evidentiaza faptul ca poate doar vrem sa eliberam resursa si sa ne continuam viata in liniste.

Varianta PHP 5.5

In manualul php.net:

In PHP 5.5 and later, a finally block may also be specified after the catch blocks. Code within the finally block will always be executed after the tryand catch blocks, regardless of whether an exception has been thrown, and before normal execution resumes.

De fiecare data cand blocul se executa, indiferent daca se executa cu succes sau nu, finally se va executa. Deci, pentru exemplul:

try {
     echo 'Do stuff' . PHP_EOL;
     throw new Exception('testing');
} finally {
     echo 'inside finally' . PHP_EOL;
}

Outputul va fi:

Do stuff
inside finally

Fatal error: Uncaught exception 'Exception' with message 'testing' in...

Daca vrem sa prindem si exceptia:

try {
     echo 'Do stuff' . PHP_EOL;
     throw new Exception('testing');
} catch (Exception $e) {
     echo 'do something with the exception' . PHP_EOL;
} finally {
     echo 'inside finally' . PHP_EOL;
}

Outputul va fi:

Do stuff
do something with the exception
inside finally

Si chiar daca luam cazul si mai particular, cand prindem exceptia, apoi o aruncam:

try {
     echo 'Do stuff' . PHP_EOL;
     throw new Exception('testing');
} catch (Exception $e) {
     echo 'do something with the exception' . PHP_EOL;
     throw $e;
} finally {
     echo 'inside finally' . PHP_EOL;
}

Pare ca acum am reusit sa prevenim executia blocului finally? Nu este tocmai asa…

Do stuff
do something with the exception
inside finally

Fatal error: Uncaught exception 'Exception' with message 'testing' in...

Cu alte cuvinte, blocul finally se executa de fiecare data, indiferent de rezultat.

Written by Claudiu Persoiu

19 May 2013 at 7:34 PM

Posted in PHP

Tagged with , ,

Generating generators in PHP 5.5

without comments

O noua versiune de PHP este pe cale sa se lanseze. In momentul cand scriu acest blog, PHP 5.5 este in beta4.

Dornic de a vedea noutatile, am compilat noua versiune beta. Lista de noutati este disponibila la: http://www.php.net/manual/en/migration55.new-features.php

Cel mai important feature il reprezinta generatoarele (generators).

Generare de generators in PHP 5.5

Un generator este practic o functie care contine un apel catre “yield”.

Sa luam exemplul de pe php.net:

<?php
function xrange($start, $limit, $step = 1) {
    for ($i = $start; $i <= $limit; $i += $step) {
        yield $i;
    }
}

echo 'Single digit odd numbers: ';

/* Note that an array is never created or returned,
 * which saves memory. */ 
foreach (xrange(1, 9, 2) as $number) {
    echo "$number ";
}
?>

Practic, generatorul (xrange in acest caz), in loc sa intoarca un array, o sa genereze cate o valoare pentru a fi prelucrata.

Dar stai… oare asta nu era deja posibil pana la aceasta versiune?

Generatori inainte de PHP 5.5

Pana la versiunea de PHP 5.5 aveam deja iteratori:

<?php

class xrange implements Iterator
{
    private $position = 0;
    private $start;
    private $limit;
    private $step;

    public function __construct($start, $limit, $step = 1)
    {
        $this->start = $start;
        $this->limit = $limit;
        $this->step = $step;
        $this->position = 0;
    }

    function rewind()
    {
        $this->position = 0;
    }

    function current()
    {
        return $this->start + ($this->position * $this->step);
    }

    function key()
    {
        return $this->position;
    }

    function next()
    {
        ++$this->position;
    }

    function valid()
    {
        return $this->current() <= $this->limit;
    }
}

echo 'Single digit odd numbers: ';

/* Note that an array is never created or returned,
 * which saves memory. */
foreach (new xrange(2, 9, 2) as $number) {
    echo "$number ";
}
?>

In afara de faptul ca Iteratorul este un obiect cu mai multe proprietati, practic putem atinge acelasi rezultat.

Dar de ce era nevoie de generatori atunci? Simplu! In loc sa folosim ~40 linii de cod, putem folosi pur si simplu 5 ca sa atingem acelasi scop.

Un alt lucru interesant este ca:

get_class(printer());

va intoarce Generator.

Deci, practic, un generator va intoarce inapoi un obiect de tip Generator, iar acest obiect extinde Iterator.

Diferenta majora, asa cum este si pe site-ul php.net, este ca generatorul nu poate fi resetat, merge intr-o singura directie.

Trimiterea de informatii catre generator

Da, generatorii functioneaza in doua sensuri, doar ca un anume generator este bun doar pentru un singur sens. Daca sintaxa de mai sus este pentru “producerea” de rezultate, sintaxa de mai jos este pentru “consumare” de date.

Sintaxa pentru un generator “consumator” este simpla:

<?php
function printer() {
    $counter = 0;
    while(true) {
        $counter++;
        $value = yield;
        echo $value . $counter . PHP_EOL;
    }
    echo ‘Never executed...' . PHP_EOL;
}

$printer = printer();
$printer->send('Hello!');
echo 'Something is happening over here...' . PHP_EOL;
$printer->send('Hello!');
?>

Outputul va fi:

Hello!1
Something is happening over here...
Hello!2

Practic, valoarea din yield poate fi folosita ca orice alta valoare. Ce este interesant este while-ul. Pe php.net este urmatorul comentariu:

// Sends the given value to the
// generator as the result of
// the yield expression and
// resumes execution of the
// generator.

Este nevoie de un loop, pentru ca generatorul se va opri dupa ce proceseaza valoarea si va continua doar atunci cand primeste o noua valoare. Daca scoatem while-ul, doar prima valoare va fi procesata, indiferent de cate ori vom apela send().

Un lucru interesant este ca ceea ce este dupa loop nu se va executa, in cazul meu linia:

echo ‘Never executed...' . PHP_EOL;

Desi pare un loc potrivit sa eliberezi o resursa (ex. BD sau fisier), de fapt nu este, pentru ca acel cod nu se va executa.

Mi se pare util la logging. Din nou, nimic ce nu putea fi facut si pana acum, dar totusi permite o abordare mult mai usoara.

Am descoperit totusi un lucru care nu functioneaza:

<?php
function printer() {
    while(true) {
        echo yield . PHP_EOL;
    }
}

$printer = printer();
$printer->send('Hello world!');

foreach($printer as $line) {
    echo $line . PHP_EOF;
}

Un pic haotic, nu? Eram curios ce se intampla:
Fatal error: Uncaught exception ‘Exception’ with message ‘Cannot rewind a generator that was already run’ in…

Odata ce a fost folosit send() pe un iterator, nu mai poti sa iterezi prin el. Evident se poate genera unul nou, cu:

printer();

Ce este si mai confuz este ca Generator este o clasa final, deci nu poate fi extinsa, iar daca incerci sa o instantiezi direct (desi chiar daca ar functiona ar fi inutil):
Catchable fatal error: The “Generator” class is reserved for internal use and cannot be manually instantiated in…

Concluzia

Este un feature interesant, pentru ca simplifica mult lucrurile atunci cand vrei sa construiesti un iterator.

De asemenea, functionalitatea de send() mi se pare foarte interesanta, nu pentru ca face ceva nou, ci pentru ca il face mai usor.

Nu-mi place in schimb ca este aceeasi sintaxa pentru ambele variante de generatori si mai mult, ce este dupa while nu se mai executa.

Mi se pare usor confuza sintaxa, pentru ca nu este o diferentiere clara. Pe de alta parte, se pare ca asta exista deja in Python, deci pentru inspiratie se pot folosi exemplele din acest limbaj.

Written by Claudiu Persoiu

10 May 2013 at 9:11 AM

Posted in PHP

Tagged with , , ,

Closures, de la Scheme la Javascript la PHP

without comments

Notiunea de closure in PHP, desi a aparut in PHP 5.3, a fost realizata intr-un mod adecvat abia in 5.4, asa cum am mai spus-o si pe blogul meu.

Wikipedia ne spune:

In computer science, a closure (also lexical closure or function closure) is a function or reference to a function together with a referencing environment—a table storing a reference to each of the non-local variables (also called free variables) of that function.

In PHP nu este un concept foarte popular sau foarte cunoscut. De multe ori acesta este confundat cu Anonymous Functions. In limbajele functionale totusi, acest concept este foarte popular, pentru ca acolo este cu adevarat nevoie de el!

Scheme

Cand Brendan Eich a conceput JavaScript, s-a bazat pe limbajul Scheme si a ajuns sa faca o implementare a acestuia cu o sintaxa de C. Sintaxa C era si este in continuare mult mai populara, iar atunci (1995) limbajul Java era foarte “la moda”.

Sintaxa Scheme este similara cu sintaxa Lisp, in sensul ca se folosesc paranteze in jurul expresiilor pentru a le rula. Operatorii sunt definiti ca si functii si la fel ca o si in cazul functiilor, se pun in partea stanga a parantezei.

Sa luam un exemplu de closure in Scheme:

(define (make-counter)
  (let ((count (begin 
                 (display "run parent function and return 0") 
                 0)))
    (lambda ()
      (set! count (+ count 1))
      (begin 
        (display "inside child function ") 
        count))))

Functia principala seteaza o variabila “count”, cu valoarea 0 si afisaza “run parent function and return 0”, apoi intoarce o alta functie lambda, care incrementeaza variabila definita in functia principala si apoi afisaza “inside child function”.

Functia rezultata din executia functiei principale o stochez intr-o variabila pentru a o putea rula ulterior de mai multe ori:

> (define counter (make-counter))
run parent function and return 0
> (counter)
inside child function 1
> (counter)
inside child function 2

Cu alte cuvinte, de fiecare data cand apelez (make-couter), acesta va intoarce o functie noua care are acces la mediul in care a fost creata. Daca pare ciudat din pricina sintaxei, promit ca in JavaScript va parea mult mai natural.

Acest concept este foarte interesant pentru incapsulare. Mediul la tipul cand functia parinte este executata se poate incapsula, iar ulterior se va folosi de accest mediu fara grija ca acesta se poate schimba din cauze exterioare.

Pentru limbajele functionale acesta este un concept foarte interesant. Cand vine vorba de limbaje obiectuale totusi, conceptul aproape inutil, pentru ca obiectele au si ele rolul de incapsulare.

JavaScript

JavaScript a fost de la inceput un hibrid, un limbaj functional, orientat obiect, cu mostenire bazata pe prototype. Iar daca acestea nu erau suficiente, sintaxa a fost preluata din Java (C).

JavaScript nu a mostenit multe de la Scheme, dar a mostenit conceptul de closure.

Un motiv pentru care era nevoie de closure in Scheme este acela ca daca o functie nu gaseste o variabila in mediul in care se afla, o va cauta in mediul superior. Sa luam un exemplu:

(define x 1)
(define (add-in-env y) (+ x y))

Daca apelam add-in-env cu 2:

(add-in-env 2) -> 3

Pare la fel de ambiguu ca si in JavaScript, dar nu este tocmai asa. In Scheme sa faci mutatie nu e la fel de usor, simplu si transparent, deci o operatie ulterioara de:

(define x 2)

va rezulta intr-o eroare.

In JavaScript a rezultat un hibrid. Mutatia este permisa, dar notiunea de a cauta o variabila in mediul in care te afli a ramas:

var x = 1;
var add_in_env = function (y) {
   return x + y;
}

add_in_env(2); // rezulta 3

Pana aici e ok, dar pentru:

x = 2;
add_in_env(2); // rezulta 4

In acest caz, lucrurile scapa foarte usor de sub control.

Dar, ca sa rezolvam problema, putem pur si simplu sa definim variabila in mediul care isi va termina executia (se va inchide = will close):

var make_counter = function () {
   console.log("run parent function and set counter to 0")
   var count = 0;

   return function () {
       count = count + 1;
       console.log("inside child function");
       return count;
   }
}

var counter = make_counter();
console.log(counter());
console.log(counter());

var counter2 = make_counter();
console.log(counter2());
console.log(counter());
console.log(counter2());

Outputul va fi:

run parent function and set counter to 0
inside child function
1
inside child function
2
run parent function and set counter to 0
inside child function
1
inside child function
3
inside child function
2

Chiar daca functia principala si-a terminat executia, mediul din interiorul ei este pastrat ca un closure pentru functia care a fost intoarsa. Doar in momentul in care si subfunctia nu mai are referinte catre ea memoria alocata pentru closure va fi dezalocata.

Chiar daca JavaScript are obiecte, acestea nu au metode private. O abordare este sa pui un “_” (underscore) in fata numelui functiei si sa o consideri privata. Din punctul meu de vedere asta este ca si cum ii rogi pe cei care vin dupa tine sa o considere o functie privata. Evident acest lucru nu este tocmai consistent.

Sa luam un exemplu:

var obj = {
   _secretFunction : function (key) { console.log(‘do secret ’ + key) },
   doStuff : function (key) { this._secretFunction(key) }
}

obj.doStuff(‘stuff’); // do secret stuff

Aparent avem o metoda publica “doStuff” si una privata “_secretFunction”. Totusi nu poti preveni un utilizator sa apeleze “_secretFunction”, sau mai rau, sa o modifice:

obj._secretFunction = function (key) { console.log('new secret ' + key); }

obj.doStuff('stuff'); // new secret stuff

Daca vrem ca functia sa fie ascunsa, iar acest lucru sa fie evident pentru toata lumea, din nou putem folosi un closure:

var obj = (function () {
   var secretFunction =  function (key) { console.log(‘do secret ’ + key) }

   return {
      doStuff : function (key) { 
         secretFunction(key) 
      }
   }
})();

obj.doStuff(‘stuff’); // do secret stuff

Pentru ca functia parinte se va executa la inceput, practic spatiul in care a fost definit secretFunction si-a terminat deja executia, incapsuland logica. Obiectul intors poate sa apeleze functia pentru ca este definit in acelasi mediu ca si obiectul.

Pare complicat prima data, dar de fapt este foarte simplu cand intelegi conceptul.

Si apoi a fost… PHP

PHP inglobeaza multe optiuni diferite. PHP s-a dezvoltat initial ca un framework Perl, ulterior engine-ul fiind scris in C.

PHP este un limbaj dinamic care inglobeaza foarte multe concepte, de la obiecte, interfete si functii anonime, pana la goto labels. Nu este foarte clara directia in care ar trebui sa se dezolte limbajul, mai degraba ofera posibilitatea pentru abordari diferite.

In istoria ciudata a PHP, undeva in versiunea 4 a fost introdusa o sintaxa pentru Anonymous Functions, dar abia in PHP 5.3 a aparut o versiune mai “normala“.

Tot in versiunea 5.3 a fost introdusa si prima varianta de closures:

$scalar = 5;

$closure = function () use ($scalar) {
     return 'Scalar: ' . $scalar . PHP_EOL;
};

echo $closure(); // Scalar: 5

$scalar = 7;

echo $closure(); // Scalar: 5

Versiunea functioneaza in mare parte, dar trebuie sa specifici ce vei trimite catre closure.

Si mai exista cateva inconveniente:

<?php 
class Foo {         
   private function privateMethod() {                 
      return 'Inside private method';         
   }

   public function bar() {                 
      $obj = $this;                 
      return function () use ($obj) {                         
         return $obj->privateMethod();
      };
   }
}

$obj = new Foo();
$closure = $obj->bar();
echo $closure();

Fatal error:  Call to private method Foo::privateMethod() from context '' in [...][...] on line 10

Nu functioneaza pentru ca nu poti trimite $this ca parametru la closure, iar daca faci artificiul de mai sus tot nu vei putea accesa metodele private. Nu uitati, asta se intampla in PHP 5.3.

Ideea de a introduce acest tip de closure mi se pare bizara. Nu este prima daca cand in PHP se introduce un feature “bizar”, dupa cum vorbeam mai sus si de Anonymous Function. Pare work in progress.

Cred ca toata lumea se astepta ca acest feature sa functioneze la fel ca in JavaScript. Cred ca doar datorita JavaScript conceptul de closure a devenit atat de popular.

In versiunea PHP 5.4 lucrurile s-au mai schimbat, avem in sfarsit closures asa cum ne asteptam:

class Foo {
   private function privateMethod() {
      return 'Inside private method';
   }

   public function bar() {
      return function () {
         return $this->privateMethod();
      };
   }
}

$obj = new Foo();
$closure = $obj->bar();
echo $closure(); // Inside private method

Functioneaza!

Poti chiar sa spui:

unset($obj);
echo $closure();

si va functiona, pentru ca obiectul in interiorul caruia a fost definit closure-ul a ramas in memorie pana cand se va termina executia scriptului, sau se va apela:

unset($closure);

Pentru mai multe detalii despre cum functioneaza closure in PHP 5.4, puteti citi acest blog.

Written by Claudiu Persoiu

10 April 2013 at 10:02 AM

Passing Magento Developer Plus certification

with 3 comments

De ceva mai bine de un an de zile am inceput sa lucrez pe platforma Magento. In primavara anului trecut, un coleg de la Optaros si-a dat examenul de certificare pentru Magento Developer Plus. De atunci a inceput sa-mi placa ideea de a da certificarea, mai mult ca sa ma motiveze sa invat dedesubturile Magento.

Acum cateva luni am fost inscris intr-un grup de studiu pentru certificare in cardul companiei. Aceasta a fost prima data cand am fost sponsorizat pentru o certificare (da, pana acum totul a fost pe banii mei). Sa te pregatesti in cadrul unui grup a fost o cu totul alta experienta.

Cei care au mai multa experienta intr-un anumit domeniu echilibreaza situatia pentru ceilalti si pot oferi explicatii mai bune din propria lor experienta. E mai usor sa intelegi cand ai exemple concrete, decat sa incerci sa-ti imaginezi scenarii.

Certificarea este disponibila prin Prometric. Deci, cand te-ai hotarat, poti intra pe site sa cumperi un voucher si sa iti programezi examenul.

Pretul unui voucher este de 260$, nu foarte mic, dar daca reusesti sa-ti convingi seful sa il plateasca, probabil nu va fi asa grav. 🙂

Dar sa trecem la subiectul mai interesant, pregatirea.

Materiale
Magento nu sta prea bine la subiectul asta, materialele sunt putine si nu sunt centralizate.

Sursele mele au fost:
Magento® Certified Developer Plus Exam Study Guide – e imperativ sa citesti ghidul si sa incerci sa gasesti raspunsurile la toate intrebarile.
Magento training – in special Fundamentals of Magento Development
– bloguri – nu vreau sa dau nume, sunt multe si multi care scriu despre problemele pe care le intalnesc si care povestesc despre ce este examenul.

Din pacate nu ai modalitatea, cum este la PHP, ZF sau Symfony, sa gasesti cam tot ce ai nevoie intr-un singur loc. Practic, depinde de noroc si skilluri de cautare, nu exista mereu o “varianta oficiala”. Lucrurile devin mai ciudate cand gasesti mai multe abordari in functie de versiune.

Cum m-am pregatit eu
Am inceput cu trainingul video. Nu este perfect, dar ajuta mult. Cred ca problema pentru majoritatea certificarilor este ca nu lucrezi cu toate modulele disponibile, la fel cum in PHP nu ai posibilitatea sa lucrezi prea mult cu socketuri si streamuri.

Chiar daca nu primesti codul si uneori sunt greu de urmarit si transcris exemplele, cred ca tutorialele video sunt una dintre cele mai importante surse in momentul actual.

In al doilea rand, cu Study Guide-ul in mana, am inceput sa incerc sa raspund la intrebarile din el. Cand am intrat in study group, munca s-a impartit la toti cei din grup. Avantajul meu a fost ca era a doua generatie de grup si am putut profita de documentatia deja construita de primul grup.

Daca te pregatesti singur, cred ca cel mai important este sa incepi, este cel mai greu pas. Iar daca nu stii de unde sa incepi, cauta pe Google intrebarile din Magento, sunt multi care au scris deja explicatiile.

Cel mai usor se gasesc raspunsuri pentru intrebarile de la primele capitole. Pe masura ce numarul capitolului creste si rezultatele pe Google scad.

Dar dupa primele intrebari ar trebui sa intelegi despre ce este vorba si teoretic nu vei mai avea nevoie de documentatie.

Foloseste Mage::log(Varien_Debug::backtrace(true, false)); pentru stack trace si xdebug (http://xdebug.org/) pentru a urmari ce se intampla in spate.
Cu rabdare, toate intrebarile isi gasesc raspunsul.

Pentru ca a fost un grup, studiul a fost mult mai usor pentru mine, dar chiar si asa, pentru a fi sigur de raspuns am studiat adanc in cod.

Examenul
Unele intrebari sunt dificile, dar sunt si intrebari accesibile. Cam toate gradele de dificultate se intalnesc in examen.

Pentru Plus, examenul dureaza 2h, nu 2.5h, cum scrie in guide.

Daca optezi pentru Plus, exista 11 intrebari de Enterprise si de dificulatate mai ridicata, din care trebuie sa rezolvi 7 corect, practic in asta consta diferenta de dificultate. Aici conteaza cata experienta ai pe Enterprise.

In fiecare ghid subiectele sunt defalcate procentual pe capitole.

Pentru ca la certificarea fara Plus nu exista intrebari de Enterprise inseamna ca trebuie raspunzi doar la procentul necesar ca sa iei examenul, nu sa faci un minim pentru un anumit capitol.

Lucrurile care se fac in mod uzual sunt studiate in amanunt, e important sa intelegi cum functioneaza fiecare functie pe care se pune accentul din fiecare modul si ce fac toate tag-urile din fisierele xml.

In general sunt lucruri cu care lucrezi, sau mai bine zis cu care este o probabilitate buna sa lucrezi din modulele listate in ghid.

Dupa examen
Inainte sa iesi din sala vei afla daca ai trecut sau nu. Cand iesi din sala vei primi o hartie cu numarul de intrebari corecte din totalul de intrebari pe fiecare sectiune.

In cazul in care nu ai avut succes, vei primi de la Magento un mail cu un voucher cu o reducere pentru o ulterioara incercare. Ei spun ca ar trebui sa mai studiezi cam 3 saptamani inainte de a incerca din nou. Oricum, dupa ce ai dat examenul o sa ai o viziune mai buna daca esti pregatit sau nu pentru o noua incercare.

Dupa cateva zile (cred ca 3 in cazul meu) vei putea sa-ti vezi si profilul pe site-ul Magento, pentru referinta.

Diploma ajunge cam intr-o luna in Romania, la adresa care este trecuta pe site-ul Magento.

Multa bafta!

Written by Claudiu Persoiu

16 February 2013 at 8:23 PM

Posted in Magento,PHP

Tagged with , , ,