nusoap.php 353 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574
  1. <?php
  2. /*
  3. $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  4. NuSOAP - Web Services Toolkit for PHP
  5. Copyright (c) 2002 NuSphere Corporation
  6. This library is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU Lesser General Public
  8. License as published by the Free Software Foundation; either
  9. version 2.1 of the License, or (at your option) any later version.
  10. This library is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. Lesser General Public License for more details.
  14. You should have received a copy of the GNU Lesser General Public
  15. License along with this library; if not, write to the Free Software
  16. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. The NuSOAP project home is:
  18. http://sourceforge.net/projects/nusoap/
  19. The primary support for NuSOAP is the Help forum on the project home page.
  20. If you have any questions or comments, please email:
  21. Dietrich Ayala
  22. dietrich@ganx4.com
  23. http://dietrich.ganx4.com/nusoap
  24. NuSphere Corporation
  25. http://www.nusphere.com
  26. */
  27. /*
  28. * Some of the standards implmented in whole or part by NuSOAP:
  29. *
  30. * SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
  31. * WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
  32. * SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
  33. * XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
  34. * Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
  35. * XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
  36. * RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
  37. * RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
  38. * RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
  39. */
  40. /* load classes
  41. // necessary classes
  42. require_once('class.soapclient.php');
  43. require_once('class.soap_val.php');
  44. require_once('class.soap_parser.php');
  45. require_once('class.soap_fault.php');
  46. // transport classes
  47. require_once('class.soap_transport_http.php');
  48. // optional add-on classes
  49. require_once('class.xmlschema.php');
  50. require_once('class.wsdl.php');
  51. // server class
  52. require_once('class.soap_server.php');*/
  53. // class variable emulation
  54. // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
  55. $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = 9;
  56. /**
  57. *
  58. * nusoap_base
  59. *
  60. * @author Dietrich Ayala <dietrich@ganx4.com>
  61. * @author Scott Nichol <snichol@users.sourceforge.net>
  62. * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  63. * @access public
  64. */
  65. class nusoap_base
  66. {
  67. /**
  68. * Identification for HTTP headers.
  69. *
  70. * @var string
  71. * @access private
  72. */
  73. var $title = 'NuSOAP';
  74. /**
  75. * Version for HTTP headers.
  76. *
  77. * @var string
  78. * @access private
  79. */
  80. var $version = '0.9.5';
  81. /**
  82. * CVS revision for HTTP headers.
  83. *
  84. * @var string
  85. * @access private
  86. */
  87. var $revision = '$Revision: 1.123 $';
  88. /**
  89. * Current error string (manipulated by getError/setError)
  90. *
  91. * @var string
  92. * @access private
  93. */
  94. var $error_str = '';
  95. /**
  96. * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
  97. *
  98. * @var string
  99. * @access private
  100. */
  101. var $debug_str = '';
  102. /**
  103. * toggles automatic encoding of special characters as entities
  104. * (should always be true, I think)
  105. *
  106. * @var boolean
  107. * @access private
  108. */
  109. var $charencoding = true;
  110. /**
  111. * the debug level for this instance
  112. *
  113. * @var integer
  114. * @access private
  115. */
  116. var $debugLevel;
  117. /**
  118. * set schema version
  119. *
  120. * @var string
  121. * @access public
  122. */
  123. var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
  124. /**
  125. * charset encoding for outgoing messages
  126. *
  127. * @var string
  128. * @access public
  129. */
  130. var $soap_defencoding = 'ISO-8859-1';
  131. //var $soap_defencoding = 'UTF-8';
  132. /**
  133. * namespaces in an array of prefix => uri
  134. *
  135. * this is "seeded" by a set of constants, but it may be altered by code
  136. *
  137. * @var array
  138. * @access public
  139. */
  140. var $namespaces = array(
  141. 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
  142. 'xsd' => 'http://www.w3.org/2001/XMLSchema',
  143. 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  144. 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
  145. );
  146. /**
  147. * namespaces used in the current context, e.g. during serialization
  148. *
  149. * @var array
  150. * @access private
  151. */
  152. var $usedNamespaces = array();
  153. /**
  154. * XML Schema types in an array of uri => (array of xml type => php type)
  155. * is this legacy yet?
  156. * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
  157. *
  158. * @var array
  159. * @access public
  160. */
  161. var $typemap = array(
  162. 'http://www.w3.org/2001/XMLSchema' => array(
  163. 'string' => 'string', 'boolean' => 'boolean', 'float' => 'double', 'double' => 'double', 'decimal' => 'double',
  164. 'duration' => '', 'dateTime' => 'string', 'time' => 'string', 'date' => 'string', 'gYearMonth' => '',
  165. 'gYear' => '', 'gMonthDay' => '', 'gDay' => '', 'gMonth' => '', 'hexBinary' => 'string', 'base64Binary' => 'string',
  166. // abstract "any" types
  167. 'anyType' => 'string', 'anySimpleType' => 'string',
  168. // derived datatypes
  169. 'normalizedString' => 'string', 'token' => 'string', 'language' => '', 'NMTOKEN' => '', 'NMTOKENS' => '', 'Name' => '', 'NCName' => '', 'ID' => '',
  170. 'IDREF' => '', 'IDREFS' => '', 'ENTITY' => '', 'ENTITIES' => '', 'integer' => 'integer', 'nonPositiveInteger' => 'integer',
  171. 'negativeInteger' => 'integer', 'long' => 'integer', 'int' => 'integer', 'short' => 'integer', 'byte' => 'integer', 'nonNegativeInteger' => 'integer',
  172. 'unsignedLong' => '', 'unsignedInt' => '', 'unsignedShort' => '', 'unsignedByte' => '', 'positiveInteger' => ''),
  173. 'http://www.w3.org/2000/10/XMLSchema' => array(
  174. 'i4' => '', 'int' => 'integer', 'boolean' => 'boolean', 'string' => 'string', 'double' => 'double',
  175. 'float' => 'double', 'dateTime' => 'string',
  176. 'timeInstant' => 'string', 'base64Binary' => 'string', 'base64' => 'string', 'ur-type' => 'array'),
  177. 'http://www.w3.org/1999/XMLSchema' => array(
  178. 'i4' => '', 'int' => 'integer', 'boolean' => 'boolean', 'string' => 'string', 'double' => 'double',
  179. 'float' => 'double', 'dateTime' => 'string',
  180. 'timeInstant' => 'string', 'base64Binary' => 'string', 'base64' => 'string', 'ur-type' => 'array'),
  181. 'http://soapinterop.org/xsd' => array('SOAPStruct' => 'struct'),
  182. 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64' => 'string', 'array' => 'array', 'Array' => 'array'),
  183. 'http://xml.apache.org/xml-soap' => array('Map')
  184. );
  185. /**
  186. * XML entities to convert
  187. *
  188. * @var array
  189. * @access public
  190. * @deprecated
  191. * @see expandEntities
  192. */
  193. var $xmlEntities = array('quot' => '"', 'amp' => '&',
  194. 'lt' => '<', 'gt' => '>', 'apos' => "'");
  195. /**
  196. * HTTP Content-type to be used for SOAP calls and responses
  197. *
  198. * @var string
  199. */
  200. var $contentType = "text/xml";
  201. /**
  202. * constructor
  203. *
  204. * @access public
  205. */
  206. function __construct()
  207. {
  208. $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
  209. }
  210. /**
  211. * gets the global debug level, which applies to future instances
  212. *
  213. * @return integer Debug level 0-9, where 0 turns off
  214. * @access public
  215. */
  216. function getGlobalDebugLevel()
  217. {
  218. return $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
  219. }
  220. /**
  221. * sets the global debug level, which applies to future instances
  222. *
  223. * @param int $level Debug level 0-9, where 0 turns off
  224. * @access public
  225. */
  226. function setGlobalDebugLevel($level)
  227. {
  228. $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = $level;
  229. }
  230. /**
  231. * gets the debug level for this instance
  232. *
  233. * @return int Debug level 0-9, where 0 turns off
  234. * @access public
  235. */
  236. function getDebugLevel()
  237. {
  238. return $this->debugLevel;
  239. }
  240. /**
  241. * sets the debug level for this instance
  242. *
  243. * @param int $level Debug level 0-9, where 0 turns off
  244. * @access public
  245. */
  246. function setDebugLevel($level)
  247. {
  248. $this->debugLevel = $level;
  249. }
  250. /**
  251. * adds debug data to the instance debug string with formatting
  252. *
  253. * @param string $string debug data
  254. * @access private
  255. */
  256. function debug($string)
  257. {
  258. if ($this->debugLevel > 0) {
  259. $this->appendDebug($this->getmicrotime() . ' ' . get_class($this) . ": $string\n");
  260. }
  261. }
  262. /**
  263. * adds debug data to the instance debug string without formatting
  264. *
  265. * @param string $string debug data
  266. * @access public
  267. */
  268. function appendDebug($string)
  269. {
  270. if ($this->debugLevel > 0) {
  271. // it would be nice to use a memory stream here to use
  272. // memory more efficiently
  273. $this->debug_str .= $string;
  274. }
  275. }
  276. /**
  277. * clears the current debug data for this instance
  278. *
  279. * @access public
  280. */
  281. function clearDebug()
  282. {
  283. // it would be nice to use a memory stream here to use
  284. // memory more efficiently
  285. $this->debug_str = '';
  286. }
  287. /**
  288. * gets the current debug data for this instance
  289. *
  290. * @return debug data
  291. * @access public
  292. */
  293. function &getDebug()
  294. {
  295. // it would be nice to use a memory stream here to use
  296. // memory more efficiently
  297. return $this->debug_str;
  298. }
  299. /**
  300. * gets the current debug data for this instance as an XML comment
  301. * this may change the contents of the debug data
  302. *
  303. * @return debug data as an XML comment
  304. * @access public
  305. */
  306. function &getDebugAsXMLComment()
  307. {
  308. // it would be nice to use a memory stream here to use
  309. // memory more efficiently
  310. while (strpos($this->debug_str, '--')) {
  311. $this->debug_str = str_replace('--', '- -', $this->debug_str);
  312. }
  313. $ret = "<!--\n" . $this->debug_str . "\n-->";
  314. return $ret;
  315. }
  316. /**
  317. * expands entities, e.g. changes '<' to '&lt;'.
  318. *
  319. * @param string $val The string in which to expand entities.
  320. * @access private
  321. */
  322. function expandEntities($val)
  323. {
  324. if ($this->charencoding) {
  325. $val = str_replace('&', '&amp;', $val);
  326. $val = str_replace("'", '&apos;', $val);
  327. $val = str_replace('"', '&quot;', $val);
  328. $val = str_replace('<', '&lt;', $val);
  329. $val = str_replace('>', '&gt;', $val);
  330. }
  331. return $val;
  332. }
  333. /**
  334. * returns error string if present
  335. *
  336. * @return mixed error string or false
  337. * @access public
  338. */
  339. function getError()
  340. {
  341. if ($this->error_str != '') {
  342. return $this->error_str;
  343. }
  344. return false;
  345. }
  346. /**
  347. * sets error string
  348. *
  349. * @return boolean $string error string
  350. * @access private
  351. */
  352. function setError($str)
  353. {
  354. $this->error_str = $str;
  355. }
  356. /**
  357. * detect if array is a simple array or a struct (associative array)
  358. *
  359. * @param mixed $val The PHP array
  360. * @return string (arraySimple|arrayStruct)
  361. * @access private
  362. */
  363. function isArraySimpleOrStruct($val)
  364. {
  365. $keyList = array_keys($val);
  366. foreach ($keyList as $keyListValue) {
  367. if (!is_int($keyListValue)) {
  368. return 'arrayStruct';
  369. }
  370. }
  371. return 'arraySimple';
  372. }
  373. /**
  374. * serializes PHP values in accordance w/ section 5. Type information is
  375. * not serialized if $use == 'literal'.
  376. *
  377. * @param mixed $val The value to serialize
  378. * @param string $name The name (local part) of the XML element
  379. * @param string $type The XML schema type (local part) for the element
  380. * @param string $name_ns The namespace for the name of the XML element
  381. * @param string $type_ns The namespace for the type of the element
  382. * @param array $attributes The attributes to serialize as name=>value pairs
  383. * @param string $use The WSDL "use" (encoded|literal)
  384. * @param boolean $soapval Whether this is called from soapval.
  385. * @return string The serialized element, possibly with child elements
  386. * @access public
  387. */
  388. function serialize_val($val, $name = false, $type = false, $name_ns = false, $type_ns = false, $attributes = false, $use = 'encoded', $soapval = false)
  389. {
  390. $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
  391. $this->appendDebug('value=' . $this->varDump($val));
  392. $this->appendDebug('attributes=' . $this->varDump($attributes));
  393. if (is_object($val) && get_class($val) == 'soapval' && (!$soapval)) {
  394. $this->debug("serialize_val: serialize soapval");
  395. $xml = $val->serialize($use);
  396. $this->appendDebug($val->getDebug());
  397. $val->clearDebug();
  398. $this->debug("serialize_val of soapval returning $xml");
  399. return $xml;
  400. }
  401. // force valid name if necessary
  402. if (is_numeric($name)) {
  403. $name = '__numeric_' . $name;
  404. } elseif (!$name) {
  405. $name = 'noname';
  406. }
  407. // if name has ns, add ns prefix to name
  408. $xmlns = '';
  409. if ($name_ns) {
  410. $prefix = 'nu' . rand(1000, 9999);
  411. $name = $prefix . ':' . $name;
  412. $xmlns .= " xmlns:$prefix=\"$name_ns\"";
  413. }
  414. // if type is prefixed, create type prefix
  415. if ($type_ns != '' && $type_ns == $this->namespaces['xsd']) {
  416. // need to fix this. shouldn't default to xsd if no ns specified
  417. // w/o checking against typemap
  418. $type_prefix = 'xsd';
  419. } elseif ($type_ns) {
  420. $type_prefix = 'ns' . rand(1000, 9999);
  421. $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
  422. }
  423. // serialize attributes if present
  424. $atts = '';
  425. if ($attributes) {
  426. foreach ($attributes as $k => $v) {
  427. $atts .= " $k=\"" . $this->expandEntities($v) . '"';
  428. }
  429. }
  430. // serialize null value
  431. if (is_null($val)) {
  432. $this->debug("serialize_val: serialize null");
  433. if ($use == 'literal') {
  434. // TODO: depends on minOccurs
  435. $xml = "<$name$xmlns$atts/>";
  436. $this->debug("serialize_val returning $xml");
  437. return $xml;
  438. } else {
  439. if (isset($type) && isset($type_prefix)) {
  440. $type_str = " xsi:type=\"$type_prefix:$type\"";
  441. } else {
  442. $type_str = '';
  443. }
  444. $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
  445. $this->debug("serialize_val returning $xml");
  446. return $xml;
  447. }
  448. }
  449. // serialize if an xsd built-in primitive type
  450. if ($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])) {
  451. $this->debug("serialize_val: serialize xsd built-in primitive type");
  452. if (is_bool($val)) {
  453. if ($type == 'boolean') {
  454. $val = $val ? 'true' : 'false';
  455. } elseif (!$val) {
  456. $val = 0;
  457. }
  458. } elseif (is_string($val)) {
  459. $val = $this->expandEntities($val);
  460. }
  461. if ($use == 'literal') {
  462. $xml = "<$name$xmlns$atts>$val</$name>";
  463. $this->debug("serialize_val returning $xml");
  464. return $xml;
  465. } else {
  466. $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
  467. $this->debug("serialize_val returning $xml");
  468. return $xml;
  469. }
  470. }
  471. // detect type and serialize
  472. $xml = '';
  473. switch (true) {
  474. case (is_bool($val) || $type == 'boolean'):
  475. $this->debug("serialize_val: serialize boolean");
  476. if ($type == 'boolean') {
  477. $val = $val ? 'true' : 'false';
  478. } elseif (!$val) {
  479. $val = 0;
  480. }
  481. if ($use == 'literal') {
  482. $xml .= "<$name$xmlns$atts>$val</$name>";
  483. } else {
  484. $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
  485. }
  486. break;
  487. case (is_int($val) || is_long($val) || $type == 'int'):
  488. $this->debug("serialize_val: serialize int");
  489. if ($use == 'literal') {
  490. $xml .= "<$name$xmlns$atts>$val</$name>";
  491. } else {
  492. $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
  493. }
  494. break;
  495. case (is_float($val) || is_double($val) || $type == 'float'):
  496. $this->debug("serialize_val: serialize float");
  497. if ($use == 'literal') {
  498. $xml .= "<$name$xmlns$atts>$val</$name>";
  499. } else {
  500. $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
  501. }
  502. break;
  503. case (is_string($val) || $type == 'string'):
  504. $this->debug("serialize_val: serialize string");
  505. $val = $this->expandEntities($val);
  506. if ($use == 'literal') {
  507. $xml .= "<$name$xmlns$atts>$val</$name>";
  508. } else {
  509. $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
  510. }
  511. break;
  512. case is_object($val):
  513. $this->debug("serialize_val: serialize object");
  514. if (get_class($val) == 'soapval') {
  515. $this->debug("serialize_val: serialize soapval object");
  516. $pXml = $val->serialize($use);
  517. $this->appendDebug($val->getDebug());
  518. $val->clearDebug();
  519. } else {
  520. if (!$name) {
  521. $name = get_class($val);
  522. $this->debug("In serialize_val, used class name $name as element name");
  523. } else {
  524. $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
  525. }
  526. foreach (get_object_vars($val) as $k => $v) {
  527. $pXml = isset($pXml) ? $pXml . $this->serialize_val($v, $k, false, false, false, false, $use) : $this->serialize_val($v, $k, false, false, false, false, $use);
  528. }
  529. }
  530. if (isset($type) && isset($type_prefix)) {
  531. $type_str = " xsi:type=\"$type_prefix:$type\"";
  532. } else {
  533. $type_str = '';
  534. }
  535. if ($use == 'literal') {
  536. $xml .= "<$name$xmlns$atts>$pXml</$name>";
  537. } else {
  538. $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
  539. }
  540. break;
  541. break;
  542. case (is_array($val) || $type):
  543. // detect if struct or array
  544. $valueType = $this->isArraySimpleOrStruct($val);
  545. if ($valueType == 'arraySimple' || preg_match('/^ArrayOf/', $type)) {
  546. $this->debug("serialize_val: serialize array");
  547. $i = 0;
  548. if (is_array($val) && count($val) > 0) {
  549. foreach ($val as $v) {
  550. if (is_object($v) && get_class($v) == 'soapval') {
  551. $tt_ns = $v->type_ns;
  552. $tt = $v->type;
  553. } elseif (is_array($v)) {
  554. $tt = $this->isArraySimpleOrStruct($v);
  555. } else {
  556. $tt = gettype($v);
  557. }
  558. $array_types[$tt] = 1;
  559. // TODO: for literal, the name should be $name
  560. $xml .= $this->serialize_val($v, 'item', false, false, false, false, $use);
  561. ++$i;
  562. }
  563. if (count($array_types) > 1) {
  564. $array_typename = 'xsd:anyType';
  565. } elseif (isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
  566. if ($tt == 'integer') {
  567. $tt = 'int';
  568. }
  569. $array_typename = 'xsd:' . $tt;
  570. } elseif (isset($tt) && $tt == 'arraySimple') {
  571. $array_typename = 'SOAP-ENC:Array';
  572. } elseif (isset($tt) && $tt == 'arrayStruct') {
  573. $array_typename = 'unnamed_struct_use_soapval';
  574. } else {
  575. // if type is prefixed, create type prefix
  576. if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']) {
  577. $array_typename = 'xsd:' . $tt;
  578. } elseif ($tt_ns) {
  579. $tt_prefix = 'ns' . rand(1000, 9999);
  580. $array_typename = "$tt_prefix:$tt";
  581. $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
  582. } else {
  583. $array_typename = $tt;
  584. }
  585. }
  586. $array_type = $i;
  587. if ($use == 'literal') {
  588. $type_str = '';
  589. } elseif (isset($type) && isset($type_prefix)) {
  590. $type_str = " xsi:type=\"$type_prefix:$type\"";
  591. } else {
  592. $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"" . $array_typename . "[$array_type]\"";
  593. }
  594. // empty array
  595. } else {
  596. if ($use == 'literal') {
  597. $type_str = '';
  598. } elseif (isset($type) && isset($type_prefix)) {
  599. $type_str = " xsi:type=\"$type_prefix:$type\"";
  600. } else {
  601. $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
  602. }
  603. }
  604. // TODO: for array in literal, there is no wrapper here
  605. $xml = "<$name$xmlns$type_str$atts>" . $xml . "</$name>";
  606. } else {
  607. // got a struct
  608. $this->debug("serialize_val: serialize struct");
  609. if (isset($type) && isset($type_prefix)) {
  610. $type_str = " xsi:type=\"$type_prefix:$type\"";
  611. } else {
  612. $type_str = '';
  613. }
  614. if ($use == 'literal') {
  615. $xml .= "<$name$xmlns$atts>";
  616. } else {
  617. $xml .= "<$name$xmlns$type_str$atts>";
  618. }
  619. foreach ($val as $k => $v) {
  620. // Apache Map
  621. if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
  622. $xml .= '<item>';
  623. $xml .= $this->serialize_val($k, 'key', false, false, false, false, $use);
  624. $xml .= $this->serialize_val($v, 'value', false, false, false, false, $use);
  625. $xml .= '</item>';
  626. } else {
  627. $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
  628. }
  629. }
  630. $xml .= "</$name>";
  631. }
  632. break;
  633. default:
  634. $this->debug("serialize_val: serialize unknown");
  635. $xml .= 'not detected, got ' . gettype($val) . ' for ' . $val;
  636. break;
  637. }
  638. $this->debug("serialize_val returning $xml");
  639. return $xml;
  640. }
  641. /**
  642. * serializes a message
  643. *
  644. * @param string $body the XML of the SOAP body
  645. * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
  646. * @param array $namespaces optional the namespaces used in generating the body and headers
  647. * @param string $style optional (rpc|document)
  648. * @param string $use optional (encoded|literal)
  649. * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
  650. * @return string the message
  651. * @access public
  652. */
  653. function serializeEnvelope($body, $headers = false, $namespaces = array(), $style = 'rpc', $use = 'encoded', $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/')
  654. {
  655. // TODO: add an option to automatically run utf8_encode on $body and $headers
  656. // if $this->soap_defencoding is UTF-8. Not doing this automatically allows
  657. // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
  658. $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
  659. $this->debug("headers:");
  660. $this->appendDebug($this->varDump($headers));
  661. $this->debug("namespaces:");
  662. $this->appendDebug($this->varDump($namespaces));
  663. // serialize namespaces
  664. $ns_string = '';
  665. foreach (array_merge($this->namespaces, $namespaces) as $k => $v) {
  666. $ns_string .= " xmlns:$k=\"$v\"";
  667. }
  668. if ($encodingStyle) {
  669. $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
  670. }
  671. // serialize headers
  672. if ($headers) {
  673. if (is_array($headers)) {
  674. $xml = '';
  675. foreach ($headers as $k => $v) {
  676. if (is_object($v) && get_class($v) == 'soapval') {
  677. $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
  678. } else {
  679. $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
  680. }
  681. }
  682. $headers = $xml;
  683. $this->debug("In serializeEnvelope, serialized array of headers to $headers");
  684. }
  685. $headers = "<SOAP-ENV:Header>" . $headers . "</SOAP-ENV:Header>";
  686. }
  687. // serialize envelope
  688. return
  689. '<?xml version="1.0" encoding="' . $this->soap_defencoding . '"?' . ">" .
  690. '<SOAP-ENV:Envelope' . $ns_string . ">" .
  691. $headers .
  692. "<SOAP-ENV:Body>" .
  693. $body .
  694. "</SOAP-ENV:Body>" .
  695. "</SOAP-ENV:Envelope>";
  696. }
  697. /**
  698. * formats a string to be inserted into an HTML stream
  699. *
  700. * @param string $str The string to format
  701. * @return string The formatted string
  702. * @access public
  703. * @deprecated
  704. */
  705. function formatDump($str)
  706. {
  707. $str = htmlspecialchars($str);
  708. return nl2br($str);
  709. }
  710. /**
  711. * contracts (changes namespace to prefix) a qualified name
  712. *
  713. * @param string $qname qname
  714. * @return string contracted qname
  715. * @access private
  716. */
  717. function contractQname($qname)
  718. {
  719. // get element namespace
  720. //$this->xdebug("Contract $qname");
  721. if (strrpos($qname, ':')) {
  722. // get unqualified name
  723. $name = substr($qname, strrpos($qname, ':') + 1);
  724. // get ns
  725. $ns = substr($qname, 0, strrpos($qname, ':'));
  726. $p = $this->getPrefixFromNamespace($ns);
  727. if ($p) {
  728. return $p . ':' . $name;
  729. }
  730. return $qname;
  731. } else {
  732. return $qname;
  733. }
  734. }
  735. /**
  736. * expands (changes prefix to namespace) a qualified name
  737. *
  738. * @param string $qname qname
  739. * @return string expanded qname
  740. * @access private
  741. */
  742. function expandQname($qname)
  743. {
  744. // get element prefix
  745. if (strpos($qname, ':') && !preg_match('/^http:\/\//', $qname)) {
  746. // get unqualified name
  747. $name = substr(strstr($qname, ':'), 1);
  748. // get ns prefix
  749. $prefix = substr($qname, 0, strpos($qname, ':'));
  750. if (isset($this->namespaces[$prefix])) {
  751. return $this->namespaces[$prefix] . ':' . $name;
  752. } else {
  753. return $qname;
  754. }
  755. } else {
  756. return $qname;
  757. }
  758. }
  759. /**
  760. * returns the local part of a prefixed string
  761. * returns the original string, if not prefixed
  762. *
  763. * @param string $str The prefixed string
  764. * @return string The local part
  765. * @access public
  766. */
  767. function getLocalPart($str)
  768. {
  769. if ($sstr = strrchr($str, ':')) {
  770. // get unqualified name
  771. return substr($sstr, 1);
  772. } else {
  773. return $str;
  774. }
  775. }
  776. /**
  777. * returns the prefix part of a prefixed string
  778. * returns false, if not prefixed
  779. *
  780. * @param string $str The prefixed string
  781. * @return mixed The prefix or false if there is no prefix
  782. * @access public
  783. */
  784. function getPrefix($str)
  785. {
  786. if ($pos = strrpos($str, ':')) {
  787. // get prefix
  788. return substr($str, 0, $pos);
  789. }
  790. return false;
  791. }
  792. /**
  793. * pass it a prefix, it returns a namespace
  794. *
  795. * @param string $prefix The prefix
  796. * @return mixed The namespace, false if no namespace has the specified prefix
  797. * @access public
  798. */
  799. function getNamespaceFromPrefix($prefix)
  800. {
  801. if (isset($this->namespaces[$prefix])) {
  802. return $this->namespaces[$prefix];
  803. }
  804. //$this->setError("No namespace registered for prefix '$prefix'");
  805. return false;
  806. }
  807. /**
  808. * returns the prefix for a given namespace (or prefix)
  809. * or false if no prefixes registered for the given namespace
  810. *
  811. * @param string $ns The namespace
  812. * @return mixed The prefix, false if the namespace has no prefixes
  813. * @access public
  814. */
  815. function getPrefixFromNamespace($ns)
  816. {
  817. foreach ($this->namespaces as $p => $n) {
  818. if ($ns == $n || $ns == $p) {
  819. $this->usedNamespaces[$p] = $n;
  820. return $p;
  821. }
  822. }
  823. return false;
  824. }
  825. /**
  826. * returns the time in ODBC canonical form with microseconds
  827. *
  828. * @return string The time in ODBC canonical form with microseconds
  829. * @access public
  830. */
  831. function getmicrotime()
  832. {
  833. if (function_exists('gettimeofday')) {
  834. $tod = gettimeofday();
  835. $sec = $tod['sec'];
  836. $usec = $tod['usec'];
  837. } else {
  838. $sec = time();
  839. $usec = 0;
  840. }
  841. return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
  842. }
  843. /**
  844. * Returns a string with the output of var_dump
  845. *
  846. * @param mixed $data The variable to var_dump
  847. * @return string The output of var_dump
  848. * @access public
  849. */
  850. function varDump($data)
  851. {
  852. ob_start();
  853. var_dump($data);
  854. $ret_val = ob_get_contents();
  855. ob_end_clean();
  856. return $ret_val;
  857. }
  858. /**
  859. * represents the object as a string
  860. *
  861. * @return string
  862. * @access public
  863. */
  864. function __toString()
  865. {
  866. return $this->varDump($this);
  867. }
  868. }
  869. // XML Schema Datatype Helper Functions
  870. //xsd:dateTime helpers
  871. /**
  872. * convert unix timestamp to ISO 8601 compliant date string
  873. *
  874. * @param int $timestamp Unix time stamp
  875. * @param boolean $utc Whether the time stamp is UTC or local
  876. * @return mixed ISO 8601 date string or false
  877. * @access public
  878. */
  879. function timestamp_to_iso8601($timestamp, $utc = true)
  880. {
  881. $datestr = date('Y-m-d\TH:i:sO', $timestamp);
  882. $pos = strrpos($datestr, "+");
  883. if ($pos === false) {
  884. $pos = strrpos($datestr, "-");
  885. }
  886. if ($pos !== false) {
  887. if (strlen($datestr) == $pos + 5) {
  888. $datestr = substr($datestr, 0, $pos + 3) . ':' . substr($datestr, -2);
  889. }
  890. }
  891. if ($utc) {
  892. $pattern = '/' .
  893. '([0-9]{4})-' . // centuries & years CCYY-
  894. '([0-9]{2})-' . // months MM-
  895. '([0-9]{2})' . // days DD
  896. 'T' . // separator T
  897. '([0-9]{2}):' . // hours hh:
  898. '([0-9]{2}):' . // minutes mm:
  899. '([0-9]{2})(\.[0-9]*)?' . // seconds ss.ss...
  900. '(Z|[+\-][0-9]{2}:?[0-9]{2})?' . // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
  901. '/';
  902. if (preg_match($pattern, $datestr, $regs)) {
  903. return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ', $regs[1], $regs[2], $regs[3], $regs[4], $regs[5], $regs[6]);
  904. }
  905. return false;
  906. } else {
  907. return $datestr;
  908. }
  909. }
  910. /**
  911. * convert ISO 8601 compliant date string to unix timestamp
  912. *
  913. * @param string $datestr ISO 8601 compliant date string
  914. * @return mixed Unix timestamp (int) or false
  915. * @access public
  916. */
  917. function iso8601_to_timestamp($datestr)
  918. {
  919. $pattern = '/' .
  920. '([0-9]{4})-' . // centuries & years CCYY-
  921. '([0-9]{2})-' . // months MM-
  922. '([0-9]{2})' . // days DD
  923. 'T' . // separator T
  924. '([0-9]{2}):' . // hours hh:
  925. '([0-9]{2}):' . // minutes mm:
  926. '([0-9]{2})(\.[0-9]+)?' . // seconds ss.ss...
  927. '(Z|[+\-][0-9]{2}:?[0-9]{2})?' . // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
  928. '/';
  929. if (preg_match($pattern, $datestr, $regs)) {
  930. // not utc
  931. if ($regs[8] != 'Z') {
  932. $op = substr($regs[8], 0, 1);
  933. $h = substr($regs[8], 1, 2);
  934. $m = substr($regs[8], strlen($regs[8]) - 2, 2);
  935. if ($op == '-') {
  936. $regs[4] = $regs[4] + $h;
  937. $regs[5] = $regs[5] + $m;
  938. } elseif ($op == '+') {
  939. $regs[4] = $regs[4] - $h;
  940. $regs[5] = $regs[5] - $m;
  941. }
  942. }
  943. return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
  944. // return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
  945. } else {
  946. return false;
  947. }
  948. }
  949. /**
  950. * sleeps some number of microseconds
  951. *
  952. * @param string $usec the number of microseconds to sleep
  953. * @access public
  954. * @deprecated
  955. */
  956. function usleepWindows($usec)
  957. {
  958. $start = gettimeofday();
  959. do {
  960. $stop = gettimeofday();
  961. $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
  962. + $stop['usec'] - $start['usec'];
  963. } while ($timePassed < $usec);
  964. }
  965. /**
  966. * Contains information for a SOAP fault.
  967. * Mainly used for returning faults from deployed functions
  968. * in a server instance.
  969. *
  970. * @author Dietrich Ayala <dietrich@ganx4.com>
  971. * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  972. * @access public
  973. */
  974. class nusoap_fault extends nusoap_base
  975. {
  976. /**
  977. * The fault code (client|server)
  978. *
  979. * @var string
  980. * @access private
  981. */
  982. var $faultcode;
  983. /**
  984. * The fault actor
  985. *
  986. * @var string
  987. * @access private
  988. */
  989. var $faultactor;
  990. /**
  991. * The fault string, a description of the fault
  992. *
  993. * @var string
  994. * @access private
  995. */
  996. var $faultstring;
  997. /**
  998. * The fault detail, typically a string or array of string
  999. *
  1000. * @var mixed
  1001. * @access private
  1002. */
  1003. var $faultdetail;
  1004. /**
  1005. * constructor
  1006. *
  1007. * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
  1008. * @param string $faultactor only used when msg routed between multiple actors
  1009. * @param string $faultstring human readable error message
  1010. * @param mixed $faultdetail detail, typically a string or array of string
  1011. */
  1012. function __construct($faultcode, $faultactor = '', $faultstring = '', $faultdetail = '')
  1013. {
  1014. parent::__construct();
  1015. $this->faultcode = $faultcode;
  1016. $this->faultactor = $faultactor;
  1017. $this->faultstring = $faultstring;
  1018. $this->faultdetail = $faultdetail;
  1019. }
  1020. /**
  1021. * serialize a fault
  1022. *
  1023. * @return string The serialization of the fault instance.
  1024. * @access public
  1025. */
  1026. function serialize()
  1027. {
  1028. $ns_string = '';
  1029. foreach ($this->namespaces as $k => $v) {
  1030. $ns_string .= "\n xmlns:$k=\"$v\"";
  1031. }
  1032. $return_msg =
  1033. '<?xml version="1.0" encoding="' . $this->soap_defencoding . '"?>' .
  1034. '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string . ">\n" .
  1035. '<SOAP-ENV:Body>' .
  1036. '<SOAP-ENV:Fault>' .
  1037. $this->serialize_val($this->faultcode, 'faultcode') .
  1038. $this->serialize_val($this->faultactor, 'faultactor') .
  1039. $this->serialize_val($this->faultstring, 'faultstring') .
  1040. $this->serialize_val($this->faultdetail, 'detail') .
  1041. '</SOAP-ENV:Fault>' .
  1042. '</SOAP-ENV:Body>' .
  1043. '</SOAP-ENV:Envelope>';
  1044. return $return_msg;
  1045. }
  1046. }
  1047. /**
  1048. * Backward compatibility
  1049. */
  1050. class soap_fault extends nusoap_fault
  1051. {
  1052. }
  1053. /**
  1054. * parses an XML Schema, allows access to it's data, other utility methods.
  1055. * imperfect, no validation... yet, but quite functional.
  1056. *
  1057. * @author Dietrich Ayala <dietrich@ganx4.com>
  1058. * @author Scott Nichol <snichol@users.sourceforge.net>
  1059. * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  1060. * @access public
  1061. */
  1062. class nusoap_xmlschema extends nusoap_base
  1063. {
  1064. // files
  1065. var $schema = '';
  1066. var $xml = '';
  1067. // namespaces
  1068. var $enclosingNamespaces;
  1069. // schema info
  1070. var $schemaInfo = array();
  1071. var $schemaTargetNamespace = '';
  1072. // types, elements, attributes defined by the schema
  1073. var $attributes = array();
  1074. var $complexTypes = array();
  1075. var $complexTypeStack = array();
  1076. var $currentComplexType = null;
  1077. var $elements = array();
  1078. var $elementStack = array();
  1079. var $currentElement = null;
  1080. var $simpleTypes = array();
  1081. var $simpleTypeStack = array();
  1082. var $currentSimpleType = null;
  1083. // imports
  1084. var $imports = array();
  1085. // parser vars
  1086. var $parser;
  1087. var $position = 0;
  1088. var $depth = 0;
  1089. var $depth_array = array();
  1090. var $message = array();
  1091. var $defaultNamespace = array();
  1092. /**
  1093. * constructor
  1094. *
  1095. * @param string $schema schema document URI
  1096. * @param string $xml xml document URI
  1097. * @param string $namespaces namespaces defined in enclosing XML
  1098. * @access public
  1099. */
  1100. function __construct($schema = '', $xml = '', $namespaces = array())
  1101. {
  1102. parent::__construct();
  1103. $this->debug('nusoap_xmlschema class instantiated, inside constructor');
  1104. // files
  1105. $this->schema = $schema;
  1106. $this->xml = $xml;
  1107. // namespaces
  1108. $this->enclosingNamespaces = $namespaces;
  1109. $this->namespaces = array_merge($this->namespaces, $namespaces);
  1110. // parse schema file
  1111. if ($schema != '') {
  1112. $this->debug('initial schema file: ' . $schema);
  1113. $this->parseFile($schema, 'schema');
  1114. }
  1115. // parse xml file
  1116. if ($xml != '') {
  1117. $this->debug('initial xml file: ' . $xml);
  1118. $this->parseFile($xml, 'xml');
  1119. }
  1120. }
  1121. /**
  1122. * parse an XML file
  1123. *
  1124. * @param string $xml path/URL to XML file
  1125. * @param string $type (schema | xml)
  1126. * @return boolean
  1127. * @access public
  1128. */
  1129. function parseFile($xml, $type)
  1130. {
  1131. // parse xml file
  1132. if ($xml != "") {
  1133. $xmlStr = @join("", @file($xml));
  1134. if ($xmlStr == "") {
  1135. $msg = 'Error reading XML from ' . $xml;
  1136. $this->setError($msg);
  1137. $this->debug($msg);
  1138. return false;
  1139. } else {
  1140. $this->debug("parsing $xml");
  1141. $this->parseString($xmlStr, $type);
  1142. $this->debug("done parsing $xml");
  1143. return true;
  1144. }
  1145. }
  1146. return false;
  1147. }
  1148. /**
  1149. * parse an XML string
  1150. *
  1151. * @param string $xml path or URL
  1152. * @param string $type (schema|xml)
  1153. * @access private
  1154. */
  1155. function parseString($xml, $type)
  1156. {
  1157. // parse xml string
  1158. if ($xml != "") {
  1159. // Create an XML parser.
  1160. $this->parser = xml_parser_create();
  1161. // Set the options for parsing the XML data.
  1162. xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
  1163. // Set the object for the parser.
  1164. xml_set_object($this->parser, $this);
  1165. // Set the element handlers for the parser.
  1166. if ($type == "schema") {
  1167. xml_set_element_handler($this->parser, 'schemaStartElement', 'schemaEndElement');
  1168. xml_set_character_data_handler($this->parser, 'schemaCharacterData');
  1169. } elseif ($type == "xml") {
  1170. xml_set_element_handler($this->parser, 'xmlStartElement', 'xmlEndElement');
  1171. xml_set_character_data_handler($this->parser, 'xmlCharacterData');
  1172. }
  1173. // Parse the XML file.
  1174. if (!xml_parse($this->parser, $xml, true)) {
  1175. // Display an error message.
  1176. $errstr = sprintf('XML error parsing XML schema on line %d: %s',
  1177. xml_get_current_line_number($this->parser),
  1178. xml_error_string(xml_get_error_code($this->parser))
  1179. );
  1180. $this->debug($errstr);
  1181. $this->debug("XML payload:\n" . $xml);
  1182. $this->setError($errstr);
  1183. }
  1184. xml_parser_free($this->parser);
  1185. unset($this->parser);
  1186. } else {
  1187. $this->debug('no xml passed to parseString()!!');
  1188. $this->setError('no xml passed to parseString()!!');
  1189. }
  1190. }
  1191. /**
  1192. * gets a type name for an unnamed type
  1193. *
  1194. * @param string Element name
  1195. * @return string A type name for an unnamed type
  1196. * @access private
  1197. */
  1198. function CreateTypeName($ename)
  1199. {
  1200. $scope = '';
  1201. for ($i = 0; $i < count($this->complexTypeStack); $i++) {
  1202. $scope .= $this->complexTypeStack[$i] . '_';
  1203. }
  1204. return $scope . $ename . '_ContainedType';
  1205. }
  1206. /**
  1207. * start-element handler
  1208. *
  1209. * @param string $parser XML parser object
  1210. * @param string $name element name
  1211. * @param string $attrs associative array of attributes
  1212. * @access private
  1213. */
  1214. function schemaStartElement($parser, $name, $attrs)
  1215. {
  1216. // position in the total number of elements, starting from 0
  1217. $pos = $this->position++;
  1218. $depth = $this->depth++;
  1219. // set self as current value for this depth
  1220. $this->depth_array[$depth] = $pos;
  1221. $this->message[$pos] = array('cdata' => '');
  1222. if ($depth > 0) {
  1223. $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
  1224. } else {
  1225. $this->defaultNamespace[$pos] = false;
  1226. }
  1227. // get element prefix
  1228. if ($prefix = $this->getPrefix($name)) {
  1229. // get unqualified name
  1230. $name = $this->getLocalPart($name);
  1231. } else {
  1232. $prefix = '';
  1233. }
  1234. // loop thru attributes, expanding, and registering namespace declarations
  1235. if (count($attrs) > 0) {
  1236. foreach ($attrs as $k => $v) {
  1237. // if ns declarations, add to class level array of valid namespaces
  1238. if (preg_match('/^xmlns/', $k)) {
  1239. //$this->xdebug("$k: $v");
  1240. //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
  1241. if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
  1242. //$this->xdebug("Add namespace[$ns_prefix] = $v");
  1243. $this->namespaces[$ns_prefix] = $v;
  1244. } else {
  1245. $this->defaultNamespace[$pos] = $v;
  1246. if (!$this->getPrefixFromNamespace($v)) {
  1247. $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
  1248. }
  1249. }
  1250. if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
  1251. $this->XMLSchemaVersion = $v;
  1252. $this->namespaces['xsi'] = $v . '-instance';
  1253. }
  1254. }
  1255. }
  1256. foreach ($attrs as $k => $v) {
  1257. // expand each attribute
  1258. $k = strpos($k, ':') ? $this->expandQname($k) : $k;
  1259. $v = strpos($v, ':') ? $this->expandQname($v) : $v;
  1260. $eAttrs[$k] = $v;
  1261. }
  1262. $attrs = $eAttrs;
  1263. } else {
  1264. $attrs = array();
  1265. }
  1266. // find status, register data
  1267. switch ($name) {
  1268. case 'all': // (optional) compositor content for a complexType
  1269. case 'choice':
  1270. case 'group':
  1271. case 'sequence':
  1272. //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
  1273. $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
  1274. //if($name == 'all' || $name == 'sequence'){
  1275. // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
  1276. //}
  1277. break;
  1278. case 'attribute': // complexType attribute
  1279. //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
  1280. $this->xdebug("parsing attribute:");
  1281. $this->appendDebug($this->varDump($attrs));
  1282. if (!isset($attrs['form'])) {
  1283. // TODO: handle globals
  1284. $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
  1285. }
  1286. if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
  1287. $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
  1288. if (!strpos($v, ':')) {
  1289. // no namespace in arrayType attribute value...
  1290. if ($this->defaultNamespace[$pos]) {
  1291. // ...so use the default
  1292. $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
  1293. }
  1294. }
  1295. }
  1296. if (isset($attrs['name'])) {
  1297. $this->attributes[$attrs['name']] = $attrs;
  1298. $aname = $attrs['name'];
  1299. } elseif (isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType') {
  1300. if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
  1301. $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
  1302. } else {
  1303. $aname = '';
  1304. }
  1305. } elseif (isset($attrs['ref'])) {
  1306. $aname = $attrs['ref'];
  1307. $this->attributes[$attrs['ref']] = $attrs;
  1308. }
  1309. if ($this->currentComplexType) { // This should *always* be
  1310. $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
  1311. }
  1312. // arrayType attribute
  1313. if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType') {
  1314. $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
  1315. $prefix = $this->getPrefix($aname);
  1316. if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
  1317. $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
  1318. } else {
  1319. $v = '';
  1320. }
  1321. if (strpos($v, '[,]')) {
  1322. $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
  1323. }
  1324. $v = substr($v, 0, strpos($v, '[')); // clip the []
  1325. if (!strpos($v, ':') && isset($this->typemap[$this->XMLSchemaVersion][$v])) {
  1326. $v = $this->XMLSchemaVersion . ':' . $v;
  1327. }
  1328. $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
  1329. }
  1330. break;
  1331. case 'complexContent': // (optional) content for a complexType
  1332. $this->xdebug("do nothing for element $name");
  1333. break;
  1334. case 'complexType':
  1335. array_push($this->complexTypeStack, $this->currentComplexType);
  1336. if (isset($attrs['name'])) {
  1337. // TODO: what is the scope of named complexTypes that appear
  1338. // nested within other c complexTypes?
  1339. $this->xdebug('processing named complexType ' . $attrs['name']);
  1340. //$this->currentElement = false;
  1341. $this->currentComplexType = $attrs['name'];
  1342. $this->complexTypes[$this->currentComplexType] = $attrs;
  1343. $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
  1344. // This is for constructs like
  1345. // <complexType name="ListOfString" base="soap:Array">
  1346. // <sequence>
  1347. // <element name="string" type="xsd:string"
  1348. // minOccurs="0" maxOccurs="unbounded" />
  1349. // </sequence>
  1350. // </complexType>
  1351. if (isset($attrs['base']) && preg_match('/:Array$/', $attrs['base'])) {
  1352. $this->xdebug('complexType is unusual array');
  1353. $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
  1354. } else {
  1355. $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
  1356. }
  1357. } else {
  1358. $name = $this->CreateTypeName($this->currentElement);
  1359. $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
  1360. $this->currentComplexType = $name;
  1361. //$this->currentElement = false;
  1362. $this->complexTypes[$this->currentComplexType] = $attrs;
  1363. $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
  1364. // This is for constructs like
  1365. // <complexType name="ListOfString" base="soap:Array">
  1366. // <sequence>
  1367. // <element name="string" type="xsd:string"
  1368. // minOccurs="0" maxOccurs="unbounded" />
  1369. // </sequence>
  1370. // </complexType>
  1371. if (isset($attrs['base']) && preg_match('/:Array$/', $attrs['base'])) {
  1372. $this->xdebug('complexType is unusual array');
  1373. $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
  1374. } else {
  1375. $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
  1376. }
  1377. }
  1378. $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false';
  1379. break;
  1380. case 'element':
  1381. array_push($this->elementStack, $this->currentElement);
  1382. if (!isset($attrs['form'])) {
  1383. if ($this->currentComplexType) {
  1384. $attrs['form'] = $this->schemaInfo['elementFormDefault'];
  1385. } else {
  1386. // global
  1387. $attrs['form'] = 'qualified';
  1388. }
  1389. }
  1390. if (isset($attrs['type'])) {
  1391. $this->xdebug("processing typed element " . $attrs['name'] . " of type " . $attrs['type']);
  1392. if (!$this->getPrefix($attrs['type'])) {
  1393. if ($this->defaultNamespace[$pos]) {
  1394. $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
  1395. $this->xdebug('used default namespace to make type ' . $attrs['type']);
  1396. }
  1397. }
  1398. // This is for constructs like
  1399. // <complexType name="ListOfString" base="soap:Array">
  1400. // <sequence>
  1401. // <element name="string" type="xsd:string"
  1402. // minOccurs="0" maxOccurs="unbounded" />
  1403. // </sequence>
  1404. // </complexType>
  1405. if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
  1406. $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
  1407. $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
  1408. }
  1409. $this->currentElement = $attrs['name'];
  1410. $ename = $attrs['name'];
  1411. } elseif (isset($attrs['ref'])) {
  1412. $this->xdebug("processing element as ref to " . $attrs['ref']);
  1413. $this->currentElement = "ref to " . $attrs['ref'];
  1414. $ename = $this->getLocalPart($attrs['ref']);
  1415. } else {
  1416. $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
  1417. $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type);
  1418. $this->currentElement = $attrs['name'];
  1419. $attrs['type'] = $this->schemaTargetNamespace . ':' . $type;
  1420. $ename = $attrs['name'];
  1421. }
  1422. if (isset($ename) && $this->currentComplexType) {
  1423. $this->xdebug("add element $ename to complexType $this->currentComplexType");
  1424. $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
  1425. } elseif (!isset($attrs['ref'])) {
  1426. $this->xdebug("add element $ename to elements array");
  1427. $this->elements[$attrs['name']] = $attrs;
  1428. $this->elements[$attrs['name']]['typeClass'] = 'element';
  1429. }
  1430. break;
  1431. case 'enumeration': // restriction value list member
  1432. $this->xdebug('enumeration ' . $attrs['value']);
  1433. if ($this->currentSimpleType) {
  1434. $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
  1435. } elseif ($this->currentComplexType) {
  1436. $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
  1437. }
  1438. break;
  1439. case 'extension': // simpleContent or complexContent type extension
  1440. $this->xdebug('extension ' . $attrs['base']);
  1441. if ($this->currentComplexType) {
  1442. $ns = $this->getPrefix($attrs['base']);
  1443. if ($ns == '') {
  1444. $this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base'];
  1445. } else {
  1446. $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
  1447. }
  1448. } else {
  1449. $this->xdebug('no current complexType to set extensionBase');
  1450. }
  1451. break;
  1452. case 'import':
  1453. if (isset($attrs['schemaLocation'])) {
  1454. $this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
  1455. $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
  1456. } else {
  1457. $this->xdebug('import namespace ' . $attrs['namespace']);
  1458. $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
  1459. if (!$this->getPrefixFromNamespace($attrs['namespace'])) {
  1460. $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
  1461. }
  1462. }
  1463. break;
  1464. case 'include':
  1465. if (isset($attrs['schemaLocation'])) {
  1466. $this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']);
  1467. $this->imports[$this->schemaTargetNamespace][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
  1468. } else {
  1469. $this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute');
  1470. }
  1471. break;
  1472. case 'list': // simpleType value list
  1473. $this->xdebug("do nothing for element $name");
  1474. break;
  1475. case 'restriction': // simpleType, simpleContent or complexContent value restriction
  1476. $this->xdebug('restriction ' . $attrs['base']);
  1477. if ($this->currentSimpleType) {
  1478. $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
  1479. } elseif ($this->currentComplexType) {
  1480. $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
  1481. if (strstr($attrs['base'], ':') == ':Array') {
  1482. $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
  1483. }
  1484. }
  1485. break;
  1486. case 'schema':
  1487. $this->schemaInfo = $attrs;
  1488. $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
  1489. if (isset($attrs['targetNamespace'])) {
  1490. $this->schemaTargetNamespace = $attrs['targetNamespace'];
  1491. }
  1492. if (!isset($attrs['elementFormDefault'])) {
  1493. $this->schemaInfo['elementFormDefault'] = 'unqualified';
  1494. }
  1495. if (!isset($attrs['attributeFormDefault'])) {
  1496. $this->schemaInfo['attributeFormDefault'] = 'unqualified';
  1497. }
  1498. break;
  1499. case 'simpleContent': // (optional) content for a complexType
  1500. if ($this->currentComplexType) { // This should *always* be
  1501. $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true';
  1502. } else {
  1503. $this->xdebug("do nothing for element $name because there is no current complexType");
  1504. }
  1505. break;
  1506. case 'simpleType':
  1507. array_push($this->simpleTypeStack, $this->currentSimpleType);
  1508. if (isset($attrs['name'])) {
  1509. $this->xdebug("processing simpleType for name " . $attrs['name']);
  1510. $this->currentSimpleType = $attrs['name'];
  1511. $this->simpleTypes[$attrs['name']] = $attrs;
  1512. $this->simpleTypes[$attrs['name']]['typeClass'] = 'simpleType';
  1513. $this->simpleTypes[$attrs['name']]['phpType'] = 'scalar';
  1514. } else {
  1515. $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
  1516. $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
  1517. $this->currentSimpleType = $name;
  1518. //$this->currentElement = false;
  1519. $this->simpleTypes[$this->currentSimpleType] = $attrs;
  1520. $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
  1521. }
  1522. break;
  1523. case 'union': // simpleType type list
  1524. $this->xdebug("do nothing for element $name");
  1525. break;
  1526. default:
  1527. $this->xdebug("do not have any logic to process element $name");
  1528. }
  1529. }
  1530. /**
  1531. * end-element handler
  1532. *
  1533. * @param string $parser XML parser object
  1534. * @param string $name element name
  1535. * @access private
  1536. */
  1537. function schemaEndElement($parser, $name)
  1538. {
  1539. // bring depth down a notch
  1540. $this->depth--;
  1541. // position of current element is equal to the last value left in depth_array for my depth
  1542. if (isset($this->depth_array[$this->depth])) {
  1543. $pos = $this->depth_array[$this->depth];
  1544. }
  1545. // get element prefix
  1546. if ($prefix = $this->getPrefix($name)) {
  1547. // get unqualified name
  1548. $name = $this->getLocalPart($name);
  1549. } else {
  1550. $prefix = '';
  1551. }
  1552. // move on...
  1553. if ($name == 'complexType') {
  1554. $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
  1555. $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
  1556. $this->currentComplexType = array_pop($this->complexTypeStack);
  1557. //$this->currentElement = false;
  1558. }
  1559. if ($name == 'element') {
  1560. $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
  1561. $this->currentElement = array_pop($this->elementStack);
  1562. }
  1563. if ($name == 'simpleType') {
  1564. $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
  1565. $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
  1566. $this->currentSimpleType = array_pop($this->simpleTypeStack);
  1567. }
  1568. }
  1569. /**
  1570. * element content handler
  1571. *
  1572. * @param string $parser XML parser object
  1573. * @param string $data element content
  1574. * @access private
  1575. */
  1576. function schemaCharacterData($parser, $data)
  1577. {
  1578. $pos = $this->depth_array[$this->depth - 1];
  1579. $this->message[$pos]['cdata'] .= $data;
  1580. }
  1581. /**
  1582. * serialize the schema
  1583. *
  1584. * @access public
  1585. */
  1586. function serializeSchema()
  1587. {
  1588. $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
  1589. $xml = '';
  1590. // imports
  1591. if (sizeof($this->imports) > 0) {
  1592. foreach ($this->imports as $ns => $list) {
  1593. foreach ($list as $ii) {
  1594. if ($ii['location'] != '') {
  1595. $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
  1596. } else {
  1597. $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
  1598. }
  1599. }
  1600. }
  1601. }
  1602. // complex types
  1603. foreach ($this->complexTypes as $typeName => $attrs) {
  1604. $contentStr = '';
  1605. // serialize child elements
  1606. if (isset($attrs['elements']) && (count($attrs['elements']) > 0)) {
  1607. foreach ($attrs['elements'] as $element => $eParts) {
  1608. if (isset($eParts['ref'])) {
  1609. $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n";
  1610. } else {
  1611. $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
  1612. foreach ($eParts as $aName => $aValue) {
  1613. // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
  1614. if ($aName != 'name' && $aName != 'type') {
  1615. $contentStr .= " $aName=\"$aValue\"";
  1616. }
  1617. }
  1618. $contentStr .= "/>\n";
  1619. }
  1620. }
  1621. // compositor wraps elements
  1622. if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
  1623. $contentStr = " <$schemaPrefix:$attrs[compositor]>\n" . $contentStr . " </$schemaPrefix:$attrs[compositor]>\n";
  1624. }
  1625. }
  1626. // attributes
  1627. if (isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)) {
  1628. foreach ($attrs['attrs'] as $attr => $aParts) {
  1629. $contentStr .= " <$schemaPrefix:attribute";
  1630. foreach ($aParts as $a => $v) {
  1631. if ($a == 'ref' || $a == 'type') {
  1632. $contentStr .= " $a=\"" . $this->contractQName($v) . '"';
  1633. } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
  1634. $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
  1635. $contentStr .= ' wsdl:arrayType="' . $this->contractQName($v) . '"';
  1636. } else {
  1637. $contentStr .= " $a=\"$v\"";
  1638. }
  1639. }
  1640. $contentStr .= "/>\n";
  1641. }
  1642. }
  1643. // if restriction
  1644. if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != '') {
  1645. $contentStr = " <$schemaPrefix:restriction base=\"" . $this->contractQName($attrs['restrictionBase']) . "\">\n" . $contentStr . " </$schemaPrefix:restriction>\n";
  1646. // complex or simple content
  1647. if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)) {
  1648. $contentStr = " <$schemaPrefix:complexContent>\n" . $contentStr . " </$schemaPrefix:complexContent>\n";
  1649. }
  1650. }
  1651. // finalize complex type
  1652. if ($contentStr != '') {
  1653. $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n" . $contentStr . " </$schemaPrefix:complexType>\n";
  1654. } else {
  1655. $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
  1656. }
  1657. $xml .= $contentStr;
  1658. }
  1659. // simple types
  1660. if (isset($this->simpleTypes) && count($this->simpleTypes) > 0) {
  1661. foreach ($this->simpleTypes as $typeName => $eParts) {
  1662. $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"" . $this->contractQName($eParts['type']) . "\">\n";
  1663. if (isset($eParts['enumeration'])) {
  1664. foreach ($eParts['enumeration'] as $e) {
  1665. $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n";
  1666. }
  1667. }
  1668. $xml .= " </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
  1669. }
  1670. }
  1671. // elements
  1672. if (isset($this->elements) && count($this->elements) > 0) {
  1673. foreach ($this->elements as $element => $eParts) {
  1674. $xml .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"/>\n";
  1675. }
  1676. }
  1677. // attributes
  1678. if (isset($this->attributes) && count($this->attributes) > 0) {
  1679. foreach ($this->attributes as $attr => $aParts) {
  1680. $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"" . $this->contractQName($aParts['type']) . "\"\n/>";
  1681. }
  1682. }
  1683. // finish 'er up
  1684. $attr = '';
  1685. foreach ($this->schemaInfo as $k => $v) {
  1686. if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') {
  1687. $attr .= " $k=\"$v\"";
  1688. }
  1689. }
  1690. $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
  1691. foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
  1692. $el .= " xmlns:$nsp=\"$ns\"";
  1693. }
  1694. $xml = $el . ">\n" . $xml . "</$schemaPrefix:schema>\n";
  1695. return $xml;
  1696. }
  1697. /**
  1698. * adds debug data to the clas level debug string
  1699. *
  1700. * @param string $string debug data
  1701. * @access private
  1702. */
  1703. function xdebug($string)
  1704. {
  1705. $this->debug('<' . $this->schemaTargetNamespace . '> ' . $string);
  1706. }
  1707. /**
  1708. * get the PHP type of a user defined type in the schema
  1709. * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
  1710. * returns false if no type exists, or not w/ the given namespace
  1711. * else returns a string that is either a native php type, or 'struct'
  1712. *
  1713. * @param string $type name of defined type
  1714. * @param string $ns namespace of type
  1715. * @return mixed
  1716. * @access public
  1717. * @deprecated
  1718. */
  1719. function getPHPType($type, $ns)
  1720. {
  1721. if (isset($this->typemap[$ns][$type])) {
  1722. //print "found type '$type' and ns $ns in typemap<br>";
  1723. return $this->typemap[$ns][$type];
  1724. } elseif (isset($this->complexTypes[$type])) {
  1725. //print "getting type '$type' and ns $ns from complexTypes array<br>";
  1726. return $this->complexTypes[$type]['phpType'];
  1727. }
  1728. return false;
  1729. }
  1730. /**
  1731. * returns an associative array of information about a given type
  1732. * returns false if no type exists by the given name
  1733. *
  1734. * For a complexType typeDef = array(
  1735. * 'restrictionBase' => '',
  1736. * 'phpType' => '',
  1737. * 'compositor' => '(sequence|all)',
  1738. * 'elements' => array(), // refs to elements array
  1739. * 'attrs' => array() // refs to attributes array
  1740. * ... and so on (see addComplexType)
  1741. * )
  1742. *
  1743. * For simpleType or element, the array has different keys.
  1744. *
  1745. * @param string $type
  1746. * @return mixed
  1747. * @access public
  1748. * @see addComplexType
  1749. * @see addSimpleType
  1750. * @see addElement
  1751. */
  1752. function getTypeDef($type)
  1753. {
  1754. //$this->debug("in getTypeDef for type $type");
  1755. if (substr($type, -1) == '^') {
  1756. $is_element = 1;
  1757. $type = substr($type, 0, -1);
  1758. } else {
  1759. $is_element = 0;
  1760. }
  1761. if ((!$is_element) && isset($this->complexTypes[$type])) {
  1762. $this->xdebug("in getTypeDef, found complexType $type");
  1763. return $this->complexTypes[$type];
  1764. } elseif ((!$is_element) && isset($this->simpleTypes[$type])) {
  1765. $this->xdebug("in getTypeDef, found simpleType $type");
  1766. if (!isset($this->simpleTypes[$type]['phpType'])) {
  1767. // get info for type to tack onto the simple type
  1768. // TODO: can this ever really apply (i.e. what is a simpleType really?)
  1769. $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
  1770. $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
  1771. $etype = $this->getTypeDef($uqType);
  1772. if ($etype) {
  1773. $this->xdebug("in getTypeDef, found type for simpleType $type:");
  1774. $this->xdebug($this->varDump($etype));
  1775. if (isset($etype['phpType'])) {
  1776. $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
  1777. }
  1778. if (isset($etype['elements'])) {
  1779. $this->simpleTypes[$type]['elements'] = $etype['elements'];
  1780. }
  1781. }
  1782. }
  1783. return $this->simpleTypes[$type];
  1784. } elseif (isset($this->elements[$type])) {
  1785. $this->xdebug("in getTypeDef, found element $type");
  1786. if (!isset($this->elements[$type]['phpType'])) {
  1787. // get info for type to tack onto the element
  1788. $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
  1789. $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
  1790. $etype = $this->getTypeDef($uqType);
  1791. if ($etype) {
  1792. $this->xdebug("in getTypeDef, found type for element $type:");
  1793. $this->xdebug($this->varDump($etype));
  1794. if (isset($etype['phpType'])) {
  1795. $this->elements[$type]['phpType'] = $etype['phpType'];
  1796. }
  1797. if (isset($etype['elements'])) {
  1798. $this->elements[$type]['elements'] = $etype['elements'];
  1799. }
  1800. if (isset($etype['extensionBase'])) {
  1801. $this->elements[$type]['extensionBase'] = $etype['extensionBase'];
  1802. }
  1803. } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
  1804. $this->xdebug("in getTypeDef, element $type is an XSD type");
  1805. $this->elements[$type]['phpType'] = 'scalar';
  1806. }
  1807. }
  1808. return $this->elements[$type];
  1809. } elseif (isset($this->attributes[$type])) {
  1810. $this->xdebug("in getTypeDef, found attribute $type");
  1811. return $this->attributes[$type];
  1812. } elseif (preg_match('/_ContainedType$/', $type)) {
  1813. $this->xdebug("in getTypeDef, have an untyped element $type");
  1814. $typeDef['typeClass'] = 'simpleType';
  1815. $typeDef['phpType'] = 'scalar';
  1816. $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
  1817. return $typeDef;
  1818. }
  1819. $this->xdebug("in getTypeDef, did not find $type");
  1820. return false;
  1821. }
  1822. /**
  1823. * returns a sample serialization of a given type, or false if no type by the given name
  1824. *
  1825. * @param string $type name of type
  1826. * @return mixed
  1827. * @access public
  1828. * @deprecated
  1829. */
  1830. function serializeTypeDef($type)
  1831. {
  1832. $str = '';
  1833. //print "in sTD() for type $type<br>";
  1834. if ($typeDef = $this->getTypeDef($type)) {
  1835. $str .= '<' . $type;
  1836. if (is_array($typeDef['attrs'])) {
  1837. foreach ($typeDef['attrs'] as $attName => $data) {
  1838. $str .= " $attName=\"{type = " . $data['type'] . "}\"";
  1839. }
  1840. }
  1841. $str .= " xmlns=\"" . $this->schema['targetNamespace'] . "\"";
  1842. if (count($typeDef['elements']) > 0) {
  1843. $str .= ">";
  1844. foreach ($typeDef['elements'] as $element => $eData) {
  1845. $str .= $this->serializeTypeDef($element);
  1846. }
  1847. $str .= "</$type>";
  1848. } elseif ($typeDef['typeClass'] == 'element') {
  1849. $str .= "></$type>";
  1850. } else {
  1851. $str .= "/>";
  1852. }
  1853. return $str;
  1854. }
  1855. return false;
  1856. }
  1857. /**
  1858. * returns HTML form elements that allow a user
  1859. * to enter values for creating an instance of the given type.
  1860. *
  1861. * @param string $name name for type instance
  1862. * @param string $type name of type
  1863. * @return string
  1864. * @access public
  1865. * @deprecated
  1866. */
  1867. function typeToForm($name, $type)
  1868. {
  1869. $buffer = '';
  1870. // get typedef
  1871. if ($typeDef = $this->getTypeDef($type)) {
  1872. // if struct
  1873. if ($typeDef['phpType'] == 'struct') {
  1874. $buffer .= '<table>';
  1875. foreach ($typeDef['elements'] as $child => $childDef) {
  1876. $buffer .= "
  1877. <tr><td align='right'>$childDef[name] (type: " . $this->getLocalPart($childDef['type']) . "):</td>
  1878. <td><input type='text' name='parameters[" . $name . "][$childDef[name]]'></td></tr>";
  1879. }
  1880. $buffer .= '</table>';
  1881. // if array
  1882. } elseif ($typeDef['phpType'] == 'array') {
  1883. $buffer .= '<table>';
  1884. for ($i = 0; $i < 3; $i++) {
  1885. $buffer .= "
  1886. <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
  1887. <td><input type='text' name='parameters[" . $name . "][]'></td></tr>";
  1888. }
  1889. $buffer .= '</table>';
  1890. // if scalar
  1891. } else {
  1892. $buffer .= "<input type='text' name='parameters[$name]'>";
  1893. }
  1894. } else {
  1895. $buffer .= "<input type='text' name='parameters[$name]'>";
  1896. }
  1897. return $buffer;
  1898. }
  1899. /**
  1900. * adds a complex type to the schema
  1901. *
  1902. * example: array
  1903. *
  1904. * addType(
  1905. * 'ArrayOfstring',
  1906. * 'complexType',
  1907. * 'array',
  1908. * '',
  1909. * 'SOAP-ENC:Array',
  1910. * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
  1911. * 'xsd:string'
  1912. * );
  1913. *
  1914. * example: PHP associative array ( SOAP Struct )
  1915. *
  1916. * addType(
  1917. * 'SOAPStruct',
  1918. * 'complexType',
  1919. * 'struct',
  1920. * 'all',
  1921. * array('myVar'=> array('name'=>'myVar','type'=>'string')
  1922. * );
  1923. *
  1924. * @param name
  1925. * @param typeClass (complexType|simpleType|attribute)
  1926. * @param phpType : currently supported are array and struct (php assoc array)
  1927. * @param compositor (all|sequence|choice)
  1928. * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
  1929. * @param elements = array ( name = array(name=>'',type=>'') )
  1930. * @param attrs = array(
  1931. * array(
  1932. * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
  1933. * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
  1934. * )
  1935. * )
  1936. * @param arrayType : namespace:name (http://www.w3.org/2001/XMLSchema:string)
  1937. * @access public
  1938. * @see getTypeDef
  1939. */
  1940. function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = array(), $attrs = array(), $arrayType = '')
  1941. {
  1942. $this->complexTypes[$name] = array(
  1943. 'name' => $name,
  1944. 'typeClass' => $typeClass,
  1945. 'phpType' => $phpType,
  1946. 'compositor' => $compositor,
  1947. 'restrictionBase' => $restrictionBase,
  1948. 'elements' => $elements,
  1949. 'attrs' => $attrs,
  1950. 'arrayType' => $arrayType
  1951. );
  1952. $this->xdebug("addComplexType $name:");
  1953. $this->appendDebug($this->varDump($this->complexTypes[$name]));
  1954. }
  1955. /**
  1956. * adds a simple type to the schema
  1957. *
  1958. * @param string $name
  1959. * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
  1960. * @param string $typeClass (should always be simpleType)
  1961. * @param string $phpType (should always be scalar)
  1962. * @param array $enumeration array of values
  1963. * @access public
  1964. * @see nusoap_xmlschema
  1965. * @see getTypeDef
  1966. */
  1967. function addSimpleType($name, $restrictionBase = '', $typeClass = 'simpleType', $phpType = 'scalar', $enumeration = array())
  1968. {
  1969. $this->simpleTypes[$name] = array(
  1970. 'name' => $name,
  1971. 'typeClass' => $typeClass,
  1972. 'phpType' => $phpType,
  1973. 'type' => $restrictionBase,
  1974. 'enumeration' => $enumeration
  1975. );
  1976. $this->xdebug("addSimpleType $name:");
  1977. $this->appendDebug($this->varDump($this->simpleTypes[$name]));
  1978. }
  1979. /**
  1980. * adds an element to the schema
  1981. *
  1982. * @param array $attrs attributes that must include name and type
  1983. * @see nusoap_xmlschema
  1984. * @access public
  1985. */
  1986. function addElement($attrs)
  1987. {
  1988. if (!$this->getPrefix($attrs['type'])) {
  1989. $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
  1990. }
  1991. $this->elements[$attrs['name']] = $attrs;
  1992. $this->elements[$attrs['name']]['typeClass'] = 'element';
  1993. $this->xdebug("addElement " . $attrs['name']);
  1994. $this->appendDebug($this->varDump($this->elements[$attrs['name']]));
  1995. }
  1996. }
  1997. /**
  1998. * Backward compatibility
  1999. */
  2000. class XMLSchema extends nusoap_xmlschema
  2001. {
  2002. }
  2003. /**
  2004. * For creating serializable abstractions of native PHP types. This class
  2005. * allows element name/namespace, XSD type, and XML attributes to be
  2006. * associated with a value. This is extremely useful when WSDL is not
  2007. * used, but is also useful when WSDL is used with polymorphic types, including
  2008. * xsd:anyType and user-defined types.
  2009. *
  2010. * @author Dietrich Ayala <dietrich@ganx4.com>
  2011. * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  2012. * @access public
  2013. */
  2014. class soapval extends nusoap_base
  2015. {
  2016. /**
  2017. * The XML element name
  2018. *
  2019. * @var string
  2020. * @access private
  2021. */
  2022. var $name;
  2023. /**
  2024. * The XML type name (string or false)
  2025. *
  2026. * @var mixed
  2027. * @access private
  2028. */
  2029. var $type;
  2030. /**
  2031. * The PHP value
  2032. *
  2033. * @var mixed
  2034. * @access private
  2035. */
  2036. var $value;
  2037. /**
  2038. * The XML element namespace (string or false)
  2039. *
  2040. * @var mixed
  2041. * @access private
  2042. */
  2043. var $element_ns;
  2044. /**
  2045. * The XML type namespace (string or false)
  2046. *
  2047. * @var mixed
  2048. * @access private
  2049. */
  2050. var $type_ns;
  2051. /**
  2052. * The XML element attributes (array or false)
  2053. *
  2054. * @var mixed
  2055. * @access private
  2056. */
  2057. var $attributes;
  2058. /**
  2059. * constructor
  2060. *
  2061. * @param string $name optional name
  2062. * @param mixed $type optional type name
  2063. * @param mixed $value optional value
  2064. * @param mixed $element_ns optional namespace of value
  2065. * @param mixed $type_ns optional namespace of type
  2066. * @param mixed $attributes associative array of attributes to add to element serialization
  2067. * @access public
  2068. */
  2069. function __construct($name = 'soapval', $type = false, $value = -1, $element_ns = false, $type_ns = false, $attributes = false)
  2070. {
  2071. parent::__construct();
  2072. $this->name = $name;
  2073. $this->type = $type;
  2074. $this->value = $value;
  2075. $this->element_ns = $element_ns;
  2076. $this->type_ns = $type_ns;
  2077. $this->attributes = $attributes;
  2078. }
  2079. /**
  2080. * return serialized value
  2081. *
  2082. * @param string $use The WSDL use value (encoded|literal)
  2083. * @return string XML data
  2084. * @access public
  2085. */
  2086. function serialize($use = 'encoded')
  2087. {
  2088. return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
  2089. }
  2090. /**
  2091. * decodes a soapval object into a PHP native type
  2092. *
  2093. * @return mixed
  2094. * @access public
  2095. */
  2096. function decode()
  2097. {
  2098. return $this->value;
  2099. }
  2100. }
  2101. /**
  2102. * transport class for sending/receiving data via HTTP and HTTPS
  2103. * NOTE: PHP must be compiled with the CURL extension for HTTPS support
  2104. *
  2105. * @author Dietrich Ayala <dietrich@ganx4.com>
  2106. * @author Scott Nichol <snichol@users.sourceforge.net>
  2107. * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  2108. * @access public
  2109. */
  2110. class soap_transport_http extends nusoap_base
  2111. {
  2112. var $url = '';
  2113. var $uri = '';
  2114. var $digest_uri = '';
  2115. var $scheme = '';
  2116. var $host = '';
  2117. var $port = '';
  2118. var $path = '';
  2119. var $request_method = 'POST';
  2120. var $protocol_version = '1.0';
  2121. var $encoding = '';
  2122. var $outgoing_headers = array();
  2123. var $incoming_headers = array();
  2124. var $incoming_cookies = array();
  2125. var $outgoing_payload = '';
  2126. var $incoming_payload = '';
  2127. var $response_status_line; // HTTP response status line
  2128. var $useSOAPAction = true;
  2129. var $persistentConnection = false;
  2130. var $ch = false; // cURL handle
  2131. var $ch_options = array(); // cURL custom options
  2132. var $use_curl = false; // force cURL use
  2133. var $proxy = null; // proxy information (associative array)
  2134. var $username = '';
  2135. var $password = '';
  2136. var $authtype = '';
  2137. var $digestRequest = array();
  2138. var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
  2139. // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
  2140. // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
  2141. // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
  2142. // passphrase: SSL key password/passphrase
  2143. // certpassword: SSL certificate password
  2144. // verifypeer: default is 1
  2145. // verifyhost: default is 1
  2146. /**
  2147. * constructor
  2148. *
  2149. * @param string $url The URL to which to connect
  2150. * @param array $curl_options User-specified cURL options
  2151. * @param boolean $use_curl Whether to try to force cURL use
  2152. * @access public
  2153. */
  2154. function __construct($url, $curl_options = null, $use_curl = false)
  2155. {
  2156. parent::__construct();
  2157. $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
  2158. $this->appendDebug($this->varDump($curl_options));
  2159. $this->setURL($url);
  2160. if (is_array($curl_options)) {
  2161. $this->ch_options = $curl_options;
  2162. }
  2163. $this->use_curl = $use_curl;
  2164. preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
  2165. $this->setHeader('User-Agent', $this->title . '/' . $this->version . ' (' . $rev[1] . ')');
  2166. }
  2167. /**
  2168. * sets a cURL option
  2169. *
  2170. * @param mixed $option The cURL option (always integer?)
  2171. * @param mixed $value The cURL option value
  2172. * @access private
  2173. */
  2174. function setCurlOption($option, $value)
  2175. {
  2176. $this->debug("setCurlOption option=$option, value=");
  2177. $this->appendDebug($this->varDump($value));
  2178. curl_setopt($this->ch, $option, $value);
  2179. }
  2180. /**
  2181. * sets an HTTP header
  2182. *
  2183. * @param string $name The name of the header
  2184. * @param string $value The value of the header
  2185. * @access private
  2186. */
  2187. function setHeader($name, $value)
  2188. {
  2189. $this->outgoing_headers[$name] = $value;
  2190. $this->debug("set header $name: $value");
  2191. }
  2192. /**
  2193. * unsets an HTTP header
  2194. *
  2195. * @param string $name The name of the header
  2196. * @access private
  2197. */
  2198. function unsetHeader($name)
  2199. {
  2200. if (isset($this->outgoing_headers[$name])) {
  2201. $this->debug("unset header $name");
  2202. unset($this->outgoing_headers[$name]);
  2203. }
  2204. }
  2205. /**
  2206. * sets the URL to which to connect
  2207. *
  2208. * @param string $url The URL to which to connect
  2209. * @access private
  2210. */
  2211. function setURL($url)
  2212. {
  2213. $this->url = $url;
  2214. $u = parse_url($url);
  2215. foreach ($u as $k => $v) {
  2216. $this->debug("parsed URL $k = $v");
  2217. $this->$k = $v;
  2218. }
  2219. // add any GET params to path
  2220. if (isset($u['query']) && $u['query'] != '') {
  2221. $this->path .= '?' . $u['query'];
  2222. }
  2223. // set default port
  2224. if (!isset($u['port'])) {
  2225. if ($u['scheme'] == 'https') {
  2226. $this->port = 443;
  2227. } else {
  2228. $this->port = 80;
  2229. }
  2230. }
  2231. $this->uri = $this->path;
  2232. $this->digest_uri = $this->uri;
  2233. // build headers
  2234. if (!isset($u['port'])) {
  2235. $this->setHeader('Host', $this->host);
  2236. } else {
  2237. $this->setHeader('Host', $this->host . ':' . $this->port);
  2238. }
  2239. if (isset($u['user']) && $u['user'] != '') {
  2240. $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
  2241. }
  2242. }
  2243. /**
  2244. * gets the I/O method to use
  2245. *
  2246. * @return string I/O method to use (socket|curl|unknown)
  2247. * @access private
  2248. */
  2249. function io_method()
  2250. {
  2251. if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm')) {
  2252. return 'curl';
  2253. }
  2254. if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm')) {
  2255. return 'socket';
  2256. }
  2257. return 'unknown';
  2258. }
  2259. /**
  2260. * establish an HTTP connection
  2261. *
  2262. * @param integer $timeout set connection timeout in seconds
  2263. * @param integer $response_timeout set response timeout in seconds
  2264. * @return boolean true if connected, false if not
  2265. * @access private
  2266. */
  2267. function connect($connection_timeout = 0, $response_timeout = 30)
  2268. {
  2269. // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
  2270. // "regular" socket.
  2271. // TODO: disabled for now because OpenSSL must be *compiled* in (not just
  2272. // loaded), and until PHP5 stream_get_wrappers is not available.
  2273. // if ($this->scheme == 'https') {
  2274. // if (version_compare(phpversion(), '4.3.0') >= 0) {
  2275. // if (extension_loaded('openssl')) {
  2276. // $this->scheme = 'ssl';
  2277. // $this->debug('Using SSL over OpenSSL');
  2278. // }
  2279. // }
  2280. // }
  2281. $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
  2282. if ($this->io_method() == 'socket') {
  2283. if (!is_array($this->proxy)) {
  2284. $host = $this->host;
  2285. $port = $this->port;
  2286. } else {
  2287. $host = $this->proxy['host'];
  2288. $port = $this->proxy['port'];
  2289. }
  2290. // use persistent connection
  2291. if ($this->persistentConnection && isset($this->fp) && is_resource($this->fp)) {
  2292. if (!feof($this->fp)) {
  2293. $this->debug('Re-use persistent connection');
  2294. return true;
  2295. }
  2296. fclose($this->fp);
  2297. $this->debug('Closed persistent connection at EOF');
  2298. }
  2299. // munge host if using OpenSSL
  2300. if ($this->scheme == 'ssl') {
  2301. $host = 'ssl://' . $host;
  2302. }
  2303. $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
  2304. // open socket
  2305. if ($connection_timeout > 0) {
  2306. $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str, $connection_timeout);
  2307. } else {
  2308. $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str);
  2309. }
  2310. // test pointer
  2311. if (!$this->fp) {
  2312. $msg = 'Couldn\'t open socket connection to server ' . $this->url;
  2313. if ($this->errno) {
  2314. $msg .= ', Error (' . $this->errno . '): ' . $this->error_str;
  2315. } else {
  2316. $msg .= ' prior to connect(). This is often a problem looking up the host name.';
  2317. }
  2318. $this->debug($msg);
  2319. $this->setError($msg);
  2320. return false;
  2321. }
  2322. // set response timeout
  2323. $this->debug('set response timeout to ' . $response_timeout);
  2324. socket_set_timeout($this->fp, $response_timeout);
  2325. $this->debug('socket connected');
  2326. return true;
  2327. } elseif ($this->io_method() == 'curl') {
  2328. if (!extension_loaded('curl')) {
  2329. // $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
  2330. $this->setError('The PHP cURL Extension is required for HTTPS or NLTM. You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.');
  2331. return false;
  2332. }
  2333. // Avoid warnings when PHP does not have these options
  2334. if (defined('CURLOPT_CONNECTIONTIMEOUT')) {
  2335. $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
  2336. } else {
  2337. $CURLOPT_CONNECTIONTIMEOUT = 78;
  2338. }
  2339. if (defined('CURLOPT_HTTPAUTH')) {
  2340. $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
  2341. } else {
  2342. $CURLOPT_HTTPAUTH = 107;
  2343. }
  2344. if (defined('CURLOPT_PROXYAUTH')) {
  2345. $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
  2346. } else {
  2347. $CURLOPT_PROXYAUTH = 111;
  2348. }
  2349. if (defined('CURLAUTH_BASIC')) {
  2350. $CURLAUTH_BASIC = CURLAUTH_BASIC;
  2351. } else {
  2352. $CURLAUTH_BASIC = 1;
  2353. }
  2354. if (defined('CURLAUTH_DIGEST')) {
  2355. $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
  2356. } else {
  2357. $CURLAUTH_DIGEST = 2;
  2358. }
  2359. if (defined('CURLAUTH_NTLM')) {
  2360. $CURLAUTH_NTLM = CURLAUTH_NTLM;
  2361. } else {
  2362. $CURLAUTH_NTLM = 8;
  2363. }
  2364. $this->debug('connect using cURL');
  2365. // init CURL
  2366. $this->ch = curl_init();
  2367. // set url
  2368. $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
  2369. // add path
  2370. $hostURL .= $this->path;
  2371. $this->setCurlOption(CURLOPT_URL, $hostURL);
  2372. // follow location headers (re-directs)
  2373. if (ini_get('safe_mode') || ini_get('open_basedir')) {
  2374. $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
  2375. $this->debug('safe_mode = ');
  2376. $this->appendDebug($this->varDump(ini_get('safe_mode')));
  2377. $this->debug('open_basedir = ');
  2378. $this->appendDebug($this->varDump(ini_get('open_basedir')));
  2379. } else {
  2380. $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
  2381. }
  2382. // ask for headers in the response output
  2383. $this->setCurlOption(CURLOPT_HEADER, 1);
  2384. // ask for the response output as the return value
  2385. $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
  2386. // encode
  2387. // We manage this ourselves through headers and encoding
  2388. // if(function_exists('gzuncompress')){
  2389. // $this->setCurlOption(CURLOPT_ENCODING, 'deflate');
  2390. // }
  2391. // persistent connection
  2392. if ($this->persistentConnection) {
  2393. // I believe the following comment is now bogus, having applied to
  2394. // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
  2395. // The way we send data, we cannot use persistent connections, since
  2396. // there will be some "junk" at the end of our request.
  2397. //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
  2398. $this->persistentConnection = false;
  2399. $this->setHeader('Connection', 'close');
  2400. }
  2401. // set timeouts
  2402. if ($connection_timeout != 0) {
  2403. $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
  2404. }
  2405. if ($response_timeout != 0) {
  2406. $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
  2407. }
  2408. if ($this->scheme == 'https') {
  2409. $this->debug('set cURL SSL verify options');
  2410. // recent versions of cURL turn on peer/host checking by default,
  2411. // while PHP binaries are not compiled with a default location for the
  2412. // CA cert bundle, so disable peer/host checking.
  2413. //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
  2414. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
  2415. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
  2416. // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
  2417. if ($this->authtype == 'certificate') {
  2418. $this->debug('set cURL certificate options');
  2419. if (isset($this->certRequest['cainfofile'])) {
  2420. $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
  2421. }
  2422. if (isset($this->certRequest['verifypeer'])) {
  2423. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
  2424. } else {
  2425. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
  2426. }
  2427. if (isset($this->certRequest['verifyhost'])) {
  2428. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
  2429. } else {
  2430. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
  2431. }
  2432. if (isset($this->certRequest['sslcertfile'])) {
  2433. $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
  2434. }
  2435. if (isset($this->certRequest['sslkeyfile'])) {
  2436. $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
  2437. }
  2438. if (isset($this->certRequest['passphrase'])) {
  2439. $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
  2440. }
  2441. if (isset($this->certRequest['certpassword'])) {
  2442. $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
  2443. }
  2444. }
  2445. }
  2446. if ($this->authtype && ($this->authtype != 'certificate')) {
  2447. if ($this->username) {
  2448. $this->debug('set cURL username/password');
  2449. $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
  2450. }
  2451. if ($this->authtype == 'basic') {
  2452. $this->debug('set cURL for Basic authentication');
  2453. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
  2454. }
  2455. if ($this->authtype == 'digest') {
  2456. $this->debug('set cURL for digest authentication');
  2457. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
  2458. }
  2459. if ($this->authtype == 'ntlm') {
  2460. $this->debug('set cURL for NTLM authentication');
  2461. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
  2462. }
  2463. }
  2464. if (is_array($this->proxy)) {
  2465. $this->debug('set cURL proxy options');
  2466. if ($this->proxy['port'] != '') {
  2467. $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'] . ':' . $this->proxy['port']);
  2468. } else {
  2469. $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
  2470. }
  2471. if ($this->proxy['username'] || $this->proxy['password']) {
  2472. $this->debug('set cURL proxy authentication options');
  2473. $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'] . ':' . $this->proxy['password']);
  2474. if ($this->proxy['authtype'] == 'basic') {
  2475. $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
  2476. }
  2477. if ($this->proxy['authtype'] == 'ntlm') {
  2478. $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
  2479. }
  2480. }
  2481. }
  2482. $this->debug('cURL connection set up');
  2483. return true;
  2484. } else {
  2485. $this->setError('Unknown scheme ' . $this->scheme);
  2486. $this->debug('Unknown scheme ' . $this->scheme);
  2487. return false;
  2488. }
  2489. }
  2490. /**
  2491. * sends the SOAP request and gets the SOAP response via HTTP[S]
  2492. *
  2493. * @param string $data message data
  2494. * @param integer $timeout set connection timeout in seconds
  2495. * @param integer $response_timeout set response timeout in seconds
  2496. * @param array $cookies cookies to send
  2497. * @return string data
  2498. * @access public
  2499. */
  2500. function send($data, $timeout = 0, $response_timeout = 30, $cookies = null)
  2501. {
  2502. $this->debug('entered send() with data of length: ' . strlen($data));
  2503. $this->tryagain = true;
  2504. $tries = 0;
  2505. while ($this->tryagain) {
  2506. $this->tryagain = false;
  2507. if ($tries++ < 2) {
  2508. // make connnection
  2509. if (!$this->connect($timeout, $response_timeout)) {
  2510. return false;
  2511. }
  2512. // send request
  2513. if (!$this->sendRequest($data, $cookies)) {
  2514. return false;
  2515. }
  2516. // get response
  2517. $respdata = $this->getResponse();
  2518. } else {
  2519. $this->setError("Too many tries to get an OK response ($this->response_status_line)");
  2520. }
  2521. }
  2522. $this->debug('end of send()');
  2523. return $respdata;
  2524. }
  2525. /**
  2526. * sends the SOAP request and gets the SOAP response via HTTPS using CURL
  2527. *
  2528. * @param string $data message data
  2529. * @param integer $timeout set connection timeout in seconds
  2530. * @param integer $response_timeout set response timeout in seconds
  2531. * @param array $cookies cookies to send
  2532. * @return string data
  2533. * @access public
  2534. * @deprecated
  2535. */
  2536. function sendHTTPS($data, $timeout = 0, $response_timeout = 30, $cookies = null)
  2537. {
  2538. return $this->send($data, $timeout, $response_timeout, $cookies);
  2539. }
  2540. /**
  2541. * if authenticating, set user credentials here
  2542. *
  2543. * @param string $username
  2544. * @param string $password
  2545. * @param string $authtype (basic|digest|certificate|ntlm)
  2546. * @param array $digestRequest (keys must be nonce, nc, realm, qop)
  2547. * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
  2548. * @access public
  2549. */
  2550. function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array())
  2551. {
  2552. $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
  2553. $this->appendDebug($this->varDump($digestRequest));
  2554. $this->debug("certRequest=");
  2555. $this->appendDebug($this->varDump($certRequest));
  2556. // cf. RFC 2617
  2557. if ($authtype == 'basic') {
  2558. $this->setHeader('Authorization', 'Basic ' . base64_encode(str_replace(':', '', $username) . ':' . $password));
  2559. } elseif ($authtype == 'digest') {
  2560. if (isset($digestRequest['nonce'])) {
  2561. $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
  2562. // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
  2563. // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
  2564. $A1 = $username . ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
  2565. // H(A1) = MD5(A1)
  2566. $HA1 = md5($A1);
  2567. // A2 = Method ":" digest-uri-value
  2568. $A2 = $this->request_method . ':' . $this->digest_uri;
  2569. // H(A2)
  2570. $HA2 = md5($A2);
  2571. // KD(secret, data) = H(concat(secret, ":", data))
  2572. // if qop == auth:
  2573. // request-digest = <"> < KD ( H(A1), unq(nonce-value)
  2574. // ":" nc-value
  2575. // ":" unq(cnonce-value)
  2576. // ":" unq(qop-value)
  2577. // ":" H(A2)
  2578. // ) <">
  2579. // if qop is missing,
  2580. // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
  2581. $unhashedDigest = '';
  2582. $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
  2583. $cnonce = $nonce;
  2584. if ($digestRequest['qop'] != '') {
  2585. $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
  2586. } else {
  2587. $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
  2588. }
  2589. $hashedDigest = md5($unhashedDigest);
  2590. $opaque = '';
  2591. if (isset($digestRequest['opaque'])) {
  2592. $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
  2593. }
  2594. $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"');
  2595. }
  2596. } elseif ($authtype == 'certificate') {
  2597. $this->certRequest = $certRequest;
  2598. $this->debug('Authorization header not set for certificate');
  2599. } elseif ($authtype == 'ntlm') {
  2600. // do nothing
  2601. $this->debug('Authorization header not set for ntlm');
  2602. }
  2603. $this->username = $username;
  2604. $this->password = $password;
  2605. $this->authtype = $authtype;
  2606. $this->digestRequest = $digestRequest;
  2607. }
  2608. /**
  2609. * set the soapaction value
  2610. *
  2611. * @param string $soapaction
  2612. * @access public
  2613. */
  2614. function setSOAPAction($soapaction)
  2615. {
  2616. $this->setHeader('SOAPAction', '"' . $soapaction . '"');
  2617. }
  2618. /**
  2619. * use http encoding
  2620. *
  2621. * @param string $enc encoding style. supported values: gzip, deflate, or both
  2622. * @access public
  2623. */
  2624. function setEncoding($enc = 'gzip, deflate')
  2625. {
  2626. if (function_exists('gzdeflate')) {
  2627. $this->protocol_version = '1.1';
  2628. $this->setHeader('Accept-Encoding', $enc);
  2629. if (!isset($this->outgoing_headers['Connection'])) {
  2630. $this->setHeader('Connection', 'close');
  2631. $this->persistentConnection = false;
  2632. }
  2633. // deprecated as of PHP 5.3.0
  2634. //set_magic_quotes_runtime(0);
  2635. $this->encoding = $enc;
  2636. }
  2637. }
  2638. /**
  2639. * set proxy info here
  2640. *
  2641. * @param string $proxyhost use an empty string to remove proxy
  2642. * @param string $proxyport
  2643. * @param string $proxyusername
  2644. * @param string $proxypassword
  2645. * @param string $proxyauthtype (basic|ntlm)
  2646. * @access public
  2647. */
  2648. function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic')
  2649. {
  2650. if ($proxyhost) {
  2651. $this->proxy = array(
  2652. 'host' => $proxyhost,
  2653. 'port' => $proxyport,
  2654. 'username' => $proxyusername,
  2655. 'password' => $proxypassword,
  2656. 'authtype' => $proxyauthtype
  2657. );
  2658. if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') {
  2659. $this->setHeader('Proxy-Authorization', ' Basic ' . base64_encode($proxyusername . ':' . $proxypassword));
  2660. }
  2661. } else {
  2662. $this->debug('remove proxy');
  2663. $proxy = null;
  2664. unsetHeader('Proxy-Authorization');
  2665. }
  2666. }
  2667. /**
  2668. * Test if the given string starts with a header that is to be skipped.
  2669. * Skippable headers result from chunked transfer and proxy requests.
  2670. *
  2671. * @param string $data The string to check.
  2672. * @returns boolean Whether a skippable header was found.
  2673. * @access private
  2674. */
  2675. function isSkippableCurlHeader(&$data)
  2676. {
  2677. $skipHeaders = array('HTTP/1.1 100',
  2678. 'HTTP/1.0 301',
  2679. 'HTTP/1.1 301',
  2680. 'HTTP/1.0 302',
  2681. 'HTTP/1.1 302',
  2682. 'HTTP/1.0 401',
  2683. 'HTTP/1.1 401',
  2684. 'HTTP/1.0 200 Connection established');
  2685. foreach ($skipHeaders as $hd) {
  2686. $prefix = substr($data, 0, strlen($hd));
  2687. if ($prefix == $hd) {
  2688. return true;
  2689. }
  2690. }
  2691. return false;
  2692. }
  2693. /**
  2694. * decode a string that is encoded w/ "chunked' transfer encoding
  2695. * as defined in RFC2068 19.4.6
  2696. *
  2697. * @param string $buffer
  2698. * @param string $lb
  2699. * @returns string
  2700. * @access public
  2701. * @deprecated
  2702. */
  2703. function decodeChunked($buffer, $lb)
  2704. {
  2705. // length := 0
  2706. $length = 0;
  2707. $new = '';
  2708. // read chunk-size, chunk-extension (if any) and CRLF
  2709. // get the position of the linebreak
  2710. $chunkend = strpos($buffer, $lb);
  2711. if ($chunkend == false) {
  2712. $this->debug('no linebreak found in decodeChunked');
  2713. return $new;
  2714. }
  2715. $temp = substr($buffer, 0, $chunkend);
  2716. $chunk_size = hexdec(trim($temp));
  2717. $chunkstart = $chunkend + strlen($lb);
  2718. // while (chunk-size > 0) {
  2719. while ($chunk_size > 0) {
  2720. $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
  2721. $chunkend = strpos($buffer, $lb, $chunkstart + $chunk_size);
  2722. // Just in case we got a broken connection
  2723. if ($chunkend == false) {
  2724. $chunk = substr($buffer, $chunkstart);
  2725. // append chunk-data to entity-body
  2726. $new .= $chunk;
  2727. $length += strlen($chunk);
  2728. break;
  2729. }
  2730. // read chunk-data and CRLF
  2731. $chunk = substr($buffer, $chunkstart, $chunkend - $chunkstart);
  2732. // append chunk-data to entity-body
  2733. $new .= $chunk;
  2734. // length := length + chunk-size
  2735. $length += strlen($chunk);
  2736. // read chunk-size and CRLF
  2737. $chunkstart = $chunkend + strlen($lb);
  2738. $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
  2739. if ($chunkend == false) {
  2740. break; //Just in case we got a broken connection
  2741. }
  2742. $temp = substr($buffer, $chunkstart, $chunkend - $chunkstart);
  2743. $chunk_size = hexdec(trim($temp));
  2744. $chunkstart = $chunkend;
  2745. }
  2746. return $new;
  2747. }
  2748. /**
  2749. * Writes the payload, including HTTP headers, to $this->outgoing_payload.
  2750. *
  2751. * @param string $data HTTP body
  2752. * @param string $cookie_str data for HTTP Cookie header
  2753. * @return void
  2754. * @access private
  2755. */
  2756. function buildPayload($data, $cookie_str = '')
  2757. {
  2758. // Note: for cURL connections, $this->outgoing_payload is ignored,
  2759. // as is the Content-Length header, but these are still created as
  2760. // debugging guides.
  2761. // add content-length header
  2762. if ($this->request_method != 'GET') {
  2763. $this->setHeader('Content-Length', strlen($data));
  2764. }
  2765. // start building outgoing payload:
  2766. if ($this->proxy) {
  2767. $uri = $this->url;
  2768. } else {
  2769. $uri = $this->uri;
  2770. }
  2771. $req = "$this->request_method $uri HTTP/$this->protocol_version";
  2772. $this->debug("HTTP request: $req");
  2773. $this->outgoing_payload = "$req\r\n";
  2774. // loop thru headers, serializing
  2775. foreach ($this->outgoing_headers as $k => $v) {
  2776. $hdr = $k . ': ' . $v;
  2777. $this->debug("HTTP header: $hdr");
  2778. $this->outgoing_payload .= "$hdr\r\n";
  2779. }
  2780. // add any cookies
  2781. if ($cookie_str != '') {
  2782. $hdr = 'Cookie: ' . $cookie_str;
  2783. $this->debug("HTTP header: $hdr");
  2784. $this->outgoing_payload .= "$hdr\r\n";
  2785. }
  2786. // header/body separator
  2787. $this->outgoing_payload .= "\r\n";
  2788. // add data
  2789. $this->outgoing_payload .= $data;
  2790. }
  2791. /**
  2792. * sends the SOAP request via HTTP[S]
  2793. *
  2794. * @param string $data message data
  2795. * @param array $cookies cookies to send
  2796. * @return boolean true if OK, false if problem
  2797. * @access private
  2798. */
  2799. function sendRequest($data, $cookies = null)
  2800. {
  2801. // build cookie string
  2802. $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
  2803. // build payload
  2804. $this->buildPayload($data, $cookie_str);
  2805. if ($this->io_method() == 'socket') {
  2806. // send payload
  2807. if (!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
  2808. $this->setError('couldn\'t write message data to socket');
  2809. $this->debug('couldn\'t write message data to socket');
  2810. return false;
  2811. }
  2812. $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
  2813. return true;
  2814. } elseif ($this->io_method() == 'curl') {
  2815. // set payload
  2816. // cURL does say this should only be the verb, and in fact it
  2817. // turns out that the URI and HTTP version are appended to this, which
  2818. // some servers refuse to work with (so we no longer use this method!)
  2819. //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
  2820. $curl_headers = array();
  2821. foreach ($this->outgoing_headers as $k => $v) {
  2822. if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
  2823. $this->debug("Skip cURL header $k: $v");
  2824. } else {
  2825. $curl_headers[] = "$k: $v";
  2826. }
  2827. }
  2828. if ($cookie_str != '') {
  2829. $curl_headers[] = 'Cookie: ' . $cookie_str;
  2830. }
  2831. $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
  2832. $this->debug('set cURL HTTP headers');
  2833. if ($this->request_method == "POST") {
  2834. $this->setCurlOption(CURLOPT_POST, 1);
  2835. $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
  2836. $this->debug('set cURL POST data');
  2837. } else {
  2838. }
  2839. // insert custom user-set cURL options
  2840. foreach ($this->ch_options as $key => $val) {
  2841. $this->setCurlOption($key, $val);
  2842. }
  2843. $this->debug('set cURL payload');
  2844. return true;
  2845. }
  2846. }
  2847. /**
  2848. * gets the SOAP response via HTTP[S]
  2849. *
  2850. * @return string the response (also sets member variables like incoming_payload)
  2851. * @access private
  2852. */
  2853. function getResponse()
  2854. {
  2855. $this->incoming_payload = '';
  2856. if ($this->io_method() == 'socket') {
  2857. // loop until headers have been retrieved
  2858. $data = '';
  2859. while (!isset($lb)) {
  2860. // We might EOF during header read.
  2861. if (feof($this->fp)) {
  2862. $this->incoming_payload = $data;
  2863. $this->debug('found no headers before EOF after length ' . strlen($data));
  2864. $this->debug("received before EOF:\n" . $data);
  2865. $this->setError('server failed to send headers');
  2866. return false;
  2867. }
  2868. $tmp = fgets($this->fp, 256);
  2869. $tmplen = strlen($tmp);
  2870. $this->debug("read line of $tmplen bytes: " . trim($tmp));
  2871. if ($tmplen == 0) {
  2872. $this->incoming_payload = $data;
  2873. $this->debug('socket read of headers timed out after length ' . strlen($data));
  2874. $this->debug("read before timeout: " . $data);
  2875. $this->setError('socket read of headers timed out');
  2876. return false;
  2877. }
  2878. $data .= $tmp;
  2879. $pos = strpos($data, "\r\n\r\n");
  2880. if ($pos > 1) {
  2881. $lb = "\r\n";
  2882. } else {
  2883. $pos = strpos($data, "\n\n");
  2884. if ($pos > 1) {
  2885. $lb = "\n";
  2886. }
  2887. }
  2888. // remove 100 headers
  2889. if (isset($lb) && preg_match('/^HTTP\/1.1 100/', $data)) {
  2890. unset($lb);
  2891. $data = '';
  2892. }//
  2893. }
  2894. // store header data
  2895. $this->incoming_payload .= $data;
  2896. $this->debug('found end of headers after length ' . strlen($data));
  2897. // process headers
  2898. $header_data = trim(substr($data, 0, $pos));
  2899. $header_array = explode($lb, $header_data);
  2900. $this->incoming_headers = array();
  2901. $this->incoming_cookies = array();
  2902. foreach ($header_array as $header_line) {
  2903. $arr = explode(':', $header_line, 2);
  2904. if (count($arr) > 1) {
  2905. $header_name = strtolower(trim($arr[0]));
  2906. $this->incoming_headers[$header_name] = trim($arr[1]);
  2907. if ($header_name == 'set-cookie') {
  2908. // TODO: allow multiple cookies from parseCookie
  2909. $cookie = $this->parseCookie(trim($arr[1]));
  2910. if ($cookie) {
  2911. $this->incoming_cookies[] = $cookie;
  2912. $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
  2913. } else {
  2914. $this->debug('did not find cookie in ' . trim($arr[1]));
  2915. }
  2916. }
  2917. } elseif (isset($header_name)) {
  2918. // append continuation line to previous header
  2919. $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
  2920. }
  2921. }
  2922. // loop until msg has been received
  2923. if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
  2924. $content_length = 2147483647; // ignore any content-length header
  2925. $chunked = true;
  2926. $this->debug("want to read chunked content");
  2927. } elseif (isset($this->incoming_headers['content-length'])) {
  2928. $content_length = $this->incoming_headers['content-length'];
  2929. $chunked = false;
  2930. $this->debug("want to read content of length $content_length");
  2931. } else {
  2932. $content_length = 2147483647;
  2933. $chunked = false;
  2934. $this->debug("want to read content to EOF");
  2935. }
  2936. $data = '';
  2937. do {
  2938. if ($chunked) {
  2939. $tmp = fgets($this->fp, 256);
  2940. $tmplen = strlen($tmp);
  2941. $this->debug("read chunk line of $tmplen bytes");
  2942. if ($tmplen == 0) {
  2943. $this->incoming_payload = $data;
  2944. $this->debug('socket read of chunk length timed out after length ' . strlen($data));
  2945. $this->debug("read before timeout:\n" . $data);
  2946. $this->setError('socket read of chunk length timed out');
  2947. return false;
  2948. }
  2949. $content_length = hexdec(trim($tmp));
  2950. $this->debug("chunk length $content_length");
  2951. }
  2952. $strlen = 0;
  2953. while (($strlen < $content_length) && (!feof($this->fp))) {
  2954. $readlen = min(8192, $content_length - $strlen);
  2955. $tmp = fread($this->fp, $readlen);
  2956. $tmplen = strlen($tmp);
  2957. $this->debug("read buffer of $tmplen bytes");
  2958. if (($tmplen == 0) && (!feof($this->fp))) {
  2959. $this->incoming_payload = $data;
  2960. $this->debug('socket read of body timed out after length ' . strlen($data));
  2961. $this->debug("read before timeout:\n" . $data);
  2962. $this->setError('socket read of body timed out');
  2963. return false;
  2964. }
  2965. $strlen += $tmplen;
  2966. $data .= $tmp;
  2967. }
  2968. if ($chunked && ($content_length > 0)) {
  2969. $tmp = fgets($this->fp, 256);
  2970. $tmplen = strlen($tmp);
  2971. $this->debug("read chunk terminator of $tmplen bytes");
  2972. if ($tmplen == 0) {
  2973. $this->incoming_payload = $data;
  2974. $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
  2975. $this->debug("read before timeout:\n" . $data);
  2976. $this->setError('socket read of chunk terminator timed out');
  2977. return false;
  2978. }
  2979. }
  2980. } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
  2981. if (feof($this->fp)) {
  2982. $this->debug('read to EOF');
  2983. }
  2984. $this->debug('read body of length ' . strlen($data));
  2985. $this->incoming_payload .= $data;
  2986. $this->debug('received a total of ' . strlen($this->incoming_payload) . ' bytes of data from server');
  2987. // close filepointer
  2988. if (
  2989. (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
  2990. (!$this->persistentConnection) || feof($this->fp)
  2991. ) {
  2992. fclose($this->fp);
  2993. $this->fp = false;
  2994. $this->debug('closed socket');
  2995. }
  2996. // connection was closed unexpectedly
  2997. if ($this->incoming_payload == '') {
  2998. $this->setError('no response from server');
  2999. return false;
  3000. }
  3001. // decode transfer-encoding
  3002. // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
  3003. // if(!$data = $this->decodeChunked($data, $lb)){
  3004. // $this->setError('Decoding of chunked data failed');
  3005. // return false;
  3006. // }
  3007. //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
  3008. // set decoded payload
  3009. // $this->incoming_payload = $header_data.$lb.$lb.$data;
  3010. // }
  3011. } elseif ($this->io_method() == 'curl') {
  3012. // send and receive
  3013. $this->debug('send and receive with cURL');
  3014. $this->incoming_payload = curl_exec($this->ch);
  3015. $data = $this->incoming_payload;
  3016. $cErr = curl_error($this->ch);
  3017. if ($cErr != '') {
  3018. $err = 'cURL ERROR: ' . curl_errno($this->ch) . ': ' . $cErr . '<br>';
  3019. // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
  3020. foreach (curl_getinfo($this->ch) as $k => $v) {
  3021. if (is_array($v)) {
  3022. $this->debug("$k: " . json_encode($v));
  3023. } else {
  3024. $this->debug("$k: $v<br>");
  3025. }
  3026. }
  3027. $this->debug($err);
  3028. $this->setError($err);
  3029. curl_close($this->ch);
  3030. return false;
  3031. } else {
  3032. //echo '<pre>';
  3033. //var_dump(curl_getinfo($this->ch));
  3034. //echo '</pre>';
  3035. }
  3036. // close curl
  3037. $this->debug('No cURL error, closing cURL');
  3038. curl_close($this->ch);
  3039. // try removing skippable headers
  3040. $savedata = $data;
  3041. while ($this->isSkippableCurlHeader($data)) {
  3042. $this->debug("Found HTTP header to skip");
  3043. if ($pos = strpos($data, "\r\n\r\n")) {
  3044. $data = ltrim(substr($data, $pos));
  3045. } elseif ($pos = strpos($data, "\n\n")) {
  3046. $data = ltrim(substr($data, $pos));
  3047. }
  3048. }
  3049. if ($data == '') {
  3050. // have nothing left; just remove 100 header(s)
  3051. $data = $savedata;
  3052. while (preg_match('/^HTTP\/1.1 100/', $data)) {
  3053. if ($pos = strpos($data, "\r\n\r\n")) {
  3054. $data = ltrim(substr($data, $pos));
  3055. } elseif ($pos = strpos($data, "\n\n")) {
  3056. $data = ltrim(substr($data, $pos));
  3057. }
  3058. }
  3059. }
  3060. // separate content from HTTP headers
  3061. if ($pos = strpos($data, "\r\n\r\n")) {
  3062. $lb = "\r\n";
  3063. } elseif ($pos = strpos($data, "\n\n")) {
  3064. $lb = "\n";
  3065. } else {
  3066. $this->debug('no proper separation of headers and document');
  3067. $this->setError('no proper separation of headers and document');
  3068. return false;
  3069. }
  3070. $header_data = trim(substr($data, 0, $pos));
  3071. $header_array = explode($lb, $header_data);
  3072. $data = ltrim(substr($data, $pos));
  3073. $this->debug('found proper separation of headers and document');
  3074. $this->debug('cleaned data, stringlen: ' . strlen($data));
  3075. // clean headers
  3076. foreach ($header_array as $header_line) {
  3077. $arr = explode(':', $header_line, 2);
  3078. if (count($arr) > 1) {
  3079. $header_name = strtolower(trim($arr[0]));
  3080. $this->incoming_headers[$header_name] = trim($arr[1]);
  3081. if ($header_name == 'set-cookie') {
  3082. // TODO: allow multiple cookies from parseCookie
  3083. $cookie = $this->parseCookie(trim($arr[1]));
  3084. if ($cookie) {
  3085. $this->incoming_cookies[] = $cookie;
  3086. $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
  3087. } else {
  3088. $this->debug('did not find cookie in ' . trim($arr[1]));
  3089. }
  3090. }
  3091. } elseif (isset($header_name)) {
  3092. // append continuation line to previous header
  3093. $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
  3094. }
  3095. }
  3096. }
  3097. $this->response_status_line = $header_array[0];
  3098. $arr = explode(' ', $this->response_status_line, 3);
  3099. $http_version = $arr[0];
  3100. $http_status = intval($arr[1]);
  3101. $http_reason = count($arr) > 2 ? $arr[2] : '';
  3102. // see if we need to resend the request with http digest authentication
  3103. if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
  3104. $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
  3105. $this->setURL($this->incoming_headers['location']);
  3106. $this->tryagain = true;
  3107. return false;
  3108. }
  3109. // see if we need to resend the request with http digest authentication
  3110. if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
  3111. $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
  3112. if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
  3113. $this->debug('Server wants digest authentication');
  3114. // remove "Digest " from our elements
  3115. $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
  3116. // parse elements into array
  3117. $digestElements = explode(',', $digestString);
  3118. foreach ($digestElements as $val) {
  3119. $tempElement = explode('=', trim($val), 2);
  3120. $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
  3121. }
  3122. // should have (at least) qop, realm, nonce
  3123. if (isset($digestRequest['nonce'])) {
  3124. $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
  3125. $this->tryagain = true;
  3126. return false;
  3127. }
  3128. }
  3129. $this->debug('HTTP authentication failed');
  3130. $this->setError('HTTP authentication failed');
  3131. return false;
  3132. }
  3133. if (
  3134. ($http_status >= 300 && $http_status <= 307) ||
  3135. ($http_status >= 400 && $http_status <= 417) ||
  3136. ($http_status >= 501 && $http_status <= 505)
  3137. ) {
  3138. $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
  3139. return false;
  3140. }
  3141. // decode content-encoding
  3142. if (isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != '') {
  3143. if (strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip') {
  3144. // if decoding works, use it. else assume data wasn't gzencoded
  3145. if (function_exists('gzinflate')) {
  3146. //$timer->setMarker('starting decoding of gzip/deflated content');
  3147. // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
  3148. // this means there are no Zlib headers, although there should be
  3149. $this->debug('The gzinflate function exists');
  3150. $datalen = strlen($data);
  3151. if ($this->incoming_headers['content-encoding'] == 'deflate') {
  3152. if ($degzdata = @gzinflate($data)) {
  3153. $data = $degzdata;
  3154. $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
  3155. if (strlen($data) < $datalen) {
  3156. // test for the case that the payload has been compressed twice
  3157. $this->debug('The inflated payload is smaller than the gzipped one; try again');
  3158. if ($degzdata = @gzinflate($data)) {
  3159. $data = $degzdata;
  3160. $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
  3161. }
  3162. }
  3163. } else {
  3164. $this->debug('Error using gzinflate to inflate the payload');
  3165. $this->setError('Error using gzinflate to inflate the payload');
  3166. }
  3167. } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
  3168. if ($degzdata = @gzinflate(substr($data, 10))) { // do our best
  3169. $data = $degzdata;
  3170. $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
  3171. if (strlen($data) < $datalen) {
  3172. // test for the case that the payload has been compressed twice
  3173. $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
  3174. if ($degzdata = @gzinflate(substr($data, 10))) {
  3175. $data = $degzdata;
  3176. $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
  3177. }
  3178. }
  3179. } else {
  3180. $this->debug('Error using gzinflate to un-gzip the payload');
  3181. $this->setError('Error using gzinflate to un-gzip the payload');
  3182. }
  3183. }
  3184. //$timer->setMarker('finished decoding of gzip/deflated content');
  3185. //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
  3186. // set decoded payload
  3187. $this->incoming_payload = $header_data . $lb . $lb . $data;
  3188. } else {
  3189. $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
  3190. $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
  3191. }
  3192. } else {
  3193. $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
  3194. $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
  3195. }
  3196. } else {
  3197. $this->debug('No Content-Encoding header');
  3198. }
  3199. if (strlen($data) == 0) {
  3200. $this->debug('no data after headers!');
  3201. $this->setError('no data present after HTTP headers');
  3202. return false;
  3203. }
  3204. return $data;
  3205. }
  3206. /**
  3207. * sets the content-type for the SOAP message to be sent
  3208. *
  3209. * @param string $type the content type, MIME style
  3210. * @param mixed $charset character set used for encoding (or false)
  3211. * @access public
  3212. */
  3213. function setContentType($type, $charset = false)
  3214. {
  3215. $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
  3216. }
  3217. /**
  3218. * specifies that an HTTP persistent connection should be used
  3219. *
  3220. * @return boolean whether the request was honored by this method.
  3221. * @access public
  3222. */
  3223. function usePersistentConnection()
  3224. {
  3225. if (isset($this->outgoing_headers['Accept-Encoding'])) {
  3226. return false;
  3227. }
  3228. $this->protocol_version = '1.1';
  3229. $this->persistentConnection = true;
  3230. $this->setHeader('Connection', 'Keep-Alive');
  3231. return true;
  3232. }
  3233. /**
  3234. * parse an incoming Cookie into it's parts
  3235. *
  3236. * @param string $cookie_str content of cookie
  3237. * @return array with data of that cookie
  3238. * @access private
  3239. */
  3240. /*
  3241. * TODO: allow a Set-Cookie string to be parsed into multiple cookies
  3242. */
  3243. function parseCookie($cookie_str)
  3244. {
  3245. $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
  3246. $data = preg_split('/;/', $cookie_str);
  3247. $value_str = $data[0];
  3248. $cookie_param = 'domain=';
  3249. $start = strpos($cookie_str, $cookie_param);
  3250. if ($start > 0) {
  3251. $domain = substr($cookie_str, $start + strlen($cookie_param));
  3252. $domain = substr($domain, 0, strpos($domain, ';'));
  3253. } else {
  3254. $domain = '';
  3255. }
  3256. $cookie_param = 'expires=';
  3257. $start = strpos($cookie_str, $cookie_param);
  3258. if ($start > 0) {
  3259. $expires = substr($cookie_str, $start + strlen($cookie_param));
  3260. $expires = substr($expires, 0, strpos($expires, ';'));
  3261. } else {
  3262. $expires = '';
  3263. }
  3264. $cookie_param = 'path=';
  3265. $start = strpos($cookie_str, $cookie_param);
  3266. if ($start > 0) {
  3267. $path = substr($cookie_str, $start + strlen($cookie_param));
  3268. $path = substr($path, 0, strpos($path, ';'));
  3269. } else {
  3270. $path = '/';
  3271. }
  3272. $cookie_param = ';secure;';
  3273. if (strpos($cookie_str, $cookie_param) !== false) {
  3274. $secure = true;
  3275. } else {
  3276. $secure = false;
  3277. }
  3278. $sep_pos = strpos($value_str, '=');
  3279. if ($sep_pos) {
  3280. $name = substr($value_str, 0, $sep_pos);
  3281. $value = substr($value_str, $sep_pos + 1);
  3282. $cookie = array('name' => $name,
  3283. 'value' => $value,
  3284. 'domain' => $domain,
  3285. 'path' => $path,
  3286. 'expires' => $expires,
  3287. 'secure' => $secure
  3288. );
  3289. return $cookie;
  3290. }
  3291. return false;
  3292. }
  3293. /**
  3294. * sort out cookies for the current request
  3295. *
  3296. * @param array $cookies array with all cookies
  3297. * @param boolean $secure is the send-content secure or not?
  3298. * @return string for Cookie-HTTP-Header
  3299. * @access private
  3300. */
  3301. function getCookiesForRequest($cookies, $secure = false)
  3302. {
  3303. $cookie_str = '';
  3304. if ((!is_null($cookies)) && (is_array($cookies))) {
  3305. foreach ($cookies as $cookie) {
  3306. if (!is_array($cookie)) {
  3307. continue;
  3308. }
  3309. $this->debug("check cookie for validity: " . $cookie['name'] . '=' . $cookie['value']);
  3310. if ((isset($cookie['expires'])) && (!empty($cookie['expires']))) {
  3311. if (strtotime($cookie['expires']) <= time()) {
  3312. $this->debug('cookie has expired');
  3313. continue;
  3314. }
  3315. }
  3316. if ((isset($cookie['domain'])) && (!empty($cookie['domain']))) {
  3317. $domain = preg_quote($cookie['domain']);
  3318. if (!preg_match("'.*$domain$'i", $this->host)) {
  3319. $this->debug('cookie has different domain');
  3320. continue;
  3321. }
  3322. }
  3323. if ((isset($cookie['path'])) && (!empty($cookie['path']))) {
  3324. $path = preg_quote($cookie['path']);
  3325. if (!preg_match("'^$path.*'i", $this->path)) {
  3326. $this->debug('cookie is for a different path');
  3327. continue;
  3328. }
  3329. }
  3330. if ((!$secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
  3331. $this->debug('cookie is secure, transport is not');
  3332. continue;
  3333. }
  3334. $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
  3335. $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
  3336. }
  3337. }
  3338. return $cookie_str;
  3339. }
  3340. }
  3341. /**
  3342. *
  3343. * nusoap_server allows the user to create a SOAP server
  3344. * that is capable of receiving messages and returning responses
  3345. *
  3346. * @author Dietrich Ayala <dietrich@ganx4.com>
  3347. * @author Scott Nichol <snichol@users.sourceforge.net>
  3348. * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  3349. * @access public
  3350. */
  3351. class nusoap_server extends nusoap_base
  3352. {
  3353. /**
  3354. * HTTP headers of request
  3355. *
  3356. * @var array
  3357. * @access private
  3358. */
  3359. var $headers = array();
  3360. /**
  3361. * HTTP request
  3362. *
  3363. * @var string
  3364. * @access private
  3365. */
  3366. var $request = '';
  3367. /**
  3368. * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
  3369. *
  3370. * @var string
  3371. * @access public
  3372. */
  3373. var $requestHeaders = '';
  3374. /**
  3375. * SOAP Headers from request (parsed)
  3376. *
  3377. * @var mixed
  3378. * @access public
  3379. */
  3380. var $requestHeader = null;
  3381. /**
  3382. * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
  3383. *
  3384. * @var string
  3385. * @access public
  3386. */
  3387. var $document = '';
  3388. /**
  3389. * SOAP payload for request (text)
  3390. *
  3391. * @var string
  3392. * @access public
  3393. */
  3394. var $requestSOAP = '';
  3395. /**
  3396. * requested method namespace URI
  3397. *
  3398. * @var string
  3399. * @access private
  3400. */
  3401. var $methodURI = '';
  3402. /**
  3403. * name of method requested
  3404. *
  3405. * @var string
  3406. * @access private
  3407. */
  3408. var $methodname = '';
  3409. /**
  3410. * method parameters from request
  3411. *
  3412. * @var array
  3413. * @access private
  3414. */
  3415. var $methodparams = array();
  3416. /**
  3417. * SOAP Action from request
  3418. *
  3419. * @var string
  3420. * @access private
  3421. */
  3422. var $SOAPAction = '';
  3423. /**
  3424. * character set encoding of incoming (request) messages
  3425. *
  3426. * @var string
  3427. * @access public
  3428. */
  3429. var $xml_encoding = '';
  3430. /**
  3431. * toggles whether the parser decodes element content w/ utf8_decode()
  3432. *
  3433. * @var boolean
  3434. * @access public
  3435. */
  3436. var $decode_utf8 = true;
  3437. /**
  3438. * HTTP headers of response
  3439. *
  3440. * @var array
  3441. * @access public
  3442. */
  3443. var $outgoing_headers = array();
  3444. /**
  3445. * HTTP response
  3446. *
  3447. * @var string
  3448. * @access private
  3449. */
  3450. var $response = '';
  3451. /**
  3452. * SOAP headers for response (text or array of soapval or associative array)
  3453. *
  3454. * @var mixed
  3455. * @access public
  3456. */
  3457. var $responseHeaders = '';
  3458. /**
  3459. * SOAP payload for response (text)
  3460. *
  3461. * @var string
  3462. * @access private
  3463. */
  3464. var $responseSOAP = '';
  3465. /**
  3466. * method return value to place in response
  3467. *
  3468. * @var mixed
  3469. * @access private
  3470. */
  3471. var $methodreturn = false;
  3472. /**
  3473. * whether $methodreturn is a string of literal XML
  3474. *
  3475. * @var boolean
  3476. * @access public
  3477. */
  3478. var $methodreturnisliteralxml = false;
  3479. /**
  3480. * SOAP fault for response (or false)
  3481. *
  3482. * @var mixed
  3483. * @access private
  3484. */
  3485. var $fault = false;
  3486. /**
  3487. * text indication of result (for debugging)
  3488. *
  3489. * @var string
  3490. * @access private
  3491. */
  3492. var $result = 'successful';
  3493. /**
  3494. * assoc array of operations => opData; operations are added by the register()
  3495. * method or by parsing an external WSDL definition
  3496. *
  3497. * @var array
  3498. * @access private
  3499. */
  3500. var $operations = array();
  3501. /**
  3502. * wsdl instance (if one)
  3503. *
  3504. * @var mixed
  3505. * @access private
  3506. */
  3507. var $wsdl = false;
  3508. /**
  3509. * URL for WSDL (if one)
  3510. *
  3511. * @var mixed
  3512. * @access private
  3513. */
  3514. var $externalWSDLURL = false;
  3515. /**
  3516. * whether to append debug to response as XML comment
  3517. *
  3518. * @var boolean
  3519. * @access public
  3520. */
  3521. var $debug_flag = false;
  3522. /**
  3523. * constructor
  3524. * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
  3525. *
  3526. * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
  3527. * @access public
  3528. */
  3529. function __construct($wsdl = false)
  3530. {
  3531. parent::__construct();
  3532. // turn on debugging?
  3533. global $debug;
  3534. global $HTTP_SERVER_VARS;
  3535. if (isset($_SERVER)) {
  3536. $this->debug("_SERVER is defined:");
  3537. $this->appendDebug($this->varDump($_SERVER));
  3538. } elseif (isset($HTTP_SERVER_VARS)) {
  3539. $this->debug("HTTP_SERVER_VARS is defined:");
  3540. $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
  3541. } else {
  3542. $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
  3543. }
  3544. if (isset($debug)) {
  3545. $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
  3546. $this->debug_flag = $debug;
  3547. } elseif (isset($_SERVER['QUERY_STRING'])) {
  3548. $qs = explode('&', $_SERVER['QUERY_STRING']);
  3549. foreach ($qs as $v) {
  3550. if (substr($v, 0, 6) == 'debug=') {
  3551. $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
  3552. $this->debug_flag = substr($v, 6);
  3553. }
  3554. }
  3555. } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
  3556. $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
  3557. foreach ($qs as $v) {
  3558. if (substr($v, 0, 6) == 'debug=') {
  3559. $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
  3560. $this->debug_flag = substr($v, 6);
  3561. }
  3562. }
  3563. }
  3564. // wsdl
  3565. if ($wsdl) {
  3566. $this->debug("In nusoap_server, WSDL is specified");
  3567. if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
  3568. $this->wsdl = $wsdl;
  3569. $this->externalWSDLURL = $this->wsdl->wsdl;
  3570. $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
  3571. } else {
  3572. $this->debug('Create wsdl from ' . $wsdl);
  3573. $this->wsdl = new wsdl($wsdl);
  3574. $this->externalWSDLURL = $wsdl;
  3575. }
  3576. $this->appendDebug($this->wsdl->getDebug());
  3577. $this->wsdl->clearDebug();
  3578. if ($err = $this->wsdl->getError()) {
  3579. die('WSDL ERROR: ' . $err);
  3580. }
  3581. }
  3582. }
  3583. /**
  3584. * processes request and returns response
  3585. *
  3586. * @param string $data usually is the value of $HTTP_RAW_POST_DATA
  3587. * @access public
  3588. */
  3589. function service($data)
  3590. {
  3591. global $HTTP_SERVER_VARS;
  3592. if (isset($_SERVER['REQUEST_METHOD'])) {
  3593. $rm = $_SERVER['REQUEST_METHOD'];
  3594. } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) {
  3595. $rm = $HTTP_SERVER_VARS['REQUEST_METHOD'];
  3596. } else {
  3597. $rm = '';
  3598. }
  3599. if (isset($_SERVER['QUERY_STRING'])) {
  3600. $qs = $_SERVER['QUERY_STRING'];
  3601. } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
  3602. $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
  3603. } else {
  3604. $qs = '';
  3605. }
  3606. $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data));
  3607. if ($rm == 'POST') {
  3608. $this->debug("In service, invoke the request");
  3609. $this->parse_request($data);
  3610. if (!$this->fault) {
  3611. $this->invoke_method();
  3612. }
  3613. if (!$this->fault) {
  3614. $this->serialize_return();
  3615. }
  3616. $this->send_response();
  3617. } elseif (preg_match('/wsdl/', $qs)) {
  3618. $this->debug("In service, this is a request for WSDL");
  3619. if ($this->externalWSDLURL) {
  3620. if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL
  3621. $this->debug("In service, re-direct for WSDL");
  3622. header('Location: ' . $this->externalWSDLURL);
  3623. } else { // assume file
  3624. $this->debug("In service, use file passthru for WSDL");
  3625. header("Content-Type: text/xml\r\n");
  3626. $pos = strpos($this->externalWSDLURL, "file://");
  3627. if ($pos === false) {
  3628. $filename = $this->externalWSDLURL;
  3629. } else {
  3630. $filename = substr($this->externalWSDLURL, $pos + 7);
  3631. }
  3632. $fp = fopen($this->externalWSDLURL, 'r');
  3633. fpassthru($fp);
  3634. }
  3635. } elseif ($this->wsdl) {
  3636. $this->debug("In service, serialize WSDL");
  3637. header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
  3638. print $this->wsdl->serialize($this->debug_flag);
  3639. if ($this->debug_flag) {
  3640. $this->debug('wsdl:');
  3641. $this->appendDebug($this->varDump($this->wsdl));
  3642. print $this->getDebugAsXMLComment();
  3643. }
  3644. } else {
  3645. $this->debug("In service, there is no WSDL");
  3646. header("Content-Type: text/html; charset=ISO-8859-1\r\n");
  3647. print "This service does not provide WSDL";
  3648. }
  3649. } elseif ($this->wsdl) {
  3650. $this->debug("In service, return Web description");
  3651. print $this->wsdl->webDescription();
  3652. } else {
  3653. $this->debug("In service, no Web description");
  3654. header("Content-Type: text/html; charset=ISO-8859-1\r\n");
  3655. print "This service does not provide a Web description";
  3656. }
  3657. }
  3658. /**
  3659. * parses HTTP request headers.
  3660. *
  3661. * The following fields are set by this function (when successful)
  3662. *
  3663. * headers
  3664. * request
  3665. * xml_encoding
  3666. * SOAPAction
  3667. *
  3668. * @access private
  3669. */
  3670. function parse_http_headers()
  3671. {
  3672. global $HTTP_SERVER_VARS;
  3673. $this->request = '';
  3674. $this->SOAPAction = '';
  3675. if (function_exists('getallheaders')) {
  3676. $this->debug("In parse_http_headers, use getallheaders");
  3677. $headers = getallheaders();
  3678. foreach ($headers as $k => $v) {
  3679. $k = strtolower($k);
  3680. $this->headers[$k] = $v;
  3681. $this->request .= "$k: $v\r\n";
  3682. $this->debug("$k: $v");
  3683. }
  3684. // get SOAPAction header
  3685. if (isset($this->headers['soapaction'])) {
  3686. $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']);
  3687. }
  3688. // get the character encoding of the incoming request
  3689. if (isset($this->headers['content-type']) && strpos($this->headers['content-type'], '=')) {
  3690. $enc = str_replace('"', '', substr(strstr($this->headers["content-type"], '='), 1));
  3691. if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
  3692. $this->xml_encoding = strtoupper($enc);
  3693. } else {
  3694. $this->xml_encoding = 'US-ASCII';
  3695. }
  3696. } else {
  3697. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
  3698. $this->xml_encoding = 'ISO-8859-1';
  3699. }
  3700. } elseif (isset($_SERVER) && is_array($_SERVER)) {
  3701. $this->debug("In parse_http_headers, use _SERVER");
  3702. foreach ($_SERVER as $k => $v) {
  3703. if (substr($k, 0, 5) == 'HTTP_') {
  3704. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
  3705. } else {
  3706. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
  3707. }
  3708. if ($k == 'soapaction') {
  3709. // get SOAPAction header
  3710. $k = 'SOAPAction';
  3711. $v = str_replace('"', '', $v);
  3712. $v = str_replace('\\', '', $v);
  3713. $this->SOAPAction = $v;
  3714. } elseif ($k == 'content-type') {
  3715. // get the character encoding of the incoming request
  3716. if (strpos($v, '=')) {
  3717. $enc = substr(strstr($v, '='), 1);
  3718. $enc = str_replace('"', '', $enc);
  3719. $enc = str_replace('\\', '', $enc);
  3720. if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
  3721. $this->xml_encoding = strtoupper($enc);
  3722. } else {
  3723. $this->xml_encoding = 'US-ASCII';
  3724. }
  3725. } else {
  3726. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
  3727. $this->xml_encoding = 'ISO-8859-1';
  3728. }
  3729. }
  3730. $this->headers[$k] = $v;
  3731. if (is_array($v)) {
  3732. $this->request .= "$k: " . json_encode($v) . "\r\n";
  3733. $this->debug("$k: " . json_encode($v));
  3734. } else {
  3735. $this->request .= "$k: $v\r\n";
  3736. $this->debug("$k: $v");
  3737. }
  3738. }
  3739. } elseif (is_array($HTTP_SERVER_VARS)) {
  3740. $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
  3741. foreach ($HTTP_SERVER_VARS as $k => $v) {
  3742. if (substr($k, 0, 5) == 'HTTP_') {
  3743. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
  3744. $k = strtolower(substr($k, 5));
  3745. } else {
  3746. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
  3747. $k = strtolower($k);
  3748. }
  3749. if ($k == 'soapaction') {
  3750. // get SOAPAction header
  3751. $k = 'SOAPAction';
  3752. $v = str_replace('"', '', $v);
  3753. $v = str_replace('\\', '', $v);
  3754. $this->SOAPAction = $v;
  3755. } elseif ($k == 'content-type') {
  3756. // get the character encoding of the incoming request
  3757. if (strpos($v, '=')) {
  3758. $enc = substr(strstr($v, '='), 1);
  3759. $enc = str_replace('"', '', $enc);
  3760. $enc = str_replace('\\', '', $enc);
  3761. if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
  3762. $this->xml_encoding = strtoupper($enc);
  3763. } else {
  3764. $this->xml_encoding = 'US-ASCII';
  3765. }
  3766. } else {
  3767. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
  3768. $this->xml_encoding = 'ISO-8859-1';
  3769. }
  3770. }
  3771. $this->headers[$k] = $v;
  3772. $this->request .= "$k: $v\r\n";
  3773. $this->debug("$k: $v");
  3774. }
  3775. } else {
  3776. $this->debug("In parse_http_headers, HTTP headers not accessible");
  3777. $this->setError("HTTP headers not accessible");
  3778. }
  3779. }
  3780. /**
  3781. * parses a request
  3782. *
  3783. * The following fields are set by this function (when successful)
  3784. *
  3785. * headers
  3786. * request
  3787. * xml_encoding
  3788. * SOAPAction
  3789. * request
  3790. * requestSOAP
  3791. * methodURI
  3792. * methodname
  3793. * methodparams
  3794. * requestHeaders
  3795. * document
  3796. *
  3797. * This sets the fault field on error
  3798. *
  3799. * @param string $data XML string
  3800. * @access private
  3801. */
  3802. function parse_request($data = '')
  3803. {
  3804. $this->debug('entering parse_request()');
  3805. $this->parse_http_headers();
  3806. $this->debug('got character encoding: ' . $this->xml_encoding);
  3807. // uncompress if necessary
  3808. if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
  3809. $this->debug('got content encoding: ' . $this->headers['content-encoding']);
  3810. if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
  3811. // if decoding works, use it. else assume data wasn't gzencoded
  3812. if (function_exists('gzuncompress')) {
  3813. if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
  3814. $data = $degzdata;
  3815. } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
  3816. $data = $degzdata;
  3817. } else {
  3818. $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
  3819. return;
  3820. }
  3821. } else {
  3822. $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
  3823. return;
  3824. }
  3825. }
  3826. }
  3827. $this->request .= "\r\n" . $data;
  3828. $data = $this->parseRequest($this->headers, $data);
  3829. $this->requestSOAP = $data;
  3830. $this->debug('leaving parse_request');
  3831. }
  3832. /**
  3833. * invokes a PHP function for the requested SOAP method
  3834. *
  3835. * The following fields are set by this function (when successful)
  3836. *
  3837. * methodreturn
  3838. *
  3839. * Note that the PHP function that is called may also set the following
  3840. * fields to affect the response sent to the client
  3841. *
  3842. * responseHeaders
  3843. * outgoing_headers
  3844. *
  3845. * This sets the fault field on error
  3846. *
  3847. * @access private
  3848. */
  3849. function invoke_method()
  3850. {
  3851. $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
  3852. //
  3853. // if you are debugging in this area of the code, your service uses a class to implement methods,
  3854. // you use SOAP RPC, and the client is .NET, please be aware of the following...
  3855. // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
  3856. // method name. that is fine for naming the .NET methods. it is not fine for properly constructing
  3857. // the XML request and reading the XML response. you need to add the RequestElementName and
  3858. // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
  3859. // generates for the method. these parameters are used to specify the correct XML element names
  3860. // for .NET to use, i.e. the names with the '.' in them.
  3861. //
  3862. $orig_methodname = $this->methodname;
  3863. if ($this->wsdl) {
  3864. if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
  3865. $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
  3866. $this->appendDebug('opData=' . $this->varDump($this->opData));
  3867. } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
  3868. // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
  3869. $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
  3870. $this->appendDebug('opData=' . $this->varDump($this->opData));
  3871. $this->methodname = $this->opData['name'];
  3872. } else {
  3873. $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
  3874. $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
  3875. return;
  3876. }
  3877. } else {
  3878. $this->debug('in invoke_method, no WSDL to validate method');
  3879. }
  3880. // if a . is present in $this->methodname, we see if there is a class in scope,
  3881. // which could be referred to. We will also distinguish between two deliminators,
  3882. // to allow methods to be called a the class or an instance
  3883. if (strpos($this->methodname, '..') > 0) {
  3884. $delim = '..';
  3885. } elseif (strpos($this->methodname, '.') > 0) {
  3886. $delim = '.';
  3887. } else {
  3888. $delim = '';
  3889. }
  3890. $this->debug("in invoke_method, delim=$delim");
  3891. $class = '';
  3892. $method = '';
  3893. if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) {
  3894. $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim));
  3895. if (class_exists($try_class)) {
  3896. // get the class and method name
  3897. $class = $try_class;
  3898. $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
  3899. $this->debug("in invoke_method, class=$class method=$method delim=$delim");
  3900. } else {
  3901. $this->debug("in invoke_method, class=$try_class not found");
  3902. }
  3903. } elseif (strlen($delim) > 0 && substr_count($this->methodname, $delim) > 1) {
  3904. $split = explode($delim, $this->methodname);
  3905. $method = array_pop($split);
  3906. $class = implode('\\', $split);
  3907. } else {
  3908. $try_class = '';
  3909. $this->debug("in invoke_method, no class to try");
  3910. }
  3911. // does method exist?
  3912. if ($class == '') {
  3913. if (!function_exists($this->methodname)) {
  3914. $this->debug("in invoke_method, function '$this->methodname' not found!");
  3915. $this->result = 'fault: method not found';
  3916. $this->fault('SOAP-ENV:Client', "method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')");
  3917. return;
  3918. }
  3919. } else {
  3920. $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
  3921. if (!in_array($method_to_compare, get_class_methods($class))) {
  3922. $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
  3923. $this->result = 'fault: method not found';
  3924. $this->fault('SOAP-ENV:Client', "method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
  3925. return;
  3926. }
  3927. }
  3928. // evaluate message, getting back parameters
  3929. // verify that request parameters match the method's signature
  3930. if (!$this->verify_method($this->methodname, $this->methodparams)) {
  3931. // debug
  3932. $this->debug('ERROR: request not verified against method signature');
  3933. $this->result = 'fault: request failed validation against method signature';
  3934. // return fault
  3935. $this->fault('SOAP-ENV:Client', "Operation '$this->methodname' not defined in service.");
  3936. return;
  3937. }
  3938. // if there are parameters to pass
  3939. $this->debug('in invoke_method, params:');
  3940. $this->appendDebug($this->varDump($this->methodparams));
  3941. $this->debug("in invoke_method, calling '$this->methodname'");
  3942. if (!function_exists('call_user_func_array')) {
  3943. if ($class == '') {
  3944. $this->debug('in invoke_method, calling function using eval()');
  3945. $funcCall = "\$this->methodreturn = $this->methodname(";
  3946. } else {
  3947. if ($delim == '..') {
  3948. $this->debug('in invoke_method, calling class method using eval()');
  3949. $funcCall = "\$this->methodreturn = " . $class . "::" . $method . "(";
  3950. } else {
  3951. $this->debug('in invoke_method, calling instance method using eval()');
  3952. // generate unique instance name
  3953. $instname = "\$inst_" . time();
  3954. $funcCall = $instname . " = new " . $class . "(); ";
  3955. $funcCall .= "\$this->methodreturn = " . $instname . "->" . $method . "(";
  3956. }
  3957. }
  3958. if ($this->methodparams) {
  3959. foreach ($this->methodparams as $param) {
  3960. if (is_array($param) || is_object($param)) {
  3961. $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
  3962. return;
  3963. }
  3964. $funcCall .= "\"$param\",";
  3965. }
  3966. $funcCall = substr($funcCall, 0, -1);
  3967. }
  3968. $funcCall .= ');';
  3969. $this->debug('in invoke_method, function call: ' . $funcCall);
  3970. @eval($funcCall);
  3971. } else {
  3972. if ($class == '') {
  3973. $this->debug('in invoke_method, calling function using call_user_func_array()');
  3974. $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array()
  3975. } elseif ($delim == '..') {
  3976. $this->debug('in invoke_method, calling class method using call_user_func_array()');
  3977. $call_arg = array($class, $method);
  3978. } else {
  3979. $this->debug('in invoke_method, calling instance method using call_user_func_array()');
  3980. $instance = new $class ();
  3981. $call_arg = array(&$instance, $method);
  3982. }
  3983. if (is_array($this->methodparams)) {
  3984. $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
  3985. } else {
  3986. $this->methodreturn = call_user_func_array($call_arg, array());
  3987. }
  3988. }
  3989. $this->debug('in invoke_method, methodreturn:');
  3990. $this->appendDebug($this->varDump($this->methodreturn));
  3991. $this->debug("in invoke_method, called method $this->methodname, received data of type " . gettype($this->methodreturn));
  3992. }
  3993. /**
  3994. * serializes the return value from a PHP function into a full SOAP Envelope
  3995. *
  3996. * The following fields are set by this function (when successful)
  3997. *
  3998. * responseSOAP
  3999. *
  4000. * This sets the fault field on error
  4001. *
  4002. * @access private
  4003. */
  4004. function serialize_return()
  4005. {
  4006. $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
  4007. // if fault
  4008. if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
  4009. $this->debug('got a fault object from method');
  4010. $this->fault = $this->methodreturn;
  4011. return;
  4012. } elseif ($this->methodreturnisliteralxml) {
  4013. $return_val = $this->methodreturn;
  4014. // returned value(s)
  4015. } else {
  4016. $this->debug('got a(n) ' . gettype($this->methodreturn) . ' from method');
  4017. $this->debug('serializing return value');
  4018. if ($this->wsdl) {
  4019. if (sizeof($this->opData['output']['parts']) > 1) {
  4020. $this->debug('more than one output part, so use the method return unchanged');
  4021. $opParams = $this->methodreturn;
  4022. } elseif (sizeof($this->opData['output']['parts']) == 1) {
  4023. $this->debug('exactly one output part, so wrap the method return in a simple array');
  4024. // TODO: verify that it is not already wrapped!
  4025. //foreach ($this->opData['output']['parts'] as $name => $type) {
  4026. // $this->debug('wrap in element named ' . $name);
  4027. //}
  4028. $opParams = array($this->methodreturn);
  4029. }
  4030. $opParams = isset($opParams) ? $opParams : [];
  4031. $return_val = $this->wsdl->serializeRPCParameters($this->methodname, 'output', $opParams);
  4032. $this->appendDebug($this->wsdl->getDebug());
  4033. $this->wsdl->clearDebug();
  4034. if ($errstr = $this->wsdl->getError()) {
  4035. $this->debug('got wsdl error: ' . $errstr);
  4036. $this->fault('SOAP-ENV:Server', 'unable to serialize result');
  4037. return;
  4038. }
  4039. } else {
  4040. if (isset($this->methodreturn)) {
  4041. $return_val = $this->serialize_val($this->methodreturn, 'return');
  4042. } else {
  4043. $return_val = '';
  4044. $this->debug('in absence of WSDL, assume void return for backward compatibility');
  4045. }
  4046. }
  4047. }
  4048. $this->debug('return value:');
  4049. $this->appendDebug($this->varDump($return_val));
  4050. $this->debug('serializing response');
  4051. if ($this->wsdl) {
  4052. $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
  4053. if ($this->opData['style'] == 'rpc') {
  4054. $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
  4055. if ($this->opData['output']['use'] == 'literal') {
  4056. // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
  4057. if ($this->methodURI) {
  4058. $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . "Response>";
  4059. } else {
  4060. $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
  4061. }
  4062. } else {
  4063. if ($this->methodURI) {
  4064. $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . "Response>";
  4065. } else {
  4066. $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
  4067. }
  4068. }
  4069. } else {
  4070. $this->debug('style is not rpc for serialization: assume document');
  4071. $payload = $return_val;
  4072. }
  4073. } else {
  4074. $this->debug('do not have WSDL for serialization: assume rpc/encoded');
  4075. $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . "Response>";
  4076. }
  4077. $this->result = 'successful';
  4078. if ($this->wsdl) {
  4079. //if($this->debug_flag){
  4080. $this->appendDebug($this->wsdl->getDebug());
  4081. // }
  4082. if (isset($this->opData['output']['encodingStyle'])) {
  4083. $encodingStyle = $this->opData['output']['encodingStyle'];
  4084. } else {
  4085. $encodingStyle = '';
  4086. }
  4087. // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
  4088. $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $this->opData['output']['use'], $encodingStyle);
  4089. } else {
  4090. $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders);
  4091. }
  4092. $this->debug("Leaving serialize_return");
  4093. }
  4094. /**
  4095. * sends an HTTP response
  4096. *
  4097. * The following fields are set by this function (when successful)
  4098. *
  4099. * outgoing_headers
  4100. * response
  4101. *
  4102. * @access private
  4103. */
  4104. function send_response()
  4105. {
  4106. $this->debug('Enter send_response');
  4107. if ($this->fault) {
  4108. $payload = $this->fault->serialize();
  4109. $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
  4110. $this->outgoing_headers[] = "Status: 500 Internal Server Error";
  4111. } else {
  4112. $payload = $this->responseSOAP;
  4113. // Some combinations of PHP+Web server allow the Status
  4114. // to come through as a header. Since OK is the default
  4115. // just do nothing.
  4116. // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
  4117. // $this->outgoing_headers[] = "Status: 200 OK";
  4118. }
  4119. // add debug data if in debug mode
  4120. if (isset($this->debug_flag) && $this->debug_flag) {
  4121. $payload .= $this->getDebugAsXMLComment();
  4122. }
  4123. $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
  4124. preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
  4125. $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (" . $rev[1] . ")";
  4126. // Let the Web server decide about this
  4127. //$this->outgoing_headers[] = "Connection: Close\r\n";
  4128. $payload = $this->getHTTPBody($payload);
  4129. $type = $this->getHTTPContentType();
  4130. $charset = $this->getHTTPContentTypeCharset();
  4131. $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
  4132. //begin code to compress payload - by John
  4133. // NOTE: there is no way to know whether the Web server will also compress
  4134. // this data.
  4135. if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
  4136. if (strstr($this->headers['accept-encoding'], 'gzip')) {
  4137. if (function_exists('gzencode')) {
  4138. if (isset($this->debug_flag) && $this->debug_flag) {
  4139. $payload .= "<!-- Content being gzipped -->";
  4140. }
  4141. $this->outgoing_headers[] = "Content-Encoding: gzip";
  4142. $payload = gzencode($payload);
  4143. } else {
  4144. if (isset($this->debug_flag) && $this->debug_flag) {
  4145. $payload .= "<!-- Content will not be gzipped: no gzencode -->";
  4146. }
  4147. }
  4148. } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
  4149. // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
  4150. // instead of gzcompress output,
  4151. // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
  4152. if (function_exists('gzdeflate')) {
  4153. if (isset($this->debug_flag) && $this->debug_flag) {
  4154. $payload .= "<!-- Content being deflated -->";
  4155. }
  4156. $this->outgoing_headers[] = "Content-Encoding: deflate";
  4157. $payload = gzdeflate($payload);
  4158. } else {
  4159. if (isset($this->debug_flag) && $this->debug_flag) {
  4160. $payload .= "<!-- Content will not be deflated: no gzcompress -->";
  4161. }
  4162. }
  4163. }
  4164. }
  4165. //end code
  4166. $this->outgoing_headers[] = "Content-Length: " . strlen($payload);
  4167. reset($this->outgoing_headers);
  4168. foreach ($this->outgoing_headers as $hdr) {
  4169. header($hdr, false);
  4170. }
  4171. print $payload;
  4172. $this->response = join("\r\n", $this->outgoing_headers) . "\r\n\r\n" . $payload;
  4173. }
  4174. /**
  4175. * takes the value that was created by parsing the request
  4176. * and compares to the method's signature, if available.
  4177. *
  4178. * @param string $operation The operation to be invoked
  4179. * @param array $request The array of parameter values
  4180. * @return boolean Whether the operation was found
  4181. * @access private
  4182. */
  4183. function verify_method($operation, $request)
  4184. {
  4185. if (isset($this->wsdl) && is_object($this->wsdl)) {
  4186. if ($this->wsdl->getOperationData($operation)) {
  4187. return true;
  4188. }
  4189. } elseif (isset($this->operations[$operation])) {
  4190. return true;
  4191. }
  4192. return false;
  4193. }
  4194. /**
  4195. * processes SOAP message received from client
  4196. *
  4197. * @param array $headers The HTTP headers
  4198. * @param string $data unprocessed request data from client
  4199. * @return mixed value of the message, decoded into a PHP type
  4200. * @access private
  4201. */
  4202. function parseRequest($headers, $data)
  4203. {
  4204. $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
  4205. $this->appendDebug($this->varDump($headers));
  4206. if (!isset($headers['content-type'])) {
  4207. $this->setError('Request not of type '.$this->contentType.' (no content-type header)');
  4208. return false;
  4209. }
  4210. if (!strstr($headers['content-type'], $this->contentType)) {
  4211. $this->setError('Request not of type '.$this->contentType.': ' . $headers['content-type']);
  4212. return false;
  4213. }
  4214. if (strpos($headers['content-type'], '=')) {
  4215. $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
  4216. $this->debug('Got response encoding: ' . $enc);
  4217. if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
  4218. $this->xml_encoding = strtoupper($enc);
  4219. } else {
  4220. $this->xml_encoding = 'US-ASCII';
  4221. }
  4222. } else {
  4223. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
  4224. $this->xml_encoding = 'ISO-8859-1';
  4225. }
  4226. $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
  4227. // parse response, get soap parser obj
  4228. $parser = new nusoap_parser($data, $this->xml_encoding, '', $this->decode_utf8);
  4229. // parser debug
  4230. $this->debug("parser debug: \n" . $parser->getDebug());
  4231. // if fault occurred during message parsing
  4232. if ($err = $parser->getError()) {
  4233. $this->result = 'fault: error in msg parsing: ' . $err;
  4234. $this->fault('SOAP-ENV:Client', "error in msg parsing:\n" . $err);
  4235. // else successfully parsed request into soapval object
  4236. } else {
  4237. // get/set methodname
  4238. $this->methodURI = $parser->root_struct_namespace;
  4239. $this->methodname = $parser->root_struct_name;
  4240. $this->debug('methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
  4241. $this->debug('calling parser->get_soapbody()');
  4242. $this->methodparams = $parser->get_soapbody();
  4243. // get SOAP headers
  4244. $this->requestHeaders = $parser->getHeaders();
  4245. // get SOAP Header
  4246. $this->requestHeader = $parser->get_soapheader();
  4247. // add document for doclit support
  4248. $this->document = $parser->document;
  4249. }
  4250. }
  4251. /**
  4252. * gets the HTTP body for the current response.
  4253. *
  4254. * @param string $soapmsg The SOAP payload
  4255. * @return string The HTTP body, which includes the SOAP payload
  4256. * @access private
  4257. */
  4258. function getHTTPBody($soapmsg)
  4259. {
  4260. return $soapmsg;
  4261. }
  4262. /**
  4263. * gets the HTTP content type for the current response.
  4264. *
  4265. * Note: getHTTPBody must be called before this.
  4266. *
  4267. * @return string the HTTP content type for the current response.
  4268. * @access private
  4269. */
  4270. function getHTTPContentType()
  4271. {
  4272. return 'text/xml';
  4273. }
  4274. /**
  4275. * gets the HTTP content type charset for the current response.
  4276. * returns false for non-text content types.
  4277. *
  4278. * Note: getHTTPBody must be called before this.
  4279. *
  4280. * @return string the HTTP content type charset for the current response.
  4281. * @access private
  4282. */
  4283. function getHTTPContentTypeCharset()
  4284. {
  4285. return $this->soap_defencoding;
  4286. }
  4287. /**
  4288. * add a method to the dispatch map (this has been replaced by the register method)
  4289. *
  4290. * @param string $methodname
  4291. * @param string $in array of input values
  4292. * @param string $out array of output values
  4293. * @access public
  4294. * @deprecated
  4295. */
  4296. function add_to_map($methodname, $in, $out)
  4297. {
  4298. $this->operations[$methodname] = array('name' => $methodname, 'in' => $in, 'out' => $out);
  4299. }
  4300. /**
  4301. * register a service function with the server
  4302. *
  4303. * @param string $name the name of the PHP function, class.method or class..method
  4304. * @param array $in assoc array of input values: key = param name, value = param type
  4305. * @param array $out assoc array of output values: key = param name, value = param type
  4306. * @param mixed $namespace the element namespace for the method or false
  4307. * @param mixed $soapaction the soapaction for the method or false
  4308. * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
  4309. * @param mixed $use optional (encoded|literal) or false
  4310. * @param string $documentation optional Description to include in WSDL
  4311. * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
  4312. * @access public
  4313. */
  4314. function register($name, $in = array(), $out = array(), $namespace = false, $soapaction = false, $style = false, $use = false, $documentation = '', $encodingStyle = '')
  4315. {
  4316. global $HTTP_SERVER_VARS;
  4317. if ($this->externalWSDLURL) {
  4318. die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
  4319. }
  4320. if (!$name) {
  4321. die('You must specify a name when you register an operation');
  4322. }
  4323. if (!is_array($in)) {
  4324. die('You must provide an array for operation inputs');
  4325. }
  4326. if (!is_array($out)) {
  4327. die('You must provide an array for operation outputs');
  4328. }
  4329. if (false == $namespace) {
  4330. }
  4331. if (false == $soapaction) {
  4332. if (isset($_SERVER)) {
  4333. $SERVER_NAME = $_SERVER['SERVER_NAME'];
  4334. $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
  4335. $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
  4336. } elseif (isset($HTTP_SERVER_VARS)) {
  4337. $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
  4338. $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
  4339. $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
  4340. } else {
  4341. $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
  4342. }
  4343. if ($HTTPS == '1' || $HTTPS == 'on') {
  4344. $SCHEME = 'https';
  4345. } else {
  4346. $SCHEME = 'http';
  4347. }
  4348. $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
  4349. }
  4350. if (false == $style) {
  4351. $style = "rpc";
  4352. }
  4353. if (false == $use) {
  4354. $use = "encoded";
  4355. }
  4356. if ($use == 'encoded' && $encodingStyle == '') {
  4357. $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
  4358. }
  4359. $this->operations[$name] = array(
  4360. 'name' => $name,
  4361. 'in' => $in,
  4362. 'out' => $out,
  4363. 'namespace' => $namespace,
  4364. 'soapaction' => $soapaction,
  4365. 'style' => $style);
  4366. if ($this->wsdl) {
  4367. $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle);
  4368. }
  4369. return true;
  4370. }
  4371. /**
  4372. * Specify a fault to be returned to the client.
  4373. * This also acts as a flag to the server that a fault has occured.
  4374. *
  4375. * @param string $faultcode
  4376. * @param string $faultstring
  4377. * @param string $faultactor
  4378. * @param string $faultdetail
  4379. * @access public
  4380. */
  4381. function fault($faultcode, $faultstring, $faultactor = '', $faultdetail = '')
  4382. {
  4383. if ($faultdetail == '' && $this->debug_flag) {
  4384. $faultdetail = $this->getDebug();
  4385. }
  4386. $this->fault = new nusoap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
  4387. $this->fault->soap_defencoding = $this->soap_defencoding;
  4388. }
  4389. /**
  4390. * Sets up wsdl object.
  4391. * Acts as a flag to enable internal WSDL generation
  4392. *
  4393. * @param string $serviceName , name of the service
  4394. * @param mixed $namespace optional 'tns' service namespace or false
  4395. * @param mixed $endpoint optional URL of service endpoint or false
  4396. * @param string $style optional (rpc|document) WSDL style (also specified by operation)
  4397. * @param string $transport optional SOAP transport
  4398. * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
  4399. */
  4400. function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style = 'rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
  4401. {
  4402. global $HTTP_SERVER_VARS;
  4403. if (isset($_SERVER)) {
  4404. $SERVER_NAME = $_SERVER['SERVER_NAME'];
  4405. $SERVER_PORT = $_SERVER['SERVER_PORT'];
  4406. $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
  4407. $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
  4408. } elseif (isset($HTTP_SERVER_VARS)) {
  4409. $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
  4410. $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
  4411. $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
  4412. $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
  4413. } else {
  4414. $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
  4415. }
  4416. // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
  4417. $colon = strpos($SERVER_NAME, ":");
  4418. if ($colon) {
  4419. $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
  4420. }
  4421. if ($SERVER_PORT == 80) {
  4422. $SERVER_PORT = '';
  4423. } else {
  4424. $SERVER_PORT = ':' . $SERVER_PORT;
  4425. }
  4426. if (false == $namespace) {
  4427. $namespace = "http://$SERVER_NAME/soap/$serviceName";
  4428. }
  4429. if (false == $endpoint) {
  4430. if ($HTTPS == '1' || $HTTPS == 'on') {
  4431. $SCHEME = 'https';
  4432. } else {
  4433. $SCHEME = 'http';
  4434. }
  4435. $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
  4436. }
  4437. if (false == $schemaTargetNamespace) {
  4438. $schemaTargetNamespace = $namespace;
  4439. }
  4440. $this->wsdl = new wsdl;
  4441. $this->wsdl->serviceName = $serviceName;
  4442. $this->wsdl->endpoint = $endpoint;
  4443. $this->wsdl->namespaces['tns'] = $namespace;
  4444. $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
  4445. $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
  4446. if ($schemaTargetNamespace != $namespace) {
  4447. $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
  4448. }
  4449. $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
  4450. if ($style == 'document') {
  4451. $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
  4452. }
  4453. $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
  4454. $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
  4455. $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
  4456. $this->wsdl->bindings[$serviceName . 'Binding'] = array(
  4457. 'name' => $serviceName . 'Binding',
  4458. 'style' => $style,
  4459. 'transport' => $transport,
  4460. 'portType' => $serviceName . 'PortType');
  4461. $this->wsdl->ports[$serviceName . 'Port'] = array(
  4462. 'binding' => $serviceName . 'Binding',
  4463. 'location' => $endpoint,
  4464. 'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/');
  4465. }
  4466. }
  4467. /**
  4468. * Backward compatibility
  4469. */
  4470. class soap_server extends nusoap_server
  4471. {
  4472. }
  4473. /**
  4474. * parses a WSDL file, allows access to it's data, other utility methods.
  4475. * also builds WSDL structures programmatically.
  4476. *
  4477. * @author Dietrich Ayala <dietrich@ganx4.com>
  4478. * @author Scott Nichol <snichol@users.sourceforge.net>
  4479. * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  4480. * @access public
  4481. */
  4482. class wsdl extends nusoap_base
  4483. {
  4484. // URL or filename of the root of this WSDL
  4485. var $wsdl;
  4486. // define internal arrays of bindings, ports, operations, messages, etc.
  4487. var $schemas = array();
  4488. var $currentSchema;
  4489. var $message = array();
  4490. var $complexTypes = array();
  4491. var $messages = array();
  4492. var $currentMessage;
  4493. var $currentOperation;
  4494. var $portTypes = array();
  4495. var $currentPortType;
  4496. var $bindings = array();
  4497. var $currentBinding;
  4498. var $ports = array();
  4499. var $currentPort;
  4500. var $opData = array();
  4501. var $status = '';
  4502. var $documentation = false;
  4503. var $endpoint = '';
  4504. // array of wsdl docs to import
  4505. var $import = array();
  4506. // parser vars
  4507. var $parser;
  4508. var $position = 0;
  4509. var $depth = 0;
  4510. var $depth_array = array();
  4511. // for getting wsdl
  4512. var $proxyhost = '';
  4513. var $proxyport = '';
  4514. var $proxyusername = '';
  4515. var $proxypassword = '';
  4516. var $timeout = 0;
  4517. var $response_timeout = 30;
  4518. var $curl_options = array(); // User-specified cURL options
  4519. var $use_curl = false; // whether to always try to use cURL
  4520. // for HTTP authentication
  4521. var $username = ''; // Username for HTTP authentication
  4522. var $password = ''; // Password for HTTP authentication
  4523. var $authtype = ''; // Type of HTTP authentication
  4524. var $certRequest = array(); // Certificate for HTTP SSL authentication
  4525. /**
  4526. * constructor
  4527. *
  4528. * @param string $wsdl WSDL document URL
  4529. * @param string $proxyhost
  4530. * @param string $proxyport
  4531. * @param string $proxyusername
  4532. * @param string $proxypassword
  4533. * @param integer $timeout set the connection timeout
  4534. * @param integer $response_timeout set the response timeout
  4535. * @param array $curl_options user-specified cURL options
  4536. * @param boolean $use_curl try to use cURL
  4537. * @access public
  4538. */
  4539. function __construct($wsdl = '', $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $curl_options = null, $use_curl = false)
  4540. {
  4541. parent::__construct();
  4542. $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
  4543. $this->proxyhost = $proxyhost;
  4544. $this->proxyport = $proxyport;
  4545. $this->proxyusername = $proxyusername;
  4546. $this->proxypassword = $proxypassword;
  4547. $this->timeout = $timeout;
  4548. $this->response_timeout = $response_timeout;
  4549. if (is_array($curl_options)) {
  4550. $this->curl_options = $curl_options;
  4551. }
  4552. $this->use_curl = $use_curl;
  4553. $this->fetchWSDL($wsdl);
  4554. }
  4555. /**
  4556. * fetches the WSDL document and parses it
  4557. *
  4558. * @access public
  4559. */
  4560. function fetchWSDL($wsdl)
  4561. {
  4562. $this->debug("parse and process WSDL path=$wsdl");
  4563. $this->wsdl = $wsdl;
  4564. // parse wsdl file
  4565. if ($this->wsdl != "") {
  4566. $this->parseWSDL($this->wsdl);
  4567. }
  4568. // imports
  4569. // TODO: handle imports more properly, grabbing them in-line and nesting them
  4570. $imported_urls = array();
  4571. $imported = 1;
  4572. while ($imported > 0) {
  4573. $imported = 0;
  4574. // Schema imports
  4575. foreach ($this->schemas as $ns => $list) {
  4576. foreach ($list as $xs) {
  4577. $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
  4578. foreach ($xs->imports as $ns2 => $list2) {
  4579. for ($ii = 0; $ii < count($list2); $ii++) {
  4580. if (!$list2[$ii]['loaded']) {
  4581. $this->schemas[$ns][$ns2]->imports[$ns2][$ii]['loaded'] = true;
  4582. $url = $list2[$ii]['location'];
  4583. if ($url != '') {
  4584. $urlparts = parse_url($url);
  4585. if (!isset($urlparts['host'])) {
  4586. $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
  4587. substr($wsdlparts['path'], 0, strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
  4588. }
  4589. if (!in_array($url, $imported_urls)) {
  4590. $this->parseWSDL($url);
  4591. $imported++;
  4592. $imported_urls[] = $url;
  4593. }
  4594. } else {
  4595. $this->debug("Unexpected scenario: empty URL for unloaded import");
  4596. }
  4597. }
  4598. }
  4599. }
  4600. }
  4601. }
  4602. // WSDL imports
  4603. $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
  4604. foreach ($this->import as $ns => $list) {
  4605. for ($ii = 0; $ii < count($list); $ii++) {
  4606. if (!$list[$ii]['loaded']) {
  4607. $this->import[$ns][$ii]['loaded'] = true;
  4608. $url = $list[$ii]['location'];
  4609. if ($url != '') {
  4610. $urlparts = parse_url($url);
  4611. if (!isset($urlparts['host'])) {
  4612. $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
  4613. substr($wsdlparts['path'], 0, strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
  4614. }
  4615. if (!in_array($url, $imported_urls)) {
  4616. $this->parseWSDL($url);
  4617. $imported++;
  4618. $imported_urls[] = $url;
  4619. }
  4620. } else {
  4621. $this->debug("Unexpected scenario: empty URL for unloaded import");
  4622. }
  4623. }
  4624. }
  4625. }
  4626. }
  4627. // add new data to operation data
  4628. foreach ($this->bindings as $binding => $bindingData) {
  4629. if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
  4630. foreach ($bindingData['operations'] as $operation => $data) {
  4631. $this->debug('post-parse data gathering for ' . $operation);
  4632. $this->bindings[$binding]['operations'][$operation]['input'] =
  4633. isset($this->bindings[$binding]['operations'][$operation]['input']) ?
  4634. array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[$bindingData['portType']][$operation]['input']) :
  4635. $this->portTypes[$bindingData['portType']][$operation]['input'];
  4636. $this->bindings[$binding]['operations'][$operation]['output'] =
  4637. isset($this->bindings[$binding]['operations'][$operation]['output']) ?
  4638. array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[$bindingData['portType']][$operation]['output']) :
  4639. $this->portTypes[$bindingData['portType']][$operation]['output'];
  4640. if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']])) {
  4641. $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']];
  4642. }
  4643. if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']])) {
  4644. $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']];
  4645. }
  4646. // Set operation style if necessary, but do not override one already provided
  4647. if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
  4648. $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
  4649. }
  4650. $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
  4651. $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[$bindingData['portType']][$operation]['documentation']) ? $this->portTypes[$bindingData['portType']][$operation]['documentation'] : '';
  4652. $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
  4653. }
  4654. }
  4655. }
  4656. }
  4657. /**
  4658. * parses the wsdl document
  4659. *
  4660. * @param string $wsdl path or URL
  4661. * @access private
  4662. */
  4663. function parseWSDL($wsdl = '')
  4664. {
  4665. $this->debug("parse WSDL at path=$wsdl");
  4666. if ($wsdl == '') {
  4667. $this->debug('no wsdl passed to parseWSDL()!!');
  4668. $this->setError('no wsdl passed to parseWSDL()!!');
  4669. return false;
  4670. }
  4671. // parse $wsdl for url format
  4672. $wsdl_props = parse_url($wsdl);
  4673. if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
  4674. $this->debug('getting WSDL http(s) URL ' . $wsdl);
  4675. // get wsdl
  4676. $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
  4677. $tr->request_method = 'GET';
  4678. $tr->useSOAPAction = false;
  4679. if ($this->proxyhost && $this->proxyport) {
  4680. $tr->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
  4681. }
  4682. if ($this->authtype != '') {
  4683. $tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
  4684. }
  4685. $tr->setEncoding('gzip, deflate');
  4686. $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
  4687. //$this->debug("WSDL request\n" . $tr->outgoing_payload);
  4688. //$this->debug("WSDL response\n" . $tr->incoming_payload);
  4689. $this->appendDebug($tr->getDebug());
  4690. // catch errors
  4691. if ($err = $tr->getError()) {
  4692. $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: ' . $err;
  4693. $this->debug($errstr);
  4694. $this->setError($errstr);
  4695. unset($tr);
  4696. return false;
  4697. }
  4698. unset($tr);
  4699. $this->debug("got WSDL URL");
  4700. } else {
  4701. // $wsdl is not http(s), so treat it as a file URL or plain file path
  4702. if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
  4703. $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
  4704. } else {
  4705. $path = $wsdl;
  4706. }
  4707. $this->debug('getting WSDL file ' . $path);
  4708. if ($fp = @fopen($path, 'r')) {
  4709. $wsdl_string = '';
  4710. while ($data = fread($fp, 32768)) {
  4711. $wsdl_string .= $data;
  4712. }
  4713. fclose($fp);
  4714. } else {
  4715. $errstr = "Bad path to WSDL file $path";
  4716. $this->debug($errstr);
  4717. $this->setError($errstr);
  4718. return false;
  4719. }
  4720. }
  4721. $this->debug('Parse WSDL');
  4722. // end new code added
  4723. // Create an XML parser.
  4724. $this->parser = xml_parser_create();
  4725. // Set the options for parsing the XML data.
  4726. // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
  4727. xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
  4728. // Set the object for the parser.
  4729. xml_set_object($this->parser, $this);
  4730. // Set the element handlers for the parser.
  4731. xml_set_element_handler($this->parser, 'start_element', 'end_element');
  4732. xml_set_character_data_handler($this->parser, 'character_data');
  4733. // Parse the XML file.
  4734. if (!xml_parse($this->parser, $wsdl_string, true)) {
  4735. // Display an error message.
  4736. $errstr = sprintf(
  4737. 'XML error parsing WSDL from %s on line %d: %s',
  4738. $wsdl,
  4739. xml_get_current_line_number($this->parser),
  4740. xml_error_string(xml_get_error_code($this->parser))
  4741. );
  4742. $this->debug($errstr);
  4743. $this->debug("XML payload:\n" . $wsdl_string);
  4744. $this->setError($errstr);
  4745. xml_parser_free($this->parser);
  4746. unset($this->parser);
  4747. return false;
  4748. }
  4749. // free the parser
  4750. xml_parser_free($this->parser);
  4751. unset($this->parser);
  4752. $this->debug('Parsing WSDL done');
  4753. // catch wsdl parse errors
  4754. if ($this->getError()) {
  4755. return false;
  4756. }
  4757. return true;
  4758. }
  4759. /**
  4760. * start-element handler
  4761. *
  4762. * @param string $parser XML parser object
  4763. * @param string $name element name
  4764. * @param string $attrs associative array of attributes
  4765. * @access private
  4766. */
  4767. function start_element($parser, $name, $attrs)
  4768. {
  4769. if ($this->status == 'schema') {
  4770. $this->currentSchema->schemaStartElement($parser, $name, $attrs);
  4771. $this->appendDebug($this->currentSchema->getDebug());
  4772. $this->currentSchema->clearDebug();
  4773. } elseif (preg_match('/schema$/', $name)) {
  4774. $this->debug('Parsing WSDL schema');
  4775. // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
  4776. $this->status = 'schema';
  4777. $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces);
  4778. $this->currentSchema->schemaStartElement($parser, $name, $attrs);
  4779. $this->appendDebug($this->currentSchema->getDebug());
  4780. $this->currentSchema->clearDebug();
  4781. } else {
  4782. // position in the total number of elements, starting from 0
  4783. $pos = $this->position++;
  4784. $depth = $this->depth++;
  4785. // set self as current value for this depth
  4786. $this->depth_array[$depth] = $pos;
  4787. $this->message[$pos] = array('cdata' => '');
  4788. // process attributes
  4789. if (count($attrs) > 0) {
  4790. // register namespace declarations
  4791. foreach ($attrs as $k => $v) {
  4792. if (preg_match('/^xmlns/', $k)) {
  4793. if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
  4794. $this->namespaces[$ns_prefix] = $v;
  4795. } else {
  4796. $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
  4797. }
  4798. if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
  4799. $this->XMLSchemaVersion = $v;
  4800. $this->namespaces['xsi'] = $v . '-instance';
  4801. }
  4802. }
  4803. }
  4804. // expand each attribute prefix to its namespace
  4805. foreach ($attrs as $k => $v) {
  4806. $k = strpos($k, ':') ? $this->expandQname($k) : $k;
  4807. if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
  4808. $v = strpos($v, ':') ? $this->expandQname($v) : $v;
  4809. }
  4810. $eAttrs[$k] = $v;
  4811. }
  4812. $attrs = $eAttrs;
  4813. } else {
  4814. $attrs = array();
  4815. }
  4816. // Set default prefix and namespace
  4817. // to prevent error Undefined variable $prefix and $namespace if (preg_match('/:/', $name)) return 0 or FALSE
  4818. $prefix = '';
  4819. $namespace = '';
  4820. // get element prefix, namespace and name
  4821. if (preg_match('/:/', $name)) {
  4822. // get ns prefix
  4823. $prefix = substr($name, 0, strpos($name, ':'));
  4824. // get ns
  4825. $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
  4826. // get unqualified name
  4827. $name = substr(strstr($name, ':'), 1);
  4828. }
  4829. // process attributes, expanding any prefixes to namespaces
  4830. // find status, register data
  4831. switch ($this->status) {
  4832. case 'message':
  4833. if ($name == 'part') {
  4834. if (isset($attrs['type'])) {
  4835. $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
  4836. $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
  4837. }
  4838. if (isset($attrs['element'])) {
  4839. $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
  4840. $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
  4841. }
  4842. }
  4843. break;
  4844. case 'portType':
  4845. switch ($name) {
  4846. case 'operation':
  4847. $this->currentPortOperation = $attrs['name'];
  4848. $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
  4849. if (isset($attrs['parameterOrder'])) {
  4850. $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
  4851. }
  4852. break;
  4853. case 'documentation':
  4854. $this->documentation = true;
  4855. break;
  4856. // merge input/output data
  4857. default:
  4858. $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
  4859. $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
  4860. break;
  4861. }
  4862. break;
  4863. case 'binding':
  4864. switch ($name) {
  4865. case 'binding':
  4866. // get ns prefix
  4867. if (isset($attrs['style'])) {
  4868. $this->bindings[$this->currentBinding]['prefix'] = $prefix;
  4869. }
  4870. $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
  4871. break;
  4872. case 'header':
  4873. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
  4874. break;
  4875. case 'operation':
  4876. if (isset($attrs['soapAction'])) {
  4877. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
  4878. }
  4879. if (isset($attrs['style'])) {
  4880. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
  4881. }
  4882. if (isset($attrs['name'])) {
  4883. $this->currentOperation = $attrs['name'];
  4884. $this->debug("current binding operation: $this->currentOperation");
  4885. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
  4886. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
  4887. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
  4888. }
  4889. break;
  4890. case 'input':
  4891. $this->opStatus = 'input';
  4892. break;
  4893. case 'output':
  4894. $this->opStatus = 'output';
  4895. break;
  4896. case 'body':
  4897. if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
  4898. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
  4899. } else {
  4900. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
  4901. }
  4902. break;
  4903. }
  4904. break;
  4905. case 'service':
  4906. switch ($name) {
  4907. case 'port':
  4908. $this->currentPort = $attrs['name'];
  4909. $this->debug('current port: ' . $this->currentPort);
  4910. $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
  4911. break;
  4912. case 'address':
  4913. $this->ports[$this->currentPort]['location'] = $attrs['location'];
  4914. $this->ports[$this->currentPort]['bindingType'] = $namespace;
  4915. $this->bindings[$this->ports[$this->currentPort]['binding']]['bindingType'] = $namespace;
  4916. $this->bindings[$this->ports[$this->currentPort]['binding']]['endpoint'] = $attrs['location'];
  4917. break;
  4918. }
  4919. break;
  4920. }
  4921. // set status
  4922. switch ($name) {
  4923. case 'import':
  4924. if (isset($attrs['location'])) {
  4925. $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
  4926. $this->debug('parsing import ' . $attrs['namespace'] . ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]) . ')');
  4927. } else {
  4928. $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
  4929. if (!$this->getPrefixFromNamespace($attrs['namespace'])) {
  4930. $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
  4931. }
  4932. $this->debug('parsing import ' . $attrs['namespace'] . ' - [no location] (' . count($this->import[$attrs['namespace']]) . ')');
  4933. }
  4934. break;
  4935. //wait for schema
  4936. //case 'types':
  4937. // $this->status = 'schema';
  4938. // break;
  4939. case 'message':
  4940. $this->status = 'message';
  4941. $this->messages[$attrs['name']] = array();
  4942. $this->currentMessage = $attrs['name'];
  4943. break;
  4944. case 'portType':
  4945. $this->status = 'portType';
  4946. $this->portTypes[$attrs['name']] = array();
  4947. $this->currentPortType = $attrs['name'];
  4948. break;
  4949. case "binding":
  4950. if (isset($attrs['name'])) {
  4951. // get binding name
  4952. if (strpos($attrs['name'], ':')) {
  4953. $this->currentBinding = $this->getLocalPart($attrs['name']);
  4954. } else {
  4955. $this->currentBinding = $attrs['name'];
  4956. }
  4957. $this->status = 'binding';
  4958. $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
  4959. $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
  4960. }
  4961. break;
  4962. case 'service':
  4963. $this->serviceName = $attrs['name'];
  4964. $this->status = 'service';
  4965. $this->debug('current service: ' . $this->serviceName);
  4966. break;
  4967. case 'definitions':
  4968. foreach ($attrs as $name => $value) {
  4969. $this->wsdl_info[$name] = $value;
  4970. }
  4971. break;
  4972. }
  4973. }
  4974. }
  4975. /**
  4976. * end-element handler
  4977. *
  4978. * @param string $parser XML parser object
  4979. * @param string $name element name
  4980. * @access private
  4981. */
  4982. function end_element($parser, $name)
  4983. {
  4984. // unset schema status
  4985. if (/*preg_match('/types$/', $name) ||*/
  4986. preg_match('/schema$/', $name)
  4987. ) {
  4988. $this->status = "";
  4989. $this->appendDebug($this->currentSchema->getDebug());
  4990. $this->currentSchema->clearDebug();
  4991. $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
  4992. $this->debug('Parsing WSDL schema done');
  4993. }
  4994. if ($this->status == 'schema') {
  4995. $this->currentSchema->schemaEndElement($parser, $name);
  4996. } else {
  4997. // bring depth down a notch
  4998. $this->depth--;
  4999. }
  5000. // end documentation
  5001. if ($this->documentation) {
  5002. //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
  5003. //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
  5004. $this->documentation = false;
  5005. }
  5006. }
  5007. /**
  5008. * element content handler
  5009. *
  5010. * @param string $parser XML parser object
  5011. * @param string $data element content
  5012. * @access private
  5013. */
  5014. function character_data($parser, $data)
  5015. {
  5016. $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
  5017. if (isset($this->message[$pos]['cdata'])) {
  5018. $this->message[$pos]['cdata'] .= $data;
  5019. }
  5020. if ($this->documentation) {
  5021. $this->documentation .= $data;
  5022. }
  5023. }
  5024. /**
  5025. * if authenticating, set user credentials here
  5026. *
  5027. * @param string $username
  5028. * @param string $password
  5029. * @param string $authtype (basic|digest|certificate|ntlm)
  5030. * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
  5031. * @access public
  5032. */
  5033. function setCredentials($username, $password, $authtype = 'basic', $certRequest = array())
  5034. {
  5035. $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
  5036. $this->appendDebug($this->varDump($certRequest));
  5037. $this->username = $username;
  5038. $this->password = $password;
  5039. $this->authtype = $authtype;
  5040. $this->certRequest = $certRequest;
  5041. }
  5042. function getBindingData($binding)
  5043. {
  5044. if (is_array($this->bindings[$binding])) {
  5045. return $this->bindings[$binding];
  5046. }
  5047. }
  5048. /**
  5049. * returns an assoc array of operation names => operation data
  5050. *
  5051. * @param string $portName WSDL port name
  5052. * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
  5053. * @return array
  5054. * @access public
  5055. */
  5056. function getOperations($portName = '', $bindingType = 'soap')
  5057. {
  5058. $ops = array();
  5059. if ($bindingType == 'soap') {
  5060. $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
  5061. } elseif ($bindingType == 'soap12') {
  5062. $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
  5063. } else {
  5064. $this->debug("getOperations bindingType $bindingType may not be supported");
  5065. }
  5066. $this->debug("getOperations for port '$portName' bindingType $bindingType");
  5067. // loop thru ports
  5068. foreach ($this->ports as $port => $portData) {
  5069. $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
  5070. if ($portName == '' || $port == $portName) {
  5071. // binding type of port matches parameter
  5072. if ($portData['bindingType'] == $bindingType) {
  5073. $this->debug("getOperations found port $port bindingType $bindingType");
  5074. //$this->debug("port data: " . $this->varDump($portData));
  5075. //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
  5076. // merge bindings
  5077. if (isset($this->bindings[$portData['binding']]['operations'])) {
  5078. $ops = array_merge($ops, $this->bindings[$portData['binding']]['operations']);
  5079. }
  5080. }
  5081. }
  5082. }
  5083. if (count($ops) == 0) {
  5084. $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
  5085. }
  5086. return $ops;
  5087. }
  5088. /**
  5089. * returns an associative array of data necessary for calling an operation
  5090. *
  5091. * @param string $operation name of operation
  5092. * @param string $bindingType type of binding eg: soap, soap12
  5093. * @return array
  5094. * @access public
  5095. */
  5096. function getOperationData($operation, $bindingType = 'soap')
  5097. {
  5098. if ($bindingType == 'soap') {
  5099. $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
  5100. } elseif ($bindingType == 'soap12') {
  5101. $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
  5102. }
  5103. // loop thru ports
  5104. foreach ($this->ports as $port => $portData) {
  5105. // binding type of port matches parameter
  5106. if ($portData['bindingType'] == $bindingType) {
  5107. // get binding
  5108. //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
  5109. foreach (array_keys($this->bindings[$portData['binding']]['operations']) as $bOperation) {
  5110. // note that we could/should also check the namespace here
  5111. if ($operation == $bOperation) {
  5112. $opData = $this->bindings[$portData['binding']]['operations'][$operation];
  5113. return $opData;
  5114. }
  5115. }
  5116. }
  5117. }
  5118. }
  5119. /**
  5120. * returns an associative array of data necessary for calling an operation
  5121. *
  5122. * @param string $soapAction soapAction for operation
  5123. * @param string $bindingType type of binding eg: soap, soap12
  5124. * @return array
  5125. * @access public
  5126. */
  5127. function getOperationDataForSoapAction($soapAction, $bindingType = 'soap')
  5128. {
  5129. if ($bindingType == 'soap') {
  5130. $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
  5131. } elseif ($bindingType == 'soap12') {
  5132. $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
  5133. }
  5134. // loop thru ports
  5135. foreach ($this->ports as $port => $portData) {
  5136. // binding type of port matches parameter
  5137. if ($portData['bindingType'] == $bindingType) {
  5138. // loop through operations for the binding
  5139. foreach ($this->bindings[$portData['binding']]['operations'] as $bOperation => $opData) {
  5140. if ($opData['soapAction'] == $soapAction) {
  5141. return $opData;
  5142. }
  5143. }
  5144. }
  5145. }
  5146. }
  5147. /**
  5148. * returns an array of information about a given type
  5149. * returns false if no type exists by the given name
  5150. *
  5151. * typeDef = array(
  5152. * 'elements' => array(), // refs to elements array
  5153. * 'restrictionBase' => '',
  5154. * 'phpType' => '',
  5155. * 'order' => '(sequence|all)',
  5156. * 'attrs' => array() // refs to attributes array
  5157. * )
  5158. *
  5159. * @param string $type the type
  5160. * @param string $ns namespace (not prefix) of the type
  5161. * @return mixed
  5162. * @access public
  5163. * @see nusoap_xmlschema
  5164. */
  5165. function getTypeDef($type, $ns)
  5166. {
  5167. $this->debug("in getTypeDef: type=$type, ns=$ns");
  5168. if ((!$ns) && isset($this->namespaces['tns'])) {
  5169. $ns = $this->namespaces['tns'];
  5170. $this->debug("in getTypeDef: type namespace forced to $ns");
  5171. }
  5172. if (!isset($this->schemas[$ns])) {
  5173. foreach ($this->schemas as $ns0 => $schema0) {
  5174. if (strcasecmp($ns, $ns0) == 0) {
  5175. $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
  5176. $ns = $ns0;
  5177. break;
  5178. }
  5179. }
  5180. }
  5181. if (isset($this->schemas[$ns])) {
  5182. $this->debug("in getTypeDef: have schema for namespace $ns");
  5183. for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
  5184. $xs = &$this->schemas[$ns][$i];
  5185. $t = $xs->getTypeDef($type);
  5186. $this->appendDebug($xs->getDebug());
  5187. $xs->clearDebug();
  5188. if ($t) {
  5189. $this->debug("in getTypeDef: found type $type");
  5190. if (!isset($t['phpType'])) {
  5191. // get info for type to tack onto the element
  5192. $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
  5193. $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
  5194. $etype = $this->getTypeDef($uqType, $ns);
  5195. if ($etype) {
  5196. $this->debug("found type for [element] $type:");
  5197. $this->debug($this->varDump($etype));
  5198. if (isset($etype['phpType'])) {
  5199. $t['phpType'] = $etype['phpType'];
  5200. }
  5201. if (isset($etype['elements'])) {
  5202. $t['elements'] = $etype['elements'];
  5203. }
  5204. if (isset($etype['attrs'])) {
  5205. $t['attrs'] = $etype['attrs'];
  5206. }
  5207. } else {
  5208. $this->debug("did not find type for [element] $type");
  5209. }
  5210. }
  5211. return $t;
  5212. }
  5213. }
  5214. $this->debug("in getTypeDef: did not find type $type");
  5215. } else {
  5216. $this->debug("in getTypeDef: do not have schema for namespace $ns");
  5217. }
  5218. return false;
  5219. }
  5220. /**
  5221. * prints html description of services
  5222. *
  5223. * @access private
  5224. */
  5225. function webDescription()
  5226. {
  5227. global $HTTP_SERVER_VARS;
  5228. if (isset($_SERVER)) {
  5229. $PHP_SELF = $_SERVER['PHP_SELF'];
  5230. } elseif (isset($HTTP_SERVER_VARS)) {
  5231. $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
  5232. } else {
  5233. $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
  5234. }
  5235. $b = '
  5236. <html><head><title>NuSOAP: ' . $this->serviceName . '</title>
  5237. <style type="text/css">
  5238. body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
  5239. p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
  5240. pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
  5241. ul { margin-top: 10px; margin-left: 20px; }
  5242. li { list-style-type: none; margin-top: 10px; color: #000000; }
  5243. .content{
  5244. margin-left: 0px; padding-bottom: 2em; }
  5245. .nav {
  5246. padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
  5247. margin-top: 10px; margin-left: 0px; color: #000000;
  5248. background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
  5249. .title {
  5250. font-family: arial; font-size: 26px; color: #ffffff;
  5251. background-color: #999999; width: 100%;
  5252. margin-left: 0px; margin-right: 0px;
  5253. padding-top: 10px; padding-bottom: 10px;}
  5254. .hidden {
  5255. position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
  5256. font-family: arial; overflow: hidden; width: 600;
  5257. padding: 20px; font-size: 10px; background-color: #999999;
  5258. layer-background-color:#FFFFFF; }
  5259. a,a:active { color: charcoal; font-weight: bold; }
  5260. a:visited { color: #666666; font-weight: bold; }
  5261. a:hover { color: cc3300; font-weight: bold; }
  5262. </style>
  5263. <script language="JavaScript" type="text/javascript">
  5264. <!--
  5265. // POP-UP CAPTIONS...
  5266. function lib_bwcheck(){ //Browsercheck (needed)
  5267. this.ver=navigator.appVersion
  5268. this.agent=navigator.userAgent
  5269. this.dom=document.getElementById?1:0
  5270. this.opera5=this.agent.indexOf("Opera 5")>-1
  5271. this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
  5272. this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
  5273. this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
  5274. this.ie=this.ie4||this.ie5||this.ie6
  5275. this.mac=this.agent.indexOf("Mac")>-1
  5276. this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
  5277. this.ns4=(document.layers && !this.dom)?1:0;
  5278. this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
  5279. return this
  5280. }
  5281. var bw = new lib_bwcheck()
  5282. //Makes crossbrowser object.
  5283. function makeObj(obj){
  5284. this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
  5285. if(!this.evnt) return false
  5286. this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
  5287. this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
  5288. this.writeIt=b_writeIt;
  5289. return this
  5290. }
  5291. // A unit of measure that will be added when setting the position of a layer.
  5292. //var px = bw.ns4||window.opera?"":"px";
  5293. function b_writeIt(text){
  5294. if (bw.ns4){this.wref.write(text);this.wref.close()}
  5295. else this.wref.innerHTML = text
  5296. }
  5297. //Shows the messages
  5298. var oDesc;
  5299. function popup(divid){
  5300. if(oDesc = new makeObj(divid)){
  5301. oDesc.css.visibility = "visible"
  5302. }
  5303. }
  5304. function popout(){ // Hides message
  5305. if(oDesc) oDesc.css.visibility = "hidden"
  5306. }
  5307. //-->
  5308. </script>
  5309. </head>
  5310. <body>
  5311. <div class=content>
  5312. <br><br>
  5313. <div class=title>' . $this->serviceName . '</div>
  5314. <div class=nav>
  5315. <p>View the <a href="' . $PHP_SELF . '?wsdl">WSDL</a> for the service.
  5316. Click on an operation name to view it&apos;s details.</p>
  5317. <ul>';
  5318. foreach ($this->getOperations() as $op => $data) {
  5319. $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
  5320. // create hidden div
  5321. $b .= "<div id='$op' class='hidden'>
  5322. <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
  5323. foreach ($data as $donnie => $marie) { // loop through opdata
  5324. if ($donnie == 'input' || $donnie == 'output') { // show input/output data
  5325. $b .= "<font color='white'>" . ucfirst($donnie) . ':</font><br>';
  5326. foreach ($marie as $captain => $tenille) { // loop through data
  5327. if ($captain == 'parts') { // loop thru parts
  5328. $b .= "&nbsp;&nbsp;$captain:<br>";
  5329. //if(is_array($tenille)){
  5330. foreach ($tenille as $joanie => $chachi) {
  5331. $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
  5332. }
  5333. //}
  5334. } else {
  5335. $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
  5336. }
  5337. }
  5338. } else {
  5339. $b .= "<font color='white'>" . ucfirst($donnie) . ":</font> $marie<br>";
  5340. }
  5341. }
  5342. $b .= '</div>';
  5343. }
  5344. $b .= '
  5345. <ul>
  5346. </div>
  5347. </div></body></html>';
  5348. return $b;
  5349. }
  5350. /**
  5351. * serialize the parsed wsdl
  5352. *
  5353. * @param mixed $debug whether to put debug=1 in endpoint URL
  5354. * @return string serialization of WSDL
  5355. * @access public
  5356. */
  5357. function serialize($debug = 0)
  5358. {
  5359. $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
  5360. $xml .= "\n<definitions";
  5361. foreach ($this->namespaces as $k => $v) {
  5362. $xml .= " xmlns:$k=\"$v\"";
  5363. }
  5364. // 10.9.02 - add poulter fix for wsdl and tns declarations
  5365. if (isset($this->namespaces['wsdl'])) {
  5366. $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
  5367. }
  5368. if (isset($this->namespaces['tns'])) {
  5369. $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
  5370. }
  5371. $xml .= '>';
  5372. // imports
  5373. if (sizeof($this->import) > 0) {
  5374. foreach ($this->import as $ns => $list) {
  5375. foreach ($list as $ii) {
  5376. if ($ii['location'] != '') {
  5377. $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
  5378. } else {
  5379. $xml .= '<import namespace="' . $ns . '" />';
  5380. }
  5381. }
  5382. }
  5383. }
  5384. // types
  5385. if (count($this->schemas) >= 1) {
  5386. $xml .= "\n<types>\n";
  5387. foreach ($this->schemas as $ns => $list) {
  5388. foreach ($list as $xs) {
  5389. $xml .= $xs->serializeSchema();
  5390. }
  5391. }
  5392. $xml .= '</types>';
  5393. }
  5394. // messages
  5395. if (count($this->messages) >= 1) {
  5396. foreach ($this->messages as $msgName => $msgParts) {
  5397. $xml .= "\n<message name=\"" . $msgName . '">';
  5398. if (is_array($msgParts)) {
  5399. foreach ($msgParts as $partName => $partType) {
  5400. // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
  5401. if (strpos($partType, ':')) {
  5402. $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
  5403. } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
  5404. // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
  5405. $typePrefix = 'xsd';
  5406. } else {
  5407. foreach ($this->typemap as $ns => $types) {
  5408. if (isset($types[$partType])) {
  5409. $typePrefix = $this->getPrefixFromNamespace($ns);
  5410. }
  5411. }
  5412. if (!isset($typePrefix)) {
  5413. die("$partType has no namespace!");
  5414. }
  5415. }
  5416. $ns = $this->getNamespaceFromPrefix($typePrefix);
  5417. $localPart = $this->getLocalPart($partType);
  5418. $typeDef = $this->getTypeDef($localPart, $ns);
  5419. if ($typeDef['typeClass'] == 'element') {
  5420. $elementortype = 'element';
  5421. if (substr($localPart, -1) == '^') {
  5422. $localPart = substr($localPart, 0, -1);
  5423. }
  5424. } else {
  5425. $elementortype = 'type';
  5426. }
  5427. $xml .= "\n" . ' <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" />';
  5428. }
  5429. }
  5430. $xml .= '</message>';
  5431. }
  5432. }
  5433. // bindings & porttypes
  5434. if (count($this->bindings) >= 1) {
  5435. $binding_xml = '';
  5436. $portType_xml = '';
  5437. foreach ($this->bindings as $bindingName => $attrs) {
  5438. $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
  5439. $binding_xml .= "\n" . ' <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
  5440. $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
  5441. foreach ($attrs['operations'] as $opName => $opParts) {
  5442. $binding_xml .= "\n" . ' <operation name="' . $opName . '">';
  5443. $binding_xml .= "\n" . ' <soap:operation soapAction="' . $opParts['soapAction'] . '" style="' . $opParts['style'] . '"/>';
  5444. if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
  5445. $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
  5446. } else {
  5447. $enc_style = '';
  5448. }
  5449. $binding_xml .= "\n" . ' <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
  5450. if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
  5451. $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
  5452. } else {
  5453. $enc_style = '';
  5454. }
  5455. $binding_xml .= "\n" . ' <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
  5456. $binding_xml .= "\n" . ' </operation>';
  5457. $portType_xml .= "\n" . ' <operation name="' . $opParts['name'] . '"';
  5458. if (isset($opParts['parameterOrder'])) {
  5459. $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
  5460. }
  5461. $portType_xml .= '>';
  5462. if (isset($opParts['documentation']) && $opParts['documentation'] != '') {
  5463. $portType_xml .= "\n" . ' <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
  5464. }
  5465. $portType_xml .= "\n" . ' <input message="tns:' . $opParts['input']['message'] . '"/>';
  5466. $portType_xml .= "\n" . ' <output message="tns:' . $opParts['output']['message'] . '"/>';
  5467. $portType_xml .= "\n" . ' </operation>';
  5468. }
  5469. $portType_xml .= "\n" . '</portType>';
  5470. $binding_xml .= "\n" . '</binding>';
  5471. }
  5472. $xml .= $portType_xml . $binding_xml;
  5473. }
  5474. // services
  5475. $xml .= "\n<service name=\"" . $this->serviceName . '">';
  5476. if (count($this->ports) >= 1) {
  5477. foreach ($this->ports as $pName => $attrs) {
  5478. $xml .= "\n" . ' <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
  5479. $xml .= "\n" . ' <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
  5480. $xml .= "\n" . ' </port>';
  5481. }
  5482. }
  5483. $xml .= "\n" . '</service>';
  5484. return $xml . "\n</definitions>";
  5485. }
  5486. /**
  5487. * determine whether a set of parameters are unwrapped
  5488. * when they are expect to be wrapped, Microsoft-style.
  5489. *
  5490. * @param string $type the type (element name) of the wrapper
  5491. * @param array $parameters the parameter values for the SOAP call
  5492. * @return boolean whether they parameters are unwrapped (and should be wrapped)
  5493. * @access private
  5494. */
  5495. function parametersMatchWrapped($type, &$parameters)
  5496. {
  5497. $this->debug("in parametersMatchWrapped type=$type, parameters=");
  5498. $this->appendDebug($this->varDump($parameters));
  5499. // split type into namespace:unqualified-type
  5500. if (strpos($type, ':')) {
  5501. $uqType = substr($type, strrpos($type, ':') + 1);
  5502. $ns = substr($type, 0, strrpos($type, ':'));
  5503. $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
  5504. if ($this->getNamespaceFromPrefix($ns)) {
  5505. $ns = $this->getNamespaceFromPrefix($ns);
  5506. $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
  5507. }
  5508. } else {
  5509. // TODO: should the type be compared to types in XSD, and the namespace
  5510. // set to XSD if the type matches?
  5511. $this->debug("in parametersMatchWrapped: No namespace for type $type");
  5512. $ns = '';
  5513. $uqType = $type;
  5514. }
  5515. // get the type information
  5516. if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
  5517. $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
  5518. return false;
  5519. }
  5520. $this->debug("in parametersMatchWrapped: found typeDef=");
  5521. $this->appendDebug($this->varDump($typeDef));
  5522. if (substr($uqType, -1) == '^') {
  5523. $uqType = substr($uqType, 0, -1);
  5524. }
  5525. $phpType = $typeDef['phpType'];
  5526. $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
  5527. $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
  5528. // we expect a complexType or element of complexType
  5529. if ($phpType != 'struct') {
  5530. $this->debug("in parametersMatchWrapped: not a struct");
  5531. return false;
  5532. }
  5533. // see whether the parameter names match the elements
  5534. if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
  5535. $elements = 0;
  5536. $matches = 0;
  5537. foreach ($typeDef['elements'] as $name => $attrs) {
  5538. if (isset($parameters[$name])) {
  5539. $this->debug("in parametersMatchWrapped: have parameter named $name");
  5540. $matches++;
  5541. } else {
  5542. $this->debug("in parametersMatchWrapped: do not have parameter named $name");
  5543. }
  5544. $elements++;
  5545. }
  5546. $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
  5547. if ($matches == 0) {
  5548. return false;
  5549. }
  5550. return true;
  5551. }
  5552. // since there are no elements for the type, if the user passed no
  5553. // parameters, the parameters match wrapped.
  5554. $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
  5555. return count($parameters) == 0;
  5556. }
  5557. /**
  5558. * serialize PHP values according to a WSDL message definition
  5559. * contrary to the method name, this is not limited to RPC
  5560. *
  5561. * TODO
  5562. * - multi-ref serialization
  5563. * - validate PHP values against type definitions, return errors if invalid
  5564. *
  5565. * @param string $operation operation name
  5566. * @param string $direction (input|output)
  5567. * @param mixed $parameters parameter value(s)
  5568. * @param string $bindingType (soap|soap12)
  5569. * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
  5570. * @access public
  5571. */
  5572. function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap')
  5573. {
  5574. $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
  5575. $this->appendDebug('parameters=' . $this->varDump($parameters));
  5576. if ($direction != 'input' && $direction != 'output') {
  5577. $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
  5578. $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
  5579. return false;
  5580. }
  5581. if (!$opData = $this->getOperationData($operation, $bindingType)) {
  5582. $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
  5583. $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
  5584. return false;
  5585. }
  5586. $this->debug('in serializeRPCParameters: opData:');
  5587. $this->appendDebug($this->varDump($opData));
  5588. // Get encoding style for output and set to current
  5589. $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
  5590. if (($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
  5591. $encodingStyle = $opData['output']['encodingStyle'];
  5592. $enc_style = $encodingStyle;
  5593. }
  5594. // set input params
  5595. $xml = '';
  5596. if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
  5597. $parts = &$opData[$direction]['parts'];
  5598. $part_count = sizeof($parts);
  5599. $style = $opData['style'];
  5600. $use = $opData[$direction]['use'];
  5601. $this->debug("have $part_count part(s) to serialize using $style/$use");
  5602. if (is_array($parameters)) {
  5603. $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
  5604. $parameter_count = count($parameters);
  5605. $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
  5606. // check for Microsoft-style wrapped parameters
  5607. if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) {
  5608. $this->debug('check whether the caller has wrapped the parameters');
  5609. if ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1) {
  5610. // TODO: consider checking here for double-wrapping, when
  5611. // service function wraps, then NuSOAP wraps again
  5612. $this->debug("change simple array to associative with 'parameters' element");
  5613. $parameters['parameters'] = $parameters[0];
  5614. unset($parameters[0]);
  5615. }
  5616. if (($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) {
  5617. $this->debug('check whether caller\'s parameters match the wrapped ones');
  5618. if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
  5619. $this->debug('wrap the parameters for the caller');
  5620. $parameters = array('parameters' => $parameters);
  5621. $parameter_count = 1;
  5622. }
  5623. }
  5624. }
  5625. foreach ($parts as $name => $type) {
  5626. $this->debug("serializing part $name of type $type");
  5627. // Track encoding style
  5628. if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
  5629. $encodingStyle = $opData[$direction]['encodingStyle'];
  5630. $enc_style = $encodingStyle;
  5631. } else {
  5632. $enc_style = false;
  5633. }
  5634. // NOTE: add error handling here
  5635. // if serializeType returns false, then catch global error and fault
  5636. if ($parametersArrayType == 'arraySimple') {
  5637. $p = array_shift($parameters);
  5638. $this->debug('calling serializeType w/indexed param');
  5639. $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
  5640. } elseif (isset($parameters[$name])) {
  5641. $this->debug('calling serializeType w/named param');
  5642. $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
  5643. } else {
  5644. // TODO: only send nillable
  5645. $this->debug('calling serializeType w/null param');
  5646. $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
  5647. }
  5648. }
  5649. } else {
  5650. $this->debug('no parameters passed.');
  5651. }
  5652. }
  5653. $this->debug("serializeRPCParameters returning: $xml");
  5654. return $xml;
  5655. }
  5656. /**
  5657. * serialize a PHP value according to a WSDL message definition
  5658. *
  5659. * TODO
  5660. * - multi-ref serialization
  5661. * - validate PHP values against type definitions, return errors if invalid
  5662. *
  5663. * @param string $operation operation name
  5664. * @param string $direction (input|output)
  5665. * @param mixed $parameters parameter value(s)
  5666. * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
  5667. * @access public
  5668. * @deprecated
  5669. */
  5670. function serializeParameters($operation, $direction, $parameters)
  5671. {
  5672. $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
  5673. $this->appendDebug('parameters=' . $this->varDump($parameters));
  5674. if ($direction != 'input' && $direction != 'output') {
  5675. $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
  5676. $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
  5677. return false;
  5678. }
  5679. if (!$opData = $this->getOperationData($operation)) {
  5680. $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
  5681. $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
  5682. return false;
  5683. }
  5684. $this->debug('opData:');
  5685. $this->appendDebug($this->varDump($opData));
  5686. // Get encoding style for output and set to current
  5687. $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
  5688. if (($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
  5689. $encodingStyle = $opData['output']['encodingStyle'];
  5690. $enc_style = $encodingStyle;
  5691. }
  5692. // set input params
  5693. $xml = '';
  5694. if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
  5695. $use = $opData[$direction]['use'];
  5696. $this->debug("use=$use");
  5697. $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
  5698. if (is_array($parameters)) {
  5699. $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
  5700. $this->debug('have ' . $parametersArrayType . ' parameters');
  5701. foreach ($opData[$direction]['parts'] as $name => $type) {
  5702. $this->debug('serializing part "' . $name . '" of type "' . $type . '"');
  5703. // Track encoding style
  5704. if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
  5705. $encodingStyle = $opData[$direction]['encodingStyle'];
  5706. $enc_style = $encodingStyle;
  5707. } else {
  5708. $enc_style = false;
  5709. }
  5710. // NOTE: add error handling here
  5711. // if serializeType returns false, then catch global error and fault
  5712. if ($parametersArrayType == 'arraySimple') {
  5713. $p = array_shift($parameters);
  5714. $this->debug('calling serializeType w/indexed param');
  5715. $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
  5716. } elseif (isset($parameters[$name])) {
  5717. $this->debug('calling serializeType w/named param');
  5718. $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
  5719. } else {
  5720. // TODO: only send nillable
  5721. $this->debug('calling serializeType w/null param');
  5722. $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
  5723. }
  5724. }
  5725. } else {
  5726. $this->debug('no parameters passed.');
  5727. }
  5728. }
  5729. $this->debug("serializeParameters returning: $xml");
  5730. return $xml;
  5731. }
  5732. /**
  5733. * serializes a PHP value according a given type definition
  5734. *
  5735. * @param string $name name of value (part or element)
  5736. * @param string $type XML schema type of value (type or element)
  5737. * @param mixed $value a native PHP value (parameter value)
  5738. * @param string $use use for part (encoded|literal)
  5739. * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
  5740. * @param boolean $unqualified a kludge for what should be XML namespace form handling
  5741. * @return string value serialized as an XML string
  5742. * @access private
  5743. */
  5744. function serializeType($name, $type, $value, $use = 'encoded', $encodingStyle = false, $unqualified = false)
  5745. {
  5746. $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
  5747. $this->appendDebug("value=" . $this->varDump($value));
  5748. if ($use == 'encoded' && $encodingStyle) {
  5749. $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
  5750. }
  5751. // if a soapval has been supplied, let its type override the WSDL
  5752. if (is_object($value) && get_class($value) == 'soapval') {
  5753. if ($value->type_ns) {
  5754. $type = $value->type_ns . ':' . $value->type;
  5755. $forceType = true;
  5756. $this->debug("in serializeType: soapval overrides type to $type");
  5757. } elseif ($value->type) {
  5758. $type = $value->type;
  5759. $forceType = true;
  5760. $this->debug("in serializeType: soapval overrides type to $type");
  5761. } else {
  5762. $forceType = false;
  5763. $this->debug("in serializeType: soapval does not override type");
  5764. }
  5765. $attrs = $value->attributes;
  5766. $value = $value->value;
  5767. $this->debug("in serializeType: soapval overrides value to $value");
  5768. if ($attrs) {
  5769. if (!is_array($value)) {
  5770. $value['!'] = $value;
  5771. }
  5772. foreach ($attrs as $n => $v) {
  5773. $value['!' . $n] = $v;
  5774. }
  5775. $this->debug("in serializeType: soapval provides attributes");
  5776. }
  5777. } else {
  5778. $forceType = false;
  5779. }
  5780. $xml = '';
  5781. if (strpos($type, ':')) {
  5782. $uqType = substr($type, strrpos($type, ':') + 1);
  5783. $ns = substr($type, 0, strrpos($type, ':'));
  5784. $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
  5785. if ($this->getNamespaceFromPrefix($ns)) {
  5786. $ns = $this->getNamespaceFromPrefix($ns);
  5787. $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
  5788. }
  5789. if ($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/') {
  5790. $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
  5791. if ($unqualified && $use == 'literal') {
  5792. $elementNS = " xmlns=\"\"";
  5793. } else {
  5794. $elementNS = '';
  5795. }
  5796. if (is_null($value)) {
  5797. if ($use == 'literal') {
  5798. // TODO: depends on minOccurs
  5799. $xml = "<$name$elementNS/>";
  5800. } else {
  5801. // TODO: depends on nillable, which should be checked before calling this method
  5802. $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
  5803. }
  5804. $this->debug("in serializeType: returning: $xml");
  5805. return $xml;
  5806. }
  5807. if ($uqType == 'Array') {
  5808. // JBoss/Axis does this sometimes
  5809. return $this->serialize_val($value, $name, false, false, false, false, $use);
  5810. }
  5811. if ($uqType == 'boolean') {
  5812. if ((is_string($value) && $value == 'false') || (!$value)) {
  5813. $value = 'false';
  5814. } else {
  5815. $value = 'true';
  5816. }
  5817. }
  5818. if ($uqType == 'string' && gettype($value) == 'string') {
  5819. $value = $this->expandEntities($value);
  5820. }
  5821. if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
  5822. $value = sprintf("%.0lf", $value);
  5823. }
  5824. // it's a scalar
  5825. // TODO: what about null/nil values?
  5826. // check type isn't a custom type extending xmlschema namespace
  5827. if (!$this->getTypeDef($uqType, $ns)) {
  5828. if ($use == 'literal') {
  5829. if ($forceType) {
  5830. $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
  5831. } else {
  5832. $xml = "<$name$elementNS>$value</$name>";
  5833. }
  5834. } else {
  5835. $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
  5836. }
  5837. $this->debug("in serializeType: returning: $xml");
  5838. return $xml;
  5839. }
  5840. $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
  5841. } elseif ($ns == 'http://xml.apache.org/xml-soap') {
  5842. $this->debug('in serializeType: appears to be Apache SOAP type');
  5843. if ($uqType == 'Map') {
  5844. $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
  5845. if (!$tt_prefix) {
  5846. $this->debug('in serializeType: Add namespace for Apache SOAP type');
  5847. $tt_prefix = 'ns' . rand(1000, 9999);
  5848. $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
  5849. // force this to be added to usedNamespaces
  5850. $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
  5851. }
  5852. $contents = '';
  5853. foreach ($value as $k => $v) {
  5854. $this->debug("serializing map element: key $k, value $v");
  5855. $contents .= '<item>';
  5856. $contents .= $this->serialize_val($k, 'key', false, false, false, false, $use);
  5857. $contents .= $this->serialize_val($v, 'value', false, false, false, false, $use);
  5858. $contents .= '</item>';
  5859. }
  5860. if ($use == 'literal') {
  5861. if ($forceType) {
  5862. $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
  5863. } else {
  5864. $xml = "<$name>$contents</$name>";
  5865. }
  5866. } else {
  5867. $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
  5868. }
  5869. $this->debug("in serializeType: returning: $xml");
  5870. return $xml;
  5871. }
  5872. $this->debug('in serializeType: Apache SOAP type, but only support Map');
  5873. }
  5874. } else {
  5875. // TODO: should the type be compared to types in XSD, and the namespace
  5876. // set to XSD if the type matches?
  5877. $this->debug("in serializeType: No namespace for type $type");
  5878. $ns = '';
  5879. $uqType = $type;
  5880. }
  5881. if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
  5882. $this->setError("$type ($uqType) is not a supported type.");
  5883. $this->debug("in serializeType: $type ($uqType) is not a supported type.");
  5884. return false;
  5885. } else {
  5886. $this->debug("in serializeType: found typeDef");
  5887. $this->appendDebug('typeDef=' . $this->varDump($typeDef));
  5888. if (substr($uqType, -1) == '^') {
  5889. $uqType = substr($uqType, 0, -1);
  5890. }
  5891. }
  5892. if (!isset($typeDef['phpType'])) {
  5893. $this->setError("$type ($uqType) has no phpType.");
  5894. $this->debug("in serializeType: $type ($uqType) has no phpType.");
  5895. return false;
  5896. }
  5897. $phpType = $typeDef['phpType'];
  5898. $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : ''));
  5899. // if php type == struct, map value to the <all> element names
  5900. if ($phpType == 'struct') {
  5901. if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
  5902. $elementName = $uqType;
  5903. if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
  5904. $elementNS = " xmlns=\"$ns\"";
  5905. } else {
  5906. $elementNS = " xmlns=\"\"";
  5907. }
  5908. } else {
  5909. $elementName = $name;
  5910. if ($unqualified) {
  5911. $elementNS = " xmlns=\"\"";
  5912. } else {
  5913. $elementNS = '';
  5914. }
  5915. }
  5916. if (is_null($value)) {
  5917. if ($use == 'literal') {
  5918. // TODO: depends on minOccurs and nillable
  5919. $xml = "<$elementName$elementNS/>";
  5920. } else {
  5921. $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
  5922. }
  5923. $this->debug("in serializeType: returning: $xml");
  5924. return $xml;
  5925. }
  5926. if (is_object($value)) {
  5927. $value = get_object_vars($value);
  5928. }
  5929. if (is_array($value)) {
  5930. $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
  5931. if ($use == 'literal') {
  5932. if ($forceType) {
  5933. $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
  5934. } else {
  5935. $xml = "<$elementName$elementNS$elementAttrs>";
  5936. }
  5937. } else {
  5938. $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
  5939. }
  5940. if (isset($typeDef['simpleContent']) && $typeDef['simpleContent'] == 'true') {
  5941. if (isset($value['!'])) {
  5942. $xml .= $value['!'];
  5943. $this->debug("in serializeType: serialized simpleContent for type $type");
  5944. } else {
  5945. $this->debug("in serializeType: no simpleContent to serialize for type $type");
  5946. }
  5947. } else {
  5948. // complexContent
  5949. $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
  5950. }
  5951. $xml .= "</$elementName>";
  5952. } else {
  5953. $this->debug("in serializeType: phpType is struct, but value is not an array");
  5954. $this->setError("phpType is struct, but value is not an array: see debug output for details");
  5955. $xml = '';
  5956. }
  5957. } elseif ($phpType == 'array') {
  5958. if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
  5959. $elementNS = " xmlns=\"$ns\"";
  5960. } else {
  5961. if ($unqualified) {
  5962. $elementNS = " xmlns=\"\"";
  5963. } else {
  5964. $elementNS = '';
  5965. }
  5966. }
  5967. if (is_null($value)) {
  5968. if ($use == 'literal') {
  5969. // TODO: depends on minOccurs
  5970. $xml = "<$name$elementNS/>";
  5971. } else {
  5972. $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
  5973. $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
  5974. ":Array\" " .
  5975. $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
  5976. ':arrayType="' .
  5977. $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
  5978. ':' .
  5979. $this->getLocalPart($typeDef['arrayType']) . "[0]\"/>";
  5980. }
  5981. $this->debug("in serializeType: returning: $xml");
  5982. return $xml;
  5983. }
  5984. if (isset($typeDef['multidimensional'])) {
  5985. $nv = array();
  5986. foreach ($value as $v) {
  5987. $cols = ',' . sizeof($v);
  5988. $nv = array_merge($nv, $v);
  5989. }
  5990. $value = $nv;
  5991. } else {
  5992. $cols = '';
  5993. }
  5994. if (is_array($value) && sizeof($value) >= 1) {
  5995. $rows = sizeof($value);
  5996. $contents = '';
  5997. foreach ($value as $k => $v) {
  5998. $this->debug("serializing array element: $k, " . (is_array($v) ? "array" : $v) . " of type: $typeDef[arrayType]");
  5999. //if (strpos($typeDef['arrayType'], ':') ) {
  6000. if (!in_array($typeDef['arrayType'], $this->typemap['http://www.w3.org/2001/XMLSchema'])) {
  6001. $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
  6002. } else {
  6003. $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
  6004. }
  6005. }
  6006. } else {
  6007. $rows = 0;
  6008. $contents = null;
  6009. }
  6010. // TODO: for now, an empty value will be serialized as a zero element
  6011. // array. Revisit this when coding the handling of null/nil values.
  6012. if ($use == 'literal') {
  6013. $xml = "<$name$elementNS>"
  6014. . $contents
  6015. . "</$name>";
  6016. } else {
  6017. $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . ':Array" ' .
  6018. $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
  6019. . ':arrayType="'
  6020. . $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
  6021. . ":" . $this->getLocalPart($typeDef['arrayType']) . "[$rows$cols]\">"
  6022. . $contents
  6023. . "</$name>";
  6024. }
  6025. } elseif ($phpType == 'scalar') {
  6026. if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
  6027. $elementNS = " xmlns=\"$ns\"";
  6028. } else {
  6029. if ($unqualified) {
  6030. $elementNS = " xmlns=\"\"";
  6031. } else {
  6032. $elementNS = '';
  6033. }
  6034. }
  6035. if ($use == 'literal') {
  6036. if ($forceType) {
  6037. $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
  6038. } else {
  6039. $xml = "<$name$elementNS>$value</$name>";
  6040. }
  6041. } else {
  6042. $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
  6043. }
  6044. }
  6045. $this->debug("in serializeType: returning: $xml");
  6046. return $xml;
  6047. }
  6048. /**
  6049. * serializes the attributes for a complexType
  6050. *
  6051. * @param array $typeDef our internal representation of an XML schema type (or element)
  6052. * @param mixed $value a native PHP value (parameter value)
  6053. * @param string $ns the namespace of the type
  6054. * @param string $uqType the local part of the type
  6055. * @return string value serialized as an XML string
  6056. * @access private
  6057. */
  6058. function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType)
  6059. {
  6060. $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
  6061. $xml = '';
  6062. if (isset($typeDef['extensionBase'])) {
  6063. $nsx = $this->getPrefix($typeDef['extensionBase']);
  6064. $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
  6065. if ($this->getNamespaceFromPrefix($nsx)) {
  6066. $nsx = $this->getNamespaceFromPrefix($nsx);
  6067. }
  6068. if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
  6069. $this->debug("serialize attributes for extension base $nsx:$uqTypex");
  6070. $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
  6071. } else {
  6072. $this->debug("extension base $nsx:$uqTypex is not a supported type");
  6073. }
  6074. }
  6075. if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
  6076. $this->debug("serialize attributes for XML Schema type $ns:$uqType");
  6077. if (is_array($value)) {
  6078. $xvalue = $value;
  6079. } elseif (is_object($value)) {
  6080. $xvalue = get_object_vars($value);
  6081. } else {
  6082. $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
  6083. $xvalue = array();
  6084. }
  6085. foreach ($typeDef['attrs'] as $aName => $attrs) {
  6086. if (isset($xvalue['!' . $aName])) {
  6087. $xname = '!' . $aName;
  6088. $this->debug("value provided for attribute $aName with key $xname");
  6089. } elseif (isset($xvalue[$aName])) {
  6090. $xname = $aName;
  6091. $this->debug("value provided for attribute $aName with key $xname");
  6092. } elseif (isset($attrs['default'])) {
  6093. $xname = '!' . $aName;
  6094. $xvalue[$xname] = $attrs['default'];
  6095. $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
  6096. } else {
  6097. $xname = '';
  6098. $this->debug("no value provided for attribute $aName");
  6099. }
  6100. if ($xname) {
  6101. $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
  6102. }
  6103. }
  6104. } else {
  6105. $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
  6106. }
  6107. return $xml;
  6108. }
  6109. /**
  6110. * serializes the elements for a complexType
  6111. *
  6112. * @param array $typeDef our internal representation of an XML schema type (or element)
  6113. * @param mixed $value a native PHP value (parameter value)
  6114. * @param string $ns the namespace of the type
  6115. * @param string $uqType the local part of the type
  6116. * @param string $use use for part (encoded|literal)
  6117. * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
  6118. * @return string value serialized as an XML string
  6119. * @access private
  6120. */
  6121. function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use = 'encoded', $encodingStyle = false)
  6122. {
  6123. $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
  6124. $xml = '';
  6125. if (isset($typeDef['extensionBase'])) {
  6126. $nsx = $this->getPrefix($typeDef['extensionBase']);
  6127. $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
  6128. if ($this->getNamespaceFromPrefix($nsx)) {
  6129. $nsx = $this->getNamespaceFromPrefix($nsx);
  6130. }
  6131. if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
  6132. $this->debug("serialize elements for extension base $nsx:$uqTypex");
  6133. $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
  6134. } else {
  6135. $this->debug("extension base $nsx:$uqTypex is not a supported type");
  6136. }
  6137. }
  6138. if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
  6139. $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
  6140. if (is_array($value)) {
  6141. $xvalue = $value;
  6142. } elseif (is_object($value)) {
  6143. $xvalue = get_object_vars($value);
  6144. } else {
  6145. $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
  6146. $xvalue = array();
  6147. }
  6148. // toggle whether all elements are present - ideally should validate against schema
  6149. if (count($typeDef['elements']) != count($xvalue)) {
  6150. $optionals = true;
  6151. }
  6152. foreach ($typeDef['elements'] as $eName => $attrs) {
  6153. if (!isset($xvalue[$eName])) {
  6154. if (isset($attrs['default'])) {
  6155. $xvalue[$eName] = $attrs['default'];
  6156. $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
  6157. }
  6158. }
  6159. // if user took advantage of a minOccurs=0, then only serialize named parameters
  6160. if (isset($optionals)
  6161. && (!isset($xvalue[$eName]))
  6162. && ((!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
  6163. ) {
  6164. if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
  6165. $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
  6166. }
  6167. // do nothing
  6168. $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
  6169. } else {
  6170. // get value
  6171. if (isset($xvalue[$eName])) {
  6172. $v = $xvalue[$eName];
  6173. } else {
  6174. $v = null;
  6175. }
  6176. if (isset($attrs['form'])) {
  6177. $unqualified = ($attrs['form'] == 'unqualified');
  6178. } else {
  6179. $unqualified = false;
  6180. }
  6181. if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
  6182. $vv = $v;
  6183. foreach ($vv as $k => $v) {
  6184. if (isset($attrs['type']) || isset($attrs['ref'])) {
  6185. // serialize schema-defined type
  6186. $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
  6187. } else {
  6188. // serialize generic type (can this ever really happen?)
  6189. $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
  6190. $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
  6191. }
  6192. }
  6193. } else {
  6194. if (is_null($v) && isset($attrs['minOccurs']) && $attrs['minOccurs'] == '0') {
  6195. // do nothing
  6196. } elseif (is_null($v) && isset($attrs['nillable']) && $attrs['nillable'] == 'true') {
  6197. // TODO: serialize a nil correctly, but for now serialize schema-defined type
  6198. $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
  6199. } elseif (isset($attrs['type']) || isset($attrs['ref'])) {
  6200. // serialize schema-defined type
  6201. $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
  6202. } else {
  6203. // serialize generic type (can this ever really happen?)
  6204. $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
  6205. $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
  6206. }
  6207. }
  6208. }
  6209. }
  6210. } else {
  6211. $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
  6212. }
  6213. return $xml;
  6214. }
  6215. /**
  6216. * adds an XML Schema complex type to the WSDL types
  6217. *
  6218. * @param string $name
  6219. * @param string $typeClass (complexType|simpleType|attribute)
  6220. * @param string $phpType currently supported are array and struct (php assoc array)
  6221. * @param string $compositor (all|sequence|choice)
  6222. * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
  6223. * @param array $elements e.g. array ( name => array(name=>'',type=>'') )
  6224. * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
  6225. * @param string $arrayType as namespace:name (xsd:string)
  6226. * @see nusoap_xmlschema
  6227. * @access public
  6228. */
  6229. function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = array(), $attrs = array(), $arrayType = '')
  6230. {
  6231. if (count($elements) > 0) {
  6232. $eElements = array();
  6233. foreach ($elements as $n => $e) {
  6234. // expand each element
  6235. $ee = array();
  6236. foreach ($e as $k => $v) {
  6237. $k = strpos($k, ':') ? $this->expandQname($k) : $k;
  6238. $v = strpos($v, ':') ? $this->expandQname($v) : $v;
  6239. $ee[$k] = $v;
  6240. }
  6241. $eElements[$n] = $ee;
  6242. }
  6243. $elements = $eElements;
  6244. }
  6245. if (count($attrs) > 0) {
  6246. foreach ($attrs as $n => $a) {
  6247. // expand each attribute
  6248. foreach ($a as $k => $v) {
  6249. $k = strpos($k, ':') ? $this->expandQname($k) : $k;
  6250. $v = strpos($v, ':') ? $this->expandQname($v) : $v;
  6251. $aa[$k] = $v;
  6252. }
  6253. $eAttrs[$n] = $aa;
  6254. }
  6255. $attrs = $eAttrs;
  6256. }
  6257. $restrictionBase = strpos($restrictionBase, ':') ? $this->expandQname($restrictionBase) : $restrictionBase;
  6258. $arrayType = strpos($arrayType, ':') ? $this->expandQname($arrayType) : $arrayType;
  6259. $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
  6260. $this->schemas[$typens][0]->addComplexType($name, $typeClass, $phpType, $compositor, $restrictionBase, $elements, $attrs, $arrayType);
  6261. }
  6262. /**
  6263. * adds an XML Schema simple type to the WSDL types
  6264. *
  6265. * @param string $name
  6266. * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
  6267. * @param string $typeClass (should always be simpleType)
  6268. * @param string $phpType (should always be scalar)
  6269. * @param array $enumeration array of values
  6270. * @see nusoap_xmlschema
  6271. * @access public
  6272. */
  6273. function addSimpleType($name, $restrictionBase = '', $typeClass = 'simpleType', $phpType = 'scalar', $enumeration = array())
  6274. {
  6275. $restrictionBase = strpos($restrictionBase, ':') ? $this->expandQname($restrictionBase) : $restrictionBase;
  6276. $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
  6277. $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
  6278. }
  6279. /**
  6280. * adds an element to the WSDL types
  6281. *
  6282. * @param array $attrs attributes that must include name and type
  6283. * @see nusoap_xmlschema
  6284. * @access public
  6285. */
  6286. function addElement($attrs)
  6287. {
  6288. $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
  6289. $this->schemas[$typens][0]->addElement($attrs);
  6290. }
  6291. /**
  6292. * register an operation with the server
  6293. *
  6294. * @param string $name operation (method) name
  6295. * @param array $in assoc array of input values: key = param name, value = param type
  6296. * @param array $out assoc array of output values: key = param name, value = param type
  6297. * @param string $namespace optional The namespace for the operation
  6298. * @param string $soapaction optional The soapaction for the operation
  6299. * @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically
  6300. * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
  6301. * @param string $documentation optional The description to include in the WSDL
  6302. * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
  6303. * @access public
  6304. */
  6305. function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = '')
  6306. {
  6307. if ($use == 'encoded' && $encodingStyle == '') {
  6308. $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
  6309. }
  6310. if ($style == 'document') {
  6311. $elements = array();
  6312. foreach ($in as $n => $t) {
  6313. $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
  6314. }
  6315. $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
  6316. $this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
  6317. $in = array('parameters' => 'tns:' . $name . '^');
  6318. $elements = array();
  6319. foreach ($out as $n => $t) {
  6320. $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
  6321. }
  6322. $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
  6323. $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified'));
  6324. $out = array('parameters' => 'tns:' . $name . 'Response' . '^');
  6325. }
  6326. // get binding
  6327. $this->bindings[$this->serviceName . 'Binding']['operations'][$name] =
  6328. array(
  6329. 'name' => $name,
  6330. 'binding' => $this->serviceName . 'Binding',
  6331. 'endpoint' => $this->endpoint,
  6332. 'soapAction' => $soapaction,
  6333. 'style' => $style,
  6334. 'input' => array(
  6335. 'use' => $use,
  6336. 'namespace' => $namespace,
  6337. 'encodingStyle' => $encodingStyle,
  6338. 'message' => $name . 'Request',
  6339. 'parts' => $in),
  6340. 'output' => array(
  6341. 'use' => $use,
  6342. 'namespace' => $namespace,
  6343. 'encodingStyle' => $encodingStyle,
  6344. 'message' => $name . 'Response',
  6345. 'parts' => $out),
  6346. 'namespace' => $namespace,
  6347. 'transport' => 'http://schemas.xmlsoap.org/soap/http',
  6348. 'documentation' => $documentation);
  6349. // add portTypes
  6350. // add messages
  6351. if ($in) {
  6352. foreach ($in as $pName => $pType) {
  6353. if (strpos($pType, ':')) {
  6354. $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ":" . $this->getLocalPart($pType);
  6355. }
  6356. $this->messages[$name . 'Request'][$pName] = $pType;
  6357. }
  6358. } else {
  6359. $this->messages[$name . 'Request'] = '0';
  6360. }
  6361. if ($out) {
  6362. foreach ($out as $pName => $pType) {
  6363. if (strpos($pType, ':')) {
  6364. $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ":" . $this->getLocalPart($pType);
  6365. }
  6366. $this->messages[$name . 'Response'][$pName] = $pType;
  6367. }
  6368. } else {
  6369. $this->messages[$name . 'Response'] = '0';
  6370. }
  6371. return true;
  6372. }
  6373. }
  6374. /**
  6375. *
  6376. * nusoap_parser class parses SOAP XML messages into native PHP values
  6377. *
  6378. * @author Dietrich Ayala <dietrich@ganx4.com>
  6379. * @author Scott Nichol <snichol@users.sourceforge.net>
  6380. * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  6381. * @access public
  6382. */
  6383. class nusoap_parser extends nusoap_base
  6384. {
  6385. var $xml = '';
  6386. var $xml_encoding = '';
  6387. var $method = '';
  6388. var $root_struct = '';
  6389. var $root_struct_name = '';
  6390. var $root_struct_namespace = '';
  6391. var $root_header = '';
  6392. var $document = ''; // incoming SOAP body (text)
  6393. // determines where in the message we are (envelope,header,body,method)
  6394. var $status = '';
  6395. var $position = 0;
  6396. var $depth = 0;
  6397. var $default_namespace = '';
  6398. var $namespaces = array();
  6399. var $message = array();
  6400. var $parent = '';
  6401. var $fault = false;
  6402. var $fault_code = '';
  6403. var $fault_str = '';
  6404. var $fault_detail = '';
  6405. var $depth_array = array();
  6406. var $debug_flag = true;
  6407. var $soapresponse = null; // parsed SOAP Body
  6408. var $soapheader = null; // parsed SOAP Header
  6409. var $responseHeaders = ''; // incoming SOAP headers (text)
  6410. var $body_position = 0;
  6411. // for multiref parsing:
  6412. // array of id => pos
  6413. var $ids = array();
  6414. // array of id => hrefs => pos
  6415. var $multirefs = array();
  6416. // toggle for auto-decoding element content
  6417. var $decode_utf8 = true;
  6418. /**
  6419. * constructor that actually does the parsing
  6420. *
  6421. * @param string $xml SOAP message
  6422. * @param string $encoding character encoding scheme of message
  6423. * @param string $method method for which XML is parsed (unused?)
  6424. * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
  6425. * @access public
  6426. */
  6427. function __construct($xml, $encoding = 'UTF-8', $method = '', $decode_utf8 = true)
  6428. {
  6429. parent::__construct();
  6430. $this->xml = $xml;
  6431. $this->xml_encoding = $encoding;
  6432. $this->method = $method;
  6433. $this->decode_utf8 = $decode_utf8;
  6434. // Check whether content has been read.
  6435. if (!empty($xml)) {
  6436. // Check XML encoding
  6437. $pos_xml = strpos($xml, '<?xml');
  6438. if ($pos_xml !== false) {
  6439. $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
  6440. if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
  6441. $xml_encoding = $res[1];
  6442. if (strtoupper($xml_encoding) != $encoding) {
  6443. $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
  6444. $this->debug($err);
  6445. if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
  6446. $this->setError($err);
  6447. return;
  6448. }
  6449. // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
  6450. } else {
  6451. $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
  6452. }
  6453. } else {
  6454. $this->debug('No encoding specified in XML declaration');
  6455. }
  6456. } else {
  6457. $this->debug('No XML declaration');
  6458. }
  6459. $this->debug('Entering nusoap_parser(), length=' . strlen($xml) . ', encoding=' . $encoding);
  6460. // Create an XML parser - why not xml_parser_create_ns?
  6461. $this->parser = xml_parser_create($this->xml_encoding);
  6462. // Set the options for parsing the XML data.
  6463. //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
  6464. xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
  6465. xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
  6466. // Set the object for the parser.
  6467. xml_set_object($this->parser, $this);
  6468. // Set the element handlers for the parser.
  6469. xml_set_element_handler($this->parser, 'start_element', 'end_element');
  6470. xml_set_character_data_handler($this->parser, 'character_data');
  6471. // Parse the XML file.
  6472. if (!xml_parse($this->parser, $xml, true)) {
  6473. // Display an error message.
  6474. $err = sprintf('XML error parsing SOAP payload on line %d: %s',
  6475. xml_get_current_line_number($this->parser),
  6476. xml_error_string(xml_get_error_code($this->parser)));
  6477. $this->debug($err);
  6478. $this->debug("XML payload:\n" . $xml);
  6479. $this->setError($err);
  6480. } else {
  6481. $this->debug('in nusoap_parser ctor, message:');
  6482. $this->appendDebug($this->varDump($this->message));
  6483. $this->debug('parsed successfully, found root struct: ' . $this->root_struct . ' of name ' . $this->root_struct_name);
  6484. // get final value
  6485. $this->soapresponse = $this->message[$this->root_struct]['result'];
  6486. // get header value
  6487. if ($this->root_header != '' && isset($this->message[$this->root_header]['result'])) {
  6488. $this->soapheader = $this->message[$this->root_header]['result'];
  6489. }
  6490. // resolve hrefs/ids
  6491. if (sizeof($this->multirefs) > 0) {
  6492. foreach ($this->multirefs as $id => $hrefs) {
  6493. $this->debug('resolving multirefs for id: ' . $id);
  6494. $idVal = $this->buildVal($this->ids[$id]);
  6495. if (is_array($idVal) && isset($idVal['!id'])) {
  6496. unset($idVal['!id']);
  6497. }
  6498. foreach ($hrefs as $refPos => $ref) {
  6499. $this->debug('resolving href at pos ' . $refPos);
  6500. $this->multirefs[$id][$refPos] = $idVal;
  6501. }
  6502. }
  6503. }
  6504. }
  6505. xml_parser_free($this->parser);
  6506. unset($this->parser);
  6507. } else {
  6508. $this->debug('xml was empty, didn\'t parse!');
  6509. $this->setError('xml was empty, didn\'t parse!');
  6510. }
  6511. }
  6512. /**
  6513. * start-element handler
  6514. *
  6515. * @param resource $parser XML parser object
  6516. * @param string $name element name
  6517. * @param array $attrs associative array of attributes
  6518. * @access private
  6519. */
  6520. function start_element($parser, $name, $attrs)
  6521. {
  6522. // position in a total number of elements, starting from 0
  6523. // update class level pos
  6524. $pos = $this->position++;
  6525. // and set mine
  6526. $this->message[$pos] = array('pos' => $pos, 'children' => '', 'cdata' => '');
  6527. // depth = how many levels removed from root?
  6528. // set mine as current global depth and increment global depth value
  6529. $this->message[$pos]['depth'] = $this->depth++;
  6530. // else add self as child to whoever the current parent is
  6531. if ($pos != 0) {
  6532. $this->message[$this->parent]['children'] .= '|' . $pos;
  6533. }
  6534. // set my parent
  6535. $this->message[$pos]['parent'] = $this->parent;
  6536. // set self as current parent
  6537. $this->parent = $pos;
  6538. // set self as current value for this depth
  6539. $this->depth_array[$this->depth] = $pos;
  6540. // get element prefix
  6541. if (strpos($name, ':')) {
  6542. // get ns prefix
  6543. $prefix = substr($name, 0, strpos($name, ':'));
  6544. // get unqualified name
  6545. $name = substr(strstr($name, ':'), 1);
  6546. }
  6547. // set status
  6548. if ($name == 'Envelope' && $this->status == '') {
  6549. $this->status = 'envelope';
  6550. } elseif ($name == 'Header' && $this->status == 'envelope') {
  6551. $this->root_header = $pos;
  6552. $this->status = 'header';
  6553. } elseif ($name == 'Body' && $this->status == 'envelope') {
  6554. $this->status = 'body';
  6555. $this->body_position = $pos;
  6556. // set method
  6557. } elseif ($this->status == 'body' && $pos == ($this->body_position + 1)) {
  6558. $this->status = 'method';
  6559. $this->root_struct_name = $name;
  6560. $this->root_struct = $pos;
  6561. $this->message[$pos]['type'] = 'struct';
  6562. $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
  6563. }
  6564. // set my status
  6565. $this->message[$pos]['status'] = $this->status;
  6566. // set name
  6567. $this->message[$pos]['name'] = htmlspecialchars($name);
  6568. // set attrs
  6569. $this->message[$pos]['attrs'] = $attrs;
  6570. // loop through atts, logging ns and type declarations
  6571. $attstr = '';
  6572. foreach ($attrs as $key => $value) {
  6573. $key_prefix = $this->getPrefix($key);
  6574. $key_localpart = $this->getLocalPart($key);
  6575. // if ns declarations, add to class level array of valid namespaces
  6576. if ($key_prefix == 'xmlns') {
  6577. if (preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/', $value)) {
  6578. $this->XMLSchemaVersion = $value;
  6579. $this->namespaces['xsd'] = $this->XMLSchemaVersion;
  6580. $this->namespaces['xsi'] = $this->XMLSchemaVersion . '-instance';
  6581. }
  6582. $this->namespaces[$key_localpart] = $value;
  6583. // set method namespace
  6584. if ($name == $this->root_struct_name) {
  6585. $this->methodNamespace = $value;
  6586. }
  6587. // if it's a type declaration, set type
  6588. } elseif ($key_localpart == 'type') {
  6589. if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') {
  6590. // do nothing: already processed arrayType
  6591. } else {
  6592. $value_prefix = $this->getPrefix($value);
  6593. $value_localpart = $this->getLocalPart($value);
  6594. $this->message[$pos]['type'] = $value_localpart;
  6595. $this->message[$pos]['typePrefix'] = $value_prefix;
  6596. if (isset($this->namespaces[$value_prefix])) {
  6597. $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
  6598. } elseif (isset($attrs['xmlns:' . $value_prefix])) {
  6599. $this->message[$pos]['type_namespace'] = $attrs['xmlns:' . $value_prefix];
  6600. }
  6601. // should do something here with the namespace of specified type?
  6602. }
  6603. } elseif ($key_localpart == 'arrayType') {
  6604. $this->message[$pos]['type'] = 'array';
  6605. /* do arrayType ereg here
  6606. [1] arrayTypeValue ::= atype asize
  6607. [2] atype ::= QName rank*
  6608. [3] rank ::= '[' (',')* ']'
  6609. [4] asize ::= '[' length~ ']'
  6610. [5] length ::= nextDimension* Digit+
  6611. [6] nextDimension ::= Digit+ ','
  6612. */
  6613. $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/';
  6614. if (preg_match($expr, $value, $regs)) {
  6615. $this->message[$pos]['typePrefix'] = $regs[1];
  6616. $this->message[$pos]['arrayTypePrefix'] = $regs[1];
  6617. if (isset($this->namespaces[$regs[1]])) {
  6618. $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
  6619. } elseif (isset($attrs['xmlns:' . $regs[1]])) {
  6620. $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:' . $regs[1]];
  6621. }
  6622. $this->message[$pos]['arrayType'] = $regs[2];
  6623. $this->message[$pos]['arraySize'] = $regs[3];
  6624. $this->message[$pos]['arrayCols'] = $regs[4];
  6625. }
  6626. // specifies nil value (or not)
  6627. } elseif ($key_localpart == 'nil') {
  6628. $this->message[$pos]['nil'] = ($value == 'true' || $value == '1');
  6629. // some other attribute
  6630. } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
  6631. $this->message[$pos]['xattrs']['!' . $key] = $value;
  6632. }
  6633. if ($key == 'xmlns') {
  6634. $this->default_namespace = $value;
  6635. }
  6636. // log id
  6637. if ($key == 'id') {
  6638. $this->ids[$value] = $pos;
  6639. }
  6640. // root
  6641. if ($key_localpart == 'root' && $value == 1) {
  6642. $this->status = 'method';
  6643. $this->root_struct_name = $name;
  6644. $this->root_struct = $pos;
  6645. $this->debug("found root struct $this->root_struct_name, pos $pos");
  6646. }
  6647. // for doclit
  6648. $attstr .= " $key=\"$value\"";
  6649. }
  6650. // get namespace - must be done after namespace atts are processed
  6651. if (isset($prefix)) {
  6652. $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
  6653. $this->default_namespace = $this->namespaces[$prefix];
  6654. } else {
  6655. $this->message[$pos]['namespace'] = $this->default_namespace;
  6656. }
  6657. if ($this->status == 'header') {
  6658. if ($this->root_header != $pos) {
  6659. $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
  6660. }
  6661. } elseif ($this->root_struct_name != '') {
  6662. $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
  6663. }
  6664. }
  6665. /**
  6666. * end-element handler
  6667. *
  6668. * @param resource $parser XML parser object
  6669. * @param string $name element name
  6670. * @access private
  6671. */
  6672. function end_element($parser, $name)
  6673. {
  6674. // position of current element is equal to the last value left in depth_array for my depth
  6675. $pos = $this->depth_array[$this->depth--];
  6676. // get element prefix
  6677. if (strpos($name, ':')) {
  6678. // get ns prefix
  6679. $prefix = substr($name, 0, strpos($name, ':'));
  6680. // get unqualified name
  6681. $name = substr(strstr($name, ':'), 1);
  6682. }
  6683. // build to native type
  6684. if (isset($this->body_position) && $pos > $this->body_position) {
  6685. // deal w/ multirefs
  6686. if (isset($this->message[$pos]['attrs']['href'])) {
  6687. // get id
  6688. $id = substr($this->message[$pos]['attrs']['href'], 1);
  6689. // add placeholder to href array
  6690. $this->multirefs[$id][$pos] = 'placeholder';
  6691. // add set a reference to it as the result value
  6692. $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
  6693. // build complexType values
  6694. } elseif ($this->message[$pos]['children'] != '') {
  6695. // if result has already been generated (struct/array)
  6696. if (!isset($this->message[$pos]['result'])) {
  6697. $this->message[$pos]['result'] = $this->buildVal($pos);
  6698. }
  6699. // build complexType values of attributes and possibly simpleContent
  6700. } elseif (isset($this->message[$pos]['xattrs'])) {
  6701. if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
  6702. $this->message[$pos]['xattrs']['!'] = null;
  6703. } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
  6704. if (isset($this->message[$pos]['type'])) {
  6705. $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
  6706. } else {
  6707. $parent = $this->message[$pos]['parent'];
  6708. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
  6709. $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
  6710. } else {
  6711. $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
  6712. }
  6713. }
  6714. }
  6715. $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
  6716. // set value of simpleType (or nil complexType)
  6717. } else {
  6718. //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
  6719. if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
  6720. $this->message[$pos]['xattrs']['!'] = null;
  6721. } elseif (isset($this->message[$pos]['type'])) {
  6722. $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
  6723. } else {
  6724. $parent = $this->message[$pos]['parent'];
  6725. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
  6726. $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
  6727. } else {
  6728. $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
  6729. }
  6730. }
  6731. /* add value to parent's result, if parent is struct/array
  6732. $parent = $this->message[$pos]['parent'];
  6733. if($this->message[$parent]['type'] != 'map'){
  6734. if(strtolower($this->message[$parent]['type']) == 'array'){
  6735. $this->message[$parent]['result'][] = $this->message[$pos]['result'];
  6736. } else {
  6737. $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
  6738. }
  6739. }
  6740. */
  6741. }
  6742. }
  6743. // for doclit
  6744. if ($this->status == 'header') {
  6745. if ($this->root_header != $pos) {
  6746. $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
  6747. }
  6748. } elseif ($pos >= $this->root_struct) {
  6749. $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
  6750. }
  6751. // switch status
  6752. if ($pos == $this->root_struct) {
  6753. $this->status = 'body';
  6754. $this->root_struct_namespace = $this->message[$pos]['namespace'];
  6755. } elseif ($pos == $this->root_header) {
  6756. $this->status = 'envelope';
  6757. } elseif ($name == 'Body' && $this->status == 'body') {
  6758. $this->status = 'envelope';
  6759. } elseif ($name == 'Header' && $this->status == 'header') { // will never happen
  6760. $this->status = 'envelope';
  6761. } elseif ($name == 'Envelope' && $this->status == 'envelope') {
  6762. $this->status = '';
  6763. }
  6764. // set parent back to my parent
  6765. $this->parent = $this->message[$pos]['parent'];
  6766. }
  6767. /**
  6768. * element content handler
  6769. *
  6770. * @param resource $parser XML parser object
  6771. * @param string $data element content
  6772. * @access private
  6773. */
  6774. function character_data($parser, $data)
  6775. {
  6776. $pos = $this->depth_array[$this->depth];
  6777. if ($this->xml_encoding == 'UTF-8') {
  6778. // TODO: add an option to disable this for folks who want
  6779. // raw UTF-8 that, e.g., might not map to iso-8859-1
  6780. // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
  6781. if ($this->decode_utf8) {
  6782. $data = utf8_decode($data);
  6783. }
  6784. }
  6785. $this->message[$pos]['cdata'] .= $data;
  6786. // for doclit
  6787. if ($this->status == 'header') {
  6788. $this->responseHeaders .= $data;
  6789. } else {
  6790. $this->document .= $data;
  6791. }
  6792. }
  6793. /**
  6794. * get the parsed message (SOAP Body)
  6795. *
  6796. * @return mixed
  6797. * @access public
  6798. * @deprecated use get_soapbody instead
  6799. */
  6800. function get_response()
  6801. {
  6802. return $this->soapresponse;
  6803. }
  6804. /**
  6805. * get the parsed SOAP Body (null if there was none)
  6806. *
  6807. * @return mixed
  6808. * @access public
  6809. */
  6810. function get_soapbody()
  6811. {
  6812. return $this->soapresponse;
  6813. }
  6814. /**
  6815. * get the parsed SOAP Header (null if there was none)
  6816. *
  6817. * @return mixed
  6818. * @access public
  6819. */
  6820. function get_soapheader()
  6821. {
  6822. return $this->soapheader;
  6823. }
  6824. /**
  6825. * get the unparsed SOAP Header
  6826. *
  6827. * @return string XML or empty if no Header
  6828. * @access public
  6829. */
  6830. function getHeaders()
  6831. {
  6832. return $this->responseHeaders;
  6833. }
  6834. /**
  6835. * decodes simple types into PHP variables
  6836. *
  6837. * @param string $value value to decode
  6838. * @param string $type XML type to decode
  6839. * @param string $typens XML type namespace to decode
  6840. * @return mixed PHP value
  6841. * @access private
  6842. */
  6843. function decodeSimple($value, $type, $typens)
  6844. {
  6845. // TODO: use the namespace!
  6846. if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
  6847. return (string) $value;
  6848. }
  6849. if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
  6850. return (int) $value;
  6851. }
  6852. if ($type == 'float' || $type == 'double' || $type == 'decimal') {
  6853. return (double) $value;
  6854. }
  6855. if ($type == 'boolean') {
  6856. if (strtolower($value) == 'false' || strtolower($value) == 'f') {
  6857. return false;
  6858. }
  6859. return (boolean) $value;
  6860. }
  6861. if ($type == 'base64' || $type == 'base64Binary') {
  6862. $this->debug('Decode base64 value');
  6863. return base64_decode($value);
  6864. }
  6865. // obscure numeric types
  6866. if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
  6867. || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
  6868. || $type == 'unsignedInt'
  6869. || $type == 'unsignedShort' || $type == 'unsignedByte'
  6870. ) {
  6871. return (int) $value;
  6872. }
  6873. // bogus: parser treats array with no elements as a simple type
  6874. if ($type == 'array') {
  6875. return array();
  6876. }
  6877. // everything else
  6878. return (string) $value;
  6879. }
  6880. /**
  6881. * builds response structures for compound values (arrays/structs)
  6882. * and scalars
  6883. *
  6884. * @param integer $pos position in node tree
  6885. * @return mixed PHP value
  6886. * @access private
  6887. */
  6888. function buildVal($pos)
  6889. {
  6890. if (!isset($this->message[$pos]['type'])) {
  6891. $this->message[$pos]['type'] = '';
  6892. }
  6893. $this->debug('in buildVal() for ' . $this->message[$pos]['name'] . "(pos $pos) of type " . $this->message[$pos]['type']);
  6894. // if there are children...
  6895. if ($this->message[$pos]['children'] != '') {
  6896. $params = [];
  6897. $this->debug('in buildVal, there are children');
  6898. $children = explode('|', $this->message[$pos]['children']);
  6899. array_shift($children); // knock off empty
  6900. // md array
  6901. if (isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != '') {
  6902. $r = 0; // rowcount
  6903. $c = 0; // colcount
  6904. foreach ($children as $child_pos) {
  6905. $this->debug("in buildVal, got an MD array element: $r, $c");
  6906. $params[$r][] = $this->message[$child_pos]['result'];
  6907. $c++;
  6908. if ($c == $this->message[$pos]['arrayCols']) {
  6909. $c = 0;
  6910. $r++;
  6911. }
  6912. }
  6913. // array
  6914. } elseif ($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array') {
  6915. $this->debug('in buildVal, adding array ' . $this->message[$pos]['name']);
  6916. foreach ($children as $child_pos) {
  6917. $params[] = &$this->message[$child_pos]['result'];
  6918. }
  6919. // apache Map type: java hashtable
  6920. } elseif ($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
  6921. $this->debug('in buildVal, Java Map ' . $this->message[$pos]['name']);
  6922. foreach ($children as $child_pos) {
  6923. $kv = explode("|", $this->message[$child_pos]['children']);
  6924. $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
  6925. }
  6926. // generic compound type
  6927. //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
  6928. } else {
  6929. // Apache Vector type: treat as an array
  6930. $this->debug('in buildVal, adding Java Vector or generic compound type ' . $this->message[$pos]['name']);
  6931. if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
  6932. $notstruct = 1;
  6933. } else {
  6934. $notstruct = 0;
  6935. }
  6936. //
  6937. foreach ($children as $child_pos) {
  6938. if ($notstruct) {
  6939. $params[] = &$this->message[$child_pos]['result'];
  6940. } else {
  6941. if (isset($params[$this->message[$child_pos]['name']])) {
  6942. // de-serialize repeated element name into an array
  6943. if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) {
  6944. $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
  6945. }
  6946. $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
  6947. } else {
  6948. $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
  6949. }
  6950. }
  6951. }
  6952. }
  6953. if (isset($this->message[$pos]['xattrs'])) {
  6954. $this->debug('in buildVal, handling attributes');
  6955. foreach ($this->message[$pos]['xattrs'] as $n => $v) {
  6956. $params[$n] = $v;
  6957. }
  6958. }
  6959. // handle simpleContent
  6960. if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
  6961. $this->debug('in buildVal, handling simpleContent');
  6962. if (isset($this->message[$pos]['type'])) {
  6963. $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
  6964. } else {
  6965. $parent = $this->message[$pos]['parent'];
  6966. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
  6967. $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
  6968. } else {
  6969. $params['!'] = $this->message[$pos]['cdata'];
  6970. }
  6971. }
  6972. }
  6973. $ret = is_array($params) ? $params : array();
  6974. $this->debug('in buildVal, return:');
  6975. $this->appendDebug($this->varDump($ret));
  6976. return $ret;
  6977. } else {
  6978. $this->debug('in buildVal, no children, building scalar');
  6979. $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
  6980. if (isset($this->message[$pos]['type'])) {
  6981. $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
  6982. $this->debug("in buildVal, return: $ret");
  6983. return $ret;
  6984. }
  6985. $parent = $this->message[$pos]['parent'];
  6986. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
  6987. $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
  6988. $this->debug("in buildVal, return: $ret");
  6989. return $ret;
  6990. }
  6991. $ret = $this->message[$pos]['cdata'];
  6992. $this->debug("in buildVal, return: $ret");
  6993. return $ret;
  6994. }
  6995. }
  6996. }
  6997. /**
  6998. * Backward compatibility
  6999. */
  7000. class soap_parser extends nusoap_parser
  7001. {
  7002. }
  7003. /**
  7004. *
  7005. * [nu]soapclient higher level class for easy usage.
  7006. *
  7007. * usage:
  7008. *
  7009. * // instantiate client with server info
  7010. * $soapclient = new nusoap_client( string path [ ,mixed wsdl] );
  7011. *
  7012. * // call method, get results
  7013. * echo $soapclient->call( string methodname [ ,array parameters] );
  7014. *
  7015. * // bye bye client
  7016. * unset($soapclient);
  7017. *
  7018. * @author Dietrich Ayala <dietrich@ganx4.com>
  7019. * @author Scott Nichol <snichol@users.sourceforge.net>
  7020. * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  7021. * @access public
  7022. */
  7023. class nusoap_client extends nusoap_base
  7024. {
  7025. var $username = ''; // Username for HTTP authentication
  7026. var $password = ''; // Password for HTTP authentication
  7027. var $authtype = ''; // Type of HTTP authentication
  7028. var $certRequest = array(); // Certificate for HTTP SSL authentication
  7029. var $requestHeaders = false; // SOAP headers in request (text)
  7030. var $responseHeaders = ''; // SOAP headers from response (incomplete namespace resolution) (text)
  7031. var $responseHeader = null; // SOAP Header from response (parsed)
  7032. var $document = ''; // SOAP body response portion (incomplete namespace resolution) (text)
  7033. var $endpoint;
  7034. var $forceEndpoint = ''; // overrides WSDL endpoint
  7035. var $proxyhost = '';
  7036. var $proxyport = '';
  7037. var $proxyusername = '';
  7038. var $proxypassword = '';
  7039. var $portName = ''; // port name to use in WSDL
  7040. var $xml_encoding = ''; // character set encoding of incoming (response) messages
  7041. var $http_encoding = false;
  7042. var $timeout = 0; // HTTP connection timeout
  7043. var $response_timeout = 30; // HTTP response timeout
  7044. var $endpointType = ''; // soap|wsdl, empty for WSDL initialization error
  7045. var $persistentConnection = false;
  7046. var $defaultRpcParams = false; // This is no longer used
  7047. var $request = ''; // HTTP request
  7048. var $response = ''; // HTTP response
  7049. var $responseData = ''; // SOAP payload of response
  7050. var $cookies = array(); // Cookies from response or for request
  7051. var $decode_utf8 = true; // toggles whether the parser decodes element content w/ utf8_decode()
  7052. var $operations = array(); // WSDL operations, empty for WSDL initialization error
  7053. var $curl_options = array(); // User-specified cURL options
  7054. var $bindingType = ''; // WSDL operation binding type
  7055. var $use_curl = false; // whether to always try to use cURL
  7056. /*
  7057. * fault related variables
  7058. */
  7059. /**
  7060. * @var fault
  7061. * @access public
  7062. */
  7063. var $fault;
  7064. /**
  7065. * @var faultcode
  7066. * @access public
  7067. */
  7068. var $faultcode;
  7069. /**
  7070. * @var faultstring
  7071. * @access public
  7072. */
  7073. var $faultstring;
  7074. /**
  7075. * @var faultdetail
  7076. * @access public
  7077. */
  7078. var $faultdetail;
  7079. /**
  7080. * constructor
  7081. *
  7082. * @param mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
  7083. * @param mixed $wsdl optional, set to 'wsdl' or true if using WSDL
  7084. * @param string $proxyhost optional
  7085. * @param string $proxyport optional
  7086. * @param string $proxyusername optional
  7087. * @param string $proxypassword optional
  7088. * @param integer $timeout set the connection timeout
  7089. * @param integer $response_timeout set the response timeout
  7090. * @param string $portName optional portName in WSDL document
  7091. * @access public
  7092. */
  7093. function __construct($endpoint, $wsdl = false, $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $portName = '')
  7094. {
  7095. parent::__construct();
  7096. $this->endpoint = $endpoint;
  7097. $this->proxyhost = $proxyhost;
  7098. $this->proxyport = $proxyport;
  7099. $this->proxyusername = $proxyusername;
  7100. $this->proxypassword = $proxypassword;
  7101. $this->timeout = $timeout;
  7102. $this->response_timeout = $response_timeout;
  7103. $this->portName = $portName;
  7104. $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
  7105. $this->appendDebug('endpoint=' . $this->varDump($endpoint));
  7106. // make values
  7107. if ($wsdl) {
  7108. if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) {
  7109. $this->wsdl = $endpoint;
  7110. $this->endpoint = $this->wsdl->wsdl;
  7111. $this->wsdlFile = $this->endpoint;
  7112. $this->debug('existing wsdl instance created from ' . $this->endpoint);
  7113. $this->checkWSDL();
  7114. } else {
  7115. $this->wsdlFile = $this->endpoint;
  7116. $this->wsdl = null;
  7117. $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint);
  7118. }
  7119. $this->endpointType = 'wsdl';
  7120. } else {
  7121. $this->debug("instantiate SOAP with endpoint at $endpoint");
  7122. $this->endpointType = 'soap';
  7123. }
  7124. }
  7125. /**
  7126. * calls method, returns PHP native type
  7127. *
  7128. * @param string $operation SOAP server URL or path
  7129. * @param mixed $params An array, associative or simple, of the parameters
  7130. * for the method call, or a string that is the XML
  7131. * for the call. For rpc style, this call will
  7132. * wrap the XML in a tag named after the method, as
  7133. * well as the SOAP Envelope and Body. For document
  7134. * style, this will only wrap with the Envelope and Body.
  7135. * IMPORTANT: when using an array with document style,
  7136. * in which case there
  7137. * is really one parameter, the root of the fragment
  7138. * used in the call, which encloses what programmers
  7139. * normally think of parameters. A parameter array
  7140. * *must* include the wrapper.
  7141. * @param string $namespace optional method namespace (WSDL can override)
  7142. * @param string $soapAction optional SOAPAction value (WSDL can override)
  7143. * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
  7144. * @param boolean $rpcParams optional (no longer used)
  7145. * @param string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
  7146. * @param string $use optional (encoded|literal) the use when serializing parameters (WSDL can override)
  7147. * @return mixed response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors
  7148. * @access public
  7149. */
  7150. function call($operation, $params = array(), $namespace = 'http://tempuri.org', $soapAction = '', $headers = false, $rpcParams = null, $style = 'rpc', $use = 'encoded')
  7151. {
  7152. $this->operation = $operation;
  7153. $this->fault = false;
  7154. $this->setError('');
  7155. $this->request = '';
  7156. $this->response = '';
  7157. $this->responseData = '';
  7158. $this->faultstring = '';
  7159. $this->faultcode = '';
  7160. $this->opData = array();
  7161. $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
  7162. $this->appendDebug('params=' . $this->varDump($params));
  7163. $this->appendDebug('headers=' . $this->varDump($headers));
  7164. if ($headers) {
  7165. $this->requestHeaders = $headers;
  7166. }
  7167. if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
  7168. $this->loadWSDL();
  7169. if ($this->getError()) {
  7170. return false;
  7171. }
  7172. }
  7173. // serialize parameters
  7174. if ($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)) {
  7175. // use WSDL for operation
  7176. $this->opData = $opData;
  7177. $this->debug("found operation");
  7178. $this->appendDebug('opData=' . $this->varDump($opData));
  7179. if (isset($opData['soapAction'])) {
  7180. $soapAction = $opData['soapAction'];
  7181. }
  7182. if (!$this->forceEndpoint) {
  7183. $this->endpoint = $opData['endpoint'];
  7184. } else {
  7185. $this->endpoint = $this->forceEndpoint;
  7186. }
  7187. $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace;
  7188. $style = $opData['style'];
  7189. $use = $opData['input']['use'];
  7190. // add ns to ns array
  7191. if ($namespace != '' && !isset($this->wsdl->namespaces[$namespace])) {
  7192. $nsPrefix = 'ns' . rand(1000, 9999);
  7193. $this->wsdl->namespaces[$nsPrefix] = $namespace;
  7194. }
  7195. $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
  7196. // serialize payload
  7197. if (is_string($params)) {
  7198. $this->debug("serializing param string for WSDL operation $operation");
  7199. $payload = $params;
  7200. } elseif (is_array($params)) {
  7201. $this->debug("serializing param array for WSDL operation $operation");
  7202. $payload = $this->wsdl->serializeRPCParameters($operation, 'input', $params, $this->bindingType);
  7203. } else {
  7204. $this->debug('params must be array or string');
  7205. $this->setError('params must be array or string');
  7206. return false;
  7207. }
  7208. $usedNamespaces = $this->wsdl->usedNamespaces;
  7209. if (isset($opData['input']['encodingStyle'])) {
  7210. $encodingStyle = $opData['input']['encodingStyle'];
  7211. } else {
  7212. $encodingStyle = '';
  7213. }
  7214. $this->appendDebug($this->wsdl->getDebug());
  7215. $this->wsdl->clearDebug();
  7216. if ($errstr = $this->wsdl->getError()) {
  7217. $this->debug('got wsdl error: ' . $errstr);
  7218. $this->setError('wsdl error: ' . $errstr);
  7219. return false;
  7220. }
  7221. } elseif ($this->endpointType == 'wsdl') {
  7222. // operation not in WSDL
  7223. $this->appendDebug($this->wsdl->getDebug());
  7224. $this->wsdl->clearDebug();
  7225. $this->setError('operation ' . $operation . ' not present in WSDL.');
  7226. $this->debug("operation '$operation' not present in WSDL.");
  7227. return false;
  7228. } else {
  7229. // no WSDL
  7230. //$this->namespaces['ns1'] = $namespace;
  7231. $nsPrefix = 'ns' . rand(1000, 9999);
  7232. // serialize
  7233. $payload = '';
  7234. if (is_string($params)) {
  7235. $this->debug("serializing param string for operation $operation");
  7236. $payload = $params;
  7237. } elseif (is_array($params)) {
  7238. $this->debug("serializing param array for operation $operation");
  7239. foreach ($params as $k => $v) {
  7240. $payload .= $this->serialize_val($v, $k, false, false, false, false, $use);
  7241. }
  7242. } else {
  7243. $this->debug('params must be array or string');
  7244. $this->setError('params must be array or string');
  7245. return false;
  7246. }
  7247. $usedNamespaces = array();
  7248. if ($use == 'encoded') {
  7249. $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
  7250. } else {
  7251. $encodingStyle = '';
  7252. }
  7253. }
  7254. // wrap RPC calls with method element
  7255. if ($style == 'rpc') {
  7256. if ($use == 'literal') {
  7257. $this->debug("wrapping RPC request with literal method element");
  7258. if ($namespace) {
  7259. // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
  7260. $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
  7261. $payload .
  7262. "</$nsPrefix:$operation>";
  7263. } else {
  7264. $payload = "<$operation>" . $payload . "</$operation>";
  7265. }
  7266. } else {
  7267. $this->debug("wrapping RPC request with encoded method element");
  7268. if ($namespace) {
  7269. $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
  7270. $payload .
  7271. "</$nsPrefix:$operation>";
  7272. } else {
  7273. $payload = "<$operation>" .
  7274. $payload .
  7275. "</$operation>";
  7276. }
  7277. }
  7278. }
  7279. // serialize envelope
  7280. $soapmsg = $this->serializeEnvelope($payload, $this->requestHeaders, $usedNamespaces, $style, $use, $encodingStyle);
  7281. $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
  7282. $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
  7283. // send
  7284. $return = $this->send($this->getHTTPBody($soapmsg), $soapAction, $this->timeout, $this->response_timeout);
  7285. if ($errstr = $this->getError()) {
  7286. $this->debug('Error: ' . $errstr);
  7287. return false;
  7288. } else {
  7289. $this->return = $return;
  7290. $this->debug('sent message successfully and got a(n) ' . gettype($return));
  7291. $this->appendDebug('return=' . $this->varDump($return));
  7292. // fault?
  7293. if (is_array($return) && isset($return['faultcode'])) {
  7294. $this->debug('got fault');
  7295. $this->setError($return['faultcode'] . ': ' . $return['faultstring']);
  7296. $this->fault = true;
  7297. foreach ($return as $k => $v) {
  7298. $this->$k = $v;
  7299. if (is_array($v)) {
  7300. $this->debug("$k = " . json_encode($v));
  7301. } else {
  7302. $this->debug("$k = $v<br>");
  7303. }
  7304. }
  7305. return $return;
  7306. } elseif ($style == 'document') {
  7307. // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
  7308. // we are only going to return the first part here...sorry about that
  7309. return $return;
  7310. } else {
  7311. // array of return values
  7312. if (is_array($return)) {
  7313. // multiple 'out' parameters, which we return wrapped up
  7314. // in the array
  7315. if (sizeof($return) > 1) {
  7316. return $return;
  7317. }
  7318. // single 'out' parameter (normally the return value)
  7319. $return = array_shift($return);
  7320. $this->debug('return shifted value: ');
  7321. $this->appendDebug($this->varDump($return));
  7322. return $return;
  7323. // nothing returned (ie, echoVoid)
  7324. } else {
  7325. return "";
  7326. }
  7327. }
  7328. }
  7329. }
  7330. /**
  7331. * check WSDL passed as an instance or pulled from an endpoint
  7332. *
  7333. * @access private
  7334. */
  7335. function checkWSDL()
  7336. {
  7337. $this->appendDebug($this->wsdl->getDebug());
  7338. $this->wsdl->clearDebug();
  7339. $this->debug('checkWSDL');
  7340. // catch errors
  7341. if ($errstr = $this->wsdl->getError()) {
  7342. $this->appendDebug($this->wsdl->getDebug());
  7343. $this->wsdl->clearDebug();
  7344. $this->debug('got wsdl error: ' . $errstr);
  7345. $this->setError('wsdl error: ' . $errstr);
  7346. } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap')) {
  7347. $this->appendDebug($this->wsdl->getDebug());
  7348. $this->wsdl->clearDebug();
  7349. $this->bindingType = 'soap';
  7350. $this->debug('got ' . count($this->operations) . ' operations from wsdl ' . $this->wsdlFile . ' for binding type ' . $this->bindingType);
  7351. } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12')) {
  7352. $this->appendDebug($this->wsdl->getDebug());
  7353. $this->wsdl->clearDebug();
  7354. $this->bindingType = 'soap12';
  7355. $this->debug('got ' . count($this->operations) . ' operations from wsdl ' . $this->wsdlFile . ' for binding type ' . $this->bindingType);
  7356. $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************');
  7357. } else {
  7358. $this->appendDebug($this->wsdl->getDebug());
  7359. $this->wsdl->clearDebug();
  7360. $this->debug('getOperations returned false');
  7361. $this->setError('no operations defined in the WSDL document!');
  7362. }
  7363. }
  7364. /**
  7365. * instantiate wsdl object and parse wsdl file
  7366. *
  7367. * @access public
  7368. */
  7369. function loadWSDL()
  7370. {
  7371. $this->debug('instantiating wsdl class with doc: ' . $this->wsdlFile);
  7372. $this->wsdl = new wsdl('', $this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword, $this->timeout, $this->response_timeout, $this->curl_options, $this->use_curl);
  7373. $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest);
  7374. $this->wsdl->fetchWSDL($this->wsdlFile);
  7375. $this->checkWSDL();
  7376. }
  7377. /**
  7378. * get available data pertaining to an operation
  7379. *
  7380. * @param string $operation operation name
  7381. * @return array array of data pertaining to the operation
  7382. * @access public
  7383. */
  7384. function getOperationData($operation)
  7385. {
  7386. if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
  7387. $this->loadWSDL();
  7388. if ($this->getError()) {
  7389. return false;
  7390. }
  7391. }
  7392. if (isset($this->operations[$operation])) {
  7393. return $this->operations[$operation];
  7394. }
  7395. $this->debug("No data for operation: $operation");
  7396. }
  7397. /**
  7398. * send the SOAP message
  7399. *
  7400. * Note: if the operation has multiple return values
  7401. * the return value of this method will be an array
  7402. * of those values.
  7403. *
  7404. * @param string $msg a SOAPx4 soapmsg object
  7405. * @param string $soapaction SOAPAction value
  7406. * @param integer $timeout set connection timeout in seconds
  7407. * @param integer $response_timeout set response timeout in seconds
  7408. * @return mixed native PHP types.
  7409. * @access private
  7410. */
  7411. function send($msg, $soapaction = '', $timeout = 0, $response_timeout = 30)
  7412. {
  7413. $this->checkCookies();
  7414. // detect transport
  7415. switch (true) {
  7416. // http(s)
  7417. case preg_match('/^http/', $this->endpoint):
  7418. $this->debug('transporting via HTTP');
  7419. if ($this->persistentConnection == true && is_object($this->persistentConnection)) {
  7420. $http =& $this->persistentConnection;
  7421. } else {
  7422. $http = new soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl);
  7423. if ($this->persistentConnection) {
  7424. $http->usePersistentConnection();
  7425. }
  7426. }
  7427. $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
  7428. $http->setSOAPAction($soapaction);
  7429. if ($this->proxyhost && $this->proxyport) {
  7430. $http->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
  7431. }
  7432. if ($this->authtype != '') {
  7433. $http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
  7434. }
  7435. if ($this->http_encoding != '') {
  7436. $http->setEncoding($this->http_encoding);
  7437. }
  7438. $this->debug('sending message, length=' . strlen($msg));
  7439. if (preg_match('/^http:/', $this->endpoint)) {
  7440. //if(strpos($this->endpoint,'http:')){
  7441. $this->responseData = $http->send($msg, $timeout, $response_timeout, $this->cookies);
  7442. } elseif (preg_match('/^https/', $this->endpoint)) {
  7443. //} elseif(strpos($this->endpoint,'https:')){
  7444. //if(phpversion() == '4.3.0-dev'){
  7445. //$response = $http->send($msg,$timeout,$response_timeout);
  7446. //$this->request = $http->outgoing_payload;
  7447. //$this->response = $http->incoming_payload;
  7448. //} else
  7449. $this->responseData = $http->sendHTTPS($msg, $timeout, $response_timeout, $this->cookies);
  7450. } else {
  7451. $this->setError('no http/s in endpoint url');
  7452. }
  7453. $this->request = $http->outgoing_payload;
  7454. $this->response = $http->incoming_payload;
  7455. $this->appendDebug($http->getDebug());
  7456. $this->UpdateCookies($http->incoming_cookies);
  7457. // save transport object if using persistent connections
  7458. if ($this->persistentConnection) {
  7459. $http->clearDebug();
  7460. if (!is_object($this->persistentConnection)) {
  7461. $this->persistentConnection = $http;
  7462. }
  7463. }
  7464. if ($err = $http->getError()) {
  7465. $this->setError('HTTP Error: ' . $err);
  7466. return false;
  7467. } elseif ($this->getError()) {
  7468. return false;
  7469. } else {
  7470. $this->debug('got response, length=' . strlen($this->responseData) . ' type=' . $http->incoming_headers['content-type']);
  7471. return $this->parseResponse($http->incoming_headers, $this->responseData);
  7472. }
  7473. break;
  7474. default:
  7475. $this->setError('no transport found, or selected transport is not yet supported!');
  7476. return false;
  7477. break;
  7478. }
  7479. }
  7480. /**
  7481. * processes SOAP message returned from server
  7482. *
  7483. * @param array $headers The HTTP headers
  7484. * @param string $data unprocessed response data from server
  7485. * @return mixed value of the message, decoded into a PHP type
  7486. * @access private
  7487. */
  7488. function parseResponse($headers, $data)
  7489. {
  7490. $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:');
  7491. $this->appendDebug($this->varDump($headers));
  7492. if (!isset($headers['content-type'])) {
  7493. $this->setError('Response not of type '.$this->contentType.' (no content-type header)');
  7494. return false;
  7495. }
  7496. if (!strstr($headers['content-type'], $this->contentType)) {
  7497. $this->setError('Response not of type '.$this->contentType.': ' . $headers['content-type']);
  7498. return false;
  7499. }
  7500. if (strpos($headers['content-type'], '=')) {
  7501. $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
  7502. $this->debug('Got response encoding: ' . $enc);
  7503. if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
  7504. $this->xml_encoding = strtoupper($enc);
  7505. } else {
  7506. $this->xml_encoding = 'US-ASCII';
  7507. }
  7508. } else {
  7509. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
  7510. $this->xml_encoding = 'ISO-8859-1';
  7511. }
  7512. $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
  7513. $parser = new nusoap_parser($data, $this->xml_encoding, $this->operations, $this->decode_utf8);
  7514. // add parser debug data to our debug
  7515. $this->appendDebug($parser->getDebug());
  7516. // if parse errors
  7517. if ($errstr = $parser->getError()) {
  7518. $this->setError($errstr);
  7519. // destroy the parser object
  7520. unset($parser);
  7521. return false;
  7522. } else {
  7523. // get SOAP headers
  7524. $this->responseHeaders = $parser->getHeaders();
  7525. // get SOAP headers
  7526. $this->responseHeader = $parser->get_soapheader();
  7527. // get decoded message
  7528. $return = $parser->get_soapbody();
  7529. // add document for doclit support
  7530. $this->document = $parser->document;
  7531. // destroy the parser object
  7532. unset($parser);
  7533. // return decode message
  7534. return $return;
  7535. }
  7536. }
  7537. /**
  7538. * sets user-specified cURL options
  7539. *
  7540. * @param mixed $option The cURL option (always integer?)
  7541. * @param mixed $value The cURL option value
  7542. * @access public
  7543. */
  7544. function setCurlOption($option, $value)
  7545. {
  7546. $this->debug("setCurlOption option=$option, value=");
  7547. $this->appendDebug($this->varDump($value));
  7548. $this->curl_options[$option] = $value;
  7549. }
  7550. /**
  7551. * sets the SOAP endpoint, which can override WSDL
  7552. *
  7553. * @param string $endpoint The endpoint URL to use, or empty string or false to prevent override
  7554. * @access public
  7555. */
  7556. function setEndpoint($endpoint)
  7557. {
  7558. $this->debug("setEndpoint(\"$endpoint\")");
  7559. $this->forceEndpoint = $endpoint;
  7560. }
  7561. /**
  7562. * set the SOAP headers
  7563. *
  7564. * @param mixed $headers String of XML with SOAP header content, or array of soapval objects for SOAP headers
  7565. * @access public
  7566. */
  7567. function setHeaders($headers)
  7568. {
  7569. $this->debug("setHeaders headers=");
  7570. $this->appendDebug($this->varDump($headers));
  7571. $this->requestHeaders = $headers;
  7572. }
  7573. /**
  7574. * get the SOAP response headers (namespace resolution incomplete)
  7575. *
  7576. * @return string
  7577. * @access public
  7578. */
  7579. function getHeaders()
  7580. {
  7581. return $this->responseHeaders;
  7582. }
  7583. /**
  7584. * get the SOAP response Header (parsed)
  7585. *
  7586. * @return mixed
  7587. * @access public
  7588. */
  7589. function getHeader()
  7590. {
  7591. return $this->responseHeader;
  7592. }
  7593. /**
  7594. * set proxy info here
  7595. *
  7596. * @param string $proxyhost
  7597. * @param string $proxyport
  7598. * @param string $proxyusername
  7599. * @param string $proxypassword
  7600. * @access public
  7601. */
  7602. function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '')
  7603. {
  7604. $this->proxyhost = $proxyhost;
  7605. $this->proxyport = $proxyport;
  7606. $this->proxyusername = $proxyusername;
  7607. $this->proxypassword = $proxypassword;
  7608. }
  7609. /**
  7610. * if authenticating, set user credentials here
  7611. *
  7612. * @param string $username
  7613. * @param string $password
  7614. * @param string $authtype (basic|digest|certificate|ntlm)
  7615. * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
  7616. * @access public
  7617. */
  7618. function setCredentials($username, $password, $authtype = 'basic', $certRequest = array())
  7619. {
  7620. $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
  7621. $this->appendDebug($this->varDump($certRequest));
  7622. $this->username = $username;
  7623. $this->password = $password;
  7624. $this->authtype = $authtype;
  7625. $this->certRequest = $certRequest;
  7626. }
  7627. /**
  7628. * use HTTP encoding
  7629. *
  7630. * @param string $enc HTTP encoding
  7631. * @access public
  7632. */
  7633. function setHTTPEncoding($enc = 'gzip, deflate')
  7634. {
  7635. $this->debug("setHTTPEncoding(\"$enc\")");
  7636. $this->http_encoding = $enc;
  7637. }
  7638. /**
  7639. * Set whether to try to use cURL connections if possible
  7640. *
  7641. * @param boolean $use Whether to try to use cURL
  7642. * @access public
  7643. */
  7644. function setUseCURL($use)
  7645. {
  7646. $this->debug("setUseCURL($use)");
  7647. $this->use_curl = $use;
  7648. }
  7649. /**
  7650. * use HTTP persistent connections if possible
  7651. *
  7652. * @access public
  7653. */
  7654. function useHTTPPersistentConnection()
  7655. {
  7656. $this->debug("useHTTPPersistentConnection");
  7657. $this->persistentConnection = true;
  7658. }
  7659. /**
  7660. * gets the default RPC parameter setting.
  7661. * If true, default is that call params are like RPC even for document style.
  7662. * Each call() can override this value.
  7663. *
  7664. * This is no longer used.
  7665. *
  7666. * @return boolean
  7667. * @access public
  7668. * @deprecated
  7669. */
  7670. function getDefaultRpcParams()
  7671. {
  7672. return $this->defaultRpcParams;
  7673. }
  7674. /**
  7675. * sets the default RPC parameter setting.
  7676. * If true, default is that call params are like RPC even for document style
  7677. * Each call() can override this value.
  7678. *
  7679. * This is no longer used.
  7680. *
  7681. * @param boolean $rpcParams
  7682. * @access public
  7683. * @deprecated
  7684. */
  7685. function setDefaultRpcParams($rpcParams)
  7686. {
  7687. $this->defaultRpcParams = $rpcParams;
  7688. }
  7689. /**
  7690. * dynamically creates an instance of a proxy class,
  7691. * allowing user to directly call methods from wsdl
  7692. *
  7693. * @return object soap_proxy object
  7694. * @access public
  7695. */
  7696. function getProxy()
  7697. {
  7698. $r = rand();
  7699. $evalStr = $this->_getProxyClassCode($r);
  7700. //$this->debug("proxy class: $evalStr");
  7701. if ($this->getError()) {
  7702. $this->debug("Error from _getProxyClassCode, so return null");
  7703. return null;
  7704. }
  7705. // eval the class
  7706. eval($evalStr);
  7707. // instantiate proxy object
  7708. eval("\$proxy = new nusoap_proxy_$r('');");
  7709. // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
  7710. $proxy->endpointType = 'wsdl';
  7711. $proxy->wsdlFile = $this->wsdlFile;
  7712. $proxy->wsdl = $this->wsdl;
  7713. $proxy->operations = $this->operations;
  7714. $proxy->defaultRpcParams = $this->defaultRpcParams;
  7715. // transfer other state
  7716. $proxy->soap_defencoding = $this->soap_defencoding;
  7717. $proxy->username = $this->username;
  7718. $proxy->password = $this->password;
  7719. $proxy->authtype = $this->authtype;
  7720. $proxy->certRequest = $this->certRequest;
  7721. $proxy->requestHeaders = $this->requestHeaders;
  7722. $proxy->endpoint = $this->endpoint;
  7723. $proxy->forceEndpoint = $this->forceEndpoint;
  7724. $proxy->proxyhost = $this->proxyhost;
  7725. $proxy->proxyport = $this->proxyport;
  7726. $proxy->proxyusername = $this->proxyusername;
  7727. $proxy->proxypassword = $this->proxypassword;
  7728. $proxy->http_encoding = $this->http_encoding;
  7729. $proxy->timeout = $this->timeout;
  7730. $proxy->response_timeout = $this->response_timeout;
  7731. $proxy->persistentConnection = &$this->persistentConnection;
  7732. $proxy->decode_utf8 = $this->decode_utf8;
  7733. $proxy->curl_options = $this->curl_options;
  7734. $proxy->bindingType = $this->bindingType;
  7735. $proxy->use_curl = $this->use_curl;
  7736. return $proxy;
  7737. }
  7738. /**
  7739. * dynamically creates proxy class code
  7740. *
  7741. * @return string PHP/NuSOAP code for the proxy class
  7742. * @access private
  7743. */
  7744. function _getProxyClassCode($r)
  7745. {
  7746. $this->debug("in getProxy endpointType=$this->endpointType");
  7747. $this->appendDebug("wsdl=" . $this->varDump($this->wsdl));
  7748. if ($this->endpointType != 'wsdl') {
  7749. $evalStr = 'A proxy can only be created for a WSDL client';
  7750. $this->setError($evalStr);
  7751. $evalStr = "echo \"$evalStr\";";
  7752. return $evalStr;
  7753. }
  7754. if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
  7755. $this->loadWSDL();
  7756. if ($this->getError()) {
  7757. return "echo \"" . $this->getError() . "\";";
  7758. }
  7759. }
  7760. $evalStr = '';
  7761. foreach ($this->operations as $operation => $opData) {
  7762. if ($operation != '') {
  7763. // create param string and param comment string
  7764. if (sizeof($opData['input']['parts']) > 0) {
  7765. $paramStr = '';
  7766. $paramArrayStr = '';
  7767. $paramCommentStr = '';
  7768. foreach ($opData['input']['parts'] as $name => $type) {
  7769. $paramStr .= "\$$name, ";
  7770. $paramArrayStr .= "'$name' => \$$name, ";
  7771. $paramCommentStr .= "$type \$$name, ";
  7772. }
  7773. $paramStr = substr($paramStr, 0, strlen($paramStr) - 2);
  7774. $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr) - 2);
  7775. $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr) - 2);
  7776. } else {
  7777. $paramStr = '';
  7778. $paramArrayStr = '';
  7779. $paramCommentStr = 'void';
  7780. }
  7781. $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
  7782. $evalStr .= "// $paramCommentStr
  7783. function " . str_replace('.', '__', $operation) . "($paramStr) {
  7784. \$params = array($paramArrayStr);
  7785. return \$this->call('$operation', \$params, '" . $opData['namespace'] . "', '" . (isset($opData['soapAction']) ? $opData['soapAction'] : '') . "');
  7786. }
  7787. ";
  7788. unset($paramStr);
  7789. unset($paramCommentStr);
  7790. }
  7791. }
  7792. $evalStr = 'class nusoap_proxy_' . $r . ' extends nusoap_client {
  7793. ' . $evalStr . '
  7794. }';
  7795. return $evalStr;
  7796. }
  7797. /**
  7798. * dynamically creates proxy class code
  7799. *
  7800. * @return string PHP/NuSOAP code for the proxy class
  7801. * @access public
  7802. */
  7803. function getProxyClassCode()
  7804. {
  7805. $r = rand();
  7806. return $this->_getProxyClassCode($r);
  7807. }
  7808. /**
  7809. * gets the HTTP body for the current request.
  7810. *
  7811. * @param string $soapmsg The SOAP payload
  7812. * @return string The HTTP body, which includes the SOAP payload
  7813. * @access private
  7814. */
  7815. function getHTTPBody($soapmsg)
  7816. {
  7817. return $soapmsg;
  7818. }
  7819. /**
  7820. * gets the HTTP content type for the current request.
  7821. *
  7822. * Note: getHTTPBody must be called before this.
  7823. *
  7824. * @return string the HTTP content type for the current request.
  7825. * @access private
  7826. */
  7827. function getHTTPContentType()
  7828. {
  7829. return $this->contentType;
  7830. }
  7831. /**
  7832. * allows you to change the HTTP ContentType of the request.
  7833. *
  7834. * @param string $contentTypeNew
  7835. * @return void
  7836. */
  7837. function setHTTPContentType($contentTypeNew = "text/xml"){
  7838. $this->contentType = $contentTypeNew;
  7839. }
  7840. /**
  7841. * gets the HTTP content type charset for the current request.
  7842. * returns false for non-text content types.
  7843. *
  7844. * Note: getHTTPBody must be called before this.
  7845. *
  7846. * @return string the HTTP content type charset for the current request.
  7847. * @access private
  7848. */
  7849. function getHTTPContentTypeCharset()
  7850. {
  7851. return $this->soap_defencoding;
  7852. }
  7853. /*
  7854. * whether or not parser should decode utf8 element content
  7855. *
  7856. * @return always returns true
  7857. * @access public
  7858. */
  7859. function decodeUTF8($bool)
  7860. {
  7861. $this->decode_utf8 = $bool;
  7862. return true;
  7863. }
  7864. /**
  7865. * adds a new Cookie into $this->cookies array
  7866. *
  7867. * @param string $name Cookie Name
  7868. * @param string $value Cookie Value
  7869. * @return boolean if cookie-set was successful returns true, else false
  7870. * @access public
  7871. */
  7872. function setCookie($name, $value)
  7873. {
  7874. if (strlen($name) == 0) {
  7875. return false;
  7876. }
  7877. $this->cookies[] = array('name' => $name, 'value' => $value);
  7878. return true;
  7879. }
  7880. /**
  7881. * gets all Cookies
  7882. *
  7883. * @return array with all internal cookies
  7884. * @access public
  7885. */
  7886. function getCookies()
  7887. {
  7888. return $this->cookies;
  7889. }
  7890. /**
  7891. * checks all Cookies and delete those which are expired
  7892. *
  7893. * @return boolean always return true
  7894. * @access private
  7895. */
  7896. function checkCookies()
  7897. {
  7898. if (sizeof($this->cookies) == 0) {
  7899. return true;
  7900. }
  7901. $this->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies');
  7902. $curr_cookies = $this->cookies;
  7903. $this->cookies = array();
  7904. foreach ($curr_cookies as $cookie) {
  7905. if (!is_array($cookie)) {
  7906. $this->debug('Remove cookie that is not an array');
  7907. continue;
  7908. }
  7909. if ((isset($cookie['expires'])) && (!empty($cookie['expires']))) {
  7910. if (strtotime($cookie['expires']) > time()) {
  7911. $this->cookies[] = $cookie;
  7912. } else {
  7913. $this->debug('Remove expired cookie ' . $cookie['name']);
  7914. }
  7915. } else {
  7916. $this->cookies[] = $cookie;
  7917. }
  7918. }
  7919. $this->debug('checkCookie: ' . sizeof($this->cookies) . ' cookies left in array');
  7920. return true;
  7921. }
  7922. /**
  7923. * updates the current cookies with a new set
  7924. *
  7925. * @param array $cookies new cookies with which to update current ones
  7926. * @return boolean always return true
  7927. * @access private
  7928. */
  7929. function UpdateCookies($cookies)
  7930. {
  7931. if (sizeof($this->cookies) == 0) {
  7932. // no existing cookies: take whatever is new
  7933. if (sizeof($cookies) > 0) {
  7934. $this->debug('Setting new cookie(s)');
  7935. $this->cookies = $cookies;
  7936. }
  7937. return true;
  7938. }
  7939. if (sizeof($cookies) == 0) {
  7940. // no new cookies: keep what we've got
  7941. return true;
  7942. }
  7943. // merge
  7944. foreach ($cookies as $newCookie) {
  7945. if (!is_array($newCookie)) {
  7946. continue;
  7947. }
  7948. if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) {
  7949. continue;
  7950. }
  7951. $newName = $newCookie['name'];
  7952. $found = false;
  7953. for ($i = 0; $i < count($this->cookies); $i++) {
  7954. $cookie = $this->cookies[$i];
  7955. if (!is_array($cookie)) {
  7956. continue;
  7957. }
  7958. if (!isset($cookie['name'])) {
  7959. continue;
  7960. }
  7961. if ($newName != $cookie['name']) {
  7962. continue;
  7963. }
  7964. $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
  7965. $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
  7966. if ($newDomain != $domain) {
  7967. continue;
  7968. }
  7969. $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
  7970. $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
  7971. if ($newPath != $path) {
  7972. continue;
  7973. }
  7974. $this->cookies[$i] = $newCookie;
  7975. $found = true;
  7976. $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
  7977. break;
  7978. }
  7979. if (!$found) {
  7980. $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
  7981. $this->cookies[] = $newCookie;
  7982. }
  7983. }
  7984. return true;
  7985. }
  7986. }
  7987. if (!extension_loaded('soap')) {
  7988. /**
  7989. * For backwards compatiblity, define soapclient unless the PHP SOAP extension is loaded.
  7990. */
  7991. class soapclient extends nusoap_client
  7992. {
  7993. }
  7994. }
  7995. /**
  7996. * caches instances of the wsdl class
  7997. *
  7998. * @author Scott Nichol <snichol@users.sourceforge.net>
  7999. * @author Ingo Fischer <ingo@apollon.de>
  8000. * @version $Id: class.wsdlcache.php,v 1.7 2007/04/17 16:34:03 snichol Exp $
  8001. * @access public
  8002. */
  8003. class nusoap_wsdlcache {
  8004. /**
  8005. * @var resource
  8006. * @access private
  8007. */
  8008. var $fplock;
  8009. /**
  8010. * @var integer
  8011. * @access private
  8012. */
  8013. var $cache_lifetime;
  8014. /**
  8015. * @var string
  8016. * @access private
  8017. */
  8018. var $cache_dir;
  8019. /**
  8020. * @var string
  8021. * @access public
  8022. */
  8023. var $debug_str = '';
  8024. /**
  8025. * constructor
  8026. *
  8027. * @param string $cache_dir directory for cache-files
  8028. * @param integer $cache_lifetime lifetime for caching-files in seconds or 0 for unlimited
  8029. * @access public
  8030. */
  8031. function __construct($cache_dir='.', $cache_lifetime=0) {
  8032. $this->fplock = array();
  8033. $this->cache_dir = $cache_dir != '' ? $cache_dir : '.';
  8034. $this->cache_lifetime = $cache_lifetime;
  8035. }
  8036. /**
  8037. * creates the filename used to cache a wsdl instance
  8038. *
  8039. * @param string $wsdl The URL of the wsdl instance
  8040. * @return string The filename used to cache the instance
  8041. * @access private
  8042. */
  8043. function createFilename($wsdl) {
  8044. return $this->cache_dir.'/wsdlcache-' . md5($wsdl);
  8045. }
  8046. /**
  8047. * adds debug data to the class level debug string
  8048. *
  8049. * @param string $string debug data
  8050. * @access private
  8051. */
  8052. function debug($string){
  8053. $this->debug_str .= get_class($this).": $string\n";
  8054. }
  8055. /**
  8056. * gets a wsdl instance from the cache
  8057. *
  8058. * @param string $wsdl The URL of the wsdl instance
  8059. * @return object wsdl The cached wsdl instance, null if the instance is not in the cache
  8060. * @access public
  8061. */
  8062. function get($wsdl) {
  8063. $filename = $this->createFilename($wsdl);
  8064. if ($this->obtainMutex($filename, "r")) {
  8065. // check for expired WSDL that must be removed from the cache
  8066. if ($this->cache_lifetime > 0) {
  8067. if (file_exists($filename) && (time() - filemtime($filename) > $this->cache_lifetime)) {
  8068. unlink($filename);
  8069. $this->debug("Expired $wsdl ($filename) from cache");
  8070. $this->releaseMutex($filename);
  8071. return null;
  8072. }
  8073. }
  8074. // see what there is to return
  8075. if (!file_exists($filename)) {
  8076. $this->debug("$wsdl ($filename) not in cache (1)");
  8077. $this->releaseMutex($filename);
  8078. return null;
  8079. }
  8080. $fp = @fopen($filename, "r");
  8081. if ($fp) {
  8082. $s = implode("", @file($filename));
  8083. fclose($fp);
  8084. $this->debug("Got $wsdl ($filename) from cache");
  8085. } else {
  8086. $s = null;
  8087. $this->debug("$wsdl ($filename) not in cache (2)");
  8088. }
  8089. $this->releaseMutex($filename);
  8090. return (!is_null($s)) ? unserialize($s) : null;
  8091. } else {
  8092. $this->debug("Unable to obtain mutex for $filename in get");
  8093. }
  8094. return null;
  8095. }
  8096. /**
  8097. * obtains the local mutex
  8098. *
  8099. * @param string $filename The Filename of the Cache to lock
  8100. * @param string $mode The open-mode ("r" or "w") or the file - affects lock-mode
  8101. * @return boolean Lock successfully obtained ?!
  8102. * @access private
  8103. */
  8104. function obtainMutex($filename, $mode) {
  8105. if (isset($this->fplock[md5($filename)])) {
  8106. $this->debug("Lock for $filename already exists");
  8107. return false;
  8108. }
  8109. $this->fplock[md5($filename)] = fopen($filename.".lock", "w");
  8110. if ($mode == "r") {
  8111. return flock($this->fplock[md5($filename)], LOCK_SH);
  8112. } else {
  8113. return flock($this->fplock[md5($filename)], LOCK_EX);
  8114. }
  8115. }
  8116. /**
  8117. * adds a wsdl instance to the cache
  8118. *
  8119. * @param object wsdl $wsdl_instance The wsdl instance to add
  8120. * @return boolean WSDL successfully cached
  8121. * @access public
  8122. */
  8123. function put($wsdl_instance) {
  8124. $filename = $this->createFilename($wsdl_instance->wsdl);
  8125. $s = serialize($wsdl_instance);
  8126. if ($this->obtainMutex($filename, "w")) {
  8127. $fp = fopen($filename, "w");
  8128. if (! $fp) {
  8129. $this->debug("Cannot write $wsdl_instance->wsdl ($filename) in cache");
  8130. $this->releaseMutex($filename);
  8131. return false;
  8132. }
  8133. fputs($fp, $s);
  8134. fclose($fp);
  8135. $this->debug("Put $wsdl_instance->wsdl ($filename) in cache");
  8136. $this->releaseMutex($filename);
  8137. return true;
  8138. } else {
  8139. $this->debug("Unable to obtain mutex for $filename in put");
  8140. }
  8141. return false;
  8142. }
  8143. /**
  8144. * releases the local mutex
  8145. *
  8146. * @param string $filename The Filename of the Cache to lock
  8147. * @return boolean Lock successfully released
  8148. * @access private
  8149. */
  8150. function releaseMutex($filename) {
  8151. $ret = flock($this->fplock[md5($filename)], LOCK_UN);
  8152. fclose($this->fplock[md5($filename)]);
  8153. unset($this->fplock[md5($filename)]);
  8154. if (! $ret) {
  8155. $this->debug("Not able to release lock for $filename");
  8156. }
  8157. return $ret;
  8158. }
  8159. /**
  8160. * removes a wsdl instance from the cache
  8161. *
  8162. * @param string $wsdl The URL of the wsdl instance
  8163. * @return boolean Whether there was an instance to remove
  8164. * @access public
  8165. */
  8166. function remove($wsdl) {
  8167. $filename = $this->createFilename($wsdl);
  8168. if (!file_exists($filename)) {
  8169. $this->debug("$wsdl ($filename) not in cache to be removed");
  8170. return false;
  8171. }
  8172. // ignore errors obtaining mutex
  8173. $this->obtainMutex($filename, "w");
  8174. $ret = unlink($filename);
  8175. $this->debug("Removed ($ret) $wsdl ($filename) from cache");
  8176. $this->releaseMutex($filename);
  8177. return $ret;
  8178. }
  8179. }
  8180. /**
  8181. * For backward compatibility
  8182. */
  8183. class wsdlcache extends nusoap_wsdlcache {
  8184. }