PHP vs Ruby – Un confronto realistico

Abbastanza spesso si vedono sviluppatori che hanno un sacco di esperienza in un certo linguaggio di programmazione giocare un pò con qualche altro linguaggio per poi fare un veloce paragone fra i due, paragone che di solito è piuttosto privo di valore. Nonostante ciò riescono a generare guerre di click che risultano in un sacco di traffico.

Invece di fare ciò, ho pensato che sarebbe interessante fare una comparazione più equa, dalla prospettiva di qualcuno a cui piace davvero scrivere script sia in PHP che Ruby e che l’ha fatto per anni. Lo scopo non è di trovare un vincitore o un linguaggio migliore ma di evidenziare un paio di cose che mi piacciono di Ruby e del suo ecosistema.

Screenshot 2015-11-21 01.20.42

DIFFERENZE CONCETTUALI

Linguaggi diversi sono spesso comparati per determinare quale è migliore nonostante le differenze di base siano per lo più ideologiche. Qualche volta una cosa sembra ottima per un gruppo di persone, mentre la stessa cosa rende il linguaggio un incubo per altri sviluppatori.

Tenendo questo a mente, prima che io parli di quella parte di Ruby che mi piace un sacco, penso sia importante spiegare alcune di queste differenze concettuali.

Metodi, Variabili, Proprietà?

PHP spesso usa una sintassi differente per accedere a proprietà, metodi o variabili. Ruby non lo fa.

PHP

Ruby

I più pedanti evidenzieranno che attr_reader :turtle definisce un metodo in modo dinamico, il quale è usato per accedere a @turtle, rendendo turtlebike la stessa cosa.  Uno sviluppatore PHP che voglia usare turtle con nessun metodo o nome di variabile esplicitamente definito sarà incredibilmente confuso riguardo a da dove arrivi.

Questo non dovrebbe causarti problemi troppo spesso ma di tanto in tanto può succedere. Qualche volta un membro di un team potrebbe cambiare un attr_reader in un metodo o vice-versa, e ciò potrebbe causare problemi.

Detto questo, Ruby permette la creazione di API veramente flessibili e la possibilità di fare cose straordinarie al volo.

Qualsiasi cosa che chiami trip.canceled_at riceverà un valore nullo per questo campo.

Type Hints vs. Duck Typing

Il PHP ha aggiunto il type hinting opzionale dalla versione 5.0 per gli argomenti. Potresti avere bisogno di array, nomi di classi specifici, interfacce o elementi astratti, etc. Il PHP 7.0 permette di ritornare il type hinting tramite il type hint scalare. E stato a lungo discusso con molti pareri negativi ma alla fine è stato approvato ed è ancora qua, tuttavia completamente opzionale.

Ruby letteralmente non ha niente di tutto ciò. Usa il “duck typing”: invece  di dire “l’argomento deve essere un’istanza di una classa che implementa FooInterface” e sapere che FooInterface avrà un metodo con argomento (int $a, array $b), essenzialmente dici:”L’argomento può essere letteralmente qualsiasi cosa che risponda al metodo, e se non risponde possiamo sempre inventarci qualcos’altro”.

Ruby

 

E’ molto flessibile ma per alcuni avrà un sapore dolce-amaro. Specialmente in un linguaggio come il PHP dove i valore int(0) o int(1) sono considerati valori boleani, prendere qualsiasi variabile e sperare che funzioni può essere una mossa che spaventa.

In PHP dobbiamo definire due funzioni differenti.

Detto questo, se volessi usare lo stesso identico approccio ma con il duck typing in PHP, potremmo facilmente farlo:

Pretendere che Ruby sia “migliore” perché usa il duck typing potrebbe portare fuori strada ma è un’argomentazione molto comune. Potresti preferire questo approccio e con PHP puoi adottarli entrambi ma prettamente a Ruby manca una feature che PHP ha. Poter fare quello che voglio dà un punto di vantaggio per PHP.

Detto questo, ci sono tanti sviluppatori PHP che sono fortemente contrarti ai type hints, desidererebbero che non ce ne fossero affatto e sono rimasti sconvolti quando la versione PHP 7.0 ne ha aggiunti di ulteriori.

Python, come Ruby, non aveva nessun tipo di type hint. Poi recentemente li ha aggiunti. Sarei interessato di sapere quante persone se la sono presa per questo cambiamento.

Features divertenti

Una volta accettate queste differenze mi è stato possibile concentrarmi sulle cose più divertenti. Sono features che ho iniziato a notare da solo usando Ruby regolarmente, o quanto meno piuttosto spesso.

Classi annidate

Le classi annidate sembrano qualcosa di alieno per gli svilupattori PHP. Le nostre classi vivono nei namespace una classe e un namespace possono condividere lo stesso nome. Quindi, se abbiamo una classe che è solo rilevante per una classe, usiamo un namespace e fine.

Abbiamo una classe chiamata Box, che può lanciare una ExplodingBoxException:

Quella dichiarazione di eccezione di classe deve stare da qualche parte. Potremmo metterla all’inizio della classe ma allora avremmo due classi in un file  e…. ciò suonerebbe molto strano per i più. Viola anche il PSR-1, che afferma:

Ciò significa che ogni classe è un file di per se, ed è un namespace di almeno un livello: un top-level vendor name.

Perciò, va nel suo file:

Per caricare quella eccezione dobbiamo avviare l’autoloader e andare nel filesystem di nuovo. Farlo non è gratis! PHP 5.6 e tutte le versioni inferiori abbassano l’overhead per le richieste seguenti se l’opcode cache è abilitata, ma comunque richiede lavoro extra.

In Ruby puoi annidare una classe dentro un’altra classe:

E’ accessibile alla classe che la definisce e anche a quelle esterne alla classe che sono:

Una classe è rilevante sono per un’altra classe? Raggruppale!

Un altro esempio sarebbe la migrazione di database.

Le migrazioni sono disponibile in molti popolari framework PHP , da CodeIgniter a Laravel. Chiunque ne abbia usato uno lo saprà, se fai riferimento a un modello o a un’altra classe nelle tue migrazioni, allora poi cambi la classe.

Ruby gira attorno al problema tramite le sue classi annidate:

Le versioni annidate dei modelli User e Account ORM  saranno usati, invece che le classi dichiarate globalmente.
Gli sviluppatori PHP spesso finiranno per seguire percorsi molto complessi per fare ciò, o scriver il codice SQL a mano, che è una perdita di tempo rispetto a copiare solo i pezzetti rilevanti di un modulo.

Debugger

XDebug funziona a meraviglia. Usare brakepoints è fantastico e ha rivoluzione il modo in cui gli sviluppatori PHP debuggano le loro applicazione, muovendosi oltre il “var_dump() +refresh” debug worflow che è selvaggiamente comune fra gli sviluppatore junior PHP.

Detto questo, arrivare a far funzionare XDebug con il tuo IDE di scelta, trovare l’addon giusto se manca, ottenere php.init sistemato per la giusto versione di PHP per attivare zend_extension=xdebug.so per la tua CLI e la tua versione web, ottenere i breakpoint anche se usi Vagrant, etc. può essere molto scomodo e difficoltoso.

Ruby ha un approccio un pò differente. E’ un pò come debuggare JavaScript nel browser, puoi semplicemente buttare i pezzi da debuggare nel tuo codice e i breakpoint sono automatici. Nel punto in cui il tuo codice esegue quella linea, avrai una istanza di un  REPL per interagire con il tuo codice.

Ci sono pochi debuggers in giro. Uno popolare è pry e un altro è byebug. Sono entrambi straordinarli. Puoi installarli via Bundler aggiungendo questo al tuo Gemfile:

E’ l’equivalente di una dipendenza dev Composer e una volta installato puoi semplicemente chiamare il debugger se stai usando Rails. Altrimenti avrai bisogno di richiamare “byebug” prima.

Una breve guida Debugging Rails Applications, mostra come funzionano le cose dopo aver inserito le debugging keyword nella tua applicazione:

Le frecce mostrano che la linea REPL instance è in esecuzione e puoi eseguire codice da quel punto. A questo punto @articles non è ancora stato definito ma puoi chiamare Article.find_recent per vedere cosa sta succedendo. Se si verifica un errore, puoi sia digitare “next” per andare alla prossima linea nello stesso contesto, o “step” per passare all’esecuzione della prossima istruzione.

 

Unless

A un sacco di gente non piace unless. Spesso è abusato, come molte features di molti linguaggi di programmazione.

La struttura di controllo di unless è diametralmente opposta a if.  Invece di eseguire il codice nel blocco quando le condizioni valutate sono vere, lo fa solo quando sono false.

Rende le cose un po’ più facili, soprattutto quando ci sono condizioni multiple, forse un || e qualche parentesi. Una espressione lunga come questa if ! (foo || bar ) && baz può diventare molto più breve: unless (foo || bar ) && baz.

Quando fu richiesta questa feature per il PHP nel 2007, la richiesta venne ignorata finché il creatore del PHP Rasmus Lerdorf disse che sarebbe stata una rottura di compatibilità e il suo significato e la sua funzione non sarebbero state così ovvie per persone di madre lingua non inglese come lui stesso.

E’ una parola strana che essenzialmente significa no-se anche se logicamente equivarrebbe a “più” poiché l’opposto di “più” sarebbe “meno” e infilarci un “un” (negazione) davanti ne capovolge il significato

Ero in disaccordo e lo sono ancora. Chi legge unless non va a pensare che significhi l’opposto di less (meno) semplicemente basandosi sull'”un” che lo precede. Se questo fosse il caso le persone leggerebbero il nome della funzione uniqid() e penserebbero che sia l’opposto di iqid().

Metodi Predicati

Ci sono alcune convenzioni utili nel mondo di Ruby che risolvono situazioni che il PHP è costretto a risolvere in altri modi. Una di queste sono i metodi predicato, che sono dei metodi che ritornano un valore booleano.  Sapendo che Ruby non ha type hints per i valori di ritorno, questo è un buon suggerimento dell’intendo degli sviluppatori del metodo.

Ruby ha molte proprietà built in, come object.nil?. Questo è basicamente $object === nil in PHP.  Un “include?” invece che “of include” è anche molto più chiaro perché sta porgendo una domanda, non eseguendo un’azione.

Molti sviluppatore PHP faranno la stessa cosa ponendo prima del nome del metodo “is” and/o “has”, quindi invece avresti isDriver() e forse hasVehicle(); ma a talvolta necessiti di usare altri prefissi. Un metodo che ho scritto in Ruby che era can_drive? sarebbe canDrive() in PHP, e se non è chiaro è un metodo predicato. Dovrei rinominarlo isAbleToDrive() per renderlo chiaro.

Sintassi degli array ancora più sintetica

In PHP definire array è facile, ed è stato reso molto meno prolisso dal PHP 5.4 con l’addizione della sintassi breve per array:

Alcuni diranno che è ancora un pò troppo prolissa. Dal Ruby 1.9 hanno aggiunto una nuova opzione, per permettere alle freccette di essere sostituite con i punti e virgola, che se fatto in PHP decurterebbe la sintassi un pò di più:

Ciò potrebbe non avvenire ma in PHP. PHP cerca di essere minimalista con la propria sintassi ed è molto spesso  contrario ad aggiungere nuovi approcci a vecchi elementi, anche se il nuovo approccio sarebbe lievemente migliore. Basicamente la dolcezza della sintassi non è una priorità per i mantenitori del PHP, mentre sembra essere uno degli obbiettivi fondamentali dei mantenitori di Ruby.

Oggetti Letterali

Ciò evidenzia una feature di Ruby che mi piacerebbe molto vedere implementata nel PHP: oggetti letterali. Nel PHP, se ti ppiacerebbe definire una StdClass con dei valori, hai due approcci:

Lo so che è come si è sempre fatto in PHP, ma potrebbe essere molto più semplice.

La sintassi proposta nel RFC corrisponde esattamente a quella di Ruby:

PHP

Ruby

Mi piacerebbe davvero che venisse implementato, ma di nuovo, è stato tentato nel passato e ha riscontrato scarso interesse.

Gestire l’eccezione di Un Metodo

Anziché try/catch in PHP, Ruby ha begin/rescue. Il modo in cui funziona è essenzialmente identico, con PHP 5.6

PHP e Ruby possono entrambi gestire una eccezione in qualsiasi punto del codice, seguendo la loro rispettiva keyword try o begin  ma Ruby può fare qualcosa di davvero intelligente: puoi saltare il metodo begin, e rescue direttamente dal corpo di una funzione/metodo:

Ruby

Se le cose vanno male, un errore può essere gestito in qualche modo invece che forzare una chiamata per gestirlo . Non sempre quello che desideravi ma è comodo avere l’opzione disponibile e senza la necessità di costruire l’intera cosa all’inizio.

Nel PHP questo non funziona ma la feature potrebbe sembrare un pò come questa se fosse implementata:

Potrebbe non essere molto importante per te, ma ci sono un sacco di piccoli aggiustamenti come questo che fanno sembrare che Ruby voglia aiutarti.

Exception Retries

Qualche mese fa ho scoperto una feature molto comoda che non avevo mai notato, la keyword retry:

In questo esempio, una rara condizione appare perché find_or_create_by, se sei sfortunato, può portare a un record creato da un altro processo dopo SELECT ma prima di INSERT.

Dato che questo può accadere solo una volta, puoi istruire il rescue a tentare nuovamente e verrà trovato con il SELECT. In altri esempio, probabilmente vorrai metterci un po’ di logica in modo che riprovi una o due volte, ma ignoriamo la questione per solo un secondo.

Concentrati su quanto fastidioso sarebbe prendere un pezzo del codice e provare di nuovo. In Ruby puoi fare questo:

PHP richiederebbe la creazione di una nuova funzione/metodo per il blocco iniziale:

L’esempio di Ruby è certamente più pulito a riprovare a eseguire il codice.

Mi è stato detto che questa feature sta attivamente venendo sviluppata e verrà annunciata presto. Teoricamente potrebbe essere una feature di PHP 7.1 se tutto va bene.

pensieri finali

Avendo usato Ruby nel passato, ormai scrivo in Ruby come fosse PHP. Lavorare con un team straordinario di sviluppatori Ruby estremamente vissuti mi ha insegnato molto negli ultimi anni riguardo le pratiche che con Ruby sono un po’ diverse e non mi dispiacciono.

L’articolo evidenzia elementi che mi mancherebbero di Ruby se dovessi tornare a codare in PHP ma che non mi fermerebbero dal farlo. Quello che gli hater del PHP regolarmente ignorano sono i progressi che vengono fatti dai contributori del PHP e mentre potrebbe non avere la feature X o Y che mi piace del Ruby, hanno fatto cose stupefacenti per il PHP 7 e il PHP 7.1 sembra avere molte interessanti sorprese.

Consultaci per l’opzione migliore per sviluppare il tuo progetto web. Che sia lato server, lato client o cosa più probabile entrambi possiamo comunque aiutarti.