PHP/XML - Problem with default namespaces when append fragment - php

I have the original file and I want to append fragment in security tag.
<!-- Original File -->
<example>
<header>
<facticeA>123</facticeA>
<facticeB>456</facticeB>
</header>
<body>
<facticeC>789</facticeC>
</body>
<security></security>
</example>
<!-- ------------ -->
<!-- Original Fragment -->
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_eb0b47cc-d4b0-44ba-a08c-90047e3a8b03" IssueInstant="2022-07-18T14:08:46.138Z" Version="2.0">
<saml2:Issuer></saml2:Issuer>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">XXXXXXXX</Signature>
</saml2:Assertion>
<!-- ----------------- -->
I use "createDocumentFragment()" and "appendXml()" PHP functions
And I have this result.
<!-- Final File -->
<example>
<header>
<facticeA>123</facticeA>
<facticeB>456</facticeB>
</header>
<body>
<facticeC>789</facticeC>
</body>
<security>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:default="http://www.w3.org/2000/09/xmldsig#" ID="_eb0b47cc-d4b0-44ba-a08c-90047e3a8b03" IssueInstant="2022-07-18T14:08:46.138Z" Version="2.0">
<saml2:Issuer/>
<default:Signature xmlns="http://www.w3.org/2000/09/xmldsig#">XXXXXXXX</default:Signature>
</saml2:Assertion>
</security>
</example>
<!-- ---------- -->
The inserted fragment is not the same than original fragment.
"Signature" tag become "default:Signature" tag. And the namespace "xmldsig" present in Signature tag is append to Assertion tag with word "default"
If I delete namespace "xmldsig" in the Signature tag I have no problem.
The inserted fragment is the same than original fragment.

This seems to be a bug in the node import. A possible workaround is to define a prefix for this namespace on the document element.
$document = new DOMDocument();
$document->preserveWhiteSpace = false;
$document->loadXML($exampleXML);
$xpath = new DOMXpath($document);
$document->documentElement->setAttributeNS(
'http://www.w3.org/2000/xmlns/', 'xmlns:sig', 'http://www.w3.org/2000/09/xmldsig#'
);
$fragment = $document->createDocumentFragment();
$fragment->appendXML($samlXML);
foreach ($xpath->evaluate('(//security)[1]') as $security) {
$security->appendChild($fragment);
}
$document->formatOutput = true;
echo $document->saveXML();
Output:
<?xml version="1.0"?>
<example xmlns:sig="http://www.w3.org/2000/09/xmldsig#">
<header>
<facticeA>123</facticeA>
<facticeB>456</facticeB>
</header>
<body>
<facticeC>789</facticeC>
</body>
<security>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_eb0b47cc-d4b0-44ba-a08c-90047e3a8b03" IssueInstant="2022-07-18T14:08:46.138Z" Version="2.0">
<saml2:Issuer/>
<sig:Signature xmlns="http://www.w3.org/2000/09/xmldsig#">XXXXXXXX</sig:Signature>
</saml2:Assertion>
</security>
</example>
Interesting enough, if you create the nodes using DOM methods it works correctly:
$document = new DOMDocument();
$document->preserveWhiteSpace = false;
$document->loadXML($exampleXML);
$xpath = new DOMXpath($document);
foreach ($xpath->evaluate('(//security)[1]') as $security) {
$security->appendChild(
$saml = $document->createElementNS('urn:oasis:names:tc:SAML:2.0:assertion', 'saml2:Assertion')
);
$saml->setAttribute('ID', '_eb0b47cc-d4b0-44ba-a08c-90047e3a8b03');
$saml->appendChild(
$document->createElementNS('urn:oasis:names:tc:SAML:2.0:assertion', 'saml2:Issuer')
);
$saml->appendChild(
$signature = $document->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'Signature')
);
$signature->textContent = 'XXXXXXXX';
}
$document->formatOutput = true;
echo $document->saveXML();
Output:
<?xml version="1.0"?>
<example>
<header>
<facticeA>123</facticeA>
<facticeB>456</facticeB>
</header>
<body>
<facticeC>789</facticeC>
</body>
<security>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_eb0b47cc-d4b0-44ba-a08c-90047e3a8b03">
<saml2:Issuer/>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">XXXXXXXX</Signature>
</saml2:Assertion>
</security>
</example>
You could create a recursive function the recreates the nodes from the fragment.

Related

Using xpath to query XML in PHP

I am using xpath for the first time to query an xml document for a value and return the corresponding name value. I have been reading numerous different sources on the topic but I can't seem to get it to output correctly. Here is my XML:
<?xml version="1.0" encoding="utf-8"?>
<books>
<book>
<name>Genesis</name>
<abrv>GEN</abrv>
<title>The First Book of Moses</title>
</book>
<book>
<name>Exodus</name>
<abrv>EXO</abrv>
<title>The Second Book of Moses</title>
</book>
<book>
<name>Leviticus</name>
<abrv>LEV</abrv>
<title>The Third Book of Moses</title>
</book>
</books>
Here is my PHP/HTML:
<?php $root = $_SERVER['DOCUMENT_ROOT'] . "/";
$bookID = 'GEN';
$xml = simplexml_load_file( $root . 'assets/xml/books.xml' ) or die( "No Page Data Found" );
$bookName = $xml->xpath( '//book[abrv="'.$bookID.'"]/name' );
?>
<article id="verseOfDay">
<div class="container">
<h2>verse of the day</h2>
<h6><?php echo $bookName; ?></h6> // Line 19
<p></p>
</div>
</article>
Finally, here is the notice that I am receiving:
NOTICE: ARRAY TO STRING CONVERSION IN /HOME/#####/MYWEBSITE.COM/ASSETS/INCLUDES/VERSEOFTHEDAY.PHP ON LINE 19
ARRAY
What am I doing wrong? In this example I am expecting it to output Genesis in the tag.
The SimpleXML xpath function returns an array of results, which will be empty if no result is found. So you need to check for that, and if you get a result, it will be at $bookName[0]. Something like this will work:
$bookNames = $xml->xpath( '//book[abrv="'.$bookID.'"]/name' );
$bookName = empty($bookNames) ? 'Not Found' : $bookNames[0];
?>
<article id="verseOfDay">
<div class="container">
<h2>verse of the day</h2>
<h6><?php echo $bookName; ?></h6>
<p></p>
</div>
</article>
Demo on 3v4l.org

Load XML file and load content

Hi I am trying to load content from an XML file called articles.xml:
It has and as the elements.
<?xml version="1.0"?>
<RecentArticles>
<Article author="The Reddest">
<Title>Silverlight and the Netflix API</Title>
<Date>1/13/2009</Date>
<Description>Description</Description>
<Link></Link>
</Article>
<Article author="The Hairiest">
<Title>Cake PHP 4 - Saving and Validating Data</Title>
<Date>1/12/2009</Date>
<Description>Description</Description>
<Link></Link>
</Article>
<Article author="The Tallest">
<Title>Silverlight 2 - Using initParams</Title>
<Date>1/6/2009</Date>
<Description>Description</Description>
<Link></Link>
</Article>
<Article author="The Fattest">
<Title>Controlling iTunes with AutoHotkey</Title>
<Date>12/12/2008</Date>
<Description>Description</Description>
<Link></Link>
</Article>
</RecentArticles>
This is the following PHP code I am using to print the elements into the table:
<!--Make table and print each xml element into it-->
<center>
<table border="1">
<tr>
<th>Title</th>
<th>Date</th>
<th>Description</th>
<th>Link</th>
</tr>
<?php
//Load the xml file int a variable for use in the table below.
$xml = simplexml_load_file("articles.xml");
echo("<tr>");
foreach ($xml->RecentArticles->Article as $entry)
{
$title = $entry['Title']
$date = $entry['Date'];
$description = $entry['Description'];
$link = $entry['Link'];
echo("<td>$title</td>");
echo("<td>$date</td>");
echo("<td>$description</td>");
echo("<td>$link</td>");
}
echo("</tr>");
?>
</table>
</center>
However nothing is printing into the table.. does anyone have any idea why?
You are accessing a (non-existent) attribute value rather than the node value for your content.
Access "Title" attribute value on the "Article" node:
$title = $entry['Title']
Access "Title" node value:
$title = $entry->Title
Example XML:
<RecentArticles>
<Article author="The Reddest" Title="This is what you are accessing with $entry['Title']">
<Title>This is what you should be accessing with $entry->Title</Title>
<Date>1/13/2009</Date>
<Description>Description</Description>
<Link></Link>
</Article>
</RecentArticles>
See this documentation for more information on how to access XML elements.

How to read all nodes of a XML file with more than three nodes levels using PHP?

I am currently writing a PHP script to read all nodes of a XML file with more than three node levels (depth > 2). However I only could read accurately upto first level child.
I would highly appreciate, if you could let me know the error I have made while I am trying to read second level child nodes.
My xml file
<?xml version="1.0" encoding="utf-8"?>
<document nipperstudio="2.3.10.3500" xmlversion="2" xmlrevision="3">
<report>
<part index="1" title="Your Report" ref="YOURREPORT">
<section index="1.1" title="Introduction" ref="INTRODUCTION">
<text>Inside the section 1.1.:</text>
<list type="bullet">
<listitem>detailed description of list item 01;</listitem>
<listitem>detailed description of list item 02;</listitem>
</list>
</section>
<section index="1.2" title="Report Conventions" ref="REPORTCONVENTIONS">
<text>This report makes use of the text conventions.</text>
<table index="3" title="Report text conventions" ref="REPORTTEXTCONVENTIONS">
<headings>
<heading>Convention</heading>
<heading>Description</heading>
</headings>
</table>
</section>
</part>
<part index="2" title="Security Audit" ref="SECURITYAUDIT">
<section index="2.1" title="Introduction" ref="INTRODUCTION">
<text>Inside the section 2.1.:</text>
<list type="bullet">
<listitem>detailed description of list item 01;</listitem>
<listitem>detailed description of list item 02;</listitem>
</list>
<section index="2.1.1" title="Issue Overview" ref="ISSUEOVERVIEW">
<text>Inside the section 2.1.1</text>
<text title="Issue Finding">The is the body text of 2.1.1.</text>
</section>
<section index="2.1.2" title="Rating Overview" ref="RATINGSYSTEM">
<text>Inside the section 2.1.1</text>
<text title="Issue Finding">The is the body text of 2.1.1.</text>
</section>
</section>
<section index="2.2" title="section title" ref="SECTION2.2">
<section index="2.2.1" title="Finding" ref="FINDING">
<text>Inside the section 2.2.1</text>
<text title="Issue Finding">The is the body text of 2.2.1.</text>
</section>
</section>
</part>
</report>
</document>
My PHP Script is given below.
Test XML Reader
<html>
<title>Test XML Reader</title>
<body>
<p>Output from xmlreader</p>
<?php
readXmlFiles();
?>
</body>
</html>
<?php
function readXmlFiles(){
// create the reader object
$reader = new XMLReader();
// reader the XML file.
$reader->open("./fwxml/03.xml"); //open the xml file to read.
while($reader->read()) {
switch($reader->nodeType) {
case (XMLREADER::ELEMENT):
if ($reader->localName == 'report') { //read the local name of the node
$node = $reader->expand();
$dom = new DomDocument();
$n = $dom->importNode($node,true);
$dom->appendChild($n);
foreach(($dom->getElementsByTagName('part')) as $fwpart) {
$parttitle = $fwpart->getAttribute('title');
echo "=====".$parttitle."=====<br>";
foreach(($fwpart->childNodes) as $cnode){
if($cnode->nodeName == 'section'){
$index = $cnode->getAttribute('index');
$title = $cnode->getAttribute('title');
$ref = $cnode->getAttribute('ref');
echo "Index = " .$index."<br>";
echo "Title = " .$title."<br>";
echo "Ref = " .$ref."<br>";
$fwsec = $dom->getElementsByTagName('section');
echo $fwsec->item(0)->nodeValue."<br>";
echo "<br><br><br>";
}//end of if
}//end of foreach
}
}
break; //end of XMLREADER::ELEMENT
case (XMLREADER::END_ELEMENT):
// do something based on when the element closes.
break;
}
}
} //end of function
?>

Preg replace divs with li but keep class active

Trying to get my head around how to create a PHP preg replace for a string that will convert
<div class="active make_link">1</div>
<div class="make_link digit">2</div>
<div class="make_link digit">3</div>
etc
to
<li class="active">1</li>
<li>2</li>
<li>3</li>
etc
Figured out how to replace the elements but not how to keep the class active.
$new_pagination = preg_replace('/<div[^>]*>(.*)<\/div>/U', '<li>$1</li>', $old_pagination);
Any ideas?
Try this..You can do this using str_ireplace too
<?php
$html='<div class="active make_link">1</div>
<div class="make_link digit">2</div>
<div class="make_link digit">3</div>';
echo str_ireplace(array('<div','</div','class="active make_link"','class="make_link digit"'),array('<li','</li','active',''),$html);
Or simple html dom:
require_once('simple_html_dom.php');
$doc = str_get_html($string);
foreach($doc->find('div') as $div){
$div->tag = 'li';
preg_match('/active/', $div->class, $m);
$div->class = #$m[0];
}
echo $doc;
This may seem a bit excessive, but it's a good use-case for XSLT:
$xslt = <<<XML
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()" /></xsl:copy>
</xsl:template>
<xsl:template match="div">
<li>
<xsl:if test="#*[name()='class' and contains(., 'active')]">
<xsl:attribute name="class">active</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="node()" />
</li>
</xsl:template>
</xsl:stylesheet>
XML;
It uses the identity rule and then overrides handling for <div>, adding a class="active" for nodes that have such a class name.
$xsl = new XSLTProcessor;
$doc = new DOMDocument;
$doc->loadXML($xslt);
$xsl->importStyleSheet($doc);
$doc = new DOMDocument;
$html = <<<HTML
<div class="active make_link">1</div>
<div class="make_link digit">2<div>test</div></div>
<div class="make_link digit">3</div>
HTML;
$doc->loadHTML($html);
echo $xsl->transformToDoc($doc)->saveHTML();

Why is my recursive loop creating too many children?

I'm using a PHP recursive loop to parse through an XML document to create a nested list, however for some reason the loop is broken and creating duplicates of elements within the list, as well as blank elements.
The XML (a list of family tree data) is structured as follows:
<?xml version="1.0" encoding="UTF-8"?>
<family>
<indi>
<id>id1</id>
<fn>Thomas</fn>
<bday></bday>
<dday></dday>
<spouse></spouse>
<family>
<indi>
<id>id1</id>
<fn>Alexander</fn>
<bday></bday>
<dday></dday>
<spouse></spouse>
<family>
</family>
</indi>
<indi>
<id>id1</id>
<fn>John</fn>
<bday></bday>
<dday></dday>
<spouse></spouse>
<family>
<indi>
<id>id1</id>
<fn>George</fn>
<bday></bday>
<dday></dday>
<spouse></spouse>
<family>
</family>
</indi>
</family>
</indi>
</family>
</indi>
</family>
And here's my PHP loop, which loads the XML file then loops through it to create a nested ul:
<?php
function outputIndi($indi) {
echo '<li>';
$id = $indi->getElementsByTagName('id')->item(0)->nodeValue;
echo '<span class="vcard person" id="' . $id . '">';
$fn = $indi->getElementsByTagName('fn')->item(0)->nodeValue;
$bday = $indi->getElementsByTagName('bday')->item(0)->nodeValue;
echo '<span class="edit fn">' . $fn . '</span>';
echo '<span class="edit bday">' . $bday . '</span>';
// ...
echo '</span>';
echo '<ul>';
$family = $indi->getElementsByTagName('family');
foreach ($family as $subIndi) {
outputIndi($subIndi);
}
echo '</ul></li>';
}
$doc = new DOMDocument();
$doc->load('armstrong.xml');
outputIndi($doc);
?>
EDIT here's the desired outcome (nested lists, with ul's signifying families and li's signifying individuals)
<ul>
<li>
<span class="vcard">
<span class="fn">Thomas</span>
<span class="bday"></span>
<span class="dday"></span>
<ul>
... repeat for all ancestors ...
</ul>
<li>
<ul>
You can see the output at http://chris-armstrong.com/gortin . Any ideas where I'm going wrong? I think it's something to do with the $subIndi value, but anytime I try and change it I get an error. Would really appreciate any help!
Sounds perfect! Could you give me an
example? Does this mean I can save the
data as XML, then load it in as nested
ul's?
Yes, you can do exactly that. Here's an XSL which renders nested UL's:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>Family tree</h2>
<ul>
<li><xsl:value-of select="indi/fn" /></li>
<!-- apply-templates will select all the indi/family nodes -->
<xsl:apply-templates select="indi/family" />
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="family">
<ul>
<li>
<div>
<xsl:value-of select="id" />: <xsl:value-of select="fn" />
(<xsl:variable name="bday" select="bday" />
to
<xsl:variable name="dday" select="dday" />)
</div>
</li>
<!-- This node matches the 'family' nodes, and we're going to apply-templates on the inner 'family' node,
so this is the same thing as recursion. -->
<xsl:apply-templates select="family" />
</ul>
</xsl:template>
</xsl:stylesheet>
I don't know php, but this article will show you how to transform XML using the style sheet above.
You can also link your style sheet by adding a stylesheet directive at the top of your XML file (see for an example).
getElementsByTagName will give you all nodes, not just immediate children:
$family = $indi->getElementsByTagName('family');
foreach ($family as $subIndi) {
outputIndi($subIndi);
}
You will call outputIndi() for grand children, etc repeatedly.
Here is an example (from another stackoverflow question):
for ($n = $indi->firstChild; $n !== null; $n = $n->nextSibling) {
if ($n instanceof DOMElement && $n->tagName == "family") {
outputIndi($n);
}
}
Replace this
$family = $indi->getElementsByTagName('family');
foreach ($family as $subIndi) {
outputIndi($subIndi);
}
by this
if(!empty($indi))
foreach($indi as $subIndi){
outputIndi($subIndi);
}
I realize
if($indi->hasChildNodes())
is better than
if(!empty($indi))

Categories