TwoDimensionalHashMap::sortPrimaryKeys()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
c 0
b 0
f 0
ccs 7
cts 7
cp 1
rs 9.4285
cc 2
eloc 6
nc 2
nop 1
crap 2
1
<?php
2
3
/*
4
 * This file is part of the puli/manager package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Puli\Manager\Util;
13
14
use OutOfBoundsException;
15
16
/**
17
 * An hash-map for values identified by a two-dimensional key.
18
 *
19
 * Every value in the store has a primary key and a secondary key. When adding
20
 * values to the store, both keys need to be defined. When retrieving values
21
 * from the store, you can either get the value for a composite key or all
22
 * values for the primary key indexed by their secondary keys.
23
 *
24
 * @since  1.0
25
 *
26
 * @author Bernhard Schussek <[email protected]>
27
 */
28
class TwoDimensionalHashMap
29
{
30
    /**
31
     * @var array[]
32
     */
33
    private $values = array();
34
35
    /**
36
     * Sets a value in the store.
37
     *
38
     * @param int|string $primaryKey   The primary key.
39
     * @param int|string $secondaryKey The secondary key.
40
     * @param mixed      $value        The value.
41
     */
42 146
    public function set($primaryKey, $secondaryKey, $value)
43
    {
44 146
        if (!isset($this->values[$primaryKey])) {
45 146
            $this->values[$primaryKey] = array();
46
        }
47
48 146
        $this->values[$primaryKey][$secondaryKey] = $value;
49 146
    }
50
51
    /**
52
     * Removes a value from the store.
53
     *
54
     * This method ignores non-existing keys.
55
     *
56
     * @param int|string $primaryKey   The primary key.
57
     * @param int|string $secondaryKey The secondary key.
58
     */
59 31
    public function remove($primaryKey, $secondaryKey)
60
    {
61 31
        unset($this->values[$primaryKey][$secondaryKey]);
62
63 31
        if (isset($this->values[$primaryKey]) && 0 === count($this->values[$primaryKey])) {
64 24
            unset($this->values[$primaryKey]);
65
        }
66 31
    }
67
68
    /**
69
     * Removes all values for the given primary key.
70
     *
71
     * This method ignores non-existing keys.
72
     *
73
     * @param int|string $primaryKey The primary key.
74
     */
75 1
    public function removeAll($primaryKey)
76
    {
77 1
        unset($this->values[$primaryKey]);
78 1
    }
79
80
    /**
81
     * Returns a value from the store.
82
     *
83
     * @param int|string $primaryKey   The primary key.
84
     * @param int|string $secondaryKey The secondary key.
85
     *
86
     * @return mixed The value.
87
     *
88
     * @throws OutOfBoundsException If no value is set for the given keys.
89
     */
90 44
    public function get($primaryKey, $secondaryKey)
91
    {
92 44 View Code Duplication
        if (!isset($this->values[$primaryKey][$secondaryKey])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
93 1
            throw new OutOfBoundsException(sprintf(
94 1
                'The key ("%s","%s") does not exist.',
95
                $primaryKey,
96
                $secondaryKey
97
            ));
98
        }
99
100 43
        return $this->values[$primaryKey][$secondaryKey];
101
    }
102
103
    /**
104
     * Returns whether the store contains the given key(s).
105
     *
106
     * The secondary key is optional. If you don't pass it, this method returns
107
     * `true` if the store contains the given primary key with any secondary
108
     * key.
109
     *
110
     * @param int|string      $primaryKey   The primary key.
111
     * @param int|string|null $secondaryKey The secondary key.
112
     *
113
     * @return bool Returns `true` if the store contains the given key(s).
114
     */
115 107
    public function contains($primaryKey, $secondaryKey = null)
116
    {
117 107
        if (null !== $secondaryKey) {
118 99
            return isset($this->values[$primaryKey][$secondaryKey]);
119
        }
120
121 86
        return isset($this->values[$primaryKey]);
122
    }
123
124
    /**
125
     * Returns the first value set for the given primary key.
126
     *
127
     * @param int|string $primaryKey The primary key.
128
     *
129
     * @return mixed The value.
130
     *
131
     * @throws OutOfBoundsException If the primary key does not exist.
132
     */
133 49 View Code Duplication
    public function getFirst($primaryKey)
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...
134
    {
135 49
        if (!isset($this->values[$primaryKey])) {
136 1
            throw new OutOfBoundsException(sprintf(
137 1
                'The key "%s" does not exist.',
138
                $primaryKey
139
            ));
140
        }
141
142 48
        return reset($this->values[$primaryKey]);
143
    }
144
145
    /**
146
     * Returns the last value set for the given primary key.
147
     *
148
     * @param int|string $primaryKey The primary key.
149
     *
150
     * @return mixed The value.
151
     *
152
     * @throws OutOfBoundsException If the primary key does not exist.
153
     */
154 2 View Code Duplication
    public function getLast($primaryKey)
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...
155
    {
156 2
        if (!isset($this->values[$primaryKey])) {
157 1
            throw new OutOfBoundsException(sprintf(
158 1
                'The key "%s" does not exist.',
159
                $primaryKey
160
            ));
161
        }
162
163 1
        return end($this->values[$primaryKey]);
164
    }
165
166
    /**
167
     * Returns the number of secondary keys set for the given primary key.
168
     *
169
     * @param int|string $primaryKey The primary key.
170
     *
171
     * @return int The number of secondary keys set for the primary key.
172
     *
173
     * @throws OutOfBoundsException If the primary key does not exist.
174
     */
175 3 View Code Duplication
    public function getCount($primaryKey)
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...
176
    {
177 3
        if (!isset($this->values[$primaryKey])) {
178 1
            throw new OutOfBoundsException(sprintf(
179 1
                'The key "%s" does not exist.',
180
                $primaryKey
181
            ));
182
        }
183
184 2
        return count($this->values[$primaryKey]);
185
    }
186
187
    /**
188
     * Returns all values set for the given primary key.
189
     *
190
     * @param int|string $primaryKey The primary key.
191
     *
192
     * @return array The values indexed by their secondary keys.
193
     *
194
     * @throws OutOfBoundsException If the primary key does not exist.
195
     */
196 78 View Code Duplication
    public function listByPrimaryKey($primaryKey)
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...
197
    {
198 78
        if (!isset($this->values[$primaryKey])) {
199 3
            throw new OutOfBoundsException(sprintf(
200 3
                'The key "%s" does not exist.',
201
                $primaryKey
202
            ));
203
        }
204
205 75
        return $this->values[$primaryKey];
206
    }
207
208
    /**
209
     * Returns all values set for the given secondary key.
210
     *
211
     * @param int|string $secondaryKey The secondary key.
212
     *
213
     * @return array The values indexed by their primary keys.
214
     *
215
     * @throws OutOfBoundsException If the secondary key does not exist.
216
     */
217 13
    public function listBySecondaryKey($secondaryKey)
218
    {
219 13
        $list = array();
220
221 13
        foreach ($this->values as $primaryKey => $valuesBySecondaryKey) {
222 13
            if (isset($valuesBySecondaryKey[$secondaryKey])) {
223 13
                $list[$primaryKey] = $valuesBySecondaryKey[$secondaryKey];
224
            }
225
        }
226
227 13
        if (!$list) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $list of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
228
            throw new OutOfBoundsException(sprintf(
229
                'The key "%s" does not exist.',
230
                $secondaryKey
231
            ));
232
        }
233
234 13
        return $list;
235
    }
236
237
    /**
238
     * Returns the secondary keys for the given primary key.
239
     *
240
     * The primary key is optional. If this argument is not provided, all secondary keys will be returned.
241
     *
242
     * @param int|string|null $primaryKey The primary key.
243
     *
244
     * @return int[]|string[] The secondary keys.
245
     *
246
     * @throws OutOfBoundsException If the primary key does not exist.
247
     */
248 31
    public function getSecondaryKeys($primaryKey = null)
249
    {
250 31 View Code Duplication
        if ($primaryKey) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
251 17
            if (!isset($this->values[$primaryKey])) {
252 1
                throw new OutOfBoundsException(sprintf(
253 1
                    'The key "%s" does not exist.',
254
                    $primaryKey
255
                ));
256
            }
257
258 16
            return array_keys($this->values[$primaryKey]);
259
        }
260
261 14
        $allSecondaryKeys = array();
262
263 14
        foreach ($this->values as $primaryKey => $valuesBySecondaryKey) {
264 13
            foreach ($valuesBySecondaryKey as $secondaryKey => $values) {
265 13
                $allSecondaryKeys[$secondaryKey] = true;
266
            }
267
        }
268
269 14
        return array_keys($allSecondaryKeys);
270
    }
271
272
    /**
273
     * Returns all primary keys.
274
     *
275
     * @return int[]|string[] The primary keys.
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use integer[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
276
     */
277 88
    public function getPrimaryKeys()
278
    {
279 88
        return array_keys($this->values);
280
    }
281
282
    /**
283
     * Returns the contents of the store as array.
284
     *
285
     * @return array[] A multi-dimensional array containing all values by
286
     *                 their primary and secondary keys.
287
     */
288 54
    public function toArray()
289
    {
290 54
        return $this->values;
291
    }
292
293
    /**
294
     * Returns whether the map is empty.
295
     *
296
     * @return bool Returns `true` if the map is empty and `false` otherwise.
297
     */
298 4
    public function isEmpty()
299
    {
300 4
        return 0 === count($this->values);
301
    }
302
303
    /**
304
     * Sorts the primary keys of the map.
305
     *
306
     * @param int[]|string[]|null $order The keys in the desired order.
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $order a bit more specific; maybe use null|integer[].
Loading history...
307
     */
308 2
    public function sortPrimaryKeys(array $order = null)
309
    {
310 2
        if (!$order) {
311 1
            ksort($this->values);
312
313 1
            return;
314
        }
315
316 1
        $orderedKeys = array_intersect_key(array_flip($order), $this->values);
317
318 1
        $this->values = array_replace($orderedKeys, $this->values);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_replace($orderedKeys, $this->values) of type array is incompatible with the declared type array<integer,array> of property $values.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
319 1
    }
320
321
    /**
322
     * Sorts the secondary keys of a map entry.
323
     *
324
     * @param int|string          $primaryKey The primary key.
325
     * @param int[]|string[]|null $order      The keys in the desired order.
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $order a bit more specific; maybe use null|integer[].
Loading history...
326
     */
327 3
    public function sortSecondaryKeys($primaryKey, array $order = null)
328
    {
329 3
        if (!isset($this->values[$primaryKey])) {
330 1
            throw new OutOfBoundsException(sprintf(
331 1
                'The key "%s" does not exist.',
332
                $primaryKey
333
            ));
334
        }
335
336 2
        if (!$order) {
337 1
            ksort($this->values[$primaryKey]);
338
339 1
            return;
340
        }
341
342 1
        $orderedKeys = array_intersect_key(array_flip($order), $this->values[$primaryKey]);
343
344 1
        $this->values[$primaryKey] = array_replace($orderedKeys, $this->values[$primaryKey]);
345 1
    }
346
}
347