PageRenderTime 60ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/PEAR/SOAP/WSDL.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 2294 lines | 1550 code | 241 blank | 503 comment | 340 complexity | 67394dd9431b6970eac9a32df6171f2a MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * This file contains the code for dealing with WSDL access and services.
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * LICENSE: This source file is subject to version 2.02 of the PHP license,
  8. * that is bundled with this package in the file LICENSE, and is available at
  9. * through the world-wide-web at http://www.php.net/license/2_02.txt. If you
  10. * did not receive a copy of the PHP license and are unable to obtain it
  11. * through the world-wide-web, please send a note to license@php.net so we can
  12. * mail you a copy immediately.
  13. *
  14. * @category Web Services
  15. * @package SOAP
  16. * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
  17. * @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
  18. * @author Chuck Hagenbuch <chuck@horde.org> Maintenance
  19. * @author Jan Schneider <jan@horde.org> Maintenance
  20. * @copyright 2003-2005 The PHP Group
  21. * @license http://www.php.net/license/2_02.txt PHP License 2.02
  22. * @link http://pear.php.net/package/SOAP
  23. */
  24. require_once 'SOAP/Base.php';
  25. require_once 'SOAP/Fault.php';
  26. require_once 'HTTP/Request.php';
  27. define('WSDL_CACHE_MAX_AGE', 43200);
  28. /**
  29. * This class parses WSDL files, and can be used by SOAP::Client to properly
  30. * register soap values for services.
  31. *
  32. * Originally based on SOAPx4 by Dietrich Ayala
  33. * http://dietrich.ganx4.com/soapx4
  34. *
  35. * @todo
  36. * - refactor namespace handling ($namespace/$ns)
  37. * - implement IDL type syntax declaration so we can generate WSDL
  38. *
  39. * @access public
  40. * @package SOAP
  41. * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
  42. * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
  43. */
  44. class SOAP_WSDL extends SOAP_Base
  45. {
  46. var $tns = null;
  47. var $definition = array();
  48. var $namespaces = array();
  49. var $ns = array();
  50. var $xsd = SOAP_XML_SCHEMA_VERSION;
  51. var $complexTypes = array();
  52. var $elements = array();
  53. var $messages = array();
  54. var $portTypes = array();
  55. var $bindings = array();
  56. var $imports = array();
  57. var $services = array();
  58. var $service = '';
  59. /**
  60. * URL to WSDL file.
  61. *
  62. * @var string
  63. */
  64. var $uri;
  65. /**
  66. * Parse documentation in the WSDL?
  67. *
  68. * @var boolean
  69. */
  70. var $docs;
  71. /**
  72. * Proxy parameters.
  73. *
  74. * @var array
  75. */
  76. var $proxy;
  77. /**
  78. * Enable tracing in the generated proxy class?
  79. *
  80. * @var boolean
  81. */
  82. var $trace = false;
  83. /**
  84. * Use WSDL cache?
  85. *
  86. * @var boolean
  87. */
  88. var $cacheUse;
  89. /**
  90. * WSDL cache directory.
  91. *
  92. * @var string
  93. */
  94. var $cacheDir;
  95. /**
  96. * Cache maximum lifetime (in seconds).
  97. *
  98. * @var integer
  99. */
  100. var $cacheMaxAge;
  101. /**
  102. * Class to use for WSDL parsing. Can be overridden for special cases,
  103. * subclasses, etc.
  104. *
  105. * @var string
  106. */
  107. var $wsdlParserClass = 'SOAP_WSDL_Parser';
  108. /**
  109. * Reserved PHP keywords.
  110. *
  111. * @link http://www.php.net/manual/en/reserved.php
  112. *
  113. * @var array
  114. */
  115. var $_reserved = array('abstract', 'and', 'array', 'as', 'break', 'case',
  116. 'catch', 'cfunction', 'class', 'clone', 'const',
  117. 'continue', 'declare', 'default', 'die', 'do',
  118. 'echo', 'else', 'elseif', 'empty', 'enddeclare',
  119. 'endfor', 'endforeach', 'endif', 'endswitch',
  120. 'endwhile', 'eval', 'exception', 'exit', 'extends',
  121. 'final', 'for', 'foreach', 'function', 'global',
  122. 'if', 'implements', 'include', 'include_once',
  123. 'interface', 'isset', 'list', 'new', 'old_function',
  124. 'or', 'php_user_filter', 'print', 'private',
  125. 'protected', 'public', 'require', 'require_once',
  126. 'return', 'static', 'switch', 'this', 'throw',
  127. 'try', 'unset', 'use', 'var', 'while', 'xor');
  128. /**
  129. * Regular expressions for invalid PHP labels.
  130. *
  131. * @link http://www.php.net/manual/en/language.variables.php.
  132. *
  133. * @var string
  134. */
  135. var $_invalid = array('/^[^a-zA-Z_\x7f-\xff]/', '/[^a-zA-Z0-9_\x7f-\xff]/');
  136. /**
  137. * SOAP_WSDL constructor.
  138. *
  139. * @param string $wsdl_uri URL to WSDL file.
  140. * @param array $proxy Options for HTTP_Request class
  141. * @see HTTP_Request.
  142. * @param boolean|string $cacheUse Use WSDL caching? The cache directory
  143. * if a string.
  144. * @param integer $cacheMaxAge Cache maximum lifetime (in seconds).
  145. * @param boolean $docs Parse documentation in the WSDL?
  146. *
  147. * @access public
  148. */
  149. function SOAP_WSDL($wsdl_uri = false,
  150. $proxy = array(),
  151. $cacheUse = false,
  152. $cacheMaxAge = WSDL_CACHE_MAX_AGE,
  153. $docs = false)
  154. {
  155. parent::SOAP_Base('WSDL');
  156. $this->uri = $wsdl_uri;
  157. $this->proxy = $proxy;
  158. $this->cacheUse = !empty($cacheUse);
  159. $this->cacheMaxAge = $cacheMaxAge;
  160. $this->docs = $docs;
  161. if (is_string($cacheUse)) {
  162. $this->cacheDir = $cacheUse;
  163. }
  164. if ($wsdl_uri) {
  165. if (!PEAR::isError($this->parseURL($wsdl_uri))) {
  166. reset($this->services);
  167. $this->service = key($this->services);
  168. }
  169. }
  170. }
  171. /**
  172. * @deprecated Use setService().
  173. */
  174. function set_service($service)
  175. {
  176. $this->setService($service);
  177. }
  178. /**
  179. * Sets the service currently to be used.
  180. *
  181. * @param string $service An (existing) service name.
  182. */
  183. function setService($service)
  184. {
  185. if (array_key_exists($service, $this->services)) {
  186. $this->service = $service;
  187. }
  188. }
  189. /**
  190. * Fills the WSDL array tree with data from a WSDL file.
  191. *
  192. * @param string $wsdl_uri URL to WSDL file.
  193. */
  194. function parseURL($wsdl_uri)
  195. {
  196. $parser = new $this->wsdlParserClass($wsdl_uri, $this, $this->docs);
  197. if ($parser->fault) {
  198. $this->_raiseSoapFault($parser->fault);
  199. }
  200. }
  201. /**
  202. * Fills the WSDL array tree with data from one or more PHP class objects.
  203. *
  204. * @param mixed $wsdl_obj An object or array of objects to add to
  205. * the internal WSDL tree.
  206. * @param string $targetNamespace The target namespace of schema types
  207. * etc.
  208. * @param string $service_name Name of the WSDL service.
  209. * @param string $service_desc Optional description of the WSDL
  210. * service.
  211. */
  212. function parseObject($wsdl_obj, $targetNamespace, $service_name,
  213. $service_desc = '')
  214. {
  215. $parser = new SOAP_WSDL_ObjectParser($wsdl_obj, $this,
  216. $targetNamespace, $service_name,
  217. $service_desc);
  218. if ($parser->fault) {
  219. $this->_raiseSoapFault($parser->fault);
  220. }
  221. }
  222. function getEndpoint($portName)
  223. {
  224. if ($this->_isfault()) {
  225. return $this->_getfault();
  226. }
  227. return (isset($this->services[$this->service]['ports'][$portName]['address']['location']))
  228. ? $this->services[$this->service]['ports'][$portName]['address']['location']
  229. : $this->_raiseSoapFault("No endpoint for port for $portName", $this->uri);
  230. }
  231. function _getPortName($operation, $service)
  232. {
  233. if (isset($this->services[$service]['ports'])) {
  234. $ports = $this->services[$service]['ports'];
  235. foreach ($ports as $port => $portAttrs) {
  236. $type = $ports[$port]['type'];
  237. if ($type == 'soap' &&
  238. isset($this->bindings[$portAttrs['binding']]['operations'][$operation])) {
  239. return $port;
  240. }
  241. }
  242. }
  243. return null;
  244. }
  245. /**
  246. * Finds the name of the first port that contains an operation of name
  247. * $operation. Always returns a SOAP portName.
  248. */
  249. function getPortName($operation, $service = null)
  250. {
  251. if ($this->_isfault()) {
  252. return $this->_getfault();
  253. }
  254. if (!$service) {
  255. $service = $this->service;
  256. }
  257. if (isset($this->services[$service]['ports'])) {
  258. if ($portName = $this->_getPortName($operation, $service)) {
  259. return $portName;
  260. }
  261. }
  262. // Try any service in the WSDL.
  263. foreach ($this->services as $serviceName => $service) {
  264. if (isset($this->services[$serviceName]['ports'])) {
  265. if ($portName = $this->_getPortName($operation, $serviceName)) {
  266. $this->service = $serviceName;
  267. return $portName;
  268. }
  269. }
  270. }
  271. return $this->_raiseSoapFault("No operation $operation in WSDL.", $this->uri);
  272. }
  273. function getOperationData($portName, $operation)
  274. {
  275. if ($this->_isfault()) {
  276. return $this->_getfault();
  277. }
  278. if (!isset($this->services[$this->service]['ports'][$portName]['binding']) ||
  279. !($binding = $this->services[$this->service]['ports'][$portName]['binding'])) {
  280. return $this->_raiseSoapFault("No binding for port $portName in WSDL.", $this->uri);
  281. }
  282. // Get operation data from binding.
  283. if (is_array($this->bindings[$binding]['operations'][$operation])) {
  284. $opData = $this->bindings[$binding]['operations'][$operation];
  285. }
  286. // Get operation data from porttype.
  287. $portType = $this->bindings[$binding]['type'];
  288. if (!$portType) {
  289. return $this->_raiseSoapFault("No port type for binding $binding in WSDL.", $this->uri);
  290. }
  291. if (is_array($type = $this->portTypes[$portType][$operation])) {
  292. if (isset($type['parameterOrder'])) {
  293. $opData['parameterOrder'] = $type['parameterOrder'];
  294. }
  295. $opData['input'] = array_merge($opData['input'], $type['input']);
  296. $opData['output'] = array_merge($opData['output'], $type['output']);
  297. }
  298. if (!$opData)
  299. return $this->_raiseSoapFault("No operation $operation for port $portName in WSDL.", $this->uri);
  300. $opData['parameters'] = false;
  301. if (isset($this->bindings[$binding]['operations'][$operation]['input']['namespace']))
  302. $opData['namespace'] = $this->bindings[$binding]['operations'][$operation]['input']['namespace'];
  303. // Message data from messages.
  304. $inputMsg = $opData['input']['message'];
  305. if (is_array($this->messages[$inputMsg])) {
  306. foreach ($this->messages[$inputMsg] as $pname => $pattrs) {
  307. if ($opData['style'] == 'document' &&
  308. $opData['input']['use'] == 'literal' &&
  309. $pname == 'parameters') {
  310. $opData['parameters'] = true;
  311. $opData['namespace'] = $this->namespaces[$pattrs['namespace']];
  312. $el = $this->elements[$pattrs['namespace']][$pattrs['type']];
  313. if (isset($el['elements'])) {
  314. foreach ($el['elements'] as $elname => $elattrs) {
  315. $opData['input']['parts'][$elname] = $elattrs;
  316. }
  317. }
  318. } else {
  319. $opData['input']['parts'][$pname] = $pattrs;
  320. }
  321. }
  322. }
  323. $outputMsg = $opData['output']['message'];
  324. if (is_array($this->messages[$outputMsg])) {
  325. foreach ($this->messages[$outputMsg] as $pname => $pattrs) {
  326. if ($opData['style'] == 'document' &&
  327. $opData['output']['use'] == 'literal' &&
  328. $pname == 'parameters') {
  329. $el = $this->elements[$pattrs['namespace']][$pattrs['type']];
  330. if (isset($el['elements'])) {
  331. foreach ($el['elements'] as $elname => $elattrs) {
  332. $opData['output']['parts'][$elname] = $elattrs;
  333. }
  334. }
  335. } else {
  336. $opData['output']['parts'][$pname] = $pattrs;
  337. }
  338. }
  339. }
  340. return $opData;
  341. }
  342. function matchMethod(&$operation)
  343. {
  344. if ($this->_isfault()) {
  345. return $this->_getfault();
  346. }
  347. // Overloading lowercases function names :(
  348. foreach ($this->services[$this->service]['ports'] as $portAttrs) {
  349. foreach (array_keys($this->bindings[$portAttrs['binding']]['operations']) as $op) {
  350. if (strcasecmp($op, $operation) == 0) {
  351. $operation = $op;
  352. }
  353. }
  354. }
  355. }
  356. /**
  357. * Given a datatype, what function handles the processing?
  358. *
  359. * This is used for doc/literal requests where we receive a datatype, and
  360. * we need to pass it to a method in out server class.
  361. *
  362. * @param string $datatype
  363. * @param string $namespace
  364. * @return string
  365. * @access public
  366. */
  367. function getDataHandler($datatype, $namespace)
  368. {
  369. // See if we have an element by this name.
  370. if (isset($this->namespaces[$namespace])) {
  371. $namespace = $this->namespaces[$namespace];
  372. }
  373. if (!isset($this->ns[$namespace])) {
  374. return null;
  375. }
  376. $nsp = $this->ns[$namespace];
  377. //if (!isset($this->elements[$nsp]))
  378. // $nsp = $this->namespaces[$nsp];
  379. if (!isset($this->elements[$nsp][$datatype])) {
  380. return null;
  381. }
  382. $checkmessages = array();
  383. // Find what messages use this datatype.
  384. foreach ($this->messages as $messagename => $message) {
  385. foreach ($message as $part) {
  386. if ($part['type'] == $datatype) {
  387. $checkmessages[] = $messagename;
  388. break;
  389. }
  390. }
  391. }
  392. // Find the operation that uses this message.
  393. foreach($this->portTypes as $porttype) {
  394. foreach ($porttype as $opname => $opinfo) {
  395. foreach ($checkmessages as $messagename) {
  396. if ($opinfo['input']['message'] == $messagename) {
  397. return $opname;
  398. }
  399. }
  400. }
  401. }
  402. return null;
  403. }
  404. function getSoapAction($portName, $operation)
  405. {
  406. if ($this->_isfault()) {
  407. return $this->_getfault();
  408. }
  409. if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'])) {
  410. return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'];
  411. }
  412. return false;
  413. }
  414. function getNamespace($portName, $operation)
  415. {
  416. if ($this->_isfault()) {
  417. return $this->_getfault();
  418. }
  419. if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'])) {
  420. return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'];
  421. }
  422. return false;
  423. }
  424. function getNamespaceAttributeName($namespace)
  425. {
  426. /* If it doesn't exist at first, flip the array and check again. */
  427. if (empty($this->ns[$namespace])) {
  428. $this->ns = array_flip($this->namespaces);
  429. }
  430. /* If it doesn't exist now, add it. */
  431. if (empty($this->ns[$namespace])) {
  432. return $this->addNamespace($namespace);
  433. }
  434. return $this->ns[$namespace];
  435. }
  436. function addNamespace($namespace)
  437. {
  438. if (!empty($this->ns[$namespace])) {
  439. return $this->ns[$namespace];
  440. }
  441. $n = count($this->ns);
  442. $attr = 'ns' . $n;
  443. $this->namespaces['ns' . $n] = $namespace;
  444. $this->ns[$namespace] = $attr;
  445. return $attr;
  446. }
  447. function _validateString($string)
  448. {
  449. return preg_match('/^[\w_:#\/]+$/', $string);
  450. }
  451. function _addArg(&$args, &$argarray, $argname)
  452. {
  453. if ($args) {
  454. $args .= ', ';
  455. }
  456. $args .= '$' . $argname;
  457. if (!$this->_validateString($argname)) {
  458. return;
  459. }
  460. if ($argarray) {
  461. $argarray .= ', ';
  462. }
  463. $argarray .= "'$argname' => $" . $argname;
  464. }
  465. function _elementArg(&$args, &$argarray, &$_argtype, $_argname)
  466. {
  467. $comments = '';
  468. $el = $this->elements[$_argtype['namespace']][$_argtype['type']];
  469. $tns = isset($this->ns[$el['namespace']])
  470. ? $this->ns[$el['namespace']]
  471. : $_argtype['namespace'];
  472. if (!empty($el['complex']) ||
  473. (isset($el['type']) &&
  474. isset($this->complexTypes[$tns][$el['type']]))) {
  475. // The element is a complex type.
  476. $comments .= " // {$_argtype['type']} is a ComplexType, refer to the WSDL for more info.\n";
  477. $attrname = "{$_argtype['type']}_attr";
  478. if (isset($el['type']) &&
  479. isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
  480. $comments .= " // {$_argtype['type']} may require attributes, refer to the WSDL for more info.\n";
  481. }
  482. $comments .= " \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n";
  483. $comments .= " \${$_argtype['type']} = new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n";
  484. $this->_addArg($args, $argarray, $_argtype['type']);
  485. if (isset($el['type']) &&
  486. isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
  487. if ($args) {
  488. $args .= ', ';
  489. }
  490. $args .= '$' . $attrname;
  491. }
  492. } elseif (isset($el['elements'])) {
  493. foreach ($el['elements'] as $ename => $element) {
  494. $comments .= " \$$ename = new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename', '" .
  495. (isset($element['type']) ? $element['type'] : false) .
  496. "', \$$ename);\n";
  497. $this->_addArg($args, $argarray, $ename);
  498. }
  499. } else {
  500. $comments .= " \$$_argname = new SOAP_Value('{{$this->namespaces[$tns]}}$_argname', '{$el['type']}', \$$_argname);\n";
  501. $this->_addArg($args, $argarray, $_argname);
  502. }
  503. return $comments;
  504. }
  505. function _complexTypeArg(&$args, &$argarray, &$_argtype, $_argname)
  506. {
  507. $comments = '';
  508. if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']])) {
  509. $comments = " // $_argname is a ComplexType {$_argtype['type']},\n" .
  510. " // refer to wsdl for more info\n";
  511. if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']]['attribute'])) {
  512. $comments .= " // $_argname may require attributes, refer to wsdl for more info\n";
  513. }
  514. $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $_argtype['type'];
  515. $comments .= " \$$_argname = new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n";
  516. }
  517. $this->_addArg($args, $argarray, $_argname);
  518. return $comments;
  519. }
  520. /**
  521. * Generates stub code from the WSDL that can be saved to a file or eval'd
  522. * into existence.
  523. */
  524. function generateProxyCode($port = '', $classname = '')
  525. {
  526. if ($this->_isfault()) {
  527. return $this->_getfault();
  528. }
  529. $multiport = count($this->services[$this->service]['ports']) > 1;
  530. if (!$port) {
  531. reset($this->services[$this->service]['ports']);
  532. $port = current($this->services[$this->service]['ports']);
  533. }
  534. // XXX currently do not support HTTP ports
  535. if ($port['type'] != 'soap') {
  536. return null;
  537. }
  538. // XXX currentPort is BAD
  539. $clienturl = $port['address']['location'];
  540. if (!$classname) {
  541. if ($multiport || $port) {
  542. $classname = 'WebService_' . $this->service . '_' . $port['name'];
  543. } else {
  544. $classname = 'WebService_' . $this->service;
  545. }
  546. $classname = $this->_sanitize($classname);
  547. }
  548. if (!$this->_validateString($classname)) {
  549. return null;
  550. }
  551. if (is_array($this->proxy) && count($this->proxy)) {
  552. $class = "class $classname extends SOAP_Client\n{\n" .
  553. " function $classname(\$path = '$clienturl')\n {\n" .
  554. " \$this->SOAP_Client(\$path, 0, 0,\n" .
  555. ' array(';
  556. foreach ($this->proxy as $key => $val) {
  557. if (is_array($val)) {
  558. $class .= "'$key' => array(";
  559. foreach ($val as $key2 => $val2) {
  560. $class .= "'$key2' => '$val2', ";
  561. }
  562. $class .= ')';
  563. } else {
  564. $class .= "'$key' => '$val', ";
  565. }
  566. }
  567. $class .= "));\n }\n";
  568. $class = str_replace(', ))', '))', $class);
  569. } else {
  570. $class = "class $classname extends SOAP_Client\n{\n" .
  571. " function $classname(\$path = '$clienturl')\n {\n" .
  572. " \$this->SOAP_Client(\$path, 0);\n" .
  573. " }\n";
  574. }
  575. // Get the binding, from that get the port type.
  576. $primaryBinding = $port['binding'];
  577. $primaryBinding = preg_replace("/^(.*:)/", '', $primaryBinding);
  578. $portType = $this->bindings[$primaryBinding]['type'];
  579. $portType = preg_replace("/^(.*:)/", '', $portType);
  580. $style = $this->bindings[$primaryBinding]['style'];
  581. // XXX currentPortType is BAD
  582. foreach ($this->portTypes[$portType] as $opname => $operation) {
  583. $binding = $this->bindings[$primaryBinding]['operations'][$opname];
  584. if (isset($binding['soapAction'])) {
  585. $soapaction = $binding['soapAction'];
  586. } else {
  587. $soapaction = null;
  588. }
  589. if (isset($binding['style'])) {
  590. $opstyle = $binding['style'];
  591. } else {
  592. $opstyle = $style;
  593. }
  594. $use = $binding['input']['use'];
  595. if ($use == 'encoded') {
  596. $namespace = $binding['input']['namespace'];
  597. } else {
  598. $bindingType = $this->bindings[$primaryBinding]['type'];
  599. $ns = $this->portTypes[$bindingType][$opname]['input']['namespace'];
  600. $namespace = $this->namespaces[$ns];
  601. }
  602. $args = '';
  603. $argarray = '';
  604. $comments = '';
  605. $wrappers = '';
  606. foreach ($operation['input'] as $argname => $argtype) {
  607. if ($argname == 'message') {
  608. foreach ($this->messages[$argtype] as $_argname => $_argtype) {
  609. $_argname = $this->_sanitize($_argname);
  610. if ($opstyle == 'document' && $use == 'literal' &&
  611. $_argtype['name'] == 'parameters') {
  612. // The type or element refered to is used for
  613. // parameters.
  614. $elattrs = null;
  615. $el = $this->elements[$_argtype['namespace']][$_argtype['type']];
  616. if ($el['complex']) {
  617. $namespace = $this->namespaces[$_argtype['namespace']];
  618. // XXX need to wrap the parameters in a
  619. // SOAP_Value.
  620. }
  621. if (isset($el['elements'])) {
  622. foreach ($el['elements'] as $elname => $elattrs) {
  623. $elname = $this->_sanitize($elname);
  624. // Is the element a complex type?
  625. if (isset($this->complexTypes[$elattrs['namespace']][$elname])) {
  626. $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
  627. } else {
  628. $this->_addArg($args, $argarray, $elname);
  629. }
  630. }
  631. }
  632. if ($el['complex'] && $argarray) {
  633. $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $el['name'];
  634. $comments .= " \${$el['name']} = new SOAP_Value('$wrapname', false, \$v = array($argarray));\n";
  635. $argarray = "'{$el['name']}' => \${$el['name']}";
  636. }
  637. } else {
  638. if (isset($_argtype['element'])) {
  639. // Element argument.
  640. $comments .= $this->_elementArg($args, $argarray, $_argtype, $_argtype['type']);
  641. } else {
  642. // Complex type argument.
  643. $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
  644. }
  645. }
  646. }
  647. }
  648. }
  649. // Validate entries.
  650. // Operation names are function names, so try to make sure it's
  651. // legal. This could potentially cause collisions, but let's try
  652. // to make everything callable and see how many problems that
  653. // causes.
  654. $opname_php = $this->_sanitize($opname);
  655. if (!$this->_validateString($opname_php)) {
  656. return null;
  657. }
  658. if ($argarray) {
  659. $argarray = "array($argarray)";
  660. } else {
  661. $argarray = 'null';
  662. }
  663. $class .= " function &$opname_php($args)\n {\n$comments$wrappers" .
  664. " \$result = \$this->call('$opname',\n" .
  665. " \$v = $argarray,\n" .
  666. " array('namespace' => '$namespace',\n" .
  667. " 'soapaction' => '$soapaction',\n" .
  668. " 'style' => '$opstyle',\n" .
  669. " 'use' => '$use'" .
  670. ($this->trace ? ",\n 'trace' => true" : '') . "));\n" .
  671. " return \$result;\n" .
  672. " }\n";
  673. }
  674. $class .= "}\n";
  675. return $class;
  676. }
  677. function generateAllProxies()
  678. {
  679. $proxycode = '';
  680. foreach (array_keys($this->services[$this->service]['ports']) as $key) {
  681. $port =& $this->services[$this->service]['ports'][$key];
  682. $proxycode .= $this->generateProxyCode($port);
  683. }
  684. return $proxycode;
  685. }
  686. function &getProxy($port = '', $name = '')
  687. {
  688. if ($this->_isfault()) {
  689. $fault =& $this->_getfault();
  690. return $fault;
  691. }
  692. $multiport = count($this->services[$this->service]['ports']) > 1;
  693. if (!$port) {
  694. reset($this->services[$this->service]['ports']);
  695. $port = current($this->services[$this->service]['ports']);
  696. }
  697. if ($multiport || $port) {
  698. $classname = 'WebService_' . $this->service . '_' . $port['name'];
  699. } else {
  700. $classname = 'WebService_' . $this->service;
  701. }
  702. if ($name) {
  703. $classname = $name . '_' . $classname;
  704. }
  705. $classname = $this->_sanitize($classname);
  706. if (!class_exists($classname)) {
  707. $proxy = $this->generateProxyCode($port, $classname);
  708. require_once 'SOAP/Client.php';
  709. eval($proxy);
  710. }
  711. $proxy = new $classname;
  712. return $proxy;
  713. }
  714. /**
  715. * Sanitizes a SOAP value, method or class name so that it can be used as
  716. * a valid PHP identifier. Invalid characters are converted into
  717. * underscores and reserved words are prefixed with an underscore.
  718. *
  719. * @param string $name The identifier to sanitize.
  720. *
  721. * @return string The sanitized identifier.
  722. */
  723. function _sanitize($name)
  724. {
  725. $name = preg_replace($this->_invalid, '_', $name);
  726. if (in_array($name, $this->_reserved)) {
  727. $name = '_' . $name;
  728. }
  729. return $name;
  730. }
  731. function &_getComplexTypeForElement($name, $namespace)
  732. {
  733. $t = null;
  734. if (isset($this->ns[$namespace]) &&
  735. isset($this->elements[$this->ns[$namespace]][$name]['type'])) {
  736. $type = $this->elements[$this->ns[$namespace]][$name]['type'];
  737. $ns = $this->elements[$this->ns[$namespace]][$name]['namespace'];
  738. if (isset($this->complexTypes[$ns][$type])) {
  739. $t = $this->complexTypes[$ns][$type];
  740. }
  741. }
  742. return $t;
  743. }
  744. function getComplexTypeNameForElement($name, $namespace)
  745. {
  746. $t = $this->_getComplexTypeForElement($name, $namespace);
  747. if ($t) {
  748. return $t['name'];
  749. }
  750. return null;
  751. }
  752. function getComplexTypeChildType($ns, $name, $child_ns, $child_name)
  753. {
  754. // Is the type an element?
  755. $t = $this->_getComplexTypeForElement($name, $ns);
  756. if ($t) {
  757. // No, get it from complex types directly.
  758. if (isset($t['elements'][$child_name]['type']))
  759. return $t['elements'][$child_name]['type'];
  760. } elseif (isset($this->ns[$ns]) &&
  761. isset($this->elements[$this->ns[$ns]][$name]['complex']) &&
  762. $this->elements[$this->ns[$ns]][$name]['complex']) {
  763. // Type is not an element but complex.
  764. return $this->elements[$this->ns[$ns]][$name]['elements'][$child_name]['type'];
  765. }
  766. return null;
  767. }
  768. /**
  769. * @param QName $name A parameter name.
  770. * @param QName $type A parameter type.
  771. *
  772. * @return array A list of [type, array element type, array element
  773. * namespace, array length].
  774. */
  775. function getSchemaType($type, $name)
  776. {
  777. // see if it's a complex type so we can deal properly with
  778. // SOAPENC:arrayType.
  779. if ($name && $type) {
  780. // XXX TODO:
  781. // look up the name in the wsdl and validate the type.
  782. foreach ($this->complexTypes as $types) {
  783. if (isset($types[$type->name])) {
  784. if (isset($types[$type->name]['type'])) {
  785. list($arraytype_ns, $arraytype, $array_depth) = isset($types[$type->name]['arrayType'])
  786. ? $this->_getDeepestArrayType($types[$type->name]['namespace'], $types[$type->name]['arrayType'])
  787. : array($this->namespaces[$types[$type->name]['namespace']], null, 0);
  788. return array($types[$type->name]['type'], $arraytype, $arraytype_ns, $array_depth);
  789. }
  790. if (isset($types[$type->name]['arrayType'])) {
  791. list($arraytype_ns, $arraytype, $array_depth) =
  792. $this->_getDeepestArrayType($types[$type->name]['namespace'], $types[$type->name]['arrayType']);
  793. return array('Array', $arraytype, $arraytype_ns, $array_depth);
  794. }
  795. if (!empty($types[$type->name]['elements'][$name->name])) {
  796. $type->name = $types[$type->name]['elements']['type'];
  797. return array($type->name, null, $this->namespaces[$types[$type->name]['namespace']], null);
  798. }
  799. break;
  800. }
  801. }
  802. }
  803. if ($type && $type->namespace) {
  804. $arrayType = null;
  805. // XXX TODO:
  806. // this code currently handles only one way of encoding array
  807. // types in wsdl need to do a generalized function to figure out
  808. // complex types
  809. $p = $this->ns[$type->namespace];
  810. if ($p && !empty($this->complexTypes[$p][$type->name])) {
  811. if ($arrayType = $this->complexTypes[$p][$type->name]['arrayType']) {
  812. $type->name = 'Array';
  813. } elseif ($this->complexTypes[$p][$type->name]['order'] == 'sequence' &&
  814. array_key_exists('elements', $this->complexTypes[$p][$type->name])) {
  815. reset($this->complexTypes[$p][$type->name]['elements']);
  816. // assume an array
  817. if (count($this->complexTypes[$p][$type->name]['elements']) == 1) {
  818. $arg = current($this->complexTypes[$p][$type->name]['elements']);
  819. $arrayType = $arg['type'];
  820. $type->name = 'Array';
  821. } else {
  822. foreach ($this->complexTypes[$p][$type->name]['elements'] as $element) {
  823. if ($element['name'] == $type->name) {
  824. $arrayType = $element['type'];
  825. $type->name = $element['type'];
  826. }
  827. }
  828. }
  829. } else {
  830. $type->name = 'Struct';
  831. }
  832. return array($type->name, $arrayType, $type->namespace, null);
  833. }
  834. }
  835. return array(null, null, null, null);
  836. }
  837. /**
  838. * Recurse through the WSDL structure looking for the innermost array type
  839. * of multi-dimensional arrays.
  840. *
  841. * Takes a namespace prefix and a type, which can be in the form 'type' or
  842. * 'type[]', and returns the full namespace URI, the type of the most
  843. * deeply nested array type found, and the number of levels of nesting.
  844. *
  845. * @access private
  846. * @return mixed array or nothing
  847. */
  848. function _getDeepestArrayType($nsPrefix, $arrayType)
  849. {
  850. static $trail = array();
  851. $arrayType = ereg_replace('\[\]$', '', $arrayType);
  852. // Protect against circular references XXX We really need to remove
  853. // trail from this altogether (it's very inefficient and in the wrong
  854. // place!) and put circular reference checking in when the WSDL info
  855. // is generated in the first place
  856. if (array_search($nsPrefix . ':' . $arrayType, $trail)) {
  857. return array(null, null, -count($trail));
  858. }
  859. if (array_key_exists($nsPrefix, $this->complexTypes) &&
  860. array_key_exists($arrayType, $this->complexTypes[$nsPrefix]) &&
  861. array_key_exists('arrayType', $this->complexTypes[$nsPrefix][$arrayType])) {
  862. $trail[] = $nsPrefix . ':' . $arrayType;
  863. $result = $this->_getDeepestArrayType($this->complexTypes[$nsPrefix][$arrayType]['namespace'],
  864. $this->complexTypes[$nsPrefix][$arrayType]['arrayType']);
  865. return array($result[0], $result[1], $result[2] + 1);
  866. }
  867. return array($this->namespaces[$nsPrefix], $arrayType, 0);
  868. }
  869. }
  870. class SOAP_WSDL_Cache extends SOAP_Base
  871. {
  872. /**
  873. * Use WSDL cache?
  874. *
  875. * @var boolean
  876. */
  877. var $_cacheUse;
  878. /**
  879. * WSDL cache directory.
  880. *
  881. * @var string
  882. */
  883. var $_cacheDir;
  884. /**
  885. * Cache maximum lifetime (in seconds)
  886. *
  887. * @var integer
  888. */
  889. var $_cacheMaxAge;
  890. /**
  891. * Constructor.
  892. *
  893. * @param boolean $cashUse Use caching?
  894. * @param integer $cacheMaxAge Cache maximum lifetime (in seconds)
  895. */
  896. function SOAP_WSDL_Cache($cacheUse = false,
  897. $cacheMaxAge = WSDL_CACHE_MAX_AGE,
  898. $cacheDir = null)
  899. {
  900. parent::SOAP_Base('WSDLCACHE');
  901. $this->_cacheUse = $cacheUse;
  902. $this->_cacheDir = $cacheDir;
  903. $this->_cacheMaxAge = $cacheMaxAge;
  904. }
  905. /**
  906. * Returns the path to the cache and creates it, if it doesn't exist.
  907. *
  908. * @private
  909. *
  910. * @return string The directory to use for the cache.
  911. */
  912. function _cacheDir()
  913. {
  914. if (!empty($this->_cacheDir)) {
  915. $dir = $this->_cacheDir;
  916. } else {
  917. $dir = getenv('WSDLCACHE');
  918. if (empty($dir)) {
  919. $dir = './wsdlcache';
  920. }
  921. }
  922. @mkdir($dir, 0700);
  923. return $dir;
  924. }
  925. /**
  926. * Retrieves a file from cache if it exists, otherwise retreive from net,
  927. * add to cache, and return from cache.
  928. *
  929. * @param string URL to WSDL
  930. * @param array proxy parameters
  931. * @param int expected MD5 of WSDL URL
  932. * @access public
  933. * @return string data
  934. */
  935. function get($wsdl_fname, $proxy_params = array(), $cache = 0)
  936. {
  937. $cachename = $md5_wsdl = $file_data = '';
  938. if ($this->_cacheUse) {
  939. // Try to retrieve WSDL from cache
  940. $cachename = $this->_cacheDir() . '/' . md5($wsdl_fname). ' .wsdl';
  941. if (file_exists($cachename) &&
  942. $file_data = file_get_contents($cachename)) {
  943. $md5_wsdl = md5($file_data);
  944. if ($cache) {
  945. if ($cache != $md5_wsdl) {
  946. return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname);
  947. }
  948. } else {
  949. $fi = stat($cachename);
  950. $cache_mtime = $fi[8];
  951. if ($cache_mtime + $this->_cacheMaxAge < time()) {
  952. // Expired, refetch.
  953. $md5_wsdl = '';
  954. }
  955. }
  956. }
  957. }
  958. // Not cached or not using cache. Retrieve WSDL from URL
  959. if (!$md5_wsdl) {
  960. // Is it a local file?
  961. if (strpos($wsdl_fname, 'file://') === 0) {
  962. $wsdl_fname = substr($wsdl_fname, 7);
  963. if (!file_exists($wsdl_fname)) {
  964. return $this->_raiseSoapFault('Unable to read local WSDL file', $wsdl_fname);
  965. }
  966. $file_data = file_get_contents($wsdl_fname);
  967. } elseif (!preg_match('|^https?://|', $wsdl_fname)) {
  968. return $this->_raiseSoapFault('Unknown schema of WSDL URL', $wsdl_fname);
  969. } else {
  970. $uri = explode('?', $wsdl_fname);
  971. $rq = new HTTP_Request($uri[0], $proxy_params);
  972. // the user agent HTTP_Request uses fouls things up
  973. if (isset($uri[1])) {
  974. $rq->addRawQueryString($uri[1]);
  975. }
  976. if (isset($proxy_params['proxy_host']) &&
  977. isset($proxy_params['proxy_port']) &&
  978. isset($proxy_params['proxy_user']) &&
  979. isset($proxy_params['proxy_pass'])) {
  980. $rq->setProxy($proxy_params['proxy_host'],
  981. $proxy_params['proxy_port'],
  982. $proxy_params['proxy_user'],
  983. $proxy_params['proxy_pass']);
  984. } elseif (isset($proxy_params['proxy_host']) &&
  985. isset($proxy_params['proxy_port'])) {
  986. $rq->setProxy($proxy_params['proxy_host'],
  987. $proxy_params['proxy_port']);
  988. }
  989. $result = $rq->sendRequest();
  990. if (PEAR::isError($result)) {
  991. return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname," . $rq->getResponseCode(), $wsdl_fname);
  992. }
  993. $file_data = $rq->getResponseBody();
  994. if (!$file_data) {
  995. return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname, no http body", $wsdl_fname);
  996. }
  997. }
  998. $md5_wsdl = md5($file_data);
  999. if ($this->_cacheUse) {
  1000. $fp = fopen($cachename, "wb");
  1001. fwrite($fp, $file_data);
  1002. fclose($fp);
  1003. }
  1004. }
  1005. if ($this->_cacheUse && $cache && $cache != $md5_wsdl) {
  1006. return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname);
  1007. }
  1008. return $file_data;
  1009. }
  1010. }
  1011. class SOAP_WSDL_Parser extends SOAP_Base
  1012. {
  1013. /**
  1014. * Define internal arrays of bindings, ports, operations,
  1015. * messages, etc.
  1016. */
  1017. var $currentMessage;
  1018. var $currentOperation;
  1019. var $currentPortType;
  1020. var $currentBinding;
  1021. var $currentPort;
  1022. /**
  1023. * Parser vars.
  1024. */
  1025. var $cache;
  1026. var $tns = null;
  1027. var $soapns = array('soap');
  1028. var $uri = '';
  1029. var $wsdl = null;
  1030. var $status = '';
  1031. var $element_stack = array();
  1032. var $parentElement = '';
  1033. var $schema = '';
  1034. var $schemaStatus = '';
  1035. var $schema_stack = array();
  1036. var $currentComplexType;
  1037. var $schema_element_stack = array();
  1038. var $currentElement;
  1039. /**
  1040. * Constructor.
  1041. */
  1042. function SOAP_WSDL_Parser($uri, &$wsdl, $docs = false)
  1043. {
  1044. parent::SOAP_Base('WSDLPARSER');
  1045. $this->cache = new SOAP_WSDL_Cache($wsdl->cacheUse,
  1046. $wsdl->cacheMaxAge,
  1047. $wsdl->cacheDir);
  1048. $this->uri = $uri;
  1049. $this->wsdl = &$wsdl;
  1050. $this->docs = $docs;
  1051. $this->parse($uri);
  1052. }
  1053. function parse($uri)
  1054. {
  1055. // Check whether content has been read.
  1056. $fd = $this->cache->get($uri, $this->wsdl->proxy);
  1057. if (PEAR::isError($fd)) {
  1058. return $this->_raiseSoapFault($fd);
  1059. }
  1060. // Create an XML parser.
  1061. $parser = xml_parser_create();
  1062. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
  1063. xml_set_object($parser, $this);
  1064. xml_set_element_handler($parser, 'startElement', 'endElement');
  1065. if ($this->docs) {
  1066. xml_set_character_data_handler($parser, 'characterData');
  1067. }
  1068. if (!xml_parse($parser, $fd, true)) {
  1069. $detail = sprintf('XML error on line %d: %s',
  1070. xml_get_current_line_number($parser),
  1071. xml_error_string(xml_get_error_code($parser)));
  1072. return $this->_raiseSoapFault("Unable to parse WSDL file $uri\n$detail");
  1073. }
  1074. xml_parser_free($parser);
  1075. return true;
  1076. }
  1077. /**
  1078. * start-element handler
  1079. */
  1080. function startElement($parser, $name, $attrs)
  1081. {
  1082. // Get element prefix.
  1083. $qname = new QName($name);
  1084. if ($qname->ns) {
  1085. $ns = $qname->ns;
  1086. if ($ns && ((!$this->tns && strcasecmp($qname->name, 'definitions') == 0) || $ns == $this->tns)) {
  1087. $name = $qname->name;
  1088. }
  1089. }
  1090. $this->currentTag = $qname->name;
  1091. $this->parentElement = '';
  1092. $stack_size = count($this->element_stack);
  1093. if ($stack_size) {
  1094. $this->parentElement = $this->element_stack[$stack_size - 1];
  1095. }
  1096. $this->element_stack[] = $this->currentTag;
  1097. // Find status, register data.
  1098. switch ($this->status) {
  1099. case 'types':
  1100. // sect 2.2 wsdl:types
  1101. // children: xsd:schema
  1102. $parent_tag = '';
  1103. $stack_size = count($this->schema_stack);
  1104. if ($stack_size) {
  1105. $parent_tag = $this->schema_stack[$stack_size - 1];
  1106. }
  1107. switch ($qname->name) {
  1108. case 'schema':
  1109. // No parent should be in the stack.
  1110. if (!$parent_tag || $parent_tag == 'types') {
  1111. if (array_key_exists('targetNamespace', $attrs)) {
  1112. $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
  1113. } else {
  1114. $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
  1115. }
  1116. $this->wsdl->complexTypes[$this->schema] = array();
  1117. $this->wsdl->elements[$this->schema] = array();
  1118. }
  1119. break;
  1120. case 'complexType':
  1121. if ($parent_tag == 'schema') {
  1122. $this->currentComplexType = $attrs['name'];
  1123. if (!isset($attrs['namespace'])) {
  1124. $attrs['namespace'] = $this->schema;
  1125. }
  1126. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType] = $attrs;
  1127. if (array_key_exists('base', $attrs)) {
  1128. $qn = new QName($attrs['base']);
  1129. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
  1130. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $qn->ns;
  1131. } else {
  1132. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
  1133. }
  1134. $this->schemaStatus = 'complexType';
  1135. } else {
  1136. $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = true;
  1137. }
  1138. break;
  1139. case 'element':
  1140. if (isset($attrs['type'])) {
  1141. $qn = new QName($attrs['type']);
  1142. $attrs['type'] = $qn->name;
  1143. if ($qn->ns && array_key_exists($qn->ns, $this->wsdl->namespaces)) {
  1144. $attrs['namespace'] = $qn->ns;
  1145. }
  1146. }
  1147. $parentElement = '';
  1148. $stack_size = count($this->schema_element_stack);
  1149. if ($stack_size > 0) {
  1150. $parentElement = $this->schema_element_stack[$stack_size - 1];
  1151. }
  1152. if (isset($attrs['ref'])) {
  1153. $qn = new QName($attrs['ref']);
  1154. $this->currentElement = $qn->name;
  1155. } else {
  1156. $this->currentElement = $attrs['name'];
  1157. }
  1158. $this->schema_element_stack[] = $this->currentElement;
  1159. if (!isset($attrs['namespace'])) {
  1160. $attrs['namespace'] = $this->schema;
  1161. }
  1162. if ($parent_tag == 'schema') {
  1163. $this->wsdl->elements[$this->schema][$this->currentElement] = $attrs;
  1164. $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = false;
  1165. $this->schemaStatus = 'element';
  1166. } elseif ($this->currentComplexType) {
  1167. // we're inside a complexType
  1168. if ((isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order']) &&
  1169. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] == 'sequence')
  1170. && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array') {
  1171. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['arrayType'] = isset($attrs['type']) ? $attrs['type'] : null;
  1172. }
  1173. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement] = $attrs;
  1174. } else {
  1175. $this->wsdl->elements[$this->schema][$parentElement]['elements'][$this->currentElement] = $attrs;
  1176. }
  1177. break;
  1178. case 'complexContent':
  1179. case 'simpleContent':
  1180. break;
  1181. case 'extension':
  1182. case 'restriction':
  1183. if ($this->schemaStatus == 'complexType') {
  1184. if (!empty($attrs['base'])) {
  1185. $qn = new QName($attrs['base']);
  1186. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
  1187. // Types that extend from other types aren't
  1188. // *of* those types. Reflect this by denoting
  1189. // which type they extend. I'm leaving the
  1190. // 'type' setting here since I'm not sure what
  1191. // removing it might break at the moment.
  1192. if ($qname->name == 'extension') {
  1193. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['extends'] = $qn->name;
  1194. }
  1195. } else {
  1196. $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
  1197. }
  1198. }
  1199. break;
  1200. case 'sequence':
  1201. if ($this->schemaStatus ==

Large files files are truncated, but you can click here to view the full file