FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
phpunit-array-asserts/src/Constraint/SequentialArray.php at master · PhrozenByte/phpunit-array-asserts · GitHub
PhrozenByte
/
phpunit-array-asserts
Public
Notifications
You must be signed in to change notification settings
Fork
2
Star
9
Code
Issues
1
Pull requests
0
Actions
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Security and quality
Insights
Expand file tree
Breadcrumbs
phpunit-array-asserts
/
src
/
Constraint
/
SequentialArray.php
Copy path
More file actions
More file actions
Latest commit
History
History
History
297 lines (261 loc) · 10.4 KB
Breadcrumbs
phpunit-array-asserts
/
src
/
Constraint
/
SequentialArray.php
Copy path
File metadata and controls
297 lines (261 loc) · 10.4 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
<?php
/**
* PHPUnitArrayAssertions - Array-related PHPUnit assertions for API testing.
*
* @copyright Copyright (c) 2020, Daniel Rudolf (<https://www.daniel-rudolf.de>)
*
* This file is copyrighted by the contributors recorded in the version control
* history of the file, available from the following original location:
*
* <https://github.com/PhrozenByte/phpunit-array-asserts/blob/master/src/Constraint/SequentialArray.php>
*
* @license http://opensource.org/licenses/MIT The MIT License
*
* SPDX-License-Identifier: MIT
* License-Filename: LICENSE
*/
declare
(strict_types=
1
);
namespace
PhrozenByte
\
PHPUnitArrayAsserts
\
Constraint
;
use
EmptyIterator
;
use
Exception
;
use
Generator
;
use
Iterator
;
use
IteratorAggregate
;
use
NoRewindIterator
;
use
PHPUnit
\
Framework
\
Constraint
\
Constraint
;
use
PHPUnit
\
Framework
\
Constraint
\
IsEqual
;
use
PHPUnit
\
Framework
\
InvalidArgumentException
;
use
Traversable
;
/**
* Constraint that asserts that a value is like a sequential array, has a
* minimum and/or maximum number of items, and that all items pass another
* constraint.
*
* Sequential arrays are defined as ordered lists with incrementing numeric
* keys starting from zero. This is especially true for native sequential
* arrays like `[ "foo", "bar" ]`. Empty arrays are considered valid, too.
* Traversable objects must have sequential keys to be considered valid.
* Requiring sequential keys can be disabled, causing this constraint to just
* check for the required number of items an whether all of its items are
* considered valid.
*
* This constraint will fully traverse any Traversable object given. This also
* means that any Generator will be fully exhausted. If possible, it will try
* to restore an Iterator's pointer to its previous state.
*
* The expected minimum and/or maximum number of items, as well as the
* constraint to apply all items to and the option to disable key checking, are
* passed in the constructor. The constraint can either be an arbitrary
* `Constraint` instance (e.g. `PHPUnit\Framework\Constraint\StringContains`),
* or any static value, requiring an exact match of the value.
*/
class
SequentialArray
extends
Constraint
{
/** @var int */
protected
$
minItems
;
/** @var int|null */
protected
$
maxItems
;
/** @var Constraint|null */
protected
$
constraint
;
/** @var bool */
protected
$
ignoreKeys
;
/**
* SequentialArray constructor.
*
* @param int $minItems required minimum number of items, defaults to 0
* @param int|null $maxItems required maximum number of items, defaults to NULL (infinite)
* @param Constraint|mixed|null $constraint optional constraint to apply all items to (defaults to NULL)
* @param bool $ignoreKeys whether to ignore non-sequential keys (defaults to FALSE)
*
* @throws InvalidArgumentException
*/
public
function
__construct
(
int
$
minItems
=
0
, ?
int
$
maxItems
=
null
,
$
constraint
=
null
,
bool
$
ignoreKeys
=
false
)
{
if
(
$
minItems
<
0
) {
throw
InvalidArgumentException::
create
(
1
,
'
non-negative integer
'
);
}
if
(
$
maxItems
!==
null
) {
if
(
$
maxItems
<
0
) {
throw
InvalidArgumentException::
create
(
2
,
'
non-negative integer
'
);
}
elseif
(
$
minItems
>
$
maxItems
) {
throw
InvalidArgumentException::
create
(
2
,
'
integer not lesser than argument #1
'
);
}
}
$
this
->
minItems
=
$
minItems
;
$
this
->
maxItems
=
$
maxItems
;
$
this
->
ignoreKeys
=
$
ignoreKeys
;
if
(
$
constraint
!==
null
) {
$
this
->
constraint
= !(
$
constraint
instanceof
Constraint) ?
new
IsEqual
(
$
constraint
) :
$
constraint
;
}
}
/**
* Returns a human-readable string representation of this Constraint.
*
* @return string string representation of the Constraint
*/
public
function
toString
():
string
{
if
(
$
this
->
maxItems
===
0
) {
return
'
is an empty array
'
;
}
$
description
= !
$
this
->
ignoreKeys
?
'
sequential array
'
:
'
list array
'
;
if
((
$
this
->
minItems
<=
1
) && (
$
this
->
maxItems
===
null
)) {
$
text
=
'
is a
'
. ((
$
this
->
minItems
>
0
) ?
'
non-empty
'
:
''
) .
'
'
.
$
description
;
$
text
.= (
$
this
->
constraint
!==
null
) ?
'
whose items match
'
:
''
;
}
else
{
$
text
=
'
is a
'
.
$
description
;
if
(
$
this
->
minItems
&&
$
this
->
maxItems
) {
if
(
$
this
->
minItems
===
$
this
->
maxItems
) {
$
text
.=
'
with exactly
'
.
$
this
->
minItems
.
'
'
. ((
$
this
->
minItems
>
1
) ?
'
items
'
:
'
item
'
);
}
else
{
$
text
.=
'
with ≥
'
.
$
this
->
minItems
.
'
and ≤
'
.
$
this
->
maxItems
.
'
items
'
;
}
}
elseif
(
$
this
->
minItems
) {
$
text
.=
'
with ≥
'
.
$
this
->
minItems
.
'
items
'
;
}
elseif
(
$
this
->
maxItems
) {
$
text
.=
'
with ≤
'
.
$
this
->
maxItems
.
'
items
'
;
}
$
text
.= (
$
this
->
constraint
!==
null
) ?
'
matching
'
:
''
;
}
if
(
$
this
->
constraint
!==
null
) {
$
text
.=
sprintf
(
'
the constraint "%s"
'
,
$
this
->
constraint
->
toString
());
}
return
$
text
;
}
/**
* Returns whether the given value matches the Constraint.
*
* @param mixed $other the value to evaluate
*
* @return bool boolean indicating whether the value matches the Constraint
*/
protected
function
matches
(
$
other
):
bool
{
[
$
valid
,
$
itemCount
,
$
itemsValid
] =
$
this
->
inspectData
(
$
other
);
if
(!
$
valid
) {
return
false
;
}
if
(!
$
itemsValid
) {
return
false
;
}
if
(
$
itemCount
<
$
this
->
minItems
) {
return
false
;
}
if
((
$
this
->
maxItems
!==
null
) && (
$
itemCount
>
$
this
->
maxItems
)) {
return
false
;
}
return
true
;
}
/**
* Inspects the given data and returns meta data.
*
* The returned array consists of three items: A boolean indicating whether
* the data structure is like a sequential array, an integer representing
* the number of items, and a boolean indicating whether all items match
* the given constraint.
*
* Sequential arrays are defined as ordered lists with incrementing numeric
* keys starting from zero. This is especially true for native sequential
* arrays like `[ "foo", "bar" ]`. Empty arrays are considered valid, too.
* Traversable objects must have sequential keys to be considered valid.
* The first item of the result array will hold TRUE for sequential data,
* FALSE otherwise.
*
* Please note that this method will fully traverse a Traversable object.
* It expects Traversables to be rewindable. For NoRewindIterator instances
* it assumes that the iterator is still in its initial state. Generators
* will be fully exhausted; if the iterator has begun already, the object
* is considered invalid. If an Iterator is given, this method will try to
* restore the object's pointer to its previous state. This will silently
* fail for NoRewindIterator instances. The behaviour for Iterators with
* non-unique keys is undefined.
*
* The second item of the result array holds an integer representing the
* number of items given. The integer will be ≥ 0 for any traversable data,
* or -1 for non-traversable data.
*
* Optionally this method will also apply all values to a given constraint.
* If all items pass the constraint, the third item of the result array
* will be TRUE, FALSE otherwise. If non-traversable data is passed, it
* will return FALSE, too.
*
* Example:
* ```php
* [ $valid, $itemCount, $itemsValid ] = $this->inspectData([ "foo", "bar" ]);
* // $valid = true; $itemCount = 2; $itemsValid = true;
* ```
*
* @param mixed $other value to inspect
*
* @return array{0: bool, 1: int, 2: bool} inspection result
*/
protected
function
inspectData
(
$
other
):
array
{
if
(
is_array
(
$
other
)) {
$
itemCount
=
count
(
$
other
);
$
valid
=
true
;
if
((
$
itemCount
>
0
) && !
$
this
->
ignoreKeys
) {
$
valid
= (
isset
(
$
other
[
0
]) && (
$
other
===
array_values
(
$
other
)));
}
$
itemsValid
=
true
;
if
(
$
valid
&& (
$
this
->
constraint
!==
null
)) {
foreach
(
$
other
as
$
item
) {
if
(!
$
this
->
constraint
->
evaluate
(
$
item
,
''
,
true
)) {
$
itemsValid
=
false
;
break
;
}
}
}
return
[
$
valid
,
$
itemCount
,
$
itemsValid
];
}
if
(
$
other
instanceof
EmptyIterator) {
return
[
true
,
0
,
true
];
}
if
(
$
other
instanceof
Traversable) {
while
(
$
other
instanceof
IteratorAggregate) {
$
other
=
$
other
->
getIterator
();
}
if
(
$
other
instanceof
Generator) {
try
{
$
other
->
rewind
();
}
catch
(
Exception
$
e
) {
return
[
false
, -
1
,
false
];
}
}
$
restorePointer
=
null
;
if
(
$
other
instanceof
Iterator) {
if
(!(
$
other
instanceof
Generator) && !(
$
other
instanceof
NoRewindIterator)) {
$
restorePointer
=
$
other
->
valid
() ?
$
other
->
key
() :
null
;
}
}
$
valid
=
true
;
$
itemCount
=
0
;
$
itemsValid
=
true
;
foreach
(
$
other
as
$
key
=>
$
item
) {
if
((
$
key
!==
$
itemCount
++) && !
$
this
->
ignoreKeys
) {
$
valid
=
false
;
}
if
(
$
valid
&&
$
itemsValid
&& (
$
this
->
constraint
!==
null
)) {
if
(!
$
this
->
constraint
->
evaluate
(
$
item
,
''
,
true
)) {
$
itemsValid
=
false
;
}
}
}
if
(
$
restorePointer
!==
null
) {
$
other
->
rewind
();
while
(
$
other
->
valid
() && (
$
other
->
key
() !==
$
restorePointer
)) {
$
other
->
next
();
}
}
return
[
$
valid
,
$
itemCount
,
$
itemsValid
];
}
return
[
false
, -
1
,
false
];
}
/**
* Returns the number of assertions performed by this Constraint.
*/
public
function
count
():
int
{
return
(
$
this
->
constraint
!==
null
) ?
$
this
->
constraint
->
count
() +
1
:
1
;
}
}
Back
|
FazBrowse Home
|
New Git URL