1

I'm trying to parse docx files in PHP. Now I want to append document.xml file (that is main part of unzipped docx file). The structure is:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"                               xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"             
mc:Ignorable="w14 wp14">
        <w:body>
            <w:p w:rsidR="00080C51" w:rsidRDefault="00080C51">
                <w:pPr>
                    <w:pStyle w:val="a3"/>
                    <w:ind w:left="1020"/>
                    <w:rPr>
                        <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/>
                        <w:sz w:val="22"/>
                        <w:szCs w:val="22"/>
                    </w:rPr>
                </w:pPr>
                <w:bookmarkStart w:id="0" w:name="_GoBack"/>
                <w:bookmarkEnd w:id="0"/>
            </w:p>
            <w:sectPr w:rsidR="00080C51">
                <w:pgSz w:w="11906" w:h="16838"/>
                <w:pgMar w:top="850" w:right="850" w:bottom="850" w:left="1417" w:header="708" w:footer="708" w:gutter="0"/>
                <w:cols w:space="708"/>
                <w:docGrid w:linePitch="360"/>
            </w:sectPr>
        </w:body>
    </w:document>

What I want to do is add new child <w:p>some text</w:p> tag to <w:body> tag. How can I do this?

There're a lot of ways to work with XML documents in PHP, like DOM, SimpleXMLElement. But which one can help me achieve this?

Daria
  • 861
  • 11
  • 29

1 Answers1

1

You can do it as follows with SimpleXML:

$data = simplexml_load_file('test.xml');

$ns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'; // the namespace

foreach ($data->children($ns) as $i) {
    // add 'p' element only if the current child name is 'body'
    if ($i->getName() == 'body') {
        $newVal = $i->addChild('p', 'testval'); // add the child
        // only if you want it to have the same attributes as the 'p' element above;
        // otherwise comment out the two lines below
        $newVal->addAttribute('w:rsidR', '00080C51', $ns);
        $newVal->addAttribute('w:rsidRDefault', '00080C51', $ns);
    }
}

print_r($data->asXML());

EDIT:

Here is a version using DOM which adds the new element as the first element in w:body:

$xmlDoc = new DOMDocument();
$xmlDoc->load("test.xml");

$ns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';

$newVal = new DOMElement('p', 'testval', $ns);

foreach ($xmlDoc->childNodes as $i) {
        foreach ($i->childNodes as $j) {
                if($j->localName == 'body') {
                        $j->insertBefore($newVal, $j->firstChild);
                }
        }
}

// add the attributes after the new DOMElement is added
$newVal->setAttributeNS($ns, 'rsidR', '00080C51');
$newVal->setAttributeNS($ns, 'rsidRDefault', '00080C51');

print_r($xmlDoc->saveXML());
VolenD
  • 3,592
  • 18
  • 23
  • Looks great. Thanks a lot. But the tags `...` should be the last, but now new child of `` appears after this tag. Maybe I can remove it and add again after adding new `` tag? Now I tested it and it seems working. – Daria May 09 '15 at 15:47
  • And unfortunately new tags attributes are coming without `w:`.. – Daria May 09 '15 at 15:54
  • 1
    @Steve.B I added a version with DOM, which adds the new child before the existing `w:p`. Also, I have forgotten the attribute namespace in the SimpleXML version, so I corrected this as well. – VolenD May 09 '15 at 16:23
  • I guess it's almost what I need. Thank you so much. Can you help me bit more? How can I call element by name like in your first solution? `$elems = $xml->children($ns); print_r($elems->name1->name2);` – Daria May 09 '15 at 17:36
  • I guess I figured it out. – Daria May 09 '15 at 18:18
  • 1
    @Steve.B You can not call by element name when the element has a namespace. However, in your case you can use `simplexml_load_file('test.xml', "SimpleXMLElement", null, $ns);` and then it will be possible. This way, adding a new value will be just a single line: `$data->body->addChild('p', 'testval');`. – VolenD May 10 '15 at 08:47