在php的开发环境中如何调取WebService?

19 篇文章 1 订阅

一.前言:

大家或多或少都听过 WebService(Web服务),有一段时间很多计算机期刊、书籍和网站都大肆的提及和宣传WebService技术,其中不乏很多吹嘘和做广告的成 分。但是不得不承认的是WebService真的是一门新兴和有前途的技术,那么WebService到底是什么?何时应该用?
  当前的应用程序开发逐步的呈现了两种迥然不同的倾向:一种是基于浏览器的瘦客户端应用程序,一种是基于浏览器的富客户端应用程序(RIA),当然后一种技术相对来说更加的时髦一些(如现在很流行的Html5技术),这里主要讲前者。
  基于浏览器的瘦客户端应用程序并不是 因为瘦客户能够提供更好的用户界面,而是因为它能够避免花在桌面应用程序发布上的高成本。发布桌面应用程序成本很高,一半是因为应用程序安装和配置的问 题,另一半是因为客户和服务器之间通信的问题。传统的Windows富客户应用程序使用DCOM来与服务器进行通信和调用远程对象。配置好DCOM使其在 一个大型的网络中正常工作将是一个极富挑战性的工作,同时也是许多IT工程师的噩梦。事实上,许多IT工程师宁愿忍受浏览器所带来的功能限制,也不愿在局 域网上去运行一个DCOM。关于客户端与服务器的通信问题,一个完美的解决方法是使用HTTP协议来通信。这是因为任何运行Web浏览器的机器都在使用 HTTP协议。同时,当前许多防火墙也配置为只允许HTTP连接。许多商用程序还面临另一个问题,那就是与其他程序的互操作性。如果所有的应用程序都是使 用COM或.NET语言写的,并且都运行在Windows平台上,那就天下太平了。然而,事实上大多数商业数据仍然在大型主机上以非关系文件(VSAM) 的形式存放,并由COBOL语言编写的大型机程序访问。而且,目前还有很多商用程序继续在使用C++、Java、Visual Basic和其他各种各样 的语言编写。现在,除了最简单的程序之外,所有的应用程序都需要与运行在其他异构平台上的应用程序集成并进行数据交换。这样的任务通常都是由特殊的方法, 如文件传输和分析,消息队列,还有仅适用于某些情况的的API,如IBM的高级程序到程序交流(APPC)等来完成的。在以前,没有一个应用程序通信标 准,是独立于平台、组建模型和编程语言的。只有通过Web Service,客户端和服务器才能够自由的用HTTP进行通信,不论两个程序的平台和编程语言是什么。

二、WebService到底是什么

WebService是一种跨编程语言和跨操作系统平台的远程调用技术。
  所谓跨编程语言和跨操作平台,就是说服务端程序采用java编写,客户端程序则可以采用其他编程语言编写,反之亦然!跨操作系统平台则是指服务端程序和客户端程序可以在不同的操作系统上运行。
所谓远程调用,就是一台计算机a上 的一个程序可以调用到另外一台计算机b上的一个对象的方法,譬如,银联提供给商场的pos刷卡系统,商场的POS机转账调用的转账方法的代码其实是跑在银 行服务器上。再比如,amazon,天气预报系统,淘宝网,校内网,百度等把自己的系统服务以webservice服务的形式暴露出来,让第三方网站和程 序可以调用这些服务功能,这样扩展了自己系统的市场占有率,往大的概念上吹,就是所谓的SOA应用。
  其实可以从多个角度来理解 WebService,从表面上看,WebService就是一个应用程序向外界暴露出一个能通过Web进行调用的API,也就是说能用编程的方法通过 Web来调用这个应用程序。我们把调用这个WebService的应用程序叫做客户端,而把提供这个WebService的应用程序叫做服务端。从深层次 看,WebService是建立可互操作的分布式应用程序的新平台,是一个平台,是一套标准。它定义了应用程序如何在Web上实现互操作性,你可以用任何 你喜欢的语言,在任何你喜欢的平台上写Web service ,只要我们可以通过Web service标准对这些服务进行查询和访问。
  WebService平台需要一套协议来实现分布式应用程序的创建。任何平台都有它的数据表示方法和类型系统。要实现互操作性,WebService平台 必须提供一套标准的类型系统,用于沟通不同平台、编程语言和组件模型中的不同类型系统。Web service平台必须提供一种标准来描述 Web service,让客户可以得到足够的信息来调用这个Web service。最后,我们还必须有一种方法来对这个Web service进行远 程调用,这种方法实际是一种远程过程调用协议(RPC)。为了达到互操作性,这种RPC协议还必须与平台和编程语言无关。

三、WebService平台技术

XML+XSD,SOAP和WSDL就是构成WebService平台的三大技术。
3.1、XML+XSD
  WebService采用HTTP协议传输数据,采用XML格式封装数据(即XML中说明调用远程服务对象的哪个方法,传递的参数是什么,以及服务对象的 返回结果是什么)。XML是WebService平台中表示数据的格式。除了易于建立和易于分析外,XML主要的优点在于它既是平台无关的,又是厂商无关 的。无关性是比技术优越性更重要的:软件厂商是不会选择一个由竞争对手所发明的技术的。
  XML解决了数据表示的问题,但它没有定义一套标准的数据类型,更没有说怎么去扩展这套数据类型。例如,整形数到底代表什么?16位,32位,64位?这 些细节对实现互操作性很重要。XML Schema(XSD)就是专门解决这个问题的一套标准。它定义了一套标准的数据类型,并给出了一种语言来扩展这套数据类型。WebService平台就 是用XSD来作为其数据类型系统的。当你用某种语言(如VB.NET或C#)来构造一个Web service时,为了符合WebService标准,所 有你使用的数据类型都必须被转换为XSD类型。你用的工具可能已经自动帮你完成了这个转换,但你很可能会根据你的需要修改一下转换过程。
3.2、SOAP
  WebService通过HTTP协议发送请求和接收结果时,发送的请求内容和结果内容都采用XML格式封装,并增加了一些特定的HTTP消息头,以说明 HTTP消息的内容格式,这些特定的HTTP消息头和XML内容格式就是SOAP协议。SOAP提供了标准的RPC方法来调用Web Service。
  SOAP协议 = HTTP协议 + XML数据格式
  SOAP协议定义了SOAP消息的格式,SOAP协议是基于HTTP协议的,SOAP也是基于XML和XSD的,XML是SOAP的数据编码方式。打个比 喻:HTTP就是普通公路,XML就是中间的绿色隔离带和两边的防护栏,SOAP就是普通公路经过加隔离带和防护栏改造过的高速公路。
3.3、WSDL
  好比我们去商店买东西,首先要知道商店里有什么东西可买,然后再来购买,商家的做法就是张贴广告海报。 WebService也一样,WebService客户端要调用一个WebService服务,首先要有知道这个服务的地址在哪,以及这个服务里有什么方 法可以调用,所以,WebService务器端首先要通过一个WSDL文件来说明自己家里有啥服务可以对外调用,服务是什么(服务中有哪些方法,方法接受 的参数是什么,返回值是什么),服务的网络地址用哪个url地址表示,服务通过什么方式来调用。
  WSDL(Web Services Description Language)就是这样一个基于XML的语言,用于描述Web Service及其函数、参数和返回值。它是WebService客户端和服务器端都 能理解的标准格式。因为是基于XML的,所以WSDL既是机器可阅读的,又是人可阅读的,这将是一个很大的好处。一些最新的开发工具既能根据你的 Web service生成WSDL文档,又能导入WSDL文档,生成调用相应WebService的代理类代码。
  WSDL 文件保存在Web服务器上,通过一个url地址就可以访问到它。客户端要调用一个WebService服务之前,要知道该服务的WSDL文件的地址。 WebService服务提供商可以通过两种方式来暴露它的WSDL文件地址:1.注册到UDDI服务器,以便被人查找;2.直接告诉给客户端调用者。

四、WebService开发

WebService开发可以分为服务器端开发和客户端开发两个方面。
4.1、服务端开发
  把公司内部系统的业务方法发布成WebService服务,供远程合作单位和个人调用。(借助一些WebService框架可以很轻松地把自己的业务对象发布成WebService服务,Java方面的典型WebService框架包括:axis,xfire,cxf 等,java ee服务器通常也支持发布WebService服务,例如JBoss。)
4.2、客户端开发
  调用别人发布的WebService服务,大多数人从事的开发都属于这个方面,例如,调用天气预报WebService服务。(使用厂 商的WSDL2Java之类的工具生成静态调用的代理类代码;使用厂商提供的客户端编程API类;使用SUN公司早期标准的jax-rpc开发包;使用 SUN公司最新标准的jax-ws开发包。当然SUN已被ORACLE收购)
4.3、WebService 的工作调用原理
对客户端而言,我们给这各类WebService客户端API传递wsdl文件的url地址,这些API就会创建出底层的代理类,我调用 这些代理,就可以访问到webservice服务。代理类把客户端的方法调用变成soap格式的请求数据再通过HTTP协议发出去,并把接收到的soap 数据变成返回值返回。对服务端而言,各类WebService框架的本质就是一个大大的Servlet,当远程调用客户端给它通过http协议发送过来 soap格式的请求数据时,它分析这个数据,就知道要调用哪个java类的哪个方法,于是去查找或创建这个对象,并调用其方法,再把方法返回的结果包装成 soap格式的数据,通过http响应消息回给客户端。

五、适用场合

1、跨防火墙通信
   如果应用程序有成千上万的用户,而且分布在世界各地,那么客户端和服务器之间的通信将是一个棘手的问题。因为客户端和服务器之间通常会有防火墙或者代理服 务器。在这种情况下,使用DCOM就不是那么简单,通常也不便于把客户端程序发布到数量如此庞大的每一个用户手中。传统的做法是,选择用浏览器作为客户 端,写下一大堆ASP页面,把应用程序的中间层暴露给最终用户。这样做的结果是开发难度大,程序很难维护。如果中间层组件换成WebService的话, 就可以从用户界面直接调用中间层组件。从大多数人的经验来看,在一个用户界面和中间层有较多交互的应用程序中,使用WebService这种结构,可以节 省花在用户界面编程上20%的开发时间。
2、应用程序集成
  企业级的应用程序开发者都知道,企业里经常都要把用不同语言写成的、在不同平台上运行的各种程序集成起来,而这种集成将花费很大的开发力量。应用程序经常 需要从运行在IBM主机上的程序中获取数据;或者把数据发送到主机或UNIX应用程序中去。即使在同一个平台上,不同软件厂商生产的各种软件也常常需要集 成起来。通过WebService,可以很容易的集成不同结构的应用程序。
3、B2B集成
  用WebService集成应用程序,可以使公司内部的商务处理更加自动化。但当交易跨越供应商和客户、突破公司的界限时会怎么样呢?跨公司的商务交易集成通常叫做B2B集成。WebService是B2B集成成功的关键。通过WebService,公司可以把关键的商务应用“暴露”给指定的供应商和客户。例如,把电子下单系统和电子发票系统“暴露”出来,客户就可以以电子的方式发送订单,供应商则可以以电子的方式发送原料采购发票。当然,这并不是一个 新的概念,EDI(电子文档交换)早就是这样了。但是,WebService的实现要比EDI简单得多,而且WebService运行在Internet 上,在世界任何地方都可轻易实现,其运行成本就相对较低。不过,WebService并不像EDI那样,是文档交换或B2B集成的完整解决方案。 WebService只是B2B集成的一个关键部分,还需要许多其它的部分才能实现集成。
  用WebService来实现B2B集成的最大好处在于可以轻易实现互操作性。只要把商务逻辑“暴露”出来,成为WebService,就可以让任何指定 的合作伙伴调用这些商务逻辑,而不管他们的系统在什么平台上运行,使用什么开发语言。这样就大大减少了花在B2B集成上的时间和成本,让许多原本无法承受 EDI的中小企业也能实现B2B集成。
4、软件和数据重用
软件重用是一个很大的主题,重用的形式很多,重用的程度有大有小。最基本的形式是源代码模块或者类一级的重用,一种形式是二进制形式的组件重用。采用 WebService应用程序可以用标准的方法把功能和数据“暴露”出来,供其它应用程序使用,达到业务级重用。

六、不适用场合

1、单机应用程序
目前,企业和个人还使用着很多桌面应用程序。其中一些只需要与本机上的其它程序通信。在这种情况下,最好就不要用WebService,只要用本地的 API就可以了。COM非常适合于在这种情况下工作,因为它既小又快。运行在同一台服务器上的服务器软件也是这样。最好直接用COM或其它本地的API来 进行应用程序间的调用。当然WebService也能用在这些场合,但那样不仅消耗太大,而且不会带来任何好处。
2、局域网的同构应用程序
在许多应用中,所有的程序都是用VB或VC开发的,都在Windows平台下使用COM,都运行在同一个局域网上。例如,有两个服务器应用程序需要相互通 信,或者有一个Win32或WinForm的客户程序要连接局域网上另一个服务器的程序。在这些程序里,使用DCOM会比SOAP/HTTP有效得多。与 此相类似,如果一个.NET程序要连接到局域网上的另一个.NET程序,应该使用.NETremoting。有趣的是,在.NETremoting 中,也可以指定使用SOAP/HTTP来进行WebService调用。不过最好还是直接通过TCP进行RPC调用,那样会有效得多。

七、实际使用案例

当开发环境为php时候怎么进行使用呢?
开启php服务的配置文件中的两个扩展:

extension=php_openssl.dll
extension=php_soap.dll

在使用之前使用phpinfo()方法输出判断当前的扩展是否已经成功安装。
在这里插入图片描述
wsdl和soap虽然是web service的两大标准,但是两者并没有必然的联系,都可以独立使用。
实现soap接口的两种方式

有wsdl文件的方式

​ 在这里先介绍标准的webservice。 那么如何创建wsdl呢?对于PHP来说这确实是件很不容易的事情,wsdl文件是可以使用脚本生成的。
下面用php中的Thinkphp5框架来举例:
public目录下完整的文件如下:
在这里插入图片描述

在Thinkphp5框架中的public目录下创建SoapDiscovery.php文件,代码如下:

<?php
/**
 * Copyright (c) 2005, Braulio Jos?Solano Rojas
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *     Redistributions of source code must retain the above copyright notice, this list of
 *     conditions and the following disclaimer.
 *     Redistributions in binary form must reproduce the above copyright notice, this list of
 *     conditions and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *     Neither the name of the Solsoft de Costa Rica S.A. nor the names of its contributors may
 *     be used to endorse or promote products derived from this software without specific
 *     prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @version $Id$
 * @copyright 2005
 */

/**
 * SoapDiscovery Class that provides Web Service Definition Language (WSDL).
 *
 * @package SoapDiscovery
 * @author Braulio Jos?Solano Rojas
 * @copyright Copyright (c) 2005 Braulio Jos?Solano Rojas
 * @version $Id$
 * @access public
 * */
class SoapDiscovery
{

    private $class_name = '';
    private $service_name = '';

    /**
     * SoapDiscovery::__construct() SoapDiscovery class Constructor.
     *
     * @param string $class_name
     * @param string $service_name
     * */
    public function __construct($class_name = '', $service_name = '', $path = './')
    {
        $this->class_name = $class_name;
        $this->service_name = $service_name;
        $this->path = $path;
    }

    /**
     * SoapDiscovery::getWSDL() Returns the WSDL of a class if the class is instantiable.
     *
     * @return string
     * */
    public function getWSDL()
    {
        header("Content-type: text/html; charset=utf-8");
        if (empty($this->service_name)) {
            throw new Exception('No service name.');
        }
        $headerWSDL = "<?xml version=\"1.0\" ?>\n";
        $headerWSDL .= "<definitions name=\"$this->service_name\" targetNamespace=\"urn:$this->service_name\" xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" xmlns:tns=\"urn:$this->service_name\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns=\"http://schemas.xmlsoap.org/wsdl/\">\n";
        $headerWSDL .= "<types xmlns=\"http://schemas.xmlsoap.org/wsdl/\" />\n";

        if (empty($this->class_name)) {
            throw new Exception('No class name.');
        }

        $class = new ReflectionClass($this->class_name);

        if (!$class->isInstantiable()) {
            throw new Exception('Class is not instantiable.');
        }

        $methods = $class->getMethods();

        $portTypeWSDL = '<portType name="' . $this->service_name . 'Port">';
        $bindingWSDL = '<binding name="' . $this->service_name . 'Binding" type="tns:' . $this->service_name . "Port\">\n<soap:binding style=\"rpc\" transport=\"http://schemas.xmlsoap.org/soap/http\" />\n";
        $serviceWSDL = '<service name="' . $this->service_name . "\">\n<documentation />\n<port name=\"" . $this->service_name . 'Port" binding="tns:' . $this->service_name . "Binding\"><soap:address location=\"http://" . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['PHP_SELF'] . "\" />\n</port>\n</service>\n";
        $messageWSDL = '';
        foreach ($methods as $method) {
            if ($method->isPublic() && !$method->isConstructor()) {
                $portTypeWSDL .= '<operation name="' . $method->getName() . "\">\n" . '<input message="tns:' . $method->getName() . "Request\" />\n<output message=\"tns:" . $method->getName() . "Response\" />\n</operation>\n";
                $bindingWSDL .= '<operation name="' . $method->getName() . "\">\n" . '<soap:operation soapAction="urn:' . $this->service_name . '#' . $this->class_name . '#' . $method->getName() . "\" />\n<input><soap:body use=\"encoded\" namespace=\"urn:$this->service_name\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" />\n</input>\n<output>\n<soap:body use=\"encoded\" namespace=\"urn:$this->service_name\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" />\n</output>\n</operation>\n";
                $messageWSDL .= '<message name="' . $method->getName() . "Request\">\n";
                $parameters = $method->getParameters();
                foreach ($parameters as $parameter) {
                    $messageWSDL .= '<part name="' . $parameter->getName() . "\" type=\"xsd:string\" />\n";
                }
                $messageWSDL .= "</message>\n";
                $messageWSDL .= '<message name="' . $method->getName() . "Response\">\n";
                $messageWSDL .= '<part name="' . $method->getName() . "\" type=\"xsd:string\" />\n";
                $messageWSDL .= "</message>\n";
            }
        }
        $portTypeWSDL .= "</portType>\n";
        $bindingWSDL .= "</binding>\n";
        //return sprintf('%s%s%s%s%s%s', $headerWSDL, $portTypeWSDL, $bindingWSDL, $serviceWSDL, $messageWSDL, '</definitions>');
        //生成wsdl文件,将上面的return注释
        $this->wsdl_name = preg_replace('/(\\\.*\\\)/', '', $this->class_name);
        $fso = fopen($this->path . $this->wsdl_name . ".wsdl", "w");
        fwrite($fso, sprintf('%s%s%s%s%s%s', $headerWSDL, $portTypeWSDL, $bindingWSDL, $serviceWSDL, $messageWSDL, '</definitions>'));
    }

    /**
     * SoapDiscovery::getDiscovery() Returns discovery of WSDL.
     *
     * @return string
     * */
    public function getDiscovery()
    {
        return "<?xml version=\"1.0\" ?>\n<disco:discovery xmlns:disco=\"http://schemas.xmlsoap.org/disco/\" xmlns:scl=\"http://schemas.xmlsoap.org/disco/scl/\">\n<scl:contractRef ref=\"http://" . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['PHP_SELF'] . "?wsdl\" />\n</disco:discovery>";
    }
}

​ 在当前public目录下创建一个提供服务的Service.php类文件或者函数就可以创建wsdl了。

<?php
class Service
{

    public function HelloWorld()
    {
        return "Hello";
    }

    public function Add($a, $b)
    {
        return $a + $b;
    }

}

//有WSDL文件方式
$server = new SoapServer('Service.wsdl', array('soap_version' => SOAP_1_2)); ##此处的Service.wsdl文件是上面生成的
$server->setClass("Service"); //注册Service类的所有方法
$server->handle(); //处理请求

//无WSDL文件方式
// $server = new SoapServer(null, array('uri' => "abcd"));
// $server->setClass("Service");
// $server->handle();

创建creat_wsdl.php用来生成Service.wsdl

<?php
require "./Service.php";
require "./SoapDiscovery.php";

$disc = new SoapDiscovery('Service', 'soap'); //第一个参数是类名(生成的wsdl文件就是以它来命名的,例如:下面的Service.wsdl),即Service类,第二个参数是服务的名字(这个可以随便写)
$disc->getWSDL();

用web访问的方式运行create_wsdl.php文件,此时会生成一个Service.wsdl的文件,下面将会用到这个文件。
在这里插入图片描述
在这里插入图片描述
在当前public目录下创建客户端调用文件client.php

<?php
ini_set('soap.wsdl_cache_enabled', "0"); //关闭wsdl缓存
$soap = new \SoapClient('http://192.168.1.75:81/Service.php?wsdl');
echo "<pre>";
echo $soap->Add(28, 2);
echo "<pre>";
echo $soap->__soapCall('Add', array(20, 2)); //或这样调用
echo "<pre>";
echo $soap->__Call('Add', array(228, 2));//或这样调用

// try {
//     $soap = new \SoapClient(null, array(
//         "location" => "http://192.168.1.75:81/Service.php?wsdl",
//         "uri" => "abcd", //资源描述符服务器和客户端必须对应
//         "style" => SOAP_RPC,
//         "use" => SOAP_ENCODED,
//     ));
//     echo "<pre>";
//     echo $soap->Add(12, 2);
//     echo "<pre>";
//     echo $soap->__soapCall('Add', array(20, 2)); //或这样调用
//     echo "<pre>";
//     echo $soap->__Call('Add', array(228, 2)); //或这样调用
// } catch (\Throwable $e) {
//     echo print_r($e->getMessage(), true);
// }

直接在web端访问当前的客户端文件。
在这里插入图片描述
方法调用成功。
注意上边Service.php中有/无WSDL文件两种方式,请注意区别。

八、如何调用第三方的Webservice接口

    //腾讯开放的查询QQ号是否在线的普通的soap调用
    public function testWebService()
    {
        $url = 'http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl';
        $client = new \SoapClient($url);
        $params = array(
            "qqCode" => '854231078',
        );
        $result = $client->qqCheckOnline($params);
        print_r($result);
    }

调用接口获取返回结果:
在这里插入图片描述
Y表示当前QQ号在线。
在使用过程中,你可以通过SoapClient对象的__getTypes()和__getFunctions()这两个方法来查看对应WSDL文件中声明的方法名和参数类型定义,这样方便我们构建$params。
如下执行这两个方法,查看QQ开放的这个WSDL文件中定义的类型与方法:

print_r($client->__getTypes());
print_r($client->__getFunctions());

在这里插入图片描述
需要注意的是:string后跟的就是当前接口的参数名称,需要严格按照参数名称和类型进行接口调用。格式为数组。

设置SOAP Headers

在安全性较高的接口中,有时候还需要加入SOAP Header头,用于校验调用方接口的身份权限,这时可以使用SoapHeader类来进行设置,如下参考代码:

$auth = array(
'UserName' => 'USERNAME',
'Password' => 'PASSWORD',
'SystemId' => array('_' => 'DATA', 'Param' => 'PARAM'),
);
$ns = 'http://WebXml.com.cn/'; // 命名空间
$header = new \SoapHeader('NAMESPACE', 'Auth', $auth, false);
$client->__setSoapHeaders($header);

设置HTTP Request Headers
由于SOAP协议本质上其实还是HTTP协议,只是改变了传输过程中的内容为XML形式,而在实际开发过程中,更有些接口对于请求的Http头也做一些校验限制,因此需要设置Http的请求头以适应需求。默认的SoapClient类的构造方法,其实包含有两个参数,其中就可以设置HTTP的请求头。
官方文档:https://www.php.net/manual/en/class.soapclient.php

$url = 'http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl';
$mode = array (
    //'soap_version' => SOAP_1_2,  // SOAP版本,默认为1.0
    'keep_alive'    => true,
    'trace'         => true,   // 跟踪异常信息,对下面的exceptions属性成对
    'encoding'      =>'UTF-8',
    'compression'   => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP | SOAP_COMPRESSION_DEFLATE,  // 压缩
    'exceptions'    => true,   //  显示异常
    'cache_wsdl'    => WSDL_CACHE_NONE,  // 是否缓存WSDL定义文件,这里设置不使用,接口稳定的情况下建议开启缓存加速
    //'proxy_host'     => "192.168.2.60",  // 代理IP
    //'proxy_port'     => 8888,   // 代理端口
    'stream_context' => stream_context_create(array(
        'http' => array('header' => 'Content-Encoding: gzip'),  // HTTP请求头设置,多个使用'\r\n'分割
        'ssl' => array(     // 设置不验证HTTPS证书
            'verify_peer' => false,
            'verify_peer_name' => false
        ))
    )
);
$client = new \SoapClient($url, $mode);

再看一个例子,公共天气预报webservice

    //公共天气预报webservice
    public function testWebService()
    {
        header("content-type:text/html;charset=utf-8");
        //指定WebService路径并初始化一个WebService客户端
        $ws = "http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL"; //webservice服务的地址
        $client = new \SoapClient($ws);

        //获取SoapClient对象引用的服务所提供的所有方法
        echo "<pre>";
        echo ("SOAP服务器提供的开放函数:");
        echo "<pre>";
        var_dump($client->__getFunctions()); //获取服务器上提供的方法
        echo "<pre>";
        echo ("SOAP服务器提供的Type:");
        echo "<pre>";
        var_dump($client->__getTypes()); //获取服务器上数据类型
        echo "<pre>";
        echo ("执行GetGUIDNode的结果:");
        echo "<pre>";
        $data = array(
            'theCityCode' => '郑州',
        );
        $result = $client->getWeather($data); //查询中国郑州的天气,返回的是一个结构体
        print_r($result); //显示结果
    }

查看返回结果:

SOAP服务器提供的开放函数:
array(12) {
  [0]=>
  string(71) "getRegionDatasetResponse getRegionDataset(getRegionDataset $parameters)"
  [1]=>
  string(74) "getRegionProvinceResponse getRegionProvince(getRegionProvince $parameters)"
  [2]=>
  string(71) "getRegionCountryResponse getRegionCountry(getRegionCountry $parameters)"
  [3]=>
  string(86) "getSupportCityDatasetResponse getSupportCityDataset(getSupportCityDataset $parameters)"
  [4]=>
  string(83) "getSupportCityStringResponse getSupportCityString(getSupportCityString $parameters)"
  [5]=>
  string(53) "getWeatherResponse getWeather(getWeather $parameters)"
  [6]=>
  string(71) "getRegionDatasetResponse getRegionDataset(getRegionDataset $parameters)"
  [7]=>
  string(74) "getRegionProvinceResponse getRegionProvince(getRegionProvince $parameters)"
  [8]=>
  string(71) "getRegionCountryResponse getRegionCountry(getRegionCountry $parameters)"
  [9]=>
  string(86) "getSupportCityDatasetResponse getSupportCityDataset(getSupportCityDataset $parameters)"
  [10]=>
  string(83) "getSupportCityStringResponse getSupportCityString(getSupportCityString $parameters)"
  [11]=>
  string(53) "getWeatherResponse getWeather(getWeather $parameters)"
}
SOAP服务器提供的Type:
array(16) {
  [0]=>
  string(27) "struct getRegionDataset {
}"
  [1]=>
  string(83) "struct getRegionDatasetResponse {
 getRegionDatasetResult getRegionDatasetResult;
}"
  [2]=>
  string(66) "struct getRegionDatasetResult {
  schema;
  any;
}"
  [3]=>
  string(28) "struct getRegionProvince {
}"
  [4]=>
  string(76) "struct getRegionProvinceResponse {
 ArrayOfString getRegionProvinceResult;
}"
  [5]=>
  string(40) "struct ArrayOfString {
 string string;
}"
  [6]=>
  string(27) "struct getRegionCountry {
}"
  [7]=>
  string(74) "struct getRegionCountryResponse {
 ArrayOfString getRegionCountryResult;
}"
  [8]=>
  string(55) "struct getSupportCityDataset {
 string theRegionCode;
}"
  [9]=>
  string(98) "struct getSupportCityDatasetResponse {
 getSupportCityDatasetResult getSupportCityDatasetResult;
}"
  [10]=>
  string(71) "struct getSupportCityDatasetResult {
  schema;
  any;
}"
  [11]=>
  string(54) "struct getSupportCityString {
 string theRegionCode;
}"
  [12]=>
  string(82) "struct getSupportCityStringResponse {
 ArrayOfString getSupportCityStringResult;
}"
  [13]=>
  string(61) "struct getWeather {
 string theCityCode;
 string theUserID;
}"
  [14]=>
  string(62) "struct getWeatherResponse {
 ArrayOfString getWeatherResult;
}"
  [15]=>
  string(51) "struct DataSet {
  schema;
  any;
}"
}
执行GetGUIDNode的结果:
stdClass Object
(
    [getWeatherResult] => stdClass Object
        (
            [string] => Array
                (
                    [0] => 河南 郑州
                    [1] => 郑州
                    [2] => 1384
                    [3] => 2022/05/17 16:02:29
                    [4] => 今日天气实况:气温:33℃;风向/风力:西风 2级;湿度:9%
                    [5] => 紫外线强度:很强。
                    [6] => 感冒指数:少发,感冒机率较低,避免长期处于空调屋中。
运动指数:较适宜,请适当降低运动强度并注意户外防风。
过敏指数:较易发,外出需远离过敏源,适当采取防护措施。
穿衣指数:炎热,建议穿短衫、短裤等清凉夏季服装。
洗车指数:较不宜,风力较大,洗车后会蒙上灰尘。
紫外线指数:很强,涂擦SPF20以上,PA++护肤品,避强光。

                    [7] => 517日 晴转多云
                    [8] => 17/32[9] => 西风4-5级转西南风3-4[10] => 0.gif
                    [11] => 1.gif
                    [12] => 518日 多云
                    [13] => 17/31[14] => 南风3-4[15] => 1.gif
                    [16] => 1.gif
                    [17] => 519日 多云
                    [18] => 17/28[19] => 南风3-4级转小于3[20] => 1.gif
                    [21] => 1.gif
                    [22] => 520日 多云转晴
                    [23] => 17/29[24] => 东南风转西南风小于3[25] => 1.gif
                    [26] => 0.gif
                    [27] => 521日 多云
                    [28] => 18/32[29] => 东南风转南风小于3[30] => 1.gif
                    [31] => 1.gif
                )

        )

)

需要注意的是接口的参数格式都为数组。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值