Completed
Push — master ( 639b92...4cfed6 )
by Donald
18s
created

Store::assertKeyExists()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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