PrioritizedObjectList::valid()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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 10
    public function add(object $object, ?int $priority = self::DEFAULT_PRIORITY): PrioritizedObjectList
48
    {
49 10
        if ($priority < 0) {
50 1
            throw new \InvalidArgumentException(
51 1
                sprintf('The priority should be a positive integer or null for the default priority, got %s instead', gettype($priority))
52
            );
53
        }
54
55 9
        $index = spl_object_hash($object);
56
57 9
        if (!isset($this->objects[$index])) {
58 9
            $this->objects[$index] = [$object, [$priority ?? static::DEFAULT_PRIORITY, $this->serial--]];
59 9
            $this->indexes[]       = $index;
60
        } else {
61
            // if object is present just override it's priority
62 1
            $this->objects[$index][1][0] = $priority ?? static::DEFAULT_PRIORITY;
63
        }
64
65
        // mark as not sorted
66 9
        $this->sorted = false;
67
68 9
        return $this;
69
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74 10
    public function count()
75
    {
76 10
        return count($this->objects);
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82 10
    public function current()
83
    {
84 10
        $this->sort();
85
86 10
        if (!isset($this->indexes[$this->cursor])) {
87 1
            throw new \OutOfBoundsException('There is no object at the current position');
88
        }
89
90 9
        return $this->objects[$this->indexes[$this->cursor]][0];
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96 8
    public function next()
97
    {
98 8
        $this->sort();
99
100 8
        ++$this->cursor;
101 8
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106 4
    public function key()
107
    {
108 4
        $this->sort();
109
110 4
        return $this->cursor;
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116 8
    public function valid()
117
    {
118 8
        $this->sort();
119
120 8
        return isset($this->indexes[$this->cursor]);
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 7
    public function rewind()
127
    {
128 7
        $this->sort();
129
130 7
        $this->cursor = 0;
131 7
    }
132
133
    /**
134
     * Sorts the list.
135
     */
136 13
    private function sort(): void
137
    {
138 13
        if ($this->sorted) {
139 13
            return;
140
        }
141
142 9
        uasort($this->objects, function ($o1, $o2) {
143 8
            return $o2[1][0] <=> $o1[1][0];
144 9
        });
145
146 9
        $this->indexes = array_keys($this->objects);
147
148 9
        $this->sorted = true;
149 9
    }
150
}
151