Passed
Push — master ( c803c1...00f1f1 )
by Eric
13:27
created

TestArrayAccess   A

Complexity

Total Complexity 5

Size/Duplication

Total Lines 53
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 53
rs 10
c 0
b 0
f 0
wmc 5
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Utility - Collection of various PHP utility functions.
7
 *
8
 * @author    Eric Sizemore <[email protected]>
9
 *
10
 * @version   2.0.0
11
 *
12
 * @copyright (C) 2017 - 2024 Eric Sizemore
13
 * @license   The MIT License (MIT)
14
 *
15
 * Copyright (C) 2017 - 2024 Eric Sizemore <https://www.secondversion.com>.
16
 *
17
 * Permission is hereby granted, free of charge, to any person obtaining a copy
18
 * of this software and associated documentation files (the "Software"), to
19
 * deal in the Software without restriction, including without limitation the
20
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
21
 * sell copies of the Software, and to permit persons to whom the Software is
22
 * furnished to do so, subject to the following conditions:
23
 *
24
 * The above copyright notice and this permission notice shall be included in
25
 * all copies or substantial portions of the Software.
26
 *
27
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33
 * THE SOFTWARE.
34
 */
35
36
namespace Esi\Utility\Tests;
37
38
use ArrayAccess;
39
use Esi\Utility\Arrays;
40
use PHPUnit\Framework\Attributes\CoversClass;
41
use PHPUnit\Framework\TestCase;
42
use stdClass;
43
44
use function is_null;
45
46
/**
47
 * Array utilities tests.
48
 *
49
 * @internal
50
 */
51
#[CoversClass(Arrays::class)]
52
class ArraysTest extends TestCase
53
{
54
    /**
55
     * Test Arrays::isAssociative().
56
     */
57
    public function testIsAssociative(): void
58
    {
59
        $array    = [0, 1, 2, 3, 4];
60
        $arrayTwo = ['test' => 'testing', 'testing' => 'what'];
61
62
        self::assertFalse(Arrays::isAssociative($array));
63
        self::assertTrue(Arrays::isAssociative($arrayTwo));
64
    }
65
66
    /**
67
     * Test Arrays::get().
68
     */
69
    public function testGet(): void
70
    {
71
        $array = ['this' => 'is', 'a' => 'test'];
72
73
        self::assertSame('is', Arrays::get($array, 'this'));
74
        self::assertSame('test', Arrays::get($array, 'a'));
75
        self::assertNull(Arrays::get($array, 'notexist'));
76
    }
77
78
    /**
79
     * Test Arrays::set().
80
     */
81
    public function testSet(): void
82
    {
83
        $array    = ['this' => 1, 'is' => 2, 'a' => 3, 'test' => 4];
84
        $newArray = ['that' => 4, 'was' => 3, 'a' => 2, 'test' => 1];
85
86
        Arrays::set($array, 'test', 5);
87
        self::assertSame(5, Arrays::get($array, 'test'));
88
89
        Arrays::set($array, null, $newArray);
90
        self::assertSame(4, Arrays::get($array, 'that'));
91
    }
92
93
    /**
94
     * Test Arrays::keyExists().
95
     */
96
    public function testKeyExists(): void
97
    {
98
        $array = ['test' => 1];
99
100
        $testArrayAccess         = new TestArrayAccess();
101
        $testArrayAccess['test'] = 1;
102
103
        self::assertTrue(Arrays::keyExists($array, 'test'));
104
        self::assertFalse(Arrays::keyExists($array, 'this'));
105
106
        self::assertTrue(Arrays::keyExists($testArrayAccess, 'test'));
107
        self::assertFalse(Arrays::keyExists($testArrayAccess, 'this'));
108
    }
109
110
    /**
111
     * Test Arrays::valueExists().
112
     */
113
    public function testValueExists(): void
114
    {
115
        $array = ['test' => 1, 1 => 'foo', 'bar' => 2];
116
117
        self::assertTrue(Arrays::valueExists($array, 1));
118
        self::assertFalse(Arrays::valueExists($array, 'test'));
119
120
        self::assertTrue(Arrays::valueExists($array, 'foo'));
121
        self::assertFalse(Arrays::valueExists($array, 'bar'));
122
    }
123
124
    public function testExistsDeprecation(): void
125
    {
126
        $array = ['test' => 1];
127
128
        $testArrayAccess         = new TestArrayAccess();
129
        $testArrayAccess['test'] = 1;
130
131
        $this->expectUserDeprecationMessage(Arrays::class . '::exists is deprecated and will be removed in v2.1.0, use ' . Arrays::class . '::keyExists instead.');
132
        self::assertTrue(Arrays::exists($array, 'test'));
133
        self::assertFalse(Arrays::exists($array, 'this'));
134
135
        self::assertTrue(Arrays::exists($testArrayAccess, 'test'));
136
        self::assertFalse(Arrays::exists($testArrayAccess, 'this'));
137
    }
138
139
    /**
140
     * Test Arrays::flatten().
141
     */
142
    public function testFlatten(): void
143
    {
144
        self::assertSame([
145
            0           => 'a',
146
            1           => 'b',
147
            2           => 'c',
148
            3           => 'd',
149
            '4.first'   => 'e',
150
            '4.0'       => 'f',
151
            '4.second'  => 'g',
152
            '4.1.0'     => 'h',
153
            '4.1.third' => 'i',
154
        ], Arrays::flatten([
155
            'a', 'b', 'c', 'd', ['first' => 'e', 'f', 'second' => 'g', ['h', 'third' => 'i']],
156
        ]));
157
158
        self::assertSame(
159
            [0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', '4.0' => 'e', '4.1' => 'f', '4.2' => 'g'],
160
            Arrays::flatten(['a', 'b', 'c', 'd', ['e', 'f', 'g']])
161
        );
162
163
        self::assertSame(
164
            ['k0' => 'a', 'k1' => 'b', 'k2' => 'c', 'k3' => 'd', 'k4.0' => 'e', 'k4.1' => 'f', 'k4.2' => 'g'],
165
            Arrays::flatten(['a', 'b', 'c', 'd', ['e', 'f', 'g']], '.', 'k')
166
        );
167
    }
168
169
    /**
170
     * Test Arrays::mapDeep().
171
     */
172
    public function testMapDeep(): void
173
    {
174
        self::assertSame([
175
            '&lt;',
176
            'abc',
177
            '&gt;',
178
            'def',
179
            ['&amp;', 'test', '123'],
180
        ], Arrays::mapDeep([
181
            '<',
182
            'abc',
183
            '>',
184
            'def',
185
            ['&', 'test', '123'],
186
        ], 'htmlentities'));
187
188
        $var       = new stdClass();
189
        $var->test = ['test' => '>'];
190
        $var->what = '<';
191
192
        $var2       = new stdClass();
193
        $var2->test = ['test' => '&gt;'];
194
        $var2->what = '&lt;';
195
196
        self::assertEquals($var2, Arrays::mapDeep($var, 'htmlentities'));
197
    }
198
199
    /**
200
     * Test Arrays::interlace().
201
     */
202
    public function testInterlace(): void
203
    {
204
        $input  = Arrays::interlace([1, 2, 3], ['a', 'b', 'c']);
205
        $expect = [1, 'a', 2, 'b', 3, 'c'];
206
207
        self::assertSame($expect, $input);
208
209
        // With one argument
210
        self::assertSame([1, 2, 3], Arrays::interlace([1, 2, 3]));
211
212
        // With no arguments
213
        self::assertFalse(Arrays::interlace());
214
    }
215
216
    /**
217
     * Test Arrays::groupBy().
218
     */
219
    public function testGroupBy(): void
220
    {
221
        $result = Arrays::groupBy([
222
            ['id' => 1, 'category' => 'A', 'value' => 'foo'],
223
            ['id' => 2, 'category' => 'B', 'value' => 'bar'],
224
            ['id' => 3, 'category' => 'A', 'value' => 'baz'],
225
            ['id' => 4, 'category' => 'B', 'value' => 'qux'],
226
        ], 'category');
227
228
        $expected = [
229
            'A' => [
230
                ['id' => 1, 'category' => 'A', 'value' => 'foo'],
231
                ['id' => 3, 'category' => 'A', 'value' => 'baz'],
232
            ],
233
            'B' => [
234
                ['id' => 2, 'category' => 'B', 'value' => 'bar'],
235
                ['id' => 4, 'category' => 'B', 'value' => 'qux'],
236
            ],
237
        ];
238
239
        self::assertSame($expected, $result);
240
    }
241
242
    /**
243
     * Test Arrays::groupBy() with non-existent/invalid key.
244
     */
245
    public function testGroupByInvalidKey(): void
246
    {
247
        $result = Arrays::groupBy([
248
            ['id' => 1, 'category' => 'A', 'value' => 'foo'],
249
            ['id' => 2, 'category' => 'B', 'value' => 'bar'],
250
            ['id' => 3, 'category' => 'A', 'value' => 'baz'],
251
            ['id' => 4, 'category' => 'B', 'value' => 'qux'],
252
        ], 'notakey');
253
254
        $expected = [];
255
256
        self::assertSame($expected, $result);
257
    }
258
}
259
260
/**
261
 * @implements ArrayAccess<mixed, mixed>
262
 */
263
class TestArrayAccess implements ArrayAccess
264
{
265
    /**
266
     * @var array<int|string, mixed>
267
     */
268
    public array $container = [
269
        'one'   => 1,
270
        'two'   => 2,
271
        'three' => 3,
272
    ];
273
274
    /**
275
     * Set an offset.
276
     */
277
    #[\Override]
278
    public function offsetSet(mixed $offset, mixed $value): void
279
    {
280
        if (is_null($offset)) {
281
            $this->container[] = $value;
282
        } else {
283
            $this->container[$offset] = $value;
284
        }
285
    }
286
287
    /**
288
     * Whether an offset exists.
289
     */
290
    #[\Override]
291
    public function offsetExists(mixed $offset): bool
292
    {
293
        return isset($this->container[$offset]);
294
    }
295
296
    /**
297
     * Unset an offset.
298
     */
299
    #[\Override]
300
    public function offsetUnset(mixed $offset): void
301
    {
302
        unset($this->container[$offset]);
303
    }
304
305
    /**
306
     * Retrieve an offset exists.
307
     */
308
    #[\Override]
309
    public function offsetGet(mixed $offset): mixed
310
    {
311
        return $this->container[$offset] ?? null;
312
    }
313
}
314