These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace BotMan\BotMan; |
||
4 | |||
5 | use Closure; |
||
6 | use Illuminate\Support\Collection; |
||
7 | use BotMan\BotMan\Commands\Command; |
||
8 | use BotMan\BotMan\Messages\Matcher; |
||
9 | use BotMan\BotMan\Drivers\DriverManager; |
||
10 | use BotMan\BotMan\Traits\ProvidesStorage; |
||
11 | use BotMan\BotMan\Interfaces\UserInterface; |
||
12 | use BotMan\BotMan\Messages\Incoming\Answer; |
||
13 | use BotMan\BotMan\Traits\HandlesExceptions; |
||
14 | use BotMan\BotMan\Handlers\ExceptionHandler; |
||
15 | use BotMan\BotMan\Interfaces\CacheInterface; |
||
16 | use BotMan\BotMan\Messages\Attachments\File; |
||
17 | use BotMan\BotMan\Interfaces\DriverInterface; |
||
18 | use BotMan\BotMan\Messages\Attachments\Audio; |
||
19 | use BotMan\BotMan\Messages\Attachments\Image; |
||
20 | use BotMan\BotMan\Messages\Attachments\Video; |
||
21 | use BotMan\BotMan\Messages\Outgoing\Question; |
||
22 | use BotMan\BotMan\Interfaces\StorageInterface; |
||
23 | use BotMan\BotMan\Traits\HandlesConversations; |
||
24 | use Symfony\Component\HttpFoundation\Response; |
||
25 | use BotMan\BotMan\Commands\ConversationManager; |
||
26 | use BotMan\BotMan\Middleware\MiddlewareManager; |
||
27 | use BotMan\BotMan\Messages\Attachments\Location; |
||
28 | use BotMan\BotMan\Exceptions\Base\BotManException; |
||
29 | use BotMan\BotMan\Interfaces\DriverEventInterface; |
||
30 | use BotMan\BotMan\Messages\Incoming\IncomingMessage; |
||
31 | use BotMan\BotMan\Messages\Outgoing\OutgoingMessage; |
||
32 | use BotMan\BotMan\Interfaces\ExceptionHandlerInterface; |
||
33 | use BotMan\BotMan\Exceptions\Core\BadMethodCallException; |
||
34 | use BotMan\BotMan\Exceptions\Core\UnexpectedValueException; |
||
35 | use BotMan\BotMan\Messages\Conversations\InlineConversation; |
||
36 | |||
37 | /** |
||
38 | * Class BotMan. |
||
39 | */ |
||
40 | class BotMan |
||
41 | { |
||
42 | use ProvidesStorage, |
||
43 | HandlesConversations, |
||
44 | HandlesExceptions; |
||
45 | |||
46 | /** @var \Illuminate\Support\Collection */ |
||
47 | protected $event; |
||
48 | |||
49 | /** @var Command */ |
||
50 | protected $command; |
||
51 | |||
52 | /** @var IncomingMessage */ |
||
53 | protected $message; |
||
54 | |||
55 | /** @var OutgoingMessage|Question */ |
||
56 | protected $outgoingMessage; |
||
57 | |||
58 | /** @var string */ |
||
59 | protected $driverName; |
||
60 | |||
61 | /** @var array|null */ |
||
62 | protected $currentConversationData; |
||
63 | |||
64 | /** @var ExceptionHandlerInterface */ |
||
65 | protected $exceptionHandler; |
||
66 | |||
67 | /** |
||
68 | * IncomingMessage service events. |
||
69 | * @var array |
||
70 | */ |
||
71 | protected $events = []; |
||
72 | |||
73 | /** |
||
74 | * The fallback message to use, if no match |
||
75 | * could be heard. |
||
76 | * @var callable|null |
||
77 | */ |
||
78 | protected $fallbackMessage; |
||
79 | |||
80 | /** @var array */ |
||
81 | protected $groupAttributes = []; |
||
82 | |||
83 | /** @var array */ |
||
84 | protected $matches = []; |
||
85 | |||
86 | /** @var DriverInterface */ |
||
87 | protected $driver; |
||
88 | |||
89 | /** @var array */ |
||
90 | protected $config = []; |
||
91 | |||
92 | /** @var MiddlewareManager */ |
||
93 | public $middleware; |
||
94 | |||
95 | /** @var ConversationManager */ |
||
96 | protected $conversationManager; |
||
97 | |||
98 | /** @var CacheInterface */ |
||
99 | private $cache; |
||
100 | |||
101 | /** @var StorageInterface */ |
||
102 | protected $storage; |
||
103 | |||
104 | /** @var Matcher */ |
||
105 | protected $matcher; |
||
106 | |||
107 | /** @var bool */ |
||
108 | protected $loadedConversation = false; |
||
109 | |||
110 | /** @var bool */ |
||
111 | protected $firedDriverEvents = false; |
||
112 | |||
113 | /** @var bool */ |
||
114 | protected $runsOnSocket = false; |
||
115 | |||
116 | /** |
||
117 | * BotMan constructor. |
||
118 | * @param CacheInterface $cache |
||
119 | * @param DriverInterface $driver |
||
120 | * @param array $config |
||
121 | * @param StorageInterface $storage |
||
122 | */ |
||
123 | public function __construct(CacheInterface $cache, DriverInterface $driver, $config, StorageInterface $storage) |
||
124 | { |
||
125 | $this->cache = $cache; |
||
126 | $this->message = new IncomingMessage('', '', ''); |
||
127 | $this->driver = $driver; |
||
128 | $this->config = $config; |
||
129 | $this->storage = $storage; |
||
130 | $this->matcher = new Matcher(); |
||
131 | $this->middleware = new MiddlewareManager($this); |
||
132 | $this->conversationManager = new ConversationManager(); |
||
133 | $this->exceptionHandler = new ExceptionHandler(); |
||
134 | } |
||
135 | |||
136 | /** |
||
137 | * Set a fallback message to use if no listener matches. |
||
138 | * |
||
139 | * @param callable $callback |
||
140 | */ |
||
141 | public function fallback($callback) |
||
142 | { |
||
143 | $this->fallbackMessage = $callback; |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * @param string $name The Driver name or class |
||
148 | */ |
||
149 | public function loadDriver($name) |
||
150 | { |
||
151 | $this->driver = DriverManager::loadFromName($name, $this->config); |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * @param DriverInterface $driver |
||
156 | */ |
||
157 | public function setDriver(DriverInterface $driver) |
||
158 | { |
||
159 | $this->driver = $driver; |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * @return DriverInterface |
||
164 | */ |
||
165 | public function getDriver() |
||
166 | { |
||
167 | return $this->driver; |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Retrieve the chat message. |
||
172 | * |
||
173 | * @return array |
||
174 | */ |
||
175 | public function getMessages() |
||
176 | { |
||
177 | return $this->getDriver()->getMessages(); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Retrieve the chat message that are sent from bots. |
||
182 | * |
||
183 | * @return array |
||
184 | */ |
||
185 | public function getBotMessages() |
||
186 | { |
||
187 | return Collection::make($this->getDriver()->getMessages())->filter(function (IncomingMessage $message) { |
||
188 | return $message->isFromBot(); |
||
189 | })->toArray(); |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * @return Answer |
||
194 | */ |
||
195 | public function getConversationAnswer() |
||
196 | { |
||
197 | return $this->getDriver()->getConversationAnswer($this->message); |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * @param bool $running |
||
202 | * @return bool |
||
203 | */ |
||
204 | public function runsOnSocket($running = null) |
||
205 | { |
||
206 | if (is_bool($running)) { |
||
207 | $this->runsOnSocket = $running; |
||
208 | } |
||
209 | |||
210 | return $this->runsOnSocket; |
||
211 | } |
||
212 | |||
213 | /** |
||
214 | * @return UserInterface |
||
215 | */ |
||
216 | public function getUser() |
||
217 | { |
||
218 | if ($user = $this->cache->get('user_'.$this->driver->getName().'_'.$this->getMessage()->getSender())) { |
||
219 | return $user; |
||
220 | } |
||
221 | |||
222 | $user = $this->getDriver()->getUser($this->getMessage()); |
||
223 | $this->cache->put('user_'.$this->driver->getName().'_'.$user->getId(), $user, $this->config['user_cache_time'] ?? 30); |
||
224 | |||
225 | return $user; |
||
226 | } |
||
227 | |||
228 | /** |
||
229 | * Get the parameter names for the route. |
||
230 | * |
||
231 | * @param $value |
||
232 | * @return array |
||
233 | */ |
||
234 | protected function compileParameterNames($value) |
||
235 | { |
||
236 | preg_match_all(Matcher::PARAM_NAME_REGEX, $value, $matches); |
||
237 | |||
238 | return array_map(function ($m) { |
||
239 | return trim($m, '?'); |
||
240 | }, $matches[1]); |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * @param string $pattern the pattern to listen for |
||
245 | * @param Closure|string $callback the callback to execute. Either a closure or a Class@method notation |
||
246 | * @param string $in the channel type to listen to (either direct message or public channel) |
||
247 | * @return Command |
||
248 | */ |
||
249 | public function hears($pattern, $callback, $in = null) |
||
250 | { |
||
251 | $command = new Command($pattern, $callback, $in); |
||
252 | $command->applyGroupAttributes($this->groupAttributes); |
||
253 | |||
254 | $this->conversationManager->listenTo($command); |
||
255 | |||
256 | return $command; |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * Listen for messaging service events. |
||
261 | * |
||
262 | * @param string $name |
||
263 | * @param Closure|string $callback |
||
264 | */ |
||
265 | public function on($name, $callback) |
||
266 | { |
||
267 | $this->events[] = [ |
||
268 | 'name' => $name, |
||
269 | 'callback' => $this->getCallable($callback), |
||
270 | ]; |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Listening for image files. |
||
275 | * |
||
276 | * @param $callback |
||
277 | * @return Command |
||
278 | */ |
||
279 | public function receivesImages($callback) |
||
280 | { |
||
281 | return $this->hears(Image::PATTERN, $callback); |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * Listening for image files. |
||
286 | * |
||
287 | * @param $callback |
||
288 | * @return Command |
||
289 | */ |
||
290 | public function receivesVideos($callback) |
||
291 | { |
||
292 | return $this->hears(Video::PATTERN, $callback); |
||
293 | } |
||
294 | |||
295 | /** |
||
296 | * Listening for audio files. |
||
297 | * |
||
298 | * @param $callback |
||
299 | * @return Command |
||
300 | */ |
||
301 | public function receivesAudio($callback) |
||
302 | { |
||
303 | return $this->hears(Audio::PATTERN, $callback); |
||
304 | } |
||
305 | |||
306 | /** |
||
307 | * Listening for location attachment. |
||
308 | * |
||
309 | * @param $callback |
||
310 | * @return Command |
||
311 | */ |
||
312 | public function receivesLocation($callback) |
||
313 | { |
||
314 | return $this->hears(Location::PATTERN, $callback); |
||
315 | } |
||
316 | |||
317 | /** |
||
318 | * Listening for files attachment. |
||
319 | * |
||
320 | * @param $callback |
||
321 | * @return Command |
||
322 | */ |
||
323 | public function receivesFiles($callback) |
||
324 | { |
||
325 | return $this->hears(File::PATTERN, $callback); |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * Create a command group with shared attributes. |
||
330 | * |
||
331 | * @param array $attributes |
||
332 | * @param \Closure $callback |
||
333 | */ |
||
334 | public function group(array $attributes, Closure $callback) |
||
335 | { |
||
336 | $previousGroupAttributes = $this->groupAttributes; |
||
337 | $this->groupAttributes = array_merge_recursive($previousGroupAttributes, $attributes); |
||
338 | |||
339 | call_user_func($callback, $this); |
||
340 | |||
341 | $this->groupAttributes = $previousGroupAttributes; |
||
342 | } |
||
343 | |||
344 | /** |
||
345 | * Fire potential driver event callbacks. |
||
346 | */ |
||
347 | protected function fireDriverEvents() |
||
348 | { |
||
349 | $driverEvent = $this->getDriver()->hasMatchingEvent(); |
||
350 | if ($driverEvent instanceof DriverEventInterface) { |
||
351 | $this->firedDriverEvents = true; |
||
352 | |||
353 | Collection::make($this->events)->filter(function ($event) use ($driverEvent) { |
||
354 | return $driverEvent->getName() === $event['name']; |
||
355 | })->each(function ($event) use ($driverEvent) { |
||
356 | /** |
||
357 | * Load the message, so driver events can reply. |
||
358 | */ |
||
359 | $messages = $this->getDriver()->getMessages(); |
||
360 | if (isset($messages[0])) { |
||
361 | $this->message = $messages[0]; |
||
362 | } |
||
363 | |||
364 | call_user_func_array($event['callback'], [$driverEvent->getPayload(), $this]); |
||
365 | }); |
||
366 | } |
||
367 | } |
||
368 | |||
369 | /** |
||
370 | * Try to match messages with the ones we should |
||
371 | * listen to. |
||
372 | */ |
||
373 | public function listen() |
||
374 | { |
||
375 | try { |
||
376 | $this->verifyServices(); |
||
377 | |||
378 | $this->fireDriverEvents(); |
||
379 | |||
380 | if ($this->firedDriverEvents === false) { |
||
381 | $this->loadActiveConversation(); |
||
382 | |||
383 | if ($this->loadedConversation === false) { |
||
384 | $this->callMatchingMessages(); |
||
385 | } |
||
386 | |||
387 | /* |
||
388 | * If the driver has a "messagesHandled" method, call it. |
||
389 | * This method can be used to trigger driver methods |
||
390 | * once the messages are handles. |
||
391 | */ |
||
392 | if (method_exists($this->getDriver(), 'messagesHandled')) { |
||
393 | $this->getDriver()->messagesHandled(); |
||
394 | } |
||
395 | } |
||
396 | |||
397 | $this->firedDriverEvents = false; |
||
398 | } catch (\Throwable $e) { |
||
399 | $this->exceptionHandler->handleException($e, $this); |
||
400 | } |
||
401 | } |
||
402 | |||
403 | /** |
||
404 | * Call matching message callbacks. |
||
405 | */ |
||
406 | protected function callMatchingMessages() |
||
407 | { |
||
408 | $matchingMessages = $this->conversationManager->getMatchingMessages($this->getMessages(), $this->middleware, $this->getConversationAnswer(), $this->getDriver()); |
||
409 | |||
410 | foreach ($matchingMessages as $matchingMessage) { |
||
411 | $this->command = $matchingMessage->getCommand(); |
||
412 | $callback = $this->command->getCallback(); |
||
413 | |||
414 | $callback = $this->getCallable($callback); |
||
415 | |||
416 | // Set the message first, so it's available for middlewares |
||
417 | $this->message = $matchingMessage->getMessage(); |
||
418 | |||
419 | $this->message = $this->middleware->applyMiddleware('heard', $matchingMessage->getMessage(), $this->command->getMiddleware()); |
||
420 | $parameterNames = $this->compileParameterNames($this->command->getPattern()); |
||
421 | |||
422 | $parameters = $matchingMessage->getMatches(); |
||
423 | if (count($parameterNames) !== count($parameters)) { |
||
424 | $parameters = array_merge( |
||
425 | //First, all named parameters (eg. function ($a, $b, $c)) |
||
426 | array_filter( |
||
427 | $parameters, |
||
428 | 'is_string', |
||
429 | ARRAY_FILTER_USE_KEY |
||
430 | ), |
||
431 | //Then, all other unsorted parameters (regex non named results) |
||
432 | array_filter( |
||
433 | $parameters, |
||
434 | 'is_integer', |
||
435 | ARRAY_FILTER_USE_KEY |
||
436 | ) |
||
437 | ); |
||
438 | } |
||
439 | |||
440 | $this->matches = $parameters; |
||
441 | array_unshift($parameters, $this); |
||
442 | |||
443 | $parameters = $this->conversationManager->addDataParameters($this->message, $parameters); |
||
444 | |||
445 | call_user_func_array($callback, $parameters); |
||
446 | } |
||
447 | |||
448 | if (empty($matchingMessages) && empty($this->getBotMessages()) && ! is_null($this->fallbackMessage)) { |
||
449 | $this->callFallbackMessage(); |
||
450 | } |
||
451 | } |
||
452 | |||
453 | /** |
||
454 | * Call the fallback method. |
||
455 | */ |
||
456 | protected function callFallbackMessage() |
||
457 | { |
||
458 | $this->message = $this->getMessages()[0]; |
||
459 | |||
460 | $this->fallbackMessage = $this->getCallable($this->fallbackMessage); |
||
461 | |||
462 | call_user_func($this->fallbackMessage, $this); |
||
463 | } |
||
464 | |||
465 | /** |
||
466 | * Verify service webhook URLs. |
||
467 | * |
||
468 | * @return null|Response |
||
469 | */ |
||
470 | protected function verifyServices() |
||
471 | { |
||
472 | return DriverManager::verifyServices($this->config); |
||
473 | } |
||
474 | |||
475 | /** |
||
476 | * @param string|Question $message |
||
477 | * @param string|array $recipients |
||
478 | * @param DriverInterface|null $driver |
||
479 | * @param array $additionalParameters |
||
480 | * @return Response |
||
481 | * @throws BotManException |
||
482 | */ |
||
483 | public function say($message, $recipients, $driver = null, $additionalParameters = []) |
||
484 | { |
||
485 | if ($driver === null && $this->driver === null) { |
||
486 | throw new BotManException('The current driver can\'t be NULL'); |
||
487 | } |
||
488 | |||
489 | $previousDriver = $this->driver; |
||
490 | $previousMessage = $this->message; |
||
491 | |||
492 | if ($driver instanceof DriverInterface) { |
||
493 | $this->setDriver($driver); |
||
494 | } elseif (is_string($driver)) { |
||
495 | $this->setDriver(DriverManager::loadFromName($driver, $this->config)); |
||
496 | } |
||
497 | |||
498 | $recipients = is_array($recipients) ? $recipients : [$recipients]; |
||
499 | |||
500 | foreach ($recipients as $recipient) { |
||
501 | $this->message = new IncomingMessage('', $recipient, ''); |
||
502 | $response = $this->reply($message, $additionalParameters); |
||
503 | } |
||
504 | |||
505 | $this->message = $previousMessage; |
||
506 | $this->driver = $previousDriver; |
||
507 | |||
508 | return $response; |
||
0 ignored issues
–
show
|
|||
509 | } |
||
510 | |||
511 | /** |
||
512 | * @param string|Question $question |
||
513 | * @param array|Closure $next |
||
514 | * @param array $additionalParameters |
||
515 | * @param null|string $recipient |
||
516 | * @param null|string $driver |
||
517 | * @return Response |
||
518 | */ |
||
519 | public function ask($question, $next, $additionalParameters = [], $recipient = null, $driver = null) |
||
520 | { |
||
521 | if (! is_null($recipient) && ! is_null($driver)) { |
||
522 | if (is_string($driver)) { |
||
523 | $driver = DriverManager::loadFromName($driver, $this->config); |
||
524 | } |
||
525 | $this->message = new IncomingMessage('', $recipient, ''); |
||
526 | $this->setDriver($driver); |
||
527 | } |
||
528 | |||
529 | $response = $this->reply($question, $additionalParameters); |
||
530 | $this->storeConversation(new InlineConversation, $next, $question, $additionalParameters); |
||
531 | |||
532 | return $response; |
||
533 | } |
||
534 | |||
535 | /** |
||
536 | * @return $this |
||
537 | */ |
||
538 | public function types() |
||
539 | { |
||
540 | $this->getDriver()->types($this->message); |
||
541 | |||
542 | return $this; |
||
543 | } |
||
544 | |||
545 | /** |
||
546 | * @param int $seconds Number of seconds to wait |
||
547 | * @return $this |
||
548 | */ |
||
549 | public function typesAndWaits($seconds) |
||
550 | { |
||
551 | $this->getDriver()->types($this->message); |
||
552 | sleep($seconds); |
||
553 | |||
554 | return $this; |
||
555 | } |
||
556 | |||
557 | /** |
||
558 | * Low-level method to perform driver specific API requests. |
||
559 | * |
||
560 | * @param string $endpoint |
||
561 | * @param array $additionalParameters |
||
562 | * @return $this |
||
563 | * @throws BadMethodCallException |
||
564 | */ |
||
565 | public function sendRequest($endpoint, $additionalParameters = []) |
||
566 | { |
||
567 | $driver = $this->getDriver(); |
||
568 | if (method_exists($driver, 'sendRequest')) { |
||
569 | return $driver->sendRequest($endpoint, $additionalParameters, $this->message); |
||
570 | } else { |
||
571 | throw new BadMethodCallException('The driver '.$this->getDriver()->getName().' does not support low level requests.'); |
||
572 | } |
||
573 | } |
||
574 | |||
575 | /** |
||
576 | * @param string|Question $message |
||
577 | * @param array $additionalParameters |
||
578 | * @return mixed |
||
579 | */ |
||
580 | public function reply($message, $additionalParameters = []) |
||
581 | { |
||
582 | $this->outgoingMessage = is_string($message) ? OutgoingMessage::create($message) : $message; |
||
583 | |||
584 | return $this->sendPayload($this->getDriver()->buildServicePayload($this->outgoingMessage, $this->message, $additionalParameters)); |
||
585 | } |
||
586 | |||
587 | /** |
||
588 | * @param $payload |
||
589 | * @return mixed |
||
590 | */ |
||
591 | public function sendPayload($payload) |
||
592 | { |
||
593 | return $this->middleware->applyMiddleware('sending', $payload, [], function ($payload) { |
||
594 | $this->outgoingMessage = null; |
||
595 | |||
596 | return $this->getDriver()->sendPayload($payload); |
||
597 | }); |
||
598 | } |
||
599 | |||
600 | /** |
||
601 | * Return a random message. |
||
602 | * @param array $messages |
||
603 | * @return $this |
||
604 | */ |
||
605 | public function randomReply(array $messages) |
||
606 | { |
||
607 | return $this->reply($messages[array_rand($messages)]); |
||
608 | } |
||
609 | |||
610 | /** |
||
611 | * Make an action for an invokable controller. |
||
612 | * |
||
613 | * @param string $action |
||
614 | * @return string |
||
615 | * @throws UnexpectedValueException |
||
616 | */ |
||
617 | protected function makeInvokableAction($action) |
||
618 | { |
||
619 | if (! method_exists($action, '__invoke')) { |
||
620 | throw new UnexpectedValueException(sprintf( |
||
621 | 'Invalid hears action: [%s]', $action |
||
622 | )); |
||
623 | } |
||
624 | |||
625 | return $action.'@__invoke'; |
||
626 | } |
||
627 | |||
628 | /** |
||
629 | * @param $callback |
||
630 | * @return array|string|Closure |
||
631 | * @throws UnexpectedValueException |
||
632 | */ |
||
633 | protected function getCallable($callback) |
||
634 | { |
||
635 | if ($callback instanceof Closure) { |
||
636 | return $callback; |
||
637 | } |
||
638 | |||
639 | if (is_array($callback)) { |
||
640 | return $callback; |
||
641 | } |
||
642 | |||
643 | if (strpos($callback, '@') === false) { |
||
644 | $callback = $this->makeInvokableAction($callback); |
||
645 | } |
||
646 | |||
647 | list($class, $method) = explode('@', $callback); |
||
648 | |||
649 | return [new $class($this), $method]; |
||
650 | } |
||
651 | |||
652 | /** |
||
653 | * @return array |
||
654 | */ |
||
655 | public function getMatches() |
||
656 | { |
||
657 | return $this->matches; |
||
658 | } |
||
659 | |||
660 | /** |
||
661 | * @return IncomingMessage |
||
662 | */ |
||
663 | public function getMessage() |
||
664 | { |
||
665 | return $this->message; |
||
666 | } |
||
667 | |||
668 | /** |
||
669 | * @return OutgoingMessage|Question |
||
670 | */ |
||
671 | public function getOutgoingMessage() |
||
672 | { |
||
673 | return $this->outgoingMessage; |
||
674 | } |
||
675 | |||
676 | /** |
||
677 | * @param string $name |
||
678 | * @param array $arguments |
||
679 | * @return mixed |
||
680 | * @throws BadMethodCallException |
||
681 | */ |
||
682 | public function __call($name, $arguments) |
||
683 | { |
||
684 | if (method_exists($this->getDriver(), $name)) { |
||
685 | // Add the current message to the passed arguments |
||
686 | $arguments[] = $this->getMessage(); |
||
687 | $arguments[] = $this; |
||
688 | |||
689 | return call_user_func_array([$this->getDriver(), $name], $arguments); |
||
690 | } |
||
691 | |||
692 | throw new BadMethodCallException('Method ['.$name.'] does not exist.'); |
||
693 | } |
||
694 | |||
695 | /** |
||
696 | * Load driver on wakeup. |
||
697 | */ |
||
698 | public function __wakeup() |
||
699 | { |
||
700 | $this->driver = DriverManager::loadFromName($this->driverName, $this->config); |
||
701 | } |
||
702 | |||
703 | /** |
||
704 | * @return array |
||
705 | */ |
||
706 | public function __sleep() |
||
707 | { |
||
708 | $this->driverName = $this->driver->getName(); |
||
709 | |||
710 | return [ |
||
711 | 'event', |
||
712 | 'exceptionHandler', |
||
713 | 'driverName', |
||
714 | 'storage', |
||
715 | 'message', |
||
716 | 'cache', |
||
717 | 'matches', |
||
718 | 'matcher', |
||
719 | 'config', |
||
720 | 'middleware', |
||
721 | ]; |
||
722 | } |
||
723 | } |
||
724 |
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:
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
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: