本文目录导读:

在PHP项目中实现版本差异对比,通常有以下几种常见方案,具体选择取决于你的业务场景:
数据库字段级别的版本对比(最常见)
适用于文章、配置、业务数据等需要记录历史版本的场景。
实现方案:快照对比
<?php
class VersionCompare
{
/**
* 对比两个版本的字段差异
* @param array $oldVersion 旧版本数据
* @param array $newVersion 新版本数据
* @return array 差异结果
*/
public function compareVersions(array $oldVersion, array $newVersion): array
{
$diff = [];
// 找出所有字段的差异
$allKeys = array_unique(array_merge(array_keys($oldVersion), array_keys($newVersion)));
foreach ($allKeys as $key) {
$oldValue = $oldVersion[$key] ?? null;
$newValue = $newVersion[$key] ?? null;
if ($oldValue !== $newValue) {
$diff[$key] = [
'old' => $oldValue,
'new' => $newValue,
'type' => $this->getChangeType($oldValue, $newValue)
];
}
}
return $diff;
}
private function getChangeType($old, $new): string
{
if ($old === null && $new !== null) return 'added';
if ($old !== null && $new === null) return 'removed';
return 'modified';
}
}
// 使用示例
$comparator = new VersionCompare();
$oldVersion = [ => '旧标题',
'content' => '旧内容',
'status' => 'draft'
];
$newVersion = [ => '新标题',
'content' => '新内容',
'status' => 'published'
];
$diff = $comparator->compareVersions($oldVersion, $newVersion);
print_r($diff);
基于Diff算法(文本级别)
对于代码、文章正文等长文本的差异对比,推荐使用成熟的Diff算法。
使用 PHP Diff 类库
<?php
// 使用 composer 安装: composer require jfcherng/php-diff
use Jfcherng\Diff\DiffHelper;
use Jfcherng\Diff\Factory\LineRendererFactory;
class TextVersionCompare
{
/**
* 对比两段文本的差异
*/
public function compareText(string $oldText, string $newText): array
{
// 基本差异
$diff = DiffHelper::calculate($oldText, $newText);
// HTML 渲染差异(用于展示)
$htmlDiff = DiffHelper::getHtml($oldText, $newText);
// 详细差异信息
$detailedDiff = $this->getDetailedDiff($oldText, $newText);
return [
'diff' => $diff, // 原始差异数据
'html' => $htmlDiff, // HTML 渲染
'detailed' => $detailedDiff // 详细差异
];
}
/**
* 获取详细的差异信息
*/
private function getDetailedDiff(string $old, string $new): array
{
$oldLines = explode("\n", $old);
$newLines = explode("\n", $new);
$diff = new \Diff($oldLines, $newLines);
$renderer = new \Diff_Renderer_Html_SideBySide();
return [
'added' => $diff->getAddedLines(),
'removed' => $diff->getRemovedLines(),
'changed' => $diff->getChangedLines(),
'html' => $diff->render($renderer)
];
}
}
基于JSON/序列化数据的版本对比
适用于配置、设置等复杂数据结构。
<?php
class JsonVersionCompare
{
/**
* 对比两个JSON版本的差异
*/
public function compareJson(string $oldJson, string $newJson): array
{
$oldData = json_decode($oldJson, true);
$newData = json_decode($newJson, true);
if ($oldData === null || $newData === null) {
throw new InvalidArgumentException('Invalid JSON data');
}
return $this->arrayDiffRecursive($oldData, $newData);
}
/**
* 递归对比数组差异
*/
private function arrayDiffRecursive(array $old, array $new, string $path = ''): array
{
$diffs = [];
// 找出新增和修改的键
foreach ($new as $key => $value) {
$currentPath = $path ? "{$path}.{$key}" : $key;
if (!array_key_exists($key, $old)) {
$diffs[] = [
'path' => $currentPath,
'type' => 'added',
'new' => $value
];
} elseif (is_array($value) && is_array($old[$key])) {
// 递归处理嵌套数组
$nestedDiffs = $this->arrayDiffRecursive(
$old[$key],
$value,
$currentPath
);
$diffs = array_merge($diffs, $nestedDiffs);
} elseif ($value !== $old[$key]) {
$diffs[] = [
'path' => $currentPath,
'type' => 'modified',
'old' => $old[$key],
'new' => $value
];
}
}
// 找出删除的键
foreach ($old as $key => $value) {
$currentPath = $path ? "{$path}.{$key}" : $key;
if (!array_key_exists($key, $new)) {
$diffs[] = [
'path' => $currentPath,
'type' => 'removed',
'old' => $value
];
}
}
return $diffs;
}
}
数据库级别的版本管理
适合需要完整版本历史记录的场景:
<?php
// 数据库表结构示例
class VersionManager
{
private $db;
public function getVersionHistory(string $table, int $recordId): array
{
// 查询版本历史
$sql = "SELECT * FROM {$table}_versions
WHERE record_id = ?
ORDER BY version DESC";
return $this->db->fetchAll($sql, [$recordId]);
}
public function createVersion(string $table, int $recordId, array $data): void
{
// 获取当前最新版本
$currentVersion = $this->getLatestVersion($table, $recordId);
// 创建新版本
$sql = "INSERT INTO {$table}_versions
(record_id, version, data, created_at)
VALUES (?, ?, ?, NOW())";
$this->db->execute($sql, [
$recordId,
$currentVersion + 1,
json_encode($data)
]);
}
public function compareTwoVersions(
string $table,
int $recordId,
int $version1,
int $version2
): array {
$v1 = $this->getVersionData($table, $recordId, $version1);
$v2 = $this->getVersionData($table, $recordId, $version2);
if ($v1 && $v2) {
$comparator = new VersionCompare();
return $comparator->compareVersions(
json_decode($v1['data'], true),
json_decode($v2['data'], true)
);
}
return [];
}
}
使用现成的第三方库
推荐几个流行的PHP版本对比库:
安装示例
composer require jfcherng/php-diff # 通用diff库 composer require sebastian/diff # PHPUnit使用的diff库 composer require cogpowered/finediff # 精细文本diff
使用 fineDiff 示例
<?php
use cogpowered\FineDiff\Diff;
use cogpowered\FineDiff\Render\Html;
class FineDiffCompare
{
public function compare(string $old, string $new): string
{
$diff = new Diff();
$renderer = new Html();
// 生成差异并渲染为HTML
return $renderer->process($old, $new);
}
public function getStats(string $old, string $new): array
{
$diff = new Diff();
$ops = $diff->generate($old, $new);
return [
'old_length' => strlen($old),
'new_length' => strlen($new),
'operations' => $ops,
'change_ratio' => $diff->getChangeRatio($old, $new)
];
}
}
前端展示方案
配合前端实现更好的可视化效果:
<?php
// 生成对比HTML
class DiffRenderer
{
public function renderHtml(array $diff): string
{
$html = '<div class="version-compare">';
foreach ($diff as $field => $change) {
$html .= sprintf(
'<div class="diff-line %s">
<span class="field-name">%s</span>
<div class="old-value">%s</div>
<div class="new-value">%s</div>
</div>',
$change['type'],
htmlspecialchars($field),
htmlspecialchars($change['old'] ?? ''),
htmlspecialchars($change['new'] ?? '')
);
}
$html .= '</div>';
return $html;
}
}
性能优化建议
对于大量数据的版本对比,考虑以下优化:
<?php
class OptimizedVersionCompare
{
/**
* 使用缓存加速频繁对比
*/
private $cache = [];
private $cacheTtl = 3600;
public function compareWithCache(array $old, array $new, string $cacheKey): array
{
$hash = md5(json_encode($old) . json_encode($new));
if (isset($this->cache[$cacheKey][$hash])) {
return $this->cache[$cacheKey][$hash];
}
$result = $this->compareVersions($old, $new);
$this->cache[$cacheKey][$hash] = $result;
return $result;
}
/**
* 分批处理大数据
*/
public function compareLargeArray(array $old, array $new, int $batchSize = 100): array
{
$diffs = [];
$chunks = array_chunk($old, $batchSize, true);
foreach ($chunks as $chunk) {
$batchDiff = $this->compareVersions($chunk, $new);
$diffs = array_merge($diffs, $batchDiff);
}
return $diffs;
}
}
选择建议
| 场景 | 推荐方案 |
|---|---|
| 文章/文档版本 | 文本Diff类库 + 数据库快照 |
| 商品/配置数据 | JSON版本对比 + 版本号管理 |
| 代码对比 | Git API 或专用Diff库 |
| 实时协作编辑 | 操作转换(OT)或CRDT算法 |
| 简单字段对比 | 直接数组比较 |
完整示例:文章版本管理系统
<?php
class ArticleVersionSystem
{
private $versionCompare;
private $db;
public function saveNewVersion(int $articleId, array $articleData): int
{
// 获取当前版本
$currentVersion = $this->getLatestVersion($articleId);
// 计算差异
if ($currentVersion) {
$diff = $this->versionCompare->compareVersions(
$currentVersion['data'],
$articleData
);
if (empty($diff)) {
return $currentVersion['version']; // 无变化
}
} else {
$diff = ['type' => 'initial'];
}
// 保存新版本
$version = ($currentVersion['version'] ?? 0) + 1;
$this->saveVersion($articleId, $version, $articleData, $diff);
return $version;
}
public function getChangesSummary(int $articleId, int $fromVersion, int $toVersion): array
{
$v1 = $this->getVersion($articleId, $fromVersion);
$v2 = $this->getVersion($articleId, $toVersion);
if (!$v1 || !$v2) {
return ['error' => '版本不存在'];
}
return [
'summary' => $this->generateSummary($v1['diff'], $v2['diff']),
'details' => $this->formatChanges($v1['data'], $v2['data']),
'fields_changed' => array_keys($v1['diff']),
'version_jump' => $toVersion - $fromVersion
];
}
}
根据你的具体需求选择合适的方案,如果是简单的字段对比,直接数组比较即可;如果需要展示详细的文本变化,建议使用成熟的Diff库。