1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace BotMan\BotMan\Drivers\Tests; |
4
|
|
|
|
5
|
|
|
use BotMan\BotMan\Users\User; |
6
|
|
|
use BotMan\BotMan\Messages\Incoming\Answer; |
7
|
|
|
use BotMan\BotMan\Interfaces\DriverInterface; |
8
|
|
|
use BotMan\BotMan\Interfaces\VerifiesService; |
9
|
|
|
use BotMan\BotMan\Messages\Outgoing\Question; |
10
|
|
|
use Symfony\Component\HttpFoundation\Request; |
11
|
|
|
use BotMan\BotMan\Drivers\Events\GenericEvent; |
12
|
|
|
use Symfony\Component\HttpFoundation\Response; |
13
|
|
|
use BotMan\BotMan\Messages\Incoming\IncomingMessage; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* A fake driver for tests. Must be used with ProxyDriver. |
17
|
|
|
* Example to set it up in a unit test: |
18
|
|
|
* <code> |
19
|
|
|
* public static function setUpBeforeClass() |
20
|
|
|
* { |
21
|
|
|
* DriverManager::loadDriver(ProxyDriver::class); |
22
|
|
|
* } |
23
|
|
|
* public function setUp() |
24
|
|
|
* { |
25
|
|
|
* $this->fakeDriver = new FakeDriver(); |
26
|
|
|
* ProxyDriver::setInstance($this->fakeDriver); |
27
|
|
|
* } |
28
|
|
|
* </code>. |
29
|
|
|
*/ |
30
|
|
|
class FakeDriver implements DriverInterface, VerifiesService |
31
|
|
|
{ |
32
|
|
|
/** @var bool */ |
33
|
|
|
public $matchesRequest = true; |
34
|
|
|
|
35
|
|
|
/** @var bool */ |
36
|
|
|
public $hasMatchingEvent = false; |
37
|
|
|
|
38
|
|
|
/** @var \BotMan\BotMan\Messages\Incoming\IncomingMessage[] */ |
39
|
|
|
public $messages = []; |
40
|
|
|
|
41
|
|
|
/** @var bool */ |
42
|
|
|
public $isBot = false; |
43
|
|
|
|
44
|
|
|
/** @var bool */ |
45
|
|
|
public $isInteractiveMessageReply = false; |
46
|
|
|
|
47
|
|
|
/** @var bool */ |
48
|
|
|
public $isConfigured = true; |
49
|
|
|
|
50
|
|
|
/** @var array */ |
51
|
|
|
private $botMessages = []; |
52
|
|
|
|
53
|
|
|
/** @var bool */ |
54
|
|
|
private $botIsTyping = false; |
55
|
|
|
|
56
|
|
|
/** @var string */ |
57
|
|
|
private $driver_name = 'Fake'; |
58
|
|
|
|
59
|
|
|
/** @var string */ |
60
|
|
|
private $event_name; |
61
|
|
|
|
62
|
|
|
/** @var array */ |
63
|
|
|
private $event_payload; |
64
|
|
|
|
65
|
|
|
/** @var string */ |
66
|
|
|
private $user_id = null; |
67
|
|
|
|
68
|
|
|
/** @var string */ |
69
|
|
|
private $user_first_name = 'Marcel'; |
70
|
|
|
|
71
|
|
|
/** @var string */ |
72
|
|
|
private $user_last_name = 'Pociot'; |
73
|
|
|
|
74
|
|
|
/** @var string */ |
75
|
|
|
private $username = 'BotMan'; |
76
|
|
|
|
77
|
|
|
/** @var array */ |
78
|
|
|
private $user_info = []; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* @return FakeDriver |
82
|
|
|
*/ |
83
|
|
|
public static function create() |
84
|
|
|
{ |
85
|
|
|
return new static; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* @return FakeDriver |
90
|
|
|
*/ |
91
|
|
|
public static function createInactive() |
92
|
|
|
{ |
93
|
|
|
$driver = new static; |
94
|
|
|
$driver->isConfigured = false; |
95
|
|
|
|
96
|
|
|
return $driver; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
public function matchesRequest() |
100
|
|
|
{ |
101
|
|
|
return $this->matchesRequest; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
public function getMessages() |
105
|
|
|
{ |
106
|
|
|
foreach ($this->messages as &$message) { |
107
|
|
|
$message->setIsFromBot($this->isBot()); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
return $this->messages; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
protected function isBot() |
114
|
|
|
{ |
115
|
|
|
return $this->isBot; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
public function isConfigured() |
119
|
|
|
{ |
120
|
|
|
return $this->isConfigured; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
public function setUser(array $user_info) |
124
|
|
|
{ |
125
|
|
|
$this->user_id = $user_info['id'] ?? $this->user_id; |
126
|
|
|
$this->user_first_name = $user_info['first_name'] ?? $this->user_first_name; |
127
|
|
|
$this->user_last_name = $user_info['last_name'] ?? $this->user_last_name; |
128
|
|
|
$this->username = $user_info['username'] ?? $this->username; |
129
|
|
|
$this->user_info = $user_info; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
public function getUser(IncomingMessage $matchingMessage) |
133
|
|
|
{ |
134
|
|
|
return new User($this->user_id ?? $matchingMessage->getSender(), $this->user_first_name, $this->user_last_name, $this->username, $this->user_info); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
public function getConversationAnswer(IncomingMessage $message) |
138
|
|
|
{ |
139
|
|
|
$answer = Answer::create($message->getText())->setMessage($message)->setValue($message->getText()); |
140
|
|
|
$answer->setInteractiveReply($this->isInteractiveMessageReply); |
141
|
|
|
|
142
|
|
|
return $answer; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function buildServicePayload($message, $matchingMessage, $additionalParameters = []) |
146
|
|
|
{ |
147
|
|
|
return $message; |
|
|
|
|
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
public function sendPayload($payload) |
151
|
|
|
{ |
152
|
|
|
$this->botMessages[] = $payload; |
153
|
|
|
$text = method_exists($payload, 'getText') ? $payload->getText() : ''; |
154
|
|
|
|
155
|
|
|
return Response::create(json_encode($text)); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
public function setName($name) |
159
|
|
|
{ |
160
|
|
|
$this->driver_name = $name; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
public function getName() |
164
|
|
|
{ |
165
|
|
|
return $this->driver_name; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
public function types(IncomingMessage $matchingMessage) |
169
|
|
|
{ |
170
|
|
|
$this->botIsTyping = true; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* Send a typing indicator and wait for the given amount of seconds. |
175
|
|
|
* @param IncomingMessage $matchingMessage |
176
|
|
|
* @param float $seconds |
177
|
|
|
* @return mixed |
178
|
|
|
*/ |
179
|
|
|
public function typesAndWaits(IncomingMessage $matchingMessage, float $seconds) |
180
|
|
|
{ |
181
|
|
|
$this->types($matchingMessage); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Returns true if types() has been called. |
186
|
|
|
* |
187
|
|
|
* @return bool |
188
|
|
|
*/ |
189
|
|
|
public function isBotTyping() |
190
|
|
|
{ |
191
|
|
|
return $this->botIsTyping; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @return void |
196
|
|
|
*/ |
197
|
|
|
public function setEventName($name) |
198
|
|
|
{ |
199
|
|
|
$this->event_name = $name; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @return void |
204
|
|
|
*/ |
205
|
|
|
public function setEventPayload($payload) |
206
|
|
|
{ |
207
|
|
|
$this->event_payload = $payload; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* @return bool|GenericEvent |
212
|
|
|
*/ |
213
|
|
|
public function hasMatchingEvent() |
214
|
|
|
{ |
215
|
|
|
if (isset($this->event_name)) { |
216
|
|
|
$event = new GenericEvent($this->event_payload); |
217
|
|
|
$event->setName($this->event_name); |
218
|
|
|
|
219
|
|
|
return $event; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
return $this->hasMatchingEvent; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Returns array of messages from bot. |
227
|
|
|
* |
228
|
|
|
* @return string[]|Question[] |
229
|
|
|
*/ |
230
|
|
|
public function getBotMessages() |
231
|
|
|
{ |
232
|
|
|
return $this->botMessages; |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* Clear received messages from bot. |
237
|
|
|
*/ |
238
|
|
|
public function resetBotMessages() |
239
|
|
|
{ |
240
|
|
|
$this->botIsTyping = false; |
241
|
|
|
$this->botMessages = []; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* Tells if the stored conversation callbacks are serialized. |
246
|
|
|
* |
247
|
|
|
* @return bool |
248
|
|
|
*/ |
249
|
|
|
public function serializesCallbacks() |
250
|
|
|
{ |
251
|
|
|
return true; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
public function verifyRequest(Request $request) |
255
|
|
|
{ |
256
|
|
|
$_SERVER['driver_verified'] = true; |
257
|
|
|
|
258
|
|
|
return true; |
259
|
|
|
} |
260
|
|
|
} |
261
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.