Jak wykonać wywołanie PHP SOAP przy użyciu klasy SoapClient

131

Jestem przyzwyczajony do pisania kodu PHP, ale rzadko używam kodowania zorientowanego obiektowo. Teraz muszę współdziałać z SOAP (jako klient) i nie jestem w stanie uzyskać prawidłowej składni. Mam plik WSDL, który pozwala mi poprawnie skonfigurować nowe połączenie za pomocą klasy SoapClient. Jednak nie jestem w stanie wykonać właściwego połączenia i zwrócić danych. Muszę przesłać następujące (uproszczone) dane:

  • Identyfikator kontaktu
  • Nazwa Kontaktu
  • Ogólny opis
  • Ilość

W dokumencie WSDL zdefiniowano dwie funkcje, ale potrzebuję tylko jednej (poniżej „FirstFunction”). Oto skrypt, który uruchamiam, aby uzyskać informacje o dostępnych funkcjach i typach:

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

A oto wynik, który generuje:

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"
}

Powiedz, że chcę zadzwonić do FirstFunction z danymi:

  • Identyfikator kontaktu: 100
  • Imię i nazwisko osoby kontaktowej: John
  • Opis ogólny: Beczka oleju
  • Kwota: 500

Jaka byłaby właściwa składnia? Próbowałem różnych opcji, ale wygląda na to, że struktura mydła jest dość elastyczna, więc jest na to wiele sposobów. Nie mogłem też tego rozgryźć na podstawie instrukcji ...


UPDATE 1: próbka z MMK:

$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));

Ale mam tę odpowiedź: Object has no 'Contact' property. Jak widać na wyjściu programu getTypes(), pojawia się structwezwanie Contact, więc myślę, że muszę jakoś wyjaśnić, że moje parametry obejmują dane kontaktowe, ale pytanie brzmi: jak?

UPDATE 2: Próbowałem też tych struktur, ten sam błąd.

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

Jak również:

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

Błąd w obu przypadkach: obiekt nie ma właściwości „Kontakt”


źródło

Odpowiedzi:

180

Oto, co musisz zrobić.

Próbowałem odtworzyć sytuację ...


  • Na potrzeby tego przykładu utworzyłem przykładową usługę WebService (WS) .NET z WebMethodwywołaniem Function1oczekującym następujących parametrów:

Funkcja 1 (kontakt kontaktowy, opis ciągu, kwota int)

  • Gdzie Contactjest tylko model, który ma gettery i setery dla idi namejak w twoim przypadku.

  • Przykładowy program WS .NET można pobrać pod adresem:

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


Kod.

Oto, co musisz zrobić po stronie PHP:

(Przetestowane i działające)

<?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);

?>

Testowanie całości.

  • Jeśli to zrobisz print_r($params), zobaczysz następujące dane wyjściowe, zgodnie z oczekiwaniami twojego WS:

Tablica ([Kontakt] => Obiekt kontaktu ([id] => 100 [nazwa] => Jan) [opis] => Baryłka ropy [ilość] => 500)

  • Podczas debugowania przykładowego WS .NET otrzymałem następujące informacje:

wprowadź opis obrazu tutaj

(Jak widać, Contactobiekt nie jest nullani inne parametry. Oznacza to, że żądanie zostało pomyślnie wykonane po stronie PHP)

  • Odpowiedź z próbki WS .NET była oczekiwana i oto co otrzymałem po stronie PHP:

object (stdClass) [3] public 'Function1Result' => string 'Szczegółowe informacje o Twoim żądaniu! id: 100, imię: Janek, opis: Baryłka ropy, ilość: 500 '(długość = 98)


Miłego kodowania!

Oscar Jara
źródło
3
Idealny! Zachowywałem się, jakbym wiedział trochę więcej o usługach SOAP niż w rzeczywistości i to doprowadziło mnie tam, gdzie potrzebowałem.
Cameron Chapman,
1
Nie zadałem tego pytania, inaczej bym to zrobił. Pytanie i odpowiedź otrzymały jednak ode mnie pozytywną opinię.
Cameron Chapman
4
@user powinien to zaakceptować :) Przy okazji, bardzo ładna odpowiedź, kompletna i bardzo jasna. +1
Yann39
Dzięki za to! Lil 'boost, aby zrozumieć strukturę SOAP.
EatCodePlaySleep
70

Z usług SOAP możesz również korzystać w ten sposób:

<?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);

To jest przykład z prawdziwą usługą i działa.

Mam nadzieję że to pomoże.

Salvador P.
źródło
Pojawia się następujący błąd: obiekt (stdClass) # 70 (1) {["GetWeatherResult"] => string (14) "Nie znaleziono danych"} Masz jakiś pomysł?
Ilker Baltaci
Wygląda na to, że zmienili łańcuchy miast. Właśnie zaktualizowałem przykład o inne połączenie z inną usługą, którą zapewniają i działa. Próbowałem użyć ciągów, które zwracają jako miasta, ale wydaje się, że nie działa dobrze, w każdym razie funkcja getCitiesByCountry () służy jako przykład, jak wykonać połączenie.
Salvador P.
30

Najpierw zainicjuj usługi sieciowe:

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

Następnie ustaw i przekaż parametry:

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

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

Zauważ, że nazwa metody jest dostępna w WSDL jako nazwa operacji, np .:

<operation name="methodname">
MMK
źródło
Dzięki! Próbowałem tego, ale pojawia się błąd „Obiekt nie ma właściwości„ Kontakt ””. Zaktualizuję moje pytanie o pełne szczegóły. Jakieś pomysły?
@ user16441 Czy możesz opublikować WSDL i schemat usługi? Zwykle zaczynam od ustalenia, jakiego XML oczekuje usługa, a następnie używam WireShark, aby dowiedzieć się, co faktycznie wysyła mój klient.
davidfmatheson
21

Nie wiem, dlaczego moja usługa internetowa ma taką samą strukturę jak Ty, ale nie potrzebuje klasy jako parametru, po prostu jest tablicą.

Na przykład: - Mój 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>[email protected]</email>
                <language>es</language>
            </receiver>
        </ns:createOrder>
    </soapenv:Body>
</soapenv:Envelope>

I var_dump:

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

Oto wynik:

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)

Więc w moim kodzie:

    $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' => '[email protected]',
            'language' => 'es'
        )
    );
    $result = $client->createOrder($params);
    var_dump($result);

ale z powodzeniem!

Tín Phạm
źródło
2
Twój przykład jest bardziej pomocny, ponieważ pokazuje zależności strukturalne
vladkras
3

Przeczytaj to;-

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

Lub

To jest dobry przykład dla funkcji SOAP „__call”. Jednak jest przestarzały.

<?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>";
?>
Abid Hussain
źródło
3

Najpierw użyj SoapUI, aby utworzyć projekt mydła z pliku wsdl. Spróbuj wysłać prośbę o zabawę z operacjami wsdl. Obserwuj, jak żądanie XML komponuje pola danych.

A potem, jeśli masz problem z uzyskaniem SoapClient, działającego tak, jak chcesz, oto jak go debuguję. Ustaw opcję śledzenia, aby funkcja __getLastRequest () była dostępna do użycia.

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

Następnie zmienna $ xml zawiera xml, który SoapClient tworzy dla twojego żądania. Porównaj ten plik XML z plikiem wygenerowanym w SoapUI.

Dla mnie SoapClient wydaje się ignorować klucze tablicy asocjacyjnej $ params i interpretować je jako tablicę indeksowaną, powodując nieprawidłowe dane parametrów w xml. Oznacza to, że jeśli zmienię kolejność danych w parametrach $ , odpowiedź $ będzie zupełnie inna:

$params = ['account' => '12345', 'user' => 'Hey'];
$response = $soapClient->__soapCall('<operation>', $params);
Sang Nguyen
źródło
3

Jeśli utworzysz obiekt SoapParam, rozwiąże to twój problem. Utwórz klasę i zamapuj ją na typ obiektu podany przez WebService, zainicjuj wartości i wyślij w żądaniu. Zobacz przykład poniżej.

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);
Umesh Chavan
źródło
1

Miałem ten sam problem, ale po prostu zawarłem argumenty w ten sposób i teraz działa.

    $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 )));

Korzystanie z tej funkcji:

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

Możesz zobaczyć plik XML żądania, czy zmienia się, czy nie, w zależności od argumentów.

Użyj [trace = 1, exceptions = 0] w opcjach SoapClient.

Martin Zvarík
źródło
0

Musisz zadeklarować klasę Contract

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,
);

lub

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

Następnie

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

lub

$response = $client->__soapCall("Function1", $params);
Ramil Amerzyanov
źródło
0

Potrzebujesz wielowymiarowej tablicy, możesz spróbować następujących rzeczy:

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

w PHP tablica jest strukturą i jest bardzo elastyczna. Zwykle w przypadku wywołań mydła używam opakowania XML, więc nie jestem pewien, czy zadziała.

EDYTOWAĆ:

To, co możesz chcieć spróbować, to utworzenie zapytania json do wysłania lub użycie go do utworzenia pliku XML buy pewnego rodzaju na tej stronie: http://onwebdev.blogspot.com/2011/08/php-converting-rss- to-json.html

James Williams
źródło
dzięki, ale to też nie zadziałało. Jak dokładnie używasz opakowań XML, być może jest to łatwiejsze w użyciu niż to ...
Najpierw musisz się upewnić, że twój WSDL może obsługiwać opakowania XML. Ale jest podobnie, budujesz żądanie w XML iw większości przypadków używasz curl. Używam SOAP z XML do przetwarzania transakcji za pośrednictwem banków. Możesz to sprawdzić jako punkt wyjścia. forums.digitalpoint.com/showthread.php?t=424619#post4004636 w3schools.com/soap/soap_intro.asp
James Williams
0

Istnieje możliwość generowania obiektów php5 z klasą WsdlInterpreter. Zobacz więcej tutaj: https://github.com/gkwelding/WSDLInterpreter

na przykład:

require_once 'WSDLInterpreter-v1.0.0/WSDLInterpreter.php';
$wsdlLocation = '<your wsdl url>?wsdl';
$wsdlInterpreter = new WSDLInterpreter($wsdlLocation);
$wsdlInterpreter->savePHP('.');
István Döbrentei
źródło
0

getLastRequest ():

Ta metoda działa tylko wtedy, gdy obiekt SoapClient został utworzony z opcją śledzenia ustawioną na wartość TRUE.

PRAWDA w tym przypadku jest reprezentowana przez 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); 

pracował dla mnie.

CollinsKe
źródło