ugrás a tartalomhoz

Fejlődés?

Hidvégi Gábor · Dec. 21. (Sze), 11.01
Egy másik fórumszálon egy érdekes vita alakult ki a PHP type hinting-jával kapcsolatban, ahol némi utánaolvasással kiderült, hogy ennek a szerepe nem más, mint a hibakeresés támogatása (bár munkát nem spórol meg, hisz a szükséges konverziókat ugyanúgy el kell végeznünk), de például (elhanyagolható mértékben) lassítja a futást, a kódot pedig rugalmatlanabbá teszi, azaz elveszik a PHP egyik óriási előnye, a dinamikus típusosság.

Ez az egész rengeteg kérdést felvet, ahol tudok, megpróbálok én is valamit válaszolni, de az csak egy szempont a sok közül:

1, Hol érdemes ilyet használni? Saját kódban, vagy ott is, ahol harmadik féltől származó rutinok vannak? Mert ez utóbbi esetben nem tudjuk garantálni, hogy a típusok nem változnak.

Saját kódban miért használná bárki is? Kiszűri vele a hanyag kollegákat? Node miért vesz fel a cég ilyeneket?

2, Érdemes-e ilyet használni?

Production kódban egy Fatal error, amit ilyen hiba okoz, megengedhetetlen, és egyébként is, nem is feltétlenül szükséges. Ráadásul a kód 100%-át lefedő tesztek szükségesek az ilyenek kiszűréséhez, aminek a megvalósítása szinte lehetetlen.

3, Szükséges-e egy ilyen featúra ahhoz, hogy jó kódot írjunk?

Robert C. Martin szerint megfelelő teszteléssel a statikus típusosság értelmét veszti.

4, Ha egy featúra hasznossága kérdéses, miért teszik bele? Növeli a PHP kódbázisát, minden újabb sor potenciális hibaforrás, és azért kell is a PHP-t foltozgatni rendesen.

5, Mi alapján döntik el, hogy egy featúra mennyire hasznos? Divat vagy racionális döntés áll a hátterében? Utóbbi esetben hol vannak a számok, ami alapján fel lehet mérni a hasznosságát? Mint a fentiekben látszik, vannak hátrányai is a dolognak, miért erőltetik rá a fejlesztőkre azzal, hogy beépítik a PHP-be?

6, A fenti fórumszálban volt egy olyan megállapítás is, hogy a PHP "egzotikus" funkcionalitását használva egy cégnek komoly munkájába került az áttérés a 7-es verzióra, míg ott, ahol csak a legalapabb funkciókból építették fel a kódot, nem, vagy csak minimális mértékben kellett módosítani, hogy 5.x után fusson 7-esen.

Ezek után felmerül az "egzotikus" funkciók hasznossága, hisz a hátrányukat látjuk: egy csomó plusz munkát hozhatnak bizonyos esetben, lehet újraírni sokmindent, tesztelni, ami sok idő és sok pénz. Hoznak-e ennyi hasznot? Ez főleg annak a fényében érdekes, hogy a fenti példák bizonyítják: az alapfunkcionalitást használva is lehet jó és működőképes szolgáltatást írni.

7, Egy programozási nyelv vagy egy szoftver csak attól fejlődik, hogy újabb és újabb featúrák kerülnek bele? A fentiek alapján például ez azért kérdéses. Vajon a hátrányokkal számolnak-e a fejlesztők és a megrendelők?

Az ezer soronkénti hibák száma átlagosan 15-50, azaz minden új featúra egy-egy újabb hibaforrás. Manapság, amikor a számítógépes bűnözés egyre nagyobb méreteket ölt, és már milliárdos nagyságrendben lopják a felhasználói adatokat, akkor valóban ez a jó irány?

Változik a világ, és változnia kell a számítástechnikának is. Most még csak robotok keresik a törhető webes alkalmazásokat, de csak idő kérdése, hogy a bűnözők mesterséges intelligenciát vessenek be ugyanerre a feladatra. Ebben az esetben pont, hogy a featúrák számának csökkenése a szükséges, hogy minél kisebb támadási felületet adjunk.

A sok featúra a felhasználói felületet is bonyolítja, és emiatt általános tapasztalat, hogy a felhasználók félnek a szoftverektől. Ha kevesebbet tud egy szolgáltatás/program, az épp szükséges minimumot, az előnyt jelenthet a vásárlók részéről, valamint olcsóbb lesz a fejlesztés is, hisz adott esetben nem lesz szükség UI specialistára.

A szolgáltatások számának csökkenése a kód gyorsulását is magával hozhatja, ami ugyancsak csökkenti a karbantartási és az üzemeltetési költségeket.

Változik a világ, és emiatt változnia kell a gondolkodásnak is. A fentiekből kiindulva ma már nem feltétlenül csak az a fejlődés, amit eddig megszoktunk, hanem akár az is, amit korábban visszafejlődésnek gondolhattunk.
 
1

Valaszok

janoszen · Dec. 21. (Sze), 12.36
Azt hiszem, ebbol a temabol egy fontos kulonbseg kimaradt, meghozza a static typing vs. a strict typing. A statikus tipusossag azt jelenti, hogy compile time tolja az arcodba a hibauzenetet a kornyezet, amig a strict typing azt jelenti, mennyire szigoruan ervenyesiti a tipusossagot a futtato.

Amit szinten erdemes megjegyezni, hogy a PHP tipuskonverzioja teljesseggel agyatlan, mert pl. '123 barackfa' == 123.

1, Hol érdemes ilyet használni? Saját kódban, vagy ott is, ahol harmadik féltől származó rutinok vannak? Mert ez utóbbi esetben nem tudjuk garantálni, hogy a típusok nem változnak.

Saját kódban miért használná bárki is? Kiszűri vele a hanyag kollegákat? Node miért vesz fel a cég ilyeneket?


Attol fugg. Ha statikus tipusossagrol beszelunk, pl. hogy compile time szurne ki azt hogy ott olyad adtam at amit nem szabad, akkor yes please. Strict typingrol lehet vitatkozni, de en szemely szerint szeretem.

Gyakori hiba a PHP-s projekteknel a tombok tulzott hasznalata. Azt varod, hogy egy tombben pl. egy BlogPost objektum van, es kozben nem azt kapod, hanem egy masik tombot. Az ebbol keletkezo hibak altalaban random hibauzenetek, pl. hogy nem lehet meghivni a getTitle() fuggvenyt null-on, aztan mehetsz debugolni hogy honnan kerult oda null. Sajnos ezen a PHP 7 strict modja nem segit, a Hacklang viszont igen.

Altalanossagban en sajat kodban hasznalom, third party libraryvel, illetve az ahhoz kapcsolodo adapter retegben nem. (Third partyban egyebkent sem tudod hasznalni mert fajlonkent kell bekapcsolni.) Szerencsere a PHP eleg intelligens hogy ezt a setupot kezelje es a nem strict -> strict hivaskor megengedje az automatikus tipuskonverziot.

2, Érdemes-e ilyet használni?

Production kódban egy Fatal error, amit ilyen hiba okoz, megengedhetetlen, és egyébként is, nem is feltétlenül szükséges. Ráadásul a kód 100%-át lefedő tesztek szükségesek az ilyenek kiszűréséhez, aminek a megvalósítása szinte lehetetlen.


A PHP nem fatal errort dob, hanem exceptiont. Hacklang szinten, plusz ott a type checker ami elore szol. Az exceptiont el tudod kapni ha akarod.

A kod 100%-os code coverage nem garantalja azt, hogy minden esetet vegig probaltal, vagy azt hogy a teszt ertelmes dolgot tesztel, pusztan azt, hogy vegrehajtottal minden kodsort legalabb egyszer. A 100% logikai lefedettseg meg ugye nem minden problema eseten lehetseges, hiszten vannak vegtelen scope-ok, ahol nem tudsz minden lehetseges esetet / esettipust letesztelni.

Egy nagyon egyszeru pelda az lenne, hogy ha tort szamokkal dolgozol (pl. penzosszeg) es ezeket normal esetben stringben tarolod. Ha most egy fuggveny float aritmetikat alkalmaza penzosszegre, erdekes bugok lephetnek elo. Ezeket nem biztos hogy megfogod tesztelessel, es jobb lenne ha a compiler rad szolna hogy hello, ezt itt nem kellene. Siman lehet hogy egy zsak teszted atmegy, egy nagyobb volumenu feldolgozasnal megis problemahoz vezet.

3, Szükséges-e egy ilyen featúra ahhoz, hogy jó kódot írjunk?

Robert C. Martin szerint megfelelő teszteléssel a statikus típusosság értelmét veszti


Azt ne felejtsuk el, hogy Robert C. Martin a sajat projektjein Rubyban dolgozik, nem pedig PHP-ban. A PHP gyengen tipusos, amig a Ruby erosen tipusos duck typinggal. Raadasul ez a blogpost 2003-ban irodott, nem tudom, mennyire tartja maig ezt az allaspontot. Arrol nem is beszelve, hogy C/C++ strict/static typingja es a Java strict/static typingja kozott fenyevek vannak, az utobbi sokkal fejlesztobaratabb.

Altalanossagban szolva ez a kiejelentes csak es kizarolag akkor igaz, ha tenyleg minden edge case-t letesztelsz. Ha pl. hogy mi van akkor, ha ott nem egy string kerul atadasra, hanem mondjuk egy objektum ami nem stringkent viselkedik. Altalaban az ilyen eseteket nem szokas letesztelni, mert annyira kicsi az elfordulasi valoszinuseguk, hogy nem eri meg vele foglalkozni.

Ha viszont egy banki, elszamolasi rendszert irsz, akkor ott fontos, hogy dogoljon meg a program mintsem hogy hamis, automatikusan typecastolt ertekekkel dolgozzon es ez esetleg nagyon nagyon sokba keruljon.

A Hacklang ebbol a szempontbol jobb mint a PHP, mert "compile time" rad szol ha baromsagot csinalsz (nem igazi compile time, hanem type checker, de megkapod a hibakat rogton). Ha nincsenek compile-time hibauzeneteid a hibas tipushasznaladra, kenytelen vagy teleszemetelni a kododat boilerplate-el a nem vart tipusok lekezelesre vagy hagyatkozol a nyelvtol kapott automatikus exceptionre.

Ekes pelda erre a problemara az, hogy ha PHP-ban stringre akarsz tesztelni:
if (
  is_int($input) ||
  is_float($input) ||
  is_string($input) ||
  is_bool($input) ||
  (
    is_object($input) &&
    method_exists($input, '__toString')
  )) { ... }
Ha nem akarsz szigoru tipusossagot, akkor ezeket ilyen formaban ellenorizni kell, nincs fuggveny arra, hogy megallapitsd egy valamirol hogy az biztonsagosan osszekonkatenalhato-e egy stringgel. Nem mindig opcio a stringre castolas, mert van amikor az eredeti objektumot is szeretned megorizni valtozatlan formaban. Ha akarod, szorakozhatsz a catchable fatal error elemzesevel, de arrol nem fogod tudni, hogy az a string konverziobol jott-e, vagy a __toString belsejebol.

4, Ha egy featúra hasznossága kérdéses, miért teszik bele? Növeli a PHP kódbázisát, minden újabb sor potenciális hibaforrás, és azért kell is a PHP-t foltozgatni rendesen.


Ezt barmilyen feature-re el lehet mondani, nincs 100% hasznos vagy 100% haszontalan feature. En peldaul az eval() fuggvenyt kidobnam a PHP-bol, mert szerintem konyorog azert, hogy legyen egy code exec vulnod. Ettol fuggetlenul nem allithatom azt, hogy az eval() 100% haszontalan vagy akar azt, hogy kerdeses a hasznossaga, mert a hasznossag ott derul ki, ahol hasznalod. Siman lehet, hogy van akinek hasznos, masnak viszont egyaltalan nem.

5, Mi alapján döntik el, hogy egy featúra mennyire hasznos? Divat vagy racionális döntés áll a hátterében? Utóbbi esetben hol vannak a számok, ami alapján fel lehet mérni a hasznosságát? Mint a fentiekben látszik, vannak hátrányai is a dolognak, miért erőltetik rá a fejlesztőkre azzal, hogy beépítik a PHP-be?


Az ilyen iranyu aggalyaidat erdemesebb lenne a PHP levlistara vinni, ugyanis ott tudjak erdemben megvalaszolni. A PHP fejlesztese a core developerek szavazatai alapjan tortenik, az ezzel kapcsolatos RFC-ket itt talalod.

Altalanossagban igaz a programnyelvekre, hogy a tulajdonosaik dontik el, hogy mit csinalnak vele. Ha nem tetszik, es open source, forkolhatod es csinalhatsz egy sajat nyelvet belole vagy karban tarthatod a regi verziot biztonsagi frissitesekkel es kompatibilitasi patchekkel. Ha van hozza kozosseg es kozossegi akarat, konnyu dolgod lesz, ha nincs, akkor nehez dolgod lesz.

A PHP-ban az a jo, hogy senki nem kenyszerit ra a szigoruan tipusos programozasra. Ha akarod, bekapcsolod, ha nem nem. Sot mi tobb, hasznalhatsz szigoruan tipusos libraryket is, es ez a te kododat egyaltalan nem erinti.

6, A fenti fórumszálban volt egy olyan megállapítás is, hogy a PHP "egzotikus" funkcionalitását használva egy cégnek komoly munkájába került az áttérés a 7-es verzióra, míg ott, ahol csak a legalapabb funkciókból építették fel a kódot, nem, vagy csak minimális mértékben kellett módosítani, hogy 5.x után fusson 7-esen.

Ezek után felmerül az "egzotikus" funkciók hasznossága, hisz a hátrányukat látjuk: egy csomó plusz munkát hozhatnak bizonyos esetben, lehet újraírni sokmindent, tesztelni, ami sok idő és sok pénz. Hoznak-e ennyi hasznot? Ez főleg annak a fényében érdekes, hogy a fenti példák bizonyítják: az alapfunkcionalitást használva is lehet jó és működőképes szolgáltatást írni.


A PHP nyelvnek nincs formalis specifikacioja. PHP 7-re eljutott oda a nyelv, hogy szuletett egy abstract syntax tree es dolgoznak egy specifikacion, de peldaul az nem adott ami a Java esten, hogy evekkel - evtizedekkel kesobb is tudsz regi Javara irt kodot forditani, futtatni. Minden PHP kod gyakorlatilag masfel evig (az adott verzio kimulasaig) ervenyes, es onnantol szamitani kell arra, hogy valami tonkre megy. Eppen ezert erdemes a PHP-ban "mainstream" modon programozni, mert igy kisebb esellyel torik el verziofrissiteskor. Sajnos a minstream definicioja meglehetosen laza, leginkabb tapasztalati alapokon nyugszik. Peldaul en eleg ovatos lennek a destruktorok hasznalataval, mert a futasi sorrendjuk nem garantalt.

7, Egy programozási nyelv vagy egy szoftver csak attól fejlődik, hogy újabb és újabb featúrák kerülnek bele? A fentiek alapján például ez azért kérdéses. Vajon a hátrányokkal számolnak-e a fejlesztők és a megrendelők?


Egy nyelv attol fejlodik, hogy tamogatnak ujabb feature-oket. Pl. Javaba is bekerult a JSR 356 nyoman a websocket tamogatas. Uj feature, de a NodeJS elterjedesere valo tekintettel nem kerdeses, hogy volt ra igeny. Vagy a C++ 11-be bekerultek a smart pointerek, amik konnyebbe teszik a beszallast es a biztonsagos programozast.

Változik a világ, és változnia kell a számítástechnikának is. Most még csak robotok keresik a törhető webes alkalmazásokat, de csak idő kérdése, hogy a bűnözők mesterséges intelligenciát vessenek be ugyanerre a feladatra. Ebben az esetben pont, hogy a featúrák számának csökkenése a szükséges, hogy minél kisebb támadási felületet adjunk.


Ez ilyen formaban eleg eros altalanositas, mert nem a feature-ok mennyisege hatarozza meg egy kod torhetoseget, hanem pl. a programozasi praktikak, kodminosegellenorzes, stb stb stb. hasznalata.

Arrol nem is beszelve, hogy itt egymassal szembe kell helyezni a futtato/fordito kornyezet potencialis hibait a futtatokornyezeten belul elkovetett hibakkal. Altalanossagban szolva ha pl. egy adott feature-el egy csomo PHP-s program biztonsagosabb lesz, cserebe a futtatokornyezetben van 1-2 javithato bug, akkor azzal jobban jarunk, mert a futtatto kornyezetet konnyebb patchelni mint a kismillio PHP programot amik a neten szanaszet hevernek. (Persze ehhez nem artana vegre egy nyelvi specifikacio es visszafele kompatibilitas a futtato kornyezetben.)

A PHP-ban ilyen feature-ok voltak pl. a register_globals kukazasa vagy a filter infrastruktura bevezetese.

A szolgáltatások számának csökkenése a kód gyorsulását is magával hozhatja, ami ugyancsak csökkenti a karbantartási és az üzemeltetési költségeket.


A szolgaltatasok nem fognak csokkenni, mert a nyelvbol nem veszel ki feature-oket, vagyis a sebesseg maximum stagnal. A PHP 7-tel jott nagy sebessegnovekedes eppen annak koszonheto, hogy kitakaritottak a kodot es beleraktak egy uj feature-t (AST).

Altalanos tapasztalat

Egy ideje mar szigoruan tipusosan programozok PHP-ban, fokozatosan bevezetve a fajljaimban, illetve egyre tobb Hack kodot is irok. Az eddigi tapasztalatom az, hogy a strict mode bekapcsolasa a meglevo programjaimban nem okozott fennakadast, viszont megfogott 1-2 olyan bugot, amit egyebkent nem vettem eszre es tenyleg bug volt.

Cserebe elkezdtem kidobalni a kodombol a mindenfele input tipus ellenorzo fuggvenyeket. (pl. az amit kaptam, szam-e?)

Hozza tartozik, hogy eleg regen tipus-orientalt szemlelettel dolgozom, igy raall a kezem a tipuskorrekt kod irasara. YMMV
2

Contractok

BlaZe · Dec. 21. (Sze), 13.14
Janoszen jól rávilágított pár dologra, én még az alábbit tenném hozzá:

2, Érdemes-e ilyet használni?

Production kódban egy Fatal error, amit ilyen hiba okoz, megengedhetetlen, és egyébként is, nem is feltétlenül szükséges. Ráadásul a kód 100%-át lefedő tesztek szükségesek az ilyenek kiszűréséhez, aminek a megvalósítása szinte lehetetlen.
A függvények, metódusok gyakorlatilag contractok két programrész között. Kikötjük, hogy egy megadott típust várunk, és cserébe azt ígérjük, hogy az adott típussal helyesen működünk. Továbbá csak azzal a típussal működünk helyesen. Ezt végiggondolva milyen garanciákat érzel a helyes működésre, ha egy függvény/metódus az elvárt alma típus helyett körtét kap paraméterül? Ha esetleg semmilyet, akkor miért akarnád továbbengedni a futást? Melyik esetben kapsz el biztosan tesztelés közben típus problémát, ha előfordul?

Van, ahol hasznos megfontolni a fail-fast designt, mielőtt elgyűrűzik esetleg valami random működés.
3

6. félre magyarázás

Pepita · Dec. 21. (Sze), 19.50
a PHP "egzotikus" funkcionalitását használva egy cégnek komoly munkájába került az áttérés a 7-es verzióra, 

Ezt senki sem állította így.
Ha a komoly munkával kapcsolatban az én comment emre céloztál, akkor:
- nem azt linkelted;
- ha gondolod, kifejtem újra ,hogy mitől volt nagy feladat.

De az idézett rész nem fedi a valóságot.

Update
17 és 2x comment em újra olvasandó Gábor, semmilyen egzotikumrol nincs szó, ellenben világosan ott van, hogy mitől volt nagy falat.
Bocs, de nem vártam tőled, hogy kiforgatod a szavaimat, ha nem ez volt a cél, akkor én kérek elnézést, de nagyon félre értettél vmit.
4

Elnézést

Hidvégi Gábor · Dec. 22. (Cs), 08.45
Bocsánat, szerintem én értettem félre valamit. Kifejtenéd, hogy miért kellett átírni a 7-esre való váltáskor a kódotokat?
5

Bocs a késésért :)

Pepita · Dec. 25. (V), 20.05
Talán az egyik legtöbb munkát jelentő változás volt az Indirekt hivatkozások / Variable variables felderítése és átírása.
Végül a '->$' -ra való keresés és szemmel / kézzel javítás volt a megoldás, ez felettébb időigényes.
Arról van szó, hogy a PHP 7 már szigorúan balról jobbra oldja fel a hivatkozásokat, szemben a korábbi verziókkal.
Changes to the handling of indirect variables, properties, and methods

Mivel a $ jel után tetszőleges változónév lehet, ami nem biztos, hogy tömb, így erre nehéz lenne regexp-et / progit írni, ami biztonsággal megtalálja. Egyszerűbb volt fenti kódrészre keresni - rengeteg találat mellett.

Második, nálunk legtöbbet előforduló hiba az Öröklődés, kompatibilis függvények volt.
Itt arról van szó, hogy a szülő class XY fv-e mondjuk 1 db (string) paraméterrel dolgozik, de egy leszármazott felüldefiniálja ezt a fv-t, és itt már több paraméter szerepel. PHP 5.x ezt megengedte, PHP 7 nem ("...should be compatible with...").
Példa:
class A  {
	public foo ($a) {
		Echo $a ;
}
} 

class B extends A {
	public foo ($b, $c = '', $a) { 
/* more args than in class A and different order-> warning */
		echo $a . $b . $c ;
}
} 
Megoldás:
a.) B->foo() fv-ét átnevezed és hozzá refaktorálsz;
b.) A osztályban felveszed ugyanezeket a paramétereket, valami lényegtelen default értékkel.

Ezeknek felderítésére (is) használtuk a Php7cc nevű progit, fontos, ha valaki ez alapján kezdene el migrálni, sok minden van, amit ez az eszköz nem tud.
Természetesen ez a kompatibilitási probléma "magától" kijött a unittesttel lefedett kódon, a "maradékot" már nehezebb volt megtalálni.

A Php7cc még megtalált pár hasznos dolgot, az alábbiakat viszont kézzel is kerestük (removed functions):
  • split fv removed from PHP7, helyette preg_split
  • mysql_… fv-ek mind kikerültek elavultságuk miatt, nálunk ez nem volt gond.
  • Php4 stílusú konstruktor: class foo { function foo() {... E_DEPRECATED. Helyette __construct()
  • Ne hívj statikusan non-static fv-t. (deprecated, will remove) - na ezt sem könnyű felderíteni.

Nagyjából ennyi, ezt is egy akkori doksinkból szedtem össze, bő fél év távlatából. :)
Amit korábban említettem, miszerint "a kód max 2%-a változott", szerintem valós, nem néztem vissza az akkori commit-okat, az biztos, hogy leginkább a sok keresés, adott kódrészlet megértése és a lehetséges (és optimális) PHP 7 -es megoldás megtalálása volt a feladat oroszlánrésze.
6

Nono

janoszen · Dec. 26. (H), 13.28
Ebben azert van par no-no amire pl a PHPStorm is hisztizik

1. Parameter default ertekek adasa, majd utana default ertekek nelkuli parameter.
2. Mivel a PHP-ban nincs overloading, ezert a fuggvenyek szignaturajanak illik megegyeznie es ugyanugy viselkedni(Liskov substitution principle)
3. Statikus fuggvenyek. Tudom, sokan szeretik mert egyszeru hasznalni, de a tesztelhetoseget elegge rontja ha mindenfele static hivasok vannak keresztbe-kasul a kodban.
4. Variable variables. Ha hasznalsz jo IDE-t (khm. PHPStorm), akkor a valtozo valtozokra nem kapsz tipusellenorzest es automatikus hibafelderitest.
7

Re: nono

Pepita · Dec. 30. (P), 13.47
1. Nincs default nélküli, olyan volt, ahol a szülőnek pl 1 paramétere volt, gyerek overload-nak pedig 2. Itt a gyerek is megkapta (ha kellett) a default értéket, amit aztán ellenőriz és hibát dob, ha kell (nem egy szép megoldás, de ez van).
2. Nem csak illik, hanem kell is, másképp kapsz szép kis warningokat. Ez nem baj, szerintem nagyon helyes, ésszel kell fejleszteni.
3. Jogos, én sem szeretem, de itt arról volt szó, hogy nem is statikus fv-t hívtak statikusként. :(
4. Jelenleg pont PHPStorm-ot használok (de igazán jó IDE-t még nem találtam). :) Igen, ez így van, mégis helyenként szükség van rá. Amiatt sem szeretem, mert kevésbé átlátható a kód.