Passed
Push — main ( 144749...cba6e1 )
by Rait
02:02
created

Synchronization::__invoke()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 32
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 27
c 1
b 0
f 0
nc 6
nop 4
dl 0
loc 32
rs 8.5546
1
<?php
2
3
namespace Raigu\OrderedListsSynchronization;
4
5
use Iterator;
6
7
/**
8
 * I can detect what elements must be added to a target and what
9
 * elements must be removed from the target in order to keep
10
 * the target in sync with the source.
11
 *
12
 * Example:
13
 * <code>
14
 * $s = new Synchronizer();
15
 * $s(
16
 *     new ArrayIterator(['A', 'B']), // source
17
 *     new ArrayIterator(['B', 'C']), // Target
18
 *     function ($element) { echo "ADD: {$element}\n"; },
19
 *     function ($element) { echo "REMOVE: {$element}\n"; }
20
 * );
21
 * </code>
22
 *
23
 * Will output:
24
 * ADD: A
25
 * REMOVE: C
26
 */
27
final class Synchronization
28
{
29
    public function __invoke(Iterator $source, Iterator $target, callable $add, callable $remove)
30
    {
31
        $s = $source->current();
32
        $t = $target->current();
33
34
        while (!is_null($s) or !is_null($t)) {
35
            if (is_null($t)) {
36
                // Reached at the end of target. Add all reminding source elements.
37
                $add($s);
38
                $source->next();
39
                $s = $source->current();
40
            } else if (is_null($s)) {
41
                // Reached at the end of source. Remove all remaining target elements.
42
                $remove($t);
43
                $target->next();
44
                $t = $target->current();
45
            } else {
46
                $sv = strval($s);
47
                $tv = strval($t);
48
                if ($sv === $tv) {
49
                    $source->next();
50
                    $s = $source->current();
51
                    $target->next();
52
                    $t = $target->current();
53
                } else if ($sv < $tv) {
54
                    $add($s);
55
                    $source->next();
56
                    $s = $source->current();
57
                } else { // $s > $t
58
                    $remove($t);
59
                    $target->next();
60
                    $t = $target->current();
61
                }
62
            }
63
        }
64
    }
65
}