RecordableHistory::calculateDiff()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.8666
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
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()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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