You are on page 1of 6

Documents >

PHPasnode.jsAlternativePart1:HelloWorld
Contents 1Background 2Problem 3Goal 4Analysis 4.1SolutionCandidates 4.2Requirements 5Implementations 5.1node.js 5.2Ratchet 5.3Wrench 6Conclusion

Background
node.jsisthenewcoolkidinwebprogrammingworld.Itsupportsasynchronous,eventdrivenwebservernatively. It'sveryeasytocommunicatewithclients(browser)throughWebSockets(usingSocket.IO)whichmakes implementingmanyrealtimefeaturespossible. AlittlebitaboutWebSocket.

Problem
IsthereanyPHPalternativetoaccomplishequaltask?Let'ssayforcountingonlineuserinrealtime.

Goal
ToimplementrealtimeusercountfunctionalityusingPHPasserverside. Usethisstackoverflowanswerasexampleofwhatwewanttoachieve.

Analysis
SolutionCandidates
Istartedwithadiscussioninredditandotherdiscussionsinstackoverflowandfoundthese: ReactEventdriven,nonblockingI/OwithPHP. Pros usedbyRatchet Cons documentationislacking latestversion0.3.3 lowlevel,fordirectI/O,can'tcommunicatewithwebsocketdirectly,betteruseRatchetfor websocket Phastlightasynchronous,eventdrivencommandlinetoolandwebserverwritteninPHP5.3+inspiredby Node.js Pros Cons latestversion0.1.xdev "Atthistime,Phastlightisonitsveryearlydevelopmentphrases"

justlikeReact,can'tcommunicatetowebsocketdirectly RatchetPHP5.3libraryforasynchronouslyservingWebSockets. Pros documentationquitegood Cons latestversion0.2.5 WrenchSimpleWebSocketClient/ServerforPHP(formerlyPHPWebSocket) Pros latestversion2.0.1 Cons documentationislacking,thereisanAPIdocbutnouserfriendlyguide UsePHPpersistentsocket Pros nothirdpartylibrarydependency Cons thearticleonlyexplainstheconcept,wehavetoimplementitmanuallyanddealwithanybug thatmighthappen veryhardtodo

Requirements
Provideawebpagewithtext"Thereare:Xconnectedusers". Everytimeanewwindowvisitsthatpage,increaseX. Everytimeawindowcloses,decreaseX. Allbrowserwindowsmustseethesamenumber. Socket.IOisanode.jslibrarythatwrapsthecommunicationbetweenclientandserverasiftheyare communicatingthroughWebSocket.Ifthebrowserdoesn'tsupportWebSocket,itwillfallbacktomoretraditional approachlikeAJAXorFlash. Currently,thereisnoPHPequivalentforSocket.IO.EvenifwecancommunicatewithSocket.IOclient (JavaScriptbrowser)usingtheaboveWebSocketserverlibraries,ifitfallsbacktoAJAXforexample,allthe librariesabovedon'thandlethat.WeneedtohandlethatmanuallybyimitatinghowSocket.IOcommunicate,orwe couldcreateourownSocket.IOreplacement. Ingeneral,becauseWebSocketisanewtechnologyandonlymajorbrowserssupportit,it'sagoodideato prepareafallbackmethodforoldbrowsers.Innode.js,wecaneasilyaccomplishthisusingSocket.IO. InPHP,onepossiblesolutionwouldbetouseGracefulWebSocketorsimilarlibraryinclientandimplementtwo versionofPHPservers(WebSocketonlyandHTTPonly).Ofcourse,thenwewillneedtosynchronizethesetwo servers. InthisdocumentIwilltrytohandleonlycommunicationthroughWebSocketbetweentheclientandserver.

Implementations
Allsourcecoderelatedtohisdocumentcanbeaccessedinmygithubrepository.

node.js
Thisishowwedoitwithnode.jsandSocket.IO: Serversideapp.js v a ra p p=r e q u i r e ( ' h t t p ' ) . c r e a t e S e r v e r ( h a n d l e r ) ,i o=r e q u i r e ( ' s o c k e t . i o ' ) . l i s t e n ( a p p ) ,f s=r e q u i r e ( ' f s ' ) a p p . l i s t e n ( 8 0 8 8 ) ; f u n c t i o nh a n d l e r( r e q ,r e s ){ v a rf i l e p a t h=_ _ d i r n a m e+' / i n d e x . h t m l ' ; f s . r e a d F i l e ( f i l e p a t h ,f u n c t i o n( e r r ,d a t a ){ i f( e r r ){

r e s . w r i t e H e a d ( 5 0 0 ) ; r e t u r nr e s . e n d ( ' E r r o rl o a d i n gf i l e ' ) ;

r e s . w r i t e H e a d ( 2 0 0 ) ; r e s . e n d ( d a t a ) ; } ) ;

v a rc o u n t=0 ; f u n c t i o nu p d a t e _ c o u n t ( s o c k e t ){ s o c k e t . e m i t ( ' m e s s a g e ' ,{ c o u n t :c o u n t } ) ; s o c k e t . b r o a d c a s t . e m i t ( ' m e s s a g e ' ,{ c o u n t :c o u n t } ) ; } i o . s o c k e t s . o n ( ' c o n n e c t i o n ' ,f u n c t i o n( s o c k e t ){ c o u n t + + ; u p d a t e _ c o u n t ( s o c k e t ) ; s o c k e t . o n ( ' d i s c o n n e c t ' ,f u n c t i o n ( ){ c o u n t ; u p d a t e _ c o u n t ( s o c k e t ) ; } ) ; } ) ; Clientsideindex.html < h t m l > < h e a d > < t i t l e > N o d e J Sr e a lt i m eu s e rc o u n t < / t i t l e > < / h e a d > < b o d y > < d i v > T h e r ea r e : < s p a ni d = " c o u n t " > 0 < / s p a n > c o n n e c t e du s e r s < / d i v > < s c r i p ts r c = " / s o c k e t . i o / s o c k e t . i o . j s " > < / s c r i p t > < s c r i p t > v a rd o m _ c o u n t=d o c u m e n t . g e t E l e m e n t B y I d ( ' c o u n t ' ) ; v a rs o c k e t=i o . c o n n e c t ( ' h t t p : / / l o c a l h o s t : 8 0 8 8 ' ) ; s o c k e t . o n ( ' m e s s a g e ' ,f u n c t i o n( d a t a ){ d o m _ c o u n t . i n n e r H T M L=d a t a . c o u n t ; } ) ; < / s c r i p t > < / b o d y > < / h t m l >

Ratchet
Mainprogramsrc/MyApp/Count.php < ? p h p n a m e s p a c eM y A p p ; u s eR a t c h e t \ M e s s a g e C o m p o n e n t I n t e r f a c e ; u s eR a t c h e t \ C o n n e c t i o n I n t e r f a c e ; c l a s sC o u n ti m p l e m e n t sM e s s a g e C o m p o n e n t I n t e r f a c e{ p r o t e c t e d$ c l i e n t s ; p u b l i cf u n c t i o n_ _ c o n s t r u c t ( ){ $ t h i s > c l i e n t s=n e w\ S p l O b j e c t S t o r a g e ; } p u b l i cf u n c t i o no n O p e n ( C o n n e c t i o n I n t e r f a c e$ c o n n ){ / /S t o r et h en e wc o n n e c t i o nt os e n dm e s s a g e st ol a t e r $ t h i s > c l i e n t s > a t t a c h ( $ c o n n ) ; $ t h i s > b r o a d c a s t C o u n t ( ) ; e c h o" N e wc o n n e c t i o n !( { $ c o n n > r e s o u r c e I d } ) \ n " ; } p r o t e c t e df u n c t i o nb r o a d c a s t C o u n t ( ){ $ c o u n t=c o u n t ( $ t h i s > c l i e n t s ) ; f o r e a c h( $ t h i s > c l i e n t sa s$ c l i e n t ){

$ c l i e n t > s e n d ( $ c o u n t ) ;

p u b l i cf u n c t i o no n M e s s a g e ( C o n n e c t i o n I n t e r f a c e$ f r o m ,$ m s g ){ } p u b l i cf u n c t i o no n C l o s e ( C o n n e c t i o n I n t e r f a c e$ c o n n ){ / /T h ec o n n e c t i o ni sc l o s e d ,r e m o v ei t ,a sw ec a nn ol o n g e rs e n di t m e s s a g e s $ t h i s > c l i e n t s > d e t a c h ( $ c o n n ) ; $ t h i s > b r o a d c a s t C o u n t ( ) ; e c h o" C o n n e c t i o n{ $ c o n n > r e s o u r c e I d }h a sd i s c o n n e c t e d \ n " ; } p u b l i cf u n c t i o no n E r r o r ( C o n n e c t i o n I n t e r f a c e$ c o n n ,\ E x c e p t i o n$ e ){ e c h o" A ne r r o rh a so c c u r r e d :{ $ e > g e t M e s s a g e ( ) } \ n " ; } $ c o n n > c l o s e ( ) ;

Theserverbin/countserver.php < ? p h p u s eR a t c h e t \ S e r v e r \ I o S e r v e r ; u s eR a t c h e t \ W e b S o c k e t \ W s S e r v e r ; u s eM y A p p \ C o u n t ; r e q u i r ed i r n a m e ( _ _ D I R _ _ ).' / v e n d o r / a u t o l o a d . p h p ' ; $ s e r v e r=I o S e r v e r : : f a c t o r y ( n e wW s S e r v e r ( n e wC o u n t ( ) ,8 0 8 8 ) ; $ s e r v e r > r u n ( ) ; Clientsidecount.html < h t m l > < h e a d > < t i t l e > R a t c h e tr e a lt i m eu s e rc o u n t < / t i t l e > < / h e a d > < b o d y > < d i v > T h e r ea r e : < s p a ni d = " c o u n t " > 0 < / s p a n > c o n n e c t e du s e r s < / d i v > < s c r i p t > v a rd o m _ c o u n t=d o c u m e n t . g e t E l e m e n t B y I d ( ' c o u n t ' ) ; v a rc o n n=n e wW e b S o c k e t ( ' w s : / / l o c a l h o s t : 8 0 8 8 ' ) ; c o n n . o n o p e n=f u n c t i o n ( e ){ } ;

c o n n . o n m e s s a g e=f u n c t i o n ( e ){ d o m _ c o u n t . i n n e r H T M L=e . d a t a ; } ; < / s c r i p t > < / b o d y > < / h t m l > Notethatwedon'thandleanyfallback,ifthebrowserdoesn'tsupportWebSocketthenthiscodewon'twork.

Wrench
Mainprogramsrc/MyApp/CountApplication.php < ? p h p

n a m e s p a c eM y A p p ; u s eW r e n c h \ A p p l i c a t i o n \ A p p l i c a t i o n ; / * * *E x a m p l ea p p l i c a t i o nf o rW r e n c h :d i s p l a yu s e rc o u n t * / c l a s sC o u n t A p p l i c a t i o ne x t e n d sA p p l i c a t i o n { p r o t e c t e d$ c l i e n t s=a r r a y ( ) ; / * * *@ s e eW r e n c h \ A p p l i c a t i o n . A p p l i c a t i o n : : o n C o n n e c t ( ) * / p u b l i cf u n c t i o no n C o n n e c t ( $ c l i e n t ) { $ i d=$ c l i e n t > g e t I d ( ) ; / /e c h o" c o n n e c t e d :$ i d \ n " ; $ t h i s > c l i e n t s [ $ i d ]=$ c l i e n t ; $ t h i s > b r o a d c a s t C o u n t ( ) ; } p u b l i cf u n c t i o no n D i s c o n n e c t ( $ c l i e n t ) { $ i d=$ c l i e n t > g e t I d ( ) ; / /e c h o" d i s c o n n e c t e d :$ i d \ n " ; u n s e t ( $ t h i s > c l i e n t s [ $ i d ] ) ; $ t h i s > b r o a d c a s t C o u n t ( ) ; } p r o t e c t e df u n c t i o nb r o a d c a s t C o u n t ( ) { $ c o u n t=c o u n t ( $ t h i s > c l i e n t s ) ; f o r e a c h( $ t h i s > c l i e n t sa s$ c l i e n t ){ $ c l i e n t > s e n d ( $ c o u n t ) ; } } p u b l i cf u n c t i o no n D a t a ( $ d a t a ,$ c l i e n t ) { / /n o tu s e d }

Theserversrc/server.php < ? p h p r e q u i r ed i r n a m e ( _ _ D I R _ _ ).' / v e n d o r / a u t o l o a d . p h p ' ; u s eW r e n c h \ B a s i c S e r v e r ; $ s e r v e r=n e wB a s i c S e r v e r ( ' w s : / / l o c a l h o s t : 8 0 8 8 ' ,a r r a y ( ' c h e c k _ o r i g i n '= >f a l s e ) ) ; / /R e g i s t e ry o u ra p p l i c a t i o n s $ s e r v e r > r e g i s t e r A p p l i c a t i o n ( ' c o u n t ' ,n e wM y A p p \ C o u n t A p p l i c a t i o n ( ) ) ; $ s e r v e r > r u n ( ) ; Clientsideindex.html < h t m l > < h e a d > < t i t l e > W r e n c hr e a lt i m eu s e rc o u n t < / t i t l e > < / h e a d > < b o d y > < d i v > T h e r ea r e : < s p a ni d = " c o u n t " > 0 < / s p a n > c o n n e c t e du s e r s < / d i v > < s c r i p t > v a rd o m _ c o u n t=d o c u m e n t . g e t E l e m e n t B y I d ( ' c o u n t ' ) ; v a rc o n n=n e wW e b S o c k e t ( ' w s : / / l o c a l h o s t : 8 0 8 8 / c o u n t ' ) ; c o n n . o n o p e n=f u n c t i o n ( e ){ } ;

c o n n . o n m e s s a g e=f u n c t i o n ( e ){ d o m _ c o u n t . i n n e r H T M L=e . d a t a ; } ; < / s c r i p t > < / b o d y > < / h t m l > JustlikeRatchet,thisimplementationdoesn'thandleanyfallback.

Conclusion
Amongthosecandidates,onlyRatchetandWrenchthatcouldaccomplishthetask.Ratchethasbetter documentationwhileWrenchhasbetterdevelopment.Evenwiththat,theystillcouldn'treplacenode.js+Socket.IO completelybecausetheydon'thavethefallbackfeature.So,keepthatinmindifyouinsistonusingPHP.

You might also like