Passed
Push — master ( 2c8815...d0e3c7 )
by Alexander
02:28
created

PrioritizedObjectList::current()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Weblabltd\Component\Util\Collection;
6
7
/**
8
 * Implements a list of prioritized objects that maintains the insertion order for items with the same priority.
9
 */
10
class PrioritizedObjectList implements \Countable, \Iterator
11
{
12
    const DEFAULT_PRIORITY = 100;
13
14
    /**
15
     * @var array
16
     */
17
    private $objects = [];
18
    /**
19
     * @var array
20
     */
21
    private $indexes = [];
22
    /**
23
     * @var int
24
     */
25
    private $cursor = 0;
26
    /**
27
     * @var int
28
     */
29
    private $serial = PHP_INT_MAX;
30
    /**
31
     * For lazy sorting the list when that's necessary.
32
     *
33
     * @var bool
34
     */
35
    private $sorted = true;
36
37
    /**
38
     * Adds an object to the list.
39
     *
40
     * Adding an object that's already in the list will effectively override the same object
41
     *
42
     * @param object   $object
43
     * @param null|int $priority Priority should be a positive number. If NULL the DEFAULT_PRIORITY will be used
44
     *
45
     * @return PrioritizedObjectList
46
     */
47 15
    public function add($object, ?int $priority = self::DEFAULT_PRIORITY): PrioritizedObjectList
48
    {
49 15
        if (!is_object($object)) {
50 5
            throw new \InvalidArgumentException(
51 5
                sprintf('Expects an an object, got "%s" instead', gettype($object))
52
            );
53
        }
54
55 10
        if ($priority < 0) {
56 1
            throw new \InvalidArgumentException(
57 1
                sprintf('The priority should be a positive integer or null for the default priority, got %s instead', gettype($priority))
58
            );
59
        }
60
61 9
        $index = spl_object_hash($object);
62
63 9
        if (!isset($this->objects[$index])) {
64 9
            $this->objects[$index] = [$object, [$priority ?? static::DEFAULT_PRIORITY, $this->serial--]];
65 9
            $this->indexes[]       = $index;
66
        } else {
67
            // if object is present just override it's priority
68 1
            $this->objects[$index][1][0] = $priority ?? static::DEFAULT_PRIORITY;
69
        }
70
71
        // mark as not sorted
72 9
        $this->sorted = false;
73
74 9
        return $this;
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80 10
    public function count()
81
    {
82 10
        return count($this->objects);
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 10
    public function current()
89
    {
90 10
        $this->sort();
91
92 10
        if (!isset($this->indexes[$this->cursor])) {
93 1
            throw new \OutOfBoundsException('There is no object at the current position');
94
        }
95
96 9
        return $this->objects[$this->indexes[$this->cursor]][0];
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 8
    public function next()
103
    {
104 8
        $this->sort();
105
106 8
        ++$this->cursor;
107 8
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112 4
    public function key()
113
    {
114 4
        $this->sort();
115
116 4
        return $this->cursor;
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122 8
    public function valid()
123
    {
124 8
        $this->sort();
125
126 8
        return isset($this->indexes[$this->cursor]);
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132 7
    public function rewind()
133
    {
134 7
        $this->sort();
135
136 7
        $this->cursor = 0;
137 7
    }
138
139
    /**
140
     * Sorts the list.
141
     */
142 13
    private function sort(): void
143
    {
144 13
        if ($this->sorted) {
145 13
            return;
146
        }
147
148 9
        uasort($this->objects, function ($o1, $o2) {
149 8
            return $o2[1][0] <=> $o1[1][0];
150 9
        });
151
152 9
        $this->indexes = array_keys($this->objects);
153
154 9
        $this->sorted = true;
155 9
    }
156
}
157