Completed
Pull Request — master (#23)
by Erin
03:28
created

KeysMatcher::doMatch()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 2
Metric Value
c 2
b 0
f 2
dl 0
loc 10
rs 9.4285
cc 2
eloc 7
nc 2
nop 1
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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $expected of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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