Passed
Pull Request — master (#12)
by Donald
03:06 queued 01:19
created

Store::keyValueContains()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 3
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php namespace Chekote\NounStore;
2
3
use InvalidArgumentException;
4
use OutOfBoundsException;
5
use RuntimeException;
6
7
class Store
8
{
9
    /** @var array */
10
    protected $nouns;
11
12
    /** @var Key */
13
    protected $keyService;
14
15 73
    public function __construct(Key $keyService = null)
16
    {
17 73
        $this->keyService = $keyService ?: Key::getInstance();
18 73
    }
19
20
    /**
21
     * Asserts that a value has been stored for the specified key.
22
     *
23
     * @param  string                   $key   The key to check. @see self::get() for formatting options.
24
     * @param  int                      $index [optional] The index of the key entry to check. If not specified, the
25
     *                                         method will ensure that at least one item is stored for the key.
26
     * @throws OutOfBoundsException     if a value has not been stored for the specified key.
27
     * @throws InvalidArgumentException if both an $index and $key are provided, but the $key contains an nth value
28
     *                                        that does not match the index.
29
     * @return mixed                    The value.
30
     */
31 23
    public function assertKeyExists($key, $index = null)
32
    {
33 23
        list($key, $index) = $this->parseKey($key, $index);
34
35 22
        if (!$this->keyExists($key, $index)) {
36 6
            throw new OutOfBoundsException("Entry '{$this->buildKey($key, $index)}' was not found in the store.");
37
        }
38
39 16
        return $this->get($key, $index);
40
    }
41
42
    /**
43
     * Asserts that the key's value matches the specified value.
44
     *
45
     * @param  string                   $key   The key to check. @see self::get() for formatting options.
46
     * @param  mixed                    $value The expected value.
47
     * @param  int                      $index [optional] The index of the key entry to retrieve. If not specified, the
48
     *                                         method will check the most recent value stored under the key.
49
     * @throws OutOfBoundsException     If a value has not been stored for the specified key.
50
     * @throws InvalidArgumentException if both an $index and $key are provided, but the $key contains an nth value
51
     *                                        that does not match the index.
52
     */
53 10
    public function assertKeyValueIs($key, $value, $index = null)
54
    {
55 10
        list($key, $index) = $this->parseKey($key, $index);
56
57 9
        $this->assertKeyExists($key, $index);
58
59 7
        if ($this->get($key, $index) != $value) {
60 2
            throw new RuntimeException(
61 2
                "Entry '{$this->buildKey($key, $index)}' does not match '" . print_r($value, true) . "'"
62
            );
63
        }
64 5
    }
65
66
    /**
67
     * Asserts that the key's value contains the specified string.
68
     *
69
     * @param  string                   $key   The key to check. @see self::get() for formatting options.
70
     * @param  string                   $value The value expected to be contained within the key's value.
71
     * @param  int                      $index [optional] The index of the key entry to retrieve. If not specified, the
72
     *                                         method will check the most recent value stored under the key.
73
     * @throws OutOfBoundsException     If a value has not been stored for the specified key.
74
     * @throws InvalidArgumentException if both an $index and $key are provided, but the $key contains an nth value
75
     *                                        that does not match the index.
76
     */
77 10
    public function assertKeyValueContains($key, $value, $index = null)
78
    {
79 10
        list($key, $index) = $this->parseKey($key, $index);
80
81 9
        $this->assertKeyExists($key, $index);
82
83 7
        if (!$this->keyValueContains($key, $value, $index)) {
84 2
            throw new RuntimeException(
85 2
                "Entry '{$this->buildKey($key, $index)}' does not contain '$value'"
86
            );
87
        }
88 5
    }
89
90
    /**
91
     * Removes all entries from the store.
92
     *
93
     * @return void
94
     */
95 1
    public function reset()
96
    {
97 1
        $this->nouns = [];
98 1
    }
99
100
    /**
101
     * Retrieves a value for the specified key.
102
     *
103
     * Each key is actually a collection. If you do not specify which item in the collection you want,
104
     * the method will return the most recent entry. You can specify the entry you want by either
105
     * using the plain english 1st, 2nd, 3rd etc in the $key param, or by specifying 0, 1, 2 etc in
106
     * the $index param. For example:
107
     *
108
     * Retrieve the most recent entry "Thing" collection:
109
     *   retrieve("Thing")
110
     *
111
     * Retrieve the 1st entry in the "Thing" collection:
112
     *   retrieve("1st Thing")
113
     *   retrieve("Thing", 0)
114
     *
115
     * Retrieve the 3rd entry in the "Thing" collection:
116
     *   retrieve("3rd Thing")
117
     *   retrieve("Thing", 2)
118
     *
119
     * Please note: The nth value in the string key is indexed from 1. In that "1st" is the first item stored.
120
     * The index parameter is indexed from 0. In that 0 is the first item stored.
121
     *
122
     * Please Note: If you specify both an $index param and an nth in the $key, they must both reference the same index.
123
     * If they do not, the method will throw an InvalidArgumentException.
124
     *
125
     * retrieve("1st Thing", 1);
126
     *
127
     * @param  string                   $key   The key to retrieve the value for. Can be prefixed with an nth descriptor.
128
     * @param  int                      $index [optional] The index of the key entry to retrieve. If not specified, the
129
     *                                         method will return the most recent value stored under the key.
130
     * @throws InvalidArgumentException if both an $index and $key are provided, but the $key contains an nth value
131
     *                                        that does not match the index.
132
     * @return mixed                    The value, or null if no value exists for the specified key/index combination.
133
     */
134 32
    public function get($key, $index = null)
135
    {
136 32
        list($key, $index) = $this->parseKey($key, $index);
137
138 31
        if (!$this->keyExists($key, $index)) {
139 5
            return;
140
        }
141
142 26
        return $index !== null ? $this->nouns[$key][$index] : end($this->nouns[$key]);
143
    }
144
145
    /**
146
     * Retrieves all values for the specified key.
147
     *
148
     * @param  string               $key The key to retrieve the values for.
149
     * @throws OutOfBoundsException if the specified $key does not exist in the store.
150
     * @return array                The values.
151
     */
152 2
    public function getAll($key)
153
    {
154 2
        if (!isset($this->nouns[$key])) {
155 1
            throw new OutOfBoundsException("'$key' does not exist in the store");
156
        }
157
158 1
        return $this->nouns[$key];
159
    }
160
161
    /**
162
     * Determines if a value has been stored for the specified key.
163
     *
164
     * @param  string                   $key   The key to check.
165
     * @param  int                      $index [optional] The index of the key entry to check. If not specified, the
166
     *                                         method will ensure that at least one item is stored for the key.
167
     * @throws InvalidArgumentException if both an $index and $key are provided, but the $key contains an nth value
168
     *                                        that does not match the index.
169
     * @return bool                     True if the a value has been stored, false if not.
170
     */
171 42
    public function keyExists($key, $index = null)
172
    {
173 42
        list($key, $index) = $this->parseKey($key, $index);
174
175 41
        return $index !== null ? isset($this->nouns[$key][$index]) : isset($this->nouns[$key]);
176
    }
177
178
    /**
179
     * Asserts that the key's value contains the specified string.
180
     *
181
     * @param  string                   $key   The key to check. @see self::get() for formatting options.
182
     * @param  string                   $value The value expected to be contained within the key's value.
183
     * @param  int                      $index [optional] The index of the key entry to retrieve. If not specified, the
184
     *                                         method will check the most recent value stored under the key.
185
     * @throws InvalidArgumentException if both an $index and $key are provided, but the $key contains an nth value
186
     *                                        that does not match the index.
187
     * @return bool                     True if the key's value contains the specified string, false if not.
188
     */
189 17
    public function keyValueContains($key, $value, $index = null)
190
    {
191 17
        list($key, $index) = $this->parseKey($key, $index);
192
193 16
        $actual = $this->get($key, $index);
194
195 16
        return is_string($actual) && strpos($actual, $value) !== false;
196
    }
197
198
    /**
199
     * Stores a value for the specified key.
200
     *
201
     * @param string $key   The key to store the value under.
202
     * @param mixed  $value The value to store.
203
     */
204 73
    public function set($key, $value)
205
    {
206 73
        $this->nouns[$key][] = $value;
207 73
    }
208
209
    /**
210
     * Parses a key into the separate key and index value.
211
     *
212
     * @example parseKey("Item"): ["Item", null]
213
     * @example parseKey("Item", 1): ["Item", 1]
214
     * @example parseKey("1st Item"): ["Item", 0]
215
     * @example parseKey("2nd Item"): ["Item", 1]
216
     * @example parseKey("3rd Item"): ["Item", 2]
217
     *
218
     * @param  string                   $key   the key to parse.
219
     * @param  int                      $index [optional] the index to return if the key does not contain one.
220
     * @throws InvalidArgumentException if both an $index and $key are provided, but the $key contains an nth value
221
     *                                        that does not match the index.
222
     * @return array                    a tuple, the 1st being the key with the nth removed, and the 2nd being the
223
     *                                        index.
224
     */
225 61
    protected function parseKey($key, $index = null)
226
    {
227 61
        if (preg_match('/^([1-9][0-9]*)(?:st|nd|rd|th) (.+)$/', $key, $matches)) {
228 38
            if ($index !== null && $index != $matches[1] - 1) {
229 11
                throw new InvalidArgumentException(
230 11
                    "$index was provided for index param when key '$key' contains an nth value, but they do not match"
231
                );
232
            }
233
234 27
            $index = $matches[1] - 1;
235 27
            $key = $matches[2];
236
        }
237
238 50
        return [$key, $index];
239
    }
240
241
    /**
242
     * Builds a key from it's separate key and index values.
243
     *
244
     * @example buildKey("Item", null): "Item"
245
     * @example buildKey("Item", 0): "1st Item"
246
     * @example buildKey("Item", 1): "2nd Item"
247
     * @example buildKey("Item", 2): "3rd Item"
248
     *
249
     * @param  string                   $key   The key to check.
250
     * @param  int                      $index The index (zero indexed) value for the key. If not specified, the method
251
     *                                         will not add an index notation to the key.
252
     * @throws InvalidArgumentException if $key is not a string.
253
     * @throws InvalidArgumentException if $index is not an int.
254
     * @return string                   the key with the index, or just the key if index is null.
255
     */
256 17
    protected function buildKey($key, $index)
257
    {
258 17
        if ($index === null) {
259 3
            return $key;
260
        }
261
262 14
        $nth = $index + 1;
263
264 14
        return $nth . $this->keyService->getOrdinal($nth) . ' ' . $key;
265
    }
266
}
267