Completed
Push — master ( 55e0d1...34569c )
by Erin
9s
created

ExceptionMatcher::match()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 19
nc 8
nop 1
dl 0
loc 27
rs 8.439
c 0
b 0
f 0
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
 * ExceptionMatcher executes a callable and determines if an exception of a given type was thrown. It optionally
9
 * matches the exception message.
10
 *
11
 * @package Peridot\Leo\Matcher
12
 */
13
class ExceptionMatcher extends AbstractMatcher
14
{
15
    /**
16
     * @var array
17
     */
18
    protected $arguments = [];
19
20
    /**
21
     * @var string
22
     */
23
    protected $expectedMessage = "";
24
25
    /**
26
     * A captured exception message
27
     *
28
     * @var string $message
29
     */
30
    protected $message;
31
32
    /**
33
     * @var TemplateInterface
34
     */
35
    protected $messageTemplate;
36
37
    /**
38
     * @param callable $expected
0 ignored issues
show
Bug introduced by
There is no parameter named $expected. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
39
     */
40
    public function __construct($exceptionType)
41
    {
42
        $this->expected = $exceptionType;
43
    }
44
45
    /**
46
     * Set arguments to be passed to the callable.
47
     *
48
     * @param array $arguments
49
     * @return $this
50
     */
51
    public function setArguments(array $arguments)
52
    {
53
        $this->arguments = $arguments;
54
        return $this;
55
    }
56
57
    /**
58
     * Set the expected message of the exception.
59
     *
60
     * @param string $message
61
     * @return $this
62
     */
63
    public function setExpectedMessage($message)
64
    {
65
        $this->expectedMessage = $message;
66
        return $this;
67
    }
68
69
    /**
70
     * Set the message thrown from an exception resulting from the
71
     * callable being invoked.
72
     *
73
     * @param string $message
74
     */
75
    public function setMessage($message)
76
    {
77
        $this->message = $message;
78
    }
79
80
    /**
81
     * Returns the arguments passed to the callable.
82
     *
83
     * @return array
84
     */
85
    public function getArguments()
86
    {
87
        return $this->arguments;
88
    }
89
90
    /**
91
     * Return the expected exception message.
92
     *
93
     * @return string
94
     */
95
    public function getExpectedMessage()
96
    {
97
        return $this->expectedMessage;
98
    }
99
100
    /**
101
     * Return the message thrown by an exception resulting from the callable
102
     * being invoked.
103
     *
104
     * @return string
105
     */
106
    public function getMessage()
107
    {
108
        return $this->message;
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     *
114
     * If the expected message has been set, the message template will be used.
115
     *
116
     * @return TemplateInterface
117
     */
118
    public function getTemplate()
119
    {
120
        if ($this->expectedMessage) {
121
            return $this->getMessageTemplate();
122
        }
123
        return parent::getTemplate();
124
    }
125
126
    /**
127
     * Set the template to be used when an expected exception message is provided.
128
     *
129
     * @param TemplateInterface $template
130
     * @return $this
131
     */
132
    public function setMessageTemplate(TemplateInterface $template)
133
    {
134
        $this->messageTemplate = $template;
135
        return $this;
136
    }
137
138
    /**
139
     * Return a template for rendering exception message templates.
140
     *
141
     * return TemplateInterface
142
     */
143
    public function getMessageTemplate()
144
    {
145
        if ($this->messageTemplate) {
146
            return $this->messageTemplate;
147
        }
148
        return $this->getDefaultMessageTemplate();
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     *
154
     * @return TemplateInterface
155
     */
156
    public function getDefaultTemplate()
157
    {
158
        $template = new ArrayTemplate([
159
            'default' => 'Expected exception of type {{expected}}',
160
            'negated' => 'Expected type of exception not to be {{expected}}'
161
        ]);
162
163
        return $template;
164
    }
165
166
    /**
167
     * Return a default template for exception message assertions.
168
     *
169
     * @return ArrayTemplate
170
     */
171
    public function getDefaultMessageTemplate()
172
    {
173
        return new ArrayTemplate([
174
            'default' => 'Expected exception message {{expected}}, got {{actual}}',
175
            'negated' => 'Expected exception message {{actual}} not to equal {{expected}}'
176
        ]);
177
    }
178
179
    /**
180
     * Executes the callable and matches the exception type and exception message.
181
     *
182
     * @param $actual
183
     * @return Match
184
     */
185
    public function match($actual)
186
    {
187
        $this->validateCallable($actual);
188
        try {
189
            call_user_func_array($actual, $this->arguments);
190
            $isMatch = false;
191
        } catch (\Exception $e) {
192
            $isMatch = $e instanceof $this->expected;
193
            $message = $e->getMessage();
194
            $this->setMessage($message);
195
        }
196
        if ($isMatch && $this->expectedMessage) {
197
            $isMatch = $message == $this->expectedMessage;
0 ignored issues
show
Bug introduced by
The variable $message does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
198
            $expected = $this->expectedMessage;
199
            $actual = $message;
200
        } else {
201
            $expected = $this->expected;
202
        }
203
204
        $isNegated = $this->isNegated();
205
206
        if ($isNegated) {
207
            $isMatch = !$isMatch;
208
        }
209
210
        return new Match($isMatch, $expected, $actual, $isNegated);
211
    }
212
213
    /**
214
     * Executes the callable and matches the exception type and exception message.
215
     *
216
     * @param $actual
217
     * @return bool
218
     */
219
    protected function doMatch($actual)
220
    {
221
        // unused
222
    }
223
224
    /**
225
     * Validate that expected is indeed a valid callable.
226
     *
227
     * @throws \BadFunctionCallException
228
     */
229
    protected function validateCallable($callable)
230
    {
231
        if (!is_callable($callable)) {
232
            $callable = rtrim(print_r($callable, true));
233
            throw new \BadFunctionCallException("Invalid callable " . $callable . " given");
234
        }
235
    }
236
}
237