simplexml get node value without typecasting - php

Is there any way to get a node value from a simplexml object without casting it?
$amount = (int)$item->amount;
This is not very beautiful in my opinion, I'm searching for a cleaner way but did not find anything so far!
//wouldn't this be nice?
$amount = $item->amount->getValue();
Thanks in advance.

No. SimpleXml only exposes a very limited API to work with nodes. It's implicit API is considered a feature. If you need more control over the nodes, use DOM:
$sxe = simplexml_load_string(
'<root><item><amount>10</amount></item></root>'
);
$node = dom_import_simplexml($sxe->item->amount);
var_dump($node->nodeValue); // string(2) "10"
The dom_import_simplexml function will convert the node from a SimpleXmlElement to a DOMElement, so you are still casting behind the scenes somehow. You no longer need to typecast explicitly though, when fetching the content from the DOMElement.
On a sidenote: personally, I find DOM superior to SimpleXml and I'd suggest not to use SimpleXml but DOM right away. If you want to familiarize yourself with it, have a look at some of my previous answers about using DOM.

Getting the value of a node without having to typecast it? Sure thing! :3
class SimplerXMLElement extends SimpleXMLElement
{
public function getValue()
{
return (string) $this;
}
}
Now you just have to use simplexml_load_string()'s second parameter to get even simpler XML elements!
More seriously though, a node is a node. If you need to use the value of a node as a string, integer and what not, it will involve typecasting at one point or another, whether you do it personally or not.

But it is the safest :). You could create a function to recursively convert the object in an array. Way back when I first starte working with XML in PHP, I remember reaching the conclusion that type casting is just the best method :)

What about xpath way?
$amount = $item->amount->xpath('text()');

Related

PHP SimpleXMLElement: can I get innertext with saveXML(), or outerHTML with (string)?

I have an application where the user writes XPath queries to use as source data from a given document. Sometimes they need just the contents of an element, sometimes they need the whole element itself. To my understanding they should be able to specify either text() or node() at the end of their query to choose which behavior.
But it seems like the way I get a string out of the SimpleXMLElement determines the behavior, regardless of the query.
When I cast the query to (string), it ALWAYS only returns inner XML.
(string) $xml->xpath('//document/head/Keywords')[0] ===
(string) $xml->xpath('//document/head/Keywords/node()')[0] ===
(string) $xml->xpath('//document/head/Keywords/text()')[0] ===
'17';
If I use ->saveXML(), it ALWAYS returns the entire tag.
$xml->xpath('//document/head/Keywords')[0]->asXML() ===
$xml->xpath('//document/head/Keywords/node()')[0]->asXML() ===
$xml->xpath('//document/head/Keywords/text()')[0]->asXML() ===
'<Keywords topic="611x27keqj">17</Keywords>';
Is there a single way that I can get a string, which allows my users to specify inner vs outer XML as a part of their XPath query?
The SimpleXML xpath() method always returns SimpleXMLElement objects representing either an element or an attribute, never text. The methods you show in the question are the correct way to use that object to get text content or full XML.
If you want richer (but less simple) XPath functionality, you will have to use the DOM, and specifically the DOMXPath class. Note that you can freely mix SimpleXML and DOM using simplexml_import_dom and dom_import_simplexml; the internal representation is the same, so you can switch between the two "wrappers" with minimal cost.

From SimpleXMLElement to SimpleXMLIterator

I have a nice function that converts a (simple, duh) xml to an array, and to do so it uses the SimpleXmlIterator class.
It works quite well, but I'd like to make it accept not only xml source strings or SimpleXmlIterator objects, but SimpleXmlElements too since I use them way more often that iterators (that I use just in that function, actually).
What I did so far was
$iter = new SimpleXmlIterator($xml->asXML());
but to me it's like passing through Tokyo to get from Paris to London. After all, SimpleXmlIterator extends SimpleXmlElement, so is there a better way to convert a SimpleXmlElement object to a SimpleXmlIterator?
You can use SimpleXMLIterator at the beginning by setting the second parameter in http://php.net/manual/de/function.simplexml-load-file.php
$yourSimpleXMLIteratorObject = simplexml_load_file("your_file.xml","SimpleXMLIterator");
all Objects resulting, are now SimpleXMLIterator-Objects
Why not parse all your XML into SimpleXMLIterator objects to begin with if you plan to use them as such?
As far as I know there is not an easy way to convert/cast from SimpleXMLElement to SimpleXMLIterator. These classes simply wrap a libxml resource so you would think there would be a way to obtain a resource handle and pass it into SimpleXML* constructors, but if there is it's news to me.

Force SimpleXML node to always be an array, even if just 1 element in it

Let's say I want a Place to have a list of phone numbers.
Some places will have 1 phone number, and some will have more than one. Others won't have one at all.
The problem is that this:
$xml->addChild('phone_number','555.555.5555');
creates a non-iterable phone_number text node:
$response->xml->phone_number;
But this:
$xml->addChild('phone_number','555.555.5555');
$xml->addChild('phone_number','555.555.5556');
creates an iterable array of phone_number:
$response->xml->phone_number[0];
$response->xml->phone_number[1];
This puts an unnecessary burden on the client. They have to detect if the result is iterable or not, and modify their code accordingly.
It would be MUCH better if I could always send back an interable array, even if it had 0 or 1 items in it... but I haven't been able to find any documentation on how to do this. In Perl I believe it's called "forcearray", but I haven't found an equivalent for PHP, which is waht i need.
Just don't use this fancy, magic interface ($obj->xml->phone_number[x]) and use SimpleXMLElement::children() method which always returns iterable object.
you should consider this
<phone_numbers>
<phone_number>555.555.5555</phone_number>
</phone_numbers>
this is more flexible
beside the children() method, you can also consider xpath
which always will yields an array to be return
example
$xml = <<<XML
<person>
<phone_numbers>
<phone_number>555.555.5555</phone_number>
</phone_numbers>
</person>
XML;
$obj = simplexml_load_string($xml);
$tels = $obj->xpath("//phone_numbers/*");
/* even more simple */
$tels = $obj->phone_numbers->children();

which is better expression to make string into xml object[php]?

which is better expression to make string into xml object[php]?
$xml = new SimpleXMLElement($xml_string);
vs
$xml = simplexml_load_string($xml_string);
same?
They're basically identical. SimpleXMLElement::__construct accepts data strings or file paths, if you set the appropriate options. simplexml_load_file and simplexml_load_string are basically convenience function wrappers that amount to the same thing as new SimpleXMLElement() with the correct options set.
They're essentially the same. Which one you use is based on personal taste. I'd go for the first. The simplexml_load_string function will create an object too, which makes it essentially an alias for the constructor.
The simplexml_load_string lets you specify the class of which you want to get the object of. If you call the simplexml_load_string without specifying the class as second parameter, then it automatically gives you the object of SimpleXMLElement.
So, in the way you are running it, they both will give you same results.

How-to Serialize/Unserialize a SimpleXML Object?

I've seen a few creative solutions for dealing with serialized SPL objects but am looking for more options (or elaborations). I store nested serialized objects - of which, one is SimpleXML - in the database, only to be un-serialized later. This obviously cause some problems.
$s = new SimpleXmlElement('<foo>bar</foo>');
$ss = serialize($s);
$su = unserialize($ss);
// Warning: unserialize() [function.unserialize]: Node no longer exists...
Does anyone have any insight into highly-reliable methods for dealing with serialized SPL objects? __sleep()/__wakeup() overrides? Cast-to-stdClass? Cast-to-string, then serialize?
Any help is appreciated.
[Edit: The scope and variation of these XML schemas are too varied to map with an ORM. They are, at their most fundamental level, arbitrary payloads in stateful processes, triggered within restful APIs.]
Questions on appropriateness notwithstanding, you can turn it back into XML like this:
$xml = $simpleXmlElem->asXML();
And then, when you pull it from the database:
$simpleXmlElem = simplexml_load_string($xml);
As for whether it's appropriate to just serialize large chunks of XML, it can be true that putting XML into the database removes much of the advantage of using a relational system, but you do have the advantage of being able to accommodate an arbitrary workload. If some fields are universal, and/or you gain benefit from normalizing them properly (e.g. you want to select based on those fields), move those into normalized columns.
More clear and OOP.
namespace MyApp;
class SimpleXMLElement extends \SimpleXMLElement
{
public function arrayToXml($array = array())
{
array_walk_recursive($array, array(&$this, 'addChildInverted'));
return $this;
}
public function addChildInverted($name ,$value)
{
parent::addChild($value,$name);
}
}
and calling
$xml = new \MyApp\SimpleXMLElement('<resultado/>');
echo $xml->arrayToXml($app->getReturnedValue())->asXML();
Wouldn't simply rendering and storing the XML be the best way to serialize any object that represents an XML structure?
What are you trying to do with the serialized data that might prevent this?
edit:
Also,
I store nested serialized objects [...] in the database, only to be un-serialized later
Why are you storing PHP serialized data in a database? There are many better ways to store objects in a database.

Categories