Completed
Push — scrutinizer-quality ( c3b7e2 )
by Erin
02:15
created

ExceptionMatcher::setMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Peridot\Leo\Matcher;
4
5
use Exception;
6
use Peridot\Leo\Matcher\Template\ArrayTemplate;
7
use Peridot\Leo\Matcher\Template\TemplateInterface;
8
use Throwable;
9
10
/**
11
 * ExceptionMatcher executes a callable and determines if an exception of a given type was thrown. It optionally
12
 * matches the exception message.
13
 *
14
 * @package Peridot\Leo\Matcher
15
 */
16
class ExceptionMatcher implements MatcherInterface
17
{
18
    use MatcherTrait;
19
20
    /**
21
     * @param string $exceptionType
22
     */
23
    public function __construct($exceptionType)
24
    {
25
        $this->expected = $exceptionType;
0 ignored issues
show
Bug introduced by
The property expected does not seem to exist. Did you mean expectedMessage?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
26
    }
27
28
    /**
29
     * Set arguments to be passed to the callable.
30
     *
31
     * @param  array $arguments
32
     * @return $this
33
     */
34
    public function setArguments(array $arguments)
35
    {
36
        $this->arguments = $arguments;
37
38
        return $this;
39
    }
40
41
    /**
42
     * Set the expected message of the exception.
43
     *
44
     * @param  string $message
45
     * @return $this
46
     */
47
    public function setExpectedMessage($message)
48
    {
49
        $this->expectedMessage = $message;
50
51
        return $this;
52
    }
53
54
    /**
55
     * Set the message thrown from an exception resulting from the
56
     * callable being invoked.
57
     *
58
     * @param string $message
59
     */
60
    public function setMessage($message)
61
    {
62
        $this->message = $message;
63
    }
64
65
    /**
66
     * Returns the arguments passed to the callable.
67
     *
68
     * @return array
69
     */
70
    public function getArguments()
71
    {
72
        return $this->arguments;
73
    }
74
75
    /**
76
     * Return the expected exception message.
77
     *
78
     * @return string
79
     */
80
    public function getExpectedMessage()
81
    {
82
        return $this->expectedMessage;
83
    }
84
85
    /**
86
     * Return the message thrown by an exception resulting from the callable
87
     * being invoked.
88
     *
89
     * @return string
90
     */
91
    public function getMessage()
92
    {
93
        return $this->message;
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     *
99
     * If the expected message has been set, the message template will be used.
100
     *
101
     * @return TemplateInterface
102
     */
103
    public function getTemplate()
104
    {
105
        if ($this->expectedMessage) {
106
            return $this->getMessageTemplate();
107
        }
108
109
        if (!isset($this->template)) {
110
            return $this->getDefaultTemplate();
111
        }
112
113
        return $this->template;
114
    }
115
116
    /**
117
     * Set the template to be used when an expected exception message is provided.
118
     *
119
     * @param  TemplateInterface $template
120
     * @return $this
121
     */
122
    public function setMessageTemplate(TemplateInterface $template)
123
    {
124
        $this->messageTemplate = $template;
125
126
        return $this;
127
    }
128
129
    /**
130
     * Return a template for rendering exception message templates.
131
     *
132
     * return TemplateInterface
133
     */
134
    public function getMessageTemplate()
135
    {
136
        if ($this->messageTemplate) {
137
            return $this->messageTemplate;
138
        }
139
140
        return $this->getDefaultMessageTemplate();
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     *
146
     * @return TemplateInterface
147
     */
148
    public function getDefaultTemplate()
149
    {
150
        $template = new ArrayTemplate([
151
            'default' => 'Expected exception of type {{expected}}',
152
            'negated' => 'Expected type of exception not to be {{expected}}',
153
        ]);
154
155
        return $template;
156
    }
157
158
    /**
159
     * Return a default template for exception message assertions.
160
     *
161
     * @return ArrayTemplate
162
     */
163
    public function getDefaultMessageTemplate()
164
    {
165
        return new ArrayTemplate([
166
            'default' => 'Expected exception message {{expected}}, got {{actual}}',
167
            'negated' => 'Expected exception message {{actual}} not to equal {{expected}}',
168
        ]);
169
    }
170
171
    /**
172
     * Executes the callable and matches the exception type and exception message.
173
     *
174
     * @param $actual
175
     * @return Match
176
     */
177
    public function match($actual)
178
    {
179
        $this->validateCallable($actual);
180
181
        list($exception, $message) = $this->callableException($actual);
182
        $this->setMessage($message);
183
184
        return $this->matchMessage($actual, $exception, $message);
185
    }
186
187
    /**
188
     * Validate that expected is indeed a valid callable.
189
     *
190
     * @throws \BadFunctionCallException
191
     */
192
    protected function validateCallable($callable)
193
    {
194
        if (!is_callable($callable)) {
195
            $callable = rtrim(print_r($callable, true));
196
            throw new \BadFunctionCallException('Invalid callable ' . $callable . ' given');
197
        }
198
    }
199
200
    private function callableException($callable)
201
    {
202
        $exception = null;
203
        $message = null;
204
205
        try {
206
            call_user_func_array($callable, $this->arguments);
207
        } catch (Exception $exception) {
208
            $message = $exception->getMessage();
209
            // fall-through ...
210
        } catch (Throwable $exception) {
211
            $message = $exception->getMessage();
212
            // fall-through ...
213
        }
214
215
        return array($exception, $message);
216
    }
217
218
    private function matchMessage($actual, $exception, $message)
219
    {
220
        if (!$this->expectedMessage || $message === $this->expectedMessage) {
221
            return $this->matchType($actual, $exception);
222
        }
223
224
        $isNegated = $this->isNegated();
225
226
        return new Match($isNegated, $this->expectedMessage, $message, $isNegated);
227
    }
228
229
    private function matchType($actual, $exception)
230
    {
231
        $isMatch = $exception instanceof $this->expected;
0 ignored issues
show
Bug introduced by
The property expected does not seem to exist. Did you mean expectedMessage?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
232
        $isNegated = $this->isNegated();
233
234
        return new Match($isMatch xor $isNegated, $this->expected, $actual, $isNegated);
0 ignored issues
show
Bug introduced by
The property expected does not seem to exist. Did you mean expectedMessage?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
235
    }
236
237
    /**
238
     * @var array
239
     */
240
    protected $arguments = [];
241
242
    /**
243
     * @var string
244
     */
245
    protected $expectedMessage = '';
246
247
    /**
248
     * A captured exception message.
249
     *
250
     * @var string
251
     */
252
    protected $message;
253
254
    /**
255
     * @var TemplateInterface
256
     */
257
    protected $messageTemplate;
258
}
259