├── xml └── .gitkeep ├── .gitignore ├── wsdl └── PersonService.php ├── non-wsdl └── PersonService.php ├── class └── Person.class.php ├── test.php ├── README.md ├── Client.php └── Service.php /xml/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | !.gitkeep 2 | xml/* 3 | -------------------------------------------------------------------------------- /wsdl/PersonService.php: -------------------------------------------------------------------------------- 1 | www.aboutc.net 5 | */ 6 | 7 | include '../Service.php'; 8 | 9 | $className = rtrim(pathinfo(__FILE__, PATHINFO_FILENAME), 'Service'); 10 | 11 | $mode = pathinfo(__DIR__, PATHINFO_FILENAME); 12 | 13 | $service = new Service($mode, $className); 14 | $service->run(); 15 | -------------------------------------------------------------------------------- /non-wsdl/PersonService.php: -------------------------------------------------------------------------------- 1 | www.aboutc.net 5 | */ 6 | 7 | include '../Service.php'; 8 | 9 | $className = rtrim(pathinfo(__FILE__, PATHINFO_FILENAME), 'Service'); 10 | 11 | $mode = pathinfo(__DIR__, PATHINFO_FILENAME); 12 | 13 | $service = new Service($mode, $className); 14 | $service->run(); 15 | -------------------------------------------------------------------------------- /class/Person.class.php: -------------------------------------------------------------------------------- 1 | www.aboutc.net 5 | */ 6 | 7 | class Person { 8 | private $name = ''; 9 | 10 | /** 11 | * __construct 12 | * @param string $name 13 | */ 14 | public function __construct($name = 'ueaner') { 15 | $this->name = $name; 16 | } 17 | 18 | /** 19 | * say something 20 | * @param string $name 21 | * @return string 22 | */ 23 | public function say($name = '') { 24 | $name = $name ? $name : $this->name; 25 | return "My name is $name."; 26 | } 27 | 28 | /** 29 | * _SERVER 30 | * @return array 31 | */ 32 | public function serverVar() { 33 | return $_SERVER; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test.php: -------------------------------------------------------------------------------- 1 | www.aboutc.net 5 | */ 6 | 7 | include 'Client.php'; 8 | 9 | $mode = 'wsdl'; // or non-wsdl 10 | 11 | $params = array( 12 | 'serverIP' => '127.0.0.1', 13 | 'serverPort' => '80', 14 | 'mode' => $mode, 15 | 'serviceName' => 'Person' 16 | ); 17 | 18 | $clientClass = new Client($params); 19 | $client = $clientClass->getClient(); 20 | 21 | try { 22 | // say 为 server 端 Person.class.php 中的函数 23 | $result = $client->__soapCall('say', array('aboutc')); 24 | // 或 25 | $result2 = $client->serverVar(); 26 | var_dump($result, $result2); 27 | } 28 | catch (SoapFault $fault){ 29 | echo 'Error Message: ' . $fault->getMessage(); 30 | } 31 | 32 | // 注: 33 | // 如果对方使用的 .NET,尝试使用以下两种形式: 34 | // 例如你需要传类似这样的参数: 35 | // $params['email'] = 'test@aboutc.net'; 36 | // $params['name'] = 'ueaner'; 37 | // $client->__soapCall('someFunction', array('param' => $params)); 38 | // $client->__soapCall('someFunction', array($params)); 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP SOAP 实例 2 | ==== 3 | 4 | 放在WEB服务的任意位置,访问:http://localhost/[path-to-soap]/test.php 5 | 6 | 7 | #### 简介 8 | 9 | 通常我们的应用服务需要在不同的平台进行交互操作的时候,会使用 [WEB服务]. 10 | 11 | 常用的WEB服务有以下三种: 12 | 13 | * [SOAP](简单对象访问协议): 支持多种协议(http/https/smtp等),W3C专门定义的一些标准 14 | * [XML-RPC](远程过程调用): 只支持http协议,没有标准 15 | * [REST](表征状态转移): 只支持http协议,是一种针对于资源理解的URI设计风格而没有标准, 16 | 加上 [OAuth](开放授权)会让你的WEB服务(或开放平台)看上去更加简洁和简单,之后的文章会详细介绍。 17 | 18 | 本篇文章重点:SOAP 简单对象访问协议(Simple Object Access Protocol)。 19 | 20 | #### PHP SOAP 21 | 22 | __模式__: 23 | 24 | SOAP 分为 WSDL 和 non-WSDL 模式,可以简单理解为:WSDL 模式对外提供 WSDL 定义文件, 25 | 而 non-WSDL 模式不对外提供 WSDL 定义文件(会有人给你发一个接口文档的)。 26 | 27 | __依赖__: 28 | 29 | `php-soap` 扩展,如果不存在此扩展,安装: 30 | 31 | # yum install php-soap 32 | 33 | 或编译 PHP:`--enable-soap`。 34 | 35 | 或使用:[nusoap] 包。 36 | 37 | __实例__: 38 | 39 | 本文使用 `php-soap` 扩展,做了一个例子,源码地址为:[https://github.com/ueaner/soap],目录结构说明: 40 | 41 | $ tar xf soap.tar.bz2 42 | 43 | $ tree -C soap 44 | soap 45 | |-- class # 提供服务的类目录 46 | |-- Person.class.php # 提供服务的类文件 47 | |-- Client.php # 客户端类 48 | |-- non-wsdl # non-WSDL 模式:提供服务的目录 49 | |-- PersonService.php # non-WSDL 模式:提供服务的文件 50 | |-- readme.txt # readme 51 | |-- Service.php # 服务端类 52 | |-- test.php # 测试文件 53 | |-- wsdl # WSDL 模式:提供服务的目录 54 | |-- PersonService.php # WSDL 模式:提供服务的文件 55 | |-- xml # WSDL 模式:生成的 WSDL xml 的目录 56 | |-- Person.wsdl # WSDL 模式:生成的 WSDL xml 的文件 57 | 58 | 4 directories, 8 files 59 | 60 | `Client.php` 和 `Service.php` 均实现了 WSDL 和 non-WSDL 两种模式。 61 | 62 | WSDL 模式 和 non-WSDL 模式对照表: 63 | 64 | WSDL 模式 non-WSDL 模式 65 | SoapServer 66 | 参数1 SomeService.php?wsdl null 67 | 参数2 uri 可有,可无 uri 68 | SoapClient 69 | 参数1 SomeService.php?wsdl null 70 | 参数2 uri 可有,可无 uri + location 71 | 72 | 这里的 `SomeService.php?wsdl` 类似 `http://127.0.0.1:80/soap/wsdl/PersonService.php?wsdl` 这样的地址(有 `?wsdl`), 73 | `location` 是类似 `http://127.0.0.1:80/soap/wsdl/PersonService.php` 这样的地址(无 `?wsdl`)。 74 | `uri` 一般为你的根域名,如 `http://localhost`,或与 `location` 参数定义相同都可。 75 | 76 | 另外 WSDL 模式对外提供 WSDL 定义的 xml 文件,所以在以 GET 方式访问 http://127.0.0.1:80/soap/wsdl/PersonService.php?wsdl 77 | 地址时会输出相应的 xml 文件,对接口对象或函数进行说明。 78 | 79 | 80 | [WEB服务]: http://zh.wikipedia.org/wiki/Web服务 81 | [SOAP]: http://zh.wikipedia.org/wiki/SOAP 82 | [XML-RPC]: http://zh.wikipedia.org/wiki/XML-RPC 83 | [REST]: http://zh.wikipedia.org/wiki/REST 84 | [OAuth]: http://zh.wikipedia.org/wiki/OAuth 85 | [nusoap]: http://sourceforge.net/projects/nusoap/ 86 | -------------------------------------------------------------------------------- /Client.php: -------------------------------------------------------------------------------- 1 | www.aboutc.net 5 | */ 6 | 7 | class Client { 8 | 9 | private $mode = 'wsdl'; 10 | 11 | private $trace = true; // 开启调试 12 | 13 | private $soapVersion = SOAP_1_2; // SOAP 版本 14 | 15 | private $encoding = 'UTF-8'; // 编码 16 | 17 | private $compression = 0; 18 | 19 | private $options = array(); 20 | 21 | private $serverIP = '127.0.0.1'; 22 | 23 | private $serverPort = '80'; 24 | 25 | private $serverDir = 'soap'; 26 | 27 | private $serviceUri = ''; 28 | 29 | private $serviceName = ''; 30 | 31 | private $wsdlCacheEnabled = 0; // WSDL 缓存:1开启,0关闭 32 | 33 | public function __construct($params = array()) { 34 | if (count($params) > 0) { 35 | foreach ($params as $key => $val) { 36 | if (isset($this->$key)) { 37 | $this->$key = $val; 38 | } 39 | } 40 | } 41 | 42 | $this->options = array( 43 | 'trace' => $this->trace, 44 | 'soap_version' => $this->soapVersion, 45 | 'encoding' => $this->encoding, 46 | 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP 47 | ); 48 | 49 | if (!$this->wsdlCacheEnabled) { 50 | $this->options['cache_wsdl'] = WSDL_CACHE_NONE; 51 | } 52 | 53 | if ($this->serviceUri == '') { 54 | // $this->serverDir = '/' . trim('/' . $this->serverDir . '/', '/') . '/'; 55 | $this->serverDir = '/' . pathinfo(dirname($_SERVER['SCRIPT_NAME']), PATHINFO_FILENAME) . '/'; 56 | $this->serviceUri = 'http://' . $this->serverIP . ':' . $this->serverPort . $this->serverDir 57 | . $this->mode. '/' . $this->serviceName . 'Service.php'; 58 | } 59 | 60 | if ($this->mode == 'wsdl') { 61 | $this->serviceUri .= '?wsdl'; 62 | } else { 63 | $this->options['uri'] = 'http://' . $_SERVER['SERVER_NAME']; // non-WSDL 模式参数 64 | $this->options['location'] = $this->serviceUri; // non-WSDL 模式参数,server 端具体路径 65 | $this->serviceUri = null; 66 | } 67 | 68 | } 69 | 70 | public function getClient() { 71 | // try { 72 | // return new SoapClient($this->serviceUri, $this->options); 73 | // } 74 | // catch (SoapFault $fault){ 75 | // echo 'Error Message: ' . $fault->getMessage(); 76 | // } 77 | 78 | // var_dump($this->serviceUri, $this->options);exit; 79 | 80 | return new SoapClient($this->serviceUri, $this->options); 81 | } 82 | 83 | public function getServiceUri() { 84 | return $this->serviceUri; 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /Service.php: -------------------------------------------------------------------------------- 1 | www.aboutc.net 5 | */ 6 | 7 | class Service { 8 | 9 | private $className = ''; 10 | 11 | private $classPath = '../class/'; 12 | 13 | private $classFileSuffix = '.class.php'; 14 | 15 | private $serviceName = ''; 16 | 17 | private $serviceNamePrefix = 'ABOUTC_'; 18 | 19 | private $wsdlCacheEnabled = 0; // WSDL 缓存:1开启,0关闭 20 | 21 | private $xmlPath = '../xml/'; 22 | 23 | private $mode = 'wsdl'; 24 | 25 | /** 26 | * 初始化函数 27 | * @param string $mode 28 | * @param string $className 29 | * @param string $serviceName 30 | */ 31 | public function __construct($mode = '', $className = '', $serviceName = '') { 32 | $this->mode = $mode; 33 | $this->className = $className; 34 | $this->serviceName = $serviceName ? $serviceName : $this->serviceNamePrefix . $className; 35 | } 36 | 37 | /** 38 | * 获取 WSDL xml 文件内容 39 | * @return string 40 | * @throws Exception 41 | */ 42 | public function getWSDL() { 43 | if (empty($this->serviceName)) { 44 | throw new Exception('No service name.'); 45 | } 46 | 47 | if (is_file($this->xmlPath . $this->className . '.wsdl')) { 48 | return file_get_contents($this->xmlPath . $this->className . '.wsdl'); 49 | } else { 50 | /** 51 | * SoapDiscovery Class that provides Web Service Definition Language (WSDL). 52 | * 53 | * @package SoapDiscovery 54 | * @author Braulio José Solano Rojas 55 | * @link http://www.phpclasses.org/browse/file/9476.html 56 | */ 57 | $headerWSDL = "\n"; 58 | $headerWSDL.= "serviceName\" targetNamespace=\"urn:$this->serviceName\" xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" xmlns:tns=\"urn:$this->serviceName\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns=\"http://schemas.xmlsoap.org/wsdl/\">\n"; 59 | $headerWSDL.= "\n"; 60 | 61 | if (empty($this->className)) { 62 | throw new Exception('No class name.'); 63 | } 64 | 65 | $class = new ReflectionClass($this->className); 66 | 67 | if (!$class->isInstantiable()) { 68 | throw new Exception('Class is not instantiable.'); 69 | } 70 | 71 | $methods = $class->getMethods(); 72 | 73 | $portTypeWSDL = "\n\n".''; 74 | $bindingWSDL = "\n\n".'\n\n"; 75 | $serviceWSDL = "\n\n".'\n\nserviceName.'Port" binding="tns:'.$this->serviceName."Binding\">\n\n\n"; 76 | $messageWSDL = "\n\n"; 77 | foreach ($methods as $method) { 78 | if ($method->isPublic() && !$method->isConstructor()) { 79 | $portTypeWSDL.= '\n".'\ngetName()."Response\" />\n\n"; 80 | $bindingWSDL.= '\n".'\nserviceName\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" />\n\n\nserviceName\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" />\n\n\n"; 81 | $messageWSDL.= "\n".'\n"; 82 | $parameters = $method->getParameters(); 83 | foreach ($parameters as $parameter) { 84 | if ($method->getDocComment()) { 85 | $pattern = '/@param\s+(string|boolean|int|integer|float|double)/i'; 86 | preg_match($pattern, $method->getDocComment(), $matches); 87 | $type = $matches[1]; 88 | } 89 | else { 90 | $type = 'string'; 91 | } 92 | $messageWSDL.= '\n"; 93 | } 94 | $messageWSDL.= "\n"; 95 | if ($method->getDocComment()) { 96 | $pattern = '/@return\s+(string|boolean|int|integer|float|double)/i'; 97 | preg_match($pattern, $method->getDocComment(), $matches); 98 | $return = isset($matches[1]) ? $matches[1] : ''; 99 | } 100 | else { 101 | $return = 'string'; 102 | } 103 | $messageWSDL.= "\n".'\n"; 104 | $messageWSDL.= '\n"; 105 | $messageWSDL.= "\n"; 106 | } 107 | } 108 | $portTypeWSDL.= "\n"; 109 | $bindingWSDL.= "\n"; 110 | $wsdl = sprintf('%s%s%s%s%s%s', $headerWSDL, $messageWSDL, $portTypeWSDL, $bindingWSDL, $serviceWSDL, ''); 111 | file_put_contents($this->xmlPath . $this->className . '.wsdl', $wsdl); 112 | return $wsdl; 113 | } 114 | } 115 | 116 | /** 117 | * 获取 wsdl 引导地址(xml格式) 118 | * @return string 119 | */ 120 | public function getDiscovery() { 121 | $wsdlRef = 'http://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['SCRIPT_NAME'] . '?wsdl'; 122 | return '' 123 | . '' 124 | . ' ' 125 | . ''; 126 | } 127 | 128 | /** 129 | * 运行服务 130 | * @throws Exception 131 | */ 132 | public function run() { 133 | if (!is_readable($this->classPath . $this->className . $this->classFileSuffix)) { 134 | throw new Exception('No class name.'); 135 | } 136 | 137 | require_once $this->classPath . $this->className . $this->classFileSuffix; 138 | // WSDL 缓存 139 | ini_set('soap.wsdl_cache_enabled', $this->wsdlCacheEnabled); 140 | 141 | // 创建 WSDL 服务 142 | if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') { 143 | $this->getServer(); 144 | } elseif ($this->mode == 'wsdl') { 145 | // 查看 WSDL xml,删除以下程序就相当于 non-WSDL 模式 146 | header('Content-type: text/xml'); 147 | if (isset($_SERVER['QUERY_STRING']) && strcasecmp($_SERVER['QUERY_STRING'], 'wsdl') == 0) { 148 | echo $this->getWSDL(); 149 | } else { 150 | echo $this->getDiscovery(); 151 | } 152 | } else { 153 | echo 'No wsdl xml file'; 154 | } 155 | } 156 | 157 | /** 158 | * 获取服务 159 | */ 160 | public function getServer() { 161 | $options['uri'] = 'http://'.$_SERVER['SERVER_NAME']; 162 | if ($this->mode == 'wsdl') { 163 | $wsdl = 'http://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['SCRIPT_NAME'] . '?wsdl'; 164 | // WSDL 模式不用传 uri 参数,但传了也不会有问题 165 | } else { 166 | $wsdl = null; 167 | } 168 | $server = new SoapServer($wsdl, $options); 169 | $server->setClass($this->className); 170 | $server->handle(); 171 | } 172 | 173 | } 174 | --------------------------------------------------------------------------------