Как сделать вызов PHP SOAP с использованием класса SoapClient

130

Я привык писать PHP-код, но не часто использую объектно-ориентированное кодирование. Теперь мне нужно взаимодействовать с SOAP (как клиент), и я не могу правильно понять синтаксис. У меня есть файл WSDL, который позволяет мне правильно настроить новое соединение с помощью класса SoapClient. Однако я не могу сделать правильный вызов и вернуть данные. Мне нужно отправить следующие (упрощенные) данные:

  • Контактный ID
  • Контактное лицо
  • Общее описание
  • Количество

В документе WSDL определены две функции, но мне нужна только одна («FirstFunction» ниже). Вот сценарий, который я запускаю, чтобы получить информацию о доступных функциях и типах:

$client = new SoapClient("http://example.com/webservices?wsdl");
var_dump($client->__getFunctions()); 
var_dump($client->__getTypes()); 

И вот результат, который он генерирует:

array(
  [0] => "FirstFunction Function1(FirstFunction $parameters)",
  [1] => "SecondFunction Function2(SecondFunction $parameters)",
);

array(
  [0] => struct Contact {
    id id;
    name name;
  }
  [1] => string "string description"
  [2] => string "int amount"
}

Скажем, я хочу позвонить в FirstFunction с данными:

  • Контактный ID: 100
  • Контактное лицо: Джон
  • Общее описание: бочка с маслом
  • Количество: 500

Какой будет правильный синтаксис? Я пробовал всевозможные варианты, но оказалось, что структура мыла довольно гибкая, поэтому есть очень много способов сделать это. По мануалу тоже не понял ...


ОБНОВЛЕНИЕ 1: пробовал образец из ММК:

$client = new SoapClient("http://example.com/webservices?wsdl");

$params = array(
  "id" => 100,
  "name" => "John",
  "description" => "Barrel of Oil",
  "amount" => 500,
);
$response = $client->__soapCall("Function1", array($params));

Но я получаю такой ответ: Object has no 'Contact' property. Как вы можете видеть в выводе getTypes(), есть structвызываемыйContact , поэтому я думаю, мне нужно как-то прояснить, что мои параметры включают данные контакта, но вопрос в том, как?

ОБНОВЛЕНИЕ 2: я также пробовал эти структуры, такая же ошибка.

$params = array(
  array(
    "id" => 100,
    "name" => "John",
  ),
  "Barrel of Oil",
  500,
);

Так же как:

$params = array(
  "Contact" => array(
    "id" => 100,
    "name" => "John",
  ),
  "description" => "Barrel of Oil",
  "amount" => 500,
);

Ошибка в обоих случаях: объект не имеет свойства «Контакт».


источник

Ответы:

178

Это то, что вам нужно сделать.

Я попытался воссоздать ситуацию ...


  • В этом примере я создал .NET-образец WebService (WS) с WebMethodвызываемым Function1объектом, ожидающим следующих параметров:

Функция1 (Контактный контакт, строковое описание, целое количество)

  • Где Contactпросто модель, у которой есть геттеры и сеттеры для idи nameкак в вашем случае.

  • Вы можете загрузить образец WS для .NET по адресу:

https://www.dropbox.com/s/6pz1w94a52o5xah/11593623.zip


Код.

Вот что вам нужно сделать на стороне PHP:

(Проверено и работает)

<?php
// Create Contact class
class Contact {
    public function __construct($id, $name) 
    {
        $this->id = $id;
        $this->name = $name;
    }
}

// Initialize WS with the WSDL
$client = new SoapClient("http://localhost:10139/Service1.asmx?wsdl");

// Create Contact obj
$contact = new Contact(100, "John");

// Set request params
$params = array(
  "Contact" => $contact,
  "description" => "Barrel of Oil",
  "amount" => 500,
);

// Invoke WS method (Function1) with the request params 
$response = $client->__soapCall("Function1", array($params));

// Print WS response
var_dump($response);

?>

Тестирование всего.

  • Если вы это сделаете, print_r($params)вы увидите следующий результат, как и ожидал ваш WS:

Массив ([Контакт] => Контактный объект ([id] => 100 [name] => Джон) [описание] => Бочка с маслом [количество] => 500)

  • Когда я отлаживал образец WS для .NET, я получил следующее:

введите описание изображения здесь

(Как видите, ни Contactобъекта, nullни других параметров нет. Это означает, что ваш запрос был успешно выполнен со стороны PHP)

  • Ответ от .NET-образца WS был ожидаемым, и вот что я получил на стороне PHP:

object (stdClass) [3] public 'Function1Result' => string 'Подробная информация по вашему запросу! id: 100, имя: John, описание: Barrel of Oil, количество: 500 футов (длина = 98)


Удачного кодирования!

Оскар Хара
источник
3
Отлично! Я действовал так, как будто знаю о службах SOAP немного больше, чем на самом деле, и это привело меня туда, где мне нужно быть.
Кэмерон Чепмен
1
Я не задавал вопроса, иначе задал бы. Однако вопрос и этот ответ получил от меня положительную оценку.
Кэмерон Чепмен,
4
@user должен принять это :) Кстати, очень хороший ответ, полный и очень ясный. +1
Yann39
Спасибо за это! Lil 'Boost для понимания структуры SOAP.
EatCodePlaySleep
69

Вы также можете использовать сервисы SOAP таким образом:

<?php 
//Create the client object
$soapclient = new SoapClient('http://www.webservicex.net/globalweather.asmx?WSDL');

//Use the functions of the client, the params of the function are in 
//the associative array
$params = array('CountryName' => 'Spain', 'CityName' => 'Alicante');
$response = $soapclient->getWeather($params);

var_dump($response);

// Get the Cities By Country
$param = array('CountryName' => 'Spain');
$response = $soapclient->getCitiesByCountry($param);

var_dump($response);

Это пример реального сервиса, и он работает.

Надеюсь это поможет.

Сальвадор П.
источник
Я получаю следующую ошибку: object (stdClass) # 70 (1) {["GetWeatherResult"] => string (14) "Data Not Found"} Есть идеи?
Ilker Baltaci
Кажется, они изменили струны городов. Я только что обновил пример, добавив еще один вызов другой службы, которую они предоставляют, и она работает. Я попытался использовать строки, которые они возвращают как города, но, похоже, не работает нормально, в любом случае функция getCitiesByCountry () служит примером того, как сделать вызов.
Сальвадор П.
30

Сначала инициализируйте веб-службы:

$client = new SoapClient("http://example.com/webservices?wsdl");

Затем установите и передайте параметры:

$params = array (
    "arg0" => $contactid,
    "arg1" => $desc,
    "arg2" => $contactname
);

$response = $client->__soapCall('methodname', array($params));

Обратите внимание, что имя метода доступно в WSDL как имя операции, например:

<operation name="methodname">
ММК
источник
Спасибо! Я пробовал это, но получаю сообщение об ошибке «У объекта нет свойства« Контакт »». Я обновлю свой вопрос с полной информацией. Любые идеи?
@ user16441 Можете ли вы опубликовать WSDL и схему службы? Обычно я начинаю с выяснения того, какой XML ожидает служба, а затем с помощью WireShark выясняю, что на самом деле отправляет мой клиент.
davidfmatheson
21

Я не знаю, почему мой веб-сервис имеет ту же структуру, что и вы, но ему не нужен параметр Class, а просто массив.

Например: - Мой WSDL:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:ns="http://www.kiala.com/schemas/psws/1.0">
    <soapenv:Header/>
    <soapenv:Body>
        <ns:createOrder reference="260778">
            <identification>
                <sender>5390a7006cee11e0ae3e0800200c9a66</sender>
                <hash>831f8c1ad25e1dc89cf2d8f23d2af...fa85155f5c67627</hash>
                <originator>VITS-STAELENS</originator>
            </identification>
            <delivery>
                <from country="ES" node=””/>
                <to country="ES" node="0299"/>
            </delivery>
            <parcel>
                <description>Zoethout thee</description>
                <weight>0.100</weight>
                <orderNumber>10K24</orderNumber>
                <orderDate>2012-12-31</orderDate>
            </parcel>
            <receiver>
                <firstName>Gladys</firstName>
                <surname>Roldan de Moras</surname>
                <address>
                    <line1>Calle General Oraá 26</line1>
                    <line2>(4º izda)</line2>
                    <postalCode>28006</postalCode>
                    <city>Madrid</city>
                    <country>ES</country>
                </address>
                <email>gverbruggen@kiala.com</email>
                <language>es</language>
            </receiver>
        </ns:createOrder>
    </soapenv:Body>
</soapenv:Envelope>

Я var_dump:

var_dump($client->getFunctions());
var_dump($client->getTypes());

Вот результат:

array
  0 => string 'OrderConfirmation createOrder(OrderRequest $createOrder)' (length=56)

array
  0 => string 'struct OrderRequest {
 Identification identification;
 Delivery delivery;
 Parcel parcel;
 Receiver receiver;
 string reference;
}' (length=130)
  1 => string 'struct Identification {
 string sender;
 string hash;
 string originator;
}' (length=75)
  2 => string 'struct Delivery {
 Node from;
 Node to;
}' (length=41)
  3 => string 'struct Node {
 string country;
 string node;
}' (length=46)
  4 => string 'struct Parcel {
 string description;
 decimal weight;
 string orderNumber;
 date orderDate;
}' (length=93)
  5 => string 'struct Receiver {
 string firstName;
 string surname;
 Address address;
 string email;
 string language;
}' (length=106)
  6 => string 'struct Address {
 string line1;
 string line2;
 string postalCode;
 string city;
 string country;
}' (length=99)
  7 => string 'struct OrderConfirmation {
 string trackingNumber;
 string reference;
}' (length=71)
  8 => string 'struct OrderServiceException {
 string code;
 OrderServiceException faultInfo;
 string message;
}' (length=97)

Итак, в моем коде:

    $client  = new SoapClient('http://packandship-ws.kiala.com/psws/order?wsdl');

    $params = array(
        'reference' => $orderId,
        'identification' => array(
            'sender' => param('kiala', 'sender_id'),
            'hash' => hash('sha512', $orderId . param('kiala', 'sender_id') . param('kiala', 'password')),
            'originator' => null,
        ),
        'delivery' => array(
            'from' => array(
                'country' => 'es',
                'node' => '',
            ),
            'to' => array(
                'country' => 'es',
                'node' => '0299'
            ),
        ),
        'parcel' => array(
            'description' => 'Description',
            'weight' => 0.200,
            'orderNumber' => $orderId,
            'orderDate' => date('Y-m-d')
        ),
        'receiver' => array(
            'firstName' => 'Customer First Name',
            'surname' => 'Customer Sur Name',
            'address' => array(
                'line1' => 'Line 1 Adress',
                'line2' => 'Line 2 Adress',
                'postalCode' => 28006,
                'city' => 'Madrid',
                'country' => 'es',
                ),
            'email' => 'test.ceres@yahoo.com',
            'language' => 'es'
        )
    );
    $result = $client->createOrder($params);
    var_dump($result);

но это успешно!

Tín Phạm
источник
1
Ваш пример более полезен, потому что он показывает структурные зависимости
vladkras
3

прочитайте это;-

http://php.net/manual/en/soapclient.call.php

Или

Это хороший пример для функции SOAP «__call». Однако это устарело.

<?php
    $wsdl = "http://webservices.tekever.eu/ctt/?wsdl";
    $int_zona = 5;
    $int_peso = 1001;
    $cliente = new SoapClient($wsdl);
    print "<p>Envio Internacional: ";
    $vem = $cliente->__call('CustoEMSInternacional',array($int_zona, $int_peso));
    print $vem;
    print "</p>";
?>
Абид Хуссейн
источник
3

Сначала используйте SoapUI, чтобы создать проект мыла из файла wsdl. Попробуйте отправить запрос поиграть с операциями wsdl. Посмотрите, как XML-запрос формирует ваши поля данных.

А затем, если у вас возникли проблемы с тем, чтобы SoapClient работал так, как вы хотите, вот как я его отлаживаю. Установите параметр трассировки так, чтобы функция __getLastRequest () была доступна для использования.

$soapClient = new SoapClient('http://yourwdsdlurl.com?wsdl', ['trace' => true]);
$params = ['user' => 'Hey', 'account' => '12345'];
$response = $soapClient->__soapCall('<operation>', $params);
$xml = $soapClient->__getLastRequest();

Затем переменная $ xml содержит xml, который SoapClient создает для вашего запроса. Сравните этот xml с тем, который был создан в SoapUI.

Для меня SoapClient, похоже, игнорирует ключи ассоциативного массива $ params и интерпретирует его как индексированный массив, вызывая неправильные данные параметров в xml. То есть, если я переупорядочу данные в $ params , ответ $ будет совершенно другим:

$params = ['account' => '12345', 'user' => 'Hey'];
$response = $soapClient->__soapCall('<operation>', $params);
Санг Нгуен
источник
3

Если вы создадите объект SoapParam, это решит вашу проблему. Создайте класс и сопоставьте его с типом объекта, заданным WebService, инициализируйте значения и отправьте запрос. См. Образец ниже.

struct Contact {

    function Contact ($pid, $pname)
    {
      id = $pid;
      name = $pname;
  }
}

$struct = new Contact(100,"John");

$soapstruct = new SoapVar($struct, SOAP_ENC_OBJECT, "Contact","http://soapinterop.org/xsd");

$ContactParam = new SoapParam($soapstruct, "Contact")

$response = $client->Function1($ContactParam);
Умеш Чаван
источник
1

У меня была такая же проблема, но я просто обернул такие аргументы, и теперь это работает.

    $args = array();
    $args['Header'] = array(
        'CustomerCode' => 'dsadsad',
        'Language' => 'fdsfasdf'
    );
    $args['RequestObject'] = $whatever;

    // this was the catch, double array with "Request"
    $response = $this->client->__soapCall($name, array(array( 'Request' => $args )));

Используя эту функцию:

 print_r($this->client->__getLastRequest());

Вы можете увидеть XML-код запроса независимо от того, меняется он или нет, в зависимости от ваших аргументов.

Используйте [trace = 1, exceptions = 0] в параметрах SoapClient.

Мартин Зварик
источник
0

Вам нужно объявить контракт класса

class Contract {
  public $id;
  public $name;
}

$contract = new Contract();
$contract->id = 100;
$contract->name = "John";

$params = array(
  "Contact" => $contract,
  "description" => "Barrel of Oil",
  "amount" => 500,
);

или

$params = array(
  $contract,
  "description" => "Barrel of Oil",
  "amount" => 500,
);

затем

$response = $client->__soapCall("Function1", array("FirstFunction" => $params));

или

$response = $client->__soapCall("Function1", $params);
Рамиль Амерзянов
источник
0

Вам нужен многомерный массив, вы можете попробовать следующее:

$params = array(
   array(
      "id" => 100,
      "name" => "John",
   ),
   "Barrel of Oil",
   500
);

в PHP массив - это очень гибкая структура. Обычно с вызовами мыла я использую оболочку XML, поэтому не уверен, что она сработает.

РЕДАКТИРОВАТЬ:

Что вы можете попробовать, так это создать json-запрос для отправки или использовать его для создания xml-запроса на покупку следующего содержания на этой странице: http://onwebdev.blogspot.com/2011/08/php-converting-rss- к json.html

Джеймс Уильямс
источник
спасибо, но тоже не вышло. Как именно использовать XML-оболочки, возможно, это проще в использовании, чем эта ...
Сначала вы должны убедиться, что ваш WSDL может обрабатывать XML-оболочки. Но похоже, вы строите запрос в XML и в большинстве случаев используете curl. Я использую SOAP с XML для обработки транзакций через банки. Вы можете проверить их в качестве отправной точки. forumdigitalpoint.com/showthread.php?t=424619#post4004636 w3schools.com/soap/soap_intro.asp
Джеймс Уильямс
0

Есть возможность генерировать объекты php5 с помощью класса WsdlInterpreter. Подробнее здесь: https://github.com/gkwelding/WSDLInterpreter

например:

require_once 'WSDLInterpreter-v1.0.0/WSDLInterpreter.php';
$wsdlLocation = '<your wsdl url>?wsdl';
$wsdlInterpreter = new WSDLInterpreter($wsdlLocation);
$wsdlInterpreter->savePHP('.');
Иштван Дёбрентей
источник
0

getLastRequest ():

Этот метод работает, только если объект SoapClient был создан с параметром трассировки, установленным на TRUE.

ИСТИНА в этом случае представлена ​​1

$wsdl = storage_path('app/mywsdl.wsdl');
try{

  $options = array(
               // 'soap_version'=>SOAP_1_1,
               'trace'=>1,
               'exceptions'=>1,

                'cache_wsdl'=>WSDL_CACHE_NONE,
             //   'stream_context' => stream_context_create($arrContextOptions)
        );
           // $client = new \SoapClient($wsdl, array('cache_wsdl' => WSDL_CACHE_NONE) );
        $client = new \SoapClient($wsdl, array('cache_wsdl' => WSDL_CACHE_NONE));
        $client     = new \SoapClient($wsdl,$options); 

работал у меня.

CollinsKe
источник