1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PeekAndPoke\Component\Slumber\Data\Addon\Journal\DomainModel; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* @author Karsten J. Gerber <[email protected]> |
7
|
|
|
*/ |
8
|
|
|
class RecordableHistory |
9
|
|
|
{ |
10
|
|
|
/** @var Record[] */ |
11
|
|
|
private $records; |
12
|
|
|
/** @var RecordDiff[] */ |
13
|
|
|
private $diffs; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* @param Record $initial |
17
|
|
|
* @param Record $final |
18
|
|
|
* @param RecordDiff[] $diffs |
19
|
|
|
* |
20
|
|
|
* @return RecordableHistory |
21
|
|
|
*/ |
22
|
2 |
|
public static function fromInitialAndFinalAndDiffs(Record $initial, Record $final, $diffs) |
23
|
|
|
{ |
24
|
2 |
|
$ret = new RecordableHistory([$initial, $final]); |
25
|
2 |
|
$ret->diffs = $diffs; |
26
|
|
|
|
27
|
2 |
|
return $ret; |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @param Record[] $records |
32
|
|
|
*/ |
33
|
3 |
|
public function __construct($records) |
34
|
|
|
{ |
35
|
3 |
|
$this->records = $records; |
36
|
3 |
|
} |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @return float |
40
|
|
|
*/ |
41
|
|
|
public function getCompactionRate() |
42
|
|
|
{ |
43
|
|
|
$compacted = 0; |
44
|
|
|
$notCompacted = 0; |
45
|
|
|
|
46
|
|
|
foreach ($this->records as $record) { |
47
|
|
|
if ($record->getIsCompacted() === false || $record->getCompactedHistory() === null) { |
48
|
|
|
$notCompacted++; |
49
|
|
|
} else { |
50
|
|
|
$compacted += \count($record->getCompactedHistory()->getDiffs()); |
51
|
|
|
} |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
if ($compacted + $notCompacted === 0) { |
55
|
|
|
return 0; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
return $compacted / ($compacted + $notCompacted); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @return Record |
63
|
|
|
*/ |
64
|
3 |
View Code Duplication |
public function getInitialRecord() |
|
|
|
|
65
|
|
|
{ |
66
|
3 |
|
$record = \count($this->records) > 0 ? $this->records[0] : new NullRecord(); |
67
|
|
|
|
68
|
3 |
|
if ($record->getIsCompacted() && |
69
|
3 |
|
$record->getCompactedHistory() !== null |
70
|
|
|
) { |
71
|
2 |
|
return $record->getCompactedHistory()->getInitialRecord(); |
72
|
|
|
} |
73
|
|
|
|
74
|
3 |
|
return $record; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @return Record |
79
|
|
|
*/ |
80
|
3 |
View Code Duplication |
public function getFinalRecord() |
|
|
|
|
81
|
|
|
{ |
82
|
3 |
|
$record = \count($this->records) > 0 ? $this->records[\count($this->records) - 1] : new NullRecord(); |
83
|
|
|
|
84
|
3 |
|
if ($record->getIsCompacted() && |
85
|
3 |
|
$record->getCompactedHistory() !== null) { |
86
|
2 |
|
return $record->getCompactedHistory()->getFinalRecord(); |
87
|
|
|
} |
88
|
|
|
|
89
|
3 |
|
return $record; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* {@inheritdoc} |
94
|
|
|
*/ |
95
|
3 |
|
public function getRecords() |
96
|
|
|
{ |
97
|
3 |
|
return $this->records; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* @return RecordDiff[] |
102
|
|
|
*/ |
103
|
3 |
|
public function getDiffs() |
104
|
|
|
{ |
105
|
3 |
|
if ($this->diffs === null) { |
106
|
3 |
|
$this->calculateDiffs(); |
107
|
|
|
} |
108
|
|
|
|
109
|
3 |
|
return $this->diffs; |
110
|
|
|
} |
111
|
|
|
|
112
|
3 |
|
protected function calculateDiffs() |
113
|
|
|
{ |
114
|
3 |
|
$this->diffs = []; |
115
|
|
|
|
116
|
3 |
|
$previous = new NullRecord(); |
117
|
|
|
|
118
|
3 |
|
foreach ($this->records as $current) { |
119
|
|
|
|
120
|
3 |
|
if ($current->getIsCompacted() === false) { |
121
|
3 |
|
$diff = $this->calculateDiff($previous, $current); |
122
|
|
|
|
123
|
3 |
|
if ($diff->getChangesCount() > 0) { |
124
|
|
|
|
125
|
3 |
|
$this->diffs[] = $diff; |
126
|
|
|
} |
127
|
|
|
|
128
|
3 |
|
$previous = $current; |
129
|
|
|
|
130
|
|
|
} else { |
131
|
|
|
|
132
|
2 |
|
$history = $current->getCompactedHistory(); |
133
|
|
|
|
134
|
2 |
|
if ($history) { |
135
|
2 |
|
foreach ($history->getDiffs() as $diff) { |
136
|
2 |
|
$this->diffs[] = $diff; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
140
|
3 |
|
$previous = $history->getFinalRecord(); |
141
|
|
|
} |
142
|
|
|
} |
143
|
3 |
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* @param Record $before |
147
|
|
|
* @param Record $after |
148
|
|
|
* |
149
|
|
|
* @return RecordDiff |
150
|
|
|
*/ |
151
|
3 |
|
protected function calculateDiff(Record $before, Record $after) |
152
|
|
|
{ |
153
|
3 |
|
$diff = new RecordDiff($after->getChangeDate(), $after->getChangedBy()); |
154
|
|
|
|
155
|
3 |
|
$beforeData = (array) $before->getRecordData(); |
156
|
3 |
|
$afterData = (array) $after->getRecordData(); |
157
|
|
|
|
158
|
3 |
|
$this->diffRecursiveFromBeforeToAfter($diff, '', $beforeData, $afterData); |
159
|
3 |
|
$this->diffRecursiveFromAfterToBefore($diff, '', $beforeData, $afterData); |
160
|
|
|
|
161
|
3 |
|
return $diff; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* @param RecordDiff $diff |
166
|
|
|
* @param string $pathInArray |
167
|
|
|
* @param mixed[] $before |
168
|
|
|
* @param mixed[] $after |
169
|
|
|
*/ |
170
|
3 |
View Code Duplication |
private function diffRecursiveFromBeforeToAfter(RecordDiff $diff, $pathInArray, $before, $after) |
|
|
|
|
171
|
|
|
{ |
172
|
3 |
|
if (! \is_array($before) && ! $before instanceof \Traversable) { |
173
|
|
|
|
174
|
3 |
|
$beforeValue = $this->convertToString($before); |
175
|
3 |
|
$afterValue = $this->convertToString($after); |
176
|
|
|
|
177
|
3 |
|
if ($beforeValue !== $afterValue) { |
178
|
3 |
|
$diff->addChange(new RecordDiffEntry($pathInArray, $beforeValue, $afterValue)); |
179
|
|
|
} |
180
|
|
|
} else { |
181
|
|
|
|
182
|
3 |
|
foreach ($before as $key => $beforeValue) { |
183
|
|
|
|
184
|
3 |
|
$afterValue = null; |
185
|
|
|
|
186
|
3 |
|
if ((\is_array($after) || $after instanceof \ArrayAccess) && isset($after[$key])) { |
187
|
3 |
|
$afterValue = $after[$key]; |
188
|
|
|
} |
189
|
|
|
|
190
|
3 |
|
$this->diffRecursiveFromBeforeToAfter( |
191
|
3 |
|
$diff, |
192
|
3 |
|
empty($pathInArray) ? $key : $pathInArray . '.' . $key, |
193
|
3 |
|
$beforeValue, |
194
|
3 |
|
$afterValue |
195
|
|
|
); |
196
|
|
|
} |
197
|
|
|
} |
198
|
3 |
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* @param RecordDiff $diff |
202
|
|
|
* @param string $pathInArray |
203
|
|
|
* @param array $before |
204
|
|
|
* @param array $after |
205
|
|
|
*/ |
206
|
3 |
View Code Duplication |
private function diffRecursiveFromAfterToBefore(RecordDiff $diff, $pathInArray, $before, $after) |
|
|
|
|
207
|
|
|
{ |
208
|
3 |
|
if (! \is_array($after) && ! $after instanceof \Traversable) { |
209
|
|
|
|
210
|
3 |
|
$beforeValue = $this->convertToString($before); |
211
|
3 |
|
$afterValue = $this->convertToString($after); |
212
|
|
|
|
213
|
3 |
|
if ($beforeValue !== $afterValue) { |
214
|
3 |
|
$diff->addChange(new RecordDiffEntry($pathInArray, $beforeValue, $afterValue)); |
215
|
|
|
} |
216
|
|
|
} else { |
217
|
|
|
|
218
|
|
|
// first compare from before to after |
219
|
3 |
|
foreach ($after as $key => $afterValue) { |
220
|
|
|
|
221
|
3 |
|
$beforeValue = null; |
222
|
|
|
|
223
|
3 |
|
if ((\is_array($before) || $before instanceof \ArrayAccess) && isset($before[$key])) { |
224
|
3 |
|
$beforeValue = $before[$key]; |
225
|
|
|
} |
226
|
|
|
|
227
|
3 |
|
$this->diffRecursiveFromAfterToBefore( |
228
|
3 |
|
$diff, |
229
|
3 |
|
empty($pathInArray) ? $key : $pathInArray . '.' . $key, |
230
|
3 |
|
$beforeValue, |
231
|
3 |
|
$afterValue |
232
|
|
|
); |
233
|
|
|
} |
234
|
|
|
} |
235
|
3 |
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* @param $value |
239
|
|
|
* @param string $fallback |
240
|
|
|
* |
241
|
|
|
* @return string |
242
|
|
|
*/ |
243
|
3 |
|
private function convertToString($value, $fallback = 'N/A') |
244
|
|
|
{ |
245
|
|
|
try { |
246
|
3 |
|
if ($value === null) { |
247
|
3 |
|
return ''; |
248
|
|
|
} |
249
|
|
|
|
250
|
3 |
|
if (is_scalar($value)) { |
251
|
3 |
|
return (string) $value; |
252
|
|
|
} |
253
|
|
|
|
254
|
3 |
|
if (\is_array($value)) { |
255
|
|
|
return json_encode($value, JSON_PRETTY_PRINT); |
256
|
|
|
} |
257
|
|
|
|
258
|
3 |
|
if ($value instanceof \DateTime) { |
259
|
|
|
return $value->format('c'); |
260
|
|
|
} |
261
|
|
|
|
262
|
3 |
|
return (string) $fallback; |
263
|
|
|
|
264
|
|
|
} catch (\Exception $e) { |
265
|
|
|
return $fallback; |
266
|
|
|
} |
267
|
|
|
} |
268
|
|
|
} |
269
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.