Search code examples
phpfilenamespacessavexmlwriter

PHP - XMLWriter write nested array to file


I'm following this example in XMLWiter functions, but I'm looking for a way to write the output to a file (sample.xml), I walked through many threads, with no luck. I know that the solution could be easy, but I'm working on PHP only from one month. Can anyone help?

<?php

class XmlConstruct extends XMLWriter
{

/**
 * Constructor.
 * @param string $prm_rootElementName A root element's name of a current xml document
 * @param string $prm_xsltFilePath Path of a XSLT file.
 * @access public
 * @param null
 */
public function __construct($prm_rootElementName, $prm_xsltFilePath=''){
    $this->openMemory(); // change this with $this->openURI('your_file.xml');
    $this->setIndent(true);
    $this->setIndentString(' ');
    $this->startDocument('1.0', 'UTF-8');

    if($prm_xsltFilePath){
        $this->writePi('xml-stylesheet', 'type="text/xsl" href="'.$prm_xsltFilePath.'"');
    }

    $this->startElement($prm_rootElementName);
}

/**
 * Set an element with a text to a current xml document.
 * @access public
 * @param string $prm_elementName An element's name
 * @param string $prm_ElementText An element's text
 * @return null
 */
public function setElement($prm_elementName, $prm_ElementText){
    $this->startElement($prm_elementName);
    $this->text($prm_ElementText);
    $this->endElement();
}

/**
 * Construct elements and texts from an array.
 * The array should contain an attribute's name in index part
 * and a attribute's text in value part.
 * @access public
 * @param array $prm_array Contains attributes and texts
 * @return null
 */
public function fromArray($prm_array){
  if(is_array($prm_array)){
    foreach ($prm_array as $index => $element){
      if(is_array($element)){
        $this->startElement($index);
        $this->fromArray($element);
        $this->endElement();
      }
      else
        $this->setElement($index, $element);

    }
  }
}

/**
 * Return the content of a current xml document.
 * @access public
 * @param null
 * @return string Xml document
 */
public function getDocument(){     //comment this function
    $this->endElement();
    $this->endDocument();
    return $this->outputMemory();
}

/**
 * Output the content of a current xml document.
 * @access public
 * @param null
 */
public function output(){               //comment this function
    header('Content-type: text/xml');
    echo $this->getDocument();
}


}

//Example:

$contents = array(
  'page_title' => 'Generate a XHTML page from XML+XSLT files',
  'welcome_msg' => 'Simple XHTML document from XML+XSLT files!',
  'prova' => array(
    "gino" => array(
      "innergino" => "gino inner value"
     ),
   "filo" => "filodata"
),
);

$XmlConstruct = new XmlConstruct('root');
$XmlConstruct->fromArray($contents);
$XmlConstruct->output();              // comment this line

?>

Solution

  • Finally, (after reading lot of pages and tried a lot of examples), I followed my way to find a solution. First of all, as explained in this post, XMLWriter is the best choice for big xml files (as in my case), this is why, after a deep look, i didn't follow the solution based on the DOM as proposed by @Seyed Quarib, although has been very instructive to read that thread. Another problem was that all my xml content needed to be wrapped in a tag with a namespace like <p:TagName>. I found out i needed to use XMLWriter::startElementNS and also XMLWriter::writeAttribute. As I said, i find this way tho output nested xml tag very handy for my purpose (that was very specific). Here is the code, hope someone else can find it usefull:

    <?php header('Content-Type: text/html; charset=utf-8') ?>
        <div class="save" style="width:100%; height:100px;">File saved</div>
        <?php
    
        class XmlConstruct extends XMLWriter
        {
    
        /**
        * Constructor.
        * @param string $prm_rootElementName A root element's name of a current xml document
        * @param string $prm_xsltFilePath Path of a XSLT file.
        * @access public
        * @param null
        */
        public function __construct($prm_rootElementName, $prm_xsltFilePath=''){
          $this->openURI('save-to-file.xml'); // changed
          $this->setIndent(true);
          $this->setIndentString(' ');
          $this->startDocument('1.0', 'UTF-8');
    
        if($prm_xsltFilePath){
          $this->writePi('xml-stylesheet', 'type="text/xsl" href="'.$prm_xsltFilePath.'"');
        }
    
          //$this->startElement($prm_rootElementName); // changed
          $this->startElementNS("p", "TagName", null);
          $this->writeAttribute ("version", "1.0");
          $this->writeAttribute ("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#");
          $this->writeAttribute ("xmlns:p", "http://www.xxxxx.gov.it/sdi/xxxxx/v1.0");
          $this->writeAttribute ("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
        }
    
        /**
        * Set an element with a text to a current xml document.
        * @access public
        * @param string $prm_elementName An element's name
        * @param string $prm_ElementText An element's text
        * @return null
        */
        public function setElement($prm_elementName, $prm_ElementText){
          $this->startElement($prm_elementName);
          $this->text($prm_ElementText);
          $this->endElement();
        }
    
        /**
        * Construct elements and texts from an array.
        * The array should contain an attribute's name in index part
        * and a attribute's text in value part.
        * @access public
        * @param array $prm_array Contains attributes and texts
        * @return null
        */
        public function fromArray($prm_array){
          if(is_array($prm_array)){
            foreach ($prm_array as $index => $element){
              if(is_array($element)){
                $this->startElement($index);
                $this->fromArray($element);
                $this->endElement();
              }
              else
                $this->setElement($index, $element);
    
            }
          }
        }
    
        /**
        * Return the content of a current xml document.
        * @access public
        * @param null
        * @return string Xml document
        */
    
        public function getDocument(){     
          $this->endElement();
          $this->endDocument();
          return $this->outputMemory();
        }
    
        /**
        * Output the content of a current xml document.
        * @access public
        * @param null
        */
    
        public function output(){              
          header('Content-type: text/xml');
          echo $this->getDocument();
        }
    
    
        }
    
        //Example:
    
        $contents = array(
          'prova' => array(
            "gino" => array(
              "innergino" => "gino inner value",
              "innergino2" => "gino inner another value"
            ),
            "filo" => "filodata",
            "arrays" => array (
              "inner-arrays" => array (
                "one-inner-array" => array (
                  "inner-content-one" => "one",
                  "inner-content-two" => "two"
                ),
                "two-inner-array" => array (
                  "inner-content-one" => "one",
                  "inner-content-two" => "two"
                ),
                "three-inner-array" => array (
                  "inner-content-one" => "one",
                  "inner-content-two" => "two"
                ),
              )
            )
          )
        );
    
        $XmlConstruct = new XmlConstruct('root');
        $XmlConstruct->fromArray($contents);
        $XmlConstruct->getDocument();           // cahnged
    
        ?>
    

    And the output is:

    <?xml version="1.0" encoding="UTF-8"?>
    <p:TagName version="1.0" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:p="http://www.xxxxx.gov.it/sdi/xxxxx/v1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <prova>
        <gino>
          <innergino>gino inner value</innergino>
          <innergino2>gino inner another value</innergino2>
        </gino>
        <filo>filodata</filo>
        <arrays>
           <inner-arrays>
             <one-inner-array>
               <inner-content-one>one</inner-content-one>
               <inner-content-two>two</inner-content-two>
            </one-inner-array>
            <two-inner-array>
              <inner-content-one>one</inner-content-one>
              <inner-content-two>two</inner-content-two>
            </two-inner-array>
            <three-inner-array>
              <inner-content-one>one</inner-content-one>
              <inner-content-two>two</inner-content-two>
            </three-inner-array>
          </inner-arrays>
        </arrays>
      </prova>
    </p:TagName>