Page 1 of 1

need to take SOAP headers out of a simplexml object

Posted: Mon Mar 29, 2010 11:31 am
by benward
Hi Guys,

I have been arguing with my computer all day. It just doesn't want to do as it's told!

essentially I have been able to build an xml file in the format that I want using simplexml and stick it in a soap header, send it to a server in a post array and get a response. This is a huge victory for me :) It is basically sending info of a motor insurance risk and getting a response of quote ref, premium, terms etc.

Code: Select all

$xmlToSend = '<?xml version="1.0" encoding="UTF-8" ?>' . "\n" .
			 '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SOAP-ENV:Body>' . "\n" .
			 substr($xml2->asXML(),22) . //XML body
			 '</SOAP-ENV:Body>' . "\n" .
			 '</SOAP-ENV:Envelope>';
I know this looks strange but I needed a soap envelope on the request so I took the <?xml version="1.0" encoding="UTF-8" ?> off of the results of the simplexml so that I could stick a soap header in between that and the xml data.

I'm then using CURL to bring the xml response into a variable:

Code: Select all

$ch = curl_init(SOURCE_URL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlToSend);
$xmlReceived = curl_exec($ch);
curl_close($ch);
Then I'm loading the results into a simpleXMLElement and attempting to display the quoted premium:

Code: Select all

$QResponse = new SimpleXMLElement($xmlReceived);

echo "<pre>Premium Returned:" . (string) $QResponse->GrossPremium . "\n";
This doesn't seem to work. It just returns blank. If I look at the $QResponse->asXML; I can see that the GrossPremium element is populated so I can only assume that the issue is to do with the SOAP header that is being returned. Is there any way of removing the SOAP header without searching the curled string for <root element> and </root element> and substringing everything in between?

I tried using PHP SOAP earlier but the WS im trying to connect to doesn't like the way the xml is formatted by the soapClient object.

Thanks for reading,
Ben

Re: need to take SOAP headers out of a simplexml object

Posted: Mon Mar 29, 2010 1:07 pm
by requinix
You don't need to trim it off. Just work around it.

What's the XML?

Re: need to take SOAP headers out of a simplexml object

Posted: Tue Mar 30, 2010 3:21 am
by benward
Request XML is:

Code: Select all

<?xml version="1.0" encoding="UTF-8" ?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SOAP-ENV:Body>
<QRequest><EffectiveDate>2010-3-30</EffectiveDate><AgentCode>38004W</AgentCode><PolicyType>S</PolicyType><CoverType>01</CoverType><TransactionType>1</TransactionType><ClassOfUse>A</ClassOfUse><DriverCode>1</DriverCode><ProtectedNCDFlag>Y</ProtectedNCDFlag><NCDYears>5</NCDYears><VoluntaryExcess>150</VoluntaryExcess><DiscretionaryLoading>0</DiscretionaryLoading><InstalmentsFlag>N</InstalmentsFlag><VehicleType>P</VehicleType><VehiclePostCode>GL2 8DJ</VehiclePostCode><VehicleABICode>52055502  </VehicleABICode><VehicleOwner>1</VehicleOwner><VehicleValue>4000</VehicleValue><VehicleYearOfMake>2005</VehicleYearOfMake><PurchasedDate>2008-07-20</PurchasedDate><AnnualMilage>5000</AnnualMilage><GarageCode>4</GarageCode><Modified>N</Modified><FortisReference>0000001A</FortisReference><DriverDetails><DriverNumber>1</DriverNumber><Proposer>Y</Proposer><Sex>M</Sex><DateOfBirth>1987-03-04</DateOfBirth><MaritalStatus>S</MaritalStatus><CommutesInVehicle>Y</CommutesInVehicle><AdjustedAge>0</AdjustedAge><LicenceType>F</LicenceType><LicenceDate>2004-11-03</LicenceDate><MainDriver>M</MainDriver><OwnsVehicle>Y</OwnsVehicle><KeepsVehicle>Y</KeepsVehicle><RelationToProposer>P</RelationToProposer><YearsResident>23</YearsResident><OccupationDetails><OccupationNumber>1</OccupationNumber><FullTime>Y</FullTime><OccupationABICode>084</OccupationABICode><EmployersABICode>077</EmployersABICode><EmploymentType>E</EmploymentType></OccupationDetails><DisabilityRecord><DisabilityNumber>1</DisabilityNumber><DisabilityCode/><DVLAAdvised>N</DVLAAdvised><Duration>0</Duration><Status/><Treatment/><Registered/><RestrictedLicence/></DisabilityRecord></DriverDetails></QRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Response XML is:

Code: Select all

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Header/><SOAP-ENV:Body><QResponse><DateGenerated>2010-03-30</DateGenerated><TimeGenerated>09.20.30</TimeGenerated><CompanyName>Fortis Insurance Ltd</CompanyName><QuoteReference>2010-03-30-09.20.28.574990      </QuoteReference><GrossPremium>1813.37</GrossPremium><NetPremium>1727.02</NetPremium><Endorsements/><Exclusions/><Sections><SectionCode>A</SectionCode><SectionDescription>Damage to the car.</SectionDescription><SectionCode>B</SectionCode><SectionDescription>Broken windscreen and window glass</SectionDescription><SectionCode>C</SectionCode><SectionDescription>Fire and theft</SectionDescription><SectionCode>D</SectionCode><SectionDescription>Personal accident</SectionDescription><SectionCode>E</SectionCode><SectionDescription>Medical expenses</SectionDescription><SectionCode>F</SectionCode><SectionDescription>Personal belongings</SectionDescription><SectionCode>G</SectionCode><SectionDescription>Liabilities to third parties</SectionDescription><SectionCode>H</SectionCode><SectionDescription>Using your car abroad</SectionDescription><SectionCode>I</SectionCode><SectionDescription>Spanish bail bond</SectionDescription><SectionCode>J</SectionCode><SectionDescription>No claim discount</SectionDescription><SectionCode>K</SectionCode><SectionDescription>No claim discount protection</SectionDescription><SectionCode>L</SectionCode><SectionDescription>Replacement locks</SectionDescription></Sections><Limitations/></QResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
Thanks.

Re: need to take SOAP headers out of a simplexml object

Posted: Tue Mar 30, 2010 3:33 am
by requinix
If you load the response into a SimpleXMLElement named $root then

Code: Select all

$root
	->children("http://schemas.xmlsoap.org/soap/envelope/")->Body
	->children("")->QResponse
will give you the <QResponse>. From there, navigate as normal.

Re: need to take SOAP headers out of a simplexml object

Posted: Tue Mar 30, 2010 4:50 am
by benward
Magic! It works :) Thanks!

Can you please explain why it works? Don't fully understand..

I'm guessing it looks for SOAP-ENV: values for the name of the element but I dont understand the ->children("")-> part?

Thanks

Re: need to take SOAP headers out of a simplexml object

Posted: Tue Mar 30, 2010 11:46 am
by requinix
The issue is namespaces: that's what the SOAP-ENV part in each tag deals with. I don't think I've made a good post about namespaces on this forum. Assuming you have a good understanding of how to use SimpleXML:

Every time you see a <foo:bar> tag you need to use namespaces. Unfortunately SimpleXML doesn't let you do

Code: Select all

$node->{"foo:bar"}
to get to the node. Instead to need a URI and the children and attributes methods.

The URI is given in an "xmlns:..." attribute somewhere. For example,

Code: Select all

<root xmlns="urn:com.example.namespaces" xmlns:foo="urn:com.example.namespaces.foo">
This defines two namespaces: the default (xmlns) and foo (xmlns:foo). Starting at <root> and applying to all child nodes (unless overridden with another xmlns or xmlns:foo),

Code: Select all

$root->children("urn:com.example.namespaces")
$root->children("urn:com.example.namespaces.foo")
Those two give the list of children in the default and foo namespaces respectively.

So if you have

Code: Select all

<?xml version="1.0" encoding="UTF-8" ?>
<SOAP-ENV:Envelope
	xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
	xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<SOAP-ENV:Body>
		<QRequest>
			<EffectiveDate>2010-3-30</EffectiveDate>
			<AgentCode>38004W</AgentCode>
			<PolicyType>S</PolicyType>
			<!-- ... -->
		</QRequest>
	</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
loaded into the $root variable (like with simplexml_load_string,

Code: Select all

/* the <SOAP-ENV:Envelope> */
$root // already have it

/* the <SOAP-ENV:Body> */
$root
	->children("http://schemas.xmlsoap.org/soap/envelope/") // switch to SOAP-ENV
	->Body // the name is what comes after the colon

/* the <QRequest> */
$root
	->children("http://schemas.xmlsoap.org/soap/envelope/") // switch to SOAP-ENV
	->Body // the name is what comes after the colon
	->children("") // switch to the default namespace
	->QRequest
(Note that the default URI for the default namespace is an empty string.)
Keep in mind that a namespace applies to the node itself, so if

Code: Select all

<QRequest xmlns="foo">
then

Code: Select all

/* the <QRequest> */
$root
	->children("http://schemas.xmlsoap.org/soap/envelope/") // switch to SOAP-ENV
	->Body // the name is what comes after the colon
	->children("") // switch to the default namespace
	->QRequest