A fordítás a következő angol eredeti alapján készült: George Schlossnagle: Advanced PHP Programing Copyright © 2004 Sams Publishing. Minden jog fenntartva! Authorized translation from the English language edition, entitled Advanced PHP Programing, lst Edition, ISBN 0672325616, by Schlossnagle, George, published by Pearson Education, Inc, publishing as Que/Sams. Copyright © 2004 by Sams Publishing Translation and Hungárián edition © 2004 Kiskapu Kft. Ali rights reserved! Ali rights reserved. No part of this book, including interior desing, cover design, and icons, may be reproduced or transmitted in any form, by any means (electronic, photocopying, recording, or otherwise) without the prior written permission of the publisher. Trademarked names appear throughout this book. Rather than üst the names and entities that own the trademarks or insert a trademark symbol with each mention of the trademarked name, the publisher st.ues that it is using the names for editorial purposes only and to the benefít of the trademark owner, with no intention of infringing upon that trademark. Fordítás és magyar változat © 2004 Kiskapu Kft. Minden jog fenntartva! A könyv egyetlen része sem sokszorosítható semmilyen módszerrel a Kiadó előzetes írásos engedélye nélkül. Ez a korlátozás kiterjed a belső tervezésre, a borítóra és az ikonokra is. A könyvben bejegyzett védjegyek és márkanevek is felbukkanhatnak. Ahelyett, hogy ezt minden egyes helyen külön jeleznénk, a Kiadó ezennel kijelenti, hogy a műben előforduló valamennyi védett nevet és jelzést szerkesztési célokra, jóhiszeműen, a név tulajdonosának érdekeit szem előtt tartva használja, és nem áll szándékában az azokkal kapcsolatos jogokat megszegni, vagy kétségbe vonni. A szerzők és a kiadó a lehető legnagyobb körültekintéssel járt el e kiadvány elkészítésekor. Sem a szerző, sem a kiadó nem vállal semminemű felelősséget vagy garanciát a könyv tartalmával, teljességével kapcsolatban. Sem a szerző, sem a kiadó nem vonható felelősségre bármilyen baleset vagy káresemény miatt, mely közvetve vagy közvetlenül kapcsolatba hozható e kiadvánnyal. Nyelvi lektor: Rézműves László Szakmai lektorok: Palócz István (1-5., 7., 8. f), Papp Győző (6., 9- f), Komáromi Zoltán (10-23- f) Fordítás: Gilicze Bálint, Rézműves László Műszaki szerkesztő: Csutak Hoffmann Levente Tördelés: Csutak Hoffmann Levente Borító: Zsédely Teréz Felelős kiadó a Kiskapu Kft. ügyvezető igazgatója © 2004 Kiskapu Kft. 1081 Budapest, Népszínház u. 31. I. 7. Telefon: (+36-1) 477-0443 Fax: (+36-1) 303-1619 http ://kiado .kiskapu. hu/ e-mail:
[email protected] ISBN: 963 9301 80 9 Készült a debreceni Kinizsi Nyomdában Felelős vezető: Bördős János
Áttekintés
Tartalomjegyzék Előszó A szerzőről Bevezetés
I. rész Megvalósítási és fejlesztési módszerek 1. fejezet Kódolási stílusok 2. fejezet Objektumközpontú programozás tervezési minták segítségével 3. fejezet Hibakezelés 4. fejezet Megvalósítás PHP nyelven: a sablonok és a Világháló 5. fejezet Megvalósítás PHP nyelven: önálló programok 6. fejezet Egységtesztelés 7. fejezet A fejlesztőkömyezet kezelése 8. fejezet Hogyan tervezzünk jó API-t?
II.rész Gyorstárak 9. fejezet Teljesítményfokozás külső módszerekkel 10. fejezet Adatösszetevők átmeneti tárolása 11. fejezet Számítási újrahasznosítás
III. rész Elosztott alkalmazások 12. fejezet Adatbázisok használata 13. fejezet A felhasználók hitelesítése és a munkamenetek biztonsága
vi
PHP fejlesztés felsőfokon
14. fejezet 15. fejezet 16. fejezet
Munkamenetek kezelése Elosztott környezet kiépítése RPC: Együttműködés távoli szolgáltatásokkal
17. fejezet 18. fejezet 19. fejezet
Teljesítménymérés: teljes alkalmazások tesztelése Profilkészítés Szintetikus mérés: kódblokkok és függvények értékelése
20. 21. 22. 23.
A PHP és a Zend Engine titkai A PHP bővítése: I. rész A PHP bővítése: II. rész SAPI-k készítése és a Zend Engine bővítménye
V. r fejezet fejezet fejezet fejezet
Tárgymutató
Tartalomjegyzék
Előszó A szerzőről Bevezetés
Megvalósitási és fejlesztési módszerek 1. fejezet
Kódolási stílusok
A megfelelő stílus kiválasztása .............................................................................. 4 A kód formázása és elrendezése............................................................................... 4 Behúzás................................................................................................................ 4 Sorhossz............................................................................................................... 7 Térközök használata .......................................................................................... 8 SQL irányelvek..................................................................................................... 8 Vezérlési szerkezetek............................................................................................ 9 Kapcsos zárójelek a vezérlési szerkezetekben ................................................9 A kapcsos zárójelek következetes használata..................................................10 for vagy while vagy foreach?..........................................................................11 Cikluson belüli vezérlés a break és a continue használatával.......................... 13 A mélyen egymásba ágyazott feltételek elkerülése..........................................14
viii
PHP fejlesztés felsőfokon
Névszimbólumok ................................................................................................ 14 Állandók és valódi globális változók ................................................................. 16 Hosszú életű változók .......................................................................................18 Ideiglenes változók .......................................................................................... 18 Több szóból álló nevek........................................................................................ 19 Függvénynevek .................................................................................................. 19 Osztálynevek...................................................................................................... 20 Tagfüggvénynevek.............................................................................................. 20 Az elnevezések következetessége .......................................................................20 A változónevek és sémanevek egyeztetése ........................................................ 21 A zavaros kódok elkerülése ................................................................................. 22 Rövid címkék......................................................................................................22 HTML készítése echo-val....................................................................................23 Következetes zárójelezés..................................................................................... 23 Dokumentáció .....................................................................................................24 Soron belüli megjegyzések.................................................................................. 25 API dokumentáció.............................................................................................. 26 A phpDocumentor használata........................................................................ 27 További olvasmányok............................................................................................. 31
2. fejezet
Objektumközpontú programozás tervezési minták segítségével
Bevezetés az objektumközpontú programozásba..................................................... 34 Öröklés ............................................................................................................ 36 Egységbe zárás....................................................................................................37 Statikus tulajdonságok és tagfüggvények............................................................. 38 Különleges tagfüggvények.................................................................................. 39 Rövid bevezetés a tervezési minták használatába .................................................. 41 Az Illesztő minta ............................................................................................. 41 A Sablon minta................................................................................................... 46 Többalakúság......................................................................................................47 Felületek és típusjelzések.................................................................................... 49 A Gyár minta .................................................................................................... 52 Az Egyke minta ............................................................................................... 54 Túlterhelés ..........................................................................................................56
PHP fejlesztés felsőfokon
Az SPL és a bejárók .......................................................................................... 62 _ _call() ........................................................................................................... 68 _ _autoloadO ................................................................................................... 71 További olvasmányok............................................................................................. 71
3. fejezet
Hibakezelés
A hibák kezelése.................................................................................................... 76 A hibák megjelenítése ...................................................................................... 76 A hibák naplózása .............................................................................................78 A hibák figyelmen kívül hagyása.........................................................................79 Műveletek végzése hiba esetén .......................................................................... 80 A külső hibák kezelése........................................................................................... 81 Kivételek................................................................................................................85 Kivételhierarchiák használata............................................................................. 88 Típusos kivételek - egy példa .............................................................................90 A kivételek láncolása.......................................................................................... 96 Konstruktőrön belüli hibák kezelése ...................................................................99 Felső szintű kivételkezelő beállítása.................................................................. 100 Adatérvényesítés ............................................................................................ 103 Mikor használjunk kivételeket? .......................................................................... 108 További olvasmányok........................................................................................... 108
4. fejezet
Megvalósítás PHP nyelven: a sablonok és a Világháló
Smarty................................................................................................................. 110 A Smarty telepítése........................................................................................... 111 Első Smarty sablonunk: Hello, Világ! .............................................................112 Lefordított sablonok a háttérben ..................................................................... 113 A Smarty vezérlési szerkezetei.......................................................................... 114 A Smarty függvényei és egyebek........................................................................117 Átmeneti tárolás a Smarty-val........................................................................... 120 A Smarty haladó szolgáltatásai..........................................................................122 Saját sablonrendszer készítése.............................................................................. 123 További olvasmányok........................................................................................... 125
ix
x
PHP fejlesztés felsőfokon
5. fejezet
Megvalósítás PHP nyelven: önálló programok
A PHP parancssori felülete: bevezetés...................................................................129 A bemenet és kimenet kezelése ..........................................................................130 A parancssori argumentumok feldolgozása...........................................................132 Gyermekfolyamatok létrehozása és kezelése ...................................................... 135 A megosztott erőforrások bezárása.................................................................... 136 Változók megosztása .......................................................................................137 Takarítás a gyermekek után...............................................................................137 Jelzések ...........................................................................................................139 SIGCHLD .................................................................................................. 140 SIGALRM.....................................................................................................142 Egyéb szokványos jelzések...........................................................................144 Démonok írása ................................................................................................... 144 A munkakönyvtár megváltoztatása.................................................................... 145 A kiváltságok feladása ................................................................................... 146 A kizárólagosság biztosítása..............................................................................147 A tanultak összefoglalása: figyelőszolgálat ........................................................ 147 További olvasmányok........................................................................................... 157
6. fejezet
Egységtesztelés
Bevezetés az egységtesztelésbe............................................................................. 161 Egységtesztek írása automatikus egységteszteléshez ........................................ 161 Első egységtesztünk.......................................................................................... l6l Több teszt hozzáadása.......................................................................................163 Kódon belüli és kívüli egységtesztek.....................................................................164 Kódon belüli tesztelés .................................................................................... 164 Önálló tesztek ................................................................................................ 166 Egyszerre több teszt futtatása............................................................................ 168 A PHPUnit további szolgáltatásai......................................................................... 169 Beszédesebb hibaüzenetek létrehozása.............................................................. 170 Több tesztfeltétel hozzáadása............................................................................ 171 A setUpO és tearDownO tagfüggvények használata.......................................... 173 Figyelők hozzáadása ...................................................................................... 174 Grafikus felület használata................................................................................176
PHP fejlesztés felsőfokon
Tesztvezérelt tervezés ..........................................................................................176 A Flesch pontszámító........................................................................................ 177 A Word osztály tesztelése..................................................................................178 1. hibajelentés ................................................................................................186 Egységtesztelés webes környezetben .................................................................. 188 További olvasmányok........................................................................................... 191
7. fejezet
A fejlesztőkörnyezet kezelése
Változatkezelés ................................................................................................... 194 A CVS alapjai ................................................................................................. 195 A fájlok módosítása ........................................................................................198 A fájlok különbségeinek vizsgálata .................................................................199 Több fejlesztő egy munkán ............................................................................. 202 Jelzőcímkék.......................................................................................................204 Ágak................................................................................................................. 205 A fejlesztési és üzemi környezet fenntartása...................................................... 206 Csomagkezelés ................................................................................................... 211 A kód csomagolása és kibocsátása..................................................................... 213 Bináris állományok csomagolása....................................................................... 216 Az Apache csomagolása.................................................................................... 217 A PHP csomagolása ........................................................................................ 218 További olvasmányok........................................................................................... 219
8. fejezet
Hogyan tervezzünk jó API-t?
Újraépíthetőség és bővíthetőség ......................................................................... 222 A logika függvényekbe zárása ......................................................................... 223 Egyszerű osztályok és függvények használata .................................................. 224 Névterek használata.......................................................................................... 225 A csatolás csökkentése...................................................................................... 227 Védekező kódolás.................................................................................................228 Kódolási szabványok felállítása ......................................................................229 Fertőtlenítési eljárások használata .................................................................. 229 További olvasmányok...........................................................................................231
xi
xii
PHP fejlesztés felsőfokon
Gyorstárak 9. fejezet Teljesítményfokozás külső módszerekkel Teljesítményfokozás a nyelv szintjén.................................................................... 235 Fordítói gyorstárak............................................................................................236 Nyelvi optimalizálok ...................................................................................... 239 HTTP gyorsítók ............................................................................................. 240 Fordított helyettesek.......................................................................................... 242 Teljesítményfokozás az operációs rendszer szintjén........................................... 246 Helyettes gyorstárak.......................................................................................... 247 Gyorstárbarát PHP alkalmazások..........................................................................248 Tartalomtömörítés................................................................................................ 253 További olvasmányok........................................................................................... 254 RFC-k............................................................................................................... 254 Fordítói gyorstárak............................................................................................254 Helyettes gyorstárak.......................................................................................... 255 Tartalomtömörítés ..........................................................................................255
10. fejezet Adatösszetevők átmeneti tárolása A gyorstárazással kapcsolatos kérdések ............................................................. 258 A gyorstárakban tárolható adatösszetevők felismerése .........................................259 Saját vagy előre elkészített osztályok - melyiket válasszuk? ............................... 259 Gyorstárak a memóriában.....................................................................................263 Gyorstárak szerkezet nélküli fájlokban.............................................................. 263 A gyorstár méretének fenntartása...................................................................... 263 A gyorstárak egyidejű használata és összhangja.................................................264 DBM alapú gyorstárak......................................................................................... 271 A gyorstárak egyidejű elérése és összhangja ................................................... 273 A tartalom érvénytelenítése és a gyorstárak karbantartása..................................273 Gyorstár a megosztott memóriában...................................................................... 278 Süti alapú gyorstárak........................................................................................... 279 A gyorstár méretének kezelése.......................................................................... 284 Egyidejű hozzáférés és az adatok összhangja.....................................................285 Gyorstárak használata az alkalmazásokban.......................................................... 285
PHP fejlesztés felsőfokon
Honlapok tárolása ........................................................................................... 288 Az Apache mod_rewrite modulja — tárolás okosan ........................................295 Oldalak részeinek tárolása.................................................................................299 Lekérdezések és gyorstárak .............................................................................. 302 További olvasmányok........................................................................................... 304
11. fejezet Számítási újrahasznosítás Példa: a Fibonacci-sorozat.................................................................................... 305 Újrahasznosítható adatok tárolása egy kérelmen belül ........................................312 Újrahasznosítható adatok tárolása kérelmek között............................................... 315 Számítási újrahasznosítás a PHP-ben....................................................................319 PCRE-k............................................................................................................. 319 Elemszám és hossz............................................................................................320 További olvasmányok........................................................................................... 320
Elosztott alkalmazások 12. fejezet Adatbázisok használata Ismerkedés az adatbázisok és lekérdezések működésével ....................................324 Lekérdezések vizsgálata az EXPLAIN segítségével...............................................327 Melyik lekérdezést javítsuk?.............................................................................. 329 Adatbázis-elérési minták...................................................................................... 331 Ad hoc lekérdezések ........................................................................................332 Az aktív rekord minta....................................................................................... 333 A leképező minta.............................................................................................. 335 Az egyesített leképező minta ........................................................................... 341 Az adatbázis-elérés hatékonyságának növelése..................................................... 343 Az eredményhalmaz korlátozása .................................................................... 343 Lusta előkészítés ............................................................................................ 345 További olvasmányok........................................................................................... 348
xiii
xiv
PHP fejlesztés felsőfokon
13. fejezet A felhasználók hitelesítése és a munkamenetek biztonsága Egyszerű hitelesítési sémák.................................................................................. 350 Egyszerű HTTP-hitelesítés ............................................................................. 351 A lekérdezési karakterlánc csatolása ...............................................................352 Sütik használata ..............................................................................................352 Felhasználók bejegyzése....................................................................................... 354 A jelszavak védelme..........................................................................................354 Védelem az emberi tényező kihasználása ellen ................................................ 357 A hitelesítés fenntartása - hogyan győződjünk meg arról, hogy még mindig ugyanahhoz beszélünk? .......................................................... 358 A $_SERVER[REMOTE_IP] változatlanságának ellenőrzése............................358 A $_SERVER[USER_AGENT] változatlanságának ellenőrzése ...................... 359 Titkosítatlan sütik használata .......................................................................... 359 A követendő módszerekről ............................................................................. 359 Titkosítás használata................................................................................... 360 Az elavulás megvalósítása........................................................................... 360 Felhasználók azonosítása .......................................................................... 361 Hitelesítés a gyakorlatban - egy példa................................................................ 361 Egyszeri feliratkozás ......................................................................................... 367 Az egyszeri feliratkozás megvalósítása.............................................................. 369 További olvasmányok........................................................................................... 375
14. fejezet
Munkamenetek kezelése
Ügyfél oldali munkamenetek ............................................................................. 378 Munkamenetek megvalósítása sütik segítségével............................................... 379 A példa továbbfejlesztése...................................................................................382 Kiszolgáló oldali munkamenetek .......................................................................383 A munkamenet-azonosító nyomon követése.......................................................385 Beépített módszerek a munkameneti azonosítók követésére ........................385 A PHP munkamenetek alapjai ......................................................................... 387 Saját munkamenet-kezelő módszerek ............................................................. 389 Szemétgyűjtés................................................................................................... 395 Szemétgyűjtés a files kezelőben ..................................................................396
PHP fejlesztés felsőfokon
Szemétgyűjtés az mm kezelőben.................................................................. 396 Szemétgyűjtés a MySession kezelőben ....................................................... 396 Ügyfél vagy kiszolgáló oldali munkamenetek - melyiket válasszuk?.................. 397 Saját beépített munkamenet-kezelők megvalósítása........................................... 397
15. fejezet
Elosztott környezet kiépítése
Mi is az a fürt?..................................................................................................... 399 Fürtök tervezése ................................................................................................. 402 Fő az előrelátás .............................................................................................. 403 Csapatjáték....................................................................................................... 404 A függvények névterei .............................................................................. 404 Hivatkozzunk a szolgáltatásokra teljes leíró nevekkel! .............................. 405 Az erőforrások névterei................................................................................ 405 Tartalom elosztása a fürtökben.......................................................................... 406 Vízszintes méretezés ...................................................................................... 407 Különleges célú fürtök...................................................................................... 407 Gyorstárak elosztott környezetben ..................................................................... 408 Központosított gyorstárak ...............................................................................411 Teljesen szétterített gyorstárak készítése a Spread segítségével ........................ 413 Adatbázisok méretezése .....................................................................................417 Mester-szolga rendszerekre épülő alkalmazások készítése ............................... 421 A többszörözés alternatívái .............................................................................423 A relációs adatbázis-kezelő rendszerek alternatívái .......................................... 424 További olvasmányok........................................................................................... 425
16. fejezet
RPC: Együttműködés távoli szolgáltatásokkal
XML-RPC............................................................................................................. 428 Egy kiszolgáló felépítése: a MetaWeblog API megvalósítása .......................... 430 Az XML-RPC szolgáltatások automatikus felderítése........................................ 435 SOAP................................................................................................................... 438 WSDL............................................................................................................... 441 A sysem.load átírása SOAP szolgáltatássá.........................................................443
xv
xvi
PHP fejlesztés felsőfokon
Amazon webszolgáltatások és összetett típusok................................................. 446 Helyettes kód készítése .................................................................................... 448 A SOAP és az XML-RPC összehasonlítása ........................................................449 További olvasmányok........................................................................................... 450 SOAP .............................................................................................................. 450 XML-RPC ........................................................................................................ 450 Webnaplózás ...................................................................................................451 Nyilvánosan elérhető webszolgáltatások .......................................................... 451
Teljesítmény 17. fejezet Teljesítménymérés: teljes alkalmazások tesztelése A szűk keresztmetszetek passzív azonosítása ...................................................... 456 Terhelésképzők.................................................................................................... 458 ab......................................................................................................................459 httperf .............................................................................................................460 A napló alapú terhelésképző ..................................................................... 461 A munkamenet-szimulátor ........................................................................462 A valósághű adatképző................................................................................ 462 Daiquiri ......................................................................................................... 463 További olvasmányok........................................................................................... 464
18. fejezet
Profilkészítés
A jó PHP profilkészítő titka.................................................................................. 466 Profilkészítő alkalmazások - a bőség zavarával küszködve....................................466 Az APD telepítése és használata ........................................................................ 467 Egy nyomkövetési példa....................................................................................... 469 Egy nagyobb alkalmazás profiljának elkészítése................................................... 471 Az általános gyengeségek felderítése ................................................................. 477 A felesleges szolgáltatások eltávolítása ............................................................... 480 További olvasmányok........................................................................................... 485
PHP fejlesztés felsőfokon
19. fejezet Szintetikus mérés: kódblokkok és függvények értékelése A mérés alapjai .................................................................................................. 489 A mérési környezet kiépítése ............................................................................. 489 A PEAR mérőcsomagja.....................................................................................490 A mérőrendszer kiépítése.................................................................................. 493 Véletlen adatok lépésenkénti használata .........................................................494 A mérőrendszer terhelésének levonása ........................................................... 495 Egyéb időmérési adatok.................................................................................... 497 Mérések a kódban ........................................................................................... 500 Mérési példák .....................................................................................................501 Karakterek keresése a karakterláncok elején...................................................... 502 Makrókifejtés .................................................................................................. 503 Beszúrás vagy összefűzés?.................................................................................510
B ovíthetoseg 20. fejezet A PHP és a Zend Engine titkai A Zend Engine működése: opkódok és optömbök................................................. 516 Változók .............................................................................................................522 Függvények.......................................................................................................... 526 Osztályok .......................................................................................................... 528 Az objektumkezelők ....................................................................................... 530 Objektumok létrehozása ................................................................................. 531 Más fontos adatszerkezetek .............................................................................. 531 A PHP kérelmek életciklusa ...............................................................................534 A SAPI réteg..................................................................................................... 535 A PHP magja ................................................................................................. 537 A PHP bővítési API ............................................................................................ 538 A Zend bővítési API.......................................................................................... 539 Összeáll a kép ................................................................................................. 541
xvii
xviii
PHP fejlesztés felsőfokon
21. fejezet A PHP bővítése: I. rész A bővítmények alapjai..........................................................................................544 Bővítményváz készítése.....................................................................................545 Bővítmények felépítése és engedélyezése .......................................................... 548 Függvények használata .................................................................................. 549 Egy egyszerű példa...................................................................................... 549 A típusok és a memória kezelése....................................................................... 551 Karakterláncok feldolgozása .......................................................................... 555 Más visszatérési makrók ............................................................................. 556 Típusok kezelése............................................................................................... 557 Típusátalakítások és elérési makrók.................................................................. 562 Erőforrások használata......................................................................................566 Hibák visszaadása .......................................................................................... 571 Modulhorgok használata ................................................................................ 572 Modulok indítása és leállítása...................................................................... 572 Modulok kikapcsolása .............................................................................. 578 Kérelmek indítása és kikapcsolása ............................................................ 579 Egy példa: a Spread ügyfél burkolója ..................................................................581 MINIT...............................................................................................................582 MSHUTDOWN................................................................................................. 583 A modul függvényei.......................................................................................... 583 A Spread modul használata ............................................................................ 591 További olvasmányok........................................................................................... 592
22. fejezet A PHP bővítése: II. rész Osztályok megvalósítása...................................................................................... 593 Új osztály létrehozása........................................................................................595 Tulajdonságok hozzáadása................................................................................ 596 Osztályöröklés.................................................................................................. 598 Tagfüggvények hozzáadása .............................................................................. 599 Konstruktőrök hozzáadása................................................................................ 602 Kivételek kiváltása............................................................................................603 Saját objektumok és privát változók.................................................................. 604
PHP fejlesztés felsőfokon
Gyártófüggvények használata .........................................................................607 Felületek létrehozása és megvalósítása ........................................................... 608 Saját munkamenet-kezelők készítése ................................................................. 610 A folyamkezelő API............................................................................................. 615 További olvasmányok........................................................................................... 626
23. fejezet
SAPI-k készítése és a Zend Engine bővítménye
A SAPI-król ........................................................................................................627 A CGI SÁPI.......................................................................................................627 A CGI SÁPI alkalmazás....................................................................................635 A beágyazási SÁPI .........................................................................................638 SÁPI bemeneti szűrők .................................................................................... 640 input_filter ............................................................................................... 641 A treat_data és a default_post_reader........................................................... 645 A Zend Engine módosítása és belső vizsgálata ....................................................646 Figyelmeztetések helyett kivételek.....................................................................646 Opkódok kiíratása ........................................................................................... 649 APD ................................................................................................................. 652 APC ................................................................................................................ 654 A Zend bővítmények visszahívható függvényei..................................................654 Házi feladat.......................................................................................................... 657
Tárgymutató
659
xix
Előszó Nemrég lapozgattam William Gibson könyveit és az Ali Tomorrow's Parties című kötetben erre bukkantam: Ami túltervezett vagy túl egyedi, annak eredménye mindig előre látható, és ez az előre láthatóság, ha nem is bukással, de az elegancia hiányával jár. Gibson elegánsan foglalja össze, miért bukik meg sok rendszer. A színes négyszögek táblára rajzolásával nincs semmi baj, de eme ragaszkodásunk a bonyolulthoz hatalmas hátrányt jelent. Amikor megtervezünk valamit, arra van szükség, hogy az adott problémára adjunk megoldást. Nem szabad előre tekintenünk, hogy a probléma vajon mi lesz évekkel később, egy nagyméretű, összetett felépítményben, amikor pedig egy általános célú eszközt építünk, nem szabad túlzott konkrétsággal megkötnünk a felhasználó kezét. A PHP maga is e kettő - a webes feladatok megoldásának konkrétsága, illetve egy bizonyos, a felhasználók kezét megkötő megoldás megadására való hajlam - között egyensúlyoz. Kevesen mondanák a PHP-re, hogy elegáns. Parancsnyelvként számos, a Világháló csataterein többéves szolgálat közben szerzett sebet hordoz. Ami elegáns benne, az a megközelítés egyszerűsége. Minden fejlesztő időről időre váltogatja, milyen megközelítést alkalmaz egy-egy feladat megoldására. Kezdetben az egyszerű megoldásokat részesítik előnyben, mert nem elég tapasztaltak ahhoz, hogy a bonyolultabb elveket megértsék. Ahogy tudásuk gyarapszik, az alkalmazott megoldások egyre bonyolultabbak lesznek, és a megoldható feladatok nagysága is fokozatosan nő. Ekkor fenyeget annak a veszélye, hogy az összetettség rutinná válik és csapdába ejt. Elegendő idő és erőforrás birtokában minden feladat megoldható szinte bármilyen eszközzel. Az eszköz csupán arra való, hogy ne legyen útban. A PHP éppen erre törekszik. Nem kényszerít ránk semmilyen programozási megközelítést, és igyekszik a lehető legkisebbre csökkenteni a közénk és a megoldandó probléma közé beékelődő rétegek számát.
xxii
PHP fejlesztés felsőfokon
Ez azt jelenti, hogy a PHP-ben minden adott, hogy megtaláljuk a legegyszerűbb és legelegánsabb megoldást, és ne kelljen elvesznünk a rétegek és felületek nyolc előadóterem tábláit elfoglaló tengerében. Természetesen az, hogy minden eszközt megkapunk, amivel elkerülhetjük egy szörnyeteg építését, nem garantálja, hogy így is lesz. De szerencsére itt van nekünk George és ez a könyv. George olyan utazásra hív minket, ami saját útjára hasonlít, nem csupán a PHPvel, hanem a programfejlesztéssel és problémamegoldással kapcsolatban általában. Pár napnyi olvasás után elsajátíthatjuk mindazt a tudást, amit ő a területen évek munkájával szerzett meg. Nem rossz üzlet, úgyhogy nem is érdemes e haszontalan előszóval vesztegetni az időt - irány az első fejezet és az utazás! Rasmus Lerdorf
A szerzőről George Schlossnagle igazgatóként dolgozik az OmniTI Computer Consulting nevű marylandi cégnél, amelynek szakterületét a nagyméretű webes és elektronikus levelezési rendszerek jelentik. Mielőtt az OmniTI-hez került volna, technikai vezetője volt számos magasszintű közösségi webhelynek, ahol tapasztalatokat szerzett a PHP igen nagy vállalati környezetekben történő alkalmazásával kapcsolatban. Állandó résztvevője a PHP közösség munkájának, hozzájárult többek között a PHP mag, illetve a PEAR és PECL bővítménytárak fejlesztéséhez. Mielőtt az információ-technológia területére lépett volna, George matematikusnak készült, és két évig szolgált a Békehadtestben, mint tanár. Tapasztalatai megtanították arra, hogy a problémamegoldásban értékelje az interdiszciplináris megközelítést, ami a bajok gyökeréig hatol, nem csupán a tüneteket kezeli.
Köszönetnyilvánítás A könyv írása során rengeteget tanultam, és ezt mindenkinek meg szeretném köszönni, aki segítette munkámat. A PHP valamennyi fejlesztőjének köszönöm, hogy kemény munkával ilyen nagyszerű terméket állítottak elő. Állandó erőfeszítéseik nélkül e kötetnek nem lett volna témája. Shelley Johnston, Dámon Jordán, Sheila Schroeder, Kitty Jarrett, és a Sams kiadó többi munkatársa: köszönöm, hogy bizalmat szavaztak nekem és könyvemnek. Nélkülük ez csak egy meg nem valósult ábránd lenne. Műszaki szerkesztőim, Brian Francé, Zak Greant és Sterling Hughes: köszönöm a fejezetvázlatok elolvasására és megjegyzésekkel ellátására szánt időt és energiát, ami nélkül biztos vagyok benne - a könyv befejezetlen maradt volna, és tele lenne hibával. Testvéremnek, Theo-nak: köszönöm a folyamatos szakmai kritikát és ösztönzést, valamint azt, hogy átvetted a munkámat, amikor a könyv befejezésén dolgoztam.
xxiv
PHP fejlesztés felsőfokon
Szüleimnek: köszönöm, hogy azzá neveltetek, aki ma vagyok, és különösen hálás vagyok anyámnak, Sherrynek, aki nagylelkűen végigolvasta a könyv valamennyi fejezetét. Remélem, büszkék lehettek rám. És aki a legfontosabb, feleségem, Pei: köszönöm, hogy megingathatatlanul mellettem álltál és egy éven át önzetlenül feláldoztad az éjszakákat és hétvégéket, hogy dolgozhassam a könyvön. Örökké hálás leszek a szeretetért, türelemért és támogatásért.
Bevezetés Ez a könyv arra törekszik, hogy az Olvasóból szakértő PHP programozót faragjon. Szakértő programozónak lenni nem csupán annyit jelent, hogy tökéletesen ismerjük a nyelvtant és a nyelv szolgáltatásait (bár ez kétségkívül segít), hanem azt is, hogy képesek vagyunk hatékonyan használni feladatok megoldására. A könyv elolvasása után tisztában leszünk a PHP erősségeivel és gyengéivel, valamint a webes és más feladatok megoldásának legjobb módszereivel. A kötet az elveket tartja szem előtt, így általános problémákat ír le, és ezekre ad egy-egy konkrét példát, szemben a „szakácskönyv" szemléletű könyvekkel, amelyekben mind a problémák, mind a megoldások egyediek. Ahogy az angol mondás tartja: „Adj egy halat, és egy napig van mit ennem - taníts meg halászni, és soha többé nem éhezem." A kötet célja, hogy megtanítsa az eszközök használatát, amelyekkel bármilyen feladatot megoldhatunk, és hogy megtanítsa kiválasztani a megfelelő eszközt. Véleményünk szerint a legkönnyebb példákon keresztül tanulni, ezért a könyvben rengeteg gyakorlati példa szerepel, amelyek bemutatják a tárgyalt fogalmakat. A valós környezettel nem rendelkező elméleti példák nem sokat érnek, így a könyvben csak valódi feladatok elvégzésére alkalmas, „igazi" kódokat találunk. Nem használtunk olyan osztályneveket, mint az angol nyelvű példákban gyakori Foo és Bar; ahol csak lehetséges volt, igyekeztünk létező nyílt forrású programokból venni a példákat, hogy igazi megvalósításokat lássunk.
xxvi
PHP fejlesztés felsőfokon
A PHP az üzleti életben Amikor 1999-ben elkezdtem hivatásszerűen foglalkozni a PHP programozással, a nyelv éppen csak kezdett több lenni egy újabb, amatőrök által használt parancsnyelvnél. Akkor adták ki a PHP 4-et az első Zend motorral, ami gyorsabbá és stabilabbá tette a nyelvet. A PHP-t használók száma ugrásszerűen növekedni kezdett, de a nagy, üzleti célú webhelyek számára még mindig nehéz volt eladni. A nehézség leginkább két forrásból eredt: • A Perl, ColdFusion és más parancsnyelveken fejlesztők nem frissítették ismereteiket a PHP fejlődésével, így nem voltak tisztában az új képességekkel. • A Java nyelven fejlesztők nagy és teljes keretrendszereket, erőteljes objektumközpontú támogatást, statikus típusokat és más, „üzleti" képességeket kívántak. Mára egyik sem jelent akadályt. A PHP többé nem ragasztónyelv, amit lelkes amatőrök használnak, hanem erőteljes parancsnyelv, amelyet felépítése ideálissá tesz a webes feladatok megoldására. Egy programozási nyelv hat követelménynek kell, hogy eleget tegyen, hogy üzleti célú alkalmazásokban is használhatóvá váljon: • • • • • •
Gyors prototípus-készítés és megvalósítás A modern programozási megközelítések (paradigmák) támogatása Méretezhetőség (Kiváló) teljesítmény Együttműködési képesség Bővíthetőség
Az első követelmény - a gyors prototípus-készítési lehetőség - születése óta erőssége a PHP-nek. A webes fejlesztések és a celofánba csomagolt szoftvertermékek közötti egyik lényeges különbség, hogy a Weben egy termék „leszállításának" szinte semmilyen költsége nincs. A csomagolt termékek esetében azonban egy aprócska hiba is azt jelentheti, hogy ezernyi CD-t írattunk tele hibás kóddal, és ezt csak úgy javíthatjuk ki, ha értesítjük valamennyi érintett vásárlót a hibajavítás létezéséről, és rávesszük őket, hogy töltsék le és telepítsék. A webes hibajavításoknál elég, ha a felhasználó legközelebb újra betölti az oldalt, ezért a webalkalmazások rugalmasan és gyakran frissíthetők. A parancsnyelvek általában is kitűnőek rugalmas programok fejlesztésére, mert lehetővé teszik, hogy anélkül fejlesszünk gyorsan és próbáljunk ki új ötleteket, hogy ismételten végig kellene járnunk a fordítás—összeszerkesztés—tesztelés-hibakeresés procedúráját. A PHP különösen jó az ilyesmire, mert nagyon gyorsan tanulható, így új fejlesztőket minimális tapasztalattal is bevonhatunk.
PHP fejlesztés felsőfokon
A PHP 5 a többi követelménynek is maradéktalanul megfelel. Amint a könyvben látni fogjuk, a PHP új objektummodellje erőteljes és a szabványoknak megfelelő objektumközpontú támogatást nyújt. A PHP fürge és méretezhető, köszönhetően az alkalmazható programozási stratégiáknak, illetve annak, hogy az üzleti logika létfontosságú részeit könnyű újra megvalósítani valamilyen alacsonyszintű nyelven. A nyelv emellett tengernyi bővítményt biztosít a más szolgáltatásokkal való együttműködésre, az adatbázis-kiszolgálóktól a SOAP-ig. Végül, a PHP megfelel a programozási nyelvek legfontosabb követelményének: egyszerűen bővíthető. Ha a nyelv nem rendelkezne a számunkra szükséges szolgáltatással vagy képességgel, mi magunk is hozzáadhatjuk.
A könyv felépítése A kötet öt, többé-kevésbé önálló részre oszlik. Bár úgy szerkesztettük meg, hogy az érdeklődő könnyen előreugorhasson egy adott fejezethez, a könyvet javasolt elejétől a végéig elolvasni, mert számos példát fokozatosan építünk fel. A könyv szerkezete a tanulás természetes folyamatához igazodik. Először azt tárgyaljuk, hogyan kell helyes PHP kódot írni, majd rátérünk az egyes módszerekre, azután a teljesítmény fokozására, végül a nyelv bővítésére. A felépítés azon alapul, hogy hisszük, egy profi programozó legfontosabb felelőssége, hogy „karbantartható" kódot írjon, és hogy könnyebb egy jól megírt kódot gyors futásúvá tenni, mint egy gyors, de silány kódot feljavítani.
I. rész Megvalósítási és fejlesztési módszerek 1. fejezet (Kódolási stílusok)
Az első fejezet a kötetben használt kódolási szokásokat mutatja be, és ezek köré egy kódolási stílust épít, valamint rávilágít a következetes, jól dokumentált kód fontosságára. 2. fejezet (Objektumközpontú programozás tervezési minták segítségével)
A második fejezet a PHP 5 objektumközpontú (objektum-orientált, OOP) programozást támogató szolgáltatásait részletezi, és az ilyen irányú képességeket általános tervezési minták környezetében mutatja be. Azzal, hogy teljes áttekintést nyújt mind a PHP 5-ben megjelent új OOP szolgáltatásokról, mind az OOP megközelítés mögött megbúvó elvekről, a fejezet hasznos lehet az OOP programozással ismerkedők és a tapasztalt programozók számára is. 3. fejezet (Hibakezelés)
Hibázni emberi dolog. A harmadik fejezet a PHP eljárásközpontú (procedurális) és OOP hibakezelő eljárásait tárgyalja, különös tekintettel a PHP 5 új, kivétel alapú hibakezelési képességeire.
xxvii
xxviii
PHP fejlesztés felsőfokon
4. fejezet (Megvalósítás PHP nyelven: a sablonok és a Világháló)
A negyedik fejezet a sablonrendszereket tekinti át, vagyis az olyan elemkészleteket, amelyek a megjelenítés és a programkód kettéválasztását segítik. A fejezet összehasonlítja a teljes és az ad hoc jellegű sablonrendszerek előnyeit és hátrányait, az előbbire példaként a Smarty-t használva. 5. fejezet (Megvalósítás PHP nyelven: önálló programok)
Manapság csak igen kevés webalkalmazásnak nincs szüksége háttérösszetevőre. Az arra való képesség, hogy már létező PHP kód újrahasznosításával írjunk kötegelt feladatokat, héjprogramokat és nem webes feldolgozó rutinokat, létfontosságú a nyelv üzleti környezetben való hasznosításában. Az ötödik fejezet az önálló programok és démonok írásának alapjait taglalja. 6. fejezet (Egységtesztelés)
Az egységtesztelés annak egyik módszere, hogy ellenőrizzük, a kód megfelel-e arra a célra, amire létrehoztuk. A hatodik fejezetben megvizsgáljuk az egységtesztelési módszereket, és azt, hogy a PHPUnit segítségével hogyan készíthetünk rugalmas egységtesztelő csomagokat. 7. fejezet (A fejlesztőkömyezet kezelése)
A kód összehangolása a legtöbb fejlesztő számára nem a legizgalmasabb feladat, mindazonáltal igen fontos. A hetedik fejezet bemutatja, hogyan tarthatjuk kézben a kódot a nagy projektekben, és átfogó bevezetést nyújt a CVS (Concurrent Versioning System, változatkövető rendszer) használatába. 8. fejezet (Hogyan tervezzünk jó API-t?)
A nyolcadik fejezet útmutatást ad egy olyan kódtár létrehozásához, ami kezelhető, rugalmas és könnyen felhasználható különböző munkák során.
II. rész Gyorstárak 9. fejezet (Teljesítményfokozás külső módszerekkel)
A gyorstárak használata valószínűleg a teljesítmény fokozásának, illetve az alkalmazás méretezésének leghatékonyabb módja. A kilencedik fejezet a PHP-n kívüli tárolási módszereket vizsgálja, és a fordítói és helyettes (proxy) gyorstárakat tárgyalja. 10. fejezet (Adatösszetevők átmeneti tárolása)
A tizedik fejezet arra összpontosít, hogyan építhetünk tárolási módszereket magába a PHP kódba, illetve hogyan és mikor alkalmazzunk gyorstárakat egy alkalmazásban. Sor kerül egy működőképes tárolórendszer kifejlesztésére is, ami több háttértárat használ.
PHP fejlesztés felsőfokon
11. fejezet (Számítási újrahasznosítás)
A tizenegyedik fejezetben megnézzük, hogyan tehetjük az egyes algoritmusokat és folyamatokat hatékonyabbá a köztes adatok átmeneti tárolásával. Lefektetjük a számítási újrahasznosítás alapelveit, és azokat gyakorlati példákkal illusztráljuk.
III. rész Elosztott alkalmazások 12. fejezet (Adatbázisok használata)
Az adatbázisok szinte minden dinamikus webhelyen központi szerepet töltenek be. A tizenkettedik fejezet a PHP és az adatbázis-rendszerek közötti híd építésének hatékony módszereit mutatja be. 13. fejezet (A felhasználók hitelesítése és a munkamenetek biztonsága)
A tizenharmadik fejezet a felhasználók azonosításának kezelését, és az ügyfél-kiszolgáló kapcsolatok biztonságát veszi górcső alá. Többek között tárgyaljuk a titkosított munkameneti információk „sütikben" (cookie) való tárolását, és teljes egészében megvalósítunk egy egyszerű bejelentkezési rendszert is. 14. fejezet (Munkamenetek kezelése)
A tizennegyedik fejezet a PHP munkameneti bővítésének ismertetésével, illetve saját munkamenet-kezelők írásával folytatja a felhasználói munkamenetek tárgyalását. 15. fejezet (Elosztott környezet kiépítése)
A tizenötödik fejezet olyan méretezhető alkalmazások építését mutatja be, amelyek egyetlen gépnél többet igényelnek. Részletezzük a számítógépfürtök összeállítását és hatékony kezelését, valamint azt, hogy hogyan kezelhetjük hatékonyan az átmeneti tároló és adatbázisrendszereket. 16. fejezet (RPC: együttműködés távoli szolgáltatásokkal)
Az egyszerű webes gép-gép kommunikációt lehetővé tevő szolgáltatások kulcsszava manapság a webszolgáltatás. Ebben a fejezetben a két legelterjedtebb webszolgáltatási protokollt nézzük meg, az XML-RPC-t és a SOAP-ot.
IV. rész Teljesítmény 17. fejezet (Teljesítménymérés: teljes alkalmazások tesztelése)
Az alkalmazás teljesítményének mérése szükséges ahhoz, hogy meggyőződhessünk róla, elbírja azt a forgalmat, amelynek feldolgozására szánták, és hogy azonosíthassuk azokat az összetevőket, amelyek szűk keresztmetszetet jelenthetnek. A tizenhetedik fejezetben áttekintjük a különböző alkalmazásmérő (benchmark) programcsomagokat, amelyekkel egy alkalmazás teljesítménye és stabilitása megmérhető.
xxix
xxx
PHP fejlesztés felsőfokon
18. fejezet (Profilkészítés)
Miután azonosítottuk a fontosabb lehetséges szűk keresztmetszeteket egy alkalmazásban, profilkészítő eszközökkel elkülöníthetjük a problémás részeket a kódban. A tizennyolcadik fejezet a profilkészítés célját és módszereit ismerteti, majd megtanít az APD (Advanced PHP Debugger) használatára, amellyel megvizsgálhatjuk a kódot. 19. fejezet (Szintetikus mérés: kódblokkok és függvények értékelése)
Két kódrészletet lehetetlen összehasonlítani, ha különbségeik mennyiségi mérésére nincs mód. A tizenkilencedik fejezet áttekinti a teljesítménymérési módszereket, illetve az egyedi mérőprogramok megvalósítását és értékelését.
V. rész Bővíthetőség 20. fejezet (A PHP és a Zend Engine titkai)
Ha tudjuk, hogyan működik a PHP „a színfalak mögött", okosabb tervezési döntéseket hozhatunk, kihasználva a PHP erősségeit és megkerülve gyengéit. A huszadik fejezet a PHP belső működésének technikai részleteit tartalmazza, illetve azt, hogy hogyan kommunikálnak a webkiszolgálókhoz hasonló alkalmazások a PHP-vel, hogyan készít az értelmező a parancsfájlokból köztes kódot, és hogyan zajlik a program végrehajtása a Zend motorban. 21. fejezet (A PHP bővítése: I. rósz)
A huszonegyedik fejezet a C nyelvű PHP bővítmények írásába vezet be. Foglalkozik a meglevő PHP kódok C nyelvre való átültetésével, illetve azzal is, hogyan írhatunk olyan bővítményeket, amelyek lehetővé teszik, hogy a PHP hozzáférjen a mások által készített C könyvtárakhoz. 22. fejezet (A PHP bővítése: II. rész)
A huszonkettedik fejezet az előző témáját folytatja magasabb szinten; olyan témakörökkel, mint az osztályok létrehozása a bővítmények kódjában, illetve az adatfolyamok és a munkamenet-kezelési képességek használata. 23. fejezet (SAPI-k készítése és a Zend Engine bővítése)
A huszonharmadik fejezet a PHP alkalmazásokba ágyazásával foglalkozik, illetve a Zend Engine bővítésével, amelynek révén megváltoztathatjuk a nyelv alapviselkedését.
PHP fejlesztés felsőfokon
Felületek és változatok A kötet a PHP 5-re épül, de az anyag mintegy tíz százalékának kivételével nem kifejezetten csak a PHP 5-re vonatkozik. (Az említett tíz százalékba a 2. és 22. fejezetekben bemutatott új, objektumközpontú szolgáltatások, illetve a 16. fejezetben a SOAP tárgyalása tartozik.) Elveket és módszereket mutatunk be, hogy kódunk gyorsabb, okosabb és jobban tervezett legyen. Reményeink szerint legalább a könyv fele hasznosnak bizonyul abban, hogy bármilyen nyelven jobb kódot írjunk. Minden programot, ami a kötetben szerepel, Linuxon írtunk és teszteltünk, de módosítás nélkül futniuk kell Solaris, OS X, FreeBSD vagy bármely más Unix-klón rendszeren is. A programok legtöbbje minimális módosítással Windowson is futtatható, bár egyes segédeszközök (mégpedig az 5. fejezetben bemutatandó pcntl függvények) esetleg nem ültethetők át teljesen.
xxxi
Megvalósítási és fejlesztési módszerek
1 Kódolási stílusok
„Minden legyen olyan egyszerű, amennyire csak lehet, de semmivel sem egyszerűbb." Albert Einstein (1879-1955) „Keresd az egyszerűséget, de ne bízz benne." Alfréd North Whitehead (1861-1947)
Nem számít, milyen szintű tudással rendelkezünk a PHP-t illetően, nem számít, mennyire ismerjük a nyelvet, annak szabályait és különböző szolgáltatásait, attól még írhatunk silány vagy zavaros kódot. A nehezen olvasható kód pedig nehezen karbantartható, és kínszenvedés benne a hibakeresés. A szegényes kódolási stílus a szakértelem hiányát mutatja. Ha pillanatnyi munkánk életünk végéig tartana és soha nem kellene másnak a kódhoz nyúlnia, akkor sem lenne elfogadható, hogy olyan kódot írjunk, ami rosszul szerkesztett. Nekem is nehézséget okoz a két-három éve általam írt könyvtárak bővítése és azokban a hibák keresése, még akkor is, ha a stílus tiszta. Ha pedig olyan kódra bukkanok, amit rossz stílusban írtam meg, gyakran éppoly sokáig tart kibogoznom annak logikáját, mintha újra megírnám a semmiből. A helyzetet bonyolítja, hogy egy programozó sem „légüres térben" dolgozik: programjainkat jelenlegi és jövőbeli kollégáinknak kell majd karbantartaniuk. Két, önmagában megfelelő stílus keveréke viszont ugyanúgy olvashatatlan és karbantarthatatlan kódot eredményezhet, mintha semmilyen stílust nem követnénk, ezért nem csak az a fontos, hogy megfelelő stílusban programozzunk, hanem az is, hogy az együtt dolgozó fejlesztők következetesen ragaszkodjanak egy közös stílushoz.
4
PHP fejlesztés felsőfokon
Megtörtént, hogy örököltem egy megközelítőleg 200 000 sorból álló kódtárat, amit három fejlesztőcsapat dolgozott ki. Volt, amikor szerencsém volt, és egy include legalább fájlon belül következetesnek bizonyult - de gyakran megesett, hogy egyetlen fájlban három különböző stílus képviselte magát.
A megfelelő stílus kiválasztása A kódolási stílus kiválasztását nem szabad elhamarkodnunk. A kód, amit írunk, „túlél" minket, és az idők során egy stílusváltás több gondot okozhat, mint amennyi nyereséget hoz. Egy olyan kód, amit minden új fejlesztő új stílusban ír, gyorsan kezelhetetlen masszává változhat. Amellett, hogy fontos képesnek lennünk egy induló munka stílusának kiválasztására, meg kell tanulnunk alkalmazkodnunk más szabványokhoz is. Nincs tökéletes szabvány: a kódolási stílus leginkább személyes ízlésünktől függ. A „tökéletes stílus" kiválasztásánál sokkal többet ér, ha a kód stílusa mindenhol következetes - tehát ne siessünk megváltoztatni egy általunk véletlenül nem kedvelt, de egyébként következetes stílust.
A kód formázása és elrendezése A kód formázása és elrendezése - amibe beletartozik a behúzás, a sorhossz meghatározása, a térközök használatának módja, vagy akár az SQL (Structured Query Language, strukturált lekérdezőnyelv) parancsok írási módja - a legfontosabb eszköz, amivel kifejezhetjük a kód logikai szerkezetét.
Behúzás Ebben a könyvben a kód szerkezetét és a kódblokkokat behúzással jelöljük. A kódszervezés eszközeként alkalmazott behúzások jelentőségét nem lehet eléggé hangsúlyozni. Számos programozó olyannyira fontosnak tartja, hogy a Python programozási nyelv például nyelvtani szabályként tartalmazza: a helytelen behúzásokat tartalmazó Python kód le sem fordítható! Bár a behúzás a PHP-ben nem kötelező, olyan erőteljes vizuális szervezőeszköz, amit célszerű következetesen felhasználnunk programjainkban. Vegyük például az alábbi kódot: if($month = = 'september' II $month == II $month == 'november') { return 3 0;
'april'
II
$month
==
'june'
}
else if($month == 'february') { if((($year % 4 == 0) && !($year % 100)) II ($year % 400 ==0)) { return 2 9; }
1. fejezet * Kódolási stílusok
else { return 28; } } else { return 31; }
Vessük össze a kódot a következő változattal, ami a behúzásoktól eltekintve teljesen megegyezik vele: if ($month == 'september' $month == 'april' $month == 'june' $month == 'november') { return 3 0;
|| || ||
}
else if($month == 'february') { i f( ( ( $ y e ar % 4 == 0) && ($year % 1 0 0 )) II ($year % 400 ==0)) { return 29; } else { return 28; } } else { return 31; }
A második változatban könnyebb áttekinteni a vezérlési logikát, mint az elsőben. Amikor a behúzásra tabulátorokat használunk, el kell döntenünk, hogy kemény vagy lágy tabulátorokat alkalmazunk, és ehhez következetesen ragaszkodnunk kell a kódban. A szokványos tabulátorok kemény tabulátorok. A lágy tabulátárok viszont tulajdonképpen nem is tabulátorok: ekkor adott számú normál szóközt használunk. A lágy tabulátorok előnye, hogy mindig ugyanúgy jelennek meg, függetlenül a szerkesztő tabulátorbeállításaitól. (A szerző a lágy tabulátorokat részesíti előnyben.) Segítségükkel könnyen fenntartható a következetes behúzás, illetve térköz-használat a kód egészében. Amennyiben kemény tabulátorokat alkalmazunk - különösen ha több fejlesztő különféle szerkesztőprogramokkal dolgozik az adott munkán -, könnyen előfordulhat, hogy a behúzási szintek összekeverednek. Pillantsunk az 1.1 és 1.2 ábrákra. Mindkettő pontosan ugyanazt a kódot mutatja, csakhogy az egyik zavaros, míg a másik tisztán olvasható.
5
6
PHP fejlesztés felsőfokon
1.1 ábra Helyesen behúzott kód.
1.2 ábra Az 1.1 ábrán látott kód, egy más beállításokat tartalmazó szerkesztőben. A használni kívánt tabulátorszélességet is előre ki kell választanunk. Négy szóköz már általában jól olvasható kódot eredményez, miközben megfelelő mennyiségű beágyazási szintet biztosít. A könyvoldalak viszont némileg kevesebb teret adnak, mint a terminálablakok, ezért a kötet minden kódjában két szóközt alkalmaztunk tabulátorszélességként.
1. fejezet • Kódolási stílusok
Számos szerkesztőprogram támogatja a forráskódban elhelyezett „mágikus" megjegyzéseken alapuló automatikus formázásészlelést. A vim-ben például az alábbi megjegyzés önműködően lágy tabulátorok használatára állítja a szerkesztőt (expandtab kapcsoló), a szélességet pedig négy szóközben szabja meg (tabstop és sof ttabstop kapcsolók): // vim: expandtab softtabstop=2 tabstop=2 shiftwidth=2 Emellett a vim : retab parancsa minden kemény tabulátort lágy tabulátorrá alakít a dokumentumban, így célszerű ezt alkalmaznunk, ha a tabulátorok használatáról át szeretnénk állni szóközökre. Az emacs-ben az alábbi megjegyzés hasonló eredménnyel jár: /* * Local variables: * tab-width: 2 * c-basic-offset: 2 * indent-tabs-mode: nil * End: */
Számos nagy programban (közéjük tartozik maga a PHP nyelv is), ilyen típusú megjegyzéseket helyeznek el minden állomány alján; hogy a fejlesztők következetesen betartsák a projekt behúzási szabályait.
Sortiossz A „hány nap van egy hónapban" függvény első sora meglehetősen hosszú, így nehezen látható át az értékellenőrzések sorrendje. Ilyen esetekben célszerű a hosszú sorokat több sorra tördelni, valahogy így: if($month == 'september' II $month == 'april' II $month == 'june' II $month == 'november') { return 3 0; }
A második sor behúzásával jelezhetjük, hogy az az első folytatása. Ha a sor különösen hosszú, érdemes az egyes feltételeket külön sorba írni, mindet behúzni, és egymás alá igazítani: if ($month $month $month $month
== 'september' I I == 'april' II == 'june' II == 'november')
{
return 30; }
7
8
PHP fejlesztés felsőfokon
A fenti módszer a függvények paramétereinél is ugyanolyan jól működik: mail("
[email protected]", "My Subject", $message_body, "From: George Schlossnagle \r\n"); Én általában minden 80 karakternél hosszabb sort több sorba írok, mert ez a szabványos Unix terminálablakok szélessége, és megfelel arra is, hogy a kódot olvashatóbb betűtípussal kinyomtassuk.
Térközök használata A térközökkel a kód logikai szerkezetét tükrözhetjük; segítségükkel például hatékonyan csoportosíthatjuk az értékadásokat, és rávilágíthatunk az összefüggésekre. Az alábbi kód rosszul formázott és nehezen olvasható: $lt = localtimeO ; $name = $_GET [ ' name ' ] ; $email = $_GET['email']; $month = $lt['tm_mon'] + 1; $year = $lt['tm_year'] + 1900; $day = $lt['tm_day']; $address = $_GET['address']; A kódblokkot feljavíthatjuk, ha térközökkel logikailag csoportosítjuk a hasonló értékadásokat, és az egyenlőségjeleket egymás alá igazítjuk: $name = $_GET['name']; $email = $_GET['email']; $address = $_GET['address']; $lt $day $month $year
= = = =
localtime(); $lt['tm_day']; $lt['tm_mon'] + 1; $lt [ ' tm^ear ' ] + 1900;
SQL irányelvek A fejezetben eddig lefektetett kódformázási és -elrendezési szabályok érvényesek mind a PHP, mind az SQL kódokra. Az adatbázisok a legtöbb mai webhely szerves részét képezik, így az SQL nélkülözhetetlen része a kódtáraknak. Az SQL lekérdezések azonban - főleg azokban az adatbázis-rendszerekben, amelyek támogatják az összetett allekérdezéseket - könnyen bonyolulttá és áttekinthetetlenné válhatnak, ezért a PHP-hez hasonlóan az SQL kódokban is használjunk bátran térközöket és sortörést.
1. fejezet • Kódolási stílusok
Vegyük például a következő lekérdezést: $query = "SELECT FirstName, LastName FROM employees, departments WHERE employees.dept_id = department.dept_id AND department.Name = 1Engineering'"; Ez egy egyszerű lekérdezés, de rosszul szerkesztett. Többféle módon is feljavíthatjuk: • nagybetűssé tehetjük a kulcsszavakat, • a kulcsszavaknál új sort kezdhetünk, • a táblamásodnevek (alias) használatával tisztábbá tehetjük a kódot. Lássunk egy példát a lekérdezés módosítására a fentiek szerint: $query = "SELECT firstname, lastname FROM employees e, departments d WHERE e.dept_id = d.dept_id AND d.name = 'Engineering'";
Vezérlési szerkezetek A vezérlési szerkezetek olyan alapvető építőelemek, amelyeket szinte minden modern programozási nyelv tartalmaz. A vezérlési szerkezetek szabják meg, milyen sorrendben hajtja végre a program az utasításokat. Két fajtájuk létezik: a feltételes utasítások és a ciklusok. Azok az utasítások, amelyek csak akkor hajtódnak végre, ha egy adott feltétel teljesül, feltételes utasítások, míg a ciklusok ismétlődően végrehajtott utasítások. Az, hogy egy feltétel teljesülését vizsgálhatjuk, és az állítás igaz vagy hamis voltától függően hajthatunk végre műveleteket, lehetővé teszi, hogy a kódba döntési logikát építsünk be. A ciklusok arra adnak módot, hogy ugyanazt a logikát ismételjük, és így meghatározatlan adatokon végezzünk bonyolult feladatokat. Kapcsos zárójelek a vezérlési szerkezetekben A PHP nyelvtana nagy részét a C programozási nyelvtől vette át. A C-hez hasonlóan az egysoros feltételes utasítások a PHP-ben sem igényelnek kapcsos zárójeleket. Az alábbi kód például gond nélkül lefut: if(isset($name)) echó "Hello $name";
9
10
PHP fejlesztés felsőfokon
Annak ellenére azonban, hogy ez működő kód, nem ajánlott a használata. Ha kihagyjuk a kapcsos zárójeleket, később nehéz lesz anélkül módosítani a kódot, hogy hibát ne vétenénk. Amennyiben például egy újabb sorral szeretnénk kiegészíteni a fenti utasítást, de nem figyelünk eléggé, ilyesmit írhatunk: if(isset($name)) echó "Hello $name"; $known_user = true; Ez egyáltalán nem azt eredményezi, amit szeretnénk. A $known_user értéke mindenképpen true lesz, pedig ezt csak akkor akarjuk, ha a $name változó létezik (isset). A keveredés elkerülése végett mindig használjunk kapcsos zárójeleket, még akkor is, ha csak egyetlen feltételes utasításunk van: if (isset($name)) { echó "Hello $name"; } else { echó "Hello Stranger"; }
A kapcsos zárójelek következetes használata Igyekezzünk következetesen használni a kapcsos zárójeleket a feltételek után. Három elterjedt módszer közül választhatunk: • BSD stílus: a zárójeleket a feltételt követő sorba tesszük, és a kulcsszóhoz, a sor elejére igazítjuk: if ($feltétel) {
// utasítás }
• GNU stílus: a zárójelek itt is a feltételt követő sorba kerülnek, de félúton behúzva a kulcsszó és a feltétel közé: if ($feltétel) {
// utasítás }
• K&R stílus: a nyitó zárójel a kulcsszóval egy sorba kerül: if ($feltétel) { // utasítás }
A K&R stílus neve Kernighan-re és Ritchie-re, A Cprogramozási nyelv című klasszikus szerzőire utal, akik könyvük kódjait e stílussal írták.
1. fejezet • Kódolási stílusok
A kapcsos zárójelek használati módja már-már vallásos színezetet ölt. A vita szenvedélyességét jelzi például, hogy a K&R stílusra időnként úgy hivatkoznak, mint ami „az egyeden igazi zárójelezési stílus". Pedig végsősoron mindegy, melyik stílus mellett döntünk; csak az számít, hogy meghozzuk a döntést és következetesen ragaszkodjunk hozzá. Nekem tetszik a K&R stílus tömörsége, kivéve amikor a feltétel több sorra törik, amikor is átláthatóbbnak találom a BSD stílust. Az utóbbit részesítem előnyben a függvények és osztályok bevezetésénél is, valahogy így: function hello($name) {
echó "Hello $name\n"; }
Az, hogy a függvénydeklarációk zárójelei egészen kikerülnek a bal margóra, lehetővé teszi, hogy első pillantásra felismerjük őket. Mindazonáltal ha olyan munkához csatlakozom, aminek már kialakult stílusa van, igazodom hozzá, még ha magam másik stílust is részesítek előnyben. Hacsak az adott stílus nem kifejezetten rossz, a következetesség mindig fontosabb. for vagy while vagy foreach? Ahol elég egy for vagy foreach, ne használjunk while ciklust. Nézzük a következő kódot: function is_prime($number) {
$i = 2; while($i < $number) { if ( ($number % $i ) == 0) {
return falsé; } $i + +; }
return true; }
Ez a ciklus nem túl hatékony. Gondoljunk bele, mi történik, ha az alábbihoz hasonló módon egy újabb vezérlési ággal bővítjük: function is_prime($number) {
If ( ($number % 2) return true;
!= 0)
}
$i = 0; while($i < $number)
{
{
11
12
PHP fejlesztés felsőfokon
// Egyszerűen ellenőrizzük, hogy $i páros-e if( ($i & 1) == 0 ) { continue; } if ( ($number % $i ) == 0) { return falsé; } $i++; }
return true; }
Ebben a példában először ellenőrizzük, hogy a szám osztható-e kettővel. Ha nem, már nincs szükség arra, hogy megnézzük, osztható-e bármilyen más páros számmal, hiszen minden páros szám közös osztója a 2. Itt véletlenül beavatkoztunk a növelő műveletbe, így végtelen ciklusba kerültünk. A véges számú ismétlődő műveletekhez természetesebb választás a f or ciklus, ahogy itt is: function is_prime($number) {
if(($number % 2)
!= 0)
{
return true; } for($i=0; $i < $number; $i++) { // Egyszerűen ellenőrizzük, hogy $i páros-e if( ($i & 1) == 0 ) { continue; } if ( ($number % $i ) = = 0 ) { return falsé; } } return true; }
Ha tömböket járunk be, a f or-nál még jobb, ha a f oreach ciklust használjuk. Lássunk erre is egy példát: $array = (3, 5, 10, 11, 99, 173); foreach($array as $number) { if(is_prime($number)) { print "$number is prime.Xn"; } }
Ez gyorsabb, mint egy f or ciklus, mert nincs benne kifejezett számláló.
1. fejezet • Kódolási stílusok Cikluson belüli vezérlés a break és a continue használatával
Amikor egy ciklust hajtunk végre, a break használatával ugorhatunk ki azokból a ciklusblokkokból, amelyek végrehajtására már nincs szükségünk. Vegyük a következő ciklust, ami egy beállítófájlt dolgoz fel: $has_ended = 0 ; while(($line = fgets($fp)) !== falsé) { if($has_ended) { } else { if(strcmp($line, '_END_') == 0) { $has_ended = 1; } if(strncmp($line, '//', 2) == 0) { } else { // utasítás feldolgozása } } }
Szeretnénk figyelmen kívül hagyni azokat a sorokat, amelyek C++ stílusú megjegyzésekkel (vagyis / / jelzéssel) kezdődnek, a feldolgozást pedig teljesen be szeretnénk szüntetn: ha egy _END_ deklarációba ütközünk. Ha a cikluson belül nem használunk vezérlési szei kezeteket, kénytelenek leszünk egy kis állapotautomatát építeni. A csúnya beágyazott uta sításokat a continue és a break alkalmazásával kerülhetjük ki: while(($line = f g e t s ( $ f p ) ) != = falsé) if(strcmp($line, '_END_') == 0) { break;
{
}
if(strncmp($line, continue;
'//',
2) == 0)
{
}
// utasítás feldolgozása }
Ez a változat nem csak rövidebb, mint a megelőző, hanem hiányoznak belőle a zavaró beágyazások is.
13
14
PHP fejlesztés felsőfokon A mélyen egymásba ágyazott feltételek elkerülése Programírás során gyakran követik el azt a hibát, hogy mélyen egymásba ágyazott feltételeket hoznak létre, amikor egy ciklus is elég lenne. íme egy jellemző kód, amiben szintén ez a hiba szerepel: $fp = fopenC'file", "r"); if ($fp) { $line = fgets($fp); if($line !== falsé) { // a $line feldolgozása } else { die("Error: Filé is empty); }
else {
die("Error: Couldn't open f i l é " ) ;
}
Ebben a példában a kód törzse (ahol a $line változó feldolgozása történik) két behúzási szinttel beljebb kezdődik, ami egyrészt zavaró, másrészt a szükségesnél hosszabb sorokat eredményez, a hibakezelő feltételeket szétszórja a kódban, és megkönnyíti a zárójelezési hibák vetését. Sokkal egyszerűbb, ha az alábbihoz hasonló módon a hibakezelést (az esetleges kivételekkel együtt) teljes egészében a kód elejére helyezzük, és megszüntetjük a felesleges beágyazást: $fp = fopenC'file", "r"); if (!$fp) { die("Error: Couldn't open filé"); } $line = fgets($fp); if($line === falsé) { die("Error: Filé is empty"); } // a $line feldolgozása
Névszimbólumok A PHP szimbólumokkal rendeli az adatokat a változónevekhez. A szimbólumok adnak módot arra, hogy későbbi újrahasznosítás céljából nevet adhassunk az adatoknak. Minden alkalommal, amikor bevezetünk egy változót, létrehozunk számára egy bejegyzést az aktuális szimbólumtáblában, és az aktuális értékéhez kötjük. íme egy példa: $foo = 'bar'; Ebben az esetben a f oo számára hozunk létre egy bejegyzést az aktuális szimbólumtáblában, és hozzákapcsoljuk aktuális értékéhez, a bar-hoz. Amikor egy osztályt vagy függvényt határozunk meg, egy másik szimbólumtáblába szúrjuk be azt. Lássunk erre is egy példát:
1. fejezet • Kódolási stílusok
function hello($name) { print "Hello $name\n"; }
Itt a hello a függvények szimbólumtáblájába kerül és a lefordított kódhoz (optree) kapcsolódik. A 20. fejezetben megnézzük, hogyan zajlanak ezek a műveletek a PHP-ben, de most arra összpontosítunk, hogyan tehetjük a kódot olvashatóbbá és könnyebben karbantarthatóvá. A PHP kód változó- és függvénynevekkel van tele. A jó elrendezéshez hasonlóan az elnevezési szabályok is azt a célt szolgálják, hogy az olvasó számára világosabbá tegyék a program logikáját. A legtöbb nagy szoftverprojekt használ valamilyen elnevezési rendszert, ami biztosítja az egyes kódrészek egységességét. Az itt bemutatott szabályokat a PHP Extension and Application Repository (PEAR, PHP bővítmény- és alkalmazástár) irányelveiből vettük át. A PEAR olyan PHP programok és osztályok gyűjteménye, amelyeket arra terveztek, hogy általános igényeket kielégítő újrahasznosítható elemek legyenek. Mint a legnagyobb nyilvános PHP kódgyűjtemény, a PEAR egyfajta szabványt biztosít, amelyhez igazodhatunk. Az első szabály, amit meg kell jegyeznünk, a változónevekhez kapcsolódik: soha ne használjunk értelmetlen változóneveket. Számos könyvben és cikkben (beleértve a számítástudománnyal foglalkozó magas szintű szövegeket) találunk jelentéssel nem bíró, általánosító változóneveket, amelyek nem segítik a kód megértését. Vegyük például az alábbi kódot: function test($baz) {
for($foo = 0; $foo < $baz; $foo++) $bar[$foo] = "test_$foo";
{
}
return $bar; }
Ezt gond nélkül lecserélhetjük a következő kódra, amiben már beszédesebb neveket találunk, amelyek jobban rávilágítanak, mi is történik itt: function create_test_array($size) {
fo r ( $ i = 0; $i < $size; $retval[$i] = "test_$i"; } return $retval;
$i++)
{
}
A PHP-ben minden osztály- vagy függvénytörzsön kívül meghatározott változó automatikusan globális változó lesz. A függvényen belül megadott változók csak az adott függvényen belülről láthatók, ha pedig azt szeretnénk, hogy egy globális változó egy függvényen belülről is elérhető legyen, a global kulcsszóval kell bevezetnünk. A változók lát-
15
16
PHP fejlesztés felsőfokon
hatóságára vonatkozó fenti megszorítások a hatóköri szabályok. Egy változó hatóköre az a kódblokk, amelyben a változó külön intézkedés nélkül elérhető. A hatóköri szabályok - amellett, hogy egyszerűek és elegánsak - feleslegessé teszik, hogy az elnevezés attól függjön, hogy egy adott változó globális-e. Az elnevezési szabály szempontjából a PHP változóit három csoportba oszthatjuk: • Valódi globális változók - olyan változók, amelyekre globális hatókörben szándékozunk hivatkozni. • Hosszú életű változók - olyan változók, amelyek bármilyen hatókörben létezhetnek, de több kódblokkban is hivatkozunk rájuk, illetve több kódblokk számára is fontos információt tárolnak. • Ideiglenes változók - kisebb kódrészekben használt változók, amelyek ideiglenes információt tárolnak.
Állandók és valódi globális változók A valódi globális változókat és az állandókat csupa nagybetűvel célszerű írni, így azonnal láthatjuk rajtuk, hogy globális változók. íme egy példa: $CACHE_PATH = '/var/cache/'; function list_cache() { global $CACHE_PATH; $dir = opendir($CACHE_PATH); while(($file = readdir($dir)) !== falsé && is_file{$file)) { $retval[] = $file; } closedir($dir);
return $retval; }
A csupa nagybetű használata révén azt is rögtön kiszúrhatjuk, ha olyan változót próbálunk globálissá tenni, amit nem kellene. A globális változók alkalmazása mindazonáltal nagy hiba a PHP programokban. Használatuk általában a következő okok miatt nem célszerű: • Bárhol megváltoztathatók, így nehezebb azonosítani az esetleges hibák helyét. • „Beszennyezik" a globális névteret. Ha egy globális változónak olyan általános nevet adunk, mint például a $ számláló, és beépítünk egy könyvtárat, ami szintén tartalmaz egy ugyanilyen nevű globális változót, mindkettő akadályozni fogja a másikat. Ahogy a kódtár nő, egyre nehezebb lesz az ilyen ütközéseket elkerülni. A megoldás általában egy elérőfüggvény (accessor function) alkalmazása.
1. fejezet • Kódolási stílusok
Az alábbi kód globális változókat használ egy maradandó (perzisztens) adatbázis-kapcsolat valamennyi változójához:
global $database_handle; global $server; global $user; global $password; $database_handle = mysql_pconnect($server, $user, $password); Ehelyett jobb, ha egy osztályt alkalmazunk:
class Mysql_Test { public $database_handle; priváté $server = 'localhost'; priváté $user = 'test1; priváté $password = ' t e s t ' ; public function _____ construct() {
$this->database_handle = mysql_pconnect($this->server,
$this->user,
$this->password);
} }
A 2. fejezetben ismét elővesszük ezt a példát, és az egykék, illetve burkoló osztályok ismertetésénél még hatékonyabb megoldásokat mutatunk be. Máskor egy bizonyos változó elérésére lehet szükség: $US_STATES = array('Alabama',
...
,
'Wyoming');
Ebben az esetben egy osztály használata túlzás lenne. Ha nem szeretnénk globális változót alkalmazni, helyettesítsük egy elérő függvénnyel, ami statikus változóként a globális tömböt kapja:
function
us_states()
{
static $us_states = array('Alabama', return $us_states;
...
,
'Wyoming');
}
Ennek a megoldásnak az is előnye, hogy a forrástömb nem módosuló (immutable) lesz, mintha a de fi ne kulcsszóval állítottuk volna be.
17
18
PHP fejlesztés felsőfokon
Hosszú életű változók A hosszú életű változóknak adjunk rövid, de leíró jellegű neveket. Az ilyen nevek növelik az olvashatóságot, és megkönnyítik a változók nyomon követését. A hosszú életű változók nem szükségszerűen globálisak, nem is feltétlenül a fő hatókörhöz tartoznak; egyszerűen olyan változók, amelyeket jelentős hosszúságú kódban használunk, illetve amelyek ábrázolását nem árt tisztábbá tenni. A következő példában a beszédes változónevek segítenek leírni a kód célját és viselkedését: function clean_cache($expiration_time) { global $CACHE_PATH; $cachefiles = list_cache(); foreach($cachefiles as $cachefile) { if(filemtime($CACHE_PATH." / " .$cachefile) $expiration_time) { unlink($CACHE_PATH." / " .$cachefile);
> time()
+
} } }
Ideiglenes változók Az ideiglenes változók neve legyen rövid és tömör. Mivel ilyen változókat általában csak kisebb kódblokkokban találunk, nem kell, hogy a nevük magyarázó jellegű legyen. Ez különösen a ciklusváltozókra igaz; ezeknek célszerű mindig az i, j, k, 1, m és n neveket adni. Vegyük az alábbi példát: $number_of_parent_indices = count($parent); for($parent_index=0; $parent_index system.load Ezt a kérelmet a POST módszerrel elküldhetjük az XML-RPC kiszolgálónak, amely megkeresi és végrehajtja a megadott tagfüggvényt (system. load), átadva a paramétereket (ez esetben nem adtunk meg egyetlen paramétert sem). Az eredmény végül visszakerül a hívó félhez. Jelen esetben ez a gép aktuális terhelése, melyet a Unix uptime nevű héjparancsától kaptunk meg. íme egy lehetséges kimenet: 0.34 Természetesen ezeket a dokumentumokat nem magunknak kell elkészítenünk és értelmeznünk. Számos XML-RPC megvalósítás létezik PHP alatt. Jómagam a PEAR XML-RPC osztályokat kedvelem, mert ezekhez hozzájutunk a PHP-vel (a PEAR telepítő használja
16. fejezet • RPC: Együttműködés távoli szolgáltatásokkal
őket). Következésképpen gyakorlatilag minden rendszeren jelen vannak - így nincs is igazán okunk, hogy mást keressünk. Az XML-RPC párbeszéd két részből áll: az ügyfél kérelméből és a kiszolgáló válaszából. Beszéljünk előbb az ügyfél kódjáról. Az ügyfél készít egy kérelem dokumentumot, elküldi a kiszolgálónak, majd értelmezi a kapott választ. Az alábbi kód a korábbiakban látott kérelmet készíti el, és értelmezi az erre kapott választ: require_once
'XML/RPC.php';
$client = new XML_RPC_Client('/xmlrpc.php', 'www.example.com'); $msg = new XML_RPC_Message('system.load1); $result = $client->send($msg); if ($result->faultCode()) { echó "Error\n"; }
print XML_RPC_decode($result->value()); Létrehozunk egy új XML_RPC_Client objektumot, megadva számára a távoli szolgáltatás URI-jét és címét. Ezután készítünk egy XML_RPC_Message objektumot, amely tartalmazza a meghívni kívánt tagfüggvény nevét (system. load). Mivel a tagfüggvénynek nem adunk meg paramétereket, további adatok átadására nincs szükség. Ezután elküldjük az üzenetet a send () tagfüggvénnyel. Az eredményt előbb megvizsgáljuk, és ha nem találunk hibát, az XML formátumból a PHP saját változótípusára alakítjuk az XML_RPC_Decode () segítségével. Természetesen a kiszolgáló oldalán is szükség van némi kódra, ami fogadja a kérelmet, megtalálja és végrehajtja a megfelelő visszahívható függvényt, majd visszaküldi a választ, íme egy lehetséges megoldás, amely kezeli az ügyfél kódjában megadott system. load tagfüggvényt: require_once 'XML/RPC/Server.php'; function system_load() {
$uptime = "uptime"; if(preg_match("/load average: ([\d.]+)/", $uptime, $matches)) { return new XML_RPC_Response( new XML_RPC_Value($matches[1] , 'string')); } }
429
430
PHP fejlesztés felsőfokon
$dispatches = array('system.load'
=> array('function' => 'system_uptime')); new XML_RPC_Server($dispatches, 1 ); A PHP függvények megbirkóznak a korábban meghatározott beérkező kérelmekkel. Magunknak mindössze a system. load kérelemmel kell foglalkoznunk, melynek a system. load () tagfüggvény hívása felel meg. Ez a Unix uptime parancsát hajtja végre, és az eredményből kiolvassa a gép egyperces átlagos terhelését. Ezután a kapott adatot becsomagolja egy XML_RPC_Value objektumba, majd a visszaküldéshez beburkolja egy XML_RPC_Response objektumba. Ezután a visszahívható (callback) függvény rákerül egy kiosztási térképre, amely meghatározza, mely függvényekhez rendelje a kiszolgáló az egyes bejövő kérelmeket. A meghívandó függvényekből készítünk egy $dispatches tömböt, amely XML-RPC tagfüggvényneveket képez le PHP függvényekre. Végezetül létrehozunk egy XML_RPC_Server objektumot, és átadjuk neki az előzőleg készített kiosztási tömböt. A második paraméter 1 értéke azt jelzi, hogy a kérelmet azonnal ki kell szolgálni a service () tagfüggvénnyel (ez egy belső hívás). A service () megvizsgálja a HTTP POST nyers adatait, megkeresi az XML-RPC kérelmet, majd a kiosztás alapján elvégzi a függvényhívást. Mivel a tagfüggvény a PHP $HTTP_RAW_POST_DATA autoglobálisra hivatkozik, semmiképpen sem szabad kikapcsolnunk a php. ini fájl always_populate_raw_post_data beállítását. Ha ezek után elhelyezzük a kiszolgálókódot a www. example. com/xmlrpc .php címen, és egy tetszőleges gépen futtatjuk az ügyfélkódot, az alábbiakat kapjuk vissza: > php system_load.php 0.34 Vagy valami hasonlót, a terhelésátlagtól függően.
Egy kiszolgáló felépítése: a MetaWeblog API megvalósítása Az XML-RPC igazi erőssége, hogy szabványos módot ad a szolgáltatások közti adatcserére. Ez különösen akkor hasznos, ha nem tudjuk ellenőrizni a kérelem útjának mindkét oldalát. Az XML-RPC-vel könnyen módot adhatunk arra, hogy bárki érintkezhessen a szolgáltatással. Jó példát adnak erre a webnaplóküldési API-k. Számos webnaplózó rendszer létezik, és rengeteg olyan eszköz, melyek segítenek a használatukban és a bejegyzések küldésében. Ha nem volnának szabványos eljárások, a széleskörű felhasználhatóság érdekében minden eszköznek támogatnia kellene minden webnaplót, vagy fordítva. Az ilyen kapcsolatrendszer megtartása lehetetlen, ha a benne részt vevő alkalmazások száma növekszik.
16. fejezet • RPC: Együttműködés távoli szolgáltatásokkal
Jóllehet a webnaplók lehetőségei és megvalósításai változatosak lehetnek, lehetséges olyan szabványos műveletcsalád meghatározása, melyek a bejegyzések küldését végzik. A webnaplóknak és az eszközöknek ezek után csak a megfelelő felületet kell megvalósítaniuk, és így mindenki mindenkivel együttműködhet. Dacára a webnaplózó rendszerek nagy számának, mindössze három webnaplóküldési API terjedt el széles körben: a Blogger API, a MetaWeblog API, valamint a MovableType API (ami valójában mindössze a MetaWeblog API bővítése). A bejegyzésküldő eszközök e három protokoll valamelyikét használják az adatátvitelhez, így ha mindegyiküket megvalósítjuk, webnaplónk képes lesz fogadni bármely eszköz bejegyzéseit. Mindez nagyszerű lehetőség arra, hogy egy új webnaplózó rendszert elfogadottá tegyünk. Természetesen először egy webnaplózó rendszerre van szükség, melyhez ezek az API-k kapcsolódhatnak. Egy teljes webnaplózó rendszer felépítése meghaladná könyvünk kereteit, így hát megelégszünk azzal, hogy egy XML-RPC réteget adunk a Serendipity webnaplóhoz. A szóban forgó API-k a bejegyzések küldését intézik, így a Serendipity alábbi eljárásaival kell érintkezniük:
function serendipity_updertEntry($entry) {} function serendipity_fetchEntry($key, $match)
{}
A serendipity_updertEntry () frissít egy bejegyzést, vagy beszúr egy újat, attól függően, hogy megadtuk-e számára az id változó értékét. A $entry valójában egy tömb, amely az alábbi adatbázistábla egy általános sorának felel meg (vagyis elemei az oszlopokat adják): CREATE TABLE serendipity_entries ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(2 00) DEFAULT NULL, timestamp INT(10) DEFAULT NULL, body TEXT, author VARCHAR(20) DEFAULT NULL, isdraft INT ); A serendipity_f etchEntry () kiolvas egy bejegyzést a táblából a megadott kulcs-érték pár alapján. A MetaWeblog API több lehetőséget biztosít, mint a Blogger API, így előbbi megvalósítására teszünk kísérletet. Tagfüggvényei közül az alábbi három fontosabbat kell megemlítenünk:
metaWeblog.newPost(blogid,username,password,item_struct,publish) returns string metaWeblog.editPost(postid,username,password,item_struct,publish) returns true metaWeblog.getPost(postid,username,password) returns item_struct
431
432
PHP fejlesztés felsőfokon
A blogid a megcélzott webnapló azonosítója (ami jól jön, ha a rendszer több webnaplót is fenntart), a username és a password a küldő azonosítására szolgál, a publish pedig egy jelző, amely megmondja, hogy a küldött bejegyzés csak vázlat-e, vagy egyenesen mehet a naplóba. Az item_struct a küldött adatok tömbje. Ahelyett, hogy saját formátumot választott volna az adatbevitelhez, Dave Winer, a MetaWeblog leírásának szerzője az RSS 2.0 leírás item elemének meghatározását választotta (ezt megtalálhatjuk a http: //blogs. law.harvard.edu/tech/rss címen). Az RSS egy szabványosított XML formátum cikkek és naplóbejegyzések közlésére. Az item bejegyzése az alábbi elemeket tartalmazza:
A szabvány emellett lehetővé teszi egyéb mezők használatát is - hivatkozásokat megjegyzésekre, egyértelmű azonosítókat, valamint kategóriákat. Mindemellett számos webnapló kibővíti az RSS szabványt, úgy, hogy az tartalmazza a content: encoded elemet is, ami a teljes küldeményt tárolja, nem csak annak összefoglalóját, melyet hagyományosan az RSS description elemében találhatunk meg. A MetaWeblog API megvalósításához a korábban említett három tagfüggvénnyel kell foglalkoznunk. Először nézzük, hogyan küldhetünk új bejegyzéseket: function metaWeblog_newPost($message) { $username = $message->params[1]->getval() ; $password = $message->params[2]->getval() ; if(!serendípity_authenticate_author($username, $password)) return new XML_RPC_Response('', 4, 'Authentication Fa i l ed' ); }
$item_struct = $message->params[3]->getval(); $publish = $message->params[4]->getval() ; $entry['title'] = $item_struct['title'] ; $entry['body'] = $item_struct['description']; $entry['author' ] = $username; $entry['isdraft' ] = ($publish == 0)?'true' : ' falsé';
{
16. fejezet • RPC: Együttműködés távoli szolgáltatásokkal
$id = serendipity_updertEntry($entry); return new XML_RPC_Response( new XML_RPC_Value($i d,
's t r i n g ' ) ) ;
}
A metaWeblog_newPost () kinyeri a kérelemből a username és a password paramétereket, majd kicsomagolja XML alakjukat PHP típusokba a getval () tagfüggvénnyel. A függvény ezután hitelesíti a megadott felhasználót. Amennyiben ez nem sikerül, visszaküld egy üres XML_RPC_Response objektumot egy „Authentication Failed" (Sikertelen hitelesítés) hibaüzenettel. Ha a hitelesítés sikeres, a metaWeblog_Post () beolvassa az item_struct paramétert, és a getval () segítségével kicsomagolja a $item_struct tömbbe. Ebből elkészíti a Serendipity $entry paraméterét, melyet átad a serendipity_updertEntry () tagfüggvényének. Végül a hívó egy XML_RPC_Response objektumot kap az új bejegyzés azonosítójával. A MetaWeblog. editPost háttérkódja igencsak hasonló a MetaWeblog. newPost tagfüggvénynél látottakhoz: function metaWeblog_editPost($message) { $postid = $message->params[0 ]->getval(); $username = $message->params[1]->getval(); $password = $message->params[2]->getval(); if(!serendipity_authenticate_author($username, $password)) { return new XML_RPC_Response('', 4, 'Authentication F a i l e d ' ) ; }
$item_struct = $message->params[3]->getval(); $publish = $message->params[4]->getval(); $entry['title'] = $item_struct['title']; $entry['body'] = $item_struct['description1] ; $entry['author'] = $username; $ e n t r y[ ' i d ' ] = $postid; $entry['isdraft'] = ($publish == 0)?'true' : ' falsé'; $id = serendipity_updertEntry($entry); return new XML_RPC_Response( new XML_RPC_Value($id?true: falsé, 'boolean')); }
Itt is ugyanazt a hitelesítést végezzük el, elkészítjük a $entry tömböt, és elküldjük a naplónak. Ha a serendipity_updertEntry a $id értékkel tér vissza, működése sikeres volt, így a válaszban a true értéket adjuk vissza — ha nem volt sikeres, a válaszunk falsé. Utolsóként a MetaWeblog.getPost megvalósítását kell elkészítenünk. Ez a serendipity_f etchEntry () segítségével hozzájut a bejegyzés adataihoz, és ebből egy, az item_struct adatokat tartalmazó XML választ készít.
433
434
PHP fejlesztés felsőfokon
Lássuk, hogy fest a kód: function metaWeblog_getPost($message) { $postid = $message->params[0]->getval(); $username = $message->params[1]->getval(); $password = $message->params[2]->getval(); if ( !serendipíty_authenticate_author($username, $password)) { return new XML_RPC_Response('', 4, 'Authentication Failed'); } $entry = serendipity_fetchEntry('id', $postid); $tmp = array( 'pubDate' => new XML_RPC_Value( XML_RPC_iso8601_encode($entry['timestamp']), 'dateTime.iso8601'), 'postid' => new XML_RPC_Value($postid, 'string'), 'author' => new XML_RPC_Value($entry['author'] , 'string'), 'description' => new XML_RPC_Value($entry['body'] , 'string'), 'title' => new XML_RPC_Value($entry['title'],'string') , 'link' => new XML_RPC_Value(serendipity_url($postid) , 'string') );
$entry = new XML_RPC_Value($tmp, ' s t r u c t ' ) ; return new XML_RPC_Response($entry); }
Figyeljük meg, hogy a bejegyzés kiolvasása után az item adataiból álló tömböt készítünk. Az XML_RPC_iso8601 () elvégzi a Serendipity által használt Unix időbélyegző átalakítását az RSS item által megkövetelt ISO 8601 szabványúra. A kapott tömb ezután becsomagolva egy XML_RPC_Value struct-ba kerül. Ez a szabványos módszer arra, hogy XMLRPC struct típust készítsünk a PHP alaptípusokból. Az eddigiekben láttunk string, boolean, dateTime . iso8601 és struct azonosítókat, melyeket átadhattunk az XML_RPC_Value objektumnak. Érdemes felsorolnunk az összes lehetőséget:
16. fejezet • RPC: Együttműködés távoli szolgáltatásokkal
A struct és az array típusok bármilyen más típust (köztük további struct, illetve array elemeket) tartalmazhatnak. Ha nem adunk meg típust, a rendszer automatikusan a string mellett dönt. Jóllehet a PHP minden adata leírható a string, struct, illetve az array típusok valamelyikével, más típusok is támogatást kaptak, mivel a más nyelven írt alkalmazásoknak esetleg jobban meghatározott adattípusokra van szükségük. A függvények bejegyzéséhez egy kiosztási tömböt készítünk: $dispatches = array( metaWeblog.newPost' => array('function1 => 'metaWeblog_newPost') , 'metaWeblog.editPost' => array('function' => 'metaWeblog_editPost') , 'metaWeblog.getPost' => array('function1 => 'metaWeblog_getPost' ) ) ; $server = new XML_RPC_Server($dispatches, 1) ; Hurrá! Programunk e pillanattól kezdve megfelel a MetaWeblog API-nak!
Az XML-RPC szolgáltatások automatikus felderítése Hasznos lehet, ha egy felhasználó valamilyen módon adatokat kérhet a kiszolgálótól az XML-RPC szolgáltatásokra nézve. Erre az XML-RPC három tagfüggvényt is rendelkezésünkre bocsát: • system. listMethods - Visszaadja a kiszolgáló által megvalósított összes tagfüggvény nevét (minden visszahívható függvényt, ami szerepel a kiosztási térképen). • system.methodSignature - A tagfüggvény neve alapján megadja a lehetséges prototípusokat. • system.methodHelp - Fogadja a tagfüggvény nevét, és a leírásával tér vissza. Mivel a PHP dinamikus nyelv, és nem követeli meg a függvényeknek átadott paraméterek számának rögzítését, a system.methodSignature által visszaadott adatokat a felhasználónak kell pontosan meghatároznia. Az XML-RPC tagfüggvényeinek változó paraméterei lehetnek, így a visszatérési érték is egy tömb, amely a lehetséges prototípusokat tartalmazza. E prototípusok maguk is tömbök - első elemük a tagfüggvény visszatérési típusa, ezután pedig a paraméterek típusai következnek. E kiegészítő adatok tárolására a kiszolgálónak bővítenie kell kiosztási térképét - ezt láthatjuk a metaWeblog.newPost tagfüggvény példáján: $dispatches = array( 'metaWeblog.newPost' => array('function' => 'metaWeblog_newPost' , 'signature' => array( array($GLOBALS['XML_RPC_String'],
435
436
PHP fejlesztés felsőfokon
$GLOBALS['XML_RPC_String'], $GLOBALS['XML_RPC_String'] , $GLOBALS['XML_RPC_String'] , $GLOBALS['XML_RPC_Struct'], $GLOBALS['XML_RPC_String']
/* );
...
) ), 'docstring' => 'Takes blogid, username, password, item_struct 'publish_flag and returns the postid of the new entry'), */
'.
E három tagfüggvény használatával kialakíthatunk egy képet az XML-RPC kiszolgáló szolgáltatásairól. Lássunk egy programot, ami egy adott kiszolgálón megadja az összes XMLRPC tagfüggvény leírását és prototípusát:
16. fejezet • RPC: Együttműködés távoli szolgáltatásokkal
ibm
íme a válasz:
90.2 5 A SOAP esete jól példázza azt, hogy egy egyszerű elgondolás nem feltétlenül jár együtt egyszerű megvalósítással. A SOAP üzenet egy borítékból áll, ami egy fejlécet és egy üzenettörzset tartalmaz. Minden elem névtereken található, ami nagyszerű gondolat, de nehézkessé teszi az XML olvasását. A legkülső címke neve Envelope - ez tartalmazza magát a SOAP üzenetet. Ez az elem az xmlsoap névtérben található, amit teljes minősített nevéből () és e névtér meghatározásából is láthatunk: xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" Ez kapcsolatot teremt a soap és az alábbi névtér-URI között: http://schemas.xmlsoap.org/soap/envelope/
A SOAP és a Schema A SOAP belső működéseihez igénybe veszi a Schema segítségét, ami egy XML alapú nyelv adatszerkezetek meghatározására és ellenőrzésére. A közmegegyezés szerint egy elem tel-
jes névtere (például http: //schemas .xmlsoap.org/soap/envelope/) egy, a névteret leíró Schema dokumentum. Ez a meghatározás azonban nem kötelező érvényű - a névtérnek még csak URL-nek sem kell lennie —, de a teljesség kedvéért ezt alkalmazzák.
439
440
PHP fejlesztés felsőfokon
A névterek ugyanazt a szerepet töltik be az XML-ben, amit más programozási nyelvekben: Segítenek elkerülni az ütközéseket két megvalósítás neve között. Vegyük a legfelső szintű címkét (). Az Envelope tulajdonságnév eszerint a soap-env névtérben található. így hát, ha a FedEX valamilyen okból egy XML formátumot határoz meg, amely az Envelope tulajdonságot használja, ezt nyugodtan megteheti a teljes névvel, és semmi baj nem történik. A SOAP Envelope-on belül négy névtér létezik: • xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" -ASOAP boríték Schema meghatározása leírja az alapvető SOAP objektumokat - ez a névtér szerepel minden SOAP kérelemben. • xmlns:xsi="http://www.w3.org/21/XMLSchema-instance" -Azxsi:type elemtulajdonság gyakran használatos az elemek típusának megadásánál. • xmlns:xsd= "http://www.w3.org/21/XMLSchema" - A Schema megad néhány alapvető adattípust, melyeket adataink meghatározásánál és az ellenőrzésnél használhatunk. • xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding" Ez a típuskódolások meghatározása, melyek a szabványos SOAP kérelmekben megjelennek. A elem szintén egy névtérben található - ez esetben egy meglehetősen hosszú nevűben: http://www.themindelectric.com/wsdl/ net.xmethods.services.stockquote.StockQuote
Figyeljük meg a Schema használatát a tőzsdei azonosító típusmeghatározásában és elrendezésében: ibm A karakterlánc típusú. Hasonlóan, a válaszban is látható az árfolyam típusának meghatározása: 90 .2 5 Itt láthatjuk, hogy az eredmény egy lebegőpontos szám. Mindez hasznunkra lehet, hiszen a Schema érvényességi eszközeivel ellenőrizhetjük dokumentumunkat. így például, ha a válasz a következő alakban érkezik, érvénytelennek tekintik, mivel a f oo nem érvényes lebegőpontos szám: foo
16. fejezet • RPC: Együttműködés távoli szolgáltatásokkal
WSDL A SOAP méltó párja a WSDL (Web Services Description Language - webszolgáltatás-leíró nyelv). Ez egy XML alapú nyelv, ami kifejezetten arra szolgál, hogy a webszolgáltatások (leggyakrabban a SOAP) lehetőségeit és tagfüggvényeit leírjuk. íme egy WSDL fájl, amely a korábban már alkalmazott tőzsdei szolgáltatás leírását adja: net.xmethods.services.stockquote.StockQuote web service Látható, hogy a WSDL sem takarékoskodik a névterek használatával, szerkezete pedig kissé felrúgni látszik a logika szabályait. A kód első vizsgálatra érdemes része a címke, amely meghatározza a végrehajtható műveleteket, valamint a ki- és bevitt üzeneteket. Esetünkben a getQuote műveletet adja meg, amely a getQuoteRequestl kérelmet fogadja, és a getQuoteResponsel választ adja vissza. A getQuoteResponsel címkéi szerint tartalma egyetlen, f loat típusú Result elem. Hasonlóképpen, a getQuoteRequestl egyetlen string típusú symbol elemet tartalmaz. Következik a címke. A -hoz a type tulajdonságon keresztül kapcsolódik egy kötés, amely megegyezik a nevével. A kötések a protokoll és az adatátvitel tulajdonságait határozzák meg (például a SOAP üzenet törzsében elhelyezett adatok kódolását), de a címeket nem. Egy kötés egyetlen protokollhoz tartozhat esetünkben a HTTP-hez, kapcsolatukat az alábbi kód adja meg: Végezetül, a címke felsorol néhány kaput, és címeket határoz meg számukra. Mivel esetünkben csak egyetlen kaput használunk, a következő kóddal hivatkozunk rá és kötjük a h t t p : / / 6 6 . 2 8 . 98. 121: 9090/soap címhez:
16. fejezet • RPC: Együttműködés távoli szolgáltatásokkal
Érdemes megjegyeznünk, hogy semmi sem köti a SOAP-ot a HTTP protokollhoz, és választ sem kell feltétlenül visszaadnia. Ez egy rugalmas, általános célú protokoll, melynek a HTTP felett működő RPC csak egyik megvalósítása. A WSDL fájl arról tájékoztat, hogy milyen szolgáltatások érhetők el, és ezt hogyan és hogyan férhetünk hozzájuk. A kérelmet és a választ ezután a SOAP valósítja meg. Szerencsére a PEAR SOAP osztályai elvégzik a munka nagyobb részét. Egy SOAP kérelem kiadásához először egy új SOAP_Client ügyfélobjektumot kell készítenünk, és át kell adnunk a WSDL fájlt az elérni kívánt szolgáltatásokkal. A SOAP_Client ezután elkészíti az összes szükséges helyettes kódot a közvetlenül végrehajtott kérelmekhez, legalábbis olyan esetekben, amikor az adatok mind egyszerű Schema típusoknak feleltethetők meg. Az alábbiakban bemutatunk egy teljes ügyfélkérelmet az xmethods.net tőzsdei szolgáltatásához: require_once "SOAP/Client.php"; $url = "http://services.xmethods.net/soap/ urn:xmethods-delayed-quotes.wsdl"; $soapclient = new SOAP_Client($url, true); $price = $soapclient->getQuote("ibm")->deserializeBody () ; print "Current price of IBM is $price\n"; A SOAP_Client ezután átvállalja a helyettes objektum készítésének terhét, mellyel közvetlenül futtathatjuk a WSDL-ben megadott tagfüggvényeket. A getQuote () hívását követően a rendszer kicsomagolja az eredményt, és a PHP saját típusaiba írja a deserializeBody () segítségével. A futtatáskor az alábbi eredményt kapjuk: > php delayed-stockquote.php Current price of IBM is 9 0 . 2 5
A sysem.load átírása SOAP szolgáltatássá SOAP programozói tudásunkat nyomban próbára is tehetjük - kíséreljük meg SOAP alatt megvalósítani az XML-RPC system. load szolgáltatását. Először is, SOAP szolgáltatásunkat a SOAP_Service különleges célú változataként kell megvalósítanunk. Ehhez legalább az alábbi négy függvény megvalósítására szükség van: • public static f unction getSOAPServiceNamespace () {> - Vissza kell adnunk az általunk meghatározott szolgáltatás névterét. • public static function getSOAPServiceName () { } - Vissza kell adnunk szolgáltatásunk nevét. • public static function getSOAPServiceDescription () { } - Vissza kell adnunk szolgáltatásunk leírását egy karakterlánc alakjában. • public static function getWSDLURI () {} - Vissza kell adnunk a szolgáltatást leíró WSDL fájl URL-jét. Ezeken felül természetesen meg kell határoznunk saját tagfüggvényeinket is.
443
444
PHP fejlesztés felsőfokon
íme az új SOAP SystemLoad megvalósítás osztályának meghatározása: require_once 'SOAP/Server.php'; class ServerHandler_SystemLoad implements SOAP_Service { public static function getSOAPServiceNamespace() { return 'http://example.org/SystemLoad/'; } public static function getSOAPServiceName() { return 'SystemLoadService'; } public static function getSOAPServiceDescription() { return 'Return the one-minute load avergae.'; } public static function getWSDLURI() { return 'http://localhost/soap/tests/SystemLoad.wsdl'; } public function SystemLoad() { $uptime = "uptime";
if(preg_match("/load averages?: ( [ \ d . ] + ) / " , return array( 'Load' => $matches[1]);
$uptime,
$matches))
{
} } }
Az XML-RPC-től eltérően a SOAP_Service tagfüggvények paramétereiket hagyományos PHP változók alakjában kapják meg. Visszatéréskor mindössze a válaszüzenet patamétereinek tömbjét kell megadnunk. A névterek választásánál szabad kezet kapunk, de a rendszer ellenőrzi azokat a megadott WSDL fájl alapján, így egymással összhangban kell legyenek. Ha meghatároztunk egy szolgáltatást, az XML-RPC-hez hasonlóan be kell jegyeztetnünk. A következő példában készítünk egy új SOAP_Server objektumot, csatoljuk az új szolgáltatást, és utasítjuk a kiszolgálópéldányt a bejövő kérelmek kezelésére: $server = new SOAP_Server; $service = new ServerHandler_System_Load; $server->addService($service); $server->service('php://input'); Van tehát egy teljes értékű kiszolgálónk, de nincs még meg a WSDL fájl, melyből az ügyfelek megtudhatnák, hogyan férhetnek hozzá ehhez a kiszolgálóhoz. Ennek elkészítése nem nehéz feladat - csak sok időbe telik. Lássuk, milyen eredményre számíthatunk: -cmessage name= ' SystemLoadRequest' /> ■coperation name= ' SystemLoad'> System Load web service
445
446
PHP fejlesztés felsőfokon
Nos, itt nem sok újdonság bukkant fel. Figyeljük meg, hogy a névterek egybecsengenek azzal, amit a ServerHandler_SystemLoad-nál láthattunk, továbbá a SystemLoad prototípusa szerint egy Load nevű lebegőpontos számmal tér vissza. A szolgáltatáshoz tartozó ügyfél hasonlít a tőzsdés példában látotthoz: include("SOAP/Client.php"); $url = "http://localhost/soap/tests/SystemLoad.wsdl"; $soapclient = new SOAP_Client($url, true); $load = $soapclient->SystemLoad()->deserializeBody () ; print "One minute system load is $load\n";
Amazon webszolgáltatások és összetett típusok A SOAP egyik legnagyobb előnye az XML-RPC-vel szemben, hogy támogatja a felhasználók által meghatározott típusokat, melyeket a Schema segítségével készíthetnek el és ellenőrizhetnek. A PEAR SOAP-megvalósítása pedig képes automatikusan átírni e saját változókat a PHP típusaira. Mindennek bemutatására lássuk, miként kereshetünk meg egy szerzőt az Amazon. com webszolgáltatási API-jával. Az Amazon nagy hangsúlyt fektet webszolgáltatásainak megfelelő működésére, és elérhetővé teszi minden keresési lehetőségét a SOAP-on keresztül. Az Amazon API használatához fejlesztőként kell bejegyeztetnünk magunkat az Amazon webhelyén, awww.amazon.com/gp/aws/landing.html címen. Ha belepillantunk az Amazon WSDL fájljába, láthatjuk, hogy a szerző keresésének művelete az alábbi blokkban található (http: //soap.amazon. com/schemas2/AmazonWebServices. wsdl): A kimeneti és bemeneti üzenetek típusát itt az alábbiak szerint határozzák meg: és
16. fejezet • RPC: Együttműködés távoli szolgáltatásokkal
Mindkettő saját, a Schema-ban meghatározott típus. íme az AuthorRequest típusos meghatározása:
Ahhoz, hogy ezt a típust PHP-ben is megjeleníthessük, készítenünk kell egy erre a célra szolgáló osztályt, amely a SchemaTypelnf o felületet is megvalósítja. Ehhez két műveletet kell megírnunk: • public static function getTypeName () { } - Visszaadja a típus nevét. • public static function getTypeNamespace () { } - Visszaadja a típus névterét. Esetünkben az osztály egyszerűen a tulajdonságok tárolójaként viselkedik. Mivel ezek a Schema alaptípusai, nincs szükség további erőfeszítésre. Lássuk tehát az AuthorRequest burkolóosztályát: class AuthorRequest implements SchemaTypelnfo { public $author; public $page; public $mode; public $tag; public $type; public $devtag; public $sort; public $variations; public $locale; public static { return public static { return }
function getTypeName() 'AuthorRequest';} function getTypeNamespace() 'http://soap.amazon.com';}
447
448
PHP fejlesztés felsőfokon
A szerző szerinti keresés megvalósításához először készítenünk kell egy SOAP_Client helyettes objektumot az Amazon WSDL fájlból: require_once 'SOAP/Client.php'; $url = 'http://soap.amazon.com/schemas2/AmazonWebServices.wsdl'; $client = new SOAP_Client($url, true); Ezután hozzunk létre egy AuthorRequest objektumot, és töltsük fel a keresés adataival: $authreq = new AuthorRequest; $authreq->author = 'schlossnagle'; $authreq->mode = 'b o ok s '; $authreq->type = 'l i t e 1 ; $authreq->devtag = 'DEVTAG';
Az eredményt a ProductInfo típusban kapjuk vissza, ami túlzottan összetett ahhoz, hogy a felépítésével itt foglalkozzunk. Mindazonáltal az alábbi rövid kóddal gyorsan utánajárhatunk, mely könyvek szerzőit hívták Schlossnagle-nek: $result = $client->AuthorSearchRequest($authreq)->deserializeBody(); Futtatása után az alábbi eredményt kapjuk: foreach ($result->Details as $detail) { print "Title: $detail->ProductName, ASIN:
$detail->Asin\n";
}
Helyettes kód készítése Nem nehéz feladat olyan kódot írni, ami dinamikusan elkészíti a helyettes (proxy) objektumokat a WSDL-ből, de ez a folyamat meglehetősen sok értelmezési munkával jár, amit jobb elkerülni, különösen, ha egy webszolgáltatást gyakran hívnak. A SOAP WSDLkezelője képes elkészíteni a PHP kódot, így közvetlenül is lebonyolíthatjuk a hívásokat, anélkül, hogy újra és újra át kellene vizsgálni a WSDL fájlt. A helyettes kód elkészítésének érdekében töltsük be az URL-t az MSDLManager: : get () tagfüggvénnyel, és hívjuk meg a generateProxyCode () függvényt. Mindezt most a SystemLoad WSDL fájl példáján mutatjuk be: Title: Advanced PHP Programming, ASIN:
0672325616
16. fejezet • RPC: Együttműködés távoli szolgáltatásokkal
A futtatás után az alábbi kódhoz jutunk: require_once 'SOAP/WSDL.php'; $url = "http://localhost/soap/tests/SystemLoad.wsdl"; $result = WSDLManager::get($url) ; print $result->generateProxyCode(); Mostantól a WSDL fájl értelmezése helyett közvetlenül ezt az osztályt hívhatjuk: class WebService_SystemLoadService_SystemLoadPort extends SOAP_Client {
public function _____ construct() {
parent: :__ construct("http://localhost/soap/tests/ SystemLoad.php" , 0); } function SystemLoad() { return $this->call("SystemLoad", $v = array(), array('namespace'=>'http://example.org/SystemLoad/', 1soapaction'=>'http://example.org/SystemLoad/', 'style'=>'rpc', 'use'=>'encoded' )) ; } }
A SOAP és az XML-RPC összehasonlítása Melyik RPC protokollt alkalmazzuk - a SOAP vagy az XML-RPC mellett tegyük le voksunkat? Nos, sok esetben a körülmények nem hagynak túlzottan nagy mozgásteret. Ha olyan szolgáltatást valósítunk meg, ami meglevő ügyfelekkel vagy kiszolgálókkal érintkezik, elvesztjük a döntés lehetőségét. így a SOAP felület használata webnaplónkban érdekes kísérlet lehet, de valószínűleg nem képes együttműködni más, már eleve meglevő eszközökkel. Ha az Amazon vagy a Google API-jaival szeretnénk együttműködni, ismét csak egyértelmű a válasz: a SOAP-ot kell használnunk. Ha azonban új szolgáltatást telepítünk, és szabad kezet kapunk a választásban, az alábbiakat érdemes megfontolnunk: • A megvalósítás szemszögéből nézve az XML-RPC sokkal kevesebb kezdeti erőfeszítést igényel, mint a SOAP. • Az XML-RPC kisebb dokumentumokat készít, melyek értelmezése kevésbé költséges, mint a SOAP-pal készítetteké.
449
450
PHP fejlesztés felsőfokon
• A SOAP lehetővé teszi a Schema segítségével meghatározott saját típusok használatát. Ez módot ad az adatok érvényességének hatékonyabb ellenőrzésére, valamint az automatikus típusátalakításra az XML és a PHP között. Az XML-RPC-ben mindenféle összetettebb adatcsomagolást kézzel kell elvégeznünk. • A WSDL nagyszerű eszköz. A SOAP automatikus felderítési és helyetteskód-készítési képességei jobbak az XML-RPC-ben elérhetőknél. • A SOAP jelentős támogatást kap az IBM-től, a Microsofttól, valamint számos, a sikerében érdekelt internetes vállalattól. Mindez azt jelenti, hogy e cégek a múltban és a jövőben is jelentős anyagi erőforrásokat és időt szentelnek arra, hogy javítsák a SOAP együttműködési készségét és a SOAP-hoz kötődő segédeszközöket fejlesszenek. • A SOAP általános, bővíthető eszköz, míg az XML-RPC célirányos protokoll, viszonylag merev meghatározással. Az XML-RPC-t vonzóan egyszerű megoldásnak tartom olyankor, amikor a megvalósítandó RPC mindkét oldala felett befolyásom van. Ilyen esetekben a helyes automatikus felderítés és a helyettes kód készítésének hiánya nem okoz fejfájást. Ha azonban olyan szolgáltatást telepítek, melyet mások is támogatnak, mindenképpen a SOAP választását tartom jobbnak széleskörű támogatottsága és hatékony segédeszközei miatt.
További olvasmányok A távoli szolgáltatásokkal való együttműködés témaköre igen széles, sokkal szélesebb annál, mint amennyit e fejezet átfogni képes. A SOAP különösen érdekes, fejlődő szabvány; maga is megérdemelne egy külön könyvet. A következőkben néhány hasznos forrásmunkát mutatunk be, témakörök szerint csoportosítva.
SOAP A SOAP leírása megtalálható a http: //www.w3 .org/TR/SOAP/ címen. A http: //www. soapware.org/bdg nagyszerű bevezetést ad a SOAP használatába. Shane Caraveo webszolgáltatásokról adott előadásainak anyaga a http: //talks .php.net címen segít megérteni, mi szükséges a SOAP sikeres használatához a PHP-ben. Jó tudnunk, hogy Shane a PHP 5 SOAP-megvalósításának vezető fejlesztője.
XML-RPC Az XML-RPC leírása megtalálható a http: //www.xmlrpc .com/spec címen. Dave Winer, az XML-RPC megalkotója egy kellemes bevezető írását a következő címen lelhetjük meg: http: //davenet. scripting.com/19 9 8/07/14/xmlRpcForNewbies.
16. fejezet • RPC: Együttműködés távoli szolgáltatásokkal
Webnaplózás A Blogger API leírását a http: //www.blogger. com/developers/api/l_docs címen találhatjuk meg. A MetaWeblog API leírása a http: / /www.xmlrpc . com/metaWeblogApi címen található. A MovableType bővítményeket kínál mind a MetaWeblog, mind a Blogger API-hez. Leírásuk a http://www.movabletype.org/docs/mtmanual_programmatic.html címen megtalálható. Az RSS egy nyílt XML formátum tartalom-közzétételhez. Leírását a http: //blogs . law.harvard. edu/tech/rss címen lelhetjük meg. Az XML-RPC példáinkban szereplő Serendipity webnaplózó rendszer a http: //www. s9y.org címen érhető el. Nyilvánosan elérhető webszolgáltatások A http: / /xmethods . net kifejezetten a webszolgáltatások fejlesztéséhez (elsősorban a SOAP és a WSDL alkalmazásokhoz) nyújt segítséget. Itt ingyenesen elérhető webszolgáltatások garmadáját találhatjuk, a szerkesztők pedig az együttműködési képességek próbára tételére hívnak fel. Az Amazon rendelkezik egy szabad SOAP felülettel. A részletekről a http: //www. amazon.com/gp/aws/landing.html címen érdeklődhetünk. A Google is rendelkezik szabad SOAP keresőfelülettel. Erről a http: / /www. google. com/apis címen olvashatunk.
451
Teljesítmény
Teljesítménymérés: teljes alkalmazások tesztelése Az alkalmazások teljesítményének mérése nehéz feladat. Szükség van egy profilkészítőre, különböző beállításcsoportokkal kell futtatásokat végeznünk, és gyakran a kimerítő elemzés is elengedhetetlen. Nagy, illetve bonyolult programok esetében a profilkészítés-hangolás ciklusa akár napokig is elhúzódhat. A profilkészítés olyan, mint a nyomozás - felderíteni a gyenge pontokat és elemezni a könyvtárak tulajdonságát nagyszerű játék. De hol kezdjük a munkát, ha 1000 PHP oldal áll előttünk? Hogyan vizsgáljuk meg hatékonyan alkalmazásunk „egészségi állapotát"? Más részről, ott van a terhelésvizsgálat feladata is. Tegyük fel, hogy projektünk, melyen hat hónapig dolgoztunk, csaknem elkészült, főnökünk pedig azt követeli, hogy a rendszer képes legyen megfelelni 1000 felhasználó terhelésének. Miként biztosíthatjuk, hogy valóban elegendő lesz a kapacitás? Hogyan fedezzük fel a szűk keresztmetszeteket, mielőtt élesben kellene alkalmaznunk művünket? Ezekre a kihívásokra sajnos túl sok fejlesztő válaszol a próba-szerencse módszerének alkalmazásával. Persze esetenként az ilyen módszerek is lehetnek eredményesek - sok fejlesztő cég rendelkezik olyan szakemberrel, aki más versenytársaknál 10-szer vagy akár 100-szor nagyobb hatékonysággal deríti fel a hibákat, de még így is csak a gondok egytizedére akadnak rá. Ismerem ezt a világot - magam is ilyen fejlesztő voltam. Értettem az alkalmazás működését, és nem is voltam buta fickó. Ha adtak egy nap gondolkodási időt, és kedvemre próbálgathattam, számos olyan feladatot megoldottam, ami más fejlesztőkön kifogott. Mindez meglehetős tiszteletet vívott ki számomra a kollégák között - legalábbis sokan csodálták ezt a majdhogynem misztikus képességemet a gondok forrásának megtalálására.
456
PHP fejlesztés felsőfokon
Történetem célja azonban nem az, hogy meggyőzzem az Olvasót arról, milyen nagyszerű képességekkel rendelkezem - valójában a cél éppen ennek ellenkezője. Módszereim ugyanis meglehetősen esetlegesek és kevéssé célzottak voltak. Még ha okosan gondolkodtam is, a megfelelő teljesítménymérési eljárások sokkal gyorsabban rávilágítottak volna a gondok gyökereire - ráadásul mindezt valószínűleg nálam jóval hatékonyabban tették volna. Az alkalmazás teljesítményének mérése nagyléptékű vizsgálatot jelent - az alábbi lehetőségekkel: • A szolgáltatások kapacitástervének összeállítása. • A profílkészítést és teljesítményhangolást igénylő oldalak felderítése. • Az alkalmazás „egészségének" megértése. Az alkalmazás teljesítményvizsgálata nem mutat rá az egyes javítandó kódrészletekre. Ha meghatároztuk a komolyabb vizsgálatot igénylő oldalak listáját, ezek elemzését már a 19. fejezet módszereivel végezhetjük.
A szűk keresztmetszetek passzív azonosítása A nagyléptékű rendszerek szűk keresztmetszetének azonosítására a legkézenfekvőbb módszere az, ha olyan adatokat vizsgálunk meg, melyeket már begyűjtöttünk, vagy igen könnyen elérhetők. Az ilyen vizsgálatok legegyszerűbbike az oldalak letöltési idejének áttekintése az Apache elérési naplóiban. A általános naplóformátum nem tartalmaz „eltelt idő" mezőt, de maga a naplózó rendszer támogatja a használatát. Ahhoz, hogy az oldal szolgáltatásához szükséges időt is feltüntessük (másodpercben), a LogFormat sort a %T beállítással kell kiegészítenünk: LogFormat "%h %1 %u %t \ " % r \ " %>s %b \"%{Referer)i\" \"%{User-Agent}i\" %T" combinedplus Ezután be kell állítanunk az új naplózási eljárást az új formátummal: CustomLog /var/apache-logs/default/access_log combinedplus Nos, ezzel el is készültünk. Új elérési naplónk (access log) így fest: 66.80.117.2 - - [23/Mar/2003:17:56:44 -0500] "GET /~george/index2.php HTTP/1.1" 200 14039 "-" "-" 1 66.80.117.2 - - [23/Mar/2003:17:56:44 -0500] "GET /-george/blog/ HTTP/1.1" 200 14039 "-" "-" 3 66.80.117.2 - - [23/Mar/2003:17:56:44 -0500]
17. fejezet • Teljesítménymérés: teljes alkalmazások tesztelése
"GET /-george/examples/ HTTP/1.1" 2 0 0 14039 " -" " - " 0 6 6 . 8 0 . 1 1 7 . 2 - - [ 2 3 / M a r / 2 0 0 3 : 1 7 : 5 6 : 4 4 -0 5 0 0 ] "GET /~george/index2.php HTTP/1.1" 2 0 0 14039 "-" "-" 1 6 6 . 8 0 . 1 1 7 . 2 - - [ 2 3 / M a r / 2 0 0 3 : 1 7 : 5 6 : 4 4 -0 5 00 ] "GET /-george/ HTTP/1.1" 2 0 0 14039 "-" "-" 1 66 . 80. 117 . 2 - - [ 2 3 / M a r / 2 0 0 3:1 7:5 6 :4 4 -0500] "GET /-george/blog/ HTTP/1.1" 2 0 0 14039 "-" "-" 2 66 . 80 . 11 7. 2 - - [2 3 / M a r / 20 0 3:1 7 :5 6 :4 4 -0500] "GET /-george/blog/ HTTP/1.1" 2 0 0 14039 "-" " -" 1 6 6 . 8 0 . 1 1 7 . 2 - - [ 2 3 / M a r / 2 0 0 3 : 1 7 :5 6 : 4 7 -0 5 0 0 ] "GET /~george/php/ HTTP/1.1" 2 0 0 1149 "-" " -" 0 Az oldal készítéséhez szükséges idő a bejegyzés utolsó mezőjében található meg. E bejegyzések közvetlen átnézése nyilván csak olyan esetekben vezet eredményre, ha egy oldallal igen súlyos gondok vannak - egyébként azonban nem sok következtetést vonhatunk le a kapott adatokból a minta kis mérete miatt. Ez ellen persze könnyen tehetünk, csak futtassuk a naplózót néhány órán keresztül, és elemezzük az eredményt ezután. Nagyobb statisztikai mintánál a számok többet mondanak. Ha elegendő adat gyűlt össze, elemzésüket az alábbi programmal is elvégezhetjük: #!/usr/local/bin/php ################## # parse_logs.php # ##################