PHP alkalmazások minőségbiztosítása
Biztosan mindenki írt már életében sokszor spagetti kódot. Ezzel mindaddig nincs is probléma amíg azt csak mi használjuk, nem csapatban dolgozunk, illetve nem szeretnénk a kódunkat a későbbiekben továbbfejleszteni, karbantartani, viszont ha igen, akkor nem árt odafigyelni néhány konvencióra, megfelelően tervezni, figyelni a helyes osztályhierarchia felépítésére, illetve, hogy a kód mások számára is egyszerűen értelmezhető legyen. Illetve az sem egy utolsó szempont, ha a kód megvalósítja a kívánt funkcionalitást, a lehető legkevesebb hibával. Persze, ennek a kivitelezése nem egyszerű feladat (főleg nagyobb projekteknél), viszont a PHP esetében több eszköz is rendelkezésünkre áll a kódunk minőségének ellenőrzésére. Ebben a cikkben először röviden megnézzük a minőség és minőségbiztosítás fogalmakat, majd a rendelkezésünkre álló eszközök közül fogunk szemezgetni.
Mi a minőség?
A minőség legegyszerűbben megfogalmazva a követelményeknek való megfelelést jelenti. Az, hogy mik a követelmények, és egy adott produktum mennyire felel meg nekik, már egy sokkal összetettebb dolog, függ attól, hogy milyen aspektusból és a termékünk mely részeit vizsgáljuk. Ha egy webalkalmazást veszünk, az valószínűleg más-más minőséget fog mutatni a végfelhasználók, illetve a fejlesztők körében, előbbiek a termék funkcionalitását, használhatóságát, szépségét fogják értékelni, utóbbiak pedig, hogy mennyire egyszerű átlátni, karbantartani, továbbfejleszteni a kódot. Ráadásul mivel az emberek különbözőek, ebből fakadóan más-más megoldások tetszenek nekik, tehát lehetséges, hogy ugyanabból a csoportból az egyik embernek jobban tetszik a termékünk, mint a másiknak. A követelmények meghatározása szintén egy összetett kérdéskör, többféle követelmény csoportot állíthatunk össze – amelyek lehetnek egymástól teljesen függetlenek, de részben összefüggők is –, attól függően, hogy a termékünknek mely részeit, ki és hogyan fogja használni. A funkcionalitásra, kinézetre vonatkozó követelményeket nagy valószínűséggel a megrendelő vagy a termékmenedzser fogja meghatározni, míg a kódminőségre vonatkozó előírásokat a vezető fejlesztő. Mi most az utóbbira, azaz a kódminőség mérésére, illetve biztosítására koncentrálunk.
A minőségbiztosítás
A minőség fogalmának tárgyalása után a minőségbiztosítás már sokkal egyszerűbben definiálható. Adott egy követelménylistánk, a cél ennek a teljesítése. A minőségbiztosítás ennek az elősegítésére, ellenőrzésére ad a kezünkbe eszközöket. Ez az eszköz lehet akár egy specifikáció, amely minél részletesebb, annál valószínűbb, hogy a végtermék a kívánt minőséget produkálja, de lehet egy ellenőrző lista is, amelyen bizonyos időközönként végigmegyünk, ellenőrizve, hogy a termék fejlesztése során teljesültek-e a követelmények, illetve a már megvalósultakból nem sérült-e valamelyik. De léteznek sokkal hatékonyabb, emberi beavatkozás nélkül futtatható eszközök is, nézzünk is meg párat.
PHPCodeSniffer
A PHPCodeSniffer egy viszonylag egyszerű eszköz, a segítségével ellenőrizhetjük, hogy a forráskód megfelel-e az általunk választott kódolási konvenciónak. Alapvetően mindegy, illetve ízlés kérdése, hogy milyen konvenciót választunk, a lényeg, hogy lehetőleg egy csapaton, illetve egy projekten belül ugyanazt használjuk, ezzel megkönnyítve a kód olvasását, értelmezését. Telepítés után a phpcs /projekt/utvonala/
parancs kiadásával tudjuk használni. Alapértelmezetten tartalmaz pár beépített kódolási konvenciót, ezeket a phpcs -i
parancs kiadásával tudjuk megtekinteni, nálam jelenleg a következők vannak telepítve: PHPCS, Squiz, PEAR, Zend, MySource. Természetesen akár sajátot is írhatunk. Bővebben itt olvashatunk róla: http://pear.php.net/package/PHP_CodeSniffer
PHPMessDetector
A PHPMessDetector az előzőnél egy komplexebb eszköz, a használatával bizonyos szabályrendszerek szerint ellenőrizhetjük a kódunk minőségét. Ezek jellemzően a kód méretét, komplexitását vizsgálják, a változóink, osztályaink, metódusaink elnevezését, a kódon belül nem használt változókat, illetve sok egyéb dolgot.
A következő beépített szabályok alapján vizsgálhatjuk a kódjainkat:
Kódméretre vonatkozó szabályok
- Ciklomatikus komplexitás (
CyclomaticComplexity
): A forráskód bonyolultságát a benne lévő elágazások alapján határozza meg. Az elágazások egy gráf pontjai, ezek, illetve a köztük lévő élek alapján számítható a következő módon: M = E - N + 2P, ahol E a gráf éleinek száma, N a gráf csúcsainak száma, P az összefüggő komponensek száma. - NPath komplexitás (
NPathComplexity
): egy metódus végrehajtása során a különböző végrehajtási útvonalak számát határozza meg. - Túl hosszú metódus (
ExcessiveMethodLength
): Túl hosszú metódus esetén az valószínűleg túl sok dolgot csinál. Próbáljuk meg szétszedni kisebbekre. - Túl hosszú osztály (
ExcessiveClassLength
): A túl hosszú osztályok nehezen olvashatóak, illetve valószínűleg túl tág feladatkörrel rendelkeznek. Próbáljuk meg szétszedni őket kisebbekre. - Túl hosszú paraméterlista (
ExcessiveParameterList
): Túl hosszú paraméterlista esetén készíthetünk olyan objektumokat, amelyek egybefogják azokat, így rövidítve egy-egy metódus paraméterlistáját, olvashatóbbá, könnyebben kezelhetőbbé téve azt. - Túl sok publikus adattag (
ExcessivePublicCount
): Túl sok publikus adattag használata egyrészt bonyolulttá teszi az osztályunk felhasználását, másrészt nehezebben tesztelhetővé, módosíthatóvá teszi azt. - Túl sok adattag (
TooManyFields
): Ha túl sok adattagunk van egy osztályon belül, azok nagy valószínűséggel átcsoportosíthatóak más osztályokba, ezzel kompaktabbá, olvashatóbbá és karbantarthatóbbá téve azt. - Túl sok metódus (
TooManyMethods
): Ha egy osztályban túl sok metódusunk van, az nehezíti az értelmezést, a karbantartást, illetve módosítást. - Túl komplex osztály (
ExcessiveClassComplexity
): A túl komplex, sok feladatkörrel rendelkező osztályok módosítása és karbantartása sokkal több időt igényelhet a szokásosnál, ezek valószínűleg szétválaszthatóak több osztályra.
Vitatott szabályok
- Szuperglobális változók (
Superglobals
): A szuperglobális változókat lehetőleg ne közvetlenül használjuk, hanem írjunk egy-egy osztályt azok elérésére, vagy használjuk a keretrendszer (már ha használunk) által erre a célra biztosított eszközöket. - CamelCase osztálynév (
CamelCaseClassName
): Az osztályainkat CamelCase konvenció szerint nevezzük el. - CamelCase adattag név (
CamelCasePropertyName
): Az osztályaink adattagjait CamelCase koncenció szerint nevezzük el. - CamelCase metódus név (
CamelCaseMethodName
): A metódusainkat CamelCase konvenció szerint nevezzük el. - CamelCase paraméter név (
CamelCaseParameterName
): A függvény és metódus paramétereinket CamelCase konvenció szerint nevezzük el. - CamelCase változónév (
CamelCaseVariableName
): A változóinkat CamelCase konvenció szerint nevezzük el.
Tervezési szabályok
- Exit kifejezés a kódban (
ExitExpression
): Az exit kifejezés használatát a kódban lehetőség szerint kerüljük. - Eval kifejezés a kódban (
EvalExpression
): Az eval használata nehezen tesztelhető kódot eredményez, illetve potenciális biztonsági rés. Kerüljük a használatát. - Goto utasítás (
GotoStatement
): A goto utasítás használata nehezíti a kód olvasásást és megértését, illetve sok esetben szinte lehetetlenné teszi az alkalmazás folyamatainak követését. Éppen ezért kerüljük a használatát. - Gyermekek száma (
NumberOfChildren
): Ha egy osztálynak túl sok leszármazottja van, az kiegyensúlyozatlan és rossz osztályhierarchiához vezethet. - Öröklődés mélysége (
DepthOfInheritance
): Ha egy osztálynak túl sok őse van, az kiegyensúlyozatlanná teheti az osztályhiearchiát. - Objektumok közötti függőség (
CouplingBetweenObjects
): A túl sok kapcsolattal rendelkező objektumok negatív hatással lehetnek a stabilitásra, a karbantarthatóságra, illetve nehezítik a kód megértését.
Elnevezési szabályok
- Túl rövid változónév (
ShortVariable
): Túl rövid változó- vagy adattagnév adása (pl.$i
). - Túl hosszú változónév (
LongVariable
): Túl hosszú változó- vagy adattagnév adása. - Túl rövid metódusnév (
ShortMethodName
): Túl rövid metódusnév megadása (pl.public function a()
). - Konstruktor neve megegyezik az őt tartalmazó osztály nevével (
ConstructorWithNameAsEnclosingClass
): A konstruktor neve nem egyezhet meg az osztály nevével, PHP5-ben erre a__construct()
metódust használjuk. - Konstans elnevezési szabályok (
ConstantNamingConventions
): A konstans nevek konvenció szerint nagybetűsek. - Boolean get metódus név (
BooleanGetMethodName
): Boolean visszatérési értékkel rendelkező metódusoknál hibás agetMethod()
elvenezés használata, helyette azis
-t, vagy ahas
-t használjuk.
Nem használt kód szabályok
- Nem használt privát adattag (
UnusedPrivateField
): Olyan osztályadattag deklarálása, amelyet nem használunk a továbbiakban. - Nem használt lokális változó (
UnusedLocalVariable
): Metóduson/függvényen belül lokális változó deklarálása, amelyet nem használunk a továbbiakban. - Nem használt privát metódus (
UnusedPrivateMethod
): Olyan privát metódus definiálása egy osztályon belül, amelyet a későbbiekben nem hívunk a kódból. - Nem használt paraméter (
UnusedFormalParameter
): Konstruktoroknál és metódusoknál megkövetelünk olyan paramétereket, amelyeket aztán nem használunk a kódban.
A fenti lista elérhető a PHPMessDetector hivatalos oldalán is részletesebben, angolul: http://phpmd.org/rules/index.html
Használata a következőképpen néz ki:
phpmd /projekt/utvonala/ text codesize
Első paraméterként a projekt útvonalát adjuk meg, utána következik, hogy milyen formában szeretnénk megkapni az eredményt, végül pedig, hogy melyik szabályrendszer szerint vizsgáljuk a kódot, esetünkben ezek a kódméretre vonatkozó szabályok.
PHPDepend
A PHPDepend szintén egy komplex eszköz, a fentihez hasonlóan rengeteg információt kaphatunk a kódunkról a segítségével. Ezeket nem sorolnám fel most részletesen, a következő oldalakon utánaolvashatunk:
- http://pdepend.org/documentation/software-metrics/index.html
- http://pdepend.org/documentation/handbook/reports/overview-pyramid.html
- http://pdepend.org/documentation/handbook/reports/abstraction-instability-chart.html
A telepítésről, használatról és a kezdeti lépésekről itt található leírás: http://pdepend.org/documentation/getting-started.html
PHPCopyPasteDetector
A PHPCopyPasteDetector a forráskódunkban található ismételt kódrészleteket fedi fel. A DRY (Don’t repeat yourself, azaz ne ismételd önmagad) elv szerint kerülnünk kell az ismétlődő kódrészleteket, ezek általában kiemelhetőek egy külön függvénybe vagy metódusba.
Használata:
phpcpd /projekt/utvonala/
PHPLoc
A PHPLoc a forráskódunkról ad bizonyos statisztikákat:
- kódsorok száma
- ciklomatikus komplexitás
- kommentelt kódsorok száma
- nem kommentelt kódsorok száma
- névterek
- interfészek
- traitek
- absztrakt és konkrét osztályok száma, átlagos osztályméret
- statikus, nem statikus, publikus, nem publikus metódusok száma, átlagos metódusméret
- függvények száma, névtelen függvények száma
- globális és osztálykonstansok száma
Használata:
phploc /projekt/utvonala/
PHPCodeCoverage
A PHPCodeCoverage egy kódlefedettség-mérő eszköz. Na de mi is az a kódlefedettség és mire jó? Alapvetően teszteléskor szokás alkalmazni, az egy-egy futás során végrehajtott, illetve nem végrehajtott kódrészletek arányát mutatja meg nekünk. Célja, hogy megtaláljuk azokat a kódsorokat, amelyekre nem jutott vezérlés a teszt során, tehát nem lett tesztelve. Fontos megjegyeznünk, hogy a 100%-os kódlefedettség bár egy szép szám, de nem érdemes törekednünk rá, mert nagyon megnöveli a projekt költségét, ezen kívül még így sem biztosítható a teljesen hibamentes működés. Érdemes tehát kísérletezgetni, és a jól bevált arany középutat követni. A lefedettségmérés nem csak tesztelés közben lehet hasznos, hanem annak a felderítésére is, ha plusz funkciók kerültek a kódba, és azokra még nem lettek tesztesetek írva. Értelemszerűen ilyenkor csökken a lefedettséget jelző számunk.
Bővebb olvasnivaló itt található: https://github.com/sebastianbergmann/php-code-coverage
Mellékesen megjegyezném, hogy az Xdebug és a PHPUnit is tartalmaz kódlefedettség-mérő funkciókat.
PHPDeadCodeDetector
A PHPDeadCodeDetector segítségével felfedhetjük a kódban található „halott” kódrészleteket, azaz azokat, amelyek sosem fognak végrehajtásra kerülni. Némi hasonlóságot fedezhetünk fel a fent említett kódlefedettség-méréssel, de ne keverjük össze őket: a lefedettségmérés esetében attól még, hogy egy futás alatt nem hívódik meg egy adott kódrészlet, az nem feltétlenül halott.
Bővebben itt olvashatunk róla: https://github.com/EHER/phpunit-all-in-one/tree/master/src/phpdcd
php -l
Bónuszként megemlíteném még a PHP beépített szintakszis ellenőrzőjét, ez nem kifejezetten minőségbiztosítási eszköz, de a segítségével elkerülhetjük, hogy syntax error kerüljön véletlenül az éles kódba
Használata:
php -l /fajl/utvonala
Összegzés, záró gondolatok
Ahogy láthatjuk, létezik jó pár eszköz a PHP alapú kódjaink minőségének ellenőrzésére. A lista korántsem teljes, érdemes körülnéznünk az interneten, rengeteg hasonló eszközt találhatunk, ezen felül érdemes megismerkednünk a funkcionális teszteléssel (Selenium), az egységteszteléssel (PHPUnit) és az integrációs tesztekkel is, amelyek bár nem képezik témáját ennek a cikknek, de fontos eszközei a webalkalmazások minőségbiztosításának. A fent tárgyalt eszközök jellemzően parancssorból futtathatóak, de hatékonyabb ha valamilyen automatizált formában integráljuk a fejlesztési folyamatainkba, például valamilyen continuous integration szerver (Jenkins) vagy build eszköz (Phing) segítségével.
A bélyegképen az Egyesült Államok Haditengerészetének fényképe látható.
■kacsandiz
Kacsándi Zsolt fő érdeklődési területei a webalkalmazások fejlesztése, fejlesztési folyamatok hatékonyságának növelése és a minőségbiztosítás. A fejlesztés mellett igyekszik képben lenni az üzemeltetéssel kapcsolatos kérdésekben, technológiákban is.Szabadidejében szívesen publikál szakmai témákban, illetve segédkezik rendezvények szervezésében.
Köszönöm a cikket, hasznos
Egy valamibe most úgy érzem bele kell kötnöm, hogy PHP minőségbiztosításról beszélsz, és említed a PHPCodeSniffer-t, de nem említed az elmúlt évben egyre jobban népszerűsödő PSR (PHP Specification Request) szabványokat, amikben nagyon sok híres framework beleegyezett, és aszerint adná ki az újabb verzióit a frameworkjeikből.
Több infoért: http://www.php-fig.org/ és a PSR 0, 1 meg 2-t érdemes elolvasni.
Elég jó lenne, ha ez a php-fig terjedne.
A cikk eszközöket mutat be
Már a PHPCodeSniffer is
Tetszik a cikk. Nekem is
Nekem is hiányérzetem van, pl: a metrika szó egyáltalán nem szerepelt a cikkben.
Érdemes lenne folytatást írni, ami csak egy eszközt és az attól kapott adatok kiértékelési módját, és/vagy az ezzel kapcsolatos mérési elv(ek)et mutatja be a jelenleginél jóval részletesebben. Esetleg ugyanezt egy gyakorlati példával, mondjuk valami ismert keretrendszerrel, vagy egy apró példakóddal...
A ciklomatikus komplexitás
A téma mélyebb kibontását én is szívesen látnám.
elég egyértelmű, hogy itt
Elég érdekes egy olyan metrikákat bemutató cikket olvasni, amiben nem szerepel a metrika szó. Nem kellene valahol a főcím környékén lennie?
Nekem pl agyalni kellett, hogy eszembe jusson az a szó, mert nem foglalkoztam egy ideje ezzel a témával. Egy kezdő még csak nem is hallott róla, így nem tud utánakeresni, csak ha előbb rákeres a többi szakszóra, és kidobja neki google, hogy ezek metrikák. A google-ben is alacsonyabb helyre fog kerülni ez a cikk a találati listán, mert nem szerepel benne egy fontos kulcsszó.
A metrika magyarul mérőszám.
Ha a szakma így utal rá
Nem arról van szó, hogy
Sonar
Kíváncsi lennék hány projecten használnak ilyen code analyzer toolokat. Nekem kicsit megdöbbentő volt, hogy amikor feltettem a dev szerveremre és ismerősöknek ajánlgattam, senkit nem érdekelt különösebben... Hogy lehet kódminőségről, de főleg stabilitásról és karbantarthatóságról beszélni megfelelő mérések és lefedettség nélkül? Vagy ilyen kis százalékot érdekel, hogy ne kelljen könyékig a trutyiban matatni?
Mindenesetre +1 a cikknek, remélem sokan fognak ismerkedni az említett cuccokkal a hatására. És nem vágják be duzzogva a sarokba, hogy márpedig az a kód jó :)
Szerintem több reklám kell
Rengeteg haszna van ezeknek.
A PMD például kidobta, hogy a $handlerMessageClass változó nincs használva, ami vagy egy hibára utal, vagy figyelmetlenségre, de mindenképp "büdös". Egy refaktorálás után elsiklottam felette, a gép nem.
A phpdepend pl. nagyon hasznos arra, hogy feltérképezzük, vannak-e körkörös függőségek a packagek (PHP-ben névterek) között. Az baj, ha van, mert nehezebben hordozható a kódunk.
A phploc-cal ilyen diagrammokat varázsolhatunk, ami kis projekt esetén csak érdekesség, de nagy, sokemberes projekt esetén elég sokatmondó.
Tervben van egy második cikk,
Örömmel hallom. Én úgy
Nem az egyes eszközök
Igaz, rosszul fogalmaztam,
Értem mire gondolsz, és
Pár ötlet
Nem mind a metrikákról szól, sőt talán a többség nem, viszont szorosan kapcsolódnak a minőséghez. És talán túl sokmindent is dobtam be :) Egy cikkbe bőven sok is lenne. A tesztelés szerintem önmagában megér egy cikket, vagy akár cikksorozazot. De talán van benne ötlet, amit fel tudsz használni. És inf3rnoval egyetértve, szerintem is érdemes példákkal alátámasztani a cikket.
Ha Zsolt nem írja meg ezt
Én régóta tervezek cikket
Imho néhány alapelvből le lehet vezetni minden kód minőséggel kapcsolatos többi elvet és mintát, és én inkább erre építkezve írnék egy átfogó cikket, mint csak úgy bedobálva az egyes elveket.
Ez legalább 3 cikk témája,
Szerintem ez a cikk elég korrekt volt ilyen szempontból, bemutatta a felhasználható eszközök egy részét, és mindenbe csak annyira ment bele, amennyire szükség volt...
Jó a témaválasztás, jó ilyet
A code coverage-hez hozzátennék annyit, hogy tapasztalataim szerint nem jó arany középutat találni, már ami a %-os lefedettséget illeti, főleg nem PHP-ben (illetve bármilyen gyengén típusos nyelvben). Egyszerűen annyi hiba lehet a kódban, hogy még az amúgy értelmetlen "hajtsuk meg és ne ellenőrizzünk semmit" típusú unit teszteknek is van értelmük (legalábbis jobb, mint a semmi).
Én inkább felbontom az alkalmazásokat, és az üzleti logikát tartalmazó részben törekszem a 100%-ra, a kevésbé fontos, vagy mondjuk úgy kevésbé fájdalmas részekben meg kevésbé törekszem a magas coveragere. Ehhez egyébként érdemes particionálni is a projektet, hogy szeparáltan lehessen managelni azt (több projekt, composer, maven, akármi).
A code coverage-et talán fél
test automation pyramid
Nem minden teszt automatizálható, ahogy közeledsz a magasabb absztrakciós szintekhez, úgy nő a költsége az automatizálásnak. Pl az UI-ra nem éri meg automatizált teszteket írni, inkább végig kell kattintgatni, hogy minden okés e. Imho teljesen korrekt az, hogy a business logic teljesen le van fedve, ahogy haladsz feljebb, úgy meg egyre kevesebb, és kevésbé részletes automatizált teszt van. Ez mondjuk az én véleményem, nem vagyok szakértő a témában... Láttam már olyan UI keretrendszert, amihez szintén írtak teszteket az egyes komponensekhez, szóval tényleg embere válogatja, kinek mire van ideje, kedve...
A code coverage-et talán fél
Pedig de.
Jó tudni. Akkor csak úgy
Netbeans
Általában, ha egy IDE
off: kep:)
A cikk végén megtalálod a
Koszi, figyelmetlen voltam :)
Fontos téma, de a cikk
Korábban említettem, hogy
Az, hogy melyik mérőszám mennyire hasznos és kinek egy szubjektív dolog, ezért nehezebb is róla általánosságban írni, az adott csapat és az adott projekt függvénye, hogy mit szeretnél mérni és azt mire akarod felhasználni.
A coverage-nek például nincs köze ahhoz, hogy egy taszk mikor van kész (a végfelhasználót nem fogja érdekelni mekkora a lefedettség, őt az érdekli, hogy működjön az adott funkció - a kettő nem függ össze), de a többivel kapcsolatban sem tudok olyan ökölszabályt kijelenteni, hogy ha ez az érték ennyi, akkor ez jó, egyébként meg rossz.
Mondok egy példát: van egy modulod, több osztályból áll, a PHPMD visít, hogy az egyiknek túl sok leszármazottja van. Nem mondhatom azt, hogy ez rossz, neked kell mérlegelned azt, hogy mekkora költség ezt átírni, elbírja-e ezt a projekt, illetve ha úgy hagyod milyen hátrányokkal kell számolnod hosszútávon.
Sokan elfelejtik azt, hogy ez egy olyan témakör, amit rengeteg külső dolog befolyásol, költségvetés, határidő, üzleti döntések. Fontos a minőségbiztosítás, de ezt sem szabad túlzásba vinni, írhatsz gyögyörűszép kódot hat hónap alatt, ha három hónapod volt befejezni a projektet.
Nagyon jó cikk,
Bizony én úgy látom, a "nyakadba vettél" vagy 5-6 másikat...
A mérőszámok hasznosságáról szerintem ne általánosságban írj, hanem a saját tapasztalataidat. Nyilván mindenki mást tapasztal, azok a plusszok majd jönnek kommentekben.
Leginkább inf3rnoval értek egyet: ez így nagyon jó, mindegyik itt bemutatott eszközön keresztül kellene egy-egy konkrét (egyszerűsített) alkalmazásteszt, végigmagyarázva a köztes miérteket.
Ez az, amit - az általad említett folytatásokkal együtt - 5-6 következő cikkre saccolok. Hát sok erőt és időt hozzá, jelenleg sajnos én nem tudok segítséget ajánlani benne - egyéb okból. De remélem megszületnek ezek a cikkek is, mert igen elhúztad ám a mézesmadzagot! :)
Ezekért "bukok" a WL-re...
Jó cikk, köszönet érte
Gratulálok a témaválasztáshoz
Biztosan troll módban
Egy tizenöt éve regisztrált nick, aki összesen háromszor írt a fórumra, öt év után hirtelen felbukkan és ír pár (számomra) semmitmondó sort, hogy egy öt éve írt poszt alá.
WTF???
Unatkozó,
Nekem volt olyan kérdés egy
Ez is egy módszer, hogy...
Úgyhogy, aki még itt van a Weblabor-on és nem olvasta volna a cikket, annak hajrá! :)
+1
Ebben van igazság :)