有需要可留言,完全开源。共同完善。
1. 项目概述
DJofficer 是一个基于 PHP 的 Word 文档(.docx)动态生成库。核心思想是在 Word 母版(模板)的基础上做修改:用户用 Word 编辑软件把模板样式调整好,再通过代码向模板中的变量位置填充数据、克隆区块、替换图片等,最终生成成品文档。
- 技术栈:PHP >= 8.1,仅依赖原生
ZipArchive+DOMDocument(Excel 操作为可选依赖 PhpSpreadsheet) - 核心优势:生成 Word 只需关注动态数据与逻辑,无需关心样式调整(样式在母版中借助 Word 完成)
- 与 PHPWord 的区别:PHPWord 逐元素写入;DJofficer 在母版基础上修改,编码效率更高,支持目录、页眉页脚、克隆等复杂能力
项目双版本
| 版本 | 目录 | 说明 |
|---|---|---|
| v0 | src/ | 原始实现,使用 ${变量名/} 文本标记定位变量 |
| v1 | v1/ | 全新架构,使用 Word 批注(comment.xml) 定位变量,是当前主推版本 |
2. 核心功能
2.1 文本变量替换
将模板中的变量替换为实际文本,支持:
- 普通字符串替换(
setValue) - 批量替换多个变量(
setValues) - 混合内容替换(
setValue传数组,支持换行 BREAK、超链接 LINK、图片 IMG 类型)
2.2 图片替换
将变量位置替换为图片(setImage),支持:
- 自动管理媒体文件(
MediaManager避免与模板图片重名) - 自定义尺寸
- 自动建立图片关系(
RelationshipManager)
2.3 超链接替换
将变量替换为超链接(setLink),支持:
- 外部链接(
\o字段) - 内部锚点链接(
#anchor,\l字段) - 保留原 run 作为显示文本
2.4 克隆(区块/段落/节)
| 方法 | 功能 |
|---|---|
cloneRange | 智能克隆:表格行或多段落范围 |
cloneParagraph | 克隆变量所在段落 N 次 |
cloneSection | 克隆整个节(含页面布局 sectPr) |
克隆时自动处理批注 id 重映射、变量名后缀(#0/#1)、comments.xml 同步。
2.5 数据绑定(bind)
bind($name, $data, $callback?) 将数据行绑定到模板范围:
- 每行数据自动克隆一次范围并填充
- 支持嵌套克隆(数据值为数组时递归绑定,生成
childVar#0#1复合变量名) - 支持
$keyMap映射模板变量名到数据键 - 支持回调函数处理每个值
2.6 表格批量填充
setTable($name, $rows, $hasHeaderRow?, $autoNumber?, $rowLimit?) 一次调用批量填充表格:
- 批量写入:所有行从模板数据行克隆,通过
DocumentFragment一次 DOM 插入(500 行仅数毫秒) - 标题行保留:
hasHeaderRow=true时首行保留为标题,数据从第二行填充 - 序号自动填充:
autoNumber=true时第一列自动填1,2,3...(符合中文习惯,首行首列为"序号"),数据自带的序号列被忽略,保证序号连贯 - 行数校验:
rowLimit限制最大数据行数,超限抛InvalidArgumentException - 性能优化:单元格文本节点每表只定位一次(全局
<w:t>索引),每个克隆行仅一次 DOM 查询
2.7 删除
delete($name, $scope?) 按范围删除内容:
SCOPE_PARAGRAPH:删除段落SCOPE_ROW:删除表格行SCOPE_TABLE:删除表格SCOPE_SECTION:删除整个节(从上一 sectPr 边界到下一 sectPr 边界)
2.8 插入
insertPageBreak:插入分页符insertEmptyParagraph:插入空行
2.9 Word 域(Field)
| 方法 | 功能 |
|---|---|
setNowPage | 当前页码(PAGE 域) |
setTotalPage | 总页数(NUMPAGES 域) |
setRef | 书签引用(REF 域) |
setPageRef | 书签页码(PAGEREF 域) |
2.10 图表数据修改
setChartData($part, $series, $cats, $data):按部件路径修改图表数据setChartDataByName($name, $series, $cats, $data):按批注名称定位图表并修改
同时更新嵌入 xlsx 源数据和 chart XML 渲染缓存,支持:
- 多 sheet 检测、combo chart、scatterChart(xVal/yVal)
- formatCode 保留、sharedStrings 共享字符串表
- 系列数量调整(不足克隆、多余删除,自动分配主题强调色避免颜色重复)
2.11 内嵌 Excel 操作
excel():返回ExcelManagerInterface(需部件路径,底层逃生舱)excelByName($name):按批注名称定位内嵌 Excel,返回BoundExcelManager,支持setCellValue/getCellValue
2.12 目录更新
updateToc() 将文档中所有 TOC 域标记为脏,Word 打开时自动刷新目录。支持复杂域(fldChar begin/end)和简单域(fldSimple)。
2.13 批量操作(batch)
batch($operations, $continueOnError?) 在一次编辑会话中执行多个操作,共享同一 DOM 上下文:
$editor->batch([
['method' => 'setValue', 'name' => 'title', 'value' => '报告'],
['method' => 'setImage', 'name' => 'logo', 'imagePath' => 'logo.png'],
['method' => 'cloneRange', 'name' => 'row', 'count' => 3],
['method' => 'setChartDataByName', 'name' => '图表1', 'seriesNames' => [...], ...],
]);
getBatchErrors() 可获取批量操作累积的错误。
2.14 变量定位(locate)
locate($name) 返回变量名对应的批注内容范围,用于预检变量是否存在及范围位置。
2.15 模板缓存
registerTemplate/unregisterTemplate/listTemplates:模板注册管理setTemplateDir/setCacheDir:自定义模板与缓存目录- 同名模板二次加载时复用解析后的 XML 与批注索引,加速加载
3. 架构设计
3.1 v1 核心目标:批注定位机制
v1 的核心链路是通过 comment.xml 中的批注文本(w:t)定位 document.xml 中的内容范围,再执行操作方法:
用户选择方法 + 提供 comment.xml 中 w:t 文本
│
▼
① w:t 文本 → comment id
(CommentIndexManager::buildVariableIndex)
│
▼
② id → document.xml 内容范围
(VariableLocator::findRangeStarts / findRangeEnd)
│
▼
③ 对内容范围执行操作
(setValue / setImage / cloneRange / bind / setChartDataByName ...)
│
▼
④ 批量执行(多个操作共享同一 DOM 上下文)
│
▼
⑤ 统一保存(save(),清理批注标记)
3.2 分层架构
┌──────────────────────────────────────────────┐
│ 门面层(Facade) │
│ WordEditor(统一 API 入口) │
│ ComponentFactory(DI 容器) │
└──────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────┐
│ Operation 组件层(业务逻辑) │
│ Binder / TextReplacer / ImageReplacer / │
│ LinkReplacer / TableBuilder / ChartManager │
│ RangeCloner / ParagraphCloner / SectionCloner│
│ ContentDeleter / FieldReplacer / TocUpdater │
│ BreakInserter / HeaderFooterManager / ... │
└──────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Tool 工具层 │ │ Xml 组件层 │ │ Zip 组件层 │
│ VariableLocator│ │ XmlDocument │ │ DocxArchive │
│ CommentIndex │ │ FragmentFactory│ │ MediaManager │
│ PartLocator │ │ NodeImporter │ │ RelationshipMgr│
│ RangeExtractor│ │ TextEncoder │ │ TempFileMgr │
│ ContainerDetector│ │ XPathBuilder │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
3.3 核心设计原则
- 名称寻址:所有面向用户的 API 以批注文本(变量名)定位,用户无需关心部件路径或 comment id
- id 中转:w:t 文本 → comment id → document.xml 内容范围,两步解析由 CommentIndexManager + VariableLocator 完成
- 范围操作:每个方法在 commentRangeStart/End 范围内操作,不越界、不误伤其他批注
- 批量执行:支持链式调用或 batch API,共享同一 DOM 上下文,最后统一保存
- 统一保存:save() 一次性写出,清理批注标记并(可选)移除 comments.xml
3.4 v0 与 v1 对比
| 维度 | v0(src/) | v1(v1/) |
|---|---|---|
| 变量定位 | ${变量名/} 文本标记 | Word 批注(comment.xml w:t) |
| 表格填充 | clones() + setValue(name#N) 逐单元格 | setTable() 批量填充 |
| 架构 | 单一大类 | 门面 + Operation + Tool/Xml/Zip 分层 |
| 性能 | 逐单元格操作,性能低 | DocumentFragment 批量插入,500 行毫秒级 |
4. 目录结构
DJofficer-main/
├── src/ # v0 原始实现
│ ├── Api/ # 公共 API
│ ├── Common/ # 公共组件
│ ├── Config/ # 配置
│ ├── Edit/Part/ # 编辑部件(Document 等)
│ ├── Read/Part/ # 读取部件
│ └── XmlTemple/ # XML 模板
├── v1/ # v1 全新架构
│ ├── Component/
│ │ ├── WordEditor.php # 门面层:统一 API 入口
│ │ ├── ComponentFactory.php # DI 容器
│ │ ├── Operation/ # 业务操作层(30+ 组件)
│ │ ├── Tool/ # 工具层(定位/范围/索引)
│ │ ├── Xml/ # XML 组件层
│ │ ├── Zip/ # ZIP 组件层
│ │ └── Exception/ # 异常定义
│ ├── Cache/ # 模板缓存层
│ └── tests/Unit/ # PHPUnit 单元测试
├── tests/ # 集成测试与样例模板
│ ├── samples/ # 测试模板(docx)
│ └── v1_test_*.php # v1 集成测试脚本
├── scripts/ # 工具脚本(索引生成、性能扫描)
├── templates/ # 模板目录
├── AGENTS.md # 架构规范
├── ARCHITECTURE_CHANGELOG.md # 架构变更记录
├── claude.md # 方法索引(自动生成)
├── CODE_WIKI.md # 代码 Wiki
└── composer.json # 依赖与自动加载
5. 快速上手
5.1 安装
composer require mkdreams/DJofficer
# 或手动引入
require_once('Autoloader.php');
5.2 模板准备
在 Word 中打开模板,选中变量文本(如 {{title}}),插入 > 批注,批注文本即变量名。表格数据行用批注标记整行。
5.3 基本使用
use DJofficer\v1\Component\WordEditor;
$editor = new WordEditor();
$editor->load('temple.docx');
// 文本替换
$editor->setValue('title', '月度报告');
$editor->setValues(['date' => '2026-08-16', 'author' => '张三']);
// 图片替换
$editor->setImage('logo', '/path/to/logo.png');
// 表格批量填充
$editor->setTable('table1', [
['姓名' => '张三', '年龄' => 25, '职业' => '工程师'],
['姓名' => '李四', '年龄' => 30, '职业' => '设计师'],
], true); // hasHeaderRow = true
// 克隆
$editor->cloneRange('people', 3);
// 保存
$editor->save('output.docx');
$editor->free();
6. 测试覆盖
| 测试类型 | 位置 | 覆盖内容 |
|---|---|---|
| PHPUnit 单元测试 | v1/tests/Unit/ | 128 项:WordEditor、TextReplacer、XmlDocument、DocxArchive、TableBuilder、ChartManager 等核心组件 |
| 集成测试 | tests/v1_test_*.php | readme(24)、temple(60)、chart(图表)、clone(19)、image(18)、table(36)、template_cache(16)等 |
| CI 流水线 | .github/workflows/phpunit.yml | PHP 8.1–8.4 矩阵监控代码质量 |
7. 相关文档
| 文档 | 说明 |
|---|---|
| AGENTS.md | 项目架构规范与核心约束 |
| ARCHITECTURE_CHANGELOG.md | 架构变更记录 |
| CODE_WIKI.md | 代码 Wiki(架构、模块、API) |
| claude.md | 方法索引(自动生成) |
| v1/README.md | v1 使用说明 |
| v1/CODE_INDEX.md | v1 代码库索引(自动生成) |
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/huluang/article/details/163804088



