Comment faire un appel PHP SOAP à l'aide de la classe SoapClient

130

J'ai l'habitude d'écrire du code PHP, mais n'utilise pas souvent le codage orienté objet. Je dois maintenant interagir avec SOAP (en tant que client) et je ne parviens pas à obtenir la bonne syntaxe. J'ai un fichier WSDL qui me permet de configurer correctement une nouvelle connexion à l'aide de la classe SoapClient. Cependant, je ne suis pas en mesure de passer le bon appel et de récupérer les données. Je dois envoyer les données (simplifiées) suivantes:

  • ID de contact
  • Nom du contact
  • Description générale
  • Montant

Il y a deux fonctions définies dans le document WSDL, mais je n'en ai besoin que d'une ("FirstFunction" ci-dessous). Voici le script que j'exécute pour obtenir des informations sur les fonctions et les types disponibles:

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

Et voici la sortie qu'il génère:

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

Dites que je veux appeler la FirstFunction avec les données:

  • Identifiant du contact: 100
  • Nom du contact: John
  • Description générale: baril de pétrole
  • Montant: 500

Quelle serait la bonne syntaxe? J'ai essayé toutes sortes d'options, mais il semble que la structure du savon soit assez flexible, il y a donc de très nombreuses façons de le faire. Impossible de le comprendre non plus dans le manuel ...


UPDATE 1: échantillon essayé 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));

Mais je reçois cette réponse: Object has no 'Contact' property. Comme vous pouvez le voir dans la sortie de getTypes(), il y a un structappelé Contact, donc je suppose que je dois en quelque sorte préciser que mes paramètres incluent les données de contact, mais la question est: comment?

MISE À JOUR 2: J'ai également essayé ces structures, même erreur.

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

Aussi bien que:

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

Erreur dans les deux cas: l'objet n'a pas de propriété 'Contact' '


la source

Réponses:

178

C'est ce que vous devez faire.

J'ai essayé de recréer la situation ...


  • Pour cet exemple, j'ai créé un exemple de WebService (WS) .NET avec un WebMethodappelé qui Function1attend les paramètres suivants:

Function1 (Contact Contact, description de la chaîne, montant entier)

  • Contactest juste un modèle qui a des getters et des setters pour idet namecomme dans votre cas.

  • Vous pouvez télécharger l'exemple de WS .NET à l'adresse:

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


Le code.

Voici ce que vous devez faire du côté PHP:

(Testé et fonctionnel)

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

?>

Tester le tout.

  • Si vous le faites, print_r($params)vous verrez la sortie suivante, comme votre WS s'y attend:

Array ([Contact] => Objet de contact ([id] => 100 [name] => John) [description] => Barrel of Oil [amount] => 500)

  • Lorsque j'ai débogué l'exemple de WS .NET, j'ai obtenu ce qui suit:

entrez la description de l'image ici

(Comme vous pouvez le voir, l' Contactobjet n'est pas nullni les autres paramètres. Cela signifie que votre requête a été effectuée avec succès du côté PHP)

  • La réponse de l'exemple de WS .NET était celle attendue et voici ce que j'ai obtenu du côté PHP:

object (stdClass) [3] public 'Function1Result' => string 'Informations détaillées sur votre demande! id: 100, nom: John, description: baril de pétrole, quantité: 500 '(longueur = 98)


Bon codage!

Oscar Jara
la source
3
Parfait! J'ai agi comme si j'en savais un peu plus sur les services SOAP que je ne le savais vraiment et cela m'a amené là où je devais être.
chapman84
1
Je n'ai pas posé la question, sinon je l'aurais fait. La question et cette réponse ont reçu un vote positif de ma part.
chapman84
4
@user devrait l'accepter :) BTW, très belle réponse, complète et très claire. +1
Yann39
Merci pour cela! Petit coup de pouce pour comprendre la structure SOAP.
EatCodePlaySleep
69

Vous pouvez également utiliser les services SOAP de cette façon:

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

Ceci est un exemple avec un vrai service, et ça marche.

J'espère que cela t'aides.

Salvador P.
la source
J'obtiens l'erreur suivante: object (stdClass) # 70 (1) {["GetWeatherResult"] => string (14) "Data Not Found"} Une idée?
Ilker Baltaci
On dirait qu'ils ont changé les chaînes des villes. Je viens de mettre à jour l'exemple avec un autre appel à un autre service qu'ils fournissent et cela fonctionne. J'ai essayé d'utiliser les chaînes qu'ils retournent en tant que villes, mais ne semble pas fonctionner correctement, de toute façon la fonction getCitiesByCountry () sert d'exemple sur la façon de passer un appel.
Salvador P.
30

Initialisez d'abord les services Web:

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

Ensuite, définissez et transmettez les paramètres:

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

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

Notez que le nom de la méthode est disponible dans WSDL comme nom d'opération, par exemple:

<operation name="methodname">
MMK
la source
Merci! J'ai essayé cela mais j'obtiens l'erreur "L'objet n'a pas de propriété 'Contact'". Mettra à jour ma question avec tous les détails. Des idées?
@ user16441 Pouvez-vous publier le WSDL et le schéma du service? Je commence généralement par déterminer ce que le service XML attend, puis j'utilise WireShark pour déterminer ce que mon client envoie réellement.
davidfmatheson
21

Je ne sais pas pourquoi mon service Web a la même structure que vous, mais il n'a pas besoin de classe pour le paramètre, c'est juste un tableau.

Par exemple: - Mon 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>

Je var_dump:

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

Voici le résultat:

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)

Donc dans mon code:

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

mais ça réussit!

Tín Phạm
la source
1
Votre exemple est plus utile, car il montre les dépendances de la structure
vladkras
3

lis ça;-

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

Ou

Ceci est un bon exemple pour la fonction SOAP "__call". Cependant, il est obsolète.

<?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
la source
3

Tout d'abord, utilisez SoapUI pour créer votre projet soap à partir du fichier wsdl. Essayez d'envoyer une demande pour jouer avec les opérations du wsdl. Observez comment la requête xml compose vos champs de données.

Et puis, si vous rencontrez des problèmes pour que SoapClient agisse comme vous le souhaitez, voici comment je le débogue. Définissez l'option trace de sorte que la fonction __getLastRequest () soit disponible.

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

Ensuite, la variable $ xml contient le xml que SoapClient compose pour votre demande. Comparez ce xml avec celui généré dans SoapUI.

Pour moi, SoapClient semble ignorer les clés du tableau associatif $ params et l'interpréter comme un tableau indexé, provoquant des données de paramètres erronées dans le xml. Autrement dit, si je réorganise les données dans $ params , la réponse $ est complètement différente:

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

Si vous créez l'objet de SoapParam, cela résoudra votre problème. Créez une classe et mappez-la avec le type d'objet donné par WebService, initialisez les valeurs et envoyez la requête. Voir l'exemple ci-dessous.

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
la source
1

J'ai eu le même problème, mais j'ai juste enveloppé les arguments comme celui-ci et cela fonctionne maintenant.

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

Utilisation de cette fonction:

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

Vous pouvez voir le XML de requête, qu'il change ou non en fonction de vos arguments.

Utilisez [trace = 1, exceptions = 0] dans les options SoapClient.

Martin Zvarík
la source
0

Vous devez déclarer un contrat de classe

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

ensuite

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

ou

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

Vous avez besoin d'un tableau multidimensionnel, vous pouvez essayer ce qui suit:

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

en PHP, un tableau est une structure et est très flexible. Normalement, avec les appels soap, j'utilise un wrapper XML donc je ne sais pas si cela fonctionnera.

ÉDITER:

Ce que vous voudrez peut-être essayer est de créer une requête json à envoyer ou de l'utiliser pour créer une sorte d'achat xml en suivant ce qui se trouve sur cette page: http://onwebdev.blogspot.com/2011/08/php-converting-rss- to-json.html

James Williams
la source
merci, mais cela n'a pas fonctionné non plus. Comment utiliser les wrappers XML exactement, c'est peut-être plus facile à utiliser que ça ...
Vous devez d'abord vous assurer que votre WSDL peut gérer les wrappers XML. Mais c'est similaire, vous construisez la requête en XML et dans la plupart des cas utilisez curl. J'ai utilisé SOAP avec XML pour traiter les transactions via les banques. Vous pouvez les consulter comme point de départ. forums.digitalpoint.com/showthread.php?t=424619#post4004636 w3schools.com/soap/soap_intro.asp
James Williams
0

Il existe une option pour générer des objets php5 avec la classe WsdlInterpreter. En savoir plus ici: https://github.com/gkwelding/WSDLInterpreter

par exemple:

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

getLastRequest ():

Cette méthode ne fonctionne que si l'objet SoapClient a été créé avec l'option de trace définie sur TRUE.

TRUE dans ce cas est représenté par 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); 

travaillé pour moi.

CollinsKe
la source