1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* ~~summary~~ |
5
|
|
|
* |
6
|
|
|
* ~~description~~ |
7
|
|
|
* |
8
|
|
|
* PHP version 5 |
9
|
|
|
* |
10
|
|
|
* @category Net |
11
|
|
|
* @package PEAR2_Net_RouterOS |
12
|
|
|
* @author Vasil Rangelov <[email protected]> |
13
|
|
|
* @copyright 2011 Vasil Rangelov |
14
|
|
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
15
|
|
|
* @version GIT: $Id$ |
16
|
|
|
* @link http://pear2.php.net/PEAR2_Net_RouterOS |
17
|
|
|
*/ |
18
|
|
|
/** |
19
|
|
|
* The namespace declaration. |
20
|
|
|
*/ |
21
|
|
|
namespace PEAR2\Net\RouterOS; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Refers to transmitter direction constants. |
25
|
|
|
*/ |
26
|
|
|
use PEAR2\Net\Transmitter as T; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Locks are released upon any exception from anywhere. |
30
|
|
|
*/ |
31
|
|
|
use Exception as E; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Represents a RouterOS response. |
35
|
|
|
* |
36
|
|
|
* @category Net |
37
|
|
|
* @package PEAR2_Net_RouterOS |
38
|
|
|
* @author Vasil Rangelov <[email protected]> |
39
|
|
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
40
|
|
|
* @link http://pear2.php.net/PEAR2_Net_RouterOS |
41
|
|
|
*/ |
42
|
|
|
class Response extends Message |
43
|
|
|
{ |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* The last response for a request. |
47
|
|
|
*/ |
48
|
|
|
const TYPE_FINAL = '!done'; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* A response with data. |
52
|
|
|
*/ |
53
|
|
|
const TYPE_DATA = '!re'; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* A response signifying error. |
57
|
|
|
*/ |
58
|
|
|
const TYPE_ERROR = '!trap'; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* A response signifying a fatal error, due to which the connection would be |
62
|
|
|
* terminated. |
63
|
|
|
*/ |
64
|
|
|
const TYPE_FATAL = '!fatal'; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var string[] An array of unrecognized words in network order. |
68
|
|
|
*/ |
69
|
|
|
protected $unrecognizedWords = array(); |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @var string The response type. |
73
|
|
|
*/ |
74
|
|
|
private $_type; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Extracts a new response from a communicator. |
78
|
|
|
* |
79
|
|
|
* @param Communicator $com The communicator from which to extract |
80
|
|
|
* the new response. |
81
|
|
|
* @param bool $asStream Whether to populate the argument values |
82
|
|
|
* with streams instead of strings. |
83
|
|
|
* @param int $sTimeout If a response is not immediately |
84
|
|
|
* available, wait this many seconds. If NULL, wait indefinitely. |
85
|
|
|
* @param int|null $usTimeout Microseconds to add to the waiting time. |
86
|
|
|
* @param Registry|null $reg An optional registry to sync the |
87
|
|
|
* response with. |
88
|
|
|
* |
89
|
|
|
* @see getType() |
90
|
|
|
* @see getArgument() |
91
|
|
|
*/ |
92
|
|
|
public function __construct( |
93
|
|
|
Communicator $com, |
94
|
|
|
$asStream = false, |
95
|
|
|
$sTimeout = 0, |
96
|
|
|
$usTimeout = null, |
97
|
|
|
Registry $reg = null |
98
|
|
|
) { |
99
|
|
|
if (null === $reg) { |
100
|
|
|
if ($com->getTransmitter()->isPersistent()) { |
101
|
|
|
$old = $com->getTransmitter() |
102
|
|
|
->lock(T\Stream::DIRECTION_RECEIVE); |
103
|
|
|
try { |
104
|
|
|
$this->_receive($com, $asStream, $sTimeout, $usTimeout); |
105
|
|
|
} catch (E $e) { |
106
|
|
|
$com->getTransmitter()->lock($old, true); |
|
|
|
|
107
|
|
|
throw $e; |
108
|
|
|
} |
109
|
|
|
$com->getTransmitter()->lock($old, true); |
|
|
|
|
110
|
|
|
} else { |
111
|
|
|
$this->_receive($com, $asStream, $sTimeout, $usTimeout); |
112
|
|
|
} |
113
|
|
|
} else { |
114
|
|
|
while (null === ($response = $reg->getNextResponse())) { |
115
|
|
|
$newResponse = new self($com, true, $sTimeout, $usTimeout); |
116
|
|
|
$tagInfo = $reg::parseTag($newResponse->getTag()); |
117
|
|
|
$newResponse->setTag($tagInfo[1]); |
118
|
|
|
if (!$reg->add($newResponse, $tagInfo[0])) { |
119
|
|
|
$response = $newResponse; |
120
|
|
|
break; |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
$this->_type = $response->_type; |
125
|
|
|
$this->attributes = $response->attributes; |
126
|
|
|
$this->unrecognizedWords = $response->unrecognizedWords; |
127
|
|
|
$this->setTag($response->getTag()); |
128
|
|
|
|
129
|
|
|
if (!$asStream) { |
130
|
|
|
foreach ($this->attributes as $name => $value) { |
131
|
|
|
$this->setAttribute( |
132
|
|
|
$name, |
133
|
|
|
stream_get_contents($value) |
134
|
|
|
); |
135
|
|
|
} |
136
|
|
|
foreach ($response->unrecognizedWords as $i => $value) { |
137
|
|
|
$this->unrecognizedWords[$i] = stream_get_contents($value); |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Extracts a new response from a communicator. |
145
|
|
|
* |
146
|
|
|
* This is the function that performs the actual receiving, while the |
147
|
|
|
* constructor is also involved in locks and registry sync. |
148
|
|
|
* |
149
|
|
|
* @param Communicator $com The communicator from which to extract |
150
|
|
|
* the new response. |
151
|
|
|
* @param bool $asStream Whether to populate the argument values |
152
|
|
|
* with streams instead of strings. |
153
|
|
|
* @param int $sTimeout If a response is not immediately |
154
|
|
|
* available, wait this many seconds. If NULL, wait indefinitely. |
155
|
|
|
* Note that if an empty sentence is received, the timeout will be |
156
|
|
|
* reset for another sentence receiving. |
157
|
|
|
* @param int|null $usTimeout Microseconds to add to the waiting time. |
158
|
|
|
* |
159
|
|
|
* @return void |
160
|
|
|
*/ |
161
|
|
|
private function _receive( |
162
|
|
|
Communicator $com, |
163
|
|
|
$asStream = false, |
164
|
|
|
$sTimeout = 0, |
165
|
|
|
$usTimeout = null |
166
|
|
|
) { |
167
|
|
|
do { |
168
|
|
|
if (!$com->getTransmitter()->isDataAwaiting( |
169
|
|
|
$sTimeout, |
170
|
|
|
$usTimeout |
171
|
|
|
)) { |
172
|
|
|
throw new SocketException( |
173
|
|
|
'No data within the time limit', |
174
|
|
|
SocketException::CODE_NO_DATA |
175
|
|
|
); |
176
|
|
|
} |
177
|
|
|
$type = $com->getNextWord(); |
178
|
|
|
} while ('' === $type); |
179
|
|
|
$this->setType($type); |
180
|
|
|
if ($asStream) { |
181
|
|
|
for ($word = $com->getNextWordAsStream(), fseek($word, 0, SEEK_END); |
182
|
|
|
ftell($word) !== 0; |
183
|
|
|
$word = $com->getNextWordAsStream(), fseek( |
184
|
|
|
$word, |
185
|
|
|
0, |
186
|
|
|
SEEK_END |
187
|
|
|
)) { |
188
|
|
|
rewind($word); |
189
|
|
|
$ind = fread($word, 1); |
190
|
|
|
if ('=' === $ind || '.' === $ind) { |
191
|
|
|
$prefix = stream_get_line($word, null, '='); |
192
|
|
|
} |
193
|
|
|
if ('=' === $ind) { |
194
|
|
|
$value = fopen('php://temp', 'r+b'); |
195
|
|
|
$bytesCopied = ftell($word); |
196
|
|
|
while (!feof($word)) { |
197
|
|
|
$bytesCopied += stream_copy_to_stream( |
198
|
|
|
$word, |
199
|
|
|
$value, |
200
|
|
|
0xFFFFF, |
201
|
|
|
$bytesCopied |
202
|
|
|
); |
203
|
|
|
} |
204
|
|
|
rewind($value); |
205
|
|
|
$this->setAttribute($prefix, $value); |
206
|
|
|
continue; |
207
|
|
|
} |
208
|
|
|
if ('.' === $ind && 'tag' === $prefix) { |
209
|
|
|
$this->setTag(stream_get_contents($word, -1, -1)); |
210
|
|
|
continue; |
211
|
|
|
} |
212
|
|
|
rewind($word); |
213
|
|
|
$this->unrecognizedWords[] = $word; |
214
|
|
|
} |
215
|
|
|
} else { |
216
|
|
|
for ($word = $com->getNextWord(); '' !== $word; |
217
|
|
|
$word = $com->getNextWord()) { |
218
|
|
|
if (preg_match('/^=([^=]+)=(.*)$/sS', $word, $matches)) { |
219
|
|
|
$this->setAttribute($matches[1], $matches[2]); |
220
|
|
|
} elseif (preg_match('/^\.tag=(.*)$/sS', $word, $matches)) { |
221
|
|
|
$this->setTag($matches[1]); |
222
|
|
|
} else { |
223
|
|
|
$this->unrecognizedWords[] = $word; |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* Sets the response type. |
231
|
|
|
* |
232
|
|
|
* Sets the response type. Valid values are the TYPE_* constants. |
233
|
|
|
* |
234
|
|
|
* @param string $type The new response type. |
235
|
|
|
* |
236
|
|
|
* @return $this The response object. |
237
|
|
|
* |
238
|
|
|
* @see getType() |
239
|
|
|
*/ |
240
|
|
|
protected function setType($type) |
241
|
|
|
{ |
242
|
|
|
switch ($type) { |
243
|
|
|
case self::TYPE_FINAL: |
244
|
|
|
case self::TYPE_DATA: |
245
|
|
|
case self::TYPE_ERROR: |
246
|
|
|
case self::TYPE_FATAL: |
247
|
|
|
$this->_type = $type; |
248
|
|
|
return $this; |
249
|
|
|
default: |
250
|
|
|
throw new UnexpectedValueException( |
251
|
|
|
'Unrecognized response type.', |
252
|
|
|
UnexpectedValueException::CODE_RESPONSE_TYPE_UNKNOWN, |
253
|
|
|
null, |
254
|
|
|
$type |
255
|
|
|
); |
256
|
|
|
} |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* Gets the response type. |
261
|
|
|
* |
262
|
|
|
* @return string The response type. |
263
|
|
|
* |
264
|
|
|
* @see setType() |
265
|
|
|
*/ |
266
|
|
|
public function getType() |
267
|
|
|
{ |
268
|
|
|
return $this->_type; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* Gets the value of an argument. |
273
|
|
|
* |
274
|
|
|
* @param string $name The name of the argument. |
275
|
|
|
* |
276
|
|
|
* @return string|resource|null The value of the specified argument. |
277
|
|
|
* Returns NULL if such an argument is not set. |
278
|
|
|
* |
279
|
|
|
* @deprecated 1.0.0b5 Use {@link static::getProperty()} instead. |
280
|
|
|
* This method will be removed upon final release, and is currently |
281
|
|
|
* left standing merely because it can't be easily search&replaced in |
282
|
|
|
* existing code, due to the fact the name "getArgument()" is shared |
283
|
|
|
* with {@link Request::getArgument()}, which is still valid. |
284
|
|
|
* @codeCoverageIgnore |
285
|
|
|
*/ |
286
|
|
|
public function getArgument($name) |
287
|
|
|
{ |
288
|
|
|
trigger_error( |
289
|
|
|
'Response::getArgument() is deprecated in favor of ' . |
290
|
|
|
'Response::getProperty() (but note that Request::getArgument() ' . |
291
|
|
|
'is still valid)', |
292
|
|
|
E_USER_DEPRECATED |
293
|
|
|
); |
294
|
|
|
return $this->getAttribute($name); |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Gets the value of a property. |
299
|
|
|
* |
300
|
|
|
* @param string $name The name of the property. |
301
|
|
|
* |
302
|
|
|
* @return string|resource|null The value of the specified property. |
303
|
|
|
* Returns NULL if such a property is not set. |
304
|
|
|
*/ |
305
|
|
|
public function getProperty($name) |
306
|
|
|
{ |
307
|
|
|
return parent::getAttribute($name); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Gets a list of unrecognized words. |
312
|
|
|
* |
313
|
|
|
* @return string[] The list of unrecognized words. |
314
|
|
|
*/ |
315
|
|
|
public function getUnrecognizedWords() |
316
|
|
|
{ |
317
|
|
|
return $this->unrecognizedWords; |
318
|
|
|
} |
319
|
|
|
} |
320
|
|
|
|
This check looks for type mismatches where the missing type is
false
. This is usually indicative of an error condtion.Consider the follow example
This function either returns a new
DateTime
object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returnedfalse
before passing on the value to another function or method that may not be able to handle afalse
.