Feb 21, 2012

Imap "érdekességek" :/

Bő két hónapja esett meg az a szerencsétlenség, hogy volt alkalmam összefutni a php imap libbel. Ami főleg azért volt számomra szerencsétlen, mert előjött pár olyan idegőrlő "érdekesség", amely nem tett túl jót a neurális hálózatomnak.
A doksi írást senki sem szereti, ez tény. Viszont aki nem, vagy csak részben ír dokumentációt másokkal szúr ki, ez esetben velem (és gondolom még sok más fejlesztővel is).
Az imap-al kapcsolatos alapvető felhasználásra nem térek ki, ezek a pontok azoknak lehetnek hasznosak, akik már valamennyire működő kódot hoztak össze, vagy ezt elolvasva a jövőben talán visszaemlékeznek az egyes részekre.
Lássunk tehát néhány hasznos információt a php-s imap-al kapcsolatban, amikkel volt "szerencsém" találkozni:

  • Postafiók megnyitás (ez jól le van dokumentálva, de az egyik vázlatpont miatt nem árt, ha látjuk)
    imap_open(string $mailbox , string $username , string $password [, int $options = NIL...);
    szerver specifikáció:
    {host:port/flag}
    (hasznos option: \OP_HALOPEN -> csak kapcsolatot nyit, de mappát nem)
  • Függvények hibát dobnak a @ operátor ellenére is
    egyszer bejelentett bug
    másszor bejelentett bug
  •  A lib szereti cache-elni a dolgokat, ezért: imap_gc(...); !
  • imap_append options paraméternek az imap_clearflag_full függvénynél az options paraméter értékei is megadhatóak (The flags which you can unset are "\\Seen", "\\Answered", "\\Flagged", "\\Deleted", and "\\Draft" (as defined by » RFC2060)). Például egy levelet olvasottként akarunk az egyik mappába helyezni.
  • Karakterkódolás: Nos, itt nem sikerült tökéletes megoldást előállítanom, így nincs is mit publikálnom sajnos (egy kisebb szkriptről volt szó, amely egyszer volt használatos és nem volt elvárt pl a magyar ékezetes betűk használata). A lényeg, hogy az ékezetes betűkkel rengeteget lehet szívni. Nem folytam nagyon bele, de valszeg rosszak a saját kódolási függvényei: imap_utf7_encode imap_utf7_decode
  • A végére még egy gyöngyszem, az imap_mail_move. Itt az a csodálatos, hogy a szerver spec (első pontban tárgyalt) nem olyan formában kell, hogy szerepeljen, mint ahogy (szinte - pl a copynál is a move-hoz hasonló, viszont append-nél már nem) minden más függvénynél (azaz {host:port/flag}/Folder), hanem neki csakis a Folder rész szükséges.
  • Ráadásként pedig az imap_getmailboxes visszatérési értéke, amely hiányosan van dokumentálva, de szerencsére az egyik kommentelő előkotorta a C forrásból a maradékot :)
Kb ezek voltak amikkel találkoztam és emlékeztem is rájuk. Lehetséges persze, hogy más függvényeknél is előjehetnek hasonlók. A cél, a figyelem felhívása volt a labilis pontokra, azok számára akiknek esetleg ezzel a könyvtárral kell dolgozniuk a jövőben.

Oct 14, 2011

SOAP Messages with Attachments

A héten futottam bele egy feladatba [problémába :)], amelynél webszolgáltatáson keresztül, nem csupán xml adatstruktúrát, de csatolt állományokat is fel kellett dolgoznom.
Egy report szerverről (java alapú) volt szó, amelynek ha elküldünk egy megfelelően felépített xml-t - mint paramétert - SOAP-on keresztül, akkor az annak megfelelő dokumentumot legenerálja, majd úgyszintén webszolgáltatáson keresztül szolgáltatja vissza. Ki lehet találni, hogy az elküldésnél még minden rendben volt, viszont amikor a válasz üzenet visszaérkezett, akkor jelentkezett a - igazából várt  - probléma.
Tegyük fel, hogy  pdf kiterjesztésben kérem el a dokumentumot. Ilyenkor a válasz üzenet multipart/related tartalom típussal (Content-Type) fog érkezni. Röviden ez azt jelenti (részletesebben itt), hogy bár egy üzenet érkezik válaszként, az több különálló részre lesz osztva. A Content-Type fejlécben megadott boundary értékével lesznek az egyes részek elválasztva, amelyek külön-külön fejlécekkel és egyedi azonosítóval (Content-Id) rendelkeznek. A boundary információ mellett - related esetben - egy start attribútumot is találunk, értéke egy Content-Id-ra való hivatkozás. Azaz a start jelöli ki - esetünkben - a SOAP envelope-ot. Az, hogy az envelope-on belül milyen hivatkozások vannak a csatolt állományokra, minket most nem érdekel, csak a különböző részeket szeretnénk feldolgozni. Amikor éppen nem az envelope-ot dolgozzuk fel, akkor figyelni kell a Content-Transfer-Encoding fejlécet, mert ez lehet bináris, de akár base64 kódolású is. A leírtak nagyrészt (A kód közel véglegesnek tekinthető, de még vár rá néhány teszteset) lefedik a SOAP Messages with Attachments fogalmát.

Nézzük egy (kölcsönzött) példát a kapott válaszüzenetre:
MIME-Version: 1.0
Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml;
        start="<http://claiming-it.com/claim061400a.xml>"
Content-Description: This is the optional message description.

--MIME_boundary
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: 8bit
Content-ID: <http://claiming-it.com/claim061400a.xml>
Content-Location: http://claiming-it.com/claim061400a.xml


<?xml version='1.0' ?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
...
<theSignedForm href="http://claiming-it.com/claim061400a.tiff"/>
...
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

--MIME_boundary
Content-Type: image/tiff
Content-Transfer-Encoding: binary
Content-ID: <http://claiming-it.com/claim061400a.tiff>
Content-Location: http://claiming-it.com/claim061400a.tiff

...binary TIFF image...
--MIME_boundary--


Egy ehhez hasonló üzenetet kellene tehát feldolgoznunk, hogy utána az egyes részek kényelmesen lekérhetőek legyenek. A gond viszont az, hogy a natív PHP SoapClient osztály az ilyen típusú üzeneteket nem támogatja, így másfelé kell nézelődnünk.
A problémára két megoldást találtam. Kibővítem a meglévő SoapClient osztályt, vagy egy kész könyvtárat használok (sem NuSOAP-ban, sem ZendFwSoap-ban nem találtam a feature-t [Ha valaki tud wso2-höz hasonló könyvtárat, kommentben kérem ossza meg]). A kész könyvtár a WSO2 Web Service Framework for PHP lenne, amely nagyon sok funkciót tud, köztük az SwA helyett ajánlott MTOM-ot (ez a kettő lényegében megegyezik, csupán az MTOM előrehaladottabb a csatolások belinkelésével).
A kibővítés mellett döntöttem, egyrészt mert a jövőre tekintve is elegendőnek tűnik a majd lentebb látható megoldás, másrészt a kódegység tartása (natív PHP SOAP osztályokat használok az érintett projektben) végett az összes SOAP-al kapcsolatos kódrészt át kellett volna írni, a könytárnak megfelelőre.
Mostmár rátérhetünk a gyakorlati részre, a kódra. Itt nem szeretnék hosszas magyarázatokba bocsátkozni, a lényeg annyi, hogy a SoapClient osztály __doRequest metódusát fogjuk felülírni. Ez az a metódus amely elküldi a kérést, majd visszatér a válasszal. Nekünk akkor kell beavatkoznunk, ha több részes üzenetet fedezünk fel a válaszban, egyébként az eddigi működést támogatjuk. Lekéréskor az összes részt, vagy csupán egyet is elkérhetünk. Egy résznek van fejléc része, amely kulcs-érték párokat tartalmaz és van tartalma, ezenkívül meg tudja mondani magáról, hogy ő maga-e az envelope.


Oct 11, 2011

Zend action controller, függvénybeli paraméterekkel


Rövid szünet után egy Zend Framework-el kapcsolatos poszt következik. Sajnos Zendéknél alapból, nincs lehetőségünk az Action metódusokban paramétereket megdani. Ez nekünk főleg, azért rossz, mert mi szeretnénk az url-ben átadott paramétereket ($_GET) azonnal lekérni a függvény paraméterein keresztül, nem pedig például a $this->getRequest()->getQuery() segítségével. Ezen probléma orvoslására már bizonyára sok megoldás született, de mivel én a Zf-et eddig nagyrészt csak komponensenként használtam főleg CodeIgniter alól, így nekem újdonság volt a dolog.
Alap esetben az url-ben szereplő paraméterek lekérdezésére a következő lehetőségünk van:
$this->_request->getQuery('query_string_parameter_name'); // zf request object
A cél eléréséhez a Zend_Controller_Action osztály dispatch metódusát kell felülírnunk. Azért van erre a függvényre szükségünk, mert ő lesz aki meghívja a megfelelő controller adott action metódusát, amelyet paraméterben megkap (egyszerű string-ként).
A kód megtekintése előtt csupán annyit emelnék még ki, hogy két lehetőségünk van az url-ben paramétereket átadni az adott akciónak:
  • module/controller/action?p1name=p1value&p2name=p2value
  • module/controller/action/p1name/p1value/p2name/p2value
(2. esetben a module, controller és az action kulcsok foglaltak)


Ui.: A fenti megoldás csupán teszt jellegű, éles környezetben nincs használva. Ezen kívül még annyi, hogy validációnál hátrányban lehetünk ilyen jellegű felhasználás esetén (változókban vannak a bemeneti értékek, a könnyebben kezelhető tömb helyett).

Sep 3, 2011

online Gomoku

Pár hete készült el a gomoku nevű (magyarul ötödölő vagy amőba, kinek hogy tetszik) játékom. Ezzel kapcsolatban íródik a mostani poszt. A technikai alapokról osztok meg néhány gondolatot.
Felületről megközelítve CSS3 és SVG(Raphael) technológiákról beszélhetünk, míg szerver oldalról természetesen :) egy PHP szerver biztosítja a kliensek közti kommunikációt (WebSocket). A PHP-ban íródott alkalmazásszerver jelenleg 100 játékosra van kofigurálva, a játékosok belépéskor azonosítókat kapnak. Az azonosító és megadott kóddal "fejlécezett" üzenetekkel kommunikálnak a szerverrel oda-vissza. Amikor egy játékos a felületen lép egyet, akkor a szervernek elküldi a lépést azonosító üzenet kódot és a lépés x, y koordinátáját. Ezt az üzenetet a szerver megkapja, majd elküldi a megfelelő játékosnak, aki az üzenetet küldő fél ellenfele.
A design nem saját, kölcsönöztem, míg a szerver alacsonyabb szint kódjáért a phpwebsocket google code-on hosztolt kód a felelős. A teszt verziót itt érhetitek el. Leginkább Chrome-ban próbálkozzatok vele, Fx4-ben a websocket le van tiltva (Fx5-6-ot ezidáig még nem próbáltam ki, így arról nem tudok nyilatkozni). (Amennyiben nincs a szerveren játékos, de ki szeretnétek próbálni, egyszerűen nyissatok még egy tabot neki.)
Belépéskor
Játék vége, győztesként

A játék a html5games.com-on

Aug 19, 2011

NetBeans vs CodeCoverage

Jelen posztban a NetBeans és a PHPUnit kapcsolatáról lesz  szó. Régóta ki szerettem volna próbálni, hogy mit is tud az egyik kedvenc IDE-m kezdeni az egységtesztelésből már ismert Code Coverage-el. Pár hete le is töltöttem a 7.0-s változatot, de mire odáig jutottam hogy ténylegesen rászánjam azt a 2x10 percet a dologra, már ki is jött a 7.0.1-es (Aug1.), így már azon teszteltem (6.9-es verziótól már tartalmazza ezt a feature-t).
 Kezdetben van nekünk egy "User" osztályunk két metódussal. A fontos most a "setName()", amely egy paramétert vár ("name), ami ha nem "string" típusú vagy ha az, de a hossza kisebb, mint három, akkor kivételt dob a függvény. Előbbi esetben a kivétel "InvalidArgumentException", míg utóbbiban "LengthException" típusú. Amennyiben nincs kivétel, a "name" attribútum értékül kapja a paraméterként adottat.


User osztály

Következő lépés, hogy létrehozzunk egy tesztet az említett osztály "setName" metódusára. Az alábbi módon könnyíthetjük meg a munkánkat:

Unit test generálás

Elkészült a tesztelő osztályunk mindenféle földi jóval. A metódusok helyesen "incomple" jelzést kaptak.

UserTest osztály

Visszamegyünk a "User" osztályunkra, ahol meg szeretnénk nézni, hogy az apró kódunk hány százalékát fedtük le eddig egységteszttel. Kemény 7.69%-on vagyunk, amit teljes egészében a konstruktor tesz ki. Ahogy a képen is láthatjuk a konstruktor zöld színű, míg az eddig le nem fedett részek pirosak.

CodeCoverage 7.69%

 Nekünk ez a lefedettség kevés (A 7.69% valóban kevés lehet, de halkan megemlíteném, hogy a 100%-os lefedettséget senki se szeresse elérni. Külön cikket megér, van is róla pár angol nyelvű, érdemes böngészni), így írunk egy tesztet a "setName" metódusra. Ebben a tesztben egy nem "string" típusú paramétert adunk át a függvénynek, amire válaszul InvalidArgumentException-t várunk (expected annotáció a kommentben).

testSetName


Miután kész a függvényünk, visszatérve a "User" osztályunkhoz láthatjuk, hogy az említett kivételt le is fedtük a tesztünkel. 30.77% ami a mai napra nekünk elég is, hiszen nem szeretnénk túlzásba vinni a kódolást a 28° melegben ezen a szép napon.

CodeCoverage 30.77%