Como fazer uma chamada SOAP PHP usando a classe SoapClient

130

Estou acostumado a escrever código PHP, mas não costumo usar codificação orientada a objetos. Agora preciso interagir com SOAP (como cliente) e não consigo obter a sintaxe correta. Eu tenho um arquivo WSDL que permite configurar corretamente uma nova conexão usando a classe SoapClient. No entanto, não consigo fazer a ligação correta e obter os dados retornados. Preciso enviar os seguintes dados (simplificados):

  • ID do contato
  • Nome de contato
  • Descrição geral
  • Montante

Existem duas funções definidas no documento WSDL, mas eu preciso apenas de uma ("FirstFunction" abaixo). Aqui está o script que eu corro para obter informações sobre as funções e tipos disponíveis:

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

E aqui está a saída que ele gera:

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

Digamos que eu queira fazer uma chamada para o FirstFunction com os dados:

  • ID do contato: 100
  • Nome do contato: John
  • Descrição Geral: Barril of Oil
  • Quantidade: 500

Qual seria a sintaxe correta? Eu tenho tentado todos os tipos de opções, mas parece que a estrutura do sabão é bastante flexível, então existem muitas maneiras de fazer isso. Também não consegui descobrir isso no manual ...


ATUALIZAÇÃO 1: amostra experimentada do 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));

Mas eu recebo esta resposta: Object has no 'Contact' property. Como você pode ver na saída de getTypes(), existe um structchamado Contact, então eu acho que, de alguma forma, preciso esclarecer que meus parâmetros incluem os dados de contato, mas a pergunta é: como?

ATUALIZAÇÃO 2: Eu também tentei essas estruturas, mesmo erro.

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

Assim como:

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

Erro nos dois casos: o objeto não possui propriedade 'Contato'


fonte

Respostas:

178

Isso é o que você precisa fazer.

Eu tentei recriar a situação ...


  • Para este exemplo, criei um WebService (WS) de amostra .NET com um WebMethodchamado Function1esperando os seguintes parâmetros:

Função1 (Contato de contato, descrição da string, valor int)

  • Onde Contactestá apenas um modelo que possui getters e setters para ide namecomo no seu caso.

  • Você pode fazer o download do WS de amostra do .NET em:

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


O código.

Isto é o que você precisa fazer no lado do PHP:

(Testado e funcionando)

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

?>

Testando a coisa toda.

  • Se você print_r($params)ver a seguinte saída, como seria esperado pelo seu WS:

Matriz ([Contato] => Objeto de contato ([id] => 100 [nome] => João) [descrição] => Barril de petróleo [quantidade] => 500)

  • Quando depurei o WS de amostra do .NET, obtive o seguinte:

insira a descrição da imagem aqui

(Como você pode ver, o Contactobjeto não é nullnem os outros parâmetros. Isso significa que sua solicitação foi feita com êxito do lado do PHP)

  • A resposta do WS de amostra do .NET foi a esperada e foi isso que obtive no lado do PHP:

objeto (stdClass) [3] public 'Function1Result' => string 'Informações detalhadas da sua solicitação! id: 100, nome: John, descrição: Barril of Oil, quantidade: 500 '(length = 98)


Feliz codificação!

Oscar Jara
fonte
3
Perfeito! Eu agi como se soubesse um pouco mais sobre os serviços SOAP do que realmente, e isso me levou aonde eu precisava estar.
chapman84
1
Eu não fiz a pergunta, caso contrário eu teria. A pergunta e esta resposta foram aprovadas por mim.
precisa saber é o seguinte
4
@ usuário deve aceitá-lo :) BTW, resposta muito agradável, completa e muito clara. +1
Yann39
Obrigado por isso! Impulso de Lil para entender a estrutura SOAP.
EatCodePlaySleep
69

Você também pode usar os serviços SOAP dessa maneira:

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

Este é um exemplo com um serviço real e funciona.

Espero que isto ajude.

Salvador P.
fonte
Eu recebo o seguinte erro: object (stdClass) # 70 (1) {["GetWeatherResult"] => string (14) "Dados não encontrados"} Alguma idéia?
Ilker Baltaci 14/06
Parece que eles mudaram as cordas das cidades. Acabei de atualizar o exemplo com outra chamada para outro serviço que eles fornecem e está funcionando. Tentei usar as strings que eles retornam como cidades, mas parece não funcionar bem, de qualquer forma a função getCitiesByCountry () serve como exemplo de como fazer uma chamada.
Salvador P.
29

Inicialize primeiro os serviços da web:

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

Em seguida, defina e passe os parâmetros:

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

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

Observe que o nome do método está disponível no WSDL como nome da operação, por exemplo:

<operation name="methodname">
MMK
fonte
Obrigado! Eu tentei isso, mas recebo o erro "O objeto não tem propriedade 'Contato'". Atualizará minha pergunta com todos os detalhes. Alguma ideia?
@ user16441 Pode publicar o WSDL e o esquema do serviço? Geralmente começo descobrindo qual XML o serviço espera e, em seguida, usando o WireShark para descobrir o que meu cliente está realmente enviando.
Davidfmatheson
21

Não sei por que meu serviço da web tem a mesma estrutura que você, mas não precisa de Class para parâmetro, apenas é array.

Por exemplo: - Meu 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>

Eu var_dump:

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

Aqui está o resultado:

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)

Então, no meu código:

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

mas com sucesso!

Tín Phạm
fonte
1
Seu exemplo é mais útil, pois mostra dependecies estrutura
vladkras
3

Leia isso;-

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

Ou

Este é um bom exemplo para a função SOAP "__call". No entanto, está obsoleto.

<?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
fonte
3

Primeiro, use o SoapUI para criar seu projeto de sabão a partir do wsdl. Tente enviar uma solicitação para jogar com as operações do wsdl. Observe como a solicitação xml compõe seus campos de dados.

E então, se você está tendo problemas para fazer o SoapClient agir como você deseja, aqui está como eu o depuro. Defina o rastreio da opção para que a função __getLastRequest () esteja disponível para uso.

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

Em seguida, a variável $ xml contém o xml que o SoapClient compõe para sua solicitação. Compare este xml com o gerado no SoapUI.

Para mim, o SoapClient parece ignorar as chaves da matriz associativa $ params e interpretá-la como matriz indexada, causando dados de parâmetros incorretos no xml. Ou seja, se eu reordenar os dados em $ params , a resposta $ será completamente diferente:

$params = ['account' => '12345', 'user' => 'Hey'];
$response = $soapClient->__soapCall('<operation>', $params);
Sang Nguyen
fonte
3

Se você criar o objeto de SoapParam, isso resolverá o seu problema. Crie uma classe e mapeie-a com o tipo de objeto fornecido pelo WebService, Inicialize os valores e envie a solicitação. Veja a amostra abaixo.

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
fonte
1

Eu tive o mesmo problema, mas acabei de encerrar os argumentos assim e funciona agora.

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

Usando esta função:

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

Você pode ver o XML da solicitação, esteja ele mudando ou não, dependendo dos seus argumentos.

Use [trace = 1, exceções = 0] nas opções do SoapClient.

Martin Zvarík
fonte
0

Você precisa declarar a classe 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,
);

ou

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

Então

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

ou

$response = $client->__soapCall("Function1", $params);
Ramil Amerzyanov
fonte
0

Você precisa de uma matriz multidimensional, pode tentar o seguinte:

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

em PHP, uma matriz é uma estrutura e é muito flexível. Normalmente, com chamadas de sabão, eu uso um wrapper XML para não ter certeza se ele funcionará.

EDITAR:

O que você pode querer tentar é criar uma consulta json para enviar ou usá-la para criar uma compra xml, seguindo o que está nesta página: http://onwebdev.blogspot.com/2011/08/php-converting-rss- to-json.html

James Williams
fonte
obrigado, mas também não funcionou. Como você usa wrappers XML exatamente, talvez seja mais fácil de usar do que isso ...
Primeiro, você precisa garantir que seu WSDL possa manipular wrappers XML. Mas é semelhante, você cria a solicitação em XML e, na maioria dos casos, usa curl. Eu uso SOAP com XML para processar transações através de bancos. Você pode conferir isso como um ponto de partida. forums.digitalpoint.com/showthread.php?t=424619#post4004636 w3schools.com/soap/soap_intro.asp
James Williams
0

Existe uma opção para gerar objetos php5 com a classe WsdlInterpreter. Veja mais aqui: https://github.com/gkwelding/WSDLInterpreter

por exemplo:

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

getLastRequest ():

Este método funciona apenas se o objeto SoapClient foi criado com a opção de rastreamento configurada como TRUE.

VERDADEIRO, neste caso, é representado por 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); 

trabalhou para mim.

CollinsKe
fonte