XML Pretty Printer in PHP5
Today I was working with debugging some XML, and needed a way to make the XML more readable. The following is a little function that should do the job for some simple XML, and you can probably tweak it to your needs. It uses PHP5's SimpleXMLElement object to parse and return the XML, then indents it in a logical way.
Comments
Thank you for the cute function, it saved me some time. For very linear responses (ie, no newlines) give this a shot:
1. Replace the explode's separator with '><' 2. Replace the join's separator with "> <"
In response to the above poster's comment:
Good start, but nothing seems to get indented like that. Maybe this is a better fix...
Replace line 16 (the line beginning with "$xml_lines") with the following:
$xml_lines = explode(" ", str_replace("><", "> <", $xml_obj->asXML()));
That's it =)
oops, line 12, not 16. And imagine backslash-Ns where those line breaks are (either will work).
After much fighting... I have included both the improvements from the comments above, and a trick that allows your xml pretty print function to work when xml is included in other xml which is what happens when there is an XML arguement to a soap call.
This makes your function work well against the results of SoapClient::__getLastResponse and SoapClient::__getLastRequest
This will save me a lot of time being able to directly read debug output from __getLastResponse and __getLastRequest
May this help the next googler.
function xml_pretty_printer($xml, $html_output=FALSE) { $xml_obj = new SimpleXMLElement($xml); $xml_lines = explode("\n", str_replace("><",">\n<",$xml_obj->asXML())); $indent_level = 0; $new_xml_lines = array(); foreach ($xml_lines as $xml_line) { if (preg_match('#^(<[a-z0-9_:-]+((s+[a-z0-9_:-]+="[^"]+")*)?>.*]+>)|(<[a-z0-9_:-]+((s+[a-z0-9_:-]+="[^"]+")*)?s*/s*>)#i', ltrim($xml_line))) { $new_line = str_pad('', $indent_level*4) . ltrim($xml_line); $new_xml_lines[] = $new_line; } elseif (preg_match('#^<[a-z0-9_:-]+((s+[a-z0-9_:-]+="[^"]+")*)?>#i', ltrim($xml_line))) {
$new_line = str_pad('', $indent_level*4) . ltrim($xml_line); $indent_level++; $new_xml_lines[] = $new_line; } elseif (preg_match('#/]+>#i', $xml_line)) { $indent_level--; if (trim($new_xml_lines[sizeof($new_xml_lines)-1]) == trim(str_replace("/", "", $xml_line))) { $new_xml_lines[sizeof($new_xml_lines)-1] .= $xml_line; } else { $new_line = str_pad('', $indent_level*4) . $xml_line; $new_xml_lines[] = $new_line; } } else { $new_line = str_pad('', $indent_level*4) . $xml_line; $new_xml_lines[] = $new_line; } } $xml = join("\n", $new_xml_lines); return ($html_output) ? '
' . $this->xmlspecialchars($xml) . '
' : $xml; }
function xmlspecialchars($text) { return str_replace(''', ''', htmlspecialchars($text, ENT_QUOTES, 'UTF-8',false)); }
Hey Fred,
In your last example, the whitespace tokens in the regexes are missing backslashes, resulting in indenting errors where entities have attributes. Replace 's' with 's' in those, and it's all good. :-)
Aaarrrggghhh... looks like backslashes are being stripped from posts (twice?), resulting in backslashes being removed from the comments.
Thanks - I've been helped! Work's great after touching up the regex's.
Read something more recent.
Statements and opinions expressed in this blog and any comments made are the private opinions of the respective poster, and, as such, iMarc LLC is neither responsible nor liable for such content.
Visitors
Nice work man, pretty easy to tweak too. I got it just right to my job.
Cheers.