我正试图编写一系列函数,这些函数将提取MS Word DOCX文件的document.xml部分,并有效地邮件合并一系列键/值对,以替换文档中定义的模板字段。我有一个函数使用xml_parse_into_struct
将XML文本转换为必要的数组,但是一旦我完成了替换文本,我将(大概)需要使用ZipArchive
方法addFromString
来创建新的document.xml文件并将其添加到DOCX zip容器中。但是,当我使用数据数组而不是XML字符串时,我不确定如何做到这一点。是否有一种方法将数组转换回XML字符串格式?
到目前为止我写的是:
// $filename = name of DOCX file to open
function get_docx_xml($filename) {
// Extract XML from DOCX file
$zip = new ZipArchive();
if ($zip->open($filename, ZIPARCHIVE::CHECKCONS) !== TRUE) { echo 'failed to open template'; exit; }
$xml = 'word/document.xml';
$data = $zip->getFromName($xml);
$zip->close();
// Create the XML parser and create an array of the results
$parser = xml_parser_create_ns();
xml_parse_into_struct($parser, $data, $vals, $index);
xml_parser_free($parser);
// Return the relevant XML information
return array('vals' => $vals, 'index' => $index);
}
这部分工作得很好,我可以print_r
两个数组并使结果有意义。然而,下面的函数不起作用——至少不是在所有情况下都起作用。如果我对要替换的字段使用某些分隔符,它可以工作,但不是所有时间,我认为这是Word的字符编码或其他格式的问题。
// $templateFile = original, unedited template; $newFile = new file name to be created; $row = array of data to merge in
function mailmerge($templateFile, $newFile, $row) {
if (!copy($templateFile, $newFile)) // make a duplicate so we dont overwrite the template
return false; // could not duplicate template
$xmldata = get_docx_xml($newFile);
$zip = new ZipArchive();
if ($zip->open($newFile, ZIPARCHIVE::CHECKCONS) !== TRUE)
return false; // probably not a docx file
$file = 'word/document.xml';
$data = $zip->getFromName($file);
foreach ($row as $key => $value) {
$data = str_replace($key, xml_escape($value), $data);
}
$zip->deleteName($file);
$zip->addFromString($file, $data);
$zip->close();
return true;
}
所以,而不是使用str_replace(这失败了很多时候),我计划循环$vals数组,我从第一个函数得到,做替换,然后保存结果数组返回到字符串,反过来,回到DOCX zip容器。
虽然我没有找到我的问题的答案,但我已经通过变通解决了这个问题。我有效地使用了一系列substr_replace调用来进行必要的更新。这是我新的和改进的邮件合并功能,如果有人需要这样的东西:
// Merge data into a Word file (mailmerge or custom)
// $templateFile = original, unedited template; $newFile = new file name to be created; $row = array of data to merge in; $delim_start = starting delimiter; $delim_end = ending delimiter
function mailmerge($templateFile, $newFile, $row, $delim_start, $delim_end) {
if (!copy($templateFile, $newFile)) // make a duplicate so we dont overwrite the template
return false; // could not duplicate template
$zip = new ZipArchive();
if ($zip->open($newFile, ZIPARCHIVE::CHECKCONS) !== TRUE)
return false; // probably not a docx file
$file = 'word/document.xml';
$data = $zip->getFromName($file);
$currentpos = 0;
foreach ($row as $key => $value) {
// Look for a naturally occuring instance of the replacement string (key) and replace as needed
if (stristr($data, $key)) {
$currentpos = strpos($data, $key) + strlen($key);
$data = str_replace($key, xml_escape($value), $data);
}
else { // Look for the key's delimiter
if (stristr($data, $delim_start, $currentpos)) {
$pos_start = strpos($data, $delim_start, $currentpos);
// Clear the initial delimiter
$data = substr_replace($data, '', $pos_start, strlen($delim_start));
// Now find the actual data (by XML key)
$datapos_start = (strpos($data, '<w:t>', $pos_start)) + 5;
$datapos_end = strpos($data, '</w:t>', $datapos_start);
// Replace the data
$data = substr_replace($data, xml_escape($value), $datapos_start, ($datapos_end - $datapos_start));
// Clear the closing delimiter (have to recalculate datapos_end due to the replacement)
$datapos_end = strpos($data, $delim_end, $datapos_start);
$data = substr_replace($data, '', $datapos_end, strlen($delim_end));
// Reset the current posistion variable for the next iteration
$currentpos = $datapos_end + 6;
}
}
}
$zip->deleteName($file);
$zip->addFromString($file, $data);
$zip->close();
return true;
}