ConfigDiffer.php
4.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
<?php
/**
* Contains \Drupal\config_update\ConfigDiffer.
*/
namespace Drupal\config_update;
use Drupal\Component\Diff\Diff;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
/**
* Provides methods related to config differences.
*/
class ConfigDiffer implements ConfigDiffInterface {
use StringTranslationTrait;
/**
* List of elements to ignore when comparing config.
*
* @var string[]
*
* @see ConfigDiffer::format().
*/
protected $ignore;
/**
* Prefix to use to indicate config hierarchy.
*
* @var string
*
* @see ConfigDiffer::format().
*/
protected $hierarchyPrefix;
/**
* Prefix to use to indicate config values.
*
* @var string
*
* @see ConfigDiffer::format().
*/
protected $valuePrefix;
/**
* Constructs a ConfigDiffer.
*
* @param TranslationInterface $translation
* String translation service.
* @param string[] $ignore
* Config components to ignore.
* @param string $hierarchy_prefix
* Prefix to use in diffs for array hierarchy.
* @param string $value_prefix
* Prefix to use in diffs for array value.
*/
public function __construct(TranslationInterface $translation, $ignore = array('uuid'), $hierarchy_prefix = '::', $value_prefix = ' : ') {
$this->stringTranslation = $translation;
$this->hierarchyPrefix = $hierarchy_prefix;
$this->valuePrefix = $value_prefix;
$this->ignore = $ignore;
}
/**
* Normalizes config for comparison.
*
* Recursively removes elements in the ignore list from configuration,
* as well as empty array values, and sorts at each level by array key, so
* that config from different storage can be compared meaningfully.
*
* @param array $config
* Configuration array to normalize.
*
* @return array
* Normalized configuration array.
*
* @see ConfigDiffer::format()
* @see ConfigDiffer::$ignore
*/
protected function normalize($config) {
// Remove "ignore" elements.
foreach ($this->ignore as $element) {
unset($config[$element]);
}
// Recursively normalize remaining elements, if they are arrays.
foreach ($config as $key => $value) {
if (is_array($value)) {
$new = $this->normalize($value);
if (count($new)) {
$config[$key] = $new;
}
else {
unset($config[$key]);
}
}
}
// Sort and return.
ksort($config);
return $config;
}
/**
* {@inheritdoc}
*/
public function same($source, $target) {
$source = $this->normalize($source);
$target = $this->normalize($target);
return $source == $target;
}
/**
* Formats config for showing differences.
*
* To compute differences, we need to separate the config into lines and use
* line-by-line differencer. The obvious way to split into lines is:
* @code
* explode("\n", Yaml::encode($config))
* @endcode
* But this would highlight meaningless differences due to the often different
* order of config files, and also loses the indentation and context of the
* config hierarchy when differences are computed, making the difference
* difficult to interpret.
*
* So, what we do instead is to take the YAML hierarchy and format it so that
* the hierarchy is shown on each line. So, if you're in element
* $config['foo']['bar'] and the value is 'value', you will see
* 'foo::bar : value'.
*
* @param array $config
* Config array to format. Normalize it first if you want to do diffs.
* @param string $prefix
* (optional) When called recursively, the prefix to put on each line. Omit
* when initially calling this function.
*
* @return string[] Array of config lines formatted so that a line-by-line
* diff will show the context in each line, and meaningful differences will
* be computed.
*
* @see ConfigDiffer::normalize()
* @see ConfigDiffer::$hierarchyPrefix
* @see ConfigDiffer::$valuePrefix
*/
protected function format($config, $prefix = '') {
$lines = array();
foreach ($config as $key => $value) {
$section_prefix = ($prefix) ? $prefix . $this->hierarchyPrefix . $key : $key;
if (is_array($value)) {
$lines[] = $section_prefix;
$newlines = $this->format($value, $section_prefix);
foreach ($newlines as $line) {
$lines[] = $line;
}
}
elseif (is_null($value)) {
$lines[] = $section_prefix . $this->valuePrefix . $this->t('(NULL)');
}
else {
$lines[] = $section_prefix . $this->valuePrefix . $value;
}
}
return $lines;
}
/**
* {@inheritdoc}
*/
public function diff($source, $target) {
$source = $this->normalize($source);
$target = $this->normalize($target);
$source_lines = $this->format($source);
$target_lines = $this->format($target);
return new Diff($source_lines, $target_lines);
}
}