debug ? $resp : array( 'faultCode' => empty($resp['faultCode']) ? -1 : $resp['faultCode'], 'faultString' => empty($resp['faultString']) ? 'Unknown error.' : $resp['faultString'], ); $xml = '' . $this->dataToXML($fault) . ''; } else $xml = '' . $this->dataToXML($resp) . ''; $xml = << $xml EOF; return $xml; } private function &dataToXML(&$data) { if (is_array($data)) { $is_assoc = false; $n = 0; foreach ($data as $k => $v) { if (!is_integer($k) || "$k" != "$n") { $is_assoc = true; break; } $n++; } if ($is_assoc) { $xml = ''; foreach ($data as $k => $v) $xml .= '' . htmlspecialchars($k) . '' . $this->dataToXML($v) . ''; $xml .= ''; } else { $xml = ''; foreach ($data as $v) $xml .= '' . $this->dataToXML($v) . ''; $xml .= ''; } } elseif (is_object($data)) { switch (get_class($data)) { case 'DateTime': $xml = '' . htmlspecialchars($data->format(DATE_ATOM)) . ''; break; case 'BinaryData': $xml = '' . $data->getBase64() . ''; break; default: throw new Exception("Unknown object type."); } } else { if (is_float($data)) $xml = '' . htmlspecialchars($data) . ''; elseif (is_integer($data)) $xml = '' . htmlspecialchars($data) . ''; else $xml = '' . htmlspecialchars($data) . ''; } return $xml; } public function &parseCall(&$rawData, &$method) { $doc = new DOMDocument; $doc->loadXML($rawData, LIBXML_NOBLANKS | LIBXML_NOENT | LIBXML_NONET); if (!$doc) throw new Exception("XML parse error."); $this->xpath = new DOMXPath($doc); $method = $this->xpath->query('/methodCall/methodName')->item(0)->nodeValue; if (!$method) throw new Exception("Method parse error."); $tmpNodes = $this->xpath->query('/methodCall/params/param/value/*')->item(0); $argv = $this->parseData($tmpNodes); $this->xpath = null; return $argv; } private function &parseData(&$node) { switch ($node->nodeName) { case 'string': $value = $node->nodeValue; break; case 'boolean': case 'i4': case 'int': $value = (int)$node->nodeValue; break; case 'double': $value = (double)$node->nodeValue; break; case 'base64': $value = new BinaryData($node->nodeValue, 'base64'); break; case 'dateTime.iso8601': $value = new DateTime($node->nodeValue); break; case 'array': $nodes = $this->xpath->query('data/value/*', $node); $value = array(); for ($i = 0; $i < $nodes->length; $i++) if ($nodes->item($i)->nodeType == XML_ELEMENT_NODE) $value[] = $this->parseData($nodes->item($i)); break; case 'struct': $nodes = $this->xpath->query('member', $node); $value = array(); for ($i = 0; $i < $nodes->length; $i++) if ($nodes->item($i)->nodeType == XML_ELEMENT_NODE) { $name = $this->xpath->query('name', $nodes->item($i))->item(0)->nodeValue; $tmpNodes = $this->xpath->query('value/*', $nodes->item($i))->item(0); $value[$name] = $this->parseData($tmpNodes); } break; default: throw new Exception('Unknow data type.'); } return $value; } public function &call(&$rawData) { try { $argv = $this->parseCall($rawData, $rpcMethod); $method_parts = explode('.', $rpcMethod); foreach ($method_parts as $part) if (!preg_match('/^\w+$/', $part)) throw new Exception('Wrong method name.'); if (count($method_parts) != 3) throw new Exception('Wrong method name.'); $file = $this->classesPath . $method_parts[0] . '.php'; $class = $this->classPrefix . $method_parts[1]; $method = $this->funcPrefix . $method_parts[2]; define('RPC_MODULE', $method_parts[0]); define('RPC_CLASS', $method_parts[1]); define('RPC_METHOD', $method_parts[2]); if (!file_exists($file)) throw new Exception('Class file not found.'); eval("include_once('$file');"); if (!class_exists($class)) throw new Exception('Class not found.'); $methods = get_class_methods($class); if (!in_array($method, $methods)) throw new Exception('Method not found.'); $resp = call_user_func(array($class, $method), $rpcMethod, $argv, $this); } catch (Exception $e) { $resp = array( 'faultCode' => $e->getCode(), 'faultString' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine() ); } return $this->responseToXML($resp); } } class BinaryData { private $data; public function __construct(&$data, $type = 'binary') { switch ($type) { case 'binary': $this->setBinary($data); break; case 'base64': $this->setBase64($data); break; default: throw new Exception('Unknown binary format.'); } } public function setBinary(&$bin) { $this->data = $bin; } public function setBase64(&$b64) { $this->data = base64_decode($b64); } public function getBinary() { return $data; } public function getBase64() { return base64_encode($data); } } ?>