Is it possible to use PHP's SimpleXML functions to create an XML object from scratch? Looking through the function list, there's ways to import an existing XML string into an object that you can then manipulate, but if I just want to generate an XML object programmatically from scratch, what's the best way to do that?
I figured out that you can use simplexml_load_string() and pass in the root string that you want, and then you've got an object you can manipulate by adding children... although this seems like kind of a hack, since I have to actually hardcode some XML into the string before it can be loaded.
I've done it using the DOMDocument functions, although it's a little confusing because I'm not sure what the DOM has to do with creating a pure XML document... so maybe it's just badly named :-)
Sure you can. Eg.
<?php
$newsXML = new SimpleXMLElement("<news></news>");
$newsXML->addAttribute('newsPagePrefix', 'value goes here');
$newsIntro = $newsXML->addChild('content');
$newsIntro->addAttribute('type', 'latest');
Header('Content-type: text/xml');
echo $newsXML->asXML();
?>
Output
<?xml version="1.0"?>
<news newsPagePrefix="value goes here">
<content type="latest"/>
</news>
Have fun.
In PHP5, you should use the Document Object Model class instead.
Example:
$domDoc = new DOMDocument;
$rootElt = $domDoc->createElement('root');
$rootNode = $domDoc->appendChild($rootElt);
$subElt = $domDoc->createElement('foo');
$attr = $domDoc->createAttribute('ah');
$attrVal = $domDoc->createTextNode('OK');
$attr->appendChild($attrVal);
$subElt->appendChild($attr);
$subNode = $rootNode->appendChild($subElt);
$textNode = $domDoc->createTextNode('Wow, it works!');
$subNode->appendChild($textNode);
echo htmlentities($domDoc->saveXML());
Please see my answer here. As dreamwerx.myopenid.com points out, it is possible to do this with SimpleXML, but the DOM extension would be the better and more flexible way. Additionally there is a third way: using XMLWriter. It's much more simple to use than the DOM and therefore it's my preferred way of writing XML documents from scratch.
$w=new XMLWriter();
$w->openMemory();
$w->startDocument('1.0','UTF-8');
$w->startElement("root");
$w->writeAttribute("ah", "OK");
$w->text('Wow, it works!');
$w->endElement();
echo htmlentities($w->outputMemory(true));
By the way: DOM stands for Document Object Model; this is the standardized API into XML documents.
Related
I am learning SimpleXML in PHP. Then I am doing simple test with SimpleXMLElement(...), I dont get anything back. Let me explain. Here is XML file:
<?xml version="1.0" encoding="UTF-8"?>
<movies>
<movie>
<title>PHP: Behind the Parser</title>
<plot>
So, this language. It's like, a programming language. Or is it a
scripting language? All is revealed in this thrilling horror spoof
of a documentary.
</plot>
<great-lines>
<line>PHP solves all my web problems</line>
</great-lines>
<rating type="thumbs">7</rating>
<rating type="stars">5</rating>
</movie>
</movies>
And here is my php file:
<?php
$xml = simplexml_load_file('example.xml');
echo $xml->getName() . "<br>"; // prints "movies"
$movies = new SimpleXMLElement($xml);
echo $movies->getName() . "...<br>"; // doesnt print anything, not event dots
echo $movies->movie[0]->plot; // even this does not print anything
?>
Only output is:
movies
Please read the comments in php file. I am trying to print xml elements in exact same way after loading file and after doing new simpleXML object. Some how it prints only first echo command results. I searched many examples and could not make it work. Where is the mistake? It is big puzzle for me, but maybe a tiny one for you.
simplexml_load_file already returns your SimpleXMLElement object. Try this:
<?php
$xml = simplexml_load_file('example.xml');
echo $xml->getName() . "<br>";
echo $xml->movie[0]->plot . "<br>\n";
?>
change this line:
$movies = new SimpleXMLElement($xml);
to this:
$movies = new SimpleXMLElement($xml->asXML());
What you are trying to do doesn't make much sense, because you are trying to load the same XML twice:
// this loads the XML from a file, giving you a SimpleXMLElement object:
$xml = simplexml_load_file('example.xml');
// this line would do what? load the XML from the XML?
$movies = new SimpleXMLElement($xml);
There are two functions for loading XML in the SimpleXML extension, both return SimpleXMLElement objects:
simplexml_load_file - takes a filename, and loads the XML in that file; with the right PHP settings, you can also give it a URL, and it will load the XML straight from there
simplexml_load_string - takes a string of XML that you've already got from somewhere else, and loads that
The third way of getting a SimpleXMLElement is calling the class's constructor (i.e. writing new SimpleXMLElement). This can actually act like either of the above: by default, it expects a string of XML (like simplexml_load_string), but you can also set the 3rd parameter to true to say that it's a path or URL (like simplexml_load_file).
The result of all three of these methods is exactly the same, they're just different ways of getting there depending on what you currently have (and, to some extent, how you want your code to look).
As a side-note, there are two more functions which do take an object of XML you've already parsed: simplexml_import_dom and dom_import_simplexml. These are actually pretty cool, because the DOM is a standard, comprehensive, but rather fiddly and verbose way of acting on XML, whereas SimpleXML is, well, simple - and using these functions you can actually use both with very little penalty, because they just change the wrapper of the object without having to re-parse the underlying XML.
try this
<?php
$movies = simplexml_load_file('sample.xml');
foreach($movies as $key=>$val)
{
echo $val->title.'<br>';
echo $val->plot.'<br>';
echo $val->rating[0];
echo $val->rating[1];
}
?>
I'm currently trying to parse some data from a forum. Here is the code:
$xml = simplexml_load_file('https://forums.eveonline.com');
$names = $xml->xpath("html/body/div/div/form/div/div/div/div/div[*]/div/div/table//tr/td[#class='topicViews']");
foreach($names as $name)
{
echo $name . "<br/>";
}
Anyway, the problem is that I'm using google xpath extension to help me get the path, and I'm guessing that google is changing the html enough to make it not come up when i use my website to do this search. Is there some type of way I can make the host look at the site through google chrome so that it gets the right code? What would you suggest?
Thanks!
My suggestion is to always use DOMDocument as opposed to SimpleXML, since it's a much nicer interface to work with and makes tasks a lot more intuitive.
The following example shows you how to load the HTML into the DOMDocument object and query the DOM using XPath. All you really need to do is find all td elements with a class name of topicViews and this will output each of the nodeValue members found in the DOMNodeList returned by this XPath query.
/* Use internal libxml errors -- turn on in production, off for debugging */
libxml_use_internal_errors(true);
/* Createa a new DomDocument object */
$dom = new DomDocument;
/* Load the HTML */
$dom->loadHTMLFile("https://forums.eveonline.com");
/* Create a new XPath object */
$xpath = new DomXPath($dom);
/* Query all <td> nodes containing specified class name */
$nodes = $xpath->query("//td[#class='topicViews']");
/* Set HTTP response header to plain text for debugging output */
header("Content-type: text/plain");
/* Traverse the DOMNodeList object to output each DomNode's nodeValue */
foreach ($nodes as $i => $node) {
echo "Node($i): ", $node->nodeValue, "\n";
}
A double '/' will make xpath search. So if you would use the xpath '//table' you would get all tables.
You can also use this deeper in your xpath structure like 'html/body/div/div/form//table' to get all tables under xpath 'html/body/div/div/form'.
This way you can make your code a bit more resilient against changes in the html source.
I do suggest learning a little about xpath if you want to use it. Copy paste only gets you so far.
A simple explanation about the syntax can be found at w3schools.com/xml/xpath_syntax.asp
I want to extract all comments below a specific node within an XML document, using PHP. I have tried both the SimpleXML and DOMDocument methods, but I keep getting blank outputs. Is there a way to retrieve comments from within a document without having to resort to Regex?
SimpleXML cannot handle comments, but the DOM extension can. Here's how you can extract all the comments. You just have to adapt the XPath expression to target the node you want.
$doc = new DOMDocument;
$doc->loadXML(
'<doc>
<node><!-- First node --></node>
<node><!-- Second node --></node>
</doc>'
);
$xpath = new DOMXPath($doc);
foreach ($xpath->query('//comment()') as $comment)
{
var_dump($comment->textContent);
}
Do you have access to an XPath API ? XPath allows you to find comments using (e.g.)
//comment()
Use XMLReader. Comments can be easily detected/found, they are xml elements of type COMMENT.
For details see PHP documentation: The XMLReader class
Code example:
$reader = new XMLReader();
$reader->open('filename.xml');
while ($reader->read()){
if ($reader->nodeType == XMLReader::COMMENT) {
$comments[] = $reader->readOuterXml();
}
}
And in array $comments you will have all comments found in XML file.
If you are using a SAX event driven-parser, the parser should have an event for comments. For example, when using Expat you would implement a handler and set it using:
void XMLCALL
XML_SetCommentHandler(XML_Parser p,
XML_CommentHandler cmnt);
I have an url return an XML page result. When I use this command:
print_r(file($url));
Its done, but when I use command:
$doc = load($url);
after that I :
print_r($doc);
it out. Its print_r out nothing. I'm quite new in work with XML in PHP someone give advise, please!
Thank you for your attention!
I am not really sure what you trying to do but for parsing an xml file in PHP there two main ways: DOM
$doc = new DOMDocument();
$doc->loadXML(file_get_contents($url));
SimpleXML
$xml = new SimpleXMLElement(file_get_contents($xmlstr));
file_get_contents Reads entire file into a string
#deceze and RageZ:
I'm using load() to get its attribute like this
$url = 'web address return an XML result';
$xml = load($url);
$node1 = $xml->getElmentsByTagName('tagname');
$value = $node1->getAttribute('attribute1');
But I have an error $xml is not an object and I check out by print_r and I get nothing but with print_r(file($url)) its print out an array as I expect!
#Franz: May be I get an error tag in XML file but I could not fixed this just work with the result!
You could also unserialize the xml into a php array and use print_r(array). Take a look here: http://articles.sitepoint.com/article/xml-php-pear-xml_serializer/3#
You will need a PEAR package for this
In PHP, I am trying to validate an XML document using a DTD specified by my application - not by the externally fetched XML document. The validate method in the DOMDocument class seems to only validate using the DTD specified by the XML document itself, so this will not work.
Can this be done, and how, or do I have to translate my DTD to an XML schema so I can use the schemaValidate method?
(this seems to have been asked in Validate XML using a custom DTD in PHP but without correct answer, since the solution only relies on DTD speicified by the target XML)
Note: XML validation could be subject to the Billion Laughs attack, and similar DoS vectors.
This essentially does what rojoca mentioned in his comment:
<?php
$xml = <<<END
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE foo SYSTEM "foo.dtd">
<foo>
<bar>baz</bar>
</foo>
END;
$root = 'foo';
$old = new DOMDocument;
$old->loadXML($xml);
$creator = new DOMImplementation;
$doctype = $creator->createDocumentType($root, null, 'bar.dtd');
$new = $creator->createDocument(null, null, $doctype);
$new->encoding = "utf-8";
$oldNode = $old->getElementsByTagName($root)->item(0);
$newNode = $new->importNode($oldNode, true);
$new->appendChild($newNode);
$new->validate();
?>
This will validate the document against the bar.dtd.
You can't just call $new->loadXML(), because that would just set the DTD to the original, and the doctype property of a DOMDocument object is read-only, so you have to copy the root node (with everything in it) to a new DOM document.
I only just had a go with this myself, so I'm not entirely sure if this covers everything, but it definitely works for the XML in my example.
Of course, the quick-and-dirty solution would be to first get the XML as a string, search and replace the original DTD by your own DTD and then load it.
I think that's only possible with XSD, see:
http://php.net/manual/en/domdocument.schemavalidate#62032