1
|
|
|
<?php |
2
|
|
|
namespace Peridot\Leo\Matcher; |
3
|
|
|
|
4
|
|
|
use Peridot\Leo\Matcher\Template\ArrayTemplate; |
5
|
|
|
use Peridot\Leo\Matcher\Template\TemplateInterface; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* KeysMatcher determines if the actual array or object has the expected keys. If the Assertion has |
9
|
|
|
* a 'contain' flag set, it will check if the expected keys are included in the object or array. |
10
|
|
|
* |
11
|
|
|
* @package Peridot\Leo\Matcher |
12
|
|
|
*/ |
13
|
|
|
class KeysMatcher extends AbstractMatcher |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* The verb used in the template. Uses "have" if the 'contain' flag is not used, otherwise |
17
|
|
|
* "contain" is used. |
18
|
|
|
* |
19
|
|
|
* @var string |
20
|
|
|
*/ |
21
|
|
|
protected $verb = 'have'; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* {@inheritdoc} |
25
|
|
|
* |
26
|
|
|
* @return TemplateInterface |
27
|
|
|
*/ |
28
|
|
|
public function getDefaultTemplate() |
29
|
|
|
{ |
30
|
|
|
$subject = 'key'; |
31
|
|
|
|
32
|
|
|
if (count($this->expected) > 1) { |
33
|
|
|
$subject = "keys"; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
$template = new ArrayTemplate([ |
37
|
|
|
'default' => "Expected {{actual}} to {$this->verb} $subject {{keys}}", |
38
|
|
|
'negated' => "Expected {{actual}} to not {$this->verb} $subject {{keys}}" |
39
|
|
|
]); |
40
|
|
|
|
41
|
|
|
return $template->setTemplateVars(['keys' => $this->getKeyString()]); |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Assert that the actual value is an array or object with the expected keys. |
46
|
|
|
* |
47
|
|
|
* @param $actual |
48
|
|
|
* @return mixed |
49
|
|
|
*/ |
50
|
|
|
protected function doMatch($actual) |
51
|
|
|
{ |
52
|
|
|
$actual = $this->getArrayValue($actual); |
53
|
|
|
if ($this->assertion->flag('contain')) { |
54
|
|
|
$this->verb = 'contain'; |
55
|
|
|
return $this->matchInclusion($actual); |
56
|
|
|
} |
57
|
|
|
$keys = array_keys($actual); |
58
|
|
|
return $keys == $this->expected; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Normalize the actual value into an array, whether it is an object |
63
|
|
|
* or an array. |
64
|
|
|
* |
65
|
|
|
* @param object|array $actual |
66
|
|
|
*/ |
67
|
|
|
protected function getArrayValue($actual) |
68
|
|
|
{ |
69
|
|
|
if (is_object($actual)) { |
70
|
|
|
return get_object_vars($actual); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
if (is_array($actual)) { |
74
|
|
|
return $actual; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
throw new \InvalidArgumentException("KeysMatcher expects object or array"); |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Returns a formatted string of expected keys. |
82
|
|
|
* |
83
|
|
|
* @return string keys |
84
|
|
|
*/ |
85
|
|
|
protected function getKeyString() |
86
|
|
|
{ |
87
|
|
|
$expected = $this->expected; |
88
|
|
|
$keys = ''; |
89
|
|
|
$tail = array_pop($expected); |
90
|
|
|
|
91
|
|
|
if ($expected) { |
|
|
|
|
92
|
|
|
$keys = implode('","', $expected) . '", and "'; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
$keys .= $tail; |
96
|
|
|
|
97
|
|
|
return $keys; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Used when the 'contain' flag exists on the Assertion. Checks |
102
|
|
|
* if the expected keys are included in the object or array. |
103
|
|
|
* |
104
|
|
|
* @param array $actual |
105
|
|
|
* @return true |
106
|
|
|
*/ |
107
|
|
|
protected function matchInclusion($actual) |
108
|
|
|
{ |
109
|
|
|
foreach ($this->expected as $key) { |
110
|
|
|
if (!array_key_exists($key, $actual)) { |
111
|
|
|
return false; |
112
|
|
|
} |
113
|
|
|
} |
114
|
|
|
return true; |
115
|
|
|
} |
116
|
|
|
} |
117
|
|
|
|
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.