Silex – menőségből jeles
Az egyik fórumtémában találkoztam a Silex nevével, és gondoltam, utánaolvasok, mi is ez. Amit találtam, megdöbbentő volt.
Mivel nem önálló termék, a telepítése kézzel nehézkes, a Composert ajánlják hozzá, ami egy újabb függőség.
Ha valaki menőségi faktor alapján választ magának keretrendszert, nem szabad tovább keresni, a Silex maximális pontszámot ér el. A javascript világából ismerős lehet a callback-ek használata, és mindjárt az is eszünkbe juthat, hogy ezektől évekig próbáltak megszabadulni (callback hell), még szabványt is módosítani kellett emiatt, node idiótaságért a nodejs közösségnek sem kell a szomszédba mennie. A node.js esetében azért van szükség callback-ekre, mert az I/O műveletek aszinkron módon történnek; viszont a PHP-ban sorosan lehet programozni, emiatt ez a választás például az olvashatóságot csökkenti, de előnnyel nem jár.
A fenti kódból azonnal látszik, hogy például a külső változókat a névtelen függvényünkbe nem egyszerű paraméterként, hanem a
Az is feltűnhet, hogy sablonozás nincs, ha például HTML vagy XML kimenetet gyártunk, azt nekünk kell megoldanunk.
A Silexben sok olyan konvenció van, ami teljesen unintuitív, és szembemegy a hagyományokkal, ilyen például a
Ez hasonló, mint jQuery-ben a
A keretrendszer lehetőséget ad egyéb HTTP metódusok (PUT, PATCH) használatára. Mindez szép, jó és "szemantikus" (ami menő), csak épp teljesen felesleges, és más szerepe nincs, csak a programot bonyolítja, hibaforrás.
Lehetőség van a kontrollereink bizonyos szűrésére, mint a fenti példában láthatjuk. Itt is előjön a keretrendszer architektúrájának hátránya: a szűrőfeltételek a kontroller után vannak, ami teljesen ellentmond a józan észnek. Ha az ember elkezdi olvasni a kódot, előbb van az, hogy mit csinálunk, mint az, hogy mikor. Sehol máshol nincs ilyen, ez teljesen unintuitív, nem véletlenül nem terjedtek el annyira a hátultesztelő ciklusok. Ráadásul elvesztjük az
De – és ez az, ami a legmenőbb – lehet a metódusokat chainingelni. Ettől a programozó egy mahagóni borítású szobában érezheti magát, az egyik kezében egy szivarral, a másik kezében egy pohár kétszáz éves whiskey-vel, s közben épp egy lenge öltözetű karibi szépség próbálja megcsókolni, a nyelve hegyén egy kis kék kapszulát egyensúlyozva.
Egy keretrendszerhez kötni magunkat mindenképp kiszolgáltatottság, mert nem rajtunk múlik, változik-e az API-ja (fő verzióváltáskor általában szokott mindenhol), mikor javítanak ki hibákat, mikor tesznek bele újakat, mikor fejezik be a fejlesztést. Főleg akkor nem túl ésszerű, ha a keretrendszer feladatait pár alap PHP függvénnyel mi is el tudjuk látni, ráadásul gyorsabban és hatékonyabban, kötöttségek nélkül. Nem beszélve a rejtett Symphony függőségről, ami merevvé és törékennyé tesz mindent, ami ráépül.
Az unintuitív elnevezések, hátultesztelő feltételek és felesleges funkcionalitás ha másra nem is jó, de kiváló "hogyan ne csináljunk valamit" példává teszi a Silexet.
■ Bemutatás
A honlapja szerint ez egy PHP mikro-keretrendszer, ami egy nagyon érdekes kijelentés. A githubról letöltve a forrása majdnem kétszáz kilobájt, ami azért nem mondható olyan kevésnek. De ez félrevezető, mert bizonyos részei a Symphony moduljaira épülnek, amit külön nem töltöttem le, de sejthető, hogy nem a tárhelyet fogja csökkenteni; a SecurityServiceProvider.php például a szülőjének ötven komponensére támaszkodik.Mivel nem önálló termék, a telepítése kézzel nehézkes, a Composert ajánlják hozzá, ami egy újabb függőség.
Használat
Nézzük meg az első példát (kicsit kiegészítve):$blogPosts = array(
1 => array(
'date' => '2011-03-29',
'author' => 'igorw',
'title' => 'Using Silex',
'body' => '...',
),
);
$app->get('/blog/{id}', function () use ($blogPosts) {
if (!isset($blogPosts[$id])) {
$app->abort(404, "Post $id does not exist.");
}
$output = '';
foreach ($blogPosts as $post) {
$output .= $post['title'];
$output .= '<br />';
}
return $output;
});
1 => array(
'date' => '2011-03-29',
'author' => 'igorw',
'title' => 'Using Silex',
'body' => '...',
),
);
$app->get('/blog/{id}', function () use ($blogPosts) {
if (!isset($blogPosts[$id])) {
$app->abort(404, "Post $id does not exist.");
}
$output = '';
foreach ($blogPosts as $post) {
$output .= $post['title'];
$output .= '<br />';
}
return $output;
});
Ha valaki menőségi faktor alapján választ magának keretrendszert, nem szabad tovább keresni, a Silex maximális pontszámot ér el. A javascript világából ismerős lehet a callback-ek használata, és mindjárt az is eszünkbe juthat, hogy ezektől évekig próbáltak megszabadulni (callback hell), még szabványt is módosítani kellett emiatt, node idiótaságért a nodejs közösségnek sem kell a szomszédba mennie. A node.js esetében azért van szükség callback-ekre, mert az I/O műveletek aszinkron módon történnek; viszont a PHP-ban sorosan lehet programozni, emiatt ez a választás például az olvashatóságot csökkenti, de előnnyel nem jár.
A fenti kódból azonnal látszik, hogy például a külső változókat a névtelen függvényünkbe nem egyszerű paraméterként, hanem a
use
kulcsszó segítségével tudjuk injektálni. A keretrendszer felépítése miatt a belső függvény paraméterlistája fix, emiatt van szükség erre a megoldásra. Ez megköti a kezünket, a minimális php verziót, és igazából semmilyen előnye nincs a hagyományos függvényhíváshoz képest, ahol szabadon adhatjuk meg a paramétereket és sorrendjüket.Az is feltűnhet, hogy sablonozás nincs, ha például HTML vagy XML kimenetet gyártunk, azt nekünk kell megoldanunk.
A Silexben sok olyan konvenció van, ami teljesen unintuitív, és szembemegy a hagyományokkal, ilyen például a
$app->get()
. Alkalmazások fejlesztésénél megszokott, hogy ha egy függvény vagy metódus neve ige, az valamilyen cselekvést jelképez (például a clean_input_variables()
megtisztítja a bejövő változókat), ha "on"-nal kezdődik, akkor pedig egy eseménykezelőt takar (például az onclick()
egy kattintást fog lekezelni). Ezzel szemben a Silexben a $app->get()
a várakozásokkal ellentétben nem fog megszerezni semmit, hanem egy HTTP GET
kérést fog feldolgozni. Ugyanígy a $app->post()
nem adatokat fog küldeni az alkalmazásunk számára, hanem a HTTP POST
kérést dolgozza fel. A $app->error()
nem hibát fog generálni, hanem hibakezelőt regisztrál.Ez hasonló, mint jQuery-ben a
click()
metódus, amitől azt várná az ember, hogy egy elemen egy kattintást fog szimulálni (mint a natív click()
metódus), de nem, mert igazából ez egy eseménykezelő hozzárendelését végzi el. Nem tudom, hogy a Silexben is ezt az illogikus nevezéktant vették-e át, vagy csak rövidíteni akartak, mindenesetre zavaró. A $app->abort()
már az alkalmazásra vonatkozik, nem a kérésre.A keretrendszer lehetőséget ad egyéb HTTP metódusok (PUT, PATCH) használatára. Mindez szép, jó és "szemantikus" (ami menő), csak épp teljesen felesleges, és más szerepe nincs, csak a programot bonyolítja, hibaforrás.
$app->get('/blog/{id}', function ($id) {
// ...
})
->when("request.headers.get('User-Agent') matches '/firefox/i'");
// ...
})
->when("request.headers.get('User-Agent') matches '/firefox/i'");
Lehetőség van a kontrollereink bizonyos szűrésére, mint a fenti példában láthatjuk. Itt is előjön a keretrendszer architektúrájának hátránya: a szűrőfeltételek a kontroller után vannak, ami teljesen ellentmond a józan észnek. Ha az ember elkezdi olvasni a kódot, előbb van az, hogy mit csinálunk, mint az, hogy mikor. Sehol máshol nincs ilyen, ez teljesen unintuitív, nem véletlenül nem terjedtek el annyira a hátultesztelő ciklusok. Ráadásul elvesztjük az
else
lehetőségét, azaz bizonyos esetekben bonyolult feltételeket vagyunk kénytelenek írni. Arról nem is beszélve, hogy a példa működéséhez újabb Symphony modult kell betölteni, pedig két függvényhívással elérhetnénk ugyanezt.De – és ez az, ami a legmenőbb – lehet a metódusokat chainingelni. Ettől a programozó egy mahagóni borítású szobában érezheti magát, az egyik kezében egy szivarral, a másik kezében egy pohár kétszáz éves whiskey-vel, s közben épp egy lenge öltözetű karibi szépség próbálja megcsókolni, a nyelve hegyén egy kis kék kapszulát egyensúlyozva.
Mire jó?
A Silex dokumentációja a legtöbbet a kontrollereknek és a routingnak szenteli, pedig igazából ez nem egy túlzottan érdekes feladat, a bejövő GET és POST paraméterek alapján pár alapif
és függvényhívás segítségével meg lehet oldani.$darabok = explode('/', $url);
if ($darabok[0] === 'blog') {
$bejegyzesek = (...) ;
$bejegyzes = filter_var($darabok[1], FILTER_SANITIZE_NUMBER_INT);
if (!isset($bejegyzesek[$bejegyzes])) {
(...)
}
}
if ($darabok[0] === 'blog') {
$bejegyzesek = (...) ;
$bejegyzes = filter_var($darabok[1], FILTER_SANITIZE_NUMBER_INT);
if (!isset($bejegyzesek[$bejegyzes])) {
(...)
}
}
Egy keretrendszerhez kötni magunkat mindenképp kiszolgáltatottság, mert nem rajtunk múlik, változik-e az API-ja (fő verzióváltáskor általában szokott mindenhol), mikor javítanak ki hibákat, mikor tesznek bele újakat, mikor fejezik be a fejlesztést. Főleg akkor nem túl ésszerű, ha a keretrendszer feladatait pár alap PHP függvénnyel mi is el tudjuk látni, ráadásul gyorsabban és hatékonyabban, kötöttségek nélkül. Nem beszélve a rejtett Symphony függőségről, ami merevvé és törékennyé tesz mindent, ami ráépül.
Az unintuitív elnevezések, hátultesztelő feltételek és felesleges funkcionalitás ha másra nem is jó, de kiváló "hogyan ne csináljunk valamit" példává teszi a Silexet.
Min lepodtel meg?
Kb egy eve elkezdtem jatszani a Piccolo nevu csodaval, ami 100% modularis es a ket nagytol fuggetlen framework akart lenni, de idokozben elmult az eletembol a PHP, szoval annyiban maradt.
Mindenen
Ez az egész témakör ezer kérdést felvet, kezdve azzal, hogy hová tűnt a racionalitás? Miért kell minden hülyeséget belepakolni ilyenekbe? Biztos, hogy mindenre szükség van? Miért kell túlbonyolítani, amikor natív megoldásokkal minden szempontból jobb lesz a kód?
Egy "DB layer" az pár kilobájtos cucc, egy "router" is, onnantól kezdve elég pár szabályt betartani, és máris egy függőséggel kevesebb.
Írj sajátot
Fentiek fényében azt gondolom, hogy neked is inkább saját framework-re lenne szükséged, miért ne fejlesztenél sajátot?
Azért egy fw-nek az a
Nagyon jó :)
Javasolnám, hogy egy kicsit
Gyakorlatilag a cikk összes gondolatára van ellenérvem. Sajnálom, hogy csak a Silex dokumentációig jutottál és azt sem sikerült teljesen átvenned/átadnod. Említenék még pár Silex adta lehetőséget, amiről talán érdemes volna beszélni mélyebben:
- Dependency Injection
- Middleware
- Hiba kezelés
- Validáció
- Milyen függőségeket érdemes használni ahhoz, hogy intuitivabb módon tudd használni a rendszert >azért nem mindig rosszak a függőségek sem
- Nem csak callbackk-ekkel tudsz operálni
- Meg egyáltalán, hogy mire érdemes használni a keretrendszert
Hogy az utolsó pontra választ adjak:
Nem minden keretrendszer jó mindenre. A Silex-et szerintem HTML oldalakhoz nem érdemes használni, működik a TWIG Silexben is egyébként, meg natívan is lehet HTML-t generálni. Inkább REST API-k készítésére használnám, amire viszont kiváló és szerintem gyors. Egy adatbázis műveleteket végrehajtó kérést opcachel simán 70 ms körül lehet tartani (tárhelyszolgáltatónál!!!), ami szerintem azért nem rossz (nyilván lehetne jobb, és lehet is, ha megfelelő környezetben fut).
És egy általánosabb gondolat:
- Érdemes keretrendszert használni?
- Szerintem igen.
- És miért?
- Minek fejlesszem le újra, amit valaki már megcsinált, nem mellesleg van hozzá dokumentáció és mögötte kommuniti. És felteszem a kérdést: jobban meg tudom csinálni a sajátomat, mint azok a valakik? És ha igen, akkor azt újra fogom használni egy másik projecten is? Ha igen, akkor egy újabb függőséggel állunk szembe, ami a sajátunk... Ha nem, akkor meg drága lesz a fejlesztés hibázási lehetőséggel, nagy eséllyel dokumentáció nélkül.
- Érdemes függőségeket használni?
- Szerintem igen.
- És miért?
- A fent leírtak miatt; tartom a véleményem.
A microframeworkok (és a Symfony is) - ahogy én azt korábban levettem - önmagában arra jók, hogy lekezeljék a kérést és adjanak lehetőséget válasz adásra, és ezt támogassa meg egyfajta alap működéssel, ami ha kell cserélhető és kiterjeszthető, adott esetben meg jó, ahogy van.
Még egy dolog: ha a project mérete a függőségek miatt nagy, akkor kell csinálni egy build scriptet, ami kivágja azokat a fájlokat, amikre biztosan nem lesz szükséged runtime (unit tesztek, readme fájlok, batch fájlok, licens fájlok, stb), és a 10mb-os projectből csináltál egy 1mb-osat.
Mélyebben
Függőségek
Már a szóban benne van minden: képzeld magad egy láncra kötött élőlénynek. A függőség keretet ad és korlátoz, mert csak egy bizonyos szintig enged elmenni. Minél több a függőség, annál kevésbe tudsz mozogni, és egyre nagyobb a veszélye, hogy megfojtod magad.
Ha embernek születtél, minden függőség rabszolgává tesz. Ha viszont ezeket a kereteket te állítod fel magadnak, akkor minden úgy fog történni, ahogy kell.
Ha kutyának születtél, akkor talán jó is, hogy vannak ilyenek, mert a kutya ösztönlény, némileg tanítható, de benne van, hogy harapsz, ezért kell a lánc. És ha kutyának születtél, ne vitatkozz emberekkel!
Mindez lefordítva azt jelenti, hogy a függőség kiszolgáltatottá tesz azoktól, akik készítik. Ezért célszerű azt a társaságot vizsgálni, akik az adott szoftvert írták. Egy PHP belátható ideig működni fog és elérhető lesz, de egy node.js esetében ez már nem mondható el, mert sosem tudhatod, megint mikor válik ketté a gyerekes attitűdökkel rendelkező fejlesztői közösség, mikor találnak ki újabb megoldást a callback hell-re stb. Instabil.
Ha a függőség a te kezedben van, az a legjobb, mert minden pontosan úgy fog történni, ahogy elképzelted.
Aztán a bejövő paramétereket tisztítani kell és eldönteni, mit csináljunk: if valid(paraméter), ment(adatok), különben hiba(). Minek kéne ezt túldimenzionálni?
Szeretném látni! Szeretem
Szeretem volna gyorsan reagálni, idő hiányában nem fejtettem ki, ha lesz rá kapacitásom megteszem.
Nem megfelelően fogalmaztam, jobb kérdés lenne talán az, hogy: az enyém tud-e többet annyival, hogy megérje lefejleszteni, van-e hozzáadott értéke egy már kész komponenshez képest.
A jó döntést meg kell tudni hozni. Tényleg van egy csomó olyan komponens, amit tök felesleges lefejleszteni, mert kész, ott van, használd, működik. Cél szentesíti az eszközt: ha nem válik be egy függőség sem az igényem kielégítéséhez, nyilván készítek egy sajátot. Ott a WordPress, meg egy rakat plugin. Eszembe nem jutna lefejleszteni egyet sem, mert mindenre van megoldás. Érdemes bele ölni a saját időmet ha más már bele ölte a sajátját, és nekem adta? Szerintem nem.
Nem félek. Miért félnék? Mindenki hibázik, lehet javítani. Dokumentálom a projectet, viszont itt nem teljesen tiszta, mire gondolsz.
Aztán a bejövő paramétereket tisztítani kell és eldönteni, mit csináljunk: if valid(paraméter), ment(adatok), különben hiba(). Minek kéne ezt túldimenzionálni?
De ezeket miért oldjam meg én, mikor megoldja helyettem más? Ismét egy kérdés: szép lesz a kódod egy rakat iftől, ahogy egy idő után azt sem tudod mi van? Van 50 sornyi validációd, meg egy sornyi adatbázis műveleted. Miért írjam meg az 50 sor validációt ha ott van rá a megfelelő komponens, ami blackboxként működik - igen, nem is akarom tudni hogyan, az a lényeg, hogy oldja meg a problémámat. Használom a validátort, és 10 sorból megúsztam egy bárminek a validációját úgy, hogy látm is mi történik, nem csak debuggolom az ifeket.
Ha visszakanyarodunk a routingra, és egy REST API-ról beszélgetünk - ahol érdemes betartani bizonyos konvenciókat, akkor azért annyira nem triviális, hogy if/include. Illetve de, az, csak nem mindegy, hogy egy 2 paramétert lekezelő routra kell írnom egy regexet, amit fél órán keresztül debuggolok, hogy miért nem jó, + keletkezik néhány sor kód, ami megint átláthatatlanabbá teszi a saját kódomat, vagy használok egy jól működő routert, ami mögött van egy normális leírás, amit egyszer elég elolvasni, felfogni és használni.
az enyém tud-e többet
Persze mindenki magából indul ki. Én például mindig magam fejlesztettem le mindent, így az évek során összejött egy kis eszközkészletem, függvénytáram, amit minden projektben használok, és van egy rálátásom, mi hogyan történik egy kliens-szerver kérés kapcsolatban.
Ha valaki világéletében keretrendszerek segítségével fejlesztett, valószínűleg fogalma nincs sok mindenről. De így miről lehet vitatkozni?
(...)
}->when("request.headers.get('User-Agent') matches '/firefox/i'")
Ekvivalens ezzel:
(...)
}
Ráadásul a PHP-s példában egyben látod az egészet, míga a Silexben a feltétel két részre van törve.
Az utóbbihoz ráadásul elég a PHP alap függvényeit használni, amik egyszerűek és ugyanolyan jól dokumentáltak, mint bármi más, sőt.
A tied azt, és csakis azt a
Egyetértek. DE, ha tudok időt spórolni, és az ügyfél ugyanannyit fizet érte, akkor nem fogom lefejleszteni.
Minden fejlesztőnek van ilyen tára, aki jó pár éve dolgozik, nekem is. Nem azt mondtam, hogy ne csinálj ilyet, mert van mikor ez kell. De ha egy olyanra van szükséged, ami készen van, akkor részemről nem kérdéses a dolog.
Ezt most kicsit magamra vettem, bocs - lehet nem jogosan... Tapasztalt fejlesztőnek tartom magam, és én is láttam már egyet, s mást, többek között ügyfeleket is, aki pont nem foglakozik azzal, hogy Silex, Symfony, saját, vagy más. Neki az a lényeg, hogy működjön, és ha olcsóbban ki lehet hozni keretrendszerből, amit jól ismersz, akkor miért ne?
Nem tudok olyan fejlesztő ismerősömről, aki valaha nem próbálkozott volna sajáttal. Szerintem az a legjobb tanuló pénz ;). Nagy részük rájött, hogy lefejlesztették azt, ami ott van az orruk előtt. A másék rész pedig dacból nem használja, mert az övé jobb - vagy úgy gondolja, hogy jobb.
Így már világos mire céloztál.
"nem tudom újra használni", értem ez alatt: túl speciális lett, hogy másnak is eladd
"drága lesz a fejlesztés": több munka magasabb költségeket generál
"nem lesz dokumentáció": aki azt mondja, hogy a saját maga által készített rendszert minden esetben olyan mélységik dokumentál, mintha azt egy közösségnek csinálná, akkor az szerintem hazudik.
Készüljön dokumentáció minden esetben, de csak annyi, amennyi kell, és amennyit elolvasnak!
Javítanék az egyik kódblokkodon, ha a teljességre törekszünk:
Igen, mert a két if más szerepet tölt be. Ennyi erővel a validációt, adatbázis szintű vizsgálatot meg még sok mást rakhatnék ide, egy darab if-be. (a getallheaders-t azért vizsgálom, mert volt már precedens korábban arra, hogy nem létezett ez a függvény, mert ha az emlékezetem nem csak, akkor ez egy apacheon futó PHP-s függvény, és nginx alatt lehet gáz > janoszen ezt talán pontosabban tudja)
Egyébként, lehet a téma
Keretrendszer
Igen
Nem tudom php-ban hogy van,
Én személy szerint nem azért használok függőségeket, mert ne tudnám jobban megírni. Ezek inkább arra valóak, hogy időt spórolj, és a projekttel foglalkozz a körítés helyett. A rosszul dokumentált függőségek néha még rosszabbak, mintha nulláról írnál egy keretrendszert a témára.
Ezzel a témával kb 10 évvel
Frameworköt teszteltél... ott általában akadnak előre letett dolgok :)
Pár dologhoz azért lenne hozzáfűzni valóm.
Bloated: És akkor mi van? Hála az istennek, hogy bloated. Francnak van kedve 10 év webezés után DB layereket, meg routereket írkálni. Ezek a komponensek pedig agyon tesztelt, optimalizált kóddal jönnek, megbízható, open soure forrásból. Akár tetszik akár nem, amit te írsz csak rosszabb lehet, még akkor is ha jó programozó vagy. Gondolom nincs mögötted 7 millió dollár (symfonyba fektetett pénz) hogy frameworköt írj. Ezeknek a dolgoknak a performance impactja pedig minimális.
Dependency rossz?: Nem. A rossz dependency rossz. Ha van egy lib amire felépítetted a projektedet, de több baj van vele mint amit használ az rossz. Ezért kell jól átgondolni, mielőtt használsz egyet. Normális esetekben nagyon is jó a dependency.
Request-response framework: Ha mindenáron csak egy tök alap return $response dologra vágysz, akkor szerintem nézz rá a Go framekre, ott elég nagy divat ez. (Bár pár dolgot azok is hoznak magukkal)
Köszönöm! +1
Ezzel a témával kb 10 évvel
Ha meg ennyi idő alatt nem
Írtam én kb mindent, a routertől a teljes frameworkig. Rengeteget lehet belőle tanulni, többek között azt is, hogy többet nem fogok írni, mert nem tudok jobbat mint ami fentvan githubon és több 100 ember munkája van benne.
Ez a legjobb példa egy kitűnő dependencyre: opcionális, de támogatott. A symfony/silex egy decoupled componens halmaz, az alapból kapott "frameworkök" csak előre definiált részhalmazok, amiből kiválaszthatod a projektednek megfelelőt.
Symfony > Symfony microkernel > Silex
Ezen kívül vannak még jobban specializált dolgok, mint pl a Symfony Rest edition, a most készülő Symfony Flex, vagy a zseniális API platform.
Hat azert
A Silex elso ranezesre nekem szimpatikus volt, bar en szeretem az OOP-sabb, osztalyra routolt requestes semakat.
A DB hianya inkabb oda vezet vissza, hogy a Doctrine kb. az egyetlen mainstream DB layer, jo lenne ha lenne legalabb egy nagyobb konkurens.
Ez igaz, hiba mindenhol van.
Jellemzoen
Ezek a komponensek pedig
Lehet, hogy az egyes elemek overhead-je minimális, de ez összeadódik, mert rákényszerülsz – pont az általánosság miatt – többnek a használatára.
A heartbleed óta pedig tudjuk, hogy mennyire lehet komolyan venni a nyílt forrást meg a many eyeballs-t.
De ha már választani kell, akkor inkább egy kisebb keretrendszert (Silex), mint egy nagyot (Symphony).
Amikor már elfelejteném,
+1
+1
HG, ennek a cikknek mi volt a
Valamint egy frameworkkel szemben a PHP backward compatibility-jét felhozni érvként szerintem eléggé öngól. Hirtelen semmi mást nem tudok mondani, ami magasabbról tesz rá, mint a PHP. Ha ezt a nyelv okozta bizonytalanságot esetleg egy framework el tudja fedni a fejlesztők elől, akkor talán megéri megtanulni.
Mondjuk ez csak egy fórum
Cél
Vegyük a routingot:
// ...
})
->when("request.headers.get('User-Agent') matches '/firefox/i'");
Miért nem intuitív? Mert ránézel a kódra, és elsőre nem egyértelmű, hogy mikor fog lefutni.
A keretrendszer készítői kiemelték az url első részletét, ami a fenti példában a "/blog"-nak felel meg, és erre építették fel a koncepciót. Miért? Mint látható, ez nem elég, mert van egy olyan további feltétel is, hogy ráadásul a böngészőnek firefoxnak kell lennie. Tehát az url == "blog" és böngésző == "firefox"-nak egyszerre kell teljesülnie, egyenértékűek. Akkor miért vették külön?
Ráadásul elképzelhető az, hogy egy feltétel több kombinációban szerepelhet, például:
if (böngésző == 'firefox')
route 'blog_firefox';
else
route 'blog';
}
Ezzel szemben Silexben már csoportosítani kell:
$blog->get('/', function () {
return 'Blog home page';
});
$blog->get('/', function () {
return 'Firefox';
})->when('User-agent = firefox');
$app->mount('/blog', $blog);
Melyik az egyszerűbb és átláthatóbb (és mellékesen gyorsabb)? Amíg te azzal szerencsétlenkedsz, hogy megtanuld, a különböző esetekben a framework melyik "szintaxisát" kell használni, én még mindig csak a natív if-eket pakolgatom nulla befektetéssel.
A fentiek fényében nem értem a kérdésed.
$app->get('/blog', function
Kismillió dolgtól függhet még, hogy egyáltalán lefusson a függvény. Nem csak attól, hogy '/blog', Firefox. Lehet, hogy menet közben megakasztja egy másik middleware. Ami fenti kód blokkban van, az két if, de mire ideér a program, addig lefut még sok if, amit egyben szerintem nem szeretnél látni soha. Lehet, hogy szét van tördelve, viszont mindennek megvan a helye. Van authentikációs szűrő, authorizációs szűrő, ilyen szűrő, olyan szűrő... azt tudod, hogy egy végpontra milyen szűrők vonatkoznak, tehát tudod hol keresd a bajt, ha van.
Előbb kiderítem, mi történik itt, mint egy includokkal teli if halmazba.
Van honnan megtanulni, tehát nincs probléma, nem veszem el más idejét. A saját kódodat ki magyarázza el nekem úgy, hogy megértsem és ne vegyem el az idődet?
Van authentikációs szűrő,
Egyébként továbbra is a routernél vagyunk, ami útválasztó. Nálunk legalábbis egy van belőle, és a klienstől kapott adatok alapján eldönti, hogy merre menjen tovább a feldolgozás. Szerintem sokkal egyszerűbb, hogy ha tudom, hogy routing probléma van, egy helyen kell keresnem, nem szétszórva ezer fájlban.
Ez azzal az előnnyel jár, hogy nulla betanulási ideje van, míg a Silex esetében utána kell járnia a programozónak, hogyan működik, fejben kell tartania azokat a helyeket, ahol routing történhet.
Auth
Autentikacio = be tudsz lepni (van valid usered es jelszavad)
Autorizacio = van jogosultsagod egy adott eroforrasra.
Egyebkent ezek egyikenek sincs helye a routingban, ennek illik az uzleti logikaban laknia.
Mi a különbség az
Ezt már megválaszolták, azt hittem ez egyértelmű, bocsi.
Nézzük a következő esetet:
- Adott egy termék, és egy adminisztrátori funkció: hozzászeretnék rendelni három kategóriát egy termékhez, a kategóriákat a megfelelő felületen egy checkbox lista segítségével jelöltem be (a listában 3 kategória volt bejelölve). Egy ilyen jellegű funkció többé kevésbbé van minden admin felületen. A rendszerben van anonymus és vásárló felhasználó is. Mit kell vizsgálnod?
- A route-ot
- Adatbázis kapcsolatot
- Be van-e lépve
- Van e joga terméket módosítani (admin-e)
- Létezik-e a módosítandó termék
- Léteznek-e a kategóriák, amit hozzá akar rendelni a termékhez
- Hozzá vannak-e már rendelve a kategóriák a termékhez
Ha minden stimmel, mehet az action.
Az eddigiek alapján ezt naggyából egy, max 2 if-fel oldanád meg. Jól értem? Ha igen, mutatnál egy példa kódot - akár pseudot?
Igen ismerem a require-t, sőt a require_once-t is :D. Viccet félre téve, igen, szívesen használók namespaceket is, kiváló eszköz arra, hogy autoloadolni lehessen MINDENT. Ráadásul segít átláthatóvá tenni, és megfelelően rétegeltté tenni az alkalmazást, meglepő mód a Silex alkalmazásaim is így vannak felépítve. Külön validációs-, authentikációs, authorizációs, controller-, service-, adatbázis- (entity+repository+pdo) rétegek mindenféle objektum tarnszformálós megoldással, meg egyéb utilokkal. Hogy miért ennyi osztály, mikor van ami működik tömbbel is (pl adatbázis kezelés)? Én az az asszociatív tömb kerülős fajta vagyok.
Nah, legalább egy valami, amiben egyet értünk, sajnos nem mindenki így áll neki ;)
Az eddigiek alapján ezt
return array('hibakod' => 500);
}
if (!belepve()) {
return array('hibakod' => 401, 'url' => 'bejelentkezes');
}
if (jogosultsag_bitmaszk('termek_modositas') & felhasznalo_kulcs() == 0) {
return array('hibakod' => 403);
}
if (!($termek_adatok = termek_adatok($_POST['termek']))) {
return array('hibakod' => 404);
}
foreach ($_POST['kategoriak'] as $kategoria) {
if (!($kategoria_adatok = kategoria_adatok($kategoria))) {
return array('hibakod' => 400);
}
if (in_array($kategoria, $termek_adatok['kategoriak'])) {
return array('hibakod' => 500);
}
}
A visszatérési tömbbe nyilvánvalóan bele lehet tenni a pontos hibakódot/hibaüzenetet.
Már csak azért sem értem, mert ha úgyis be kell tölteni mindent, akkor minek használod az autoloadert? Nem egyszerűbb beletenni a fájlneveket egy tömbbe, végigiterálni és beinclude-olni őket?
Annyi if-fel oldanám meg,
És ezt az összes actionbe beteszed, holott az első kettő mondjuk 100 másik action feltétele?
:D ... bocs.. erre nem tudok mást mondani. Egyébként nem kell betölteni mindent. Ha van 1000 osztályom/fájlom, de egy kérés alkalmával csak 14-et használok, akkor az a 14 lesz betöltve.
Nem. Ott az autoloader, megoldja, akkor megint minek dolgozzak vele feleslegesen?
Én százszor inkább dolgoznék
Ez egy példa saját projectről jó régről, mutat némi hasonlóságot. Azóta ebbe is alaposan bele tudnék kötni, de speciel nem azért, mert az if jobb lenne, vagy mert bármilyen szempontból is hatékonyabb. Annál ez is sokkal jobb. Nem tudom látod-e mire gondolok. Szerintem akik a Silexet favorizálnák jobban ebben a threadben, elég hamar meglátják mi itt a baj :)
Ami a (végrehajtási) sebességet illeti, azt akkor érdemes döntési szempontként felhasználni, ha olyan kimérT lassulást okoz, ami nem elfogadható. Egyébként a fejlesztő ideje sokkal drágább, mint a vas. És ahol ilyen szinten felmerül a mikrooptimalizáció, ott már az architektúra rég támogatja a skálázást. Ha nem, architektet kell cserélni, nem metódushívást.
Ebben az általad hozott esetben ráadásul arról se vagyok meggyőződve, hogy a sok if feltétlenül gyorsabb. Tekintsünk pl 5000 commandot. Az neked átlagosan 2500, worst case 5000 if. Nekem meg egy hashtable. Nincs kedvem lemérni, de szerintem én nyertem :) Nem tudom hol fordul, de valószínűleg már hamarabb.
Egyszerűségre kell törekedni
Szeretném BlaZe álláspontját
Random Silex project, írta smokey, részlet:
Ha már az actionben van az alkalmazás, akkor tudom, hogy:
- A user nek van joga lefuttatni
- Valid az adat
- És rendelkezésre áll minden infó, hogy végbe menjen az action.
A service osztályba egyébként még be van injectálva ez az (repository osztályok, mailerek, logger, stb.)
A fenti kód egy REST API alkalmazás részlete. Ránézek és magas szinten látom, mi történik. Az URI-k erőforrásra mutatnak, nincs bennük ige, csak főnevek. Az ige a HTTP verb, ami gyakorlatilag megmondja, hogy mit fog csinálni egy adott erőforrássa. Üzleti értelemben látod mi történik. Hogy forráskód szinten mi történik, az más kérdés.
Látom az összes belépési pontot. Ezeken keresztül engedek be bárkit is. Interface, szerintem tök jó.
Jah, és mi nincs a kódban? Anonymus. Hogy ezt mi oldja meg? Egy jó kis függőség ;). Mi a kód előnye? A DI segítségével sikerült úgy megcsinálni, hogy controller szintől befelé nem függök a Silextől (Akkor miért használok Silexet? Mert rugalmas, és megfelelő módon oldja meg a problémámat). Az alkalmazás üzleti része egy függőség, ami composerrel van behúzva, gyakorlatilag natív PHP a megfelelő libekkel. Ott van még a Silex függőségként, a kettőből pedig kerekedik egy alkalmazás.
Mit értem el ezzel, van egy karbantartható, jól szervezett, TESZTELHETŐ alkalmazásom!
PUT vs PATCH, nem menőségből van ott. Ahogy a mellékelt ábra mutatja: PUT-tal a full objektumot módosítom, PATCH-el pedig csak annak egy részét. Technikailag és üzletileg is más.
Akár le is dokumentálhattam volna kód szinten, hogy mi mit csinál, de minek, mikor az a fenti pár kódsor ezt megteszi. Részletes dokumentáció pedig az API doksiban.
Kérdések
Én hogyan oldanám meg?
A felsorolásodból látszik, hogy egyszerű megfeleltetéssel, hash táblával és egy ciklussal el lehet végezni a feladatot:
'/' => 'userInfo',
'/personal' => 'userPersonalInfo',
'/images' => 'userImages',
);
foreach (...)
Mivel a GET és a PUT/PATCH és társaik egy URL-en vannak, ezért a kérés feldolgozását magában a metódusban végezném:
if (isset($_POST['akcio']) and $_POST['akcio'] === 'modositas') {
ellenorizParameters();
saveFelhasznalo();
atdobAddress('/');
}
$adatok = getFelhasznaloInfo();
return 'akármi';
Így nincs szükség a HTTP igék miatt az URL-ek megkülönböztetésére.
Miért van itt is – mint Blaze
Én sem szeretem a redundanciát, de a routoknál valahogy jobb szeretem látni a teljes képet ilyen módon.
Nem tudom, jó kérdés. Ez alatt mit értesz: nincsenek egymás mellett az ugyanahhoz az url-hez tartozó műveletek?
Képfeltöltéskor PUT request szokott menni.
Pontosítanék, UserController, nem User. Szerintem pont, hogy tükrözik.
REST APIról beszélünk, tehát nincs munkamenet, user id attól függetlenül van, JWT authentikáció segítségével. Ugyanazt a műveletet végrehajthatja egy felhasználó, illetve egy admin is. Ha adminnal vagyok belépve, és ő egy másik userhez akar képet feltölteni, akkor ilyen módon tudja megcímezni az erőforrást, vagyi az X userhez tartozó képek listáját, amibe bele PUT-tól egy képet.
REST API, nincs indexelés. Ez egy regisztráció X-edik lépésében használt validációs pont.
A felhasználó semmit, viszont a REST kliens hibakezelője már annál inkább sok mindent.
Mert a felhasználó bizonyos részét (jelszó, személyes infó, kapcsolati infó) módosítani tök más logika, mint módosítani az egyéb más helyen tárolt felhasználói adatokat. Így egy 200 soros if halmaz helyett rendelkezésemre áll 4-5 olyan végpont, amiről ránézésre megmondom, hogy mit csinál.
És így lett a spagetti. Egy idő után az ilyen megoldásokkal az a baj, hogy az alkalmazás minden rétegében ott lesz minden. Nem mondom, hogy minden esetben, de egy adatbázis lekérdezésért felelős osztályban $_SESSION használat ne legyen. Az nem oda való. Úgyanúgy egy controllerbe se legyen egy darab lekérdezés se.
Off: POST/PUT
Az nem úgy szok' lenni, hogy új tartalmat
POST
-tal ozunk létre, meglévőt pedigPUT
-tal írunk felül? APATCH
-ről most eltekintek az egyszerűség kedvéért.Naggyából. Pontosabb, hogyha
Ettől függetlenül az én példám nem ezt tükrözi, jogosabb volna POST-tal fájlt feltölteni, és ahogy nézem elég sok helyen így javasolják. Régebben volt egy Amazon S3-as fájlfeltöltős story, ahol PUT requesttel kellett feltölteni (amit akkor egyébként én sem értettem, de elhittem a backendes embernek, hogy fájl felöltéskor jóvanazúgy'), innen maradt az emlék; rosszul.
Szvsz az ilyesmi tipikusan
A kohézió szerintem
Én úgy vagyok vele, hogyha
A beillesztett kódod pont
Nem ezekkel van a baj. Smokey elég nagy betűkkel kiemelte a lényeget: TESZTELHETŐ. Na az én megoldásom sem az, bár kb fél óra alatt azzá tehető. A te megoldásod viszont nagyon messze van tőle. Ezért kellenek az ilyen "túlbonyolítások". Egyrészt hogy tesztelhető legyen, másrészt hogy ne egy refaktorálás vezesse egy business feature szállítási idejét.
Teszt
A legjobb tudomásom szerint a teszt arra való, hogy egy szoftveregységre bemenő paramétereket eresztünk, majd ellenőrizzük, hogy a kapott adatok megfelelnek-e az elvártaknak.
Hogy adsz neki teszt
Teszt
Ezzel még elég messze vagy az
Miért?
Teszteknél nem foglalkozunk a működéssel, hanem azt nézzük, hogy az elvárt eredményeket kapjuk-e. Hiba esetén viszont a javítása közben már figyeljük, mivel mi történik.
Amit eddig mutattál kódot,
Plusz nem mindig elég a black box tesztelés. Ha van egy belső állapotod (pl egy state machine), akkor arra kíváncsi leszel pl, hogy hogy változik az állapot az egymást követő inputokra és requestekre. Ez meg nem feltétlenül jelenik meg a kimeneten, vagy nem a belső reprezentációban.
De ha tudsz mutatni példát hogy csinálod, akkor lehet jobban megértjük egymást.
Példa
Ezekre épülnek az eseménykezelők, kvázi API parancsok, amelyekkel az űrlapokat vezéreljük:
'bejovo_adatok' => array(
'esemeny' => 'onclick',
'formid' => 'asd123',
'nodeid' => 'sdf872',
'recordid' => '5207',
),
'vart_adatok' => array(
array(
'fuggveny' => 'urlapelem_letezik',
'parameterek' => array('iop244', 'kla398),
'ertek' => true
),
array(
'valasz' => array('ertekek', 'iop244', 's0', 'kla398'),
'ertek' => 'kiírandó érték'
),
),
);
Itt az történik, hogy a bejövő kérésben megkapjuk egy űrlapelem koordinátáit, valamint azt, hogy rákattintottak, ezt a rendszerünk feldolgozza.
Ez után megvizsgálhatjuk a kapott adatokat, például a rendszerünk egy függvényét (urlapelem_letezik) meghívjuk a kapott paraméterekkel, és megnézzük, hogy a kapott érték megegyezik-e az 'ertek'-kel. Így közvetve láthatjuk az alkalmazás memóriatérképét, amiről akár dumpot is kérhetünk minden fázisban.
Ugyanígy lehetőség van a visszaadott válaszban túrkálni, ellenőrizhetjük, hogy a $valasz['ertekek']['iop244']['s0']['kla398'] megegyezik-e azzal, hogy 'kiírandó érték'.
Függőségek?
Teszt
Senki sem akadályoz meg, hogy bármikor lecseréljem a modulokat. Ha belegondolsz, ez pont az IoC, csak a sok-sok mellébeszélés nélkül.
Jelenleg egyébként timeout-tal dolgozunk, amit írsz, arra később lesz szükségünk, úgyhogy még nem foglalkoztunk vele.
Persze, ezt megteheted. Ha
Occam borotvájáról meg sokan leírták már itt, hogy nem döntési modell...
Kémkedés és borotválkozás
rename_function($_regi_fv, 'regi_' . $_regi_fv);
override_function($_regi_fv, '', 'call_user_func_array("' . $_uj_fv . '", func_get_args());');
}
felulir('db_kapcsolodas', 'db_kapcsolodas_teszt');
Occam borotvájáról már korábban beszéltünk; azt kell csak eldönteni, hogy mi az egyszerűbb megoldás. Ha van egy kész keretrendszered/rutinkönyvtárad, ami elvégzi a feladatot, akkor valóban őrültség újat írni. Mi is használunk ilyet, például PDF nyomtatásra vagy SQL parse-olásra. Ezek elvégzik a feladatukat, szerencsére egyik formátum sem változott az évek során, ezért nincs ok hozzányúlni.
Ezzel szemben az általunk használt Ext.js keretrendszer – a licenszproblémáktól eltekintve – folyamatosan csak problémát okozott, jó példa erre a beépített kombobox, aminek a nettó forráskódja több mint nyolcszáz soros, hat-nyolcsoros öröklődéssel. A legújabb i7 is letérdelt, amikor olyan űrlapokat kellett kirajzolni, amelynek minden sorában volt hat-nyolc ilyen elem. Ez még mondjuk elviselhető lett volna, de az nem, hogy állandóan javítani kellett, mert túl összetett volt, és mindig valami baj volt vele.
Fogtam, újraírtam százhúsz sorból egy délután, és most a legbonyolultabb űrlapunk renderelése is 150ms alatt van egy Atom processzoros gépen.
Gondolj csak bele: hány hibát lehet véteni nyolcszáz, és hányat százhúsz soron?
Végtelen
A hibák száma, súlyossága nem következik a sorok számából. Ha így lenne, akkor még mindig statikus html oldalakat gyártanánk, egy multinak is max 5 oldal lenne a teljes honlapja. Nehogy túl sok sor legyen... :)
A hibák száma, súlyossága elsősorban a fejlesztőtől függ, de jelentősen hatnak rá a külső függőségek (másik szar kód pl) is.
Nyilván ugyanaz a fejlesztő több soron több "darab" hibát tud ejteni, de gondolj bele abba is, hogy ha "igyekszem", js-ben simán követhetek el 120 soron annyi hibát, mint Te 800-on... :-D
Statisztika
Ez átlag. Benne van a 0 és az
Ez semmiben nem más, mint
Szerintem meglepődnél, ha ezeket a módszereket olyan helyen próbálnád alkalmazni, ahol van több fejlesztő csapat, neadjisten fluktuáció is, meg több lokáció. Ott derül ki igazán melyik módszerek működnek, melyek nem.
Occam: Javaslom ismerkedj meg döntési modellekkel. Nem véletlenül vannak kifejlesztve. Occam borotvája nem döntési modell továbbra sem. Ha az lenne, nem lenne rá szükség, hisz a döntés trivialitásra redukálódna, opciók nélkül.