Többszintű automatikus kódellenőrzés Drupal-fejlesztéshez

Az egész úgy kezdődött, hogy LinkedIn-en láttam Claudio Beatrice megosztását a “Static Analysis for PHP, from PHPDay Italy 2012” SlideShare diákról, ahol nem a fő téma fogott meg, ami az igen nehézkesen beállítható Facebook’s HpHp (HipHop, ha úgy ismerősebb) volt, egész pontosan annak a villámgyors statikus kódellenőrzője, hanem a felvezető diákban a PHP_CodeSniffer emlegetése, ahol azt hozzák fel ellene érvként, hogy lassú. De milyen esetben lassú? 5-10 fájl átnézni biztosan nem az. Mondjuk egy teljes Drupal 7 core átnézést tényleg ne kíséreljünk meg vele alacsony válaszidőt várva. Aztán a következő állomás a drupal.org volt, ahol ráakadtam a Drupal Code Sniffer projektre. Gondolkodás nélkül clone-oztam a repository-t, csak második nekifutásra tűnt fel, hogy ezt beolvasztották a coder modulba tavaly szeptemberben. A coder_review modult szerintem a legtöbbünk aktívan használja, de azért vannak limitációi. Először is zömmel reguláris kifejezésekkel dolgozik, azaz néha tévesen ad riasztást. Másrészt pedig inicializálni kell a Drupalt a használatához, a coder modult be kell kapcsolni, stb. A PHP_CodeSniffer egy olyan statikus kódelemző, amely meglehetősen robusztus, ugyanis a Zend engine lexikális elemzőjét használja. Szóval a coder 7.x-2.x ága tartalmaz egy coder_sniffer almodult, ami tulajdonképpen nem más, mint egy szabálykészlet a PHP_CodeSnifferhez.
Nagyszerű, bár eddig is le tudtuk futtatni a coder_review modult, ha akartuk, van neki drush parancsa végülis, még a browsert sem kell hozzá elindítani. Ez igaz. Ahogy néztem a CodeSniffer szabályainak köszönhetően a coder_review használatához képest több értelmes javaslatot kapunk s remélhetőleg nulla téves riasztást, bár ugye a szabályokban itt is lehet hiba. Egyelőre draft-ként aposztrofálják a README-ben, de szerintem nagyon jól használható. A PHP_CodeSniffert egyes fájlokra is meg tudjuk hívni természetesen, a coder modullal csak modulokat ellenőrizhetünk, amennyire a drush --help code-review -nek hinni lehet.
A kissé robusztusabb felépítés még mindig semmi. Be lehet rakni ezt pre-commit hookba is, s akkor máris védve vagyunk minden hiba ellen. Hát nem nagyszerű? Nem olyan biztos. Lehetnek olyan esetek, amikor a lehető legkisebb módosítással járó kommitot akarjuk megtenni, adott esetben egy contrib modulon vagy régebbi kódon, amely nem a most aktuális coding standard jegyében készült (akik régóta Drupaloznak, emlékezhetnek rá, hogy apróbb módosítások voltak, Implementation of, Implements vagy a pont operátorral kapcsolatos huzavona). Szóval hogy a CodeSniffer döntse el, hogy megtehetem-e a kommitot, nos, ez egyáltalán nem tűnt jó ötletnek.
Viszont eszembe jutott, hogy mit írnak a FAQ-ban, mégpedig hogy ez az eszköz nem ellenőrzi le, hogy szintaktikailag helyes-e a kódunk. Így jutunk el a többszintű ellenőrzésig. A CodeSniffert beállítottam, hogy a git commit parancsaim előtt fusson le, tájékoztasson, ha van hiba, s hiba esetén eldöntöm, hogy szeretnék-e kommitolni. Pre-commit GIT hook-nak kerestem egy rövid szkriptet, ami a PHP beépített szintakszis-ellenőrzőjét hívja meg. Ezt csupán a Drupalos fájlkiterjesztésekhez kellett szabnom és ez az első védelmi vonal kész is volt. Azok számára, akik nem olyan járatosak benne, a GIT vagy nem hajt végre semmilyen szkriptet kommit előtt vagy pontosan egyet, ami az adott repository .git/hooks/pre-commit fájljában van. Ha több szkriptet akarunk meghívni, arról magunknak kell gondoskodni. A pre-commit exit kódjától függ, hogy a kommit lefut-e vagy hibával tér vissza.

Ha valaki hozzám hasonlóan a CodeSniffert is szeretné integrálni, mintegy opcionális extra védelemként, akkor mindenképpen a személyes fejlesztői szokásai a meghatározóak. Akik valamilyen IDE-t használnak a GIT kezelésére is, azoknak akkor nyilván azon a szinten érdemes körüljárni a témát (A PhpStorm természetesen támogatja és arra is képes, hogy kommit előtt az érintett fájlokra code inspection-t végezzen). Én viszont ezeket parancssorból csinálom szinte kivétel nélkül. A coder_sniffer README-jében sokféle use-case-re adnak linket egyébként.
Először is kerestem egy pre-commit hookot, amit a CodeSnifferhez írtak, majd írtam hozzá egy wrappert. Hiba esetén rákérdez, s csak akkor lép ki hibának megfelelő státuszkóddal, ha megerősítem, hogy nem akarom lefuttatni a kommitot. Egyébként ebben az a különösen hasznos, hogy kizárólag a kommit által érintett fájlokra fut le, így nem kell átnéznünk olyan riasztásokat, amik biztosan nem a módosításainkkal függnek össze. Valaki akár ezt hátrányként is értékelheti, ez esetben a szkript nyilván módosítható. A GIT parancsokat eleve aliasokon keresztül adom ki, csupán ezt az aliast módosítottam, hogy meghívja a refaktorált pre-commit hookot.

Ha a CodeSniffer exit státuszkóddal lép ki, átnézve az outputot én is az elvetés mellett döntök, akkot a git commit nem hívódik meg. Ha nem talál hibát, felhasználói interakció nélkül lefut a kommit.
Egyelőre ez a beállítás jónak tűnik, annyit persze lehetne variálni rajta, hogy a CodeSniffer is pre-commit hook, s több szkript fut le pre-commitkor, nyilván megoldható ez is. A coder modul általi kódellenőrzést használtam eddig is, de aránylag ritkán, egy menetben gyomláltam ki a bekerült problémás részeket zömmel deploy előtt. Reményeim szerint ezzel a módszerrel az efféle hibák jelentős része be sem fog kerülni a repository-ba egyáltalán. S diktatórikus kedvű devops-ok pedig ugyanezeket megtehetik akár feltétel nélkül is a szerver-oldali pre-receive hookban. :)

A szkriptek és a beállítások itt elérhetők: https://github.com/AronNovak/dr_codeinspect/
Pull requesteket szívesen látok természetesen.