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 (!isset($actual[$key])) {
111: return false;
112: }
113: }
114: return true;
115: }
116: }
117: