| 1 |
lars |
1 |
<?php
|
|
|
2 |
/**
|
|
|
3 |
* This file contains the code for the SOAP server.
|
|
|
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 |
|
|
|
25 |
require_once 'SOAP/Base.php';
|
|
|
26 |
require_once 'SOAP/Fault.php';
|
|
|
27 |
require_once 'SOAP/Parser.php';
|
|
|
28 |
require_once 'SOAP/Value.php';
|
|
|
29 |
require_once 'SOAP/WSDL.php';
|
|
|
30 |
|
|
|
31 |
/**
|
|
|
32 |
* SOAP Server Class
|
|
|
33 |
*
|
|
|
34 |
* Originaly based on SOAPx4 by Dietrich Ayala
|
|
|
35 |
* http://dietrich.ganx4.com/soapx4
|
|
|
36 |
*
|
|
|
37 |
* @access public
|
|
|
38 |
* @package SOAP
|
|
|
39 |
* @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
|
|
|
40 |
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
|
|
41 |
*/
|
|
|
42 |
class SOAP_Server extends SOAP_Base
|
|
|
43 |
{
|
|
|
44 |
/**
|
|
|
45 |
*
|
|
|
46 |
* @var array
|
|
|
47 |
*/
|
|
|
48 |
var $dispatch_map = array(); // create empty dispatch map
|
|
|
49 |
var $dispatch_objects = array();
|
|
|
50 |
var $soapobject = null;
|
|
|
51 |
var $call_methodname = null;
|
|
|
52 |
var $callHandler = null;
|
|
|
53 |
var $callValidation = true;
|
|
|
54 |
|
|
|
55 |
/**
|
|
|
56 |
* A list of headers that are going to be sent back to the client.
|
|
|
57 |
*
|
|
|
58 |
* @var array
|
|
|
59 |
*/
|
|
|
60 |
var $headers = array();
|
|
|
61 |
|
|
|
62 |
/**
|
|
|
63 |
*
|
|
|
64 |
* @var string
|
|
|
65 |
*/
|
|
|
66 |
var $request = '';
|
|
|
67 |
|
|
|
68 |
/**
|
|
|
69 |
*
|
|
|
70 |
* @var string XML-Encoding
|
|
|
71 |
*/
|
|
|
72 |
var $xml_encoding = SOAP_DEFAULT_ENCODING;
|
|
|
73 |
var $response_encoding = 'UTF-8';
|
|
|
74 |
|
|
|
75 |
var $result = 'successful'; // for logging interop results to db
|
|
|
76 |
|
|
|
77 |
var $endpoint = ''; // the uri to ME!
|
|
|
78 |
|
|
|
79 |
var $service = ''; //soapaction header
|
|
|
80 |
var $method_namespace = null;
|
|
|
81 |
|
|
|
82 |
/**
|
|
|
83 |
* Options.
|
|
|
84 |
*
|
|
|
85 |
* @var array
|
|
|
86 |
*/
|
|
|
87 |
var $_options = array('use' => 'encoded',
|
|
|
88 |
'style' => 'rpc',
|
|
|
89 |
'parameters' => 0,
|
|
|
90 |
'http_status_success' => '200 OK',
|
|
|
91 |
'http_status_fault' => '500 SOAP Fault');
|
|
|
92 |
|
|
|
93 |
function SOAP_Server($options = null)
|
|
|
94 |
{
|
|
|
95 |
ini_set('track_errors', 1);
|
|
|
96 |
parent::SOAP_Base('Server');
|
|
|
97 |
|
|
|
98 |
if (is_array($options)) {
|
|
|
99 |
if (isset($options['use'])) {
|
|
|
100 |
$this->_options['use'] = $options['use'];
|
|
|
101 |
}
|
|
|
102 |
if (isset($options['style'])) {
|
|
|
103 |
$this->_options['style'] = $options['style'];
|
|
|
104 |
}
|
|
|
105 |
if (isset($options['parameters'])) {
|
|
|
106 |
$this->_options['parameters'] = $options['parameters'];
|
|
|
107 |
}
|
|
|
108 |
}
|
|
|
109 |
// assume we encode with section 5
|
|
|
110 |
$this->_section5 = true;
|
|
|
111 |
if ($this->_options['use'] == 'literal') {
|
|
|
112 |
$this->_section5 = false;
|
|
|
113 |
}
|
|
|
114 |
}
|
|
|
115 |
|
|
|
116 |
/**
|
|
|
117 |
* Error handler for errors that happen in proxied methods.
|
|
|
118 |
*
|
|
|
119 |
* To always return a valid SOAP response even on errors that don't happen
|
|
|
120 |
* in this code, the errors are catched, transformed to a SOAP fault and
|
|
|
121 |
* immediately sent to the client.
|
|
|
122 |
*
|
|
|
123 |
* @see http://www.php.net/set_error_handler
|
|
|
124 |
*/
|
|
|
125 |
function _errorHandler($errno, $errmsg, $filename, $linenum)
|
|
|
126 |
{
|
|
|
127 |
/* The error handler should ignore '0' errors, eg. hidden by @ - see
|
|
|
128 |
* the set_error_handler manual page. (thanks to Alan Knowles). */
|
|
|
129 |
if (!$errno || !error_reporting() || $errno == E_NOTICE ||
|
|
|
130 |
(defined('E_STRICT') && $errno == constant('E_STRICT'))) {
|
|
|
131 |
return false;
|
|
|
132 |
}
|
|
|
133 |
|
|
|
134 |
$this->fault = new SOAP_Fault($errmsg, 'Server', 'PHP', "Errno: $errno\nFilename: $filename\nLineno: $linenum\n");
|
|
|
135 |
|
|
|
136 |
$this->_sendResponse();
|
|
|
137 |
exit;
|
|
|
138 |
}
|
|
|
139 |
|
|
|
140 |
function _getContentEncoding($content_type)
|
|
|
141 |
{
|
|
|
142 |
/* Get the character encoding of the incoming request treat incoming
|
|
|
143 |
* data as UTF-8 if no encoding set. */
|
|
|
144 |
$this->xml_encoding = 'UTF-8';
|
|
|
145 |
if (strpos($content_type, '=')) {
|
|
|
146 |
$enc = strtoupper(str_replace('"', '', substr(strstr($content_type, '='), 1)));
|
|
|
147 |
if (!in_array($enc, $this->_encodings)) {
|
|
|
148 |
return false;
|
|
|
149 |
}
|
|
|
150 |
$this->xml_encoding = $enc;
|
|
|
151 |
}
|
|
|
152 |
|
|
|
153 |
return true;
|
|
|
154 |
}
|
|
|
155 |
|
|
|
156 |
|
|
|
157 |
/**
|
|
|
158 |
* Parses the request and posts or returns the response.
|
|
|
159 |
*
|
|
|
160 |
* @param string $data The SOAP request data.
|
|
|
161 |
* @param string $endpoint The service endpoint. Determined automatically
|
|
|
162 |
* if left empty.
|
|
|
163 |
* @param boolean $test
|
|
|
164 |
* @param boolean $return Whether to return the SOAP response data
|
|
|
165 |
* instead of sending it to the client.
|
|
|
166 |
*/
|
|
|
167 |
function service($data, $endpoint = '', $test = false, $return = false)
|
|
|
168 |
{
|
|
|
169 |
$response = null;
|
|
|
170 |
$attachments = array();
|
|
|
171 |
$useEncoding = 'DIME';
|
|
|
172 |
|
|
|
173 |
/* Figure out our endpoint. */
|
|
|
174 |
$this->endpoint = $endpoint;
|
|
|
175 |
if (!$test && !$this->endpoint) {
|
|
|
176 |
/* We'll try to build our endpoint. */
|
|
|
177 |
$this->endpoint = 'http://' . $_SERVER['SERVER_NAME'];
|
|
|
178 |
if (isset($_SERVER['SERVER_PORT'])) {
|
|
|
179 |
$this->endpoint .= ':' . $_SERVER['SERVER_PORT'];
|
|
|
180 |
}
|
|
|
181 |
$this->endpoint .= $_SERVER['SCRIPT_NAME'];
|
|
|
182 |
}
|
|
|
183 |
|
|
|
184 |
/* Get the character encoding of the incoming request treat incoming
|
|
|
185 |
* data as UTF-8 if no encoding set. */
|
|
|
186 |
if (isset($_SERVER['CONTENT_TYPE'])) {
|
|
|
187 |
if (strcasecmp($_SERVER['CONTENT_TYPE'], 'application/dime') == 0) {
|
|
|
188 |
$this->_decodeDIMEMessage($data, $this->headers, $attachments);
|
|
|
189 |
$useEncoding = 'DIME';
|
|
|
190 |
} elseif (stristr($_SERVER['CONTENT_TYPE'], 'multipart/related')) {
|
|
|
191 |
/* This is a mime message, let's decode it. */
|
|
|
192 |
$data = 'Content-Type: ' .
|
|
|
193 |
stripslashes($_SERVER['CONTENT_TYPE']) .
|
|
|
194 |
"\r\n\r\n" . $data;
|
|
|
195 |
$this->_decodeMimeMessage($data, $this->headers, $attachments);
|
|
|
196 |
$useEncoding = 'Mime';
|
|
|
197 |
}
|
|
|
198 |
if (!isset($this->headers['content-type'])) {
|
|
|
199 |
$this->headers['content-type'] = stripslashes($_SERVER['CONTENT_TYPE']);
|
|
|
200 |
}
|
|
|
201 |
if (!$this->fault &&
|
|
|
202 |
!$this->_getContentEncoding($this->headers['content-type'])) {
|
|
|
203 |
$this->xml_encoding = SOAP_DEFAULT_ENCODING;
|
|
|
204 |
/* Found encoding we don't understand; return a fault. */
|
|
|
205 |
$this->_raiseSoapFault('Unsupported encoding, use one of ISO-8859-1, US-ASCII, UTF-8', '', '', 'Server');
|
|
|
206 |
}
|
|
|
207 |
}
|
|
|
208 |
|
|
|
209 |
/* If this is not a POST with Content-Type text/xml, try to return a
|
|
|
210 |
* WSDL file. */
|
|
|
211 |
if (!$this->fault && !$test &&
|
|
|
212 |
((isset($_SERVER['REQUEST_METHOD']) &&
|
|
|
213 |
$_SERVER['REQUEST_METHOD'] != 'POST') ||
|
|
|
214 |
(isset($this->headers['content-type']) &&
|
|
|
215 |
strncmp($this->headers['content-type'], 'text/xml', 8) != 0))) {
|
|
|
216 |
/* This is not possibly a valid SOAP request, try to return a WSDL
|
|
|
217 |
* file. */
|
|
|
218 |
$got = isset($this->headers['content-type']) ? $this->headers['content-type'] : 'Nothing!';
|
|
|
219 |
$this->_raiseSoapFault('Invalid SOAP request, must be POST with content-type: text/xml, got: ' . $got, '', '', 'Server');
|
|
|
220 |
}
|
|
|
221 |
|
|
|
222 |
if (!$this->fault) {
|
|
|
223 |
/* $response is a SOAP_Msg object. */
|
|
|
224 |
$soap_msg = $this->parseRequest($data, $attachments);
|
|
|
225 |
|
|
|
226 |
/* Handle Mime or DIME encoding. */
|
|
|
227 |
/* TODO: DIME decoding should move to the transport, do it here
|
|
|
228 |
* for now and for ease of getting it done. */
|
|
|
229 |
if (count($this->_attachments)) {
|
|
|
230 |
if ($useEncoding == 'Mime') {
|
|
|
231 |
$soap_msg = $this->_makeMimeMessage($soap_msg);
|
|
|
232 |
} else {
|
|
|
233 |
// default is dime
|
|
|
234 |
$soap_msg = $this->_makeDIMEMessage($soap_msg);
|
|
|
235 |
$this->headers['Content-Type'] = 'application/dime';
|
|
|
236 |
}
|
|
|
237 |
if (PEAR::isError($soap_msg)) {
|
|
|
238 |
return $this->_raiseSoapFault($soap_msg);
|
|
|
239 |
}
|
|
|
240 |
}
|
|
|
241 |
|
|
|
242 |
if (is_array($soap_msg)) {
|
|
|
243 |
$response = $soap_msg['body'];
|
|
|
244 |
if (count($soap_msg['headers'])) {
|
|
|
245 |
$this->headers = $soap_msg['headers'];
|
|
|
246 |
}
|
|
|
247 |
} else {
|
|
|
248 |
$response = $soap_msg;
|
|
|
249 |
}
|
|
|
250 |
}
|
|
|
251 |
|
|
|
252 |
if ($return) {
|
|
|
253 |
if ($this->fault) {
|
|
|
254 |
$response = $this->fault->message();
|
|
|
255 |
}
|
|
|
256 |
return $response;
|
|
|
257 |
}
|
|
|
258 |
|
|
|
259 |
$this->_sendResponse($response);
|
|
|
260 |
}
|
|
|
261 |
|
|
|
262 |
/**
|
|
|
263 |
* Sends the final HTTP response to the client, including the HTTP header
|
|
|
264 |
* and the HTTP body.
|
|
|
265 |
*
|
|
|
266 |
* If an error happened, it returns a SOAP fault instead of the response
|
|
|
267 |
* body.
|
|
|
268 |
*
|
|
|
269 |
* @param string $response The response body.
|
|
|
270 |
*/
|
|
|
271 |
function _sendResponse($response = '')
|
|
|
272 |
{
|
|
|
273 |
/* Make distinction between the different SAPIs, running PHP as CGI or
|
|
|
274 |
* as a module. */
|
|
|
275 |
if (stristr(php_sapi_name(), 'cgi') === 0) {
|
|
|
276 |
$hdrs_type = 'Status:';
|
|
|
277 |
} else {
|
|
|
278 |
$hdrs_type = 'HTTP/1.1';
|
|
|
279 |
}
|
|
|
280 |
|
|
|
281 |
if ($this->fault) {
|
|
|
282 |
$hdrs = $hdrs_type . ' ' . $this->_options['http_status_fault'] . "\r\n";
|
|
|
283 |
$response = $this->fault->message($this->response_encoding);
|
|
|
284 |
} else {
|
|
|
285 |
$hdrs = $hdrs_type . ' ' . $this->_options['http_status_success'] . "\r\n";
|
|
|
286 |
}
|
|
|
287 |
header($hdrs);
|
|
|
288 |
|
|
|
289 |
$this->headers['Server'] = SOAP_LIBRARY_NAME;
|
|
|
290 |
if (!isset($this->headers['Content-Type'])) {
|
|
|
291 |
$this->headers['Content-Type'] = 'text/xml; charset=' .
|
|
|
292 |
$this->response_encoding;
|
|
|
293 |
}
|
|
|
294 |
$this->headers['Content-Length'] = strlen($response);
|
|
|
295 |
|
|
|
296 |
foreach ($this->headers as $k => $v) {
|
|
|
297 |
header("$k: $v");
|
|
|
298 |
$hdrs .= "$k: $v\r\n";
|
|
|
299 |
}
|
|
|
300 |
|
|
|
301 |
$this->response = $hdrs . "\r\n" . $response;
|
|
|
302 |
print $response;
|
|
|
303 |
}
|
|
|
304 |
|
|
|
305 |
function &callMethod($methodname, &$args)
|
|
|
306 |
{
|
|
|
307 |
if ($this->callHandler) {
|
|
|
308 |
$ret = @call_user_func_array($this->callHandler, array($methodname, $args));
|
|
|
309 |
return $ret;
|
|
|
310 |
}
|
|
|
311 |
|
|
|
312 |
set_error_handler(array($this, '_errorHandler'));
|
|
|
313 |
|
|
|
314 |
if ($args) {
|
|
|
315 |
/* Call method with parameters. */
|
|
|
316 |
if (isset($this->soapobject) && is_object($this->soapobject)) {
|
|
|
317 |
$ret = call_user_func_array(array(&$this->soapobject, $methodname), $args);
|
|
|
318 |
} else {
|
|
|
319 |
$ret = call_user_func_array($methodname, $args);
|
|
|
320 |
}
|
|
|
321 |
} else {
|
|
|
322 |
/* Call method withour parameters. */
|
|
|
323 |
if (is_object($this->soapobject)) {
|
|
|
324 |
$ret = call_user_func(array(&$this->soapobject, $methodname));
|
|
|
325 |
} else {
|
|
|
326 |
$ret = call_user_func($methodname);
|
|
|
327 |
}
|
|
|
328 |
}
|
|
|
329 |
|
|
|
330 |
restore_error_handler();
|
|
|
331 |
|
|
|
332 |
return $ret;
|
|
|
333 |
}
|
|
|
334 |
|
|
|
335 |
/**
|
|
|
336 |
* Creates SOAP_Value objects with return values from method.
|
|
|
337 |
* Uses method signature to determine type.
|
|
|
338 |
*
|
|
|
339 |
* @param mixed $method_response The result(s).
|
|
|
340 |
* @param array|string $type The type(s) of the return value(s).
|
|
|
341 |
* @param string $return_name The name of the return value.
|
|
|
342 |
* @param string $namespace The namespace of the return value.
|
|
|
343 |
*
|
|
|
344 |
* @return array List of SOAP_Value objects.
|
|
|
345 |
*/
|
|
|
346 |
function buildResult(&$method_response, &$return_type,
|
|
|
347 |
$return_name = 'return', $namespace = '')
|
|
|
348 |
{
|
|
|
349 |
if (is_a($method_response, 'SOAP_Value')) {
|
|
|
350 |
$return_val = array($method_response);
|
|
|
351 |
} else {
|
|
|
352 |
if (is_array($return_type) && is_array($method_response)) {
|
|
|
353 |
$i = 0;
|
|
|
354 |
|
|
|
355 |
foreach ($return_type as $key => $type) {
|
|
|
356 |
if (is_numeric($key)) {
|
|
|
357 |
$key = 'item';
|
|
|
358 |
}
|
|
|
359 |
if (is_a($method_response[$i], 'SOAP_Value')) {
|
|
|
360 |
$return_val[] =& $method_response[$i++];
|
|
|
361 |
} else {
|
|
|
362 |
$qn = new QName($key, $namespace);
|
|
|
363 |
$return_val[] = new SOAP_Value($qn->fqn(), $type, $method_response[$i++]);
|
|
|
364 |
}
|
|
|
365 |
}
|
|
|
366 |
} else {
|
|
|
367 |
if (is_array($return_type)) {
|
|
|
368 |
$keys = array_keys($return_type);
|
|
|
369 |
if (!is_numeric($keys[0])) {
|
|
|
370 |
$return_name = $keys[0];
|
|
|
371 |
}
|
|
|
372 |
$values = array_values($return_type);
|
|
|
373 |
$return_type = $values[0];
|
|
|
374 |
}
|
|
|
375 |
$qn = new QName($return_name, $namespace);
|
|
|
376 |
$return_val = array(new SOAP_Value($qn->fqn(), $return_type, $method_response));
|
|
|
377 |
}
|
|
|
378 |
}
|
|
|
379 |
return $return_val;
|
|
|
380 |
}
|
|
|
381 |
|
|
|
382 |
function parseRequest($data = '', $attachments = null)
|
|
|
383 |
{
|
|
|
384 |
/* Parse response, get SOAP_Parser object. */
|
|
|
385 |
$parser = new SOAP_Parser($data, $this->xml_encoding, $attachments);
|
|
|
386 |
|
|
|
387 |
if ($parser->fault) {
|
|
|
388 |
/* Fault occurred during message parsing. */
|
|
|
389 |
$this->fault = $parser->fault;
|
|
|
390 |
return null;
|
|
|
391 |
}
|
|
|
392 |
if (!count($parser->root_struct_name)) {
|
|
|
393 |
/* No method specified. */
|
|
|
394 |
$this->_raiseSoapFault('No method specified in request.');
|
|
|
395 |
return null;
|
|
|
396 |
}
|
|
|
397 |
|
|
|
398 |
/* Handle message headers. */
|
|
|
399 |
$request_headers = $parser->getHeaders();
|
|
|
400 |
$header_results = array();
|
|
|
401 |
|
|
|
402 |
if ($request_headers) {
|
|
|
403 |
if (!is_a($request_headers, 'SOAP_Value')) {
|
|
|
404 |
$this->_raiseSoapFault('Parser did not return SOAP_Value object: ' . $request_headers, '', '', 'Server');
|
|
|
405 |
return null;
|
|
|
406 |
}
|
|
|
407 |
if ($request_headers->value) {
|
|
|
408 |
/* Handle headers now. */
|
|
|
409 |
foreach ($request_headers->value as $header_val) {
|
|
|
410 |
$f_exists = $this->validateMethod($header_val->name, $header_val->namespace);
|
|
|
411 |
|
|
|
412 |
/* TODO: this does not take into account message routing
|
|
|
413 |
* yet. */
|
|
|
414 |
$myactor = !$header_val->actor ||
|
|
|
415 |
$header_val->actor == 'http://schemas.xmlsoap.org/soap/actor/next' ||
|
|
|
416 |
$header_val->actor == $this->endpoint;
|
|
|
417 |
|
|
|
418 |
if (!$f_exists && $header_val->mustunderstand && $myactor) {
|
|
|
419 |
$this->_raiseSoapFault('I don\'t understand header ' . $header_val->name, '', '', 'MustUnderstand');
|
|
|
420 |
return null;
|
|
|
421 |
}
|
|
|
422 |
|
|
|
423 |
/* We only handle the header if it's for us. */
|
|
|
424 |
$isok = $f_exists && $myactor;
|
|
|
425 |
|
|
|
426 |
if ($isok) {
|
|
|
427 |
/* Call our header now! */
|
|
|
428 |
$header_method = $header_val->name;
|
|
|
429 |
$header_data = array($this->_decode($header_val));
|
|
|
430 |
/* If there are parameters to pass. */
|
|
|
431 |
$hr =& $this->callMethod($header_method, $header_data);
|
|
|
432 |
if (PEAR::isError($hr)) {
|
|
|
433 |
$this->_raiseSoapFault($hr);
|
|
|
434 |
return null;
|
|
|
435 |
}
|
|
|
436 |
$results = $this->buildResult($hr, $this->return_type, $header_method, $header_val->namespace);
|
|
|
437 |
$header_results[] = $results[0];
|
|
|
438 |
}
|
|
|
439 |
}
|
|
|
440 |
}
|
|
|
441 |
}
|
|
|
442 |
|
|
|
443 |
/* Handle the method call. */
|
|
|
444 |
/* Evaluate message, getting back a SOAP_Value object. */
|
|
|
445 |
$this->call_methodname = $this->methodname = $parser->root_struct_name[0];
|
|
|
446 |
|
|
|
447 |
/* Figure out the method namespace. */
|
|
|
448 |
$this->method_namespace = $parser->message[$parser->root_struct[0]]['namespace'];
|
|
|
449 |
|
|
|
450 |
if ($this->_wsdl) {
|
|
|
451 |
$this->_setSchemaVersion($this->_wsdl->xsd);
|
|
|
452 |
$dataHandler = $this->_wsdl->getDataHandler($this->methodname, $this->method_namespace);
|
|
|
453 |
if ($dataHandler)
|
|
|
454 |
$this->call_methodname = $this->methodname = $dataHandler;
|
|
|
455 |
|
|
|
456 |
$this->_portName = $this->_wsdl->getPortName($this->methodname);
|
|
|
457 |
if (PEAR::isError($this->_portName)) {
|
|
|
458 |
$this->_raiseSoapFault($this->_portName);
|
|
|
459 |
return null;
|
|
|
460 |
}
|
|
|
461 |
$opData = $this->_wsdl->getOperationData($this->_portName, $this->methodname);
|
|
|
462 |
if (PEAR::isError($opData)) {
|
|
|
463 |
$this->_raiseSoapFault($opData);
|
|
|
464 |
return null;
|
|
|
465 |
}
|
|
|
466 |
$this->_options['style'] = $opData['style'];
|
|
|
467 |
$this->_options['use'] = $opData['output']['use'];
|
|
|
468 |
$this->_options['parameters'] = $opData['parameters'];
|
|
|
469 |
}
|
|
|
470 |
|
|
|
471 |
/* Does method exist? */
|
|
|
472 |
if (!$this->methodname ||
|
|
|
473 |
!$this->validateMethod($this->methodname, $this->method_namespace)) {
|
|
|
474 |
$this->_raiseSoapFault('method "' . $this->method_namespace . $this->methodname . '" not defined in service', '', '', 'Server');
|
|
|
475 |
return null;
|
|
|
476 |
}
|
|
|
477 |
|
|
|
478 |
if (!$request_val = $parser->getResponse()) {
|
|
|
479 |
return null;
|
|
|
480 |
}
|
|
|
481 |
if (!is_a($request_val, 'SOAP_Value')) {
|
|
|
482 |
$this->_raiseSoapFault('Parser did not return SOAP_Value object: ' . $request_val, '', '', 'Server');
|
|
|
483 |
return null;
|
|
|
484 |
}
|
|
|
485 |
|
|
|
486 |
/* Verify that SOAP_Value objects in request match the methods
|
|
|
487 |
* signature. */
|
|
|
488 |
if (!$this->verifyMethod($request_val)) {
|
|
|
489 |
/* verifyMethod() creates the fault. */
|
|
|
490 |
return null;
|
|
|
491 |
}
|
|
|
492 |
|
|
|
493 |
/* Need to set special error detection inside the value class to
|
|
|
494 |
* differentiate between no params passed, and an error decoding. */
|
|
|
495 |
$request_data = $this->__decodeRequest($request_val);
|
|
|
496 |
if (PEAR::isError($request_data)) {
|
|
|
497 |
$this->_raiseSoapFault($request_data);
|
|
|
498 |
return null;
|
|
|
499 |
}
|
|
|
500 |
$method_response =& $this->callMethod($this->call_methodname, $request_data);
|
|
|
501 |
if (PEAR::isError($method_response)) {
|
|
|
502 |
$this->_raiseSoapFault($method_response);
|
|
|
503 |
return null;
|
|
|
504 |
}
|
|
|
505 |
|
|
|
506 |
if ($this->_options['parameters'] ||
|
|
|
507 |
!$method_response ||
|
|
|
508 |
$this->_options['style'] == 'rpc') {
|
|
|
509 |
/* Get the method result. */
|
|
|
510 |
if (is_null($method_response)) {
|
|
|
511 |
$return_val = null;
|
|
|
512 |
} else {
|
|
|
513 |
$return_val = $this->buildResult($method_response, $this->return_type);
|
|
|
514 |
}
|
|
|
515 |
|
|
|
516 |
$qn = new QName($this->methodname . 'Response', $this->method_namespace);
|
|
|
517 |
$methodValue = new SOAP_Value($qn->fqn(), 'Struct', $return_val);
|
|
|
518 |
} else {
|
|
|
519 |
$methodValue =& $method_response;
|
|
|
520 |
}
|
|
|
521 |
return $this->makeEnvelope($methodValue, $header_results, $this->response_encoding);
|
|
|
522 |
}
|
|
|
523 |
|
|
|
524 |
function &__decodeRequest($request, $shift = false)
|
|
|
525 |
{
|
|
|
526 |
if (!$request) {
|
|
|
527 |
$decoded = null;
|
|
|
528 |
return $decoded;
|
|
|
529 |
}
|
|
|
530 |
|
|
|
531 |
/* Check for valid response. */
|
|
|
532 |
if (PEAR::isError($request)) {
|
|
|
533 |
$fault = &$this->_raiseSoapFault($request);
|
|
|
534 |
return $fault;
|
|
|
535 |
} else if (!is_a($request, 'SOAP_Value')) {
|
|
|
536 |
$fault = &$this->_raiseSoapFault('Invalid data in server::__decodeRequest');
|
|
|
537 |
return $fault;
|
|
|
538 |
}
|
|
|
539 |
|
|
|
540 |
/* Decode to native php datatype. */
|
|
|
541 |
$requestArray = $this->_decode($request);
|
|
|
542 |
/* Fault? */
|
|
|
543 |
if (PEAR::isError($requestArray)) {
|
|
|
544 |
$fault = &$this->_raiseSoapFault($requestArray);
|
|
|
545 |
return $fault;
|
|
|
546 |
}
|
|
|
547 |
if (is_object($requestArray) &&
|
|
|
548 |
get_class($requestArray) == 'stdClass') {
|
|
|
549 |
$requestArray = get_object_vars($requestArray);
|
|
|
550 |
} elseif ($this->_options['style'] == 'document') {
|
|
|
551 |
$requestArray = array($requestArray);
|
|
|
552 |
}
|
|
|
553 |
if (is_array($requestArray)) {
|
|
|
554 |
if (isset($requestArray['faultcode']) ||
|
|
|
555 |
isset($requestArray[SOAP_BASE::SOAPENVPrefix().':faultcode'])) {
|
|
|
556 |
$faultcode = $faultstring = $faultdetail = $faultactor = '';
|
|
|
557 |
foreach ($requestArray as $k => $v) {
|
|
|
558 |
if (stristr($k, 'faultcode')) {
|
|
|
559 |
$faultcode = $v;
|
|
|
560 |
}
|
|
|
561 |
if (stristr($k, 'faultstring')) {
|
|
|
562 |
$faultstring = $v;
|
|
|
563 |
}
|
|
|
564 |
if (stristr($k, 'detail')) {
|
|
|
565 |
$faultdetail = $v;
|
|
|
566 |
}
|
|
|
567 |
if (stristr($k, 'faultactor')) {
|
|
|
568 |
$faultactor = $v;
|
|
|
569 |
}
|
|
|
570 |
}
|
|
|
571 |
$fault = &$this->_raiseSoapFault($faultstring, $faultdetail, $faultactor, $faultcode);
|
|
|
572 |
return $fault;
|
|
|
573 |
}
|
|
|
574 |
/* Return array of return values. */
|
|
|
575 |
if ($shift && count($requestArray) == 1) {
|
|
|
576 |
$decoded = array_shift($requestArray);
|
|
|
577 |
return $decoded;
|
|
|
578 |
}
|
|
|
579 |
return $requestArray;
|
|
|
580 |
}
|
|
|
581 |
return $requestArray;
|
|
|
582 |
}
|
|
|
583 |
|
|
|
584 |
function verifyMethod($request)
|
|
|
585 |
{
|
|
|
586 |
if (!$this->callValidation) {
|
|
|
587 |
return true;
|
|
|
588 |
}
|
|
|
589 |
|
|
|
590 |
$params = $request->value;
|
|
|
591 |
|
|
|
592 |
/* Get the dispatch map if one exists. */
|
|
|
593 |
$map = null;
|
|
|
594 |
if (array_key_exists($this->methodname, $this->dispatch_map)) {
|
|
|
595 |
$map = $this->dispatch_map[$this->methodname];
|
|
|
596 |
} elseif (isset($this->soapobject)) {
|
|
|
597 |
if (method_exists($this->soapobject, '__dispatch')) {
|
|
|
598 |
$map = $this->soapobject->__dispatch($this->methodname);
|
|
|
599 |
} elseif (method_exists($this->soapobject, $this->methodname)) {
|
|
|
600 |
/* No map, all public functions are SOAP functions. */
|
|
|
601 |
return true;
|
|
|
602 |
}
|
|
|
603 |
}
|
|
|
604 |
if (!$map) {
|
|
|
605 |
$this->_raiseSoapFault('SOAP request specified an unhandled method "' . $this->methodname . '"', '', '', 'Client');
|
|
|
606 |
return false;
|
|
|
607 |
}
|
|
|
608 |
|
|
|
609 |
/* If we aliased the SOAP method name to a PHP function, change
|
|
|
610 |
* call_methodname so we do the right thing. */
|
|
|
611 |
if (array_key_exists('alias', $map) && !empty($map['alias'])) {
|
|
|
612 |
$this->call_methodname = $map['alias'];
|
|
|
613 |
}
|
|
|
614 |
|
|
|
615 |
/* If there are input parameters required. */
|
|
|
616 |
if ($map['in']) {
|
|
|
617 |
$this->input_value = count($map['in']);
|
|
|
618 |
$this->return_type = false;
|
|
|
619 |
if (is_array($map['out'])) {
|
|
|
620 |
$this->return_type = count($map['out']) > 1
|
|
|
621 |
? $map['out']
|
|
|
622 |
: array_shift($map['out']);
|
|
|
623 |
}
|
|
|
624 |
if (is_array($params)) {
|
|
|
625 |
/* Validate the number of parameters. */
|
|
|
626 |
if (count($params) == count($map['in'])) {
|
|
|
627 |
/* Make array of param types. */
|
|
|
628 |
foreach ($params as $param) {
|
|
|
629 |
$p[] = strtolower($param->type);
|
|
|
630 |
}
|
|
|
631 |
$sig_t = array_values($map['in']);
|
|
|
632 |
/* Validate each param's type. */
|
|
|
633 |
for ($i = 0; $i < count($p); $i++) {
|
|
|
634 |
/* If SOAP types do not match, it's still fine if the
|
|
|
635 |
* mapped php types match this allows using plain PHP
|
|
|
636 |
* variables to work (i.e. stuff like Decimal would
|
|
|
637 |
* fail otherwise). We consider this only error if the
|
|
|
638 |
* types exist in our type maps, and they differ. */
|
|
|
639 |
if (strcasecmp($sig_t[$i], $p[$i]) != 0 &&
|
|
|
640 |
isset($this->_typemap[SOAP_XML_SCHEMA_VERSION][$sig_t[$i]]) &&
|
|
|
641 |
strcasecmp($this->_typemap[SOAP_XML_SCHEMA_VERSION][$sig_t[$i]], $this->_typemap[SOAP_XML_SCHEMA_VERSION][$p[$i]]) != 0) {
|
|
|
642 |
|
|
|
643 |
$param = $params[$i];
|
|
|
644 |
$this->_raiseSoapFault("SOAP request contained mismatching parameters of name $param->name had type [{$p[$i]}], which did not match signature's type: [{$sig_t[$i]}], matched? " . (strcasecmp($sig_t[$i], $p[$i])), '', '', 'Client');
|
|
|
645 |
return false;
|
|
|
646 |
}
|
|
|
647 |
}
|
|
|
648 |
return true;
|
|
|
649 |
} else {
|
|
|
650 |
/* Wrong number of params. */
|
|
|
651 |
$this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" required ' . count($map['in']) . ' and request provided ' . count($params), '', '', 'Client');
|
|
|
652 |
return false;
|
|
|
653 |
}
|
|
|
654 |
} else {
|
|
|
655 |
/* No params. */
|
|
|
656 |
$this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" requires ' . count($map['in']) . ' parameters, and request provided none.', '', '', 'Client');
|
|
|
657 |
return false;
|
|
|
658 |
}
|
|
|
659 |
}
|
|
|
660 |
|
|
|
661 |
/* We'll try it anyway. */
|
|
|
662 |
return true;
|
|
|
663 |
}
|
|
|
664 |
|
|
|
665 |
function validateMethod($methodname, $namespace = null)
|
|
|
666 |
{
|
|
|
667 |
unset($this->soapobject);
|
|
|
668 |
|
|
|
669 |
if (!$this->callValidation) {
|
|
|
670 |
return true;
|
|
|
671 |
}
|
|
|
672 |
|
|
|
673 |
/* No SOAP access to private functions. */
|
|
|
674 |
if ($methodname[0] == '_') {
|
|
|
675 |
return false;
|
|
|
676 |
}
|
|
|
677 |
|
|
|
678 |
/* if it's in our function list, ok */
|
|
|
679 |
if (array_key_exists($methodname, $this->dispatch_map) &&
|
|
|
680 |
(!$namespace ||
|
|
|
681 |
!array_key_exists('namespace', $this->dispatch_map[$methodname]) ||
|
|
|
682 |
$namespace == $this->dispatch_map[$methodname]['namespace'])) {
|
|
|
683 |
if (array_key_exists('namespace', $this->dispatch_map[$methodname]))
|
|
|
684 |
$this->method_namespace = $this->dispatch_map[$methodname]['namespace'];
|
|
|
685 |
return true;
|
|
|
686 |
}
|
|
|
687 |
|
|
|
688 |
/* if it's in an object, it's ok */
|
|
|
689 |
if (isset($this->dispatch_objects[$namespace])) {
|
|
|
690 |
$c = count($this->dispatch_objects[$namespace]);
|
|
|
691 |
for ($i = 0; $i < $c; $i++) {
|
|
|
692 |
$obj =& $this->dispatch_objects[$namespace][$i];
|
|
|
693 |
/* If we have a dispatch map, and the function is not in the
|
|
|
694 |
* dispatch map, then it is not callable! */
|
|
|
695 |
if (method_exists($obj, '__dispatch')) {
|
|
|
696 |
if ($obj->__dispatch($methodname)) {
|
|
|
697 |
$this->method_namespace = $namespace;
|
|
|
698 |
$this->soapobject =& $obj;
|
|
|
699 |
return true;
|
|
|
700 |
}
|
|
|
701 |
} elseif (method_exists($obj, $methodname)) {
|
|
|
702 |
$this->method_namespace = $namespace;
|
|
|
703 |
$this->soapobject =& $obj;
|
|
|
704 |
return true;
|
|
|
705 |
}
|
|
|
706 |
}
|
|
|
707 |
}
|
|
|
708 |
|
|
|
709 |
return false;
|
|
|
710 |
}
|
|
|
711 |
|
|
|
712 |
function addObjectMap(&$obj, $namespace = null, $service_name = 'Default',
|
|
|
713 |
$service_desc = '')
|
|
|
714 |
{
|
|
|
715 |
if (!$namespace) {
|
|
|
716 |
if (isset($obj->namespace)) {
|
|
|
717 |
// XXX a bit of backwards compatibility
|
|
|
718 |
$namespace = $obj->namespace;
|
|
|
719 |
} else {
|
|
|
720 |
$this->_raiseSoapFault('No namespace provided for class!', '', '', 'Server');
|
|
|
721 |
return false;
|
|
|
722 |
}
|
|
|
723 |
}
|
|
|
724 |
if (!isset($this->dispatch_objects[$namespace])) {
|
|
|
725 |
$this->dispatch_objects[$namespace] = array();
|
|
|
726 |
}
|
|
|
727 |
$this->dispatch_objects[$namespace][] =& $obj;
|
|
|
728 |
|
|
|
729 |
// Create internal WSDL structures for object
|
|
|
730 |
|
|
|
731 |
// XXX Because some internal workings of PEAR::SOAP decide whether to
|
|
|
732 |
// do certain things by the presence or absence of _wsdl, we should
|
|
|
733 |
// only create a _wsdl structure if we know we can fill it; if
|
|
|
734 |
// __dispatch_map or __typedef for the object is missing, we should
|
|
|
735 |
// avoid creating it. Later, when we are using PHP 5 introspection, we
|
|
|
736 |
// will be able to make the data for all objects without any extra
|
|
|
737 |
// information from the developers, and this condition should be
|
|
|
738 |
// dropped.
|
|
|
739 |
|
|
|
740 |
// XXX Known issue: if imported WSDL (bindWSDL) or another WSDL source
|
|
|
741 |
// is used to add _wsdl structure information, then addObjectWSDL is
|
|
|
742 |
// used, there is a high possibility of _wsdl data corruption;
|
|
|
743 |
// therefore you should avoid using __dispatch_map/__typedef
|
|
|
744 |
// definitions AND other WSDL data sources in the same service. We
|
|
|
745 |
// exclude classes that don't have __typedefs to allow external WSDL
|
|
|
746 |
// files to be used with classes with no internal type definitions
|
|
|
747 |
// (the types are defined in the WSDL file). When addObjectWSDL is
|
|
|
748 |
// refactored to not cause corruption, this restriction can be
|
|
|
749 |
// relaxed.
|
|
|
750 |
|
|
|
751 |
// In summary, if you add an object with both a dispatch map and type
|
|
|
752 |
// definitions, then previous WSDL file operation and type definitions
|
|
|
753 |
// will be overwritten.
|
|
|
754 |
if (isset($obj->__dispatch_map) && isset($obj->__typedef)) {
|
|
|
755 |
$this->addObjectWSDL($obj, $namespace, $service_name, $service_desc);
|
|
|
756 |
}
|
|
|
757 |
|
|
|
758 |
return true;
|
|
|
759 |
}
|
|
|
760 |
|
|
|
761 |
/**
|
|
|
762 |
* Adds a method to the dispatch map.
|
|
|
763 |
*/
|
|
|
764 |
function addToMap($methodname, $in, $out, $namespace = null, $alias = null)
|
|
|
765 |
{
|
|
|
766 |
if (!$this->callHandler && !function_exists($methodname)) {
|
|
|
767 |
$this->_raiseSoapFault('Error mapping function', '', '', 'Server');
|
|
|
768 |
return false;
|
|
|
769 |
}
|
|
|
770 |
|
|
|
771 |
$this->dispatch_map[$methodname]['in'] = $in;
|
|
|
772 |
$this->dispatch_map[$methodname]['out'] = $out;
|
|
|
773 |
$this->dispatch_map[$methodname]['alias'] = $alias;
|
|
|
774 |
if ($namespace) {
|
|
|
775 |
$this->dispatch_map[$methodname]['namespace'] = $namespace;
|
|
|
776 |
}
|
|
|
777 |
|
|
|
778 |
return true;
|
|
|
779 |
}
|
|
|
780 |
|
|
|
781 |
function setCallHandler($callHandler, $validation = true)
|
|
|
782 |
{
|
|
|
783 |
$this->callHandler = $callHandler;
|
|
|
784 |
$this->callValidation = $validation;
|
|
|
785 |
}
|
|
|
786 |
|
|
|
787 |
/**
|
|
|
788 |
* @deprecated use bindWSDL from now on
|
|
|
789 |
*/
|
|
|
790 |
function bind($wsdl_url)
|
|
|
791 |
{
|
|
|
792 |
$this->bindWSDL($wsdl_url);
|
|
|
793 |
}
|
|
|
794 |
|
|
|
795 |
/**
|
|
|
796 |
* @param string a url to a WSDL resource
|
|
|
797 |
* @return void
|
|
|
798 |
*/
|
|
|
799 |
function bindWSDL($wsdl_url)
|
|
|
800 |
{
|
|
|
801 |
/* Instantiate WSDL class. */
|
|
|
802 |
$this->_wsdl = new SOAP_WSDL($wsdl_url);
|
|
|
803 |
if ($this->_wsdl->fault) {
|
|
|
804 |
$this->_raiseSoapFault($this->_wsdl->fault);
|
|
|
805 |
}
|
|
|
806 |
}
|
|
|
807 |
|
|
|
808 |
/**
|
|
|
809 |
* @return void
|
|
|
810 |
*/
|
|
|
811 |
function addObjectWSDL($wsdl_obj, $targetNamespace, $service_name,
|
|
|
812 |
$service_desc = '')
|
|
|
813 |
{
|
|
|
814 |
if (!isset($this->_wsdl)) {
|
|
|
815 |
$this->_wsdl = new SOAP_WSDL;
|
|
|
816 |
}
|
|
|
817 |
|
|
|
818 |
$this->_wsdl->parseObject($wsdl_obj, $targetNamespace, $service_name, $service_desc);
|
|
|
819 |
|
|
|
820 |
if ($this->_wsdl->fault) {
|
|
|
821 |
$this->_raiseSoapFault($this->_wsdl->fault);
|
|
|
822 |
}
|
|
|
823 |
}
|
|
|
824 |
|
|
|
825 |
}
|