How to write CDATA using SimpleXmlElement?

I have this code to create and update xml file:

<?php
$xmlFile    = 'config.xml';
$xml        = new SimpleXmlElement('<site/>');
$xml->title = 'Site Title';
$xml->title->addAttribute('lang', 'en');
$xml->saveXML($xmlFile);
?>

This generates the following xml file:

<?xml version="1.0"?>
<site>
  <title lang="en">Site Title</title>
</site>

The question is: is there a way to add CDATA with this method/technique to create xml code below?

<?xml version="1.0"?>
<site>
  <title lang="en"><![CDATA[Site Title]]></title>
</site>

Solution 1:

Got it! I adapted the code from this great solution (archived version):

    <?php
    
    // http://coffeerings.posterous.com/php-simplexml-and-cdata
    class SimpleXMLExtended extends SimpleXMLElement {

      public function addCData( $cdata_text ) {
        $node = dom_import_simplexml( $this ); 
        $no   = $node->ownerDocument;
        
        $node->appendChild( $no->createCDATASection( $cdata_text ) ); 
      }
    
    }

    $xmlFile    = 'config.xml';
    
    // instead of $xml = new SimpleXMLElement( '<site/>' );
    $xml        = new SimpleXMLExtended( '<site/>' );
    
    $xml->title = NULL; // VERY IMPORTANT! We need a node where to append
    
    $xml->title->addCData( 'Site Title' );
    $xml->title->addAttribute( 'lang', 'en' );
    
    $xml->saveXML( $xmlFile );
    
    ?>

XML file generated:

    <?xml version="1.0"?>
    <site>
      <title lang="en"><![CDATA[Site Title]]></title>
    </site>

Thank you Petah

Solution 2:

Here's my version of this class that has a quick addChildWithCDATA method, based on your answer:

    Class SimpleXMLElementExtended extends SimpleXMLElement {

  /**
   * Adds a child with $value inside CDATA
   * @param unknown $name
   * @param unknown $value
   */
  public function addChildWithCDATA($name, $value = NULL) {
    $new_child = $this->addChild($name);

    if ($new_child !== NULL) {
      $node = dom_import_simplexml($new_child);
      $no   = $node->ownerDocument;
      $node->appendChild($no->createCDATASection($value));
    }

    return $new_child;
  }
}

Simply use it like that:

$node = new SimpleXMLElementExtended();
$node->addChildWithCDATA('title', 'Text that can contain any unsafe XML charachters like & and <>');

Solution 3:

You can also create a helper function for this, if you'd rather not extend SimpleXMLElement:

 /**
  * Adds a CDATA property to an XML document.
  *
  * @param string $name
  *   Name of property that should contain CDATA.
  * @param string $value
  *   Value that should be inserted into a CDATA child.
  * @param object $parent
  *   Element that the CDATA child should be attached too.
  */
 $add_cdata = function($name, $value, &$parent) {
   $child = $parent->addChild($name);

   if ($child !== NULL) {
     $child_node = dom_import_simplexml($child);
     $child_owner = $child_node->ownerDocument;
     $child_node->appendChild($child_owner->createCDATASection($value));
   }

   return $child;
 };

Solution 4:

    class MySimpleXMLElement extends SimpleXMLElement{

        public function addChildWithCData($name , $value) {
            $new = parent::addChild($name);
            $base = dom_import_simplexml($new);
            $docOwner = $base->ownerDocument;
            $base->appendChild($docOwner->createCDATASection($value));
        }

    }

        $simpleXmlElemntObj = new MySimpleXMLElement('<site/>');

        /* USAGE */

        /* Standard */
        $simpleXmlElemntObj->addChild('Postcode','1111');

       /* With CDATA */
       $simpleXmlElemntObj->addChildWithCData('State','Processing');


    /* RESULT */
    /*
    <?xml version="1.0"?>
    <site>
        <Postcode>1111</Postcode>
        <State><![CDATA[Processing]]></State>
    </site>
   */