Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

php SoapVar not setting attributes

Tags:

php

soap

I'm trying to add a few attributes to a soap request. Top rated comment on PHP.net (http://php.net/manual/en/soapvar.soapvar.php) and here and here on SO all say the same thing:

$param = array(
    "_" => 'value',
    'attrName' => 'attributeName'
);
$p = new SoapVar($param, SOAP_ENC_OBJECT);

should return

<param attrName="attributeName">value</param>

Which would be great, except when I run that block of code, I get this XML:

<param>
    <_>value</_>
    <attrName>attributeName</attrName>
</param>

which is clearly wrong. Surely I'm not the only person in the world to have this problem? Did the documentation on that little bit of functionality change since 2011?

like image 728
mounty Avatar asked Jul 21 '14 17:07

mounty


3 Answers

Yes, you are not the only one who having this issue - I have seen countless number of posts where people claim that array passed to SoapVar with SOAP_ENC_OBJECT solves the issue while other people claim otherwise on the same posts. And documentation is not clear on the reasons (apart from my post year ago in comments section of php.net).

Facing the same issue I have read the sources of PHP SOAP extension. Basically the syntax you have used is absolutely correct:

$param = array(
    "_" => 'value',
    'attrName' => 'attributeName'
);
$p = new SoapVar($param, SOAP_ENC_OBJECT);

What the documentation does not say: this syntax may produce two different results (and in fact even more: PHP SOAP may express it in eight different ways). And as you may see the code above is ambiguous: what in the code above says that 'attrName' is an attribute and not an element? Nothing. The code above just don't have sufficient amount of information for SoapClient to decide what 'attrName' is and so it defaults to "an element".

SoapClient may operate in two modes: non-WSDL and WSDL. In former mode you would never get the result you want: SoapClient relies on type information in order to turn array element into attribute. As type information is not present in non-WSDL mode SoapClient represents the provided array as set of elements - exactly what you got. In WSDL mode type information is present and therefore SoapClient knows elements and attributes names and may match them to array indexes. So you MUST have your SoapClient in WSDL mode if you want your attributes.

Basically in order to achieve what you want you need to have WSDL file in document/literal mode with <xsd:attribute name="attrName" type="xsd:string"/> in appropriate section in <xsd:schema> block.

The reason why some people claim success about array use for attributes and other people say "It does not work" lies solely in their setup: some people have WSDL files to consume, some people just trying to do new SoapClient(null, array(...)); (which of course fails their expectations)

like image 70
Vladimir Bashkirtsev Avatar answered Sep 21 '22 01:09

Vladimir Bashkirtsev


For the moment I'm using the DOMDocument object to create the node. Wish there was an easier way, but in the interest of answering this question and providing a workaround, here goes:

$dom = new DOMDocument("1.0", "utf-8");
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$param = $dom->createElement('param');
$paramAttr = $dom->createAttribute('attrName');
$paramAttr->value = "attributeName";
$param->value = "value";
$param->appendChild($paramAttr);
$dom->appendChild($param);
$soap->param = new SoapVar($dom->saveXML($dom->documentElement), XSD_ANYXML);

So not ideal. Another way to do it would be to write the XML yourself:

$soap->param = new SoapVar("<param attrName='attributeName'>value</param>", XSD_ANYXML);

I don't like either way, but when you've got a deadline, you go with what works.

like image 25
mounty Avatar answered Sep 22 '22 01:09

mounty


I know this is an old question, but I wanted to provide an answer for anyone else that stumbles upon this post. I ran into the same issue as mounty and was able to work around it using a solution that does not require XSD_ANYXML. I was unable to get the SoapVar with an array approach working, but was able to get it working using the same keys as class properties. For example, using the code below:

class MyParam {
  public $_;
  public $attrName;

  public function __construct($paramValue, $attrValue)
  {
    $this->_ = $paramValue;
    $this->attrName = $attrValue;
  }
}

$soap->param = new MyParam("value", "attributeName");

I was only able to figure this out after using the wsdl2phpgenerator library to generate PHP classes from a WSDL containing data types needing attributes and saw this pattern being used.

like image 43
Matthew Gerrior Avatar answered Sep 21 '22 01:09

Matthew Gerrior