SimpleXMLElement is removing attributes (php 7.2) - php

Does anybody know why SimpleXMLElement is removing the attributes in my XML??
I have XML data that looks like this (note the translation "language" attribute):
<events>
<event id="d8f17143-0c67-48aa-a7f1-003a5ddbd28f">
<details>
<names>
<translation language="en">English title</translation>
<translation language="de">German title</translation>
</names>
</details>
</event>
</events>
I run it through SimpleXmlElement like so:
$xmlConvertedData = new \SimpleXMLElement($xml);
I dump out the data and it looks like so:
object(SimpleXMLElement)#958 (2) {
["#attributes"]=>
array(1) {
["Index"]=>
string(1) "1"
}
["Events"]=>
object(SimpleXMLElement)#956 (1) {
["Event"]=>
array(1) {
[0]=>
object(SimpleXMLElement)#959 (1) {
["Details"]=>
object(SimpleXMLElement)#826 (13) {
["Names"]=>
object(SimpleXMLElement)#834 (1) {
["Translation"]=>
array(2) {
[0]=>
string(32) "English title"
[1]=>
string(33) "German title"
}
}
}
}
}
}
}
...notice "translation" no longer has a "language" attribute, just an ID number 0 and 1. I need to know the attribute value because the XML does not always show the same language first.
(I edited the shortened the sample code to one record, so please ignore the #958 part)

Do not use any of the print_r() or var_dump() on a SimpleXML object, this will abbreviate the output as there is potentially a lot of it. If you want to check the document loaded use asXML()...
echo $xmlConvertedData->asXML();
or to output the one elements language...
echo $xmlConvertedData->event[0]->details->names->translation['language'];
( You also need to correct the last element of the sample - </events>)

Related

PHP & XPath Query

I have an XML file similar to the text below:
<?xml version="1.0" standalone="yes"?>
<Calendar xmlns="urn:AvmInterchangeSchema-Calendario-1.0">
<Date>
<Day>12/04/2017</Day>
<TypesDay>
<Type>Test 1</Type>
<Type>Test 2</Type>
<Type>Test 3</Type>
</TypesDay>
</Date>
</Calendar>
And I am using this Xpath to select the nodes:
$xml = simplexml_load_file("file.xml");
$response = $xml->xpath('//*[text()="'.date("d/m/Y").'"]');
How can I take the "TypesDay" entries if that condition is met?
I hope not to create duplicates ... I'm going crazy for hours and it will definitely be a trivial thing.
There are a few approaches to do this. First of all, you should register the namespace:
$xml->registerXPathNamespace('x', 'urn:AvmInterchangeSchema-Calendario-1.0');
I'm assuming that your node name with the value 12/04/2017 could change.
First
Find a node called TypesDay inside the named namespace x that the parent node has a child node with value 12/04/2017
$response = $xml->xpath('//*[*[text()="'.date("d/m/Y").'"]]/x:TypesDay');
Second
Find a node called TypesDay inside the named namespace x that is sibling of node with value 12/04/2017
$response = $xml->xpath('//*[text()="'.date("d/m/Y").'"]/following-sibling::x:TypesDay');
The result for both is:
array(1) {
[0]=>
object(SimpleXMLElement)#2 (1) {
["Type"]=>
array(3) {
[0]=>
string(6) "Test 1"
[1]=>
string(6) "Test 2"
[2]=>
string(6) "Test 3"
}
}
}
After all, if you want only the entries, just add the next level /x:Type:
$response = $xml->xpath('//*[*[text()="'.date("d/m/Y").'"]]/x:TypesDay/x:Type');
Or:
$response = $xml->xpath('//*[text()="'.date("d/m/Y").'"]/following-sibling::x:TypesDay/x:Type');
Result:
array(3) {
[0]=>
object(SimpleXMLElement)#3 (1) {
[0]=>
string(6) "Test 1"
}
[1]=>
object(SimpleXMLElement)#4 (1) {
[0]=>
string(6) "Test 2"
}
[2]=>
object(SimpleXMLElement)#5 (1) {
[0]=>
string(6) "Test 3"
}
}

PHP SoapClient dropping fields?

I'm using the php soap client functionality to invoke a call into Salesforce….and running into something weird that I can’ t figure out. It seems a field is getting dropped within the PHP call...I can see it in the getLastReponse, but its not passed along into the object from the query() call. I don't believe this is an issue with Salesforce or the wsdl at all.
The query passed into Salesforce:
$xml_array_query[‘query’]=“select Id, LastName, FirstName, Username, LMS_ID__c,
UserRole.Name, Name from User where IsActive=true and Name like '%Bunting%'”;
Invoke the client (assume already authenticated ok)
$client = new SoapClient($wsdl, array('trace' => $trace, 'exceptions' => $exceptions, 'location' => $serverurl));
$header = new SoapHeader('urn:enterprise.soap.sforce.com', 'SessionHeader', array ('sessionId'=>$sessionid));
$client->__setSoapHeaders($header);
$response = $client->query($xml_array_query['query']);
Then output the last response (formatting out the XML for viewing purposes):
echo 'Last response: '. $client->__getLastResponse()."\n";
Last response: <?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:enterprise.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
<soapenv:Header>
<LimitInfoHeader>
<limitInfo>
<current>43227</current>
<limit>895000</limit>
<type>API REQUESTS</type>
</limitInfo>
</LimitInfoHeader>
</soapenv:Header>
<soapenv:Body>
<queryResponse>
<result>
<done>true</done>
<queryLocator xsi:nil="true"/>
<records xsi:type="sf:User">
<sf:Id>005d0x</sf:Id>
<sf:FirstName>Susie</sf:FirstName>
<sf:LMS_ID__c>BuntingSusieCC</sf:LMS_ID__c> <------- KEY FIELD!
<sf:LastName>Bunting</sf:LastName>
<sf:Name>Susie Bunting</sf:Name>
<sf:UserRole xsi:type="sf:UserRole">
<sf:Id xsi:nil="true"/>
<sf:Name>Agent Role</sf:Name>
</sf:UserRole>
<sf:Username>susie.bunting</sf:Username>
</records>
<size>1</size>
</result>
</queryResponse>
</soapenv:Body>
</soapenv:Envelope>
So, I know the query returned the LMS_ID__c field from the server...
But it doesn't look like the field and value are getting passed into the object...
var_dump($response);
object(stdClass)#6 (1) {
["result"]=>
object(stdClass)#7 (4) {
["done"]=>
bool(true)
["queryLocator"]=>
NULL
["records"]=>
array(2) {
[0]=>
object(stdClass)#8 (6) {
["Id"]=>
string(18) "005d0x"
["FirstName"]=>
string(6) "Susie"
["LastName"]=>
string(7) "Bunting"
["Name"]=>
string(14) "Susie Bunting"
["UserRole"]=>
object(stdClass)#9 (2) {
["Id"]=>
NULL
["Name"]=>
string(18) "Agent Role"
}
["Username"]=>
string(27) "susie.bunting"
}
}
["size"]=>
int(1)
}
}
Where does the LMS_ID_c field go??? Its in the getLastReponse output, but when viewing the object from the query(), its not there.
It was my understanding that the soapclient would parse the XML to create the object and elements, is that not true?
Is this some quirk because of the underscores in the field name, and the instance of a double-underscore? I wouldn't think so, but I can't figured this out.
Any help appreciated...
Turns out the issue was with the caching of the wsdl file apparently. I added this to the head of my script, and resolve the issue:
ini_set('soap.wsdl_cache_enabled',0);

Getting paragraph in XML DOM (PHP)

I'm trying to create database of new releases from boomkat.com RSS feed. Feed is located here:
link
Now, I'm having issues with selection of stuff inside paragraph tags.
One paragraph in RSS feed looks like this:
<p>GOAT<br/>World Music<br/>ROCKET RECORDINGS<br/>INDIE / ROCK / ALTERNATIVE<br/>MP3 Release</p>
What I did so far is this:
<?php
$dom = new DOMDocument;
$dom->validateOnParse = true;
$dom->load("http://feeds.boomkat.com/boomkat_downloads_just_arrived");
$content = $dom->getElementsByTagName('content');
foreach ($content as $result) {
echo $result->nodeValue, PHP_EOL;
}
?>
But that gives me whole feed. Writing 'p' in getElementsByTagName doesn't work.
I would suggest using DOMDocument::loadHTMLFile() method instead of DOMDocument::load() (as load() is strictly for reading XML, not HTML).
The reason why you're getting the whole document, is because you are querying the entire document for a element called "content". There is no such HTML element. Instead you should be using
$dom->getElementsByTagName('p');
This will grab all the tags in the HTML document, and then you can loop over that. The primary reason why querying tags with "p" doesn't work, is because you need to load the document as strict HTML, and not use the default XML.
OK, well I don't understand why you're having problems, but I just tried what I suggested with the URL you provided, and got a proper print out of all the text of each <p> tag.
Here's the code:
$doc = new DOMDocument();
$doc->loadHTMLFile("http://boomkat.com/downloads/601228-goat-world-music");
$content = $doc->getElementsByTagName("p");
foreach($content as $element) {
Util::debug($element->textContent); // helper method similar to PHP's var_dump()
}
Here's the results I was able to print to the screen:
string(91) "Residual Echoes have come up with a really rather lovely disc of psychedelic folk goodness."
string(8) "MAMMATUS"
string(8) "Mammatus"
string(17) "ROCKET RECORDINGS"
string(45) "MP3 Download // £2.95FLAC Download // £3.95"
string(0) ""
string(19) "SERPENTINA SATELITE"
string(16) "Mecanica Celeste"
string(17) "ROCKET RECORDINGS"
string(45) "MP3 Download // £3.95FLAC Download // £4.95"
string(0) ""
string(12) "SUNCOIL SECT"
string(25) "One Note Obscures Another"
string(17) "ROCKET RECORDINGS"
string(45) "MP3 Download // £6.99FLAC Download // £7.99"
string(0) ""
string(16) "TEETH OF THE SEA"
string(10) "Hypnoticon"
string(17) "ROCKET RECORDINGS"
string(45) "MP3 Download // £2.50FLAC Download // £3.50"
string(52) "Proggy kosmiche rock from London's Teeth Of The Sea."
string(16) "TEETH OF THE SEA"
string(21) "Orphaned By the Ocean"
string(17) "ROCKET RECORDINGS"
string(45) "MP3 Download // £5.99FLAC Download // £6.99"
Was this something you were doing in the code?

PHP xpath result as array of strings

I have seen several similar questions, but not found the exact answer I'm looking for. It may be that the functionality I am looking for does not exist.
If I do an xpath query that results in an array of objects, but each object only holds one value, a string, I'd like to quickly convert that into an array of strings. Obviously I can do a foreach on the object and push the string value onto a new array, but if there is a built in function I'm not thinking of, please let me know.
example:
array(3) {
[0]=>
object(SimpleXMLElement)#24 (1) {
[0]=>
string(20) "Network Media Player"
}
[1]=>
object(SimpleXMLElement)#25 (1) {
[0]=>
string(12) "Music Player"
}
[2]=>
object(SimpleXMLElement)#26 (1) {
[0]=>
string(8) "Juke Box"
}
}
I'd like that to become
array('Network Media Player','Music Player','Juke Box')
Here's my test :
<pre><?php
$xml = "<data>
<item>
<value>Network Media Player</value>
</item>
<item>
<value>Music Player</value>
</item>
<item>
<value>Jukebox Player</value>
</item>
</data>";
$sx = simplexml_load_string($xml);
print_r($sx);
print_r(explode("|",implode("|",$sx->xpath("//data/item/value"))));
?></pre>
and here's the result : http://codepad.org/ZkaWpzMc

Using PHP to manage GMail Mail Filter XML Files

Since GMail made it possible to import and export the mail filters, I'd like to manage the XML files that are exported from GMail using a PHP script, as there are some known issues with the number of characters in the search filters.
I've found the simplexml_load_file function in PHP, and have performed var_dump() against the performed export, but I now don't seem to be able to access the apps namespace within the generated XML file.
Following various pages within the PHP manual, I created this very simple script to read the XML out so I can start to process out the filters I've already created. Unfortunately, it seems to be missing the key parts!
<pre><?php
if(file_exists("mailfilters.xml")) {
$xml = simplexml_load_file("mailfilters.xml");
$namespaces = $xml->getNamespaces(true);
foreach ($namespaces as $prefix => $ns) {
$xml->registerXPathNamespace($prefix, $ns);
}
var_dump($xml);
} else {
die("Failed to open filter file");
}
?></pre>
This returns this data (extract)
["entry"]=>
array(268) {
[0]=>
object(SimpleXMLElement)#3 (5) {
["category"]=>
object(SimpleXMLElement)#271 (1) {
["#attributes"]=>
array(1) {
["term"]=>
string(6) "filter"
}
}
["title"]=>
string(11) "Mail Filter"
["id"]=>
string(45) "tag:mail.google.com,2008:filter:1284991916868"
["updated"]=>
string(20) "2010-10-28T11:59:31Z"
["content"]=>
object(SimpleXMLElement)#272 (0) {
}
}
[1]=>
object(SimpleXMLElement)#4 (5) {
["category"]=>
object(SimpleXMLElement)#272 (1) {
["#attributes"]=>
array(1) {
["term"]=>
string(6) "filter"
}
}
["title"]=>
string(11) "Mail Filter"
["id"]=>
string(45) "tag:mail.google.com,2008:filter:1284991925003"
["updated"]=>
string(20) "2010-10-28T11:59:31Z"
["content"]=>
object(SimpleXMLElement)#271 (0) {
}
}
Here's an extract from the XML file I have downloaded today, used to create the above output:
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>
<title>Mail Filters</title>
<id>tag:mail.google.com,2008:filters:1284991916868,...,1287734777820</id>
<updated>2010-10-28T11:59:31Z</updated>
<author>
<name>My Name</name>
<email>my#email.addr.es</email>
</author>
<entry>
<category term='filter'></category>
<title>Mail Filter</title>
<id>tag:mail.google.com,2008:filter:1284991916868</id>
<updated>2010-10-28T11:59:31Z</updated>
<content></content>
<apps:property name='from' value='an#email.addr.es'/>
<apps:property name='shouldArchive' value='true'/>
<apps:property name='shouldTrash' value='true'/>
</entry>
<entry>
<category term='filter'></category>
<title>Mail Filter</title>
<id>tag:mail.google.com,2008:filter:1284993579743</id>
<updated>2010-10-28T11:59:31Z</updated>
<content></content>
<apps:property name='subject' value='Some Relevant Subject'/>
<apps:property name='label' value='MyCoolLabel'/>
<apps:property name='shouldArchive' value='true'/>
</entry>
Other "apps:property" verbs include:
<apps:property name='hasTheWord' value=''/>
<apps:property name='shouldAlwaysMarkAsImportant' value=''/>
<apps:property name='doesNotHaveTheWord' value=''/>
I recently had a similar "problem". I was trying to get the email signature settings from Google Apps. This is what the XML Response from Google looks like:
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>
<id>https://apps-apis.google.com/a/feeds/emailsettings/2.0/domain.com/user/signature</id>
<updated>2012-08-31T15:22:03.067Z</updated>
<link rel='self' type='application/atom+xml' href='https://apps-apis.google.com/a/feeds/emailsettings/2.0/domain.com/user/signature?xoauth_requestor_id=user#domain.com'/>
<link rel='edit' type='application/atom+xml' href='https://apps-apis.google.com/a/feeds/emailsettings/2.0/domain.com/user/signature?xoauth_requestor_id=user#domain.com'/>
<apps:property name='signature' value='Here goes the email signature...'/>
</entry>
I was not able the get the attributes out of apps:property using the SimpleXMLElement. This is how i finally solved it:
// create SimpleXMLElement with XML input as string
$xml = new SimpleXMLElement($feed);
// get namespace from XML
$namespaces = $xml->getNamespaces(true);
// get children (using namespace) and attributes
$appsProperty = $xml->children($namespaces['apps'])->attributes();
// attributes are now stored in array $appsProperty and can be accessed
echo "Name: ".$appsProperty['name'];
echo "Signature: ".$appsProperty['value'];
Jon,
Full marks for linking this from Facebook, I'd never have seen it otherwise and I was doing EXACTLY this yesterday, for a google service!! I think using XPath is a bit of a red herring, let me show you how to access the elements and hopefully it will give you enough information for what you want to do.
You're on the right tracks with $xml->getNamespaces() - but don't iterate there, you need to use the namespace you want on the parent element you want. Firstly, you need to work only on the entry elements, since these are the majority, you might as well loop and check if you have the right one:
foreach($xml as $tag) {
if($tag->getName() == "entry") {
// awesome, do Cool Stuff (TM) here
}
}
Now, you have the element in the $tag variable, and you want the apps: namespaced child elements. So do this:
$tag->getChildren($namespaces['apps']);
When you iterate over that collection, you will see the info you wanted.
HTH,
Lorna

Categories