Cómo hacer una llamada PHP SOAP usando la clase SoapClient

130

Estoy acostumbrado a escribir código PHP, pero a menudo no uso la codificación orientada a objetos. Ahora necesito interactuar con SOAP (como cliente) y no puedo obtener la sintaxis correcta. Tengo un archivo WSDL que me permite configurar correctamente una nueva conexión usando la clase SoapClient. Sin embargo, no puedo hacer la llamada correcta y obtener los datos devueltos. Necesito enviar los siguientes datos (simplificados):

  • ID de contacto
  • Nombre de contacto
  • Descripción general
  • Cantidad

Hay dos funciones definidas en el documento WSDL, pero solo necesito una ("Primera función" a continuación). Aquí está el script que ejecuto para obtener información sobre las funciones y tipos disponibles:

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

Y aquí está la salida que genera:

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 quiero hacer una llamada a FirstFunction con los datos:

  • ID de contacto: 100
  • Nombre de contacto: John
  • Descripción general: Barril de petróleo
  • Cantidad: 500

¿Cuál sería la sintaxis correcta? He estado probando todo tipo de opciones, pero parece que la estructura del jabón es bastante flexible, por lo que hay muchas maneras de hacerlo. No pude resolverlo del manual tampoco ...


ACTUALIZACIÓN 1: muestra probada de 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));

Pero me da esta respuesta: Object has no 'Contact' property. Como puede ver en la salida de getTypes(), hay un structllamado Contact, así que supongo que de alguna manera necesito dejar en claro que mis parámetros incluyen los datos de contacto, pero la pregunta es: ¿cómo?

ACTUALIZACIÓN 2: También probé estas estructuras, el mismo error.

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

Tanto como:

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

Error en ambos casos: el objeto no tiene la propiedad 'Contacto'


fuente

Respuestas:

178

Esto es lo que necesitas hacer.

Traté de recrear la situación ...


  • Para este ejemplo, creé un WebService (WS) de muestra .NET con un WebMethodllamado Function1esperando los siguientes parámetros:

Función1 (contacto contacto, descripción de cadena, cantidad int)

  • Donde Contactes solo un modelo que tiene captadores y establecedores para idy nameen su caso.

  • Puede descargar la muestra WS de .NET en:

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


El código.

Esto es lo que debe hacer en el lado de PHP:

(Probado y 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);

?>

Probando todo el asunto.

  • Si lo hace print_r($params), verá el siguiente resultado, como su WS esperaría:

Matriz ([Contacto] => Objeto de contacto ([id] => 100 [nombre] => John) [descripción] => Barril de petróleo [cantidad] => 500)

  • Cuando depuré el WS de muestra de .NET obtuve lo siguiente:

ingrese la descripción de la imagen aquí

(Como puede ver, el Contactobjeto no es nullni los otros parámetros. Eso significa que su solicitud se realizó con éxito desde el lado de PHP)

  • La respuesta de la muestra WS de .NET fue la esperada y esto es lo que obtuve en el lado de PHP:

object (stdClass) [3] public 'Function1Result' => string '¡Información detallada de su solicitud! id: 100, nombre: John, descripción: Barril de petróleo, cantidad: 500 '(longitud = 98)


¡Feliz codificación!

Oscar Jara
fuente
3
¡Perfecto! Actué como si supiera un poco más sobre los servicios SOAP de lo que realmente sabía y esto me llevó a donde necesitaba estar.
chapman84
1
No hice la pregunta, de lo contrario lo habría hecho. Sin embargo, la pregunta y esta respuesta me dieron un voto positivo.
chapman84
44
@user debería aceptarlo :) Por cierto, muy buena respuesta, completa y muy clara. +1
Yann39
¡Gracias por esto! Lil 'boost para entender la estructura SOAP.
EatCodePlaySleep
69

También puede usar los servicios SOAP de esta manera:

<?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 es un ejemplo con un servicio real, y funciona.

Espero que esto ayude.

Salvador P.
fuente
Me sale el siguiente error: object (stdClass) # 70 (1) {["GetWeatherResult"] => string (14) "Datos no encontrados"} ¿Alguna idea?
Ilker Baltaci
Parece que han cambiado las cuerdas de las ciudades. Acabo de actualizar el ejemplo con otra llamada a otro servicio que brindan y está funcionando. Traté de usar las cadenas que devuelven como ciudades, pero no parece funcionar bien, de todos modos la función getCitiesByCountry () sirve como ejemplo sobre cómo hacer una llamada.
Salvador P.
29

Primero inicialice los servicios web:

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

Luego establezca y pase los parámetros:

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

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

Tenga en cuenta que el nombre del método está disponible en WSDL como nombre de la operación, por ejemplo:

<operation name="methodname">
MMK
fuente
¡Gracias! He intentado esto pero aparece el error "El objeto no tiene propiedad 'Contacto'". Actualizaré mi pregunta con todos los detalles. ¿Algunas ideas?
@ user16441 ¿Puede publicar el WSDL y el esquema del servicio? Por lo general, empiezo averiguando qué XML espera el servicio, luego uso WireShark para averiguar qué está enviando realmente mi cliente.
davidfmatheson
21

No sé por qué mi servicio web tiene la misma estructura que usted, pero no necesita Clase para el parámetro, solo es una matriz.

Por ejemplo: - Mi 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>

Yo var_dump:

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

Aquí está el 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)

Entonces en mi 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);

pero con exito!

Tín Phạm
fuente
1
Su ejemplo es más útil, porque muestra dependencias de la estructura
vladkras
3

Lee esto;-

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

O

Este es un buen ejemplo para la función SOAP "__call". Sin embargo, está en desuso.

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

Primero, usa SoapUI para crear su proyecto de jabón desde el wsdl. Intenta enviar una solicitud para jugar con las operaciones de wsdl. Observe cómo la solicitud xml compone sus campos de datos.

Y luego, si tiene problemas para que SoapClient actúe como lo desea, así es como lo depuro. Establezca la opción trace para que la función __getLastRequest () esté disponible para su uso.

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

Luego, la variable $ xml contiene el xml que SoapClient compone para su solicitud. Compare este xml con el generado en SoapUI.

Para mí, SoapClient parece ignorar las claves de la matriz asociativa $ params e interpretarla como una matriz indexada, lo que causa datos de parámetros incorrectos en el xml. Es decir, si vuelvo a ordenar los datos en $ params , la respuesta $ es completamente diferente:

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

Si crea el objeto de SoapParam, esto resolverá su problema. Cree una clase y correlacione con el tipo de objeto proporcionado por WebService, Inicialice los valores y envíe la solicitud. Vea la muestra a continuación.

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

Tuve el mismo problema, pero acabo de envolver los argumentos como este y funciona ahora.

    $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 función:

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

Puede ver el XML de solicitud si está cambiando o no, dependiendo de sus argumentos.

Utilice [trace = 1, excepciones = 0] en las opciones de SoapClient.

Martin Zvarík
fuente
0

Necesitas declarar contrato de clase

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

o

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

Luego

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

o

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

Necesita una matriz multidimensional, puede probar lo siguiente:

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

en PHP una matriz es una estructura y es muy flexible. Normalmente con llamadas de jabón, uso un contenedor XML, así que no estoy seguro de si funcionará.

EDITAR:

Lo que puede intentar es crear una consulta json para enviar o usarla para crear una compra xml, siguiendo lo que está en esta página: http://onwebdev.blogspot.com/2011/08/php-converting-rss- to-json.html

James Williams
fuente
gracias, pero tampoco funcionó. ¿Cómo se usan las envolturas XML exactamente, tal vez eso es más fácil de usar que esto ...
Primero debe asegurarse de que su WSDL pueda manejar envoltorios XML. Pero es similar, crea la solicitud en XML y, en la mayoría de los casos, usa curl. He usado SOAP con XML para procesar transacciones a través de bancos. Puede consultarlos como punto de partida. forums.digitalpoint.com/showthread.php?t=424619#post4004636 w3schools.com/soap/soap_intro.asp
James Williams
0

Hay una opción para generar objetos php5 con la clase WsdlInterpreter. Ver más aquí: https://github.com/gkwelding/WSDLInterpreter

por ejemplo:

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

getLastRequest ():

Este método solo funciona si el objeto SoapClient se creó con la opción de rastreo establecida en TRUE.

VERDADERO en este caso está 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); 

trabajó para mi.

CollinsKe
fuente