WSDL.php 83 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921
  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more |
  17. // | Authors: Dietrich Ayala <dietrich@ganx4.com> Original Author |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: WSDL.php,v 1.51.2.1 2003/12/18 21:15:17 arnaud Exp $
  21. //
  22. require_once 'SOAP/Base.php';
  23. require_once 'SOAP/Fault.php';
  24. require_once 'HTTP/Request.php';
  25. define('WSDL_CACHE_MAX_AGE', 43200);
  26. define('WSDL_CACHE_USE', 0); // set to zero to turn off caching
  27. /**
  28. * SOAP_WSDL
  29. * this class parses wsdl files, and can be used by SOAP::Client to properly register
  30. * soap values for services
  31. *
  32. * originaly based on SOAPx4 by Dietrich Ayala http://dietrich.ganx4.com/soapx4
  33. *
  34. * TODO:
  35. * add wsdl caching
  36. * refactor namespace handling ($namespace/$ns)
  37. * implement IDL type syntax declaration so we can generate WSDL
  38. *
  39. * @access public
  40. * @version $Id: WSDL.php,v 1.51.2.1 2003/12/18 21:15:17 arnaud Exp $
  41. * @package SOAP::Client
  42. * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
  43. * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
  44. */
  45. class SOAP_WSDL extends SOAP_Base
  46. {
  47. var $tns = null;
  48. var $definition = array();
  49. var $namespaces = array();
  50. var $ns = array();
  51. var $xsd = SOAP_XML_SCHEMA_VERSION;
  52. var $complexTypes = array();
  53. var $elements = array();
  54. var $messages = array();
  55. var $portTypes = array();
  56. var $bindings = array();
  57. var $imports = array();
  58. var $services = array();
  59. var $service = '';
  60. var $uri = '';
  61. /**
  62. * Proxy parameters
  63. *
  64. * @var array
  65. */
  66. var $proxy = null;
  67. var $trace = 0;
  68. /**
  69. * Use WSDL cache
  70. *
  71. * @var boolean
  72. */
  73. var $cacheUse = null;
  74. /**
  75. * Cache max lifetime (in seconds)
  76. *
  77. * @var int
  78. */
  79. var $cacheMaxAge = null;
  80. /**
  81. * SOAP_WSDL constructor
  82. *
  83. * @param string endpoint_uri (URL to WSDL file)
  84. * @param array contains options for HTTP_Request class (see HTTP/Request.php)
  85. * @param boolean use WSDL caching
  86. * @param int cache max lifetime (in seconds)
  87. * @access public
  88. */
  89. function SOAP_WSDL($wsdl_uri = false, $proxy = array(),
  90. $cacheUse = WSDL_CACHE_USE,
  91. $cacheMaxAge = WSDL_CACHE_MAX_AGE) {
  92. parent::SOAP_Base('WSDL');
  93. $this->uri = $wsdl_uri;
  94. $this->proxy = $proxy;
  95. $this->cacheUse = $cacheUse;
  96. $this->cacheMaxAge = $cacheMaxAge;
  97. if ($wsdl_uri) {
  98. $this->parseURL($wsdl_uri);
  99. reset($this->services);
  100. $this->service = key($this->services);
  101. }
  102. }
  103. function set_service($service) {
  104. if (array_key_exists($service, $this->services)) {
  105. $this->service = $service;
  106. }
  107. }
  108. /**
  109. * @deprecated use parseURL instead
  110. */
  111. function parse($wsdl_uri, $proxy = array()) {
  112. $this->parseURL($wsdl_uri, $proxy);
  113. }
  114. /**
  115. * Fill the WSDL array tree with data from a WSDL file
  116. *
  117. * @param string
  118. * @param array proxi related parameters
  119. * @return void
  120. */
  121. function parseURL($wsdl_uri, $proxy = array()) {
  122. $parser =& new SOAP_WSDL_Parser($wsdl_uri, $this);
  123. if ($parser->fault) {
  124. $this->_raiseSoapFault($parser->fault);
  125. }
  126. }
  127. /**
  128. * Fill the WSDL array tree with data from one or more PHP class objects
  129. *
  130. * @param mixed $wsdl_obj An object or array of objects to add to the internal WSDL tree
  131. * @param string $service_name Name of the WSDL <service>
  132. * @param string $service_desc Optional description of the WSDL <service>
  133. * @return void
  134. */
  135. function parseObject(&$wsdl_obj, $targetNamespace, $service_name, $service_desc = '')
  136. {
  137. $parser =& new SOAP_WSDL_ObjectParser($wsdl_obj, $this, $targetNamespace, $service_name, $service_desc);
  138. if ($parser->fault) {
  139. $this->_raiseSoapFault($parser->fault);
  140. }
  141. }
  142. function getEndpoint($portName)
  143. {
  144. return (isset($this->services[$this->service]['ports'][$portName]['address']['location']))
  145. ? $this->services[$this->service]['ports'][$portName]['address']['location']
  146. : $this->_raiseSoapFault("no endpoint for port for $portName", $this->uri);
  147. }
  148. function _getPortName($operation,$service) {
  149. if (isset($this->services[$service]['ports'])) {
  150. foreach ($this->services[$service]['ports'] as $port => $portAttrs) {
  151. $type = $this->services[$service]['ports'][$port]['type'];
  152. if ($type == 'soap' &&
  153. isset($this->bindings[$portAttrs['binding']]['operations'][$operation])) {
  154. return $port;
  155. }
  156. }
  157. }
  158. return null;
  159. }
  160. // find the name of the first port that contains an operation of name $operation
  161. // always returns a the soap portName
  162. function getPortName($operation, $service = null)
  163. {
  164. if (!$service) $service = $this->service;
  165. if (isset($this->services[$service]['ports'])) {
  166. $portName = $this->_getPortName($operation,$service);
  167. if ($portName) return $portName;
  168. }
  169. // try any service in the wsdl
  170. foreach ($this->services as $serviceName=>$service) {
  171. if (isset($this->services[$serviceName]['ports'])) {
  172. $portName = $this->_getPortName($operation,$serviceName);
  173. if ($portName) {
  174. $this->service = $serviceName;
  175. return $portName;
  176. }
  177. }
  178. }
  179. return $this->_raiseSoapFault("no operation $operation in wsdl", $this->uri);
  180. }
  181. function getOperationData($portName,$operation)
  182. {
  183. if (isset($this->services[$this->service]['ports'][$portName]['binding'])
  184. && $binding = $this->services[$this->service]['ports'][$portName]['binding']) {
  185. // get operation data from binding
  186. if (is_array($this->bindings[$binding]['operations'][$operation])) {
  187. $opData = $this->bindings[$binding]['operations'][$operation];
  188. }
  189. // get operation data from porttype
  190. $portType = $this->bindings[$binding]['type'];
  191. if (!$portType) {
  192. return $this->_raiseSoapFault("no port type for binding $binding in wsdl " . $this->uri);
  193. }
  194. if (is_array($this->portTypes[$portType][$operation])) {
  195. if (isset($this->portTypes[$portType][$operation]['parameterOrder']))
  196. $opData['parameterOrder'] = $this->portTypes[$portType][$operation]['parameterOrder'];
  197. $opData['input'] = array_merge($opData['input'], $this->portTypes[$portType][$operation]['input']);
  198. $opData['output'] = array_merge($opData['output'], $this->portTypes[$portType][$operation]['output']);
  199. }
  200. if (!$opData)
  201. return $this->_raiseSoapFault("no operation $operation for port $portName, in wsdl", $this->uri);
  202. $opData['parameters'] = false;
  203. if (isset($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace']))
  204. $opData['namespace'] = $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'];
  205. // message data from messages
  206. $inputMsg = $opData['input']['message'];
  207. if (is_array($this->messages[$inputMsg])) {
  208. foreach ($this->messages[$inputMsg] as $pname => $pattrs) {
  209. if ($opData['style'] == 'document' && $opData['input']['use'] == 'literal'
  210. && $pname == 'parameters') {
  211. $opData['parameters'] = true;
  212. $opData['namespace'] = $this->namespaces[$pattrs['namespace']];
  213. $el = $this->elements[$pattrs['namespace']][$pattrs['type']];
  214. if (isset($el['elements'])) {
  215. foreach ($el['elements'] as $elname => $elattrs) {
  216. $opData['input']['parts'][$elname] = $elattrs;
  217. }
  218. }
  219. } else {
  220. $opData['input']['parts'][$pname] = $pattrs;
  221. }
  222. }
  223. }
  224. $outputMsg = $opData['output']['message'];
  225. if (is_array($this->messages[$outputMsg])) {
  226. foreach ($this->messages[$outputMsg] as $pname => $pattrs) {
  227. if ($opData['style'] == 'document' && $opData['output']['use'] == 'literal'
  228. && $pname == 'parameters') {
  229. $el = $this->elements[$pattrs['namespace']][$pattrs['type']];
  230. if (isset($el['elements'])) {
  231. foreach ($el['elements'] as $elname => $elattrs) {
  232. $opData['output']['parts'][$elname] = $elattrs;
  233. }
  234. }
  235. } else {
  236. $opData['output']['parts'][$pname] = $pattrs;
  237. }
  238. }
  239. }
  240. return $opData;
  241. }
  242. return $this->_raiseSoapFault("no binding for port $portName in wsdl", $this->uri);
  243. }
  244. function matchMethod(&$operation) {
  245. // Overloading lowercases function names :(
  246. foreach ($this->services[$this->service]['ports'] as $port => $portAttrs) {
  247. foreach (array_keys($this->bindings[$portAttrs['binding']]['operations']) as $op) {
  248. if (strcasecmp($op, $operation) == 0) {
  249. $operation = $op;
  250. }
  251. }
  252. }
  253. }
  254. /**
  255. * getDataHandler
  256. *
  257. * Given a datatype, what function handles the processing?
  258. * this is used for doc/literal requests where we receive
  259. * a datatype, and we need to pass it to a method in out
  260. * server class
  261. *
  262. * @param string datatype
  263. * @param string namespace
  264. * @returns string methodname
  265. * @access public
  266. */
  267. function getDataHandler($datatype, $namespace) {
  268. // see if we have an element by this name
  269. if (isset($this->namespaces[$namespace]))
  270. $namespace = $this->namespaces[$namespace];
  271. if (isset($this->ns[$namespace])) {
  272. $nsp = $this->ns[$namespace];
  273. #if (!isset($this->elements[$nsp]))
  274. # $nsp = $this->namespaces[$nsp];
  275. if (isset($this->elements[$nsp][$datatype])) {
  276. $checkmessages = array();
  277. // find what messages use this datatype
  278. foreach ($this->messages as $messagename=>$message) {
  279. foreach ($message as $partname=>$part) {
  280. if ($part['type']==$datatype) {
  281. $checkmessages[] = $messagename;
  282. break;
  283. }
  284. }
  285. }
  286. // find the operation that uses this message
  287. $dataHandler = NULL;
  288. foreach($this->portTypes as $portname=>$porttype) {
  289. foreach ($porttype as $opname=>$opinfo) {
  290. foreach ($checkmessages as $messagename) {
  291. if ($opinfo['input']['message'] == $messagename) {
  292. return $opname;
  293. }
  294. }
  295. }
  296. }
  297. }
  298. }
  299. return null;
  300. }
  301. function getSoapAction($portName, $operation)
  302. {
  303. if (isset($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction']) &&
  304. $soapAction = $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction']) {
  305. return $soapAction;
  306. }
  307. return false;
  308. }
  309. function getNamespace($portName, $operation)
  310. {
  311. if (isset($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]) &&
  312. isset($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace']) &&
  313. $namespace = $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace']) {
  314. return $namespace;
  315. }
  316. return false;
  317. }
  318. function getNamespaceAttributeName($namespace) {
  319. /* if it doesn't exist at first, flip the array and check again */
  320. if (!array_key_exists($namespace, $this->ns)) {
  321. $this->ns = array_flip($this->namespaces);
  322. }
  323. /* if it doesn't exist now, add it */
  324. if (!array_key_exists($namespace, $this->ns)) {
  325. return $this->addNamespace($namespace);
  326. }
  327. return $this->ns[$namespace];
  328. }
  329. function addNamespace($namespace) {
  330. if (array_key_exists($namespace, $this->ns)) {
  331. return $this->ns[$namespace];
  332. }
  333. $n = count($this->ns);
  334. $attr = 'ns'.$n;
  335. $this->namespaces['ns'.$n] = $namespace;
  336. $this->ns[$namespace] = $attr;
  337. return $attr;
  338. }
  339. function _validateString($string) {
  340. // XXX this should be done sooner or later
  341. return true;
  342. #if (preg_match("/^[\w_:#\/]+$/",$string)) return true;
  343. #return false;
  344. }
  345. function _addArg(&$args, &$argarray, $argname)
  346. {
  347. if ($args) $args .= ", ";
  348. $args .= "\$".$argname;
  349. if (!$this->_validateString($argname)) return NULL;
  350. if ($argarray) $argarray .= ", ";
  351. $argarray .= "\"$argname\"=>\$".$argname;
  352. }
  353. function _elementArg(&$args, &$argarray, &$_argtype, $_argname)
  354. {
  355. $comments = '';
  356. $el = $this->elements[$_argtype['namespace']][$_argtype['type']];
  357. $tns = isset($this->ns[$el['namespace']])?$this->ns[$el['namespace']]:$_argtype['namespace'];
  358. if (isset($this->complexTypes[$tns][$el['type']])) {
  359. // the element is actually a complex type!
  360. $comments = " // {$el['type']} is a ComplexType, refer to wsdl for more info\n";
  361. $attrname = "{$_argtype['type']}_attr";
  362. if (isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
  363. $comments .= " // {$el['type']} may require attributes, refer to wsdl for more info\n";
  364. }
  365. $comments .= " \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n";
  366. $comments .= " \${$_argtype['type']} =& new SOAP_Value('{$_argtype['type']}',false,\${$_argtype['type']},\$$attrname);\n";
  367. $this->_addArg($args,$argarray,$_argtype['type']);
  368. if (isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
  369. if ($args) $args .= ", ";
  370. $args .= "\$".$attrname;
  371. }
  372. #$comments = $this->_complexTypeArg($args,$argarray,$el,$_argtype['type']);
  373. } else if (isset($el['elements'])) {
  374. foreach ($el['elements'] as $ename => $element) {
  375. $comments .= " \$$ename =& new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename','{$element['type']}',\$$ename);\n";
  376. $this->_addArg($args,$argarray,$ename);
  377. }
  378. } else {
  379. #$echoStringParam =& new SOAP_Value('{http://soapinterop.org/xsd}echoStringParam',false,$echoStringParam);
  380. $comments .= " \$$_argname =& new SOAP_Value('{{$this->namespaces[$tns]}}$_argname','{$el['type']}',\$$_argname);\n";
  381. $this->_addArg($args,$argarray,$_argname);
  382. }
  383. return $comments;
  384. }
  385. function _complexTypeArg(&$args, &$argarray, &$_argtype, $_argname)
  386. {
  387. $comments = '';
  388. if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']])) {
  389. $comments = " // $_argname is a ComplexType {$_argtype['type']},\n";
  390. $comments .= " //refer to wsdl for more info\n";
  391. if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']]['attribute'])) {
  392. $comments .= " // $_argname may require attributes, refer to wsdl for more info\n";
  393. }
  394. $wrapname = '{'.$this->namespaces[$_argtype['namespace']].'}'.$_argtype['type'];
  395. $comments .= " \$$_argname =& new SOAP_Value('$_argname','$wrapname',\$$_argname);\n";
  396. }
  397. $this->_addArg($args,$argarray,$_argname);
  398. return $comments;
  399. }
  400. /**
  401. * generateProxyCode
  402. * generates stub code from the wsdl that can be saved to a file, or eval'd into existence
  403. */
  404. function generateProxyCode($port = '', $classname='')
  405. {
  406. $multiport = count($this->services[$this->service]['ports']) > 1;
  407. if (!$port) {
  408. reset($this->services[$this->service]['ports']);
  409. $port = current($this->services[$this->service]['ports']);
  410. }
  411. // XXX currently do not support HTTP ports
  412. if ($port['type'] != 'soap') return NULL;
  413. // XXX currentPort is BAD
  414. $clienturl = $port['address']['location'];
  415. if (!$classname) {
  416. if ($multiport || $port) {
  417. $classname = 'WebService_'.$this->service.'_'.$port['name'];
  418. } else {
  419. $classname = 'WebService_'.$this->service;
  420. }
  421. $classname = str_replace('.','_',$classname);
  422. }
  423. if (!$this->_validateString($classname)) return NULL;
  424. if (is_array($this->proxy) && count($this->proxy) > 0) {
  425. $class = "class $classname extends SOAP_Client\n{\n".
  426. " function $classname()\n{\n".
  427. " \$this->SOAP_Client(\"$clienturl\", 0, 0,
  428. array(";
  429. foreach($this->proxy as $key => $val) {
  430. if (is_array($val)) {
  431. $class .= "\"$key\" => array(";
  432. foreach ($val as $key2 => $val2) {
  433. $class .= "\"$key2\" => \"$val2\",";
  434. }
  435. $class .= ')';
  436. } else {
  437. $class .= "\"$key\"=>\"$val\",";
  438. }
  439. }
  440. $class .= "));\n }\n";
  441. $class = str_replace(',))', '))', $class);
  442. } else {
  443. $class = "class $classname extends SOAP_Client\n{\n".
  444. " function $classname()\n{\n".
  445. " \$this->SOAP_Client(\"$clienturl\", 0);\n".
  446. " }\n";
  447. }
  448. // get the binding, from that get the port type
  449. $primaryBinding = $port['binding']; //$this->services[$this->service]['ports'][$port['name']]["binding"];
  450. $primaryBinding = preg_replace("/^(.*:)/","",$primaryBinding);
  451. $portType = $this->bindings[$primaryBinding]['type'];
  452. $portType = preg_replace("/^(.*:)/","",$portType);
  453. $style = $this->bindings[$primaryBinding]['style'];
  454. // XXX currentPortType is BAD
  455. foreach ($this->portTypes[$portType] as $opname => $operation) {
  456. $soapaction = $this->bindings[$primaryBinding]['operations'][$opname]['soapAction'];
  457. if (isset($this->bindings[$primaryBinding]['operations'][$opname]['style'])) {
  458. $opstyle = $this->bindings[$primaryBinding]['operations'][$opname]['style'];
  459. } else {
  460. $opstyle = $style;
  461. }
  462. $use = $this->bindings[$primaryBinding]['operations'][$opname]['input']['use'];
  463. if ($use == 'encoded') {
  464. $namespace = $this->bindings[$primaryBinding]['operations'][$opname]['input']['namespace'];
  465. } else {
  466. $bindingType = $this->bindings[$primaryBinding]['type'];
  467. $ns = $this->portTypes[$bindingType][$opname]['input']['namespace'];
  468. $namespace = $this->namespaces[$ns];
  469. }
  470. $args = '';
  471. $argarray = '';
  472. $comments = '';
  473. $wrappers = '';
  474. foreach ($operation['input'] as $argname => $argtype) {
  475. if ($argname == "message") {
  476. foreach ($this->messages[$argtype] as $_argname => $_argtype) {
  477. $comments = '';
  478. if ($opstyle == 'document' && $use == 'literal' &&
  479. $_argtype['name'] == 'parameters') {
  480. // the type or element refered to is used for parameters!
  481. $elattrs = null;
  482. $element = $_argtype['element'];
  483. $el = $this->elements[$_argtype['namespace']][$_argtype['type']];
  484. if($el['complex']) {
  485. $namespace = $this->namespaces[$_argtype['namespace']];
  486. // XXX need to wrap the parameters in a soap_value
  487. }
  488. if (isset($el['elements'])) {
  489. foreach ($el['elements'] as $elname => $elattrs) {
  490. // is the element a complex type?
  491. if (isset($this->complexTypes[$elattrs['namespace']][$elname])) {
  492. $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
  493. } else {
  494. $this->_addArg($args, $argarray, $elname);
  495. }
  496. }
  497. }/* else {
  498. $comments = $this->_complexTypeArg($args, $argarray, $elattrs, $elattrs['name']);
  499. }*/
  500. if($el['complex'] && $argarray) {
  501. $wrapname = '{'.$this->namespaces[$_argtype['namespace']].'}'.$el['name'];
  502. $comments .= " \${$el['name']} =& new SOAP_Value('$wrapname',false,\$v=array($argarray));\n";
  503. $argarray = "'{$el['name']}'=>\${$el['name']}";
  504. }
  505. } else
  506. if (isset($_argtype['element'])) {
  507. // element argument
  508. $comments = $this->_elementArg($args, $argarray, $_argtype, $_argtype['type']);
  509. } else {
  510. // complex type argument
  511. $comments = $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
  512. }
  513. }
  514. }
  515. }
  516. // validate entries
  517. if (!$this->_validateString($opname)) return NULL;
  518. if (!$this->_validateString($namespace)) return NULL;
  519. if (!$this->_validateString($soapaction)) return NULL;
  520. if ($argarray) {
  521. $argarray = "array($argarray)";
  522. } else {
  523. $argarray = 'null';
  524. }
  525. $class .= " function &$opname($args) {\n$comments$wrappers".
  526. " return \$this->call(\"$opname\", \n".
  527. " \$v = $argarray, \n".
  528. " array('namespace'=>'$namespace',\n".
  529. " 'soapaction'=>'$soapaction',\n".
  530. " 'style'=>'$opstyle',\n".
  531. " 'use'=>'$use'".
  532. ($this->trace?",'trace'=>1":"")." ));\n".
  533. " }\n";
  534. }
  535. $class .= "}\n";
  536. return $class;
  537. }
  538. function generateAllProxies()
  539. {
  540. $proxycode = '';
  541. foreach (array_keys($this->services[$this->service]['ports']) as $key) {
  542. $port =& $this->services[$this->service]['ports'][$key];
  543. $proxycode .= $this->generateProxyCode($port);
  544. }
  545. return $proxycode;
  546. }
  547. function &getProxy($port = '', $name = '')
  548. {
  549. $multiport = count($this->services[$this->service]['ports']) > 1;
  550. if (!$port) {
  551. reset($this->services[$this->service]['ports']);
  552. $port = current($this->services[$this->service]['ports']);
  553. }
  554. if ($multiport || $port) {
  555. $classname = 'WebService_' . $this->service . '_' . $port['name'];
  556. } else {
  557. $classname = 'WebService_' . $this->service;
  558. }
  559. if ($name) {
  560. $classname = $name . '_' . $classname;
  561. }
  562. $classname = preg_replace('/[ .\(\)]+/', '_', $classname);
  563. if (!class_exists($classname)) {
  564. $proxy = $this->generateProxyCode($port, $classname);
  565. eval($proxy);
  566. }
  567. return new $classname;
  568. }
  569. function &_getComplexTypeForElement($name, $namespace)
  570. {
  571. $t = NULL;
  572. if (isset($this->ns[$namespace]) &&
  573. isset($this->elements[$this->ns[$namespace]][$name]['type'])) {
  574. $type = $this->elements[$this->ns[$namespace]][$name]['type'];
  575. $ns = $this->elements[$this->ns[$namespace]][$name]['namespace'];
  576. if (isset($this->complexTypes[$ns][$type])) {
  577. $t = $this->complexTypes[$ns][$type];
  578. }
  579. }
  580. return $t;
  581. }
  582. function getComplexTypeNameForElement($name, $namespace)
  583. {
  584. $t = $this->_getComplexTypeForElement($name, $namespace);
  585. if ($t) {
  586. return $t['name'];
  587. }
  588. return NULL;
  589. }
  590. function getComplexTypeChildType($ns, $name, $child_ns, $child_name) {
  591. // is the type an element?
  592. $t = $this->_getComplexTypeForElement($name, $ns);
  593. if ($t) {
  594. // no, get it from complex types directly
  595. if (isset($t['elements'][$child_name]['type']))
  596. return $t['elements'][$child_name]['type'];
  597. }
  598. return NULL;
  599. }
  600. function getSchemaType($type, $name, $type_namespace)
  601. {
  602. # see if it's a complex type so we can deal properly with SOAPENC:arrayType
  603. if ($name && $type) {
  604. # XXX TODO:
  605. # look up the name in the wsdl and validate the type
  606. foreach ($this->complexTypes as $ns=> $types) {
  607. if (array_key_exists($type, $types)) {
  608. if (array_key_exists('type', $types[$type])) {
  609. list($arraytype_ns, $arraytype, $array_depth) = isset($types[$type]['arrayType'])?
  610. $this->_getDeepestArrayType($types[$type]['namespace'], $types[$type]['arrayType'])
  611. : array($this->namespaces[$types[$type]['namespace']], NULL, 0);
  612. return array($types[$type]['type'], $arraytype, $arraytype_ns, $array_depth);
  613. }
  614. if (array_key_exists('arrayType', $types[$type])) {
  615. list($arraytype_ns, $arraytype, $array_depth) =
  616. $this->_getDeepestArrayType($types[$type]['namespace'], $types[$type]['arrayType']);
  617. return array('Array', $arraytype, $arraytype_ns, $array_depth);
  618. }
  619. if (array_key_exists('elements', $types[$type]) &&
  620. array_key_exists($name, $types[$type]['elements'])) {
  621. $type = $types[$type]['elements']['type'];
  622. return array($type, NULL, $this->namespaces[$types[$type]['namespace']], NULL);
  623. }
  624. }
  625. }
  626. }
  627. if ($type && $type_namespace) {
  628. $arrayType = NULL;
  629. # XXX TODO:
  630. # this code currently handles only one way of encoding array types in wsdl
  631. # need to do a generalized function to figure out complex types
  632. $p = $this->ns[$type_namespace];
  633. if ($p &&
  634. array_key_exists($p, $this->complexTypes) &&
  635. array_key_exists($type, $this->complexTypes[$p])) {
  636. if ($arrayType = $this->complexTypes[$p][$type]['arrayType']) {
  637. $type = 'Array';
  638. } else if ($this->complexTypes[$p][$type]['order']=='sequence' &&
  639. array_key_exists('elements', $this->complexTypes[$p][$type])) {
  640. reset($this->complexTypes[$p][$type]['elements']);
  641. # assume an array
  642. if (count($this->complexTypes[$p][$type]['elements']) == 1) {
  643. $arg = current($this->complexTypes[$p][$type]['elements']);
  644. $arrayType = $arg['type'];
  645. $type = 'Array';
  646. } else {
  647. foreach($this->complexTypes[$p][$type]['elements'] as $element) {
  648. if ($element['name'] == $type) {
  649. $arrayType = $element['type'];
  650. $type = $element['type'];
  651. }
  652. }
  653. }
  654. } else {
  655. $type = 'Struct';
  656. }
  657. return array($type, $arrayType, $type_namespace, null);
  658. }
  659. }
  660. return array(null, null, null, null);
  661. }
  662. /** Recurse through the WSDL structure looking for the innermost array type of multi-dimensional arrays.
  663. *
  664. * Takes a namespace prefix and a type, which can be in the form 'type' or 'type[]',
  665. * and returns the full namespace URI, the type of the most deeply nested array type found,
  666. * and the number of levels of nesting.
  667. *
  668. * @access private
  669. * @return mixed array or nothing
  670. */
  671. function _getDeepestArrayType($nsPrefix, $arrayType)
  672. {
  673. static $trail = array();
  674. $arrayType = ereg_replace('\[\]$', '', $arrayType);
  675. // Protect against circular references
  676. // XXX We really need to remove trail from this altogether (it's very inefficient and
  677. // in the wrong place!) and put circular reference checking in when the WSDL info
  678. // is generated in the first place
  679. if (array_search($nsPrefix . ':' . $arrayType, $trail)) {
  680. return array(NULL, NULL, -count($trail));
  681. }
  682. if (array_key_exists($nsPrefix, $this->complexTypes) &&
  683. array_key_exists($arrayType, $this->complexTypes[$nsPrefix]) &&
  684. array_key_exists('arrayType', $this->complexTypes[$nsPrefix][$arrayType])) {
  685. $trail[] = $nsPrefix . ':' . $arrayType;
  686. $result = $this->_getDeepestArrayType( $this->complexTypes[$nsPrefix][$arrayType]['namespace'],
  687. $this->complexTypes[$nsPrefix][$arrayType]['arrayType']);
  688. return array($result[0], $result[1], $result[2] + 1);
  689. }
  690. return array($this->namespaces[$nsPrefix], $arrayType, 0);
  691. }
  692. }
  693. class SOAP_WSDL_Cache extends SOAP_Base
  694. {
  695. // Cache settings
  696. /**
  697. * Use WSDL cache
  698. *
  699. * @var boolean
  700. */
  701. var $_cacheUse = null;
  702. /**
  703. * Cache max lifetime (in seconds)
  704. *
  705. * @var int
  706. */
  707. var $_cacheMaxAge = null;
  708. /**
  709. * SOAP_WSDL_Cache constructor
  710. *
  711. * @param boolean use caching
  712. * @param int cache max lifetime (in seconds)
  713. * @access public
  714. */
  715. function SOAP_WSDL_Cache($cacheUse = WSDL_CACHE_USE,
  716. $cacheMaxAge = WSDL_CACHE_MAX_AGE) {
  717. parent::SOAP_Base('WSDLCACHE');
  718. $this->_cacheUse = $cacheUse;
  719. $this->_cacheMaxAge = $cacheMaxAge;
  720. }
  721. /**
  722. * _cacheDir
  723. * return the path to the cache, if it doesn't exist, make it
  724. */
  725. function _cacheDir() {
  726. $dir = getenv("WSDLCACHE");
  727. if (!$dir) $dir = "./wsdlcache";
  728. @mkdir($dir, 0700);
  729. return $dir;
  730. }
  731. /**
  732. * Retrieves a file from cache if it exists, otherwise retreive from net,
  733. * add to cache, and return from cache.
  734. *
  735. * @param string URL to WSDL
  736. * @param array proxy parameters
  737. * @param int expected MD5 of WSDL URL
  738. * @access public
  739. * @return string data
  740. */
  741. function get($wsdl_fname, $proxy_params = array(), $cache = 0) {
  742. $cachename = $md5_wsdl = $file_data = '';
  743. if ($this->_cacheUse) {
  744. // Try to retrieve WSDL from cache
  745. $cachename = SOAP_WSDL_Cache::_cacheDir() . '/' . md5($wsdl_fname). '.wsdl';
  746. if (file_exists($cachename)) {
  747. $wf = fopen($cachename,'rb');
  748. if ($wf) {
  749. // Reading cached file
  750. $file_data = fread($wf, filesize($cachename));
  751. $md5_wsdl = md5($file_data);
  752. fclose($wf);
  753. }
  754. if ($cache) {
  755. if ($cache != $md5_wsdl) {
  756. return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname);
  757. }
  758. } else {
  759. $fi = stat($cachename);
  760. $cache_mtime = $fi[8];
  761. #print cache_mtime, time()
  762. if ($cache_mtime + $this->_cacheMaxAge < time()) {
  763. # expired
  764. $md5_wsdl = ''; # refetch
  765. }
  766. }
  767. }
  768. }
  769. if (!$md5_wsdl) {
  770. // Not cached or not using cache. Retrieve WSDL from URL
  771. // is it a local file?
  772. // this section should be replace by curl at some point
  773. if (!preg_match('/^(https?|file):\/\//',$wsdl_fname)) {
  774. if (!file_exists($wsdl_fname)) {
  775. return $this->_raiseSoapFault("Unable to read local WSDL $wsdl_fname", $wsdl_fname);
  776. }
  777. if (function_exists('file_get_contents')) {
  778. $file_data = file_get_contents($wsdl_fname);
  779. } else {
  780. $file_data = implode('',file($wsdl_fname));
  781. }
  782. } else {
  783. $uri = explode('?',$wsdl_fname);
  784. $rq =& new HTTP_Request($uri[0], $proxy_params);
  785. // the user agent HTTP_Request uses fouls things up
  786. if (isset($uri[1])) {
  787. $rq->addRawQueryString($uri[1]);
  788. }
  789. if (isset($proxy_params['proxy_user']) && isset($proxy_params['proxy_pass'])) {
  790. $rq->setProxy($proxy_params["proxy_host"],$proxy_params["proxy_port"],
  791. $proxy_params["proxy_user"],$proxy_params["proxy_pass"]);
  792. }
  793. $result = $rq->sendRequest();
  794. if (PEAR::isError($result)) {
  795. return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname," . $rq->getResponseCode(), $wsdl_fname);
  796. }
  797. $file_data = $rq->getResponseBody();
  798. if (!$file_data) {
  799. return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname, no http body", $wsdl_fname);
  800. }
  801. }
  802. $md5_wsdl = md5($file_data);
  803. if ($this->_cacheUse) {
  804. $fp = fopen($cachename, "wb");
  805. fwrite($fp, $file_data);
  806. fclose($fp);
  807. }
  808. }
  809. if ($this->_cacheUse && $cache && $cache != $md5_wsdl) {
  810. return $this->_raiseSoapFault("WSDL Checksum error!", $wsdl_fname);
  811. }
  812. return $file_data;
  813. }
  814. }
  815. class SOAP_WSDL_Parser extends SOAP_Base
  816. {
  817. // define internal arrays of bindings, ports, operations, messages, etc.
  818. var $currentMessage;
  819. var $currentOperation;
  820. var $currentPortType;
  821. var $currentBinding;
  822. var $currentPort;
  823. // parser vars
  824. var $cache;
  825. var $tns = null;
  826. var $soapns = array('soap');
  827. var $uri = '';
  828. var $wsdl = null;
  829. var $status = '';
  830. var $element_stack = array();
  831. var $parentElement = '';
  832. var $schema = '';
  833. var $schemaStatus = '';
  834. var $schema_stack = array();
  835. var $currentComplexType;
  836. var $schema_element_stack = array();
  837. var $currentElement;
  838. // constructor
  839. function SOAP_WSDL_Parser($uri, &$wsdl, $docs = false) {
  840. parent::SOAP_Base('WSDLPARSER');
  841. $this->cache =& new SOAP_WSDL_Cache($wsdl->cacheUse, $wsdl->cacheMaxAge);
  842. $this->uri = $uri;
  843. $this->wsdl = &$wsdl;
  844. $this->docs = $docs;
  845. $this->parse($uri);
  846. }
  847. function parse($uri) {
  848. // Check whether content has been read.
  849. $fd = $this->cache->get($uri, $this->wsdl->proxy);
  850. if (PEAR::isError($fd)) {
  851. return $this->_raiseSoapFault($fd);
  852. }
  853. // Create an XML parser.
  854. $parser = xml_parser_create();
  855. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
  856. xml_set_object($parser, $this);
  857. xml_set_element_handler($parser, 'startElement', 'endElement');
  858. if ($this->docs) {
  859. xml_set_character_data_handler($parser, 'characterData');
  860. }
  861. if (!xml_parse($parser,$fd, true)) {
  862. $detail = sprintf('XML error on line %d: %s',
  863. xml_get_current_line_number($parser),
  864. xml_error_string(xml_get_error_code($parser)));
  865. //print $fd;
  866. return $this->_raiseSoapFault("Unable to parse WSDL file $uri\n$detail");
  867. }
  868. xml_parser_free($parser);
  869. return true;
  870. }
  871. // start-element handler
  872. function startElement($parser, $name, $attrs) {
  873. // get element prefix
  874. $qname =& new QName($name);
  875. if ($qname->ns) {
  876. $ns = $qname->ns;
  877. if ($ns && ((!$this->tns && strcasecmp($qname->name,'definitions') == 0) || $ns == $this->tns)) {
  878. $name = $qname->name;
  879. }
  880. }
  881. $this->currentTag = $qname->name;
  882. $this->parentElement = '';
  883. $stack_size = count($this->element_stack);
  884. if ($stack_size > 0) {
  885. $this->parentElement = $this->element_stack[$stack_size-1];
  886. }
  887. $this->element_stack[] = $this->currentTag;
  888. // find status, register data
  889. switch($this->status) {
  890. case 'types':
  891. // sect 2.2 wsdl:types
  892. // children: xsd:schema
  893. $parent_tag = '';
  894. $stack_size = count($this->schema_stack);
  895. if ($stack_size > 0) {
  896. $parent_tag = $this->schema_stack[$stack_size-1];
  897. }
  898. switch($qname->name) {
  899. case 'schema':
  900. // no parent should be in the stack
  901. if (!$parent_tag || $parent_tag == 'types') {
  902. if (array_key_exists('targetNamespace', $attrs)) {
  903. $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
  904. } else {
  905. $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
  906. }
  907. $this->wsdl->complexTypes[$this->schema] = array();
  908. $this->wsdl->elements[$this->schema] = array();
  909. }
  910. break;
  911. case 'complexType':
  912. if ($parent_tag == 'schema') {
  913. $this->currentComplexType = $attrs['name'];
  914. if (!isset($attrs['namespace'])) $attrs['namespace'] = $this->schema;
  915. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType] = $attrs;
  916. if (array_key_exists('base',$attrs)) {
  917. $qn =& new QName($attrs['base']);
  918. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
  919. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $qn->ns;
  920. } else {
  921. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
  922. }
  923. $this->schemaStatus = 'complexType';
  924. } else {
  925. $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = TRUE;
  926. }
  927. break;
  928. case 'element':
  929. if (isset($attrs['type'])) {
  930. $qn =& new QName($attrs['type']);
  931. $attrs['type'] = $qn->name;
  932. #$this->wsdl->getNamespaceAttributeName
  933. if ($qn->ns && array_key_exists($qn->ns, $this->wsdl->namespaces)) {
  934. $attrs['namespace'] = $qn->ns;
  935. }
  936. }
  937. $parentElement = '';
  938. $stack_size = count($this->schema_element_stack);
  939. if ($stack_size > 0) {
  940. $parentElement = $this->schema_element_stack[$stack_size-1];
  941. }
  942. if (isset($attrs['ref'])) {
  943. $this->currentElement = $attrs['ref'];
  944. } else {
  945. $this->currentElement = $attrs['name'];
  946. }
  947. $this->schema_element_stack[] = $this->currentElement;
  948. if (!isset($attrs['namespace'])) $attrs['namespace'] = $this->schema;
  949. if ($parent_tag == 'schema') {
  950. $this->wsdl->elements[$this->schema][$this->currentElement] = $attrs;
  951. $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = FALSE;
  952. $this->schemaStatus = 'element';
  953. } else if ($this->currentComplexType) {
  954. // we're inside a complexType
  955. if ((isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order']) &&
  956. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] == 'sequence')
  957. && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array') {
  958. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['arrayType'] = $attrs['type'];
  959. }
  960. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement] = $attrs;
  961. } else {
  962. $this->wsdl->elements[$this->schema][$parentElement]['elements'][$this->currentElement] = $attrs;
  963. }
  964. break;
  965. case 'complexContent':
  966. case 'simpleContent':
  967. break;
  968. case 'extension':
  969. case 'restriction':
  970. if ($this->schemaStatus == 'complexType') {
  971. if ($attrs['base']) {
  972. $qn =& new QName($attrs['base']);
  973. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
  974. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $qn->ns;
  975. } else {
  976. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
  977. }
  978. }
  979. break;
  980. case 'sequence':
  981. if ($this->schemaStatus == 'complexType') {
  982. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
  983. #if (!array_key_exists('type',$this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) {
  984. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
  985. #}
  986. }
  987. break;
  988. case 'all':
  989. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
  990. if (!array_key_exists('type',$this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) {
  991. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
  992. }
  993. break;
  994. case 'choice':
  995. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
  996. if (!array_key_exists('type',$this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) {
  997. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
  998. }
  999. case 'attribute':
  1000. if ($this->schemaStatus == 'complexType') {
  1001. if (isset($attrs['name'])) {
  1002. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['attribute'][$attrs['name']] = $attrs;
  1003. } else
  1004. if (isset($attrs['ref'])) {
  1005. $q =& new QName($attrs['ref']);
  1006. foreach ($attrs as $k => $v) {
  1007. if ($k != 'ref' && strstr($k, $q->name)) {
  1008. $vq =& new QName($v);
  1009. if ($q->name == 'arrayType') {
  1010. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name.$vq->arrayInfo;
  1011. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
  1012. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $vq->ns;
  1013. } else {
  1014. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name;
  1015. }
  1016. }
  1017. }
  1018. }
  1019. }
  1020. break;
  1021. }
  1022. $this->schema_stack[] = $qname->name;
  1023. break;
  1024. case 'message':
  1025. // sect 2.3 wsdl:message child wsdl:part
  1026. switch($qname->name) {
  1027. case 'part':
  1028. $qn = NULL;
  1029. if (isset($attrs['type'])) {
  1030. $qn =& new QName($attrs['type']);
  1031. } else if (isset($attrs['element'])) {
  1032. $qn =& new QName($attrs['element']);
  1033. }
  1034. if ($qn) {
  1035. $attrs['type'] = $qn->name;
  1036. $attrs['namespace'] = $qn->ns;
  1037. }
  1038. $this->wsdl->messages[$this->currentMessage][$attrs['name']] = $attrs;
  1039. // error in wsdl
  1040. case 'documentation':
  1041. break;
  1042. default:
  1043. break;
  1044. }
  1045. break;
  1046. case 'portType':
  1047. // sect 2.4
  1048. switch($qname->name) {
  1049. case 'operation':
  1050. // attributes: name
  1051. // children: wsdl:input wsdl:output wsdl:fault
  1052. $this->currentOperation = $attrs['name'];
  1053. #$this->wsdl->portTypes[$this->currentPortType][$this->currentOperation]['parameterOrder'] = $attrs['parameterOrder'];
  1054. $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation] = $attrs;
  1055. break;
  1056. case 'input':
  1057. case 'output':
  1058. case 'fault':
  1059. // wsdl:input wsdl:output wsdl:fault
  1060. // attributes: name message parameterOrder(optional)
  1061. if ($this->currentOperation) {
  1062. if (isset($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name])) {
  1063. $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = array_merge($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name],$attrs);
  1064. } else {
  1065. $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = $attrs;
  1066. }
  1067. if (array_key_exists('message',$attrs)) {
  1068. $qn =& new QName($attrs['message']);
  1069. $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['message'] = $qn->name;
  1070. $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['namespace'] = $qn->ns;
  1071. }
  1072. }
  1073. break;
  1074. case 'documentation':
  1075. break;
  1076. default:
  1077. break;
  1078. }
  1079. break;
  1080. case 'binding':
  1081. $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
  1082. switch($ns) {
  1083. case SCHEMA_SOAP:
  1084. // this deals with wsdl section 3 soap binding
  1085. switch($qname->name) {
  1086. case 'binding':
  1087. // sect 3.3
  1088. // soap:binding, attributes: transport(required), style(optional, default = document)
  1089. // if style is missing, it is assumed to be 'document'
  1090. if (!isset($attrs['style'])) $attrs['style'] = 'document';
  1091. $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding],$attrs);
  1092. break;
  1093. case 'operation':
  1094. // sect 3.4
  1095. // soap:operation, attributes: soapAction(required), style(optional, default = soap:binding:style)
  1096. if (!isset($attrs['style'])) $attrs['style'] = $this->wsdl->bindings[$this->currentBinding]['style'];
  1097. $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = $attrs;
  1098. break;
  1099. case 'body':
  1100. // sect 3.5
  1101. // soap:body attributes:
  1102. // part - optional. listed parts must appear in body, missing means all parts appear in body
  1103. // use - required. encoded|literal
  1104. // encodingStyle - optional. space seperated list of encodings (uri's)
  1105. $this->wsdl->bindings[$this->currentBinding]
  1106. ['operations'][$this->currentOperation][$this->opStatus] = $attrs;
  1107. break;
  1108. case 'fault':
  1109. // sect 3.6
  1110. // soap:fault attributes: name use encodingStyle namespace
  1111. $this->wsdl->bindings[$this->currentBinding]
  1112. ['operations'][$this->currentOperation][$this->opStatus] = $attrs;
  1113. break;
  1114. case 'header':
  1115. // sect 3.7
  1116. // soap:header attributes: message part use encodingStyle namespace
  1117. $this->wsdl->bindings[$this->currentBinding]
  1118. ['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
  1119. break;
  1120. case 'headerfault':
  1121. // sect 3.7
  1122. // soap:header attributes: message part use encodingStyle namespace
  1123. $header = count($this->wsdl->bindings[$this->currentBinding]
  1124. ['operations'][$this->currentOperation][$this->opStatus]['headers'])-1;
  1125. $this->wsdl->bindings[$this->currentBinding]
  1126. ['operations'][$this->currentOperation][$this->opStatus]['headers'][$header]['fault'] = $attrs;
  1127. break;
  1128. case 'documentation':
  1129. break;
  1130. default:
  1131. // error! not a valid element inside binding
  1132. break;
  1133. }
  1134. break;
  1135. case SCHEMA_WSDL:
  1136. // XXX verify correct namespace
  1137. // for now, default is the 'wsdl' namespace
  1138. // other possible namespaces include smtp, http, etc. for alternate bindings
  1139. switch($qname->name) {
  1140. case 'operation':
  1141. // sect 2.5
  1142. // wsdl:operation attributes: name
  1143. $this->currentOperation = $attrs['name'];
  1144. break;
  1145. case 'output':
  1146. case 'input':
  1147. case 'fault':
  1148. // sect 2.5
  1149. // wsdl:input attributes: name
  1150. $this->opStatus = $qname->name;
  1151. break;
  1152. case 'documentation':
  1153. break;
  1154. default:
  1155. break;
  1156. }
  1157. break;
  1158. case SCHEMA_WSDL_HTTP:
  1159. switch($qname->name) {
  1160. case 'binding':
  1161. // sect 4.4
  1162. // http:binding attributes: verb
  1163. // parent: wsdl:binding
  1164. $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding],$attrs);
  1165. break;
  1166. case 'operation':
  1167. // sect 4.5
  1168. // http:operation attributes: location
  1169. // parent: wsdl:operation
  1170. $this->wsdl->bindings[$this->currentBinding]['operations']
  1171. [$this->currentOperation] = $attrs;
  1172. break;
  1173. case 'urlEncoded':
  1174. // sect 4.6
  1175. // http:urlEncoded attributes: location
  1176. // parent: wsdl:input wsdl:output etc.
  1177. $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
  1178. [$this->currentOperation]['uri'] = 'urlEncoded';
  1179. break;
  1180. case 'urlReplacement':
  1181. // sect 4.7
  1182. // http:urlReplacement attributes: location
  1183. // parent: wsdl:input wsdl:output etc.
  1184. $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
  1185. [$this->currentOperation]['uri'] = 'urlReplacement';
  1186. break;
  1187. case 'documentation':
  1188. break;
  1189. default:
  1190. // error
  1191. break;
  1192. }
  1193. case SCHEMA_MIME:
  1194. // sect 5
  1195. // all mime parts are children of wsdl:input, wsdl:output, etc.
  1196. // unsuported as of yet
  1197. switch($qname->name) {
  1198. case 'content':
  1199. // sect 5.3 mime:content
  1200. // <mime:content part="nmtoken"? type="string"?/>
  1201. // part attribute only required if content is child of multipart related,
  1202. // it contains the name of the part
  1203. // type attribute contains the mime type
  1204. case 'multipartRelated':
  1205. // sect 5.4 mime:multipartRelated
  1206. case 'part':
  1207. case 'mimeXml':
  1208. // sect 5.6 mime:mimeXml
  1209. // <mime:mimeXml part="nmtoken"?/>
  1210. //
  1211. case 'documentation':
  1212. break;
  1213. default:
  1214. // error
  1215. break;
  1216. }
  1217. case SCHEMA_DIME:
  1218. // DIME is defined in:
  1219. // http://gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm
  1220. // all DIME parts are children of wsdl:input, wsdl:output, etc.
  1221. // unsuported as of yet
  1222. switch($qname->name) {
  1223. case 'message':
  1224. // sect 4.1 dime:message
  1225. // appears in binding section
  1226. $this->wsdl->bindings[$this->currentBinding]['dime'] = $attrs;
  1227. break;
  1228. default:
  1229. break;
  1230. }
  1231. default:
  1232. break;
  1233. }
  1234. break;
  1235. case 'service':
  1236. $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
  1237. switch($qname->name) {
  1238. case 'port':
  1239. // sect 2.6 wsdl:port attributes: name binding
  1240. $this->currentPort = $attrs['name'];
  1241. $this->wsdl->services[$this->currentService]['ports'][$this->currentPort] = $attrs;
  1242. // XXX hack to deal with binding namespaces
  1243. $qn =& new QName($attrs['binding']);
  1244. $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['binding'] = $qn->name;
  1245. $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['namespace'] = $qn->ns;
  1246. break;
  1247. case 'address':
  1248. $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['address'] = $attrs;
  1249. // what TYPE of port is it? SOAP or HTTP?
  1250. $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
  1251. switch($ns) {
  1252. case SCHEMA_WSDL_HTTP:
  1253. $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='http';
  1254. break;
  1255. case SCHEMA_SOAP:
  1256. $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
  1257. break;
  1258. default:
  1259. // shouldn't happen, we'll assume soap
  1260. $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
  1261. }
  1262. break;
  1263. case 'documentation':
  1264. break;
  1265. default:
  1266. break;
  1267. }
  1268. }
  1269. // top level elements found under wsdl:definitions
  1270. // set status
  1271. switch($qname->name) {
  1272. case 'import':
  1273. // sect 2.1.1 wsdl:import attributes: namespace location
  1274. if (array_key_exists('location',$attrs) &&
  1275. !isset($this->wsdl->imports[$attrs['namespace']])) {
  1276. $uri = $attrs['location'];
  1277. $location = parse_url($uri);
  1278. if (!isset($location['scheme'])) {
  1279. $base = parse_url($this->uri);
  1280. $uri = $this->merge_url($base,$uri);
  1281. }
  1282. $import_parser =& new SOAP_WSDL_Parser($uri, $this->wsdl);
  1283. if ($import_parser->fault) {
  1284. return FALSE;
  1285. }
  1286. $this->currentImport = $attrs['namespace'];
  1287. $this->wsdl->imports[$this->currentImport] = $attrs;
  1288. }
  1289. $this->status = '';
  1290. case 'types':
  1291. // sect 2.2 wsdl:types
  1292. $this->status = 'types';
  1293. break;
  1294. case 'message':
  1295. // sect 2.3 wsdl:message attributes: name children:wsdl:part
  1296. $this->status = 'message';
  1297. if (isset($attrs['name'])) {
  1298. $this->currentMessage = $attrs['name'];
  1299. $this->wsdl->messages[$this->currentMessage] = array();
  1300. }
  1301. break;
  1302. case 'portType':
  1303. // sect 2.4 wsdl:portType
  1304. // attributes: name
  1305. // children: wsdl:operation
  1306. $this->status = 'portType';
  1307. $this->currentPortType = $attrs['name'];
  1308. $this->wsdl->portTypes[$this->currentPortType] = array();
  1309. break;
  1310. case 'binding':
  1311. // sect 2.5 wsdl:binding attributes: name type
  1312. // children: wsdl:operation soap:binding http:binding
  1313. if ($qname->ns && $qname->ns != $this->tns) break;
  1314. $this->status = 'binding';
  1315. $this->currentBinding = $attrs['name'];
  1316. $qn =& new QName($attrs['type']);
  1317. $this->wsdl->bindings[$this->currentBinding]['type'] = $qn->name;
  1318. $this->wsdl->bindings[$this->currentBinding]['namespace'] = $qn->ns;
  1319. break;
  1320. case 'service':
  1321. // sect 2.7 wsdl:service attributes: name children: ports
  1322. $this->currentService = $attrs['name'];
  1323. $this->wsdl->services[$this->currentService]['ports'] = array();
  1324. $this->status = 'service';
  1325. break;
  1326. case 'definitions':
  1327. // sec 2.1 wsdl:definitions
  1328. // attributes: name targetNamespace xmlns:*
  1329. // children: wsdl:import wsdl:types wsdl:message wsdl:portType wsdl:binding wsdl:service
  1330. #$this->status = 'definitions';
  1331. $this->wsdl->definition = $attrs;
  1332. foreach ($attrs as $key => $value) {
  1333. if (strstr($key,'xmlns:') !== FALSE) {
  1334. $qn =& new QName($key);
  1335. // XXX need to refactor ns handling
  1336. $this->wsdl->namespaces[$qn->name] = $value;
  1337. $this->wsdl->ns[$value] = $qn->name;
  1338. if ($key == 'targetNamespace' ||
  1339. strcasecmp($value,SOAP_SCHEMA) == 0) {
  1340. $this->soapns[] = $qn->name;
  1341. } else {
  1342. if (in_array($value, $this->_XMLSchema)) {
  1343. $this->wsdl->xsd = $value;
  1344. }
  1345. }
  1346. }
  1347. }
  1348. if (isset($ns) && $ns) {
  1349. $namespace = 'xmlns:'.$ns;
  1350. if (!$this->wsdl->definition[$namespace]) {
  1351. return $this->_raiseSoapFault("parse error, no namespace for $namespace",$this->uri);
  1352. }
  1353. $this->tns = $ns;
  1354. }
  1355. break;
  1356. }
  1357. }
  1358. // end-element handler
  1359. function endElement($parser, $name)
  1360. {
  1361. $stacksize = count($this->element_stack);
  1362. if ($stacksize > 0) {
  1363. if ($this->element_stack[count($this->element_stack)-1] == 'definitions') {
  1364. $this->status = '';
  1365. }
  1366. array_pop($this->element_stack);
  1367. }
  1368. if (stristr($name,'schema')) {
  1369. array_pop($this->schema_stack);
  1370. $this->schema = '';
  1371. }
  1372. if ($this->schema) {
  1373. array_pop($this->schema_stack);
  1374. if (count($this->schema_stack) <= 1) {
  1375. /* correct the type for sequences with multiple elements */
  1376. if (isset($this->currentComplexType) && isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])
  1377. && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array'
  1378. && array_key_exists('elements',$this->wsdl->complexTypes[$this->schema][$this->currentComplexType])
  1379. && count($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements']) > 1) {
  1380. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
  1381. }
  1382. }
  1383. if (stristr($name,'complexType')) {
  1384. $this->currentComplexType = '';
  1385. if (count($this->schema_element_stack) > 0)
  1386. $this->currentElement = array_pop($this->schema_element_stack);
  1387. else
  1388. $this->currentElement = '';
  1389. } else if (stristr($name,'element')) {
  1390. if (count($this->schema_element_stack) > 0)
  1391. $this->currentElement = array_pop($this->schema_element_stack);
  1392. else
  1393. $this->currentElement = '';
  1394. }
  1395. }
  1396. // position of current element is equal to the last value left in depth_array for my depth
  1397. //$pos = $this->depth_array[$this->depth];
  1398. // bring depth down a notch
  1399. //$this->depth--;
  1400. }
  1401. // element content handler
  1402. function characterData($parser, $data)
  1403. {
  1404. # store the documentation in the WSDL file
  1405. if ($this->currentTag == 'documentation') {
  1406. if ($this->status == 'service') {
  1407. $this->wsdl->services[$this->currentService][$this->currentTag] .= $data;
  1408. } else if ($this->status == 'portType') {
  1409. if ($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$this->currentTag])
  1410. $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$this->currentTag] .= data;
  1411. else
  1412. $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$this->currentTag] = data;
  1413. } else if ($this->status == 'binding') {
  1414. if ($this->wsdl->bindings[$this->currentBinding][$this->currentTag])
  1415. $this->wsdl->bindings[$this->currentBinding][$this->currentTag] .= data;
  1416. else
  1417. $this->wsdl->bindings[$this->currentBinding][$this->currentTag] = data;
  1418. } else if ($this->status == 'message') {
  1419. if ($this->wsdl->messages[$this->currentMessage][$this->currentTag])
  1420. $this->wsdl->messages[$this->currentMessage][$this->currentTag] .= data;
  1421. else
  1422. $this->wsdl->messages[$this->currentMessage][$this->currentTag] = data;
  1423. } else if ($this->status == 'operation') {
  1424. if ($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$this->currentTag])
  1425. $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$this->currentTag] .= data;
  1426. else
  1427. $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$this->currentTag] = data;
  1428. }
  1429. }
  1430. }
  1431. // $parsed is a parse_url() resulting array
  1432. function merge_url($parsed,$path) {
  1433. if (! is_array($parsed)) return false;
  1434. if (isset($parsed['scheme'])) {
  1435. $sep = (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://');
  1436. $uri = $parsed['scheme'] . $sep;
  1437. } else {
  1438. $uri = '';
  1439. }
  1440. if (isset($parsed['pass'])) {
  1441. $uri .= "$parsed[user]:$parsed[pass]@";
  1442. } elseif (isset($parsed['user'])) {
  1443. $uri .= "$parsed[user]@";
  1444. }
  1445. if (isset($parsed['host'])) $uri .= $parsed['host'];
  1446. if (isset($parsed['port'])) $uri .= ":$parsed[port]";
  1447. if ($path[0]!='/' && isset($parsed['path'])) {
  1448. if ($parsed['path'][strlen($parsed['path'])-1] != '/') {
  1449. $path = dirname($parsed['path']).'/'.$path;
  1450. } else {
  1451. $path = $parsed['path'].$path;
  1452. }
  1453. $path = $this->_normalize($path);
  1454. }
  1455. $sep = $path[0]=='/'?'':'/';
  1456. $uri .= $sep.$path;
  1457. return $uri;
  1458. }
  1459. function _normalize($path_str){
  1460. $pwd='';
  1461. $strArr=preg_split("/(\/)/",$path_str,-1,PREG_SPLIT_NO_EMPTY);
  1462. $pwdArr="";
  1463. $j=0;
  1464. for($i=0;$i<count($strArr);$i++){
  1465. if($strArr[$i]!=".."){
  1466. if($strArr[$i]!="."){
  1467. $pwdArr[$j]=$strArr[$i];
  1468. $j++;
  1469. }
  1470. }else{
  1471. array_pop($pwdArr);
  1472. $j--;
  1473. }
  1474. }
  1475. $pStr=implode("/",$pwdArr);
  1476. $pwd=(strlen($pStr)>0) ? ("/".$pStr) : "/";
  1477. return $pwd;
  1478. }
  1479. }
  1480. /**
  1481. * Parses the types and methods used in web service objects into the internal
  1482. * data structures used by SOAP_WSDL.
  1483. *
  1484. * Assumes the SOAP_WSDL class is unpopulated to start with.
  1485. *
  1486. * @author Chris Coe <info@intelligentstreaming.com>
  1487. */
  1488. class SOAP_WSDL_ObjectParser extends SOAP_Base
  1489. {
  1490. // Target namespace for the WSDL document will have the following prefix
  1491. var $tnsPrefix = 'tns';
  1492. // Reference to the SOAP_WSDL object to populate
  1493. var $wsdl = null;
  1494. /** Constructor
  1495. *
  1496. * @param $objects Reference to the object or array of objects to parse
  1497. * @param $wsdl Reference to the SOAP_WSDL object to populate
  1498. * @param $targetNamespace The target namespace of schema types etc.
  1499. * @param $service_name Name of the WSDL <service>
  1500. * @param $service_desc Optional description of the WSDL <service>
  1501. */
  1502. function SOAP_WSDL_ObjectParser(&$objects, &$wsdl, $targetNamespace, $service_name, $service_desc = '') {
  1503. parent::SOAP_Base('WSDLOBJECTPARSER');
  1504. $this->wsdl = &$wsdl;
  1505. // Set up the SOAP_WSDL object
  1506. $this->_initialise($service_name);
  1507. // Parse each web service object
  1508. $wsdl_ref = (is_array($objects)? $objects : array(&$objects));
  1509. foreach ($wsdl_ref as $ref_item) {
  1510. if (!is_object($ref_item))
  1511. return $this->_raiseSoapFault('Invalid web service object passed to object parser', 'urn:' . get_class($object));
  1512. if ($this->_parse($ref_item, $targetNamespace, $service_name) != true)
  1513. break;
  1514. }
  1515. // Build bindings from abstract data
  1516. if ($this->fault == NULL)
  1517. $this->_generateBindingsAndServices($targetNamespace, $service_name, $service_desc);
  1518. }
  1519. /** Initialise the SOAP_WSDL tree (destructive)
  1520. *
  1521. * If the object has already been initialised, the only effect will be to
  1522. * change the tns namespace to the new service name
  1523. *
  1524. * @param $service_name Name of the WSDL <service>
  1525. * @access private
  1526. */
  1527. function _initialise($service_name) {
  1528. // Set up the basic namespaces that all WSDL definitions use
  1529. $this->wsdl->namespaces['wsdl'] = SCHEMA_WSDL; // WSDL language
  1530. $this->wsdl->namespaces['soap'] = SCHEMA_SOAP; // WSDL SOAP bindings
  1531. $this->wsdl->namespaces[$this->tnsPrefix] = 'urn:' . $service_name; // Target namespace
  1532. $this->wsdl->namespaces['xsd'] = array_search('xsd', $this->_namespaces); // XML Schema
  1533. $this->wsdl->namespaces['SOAP-ENC'] = array_search('SOAP-ENC', $this->_namespaces); // SOAP types
  1534. // XXX Refactor $namespace/$ns for Shane :-)
  1535. unset($this->wsdl->ns['urn:' . $service_name]);
  1536. $this->wsdl->ns += array_flip($this->wsdl->namespaces);
  1537. // Imports are not implemented in WSDL generation from classes
  1538. // *** <wsdl:import> ***
  1539. }
  1540. /** Parser - takes a single object to add to tree (non-destructive)
  1541. *
  1542. * @param $object Reference to the object to parse
  1543. * @param $service_name Name of the WSDL <service>
  1544. * @access private
  1545. */
  1546. function _parse(&$object, $schemaNamespace, $service_name) {
  1547. // Create namespace prefix for the schema
  1548. // XXX not very elegant :-(
  1549. list ($schPrefix, $foo) = $this->_getTypeNs('{'.$schemaNamespace.'}');
  1550. unset($foo);
  1551. // Parse all the types defined by the object in whatever
  1552. // schema language we are using (currently __typedef arrays)
  1553. // *** <wsdl:types> ***
  1554. foreach ($object->__typedef as $typeName => $typeValue)
  1555. {
  1556. // Get/create namespace definition
  1557. list($nsPrefix, $typeName) = $this->_getTypeNs($typeName);
  1558. // Create type definition
  1559. $this->wsdl->complexTypes[$schPrefix][$typeName] = array("name" => $typeName);
  1560. $thisType =& $this->wsdl->complexTypes[$schPrefix][$typeName];
  1561. // According to Dmitri's documentation, __typedef comes in two
  1562. // flavors:
  1563. // Array = array(array("item" => "value"))
  1564. // Struct = array("item1" => "value1", "item2" => "value2", ...)
  1565. if (is_array($typeValue))
  1566. {
  1567. reset($typeValue);
  1568. if (is_array(current($typeValue)) && count($typeValue) == 1
  1569. && count(current($typeValue)) == 1)
  1570. {
  1571. // It's an array
  1572. $thisType['type'] = 'Array';
  1573. reset(current($typeValue));
  1574. list($nsPrefix, $typeName) = $this->_getTypeNs(current(current($typeValue)));
  1575. $thisType['namespace'] = $nsPrefix;
  1576. $thisType['arrayType'] = $typeName . '[]';
  1577. }
  1578. else if (!is_array(current($typeValue)))
  1579. {
  1580. // It's a struct
  1581. $thisType['type'] = 'Struct';
  1582. $thisType['order'] = 'all';
  1583. $thisType['namespace'] = $nsPrefix;
  1584. $thisType['elements'] = array();
  1585. foreach ($typeValue as $elementName => $elementType)
  1586. {
  1587. list($nsPrefix, $typeName) = $this->_getTypeNs($elementType);
  1588. $thisType['elements'][$elementName]['name'] = $elementName;
  1589. $thisType['elements'][$elementName]['type'] = $typeName;
  1590. $thisType['elements'][$elementName]['namespace'] = $nsPrefix;
  1591. }
  1592. }
  1593. else
  1594. {
  1595. // It's erroneous
  1596. return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object));
  1597. }
  1598. } else {
  1599. // It's erroneous
  1600. return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object));
  1601. }
  1602. }
  1603. // Create an empty element array with the target namespace prefix,
  1604. // to match the results of WSDL parsing
  1605. $this->wsdl->elements[$schPrefix] = array();
  1606. // Populate tree with message information
  1607. // *** <wsdl:message> ***
  1608. foreach ($object->__dispatch_map as $operationName => $messages)
  1609. {
  1610. foreach ($messages as $messageType => $messageParts)
  1611. {
  1612. unset($thisMessage);
  1613. switch ($messageType) {
  1614. case 'in':
  1615. $this->wsdl->messages[$operationName . 'Request'] = array();
  1616. $thisMessage =& $this->wsdl->messages[$operationName . 'Request'];
  1617. break;
  1618. case 'out':
  1619. $this->wsdl->messages[$operationName . 'Response'] = array();
  1620. $thisMessage =& $this->wsdl->messages[$operationName . 'Response'];
  1621. break;
  1622. case 'alias':
  1623. // Do nothing
  1624. break;
  1625. default:
  1626. // Error condition
  1627. break;
  1628. }
  1629. if (isset($thisMessage))
  1630. {
  1631. foreach ($messageParts as $partName => $partType)
  1632. {
  1633. list ($nsPrefix, $typeName) = $this->_getTypeNs($partType);
  1634. $thisMessage[$partName] = array(
  1635. 'name' => $partName,
  1636. 'type' => $typeName,
  1637. 'namespace' => $nsPrefix
  1638. );
  1639. }
  1640. }
  1641. }
  1642. }
  1643. // Populate tree with portType information
  1644. // XXX Current implementation only supports one portType that
  1645. // encompasses all of the operations available.
  1646. // *** <wsdl:portType> ***
  1647. if (!isset($this->wsdl->portTypes[$service_name . 'Port']))
  1648. $this->wsdl->portTypes[$service_name . 'Port'] = array();
  1649. $thisPortType =& $this->wsdl->portTypes[$service_name . 'Port'];
  1650. foreach ($object->__dispatch_map as $operationName => $messages)
  1651. {
  1652. $thisPortType[$operationName] = array('name' => $operationName);
  1653. foreach ($messages as $messageType => $messageParts)
  1654. {
  1655. switch ($messageType) {
  1656. case 'in':
  1657. $thisPortType[$operationName]['input'] = array(
  1658. 'message' => $operationName . 'Request',
  1659. 'namespace' => $this->tnsPrefix);
  1660. break;
  1661. case 'out':
  1662. $thisPortType[$operationName]['output'] = array(
  1663. 'message' => $operationName . 'Response',
  1664. 'namespace' => $this->tnsPrefix);
  1665. break;
  1666. default:
  1667. break;
  1668. }
  1669. }
  1670. }
  1671. return true;
  1672. }
  1673. /** Take all the abstract WSDL data and build concrete bindings and services (destructive)
  1674. *
  1675. * XXX Current implementation discards $service_desc.
  1676. *
  1677. * @param $schemaNamespace Namespace for types etc.
  1678. * @param $service_name Name of the WSDL <service>
  1679. * @param $service_desc Optional description of the WSDL <service>
  1680. * @access private
  1681. */
  1682. function _generateBindingsAndServices($schemaNamespace, $service_name, $service_desc = '')
  1683. {
  1684. // Populate tree with bindings information
  1685. // XXX Current implementation only supports one binding that
  1686. // matches the single portType and all of its operations.
  1687. // XXX Is this the correct use of $schemaNamespace here?
  1688. // *** <wsdl:binding> ***
  1689. $this->wsdl->bindings[$service_name . 'Binding'] = array(
  1690. 'type' => $service_name . 'Port',
  1691. 'namespace' => $this->tnsPrefix,
  1692. 'style' => 'rpc',
  1693. 'transport' => SCHEMA_SOAP_HTTP,
  1694. 'operations' => array());
  1695. $thisBinding =& $this->wsdl->bindings[$service_name . 'Binding'];
  1696. foreach ($this->wsdl->portTypes[$service_name . 'Port'] as $operationName => $operationData)
  1697. {
  1698. $thisBinding['operations'][$operationName] = array(
  1699. 'soapAction' => $schemaNamespace . '#' . $operationName,
  1700. 'style' => $thisBinding['style']);
  1701. foreach (array('input', 'output') as $messageType)
  1702. if (isset($operationData[$messageType]))
  1703. $thisBinding['operations'][$operationName][$messageType] = array(
  1704. 'use' => 'encoded',
  1705. 'namespace' => $schemaNamespace,
  1706. 'encodingStyle' => SOAP_SCHEMA_ENCODING);
  1707. }
  1708. // Populate tree with service information
  1709. // XXX Current implementation supports one service which groups
  1710. // all of the ports together, one port per binding
  1711. // XXX What about https?
  1712. // *** <wsdl:service> ***
  1713. $this->wsdl->services[$service_name . 'Service'] = array('ports' => array());
  1714. $thisService =& $this->wsdl->services[$service_name . 'Service']['ports'];
  1715. foreach ($this->wsdl->bindings as $bindingName => $bindingData)
  1716. {
  1717. $thisService[$bindingData['type']] = array(
  1718. 'name' => $bindingData['type'],
  1719. 'binding' => $bindingName,
  1720. 'namespace' => $this->tnsPrefix,
  1721. 'address' => array('location' =>
  1722. 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] .
  1723. (isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '')),
  1724. 'type' => 'soap');
  1725. }
  1726. // Set service
  1727. $this->wsdl->set_service($service_name . 'Service');
  1728. $this->wsdl->uri = $this->wsdl->namespaces[$this->tnsPrefix];
  1729. // Create WSDL definition
  1730. // *** <wsdl:definitions> ***
  1731. $this->wsdl->definition = array(
  1732. 'name' => $service_name,
  1733. 'targetNamespace' => $this->wsdl->namespaces[$this->tnsPrefix],
  1734. 'xmlns' => SCHEMA_WSDL);
  1735. foreach ($this->wsdl->namespaces as $nsPrefix => $namespace)
  1736. $this->wsdl->definition['xmlns:' . $nsPrefix] = $namespace;
  1737. }
  1738. // This function is adapted from Dmitri V's implementation of
  1739. // DISCO/WSDL generation. It separates namespace from type name in a
  1740. // __typedef key and creates a new namespace entry in the WSDL structure
  1741. // if the namespace has not been used before. The namespace prefix and
  1742. // type name are returned. If no namespace is specified, xsd is assumed.
  1743. //
  1744. // We will not need this function anymore once __typedef is eliminated.
  1745. function _getTypeNs($type) {
  1746. preg_match_all("'\{(.*)\}'sm",$type,$m);
  1747. if (isset($m[1][0]) && $m[1][0] != '') {
  1748. if (!array_key_exists($m[1][0],$this->wsdl->ns)) {
  1749. $ns_pref = 'ns' . count($this->wsdl->namespaces);
  1750. $this->wsdl->ns[$m[1][0]] = $ns_pref;
  1751. $this->wsdl->namespaces[$ns_pref] = $m[1][0];
  1752. }
  1753. $typens = $this->wsdl->ns[$m[1][0]];
  1754. $type = ereg_replace($m[0][0],'',$type);
  1755. } else {
  1756. $typens = 'xsd';
  1757. }
  1758. return array($typens,$type);
  1759. }
  1760. }
  1761. ?>