├── 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".'\n\n";
80 | $bindingWSDL.= '\n".'\nserviceName\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" />\n\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 |
--------------------------------------------------------------------------------