Search code examples
phpxmlplesk

Plesk XML formatting


In PHP i have this code for making a XML header for the plesk API.

$request = <<<EOF
<packet version="1.6.7.0">
<mail>
<update>
   <set>
      <filter>
         <site-id>$site_id</site-id>
         <mailname>
            <name>$name</name>
            <autoresponder>
               <enabled>true</enabled>
               <subject>$subject</subject>
               <text>$mail_body</text>
               <end_date>$date</end_date>
            </autoresponder>
         </mailname>
      </filter>
   </set>
</update>
</mail>
</packet>
EOF;

However i get this response: 1014 Parser error: Cannot parse the XML from the source specified

I have put the xml into a formatting of 2, 3 ,4 and tab spacing and it doesnt seem to be able to parse it.

What am i doing wrong?


Solution

  • You can't guess to create a valid XML by string concatenation, especially when you have complex contents like an email text.

    No all characters are allowed inside XML tags: you have to properly escape not-allowed characters. Fortunately, php have some parser that do this job for you.

    First of all, create an empty XML template (check its validity using a XML validator):

    $xml = '<?xml version="1.0" encoding="utf-8" ?>
    <packet version="1.6.7.0">
    <mail>
    <update>
        <set>
            <filter>
                <site-id/>
                <mailname>
                    <name/>
                    <autoresponder>
                        <enabled/>
                        <subject/>
                        <text/>
                        <end_date/>
                    </autoresponder>
                </mailname>
            </filter>
        </set>
    </update>
    </mail>
    </packet>
    ';
    

    Then, load it into a DOMDocument object and init a DOMXPath object:

    $dom = new DomDocument();
    $dom->loadXML( $xml );
    $xpath = new DOMXPath( $dom );
    

    Then, find each node that you want to change and set/update its node value:

    $nodes = $xpath->query( 'mail/update/set/filter/site-id' );
    $nodes->item(0)->nodeValue = $site_id;
    
    $nodes = $xpath->query( 'mail/update/set/filter/mailname/name' );
    $nodes->item(0)->nodeValue = $name;
    

    For the <autoresponder> children, you can perform a loop through each child, using * at the end of your search pattern:

    $nodes = $xpath->query( 'mail/update/set/filter/mailname/autoresponder/*' );
    foreach( $nodes as $node )
    {
        if( 'enabled' == $node->nodeName )
        {
            $node->nodeValue = 'true';
        }
        elseif( 'subject' == $node->nodeName )
        {
            $node->nodeValue = $subject;
        }
        elseif( 'text' == $node->nodeName )
        {
            $cdata = $dom->createCDATASection( $mail_body );
            $node->appendChild( $cdata );
        }
        elseif( 'end_date' == $node->nodeName )
        {
            $node->nodeValue = $date;
        }
    }
    

    Note the different syntax adopted for mail body: I use a CDATA node here: if your XML doesn't allow CDATA, replace it with standard ->nodeValue syntax. Or — instead — you can have to use CDATA method for all the nodes.

    When the XML is ready, you can echo it by:

    echo $dom->saveXML();
    

    DOMXPath allow to perform complex searches in the XML tree: it's not mandatory in your case, because you start from a short, empty, unambiguous template. I use it for demonstration purpose, but you can replace a line like this:

    $nodes = $xpath->query( 'mail/update/set/filter/site-id' );
    

    with:

    $nodes = $dom->getElementsByTagName( 'site-id' );
    

    and it will work fine.