Completed
Branch develop (b42f4c)
by Vasil
13:57
created

Response   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 302
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 38
c 0
b 0
f 0
lcom 1
cbo 6
dl 0
loc 302
rs 8.3999

7 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 64 12
C _receive() 0 73 17
B setType() 0 18 5
A getType() 0 4 1
A getArgument() 0 10 1
A getProperty() 0 4 1
A getUnrecognizedWords() 0 4 1
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
     * An array of unrecognized words in network order.
68
     *
69
     * @var string[]
70
     */
71
    protected $unrecognizedWords = array();
72
73
    /**
74
     * The response type.
75
     *
76
     * @var string
77
     */
78
    private $_type;
79
80
    /**
81
     * Extracts a new response from a communicator.
82
     *
83
     * @param Communicator  $com       The communicator from which to extract
84
     *     the new response.
85
     * @param int|null      $streamOn  Threshold after which to stream
86
     *     a word. NULL to disable streaming altogether.
87
     * @param int           $sTimeout  If a response is not immediately
88
     *     available, wait this many seconds. If NULL, wait indefinitely.
89
     * @param int|null      $usTimeout Microseconds to add to the waiting time.
90
     * @param Registry|null $reg       An optional registry to sync the
91
     *     response with.
92
     *
93
     * @see getType()
94
     * @see getArgument()
95
     */
96
    public function __construct(
97
        Communicator $com,
98
        $streamOn = null,
99
        $sTimeout = 0,
100
        $usTimeout = null,
101
        Registry $reg = null
102
    ) {
103
        if (null === $reg) {
104
            if ($com->getTransmitter()->isPersistent()) {
105
                $old = $com->getTransmitter()
106
                    ->lock(T\Stream::DIRECTION_RECEIVE);
107
                try {
108
                    $this->_receive($com, $streamOn, $sTimeout, $usTimeout);
109
                } catch (E $e) {
110
                    $com->getTransmitter()->lock($old, true);
111
                    throw $e;
112
                }
113
                $com->getTransmitter()->lock($old, true);
114
            } else {
115
                $this->_receive($com, $streamOn, $sTimeout, $usTimeout);
116
            }
117
        } else {
118
            while (null === ($response = $reg->getNextResponse())) {
119
                $newResponse = new self($com, 0, $sTimeout, $usTimeout);
120
                $tagInfo = $reg::parseTag($newResponse->getTag());
121
                $newResponse->setTag($tagInfo[1]);
122
                if (!$reg->add($newResponse, $tagInfo[0])) {
123
                    $response = $newResponse;
124
                    break;
125
                }
126
            }
127
128
            $this->_type = $response->_type;
129
            $this->setTag($response->getTag());
130
            $this->attributes = $response->attributes;
131
            $this->unrecognizedWords = $response->unrecognizedWords;
132
            if (null === $streamOn) {
133
                foreach ($response->attributes as $name => $value) {
134
                    $this->setAttribute(
135
                        $name,
136
                        stream_get_contents($value)
137
                    );
138
                }
139
                foreach ($response->unrecognizedWords as $i => $value) {
140
                    $this->unrecognizedWords[$i] = stream_get_contents($value);
141
                }
142
            } else {
143
                foreach ($response->attributes as $name => $value) {
144
                    fseek($value, 0, SEEK_END);
145
                    $valueLength = ftell($value);
146
                    rewind($value);
147
                    if ((strlen($name) + 2 + $valueLength) < $streamOn) {
148
                        $this->setAttribute(
149
                            $name,
150
                            stream_get_contents($value)
151
                        );
152
                    }
153
                }
154
                foreach ($response->unrecognizedWords as $i => $value) {
155
                    $this->unrecognizedWords[$i] = stream_get_contents($value);
156
                }
157
            }
158
        }
159
    }
160
161
    /**
162
     * Extracts a new response from a communicator.
163
     *
164
     * This is the function that performs the actual receiving, while the
165
     * constructor is also involved in locks and registry sync.
166
     *
167
     * @param Communicator $com       The communicator from which to extract
168
     *     the new response.
169
     * @param int|null     $streamOn  Threshold after which to stream
170
     *     a word. NULL to disable streaming altogether.
171
     * @param int          $sTimeout  If a response is not immediately
172
     *     available, wait this many seconds. If NULL, wait indefinitely.
173
     *     Note that if an empty sentence is received, the timeout will be
174
     *     reset for another sentence receiving.
175
     * @param int|null     $usTimeout Microseconds to add to the waiting time.
176
     *
177
     * @return void
178
     */
179
    private function _receive(
180
        Communicator $com,
181
        $streamOn = null,
182
        $sTimeout = 0,
183
        $usTimeout = null
184
    ) {
185
        do {
186
            if (!$com->getTransmitter()->isDataAwaiting(
187
                $sTimeout,
188
                $usTimeout
189
            )) {
190
                throw new SocketException(
191
                    'No data within the time limit',
192
                    SocketException::CODE_NO_DATA
193
                );
194
            }
195
            $type = $com->getNextWord();
196
        } while ('' === $type);
197
        $this->setType($type);
198
        if (null === $streamOn) {
199
            for ($word = $com->getNextWord(); '' !== $word;
200
                $word = $com->getNextWord()) {
201
                if (preg_match('/^=([^=]+)=(.*)$/sS', $word, $matches)) {
202
                    $this->setAttribute($matches[1], $matches[2]);
203
                } elseif (preg_match('/^\.tag=(.*)$/sS', $word, $matches)) {
204
                    $this->setTag($matches[1]);
205
                } else {
206
                    $this->unrecognizedWords[] = $word;
207
                }
208
            }
209
        } else {
210
            while ($com->getNextWordLength() !== 0) {
211
                if ($com->getNextWordLength() < $streamOn) {
212
                    $word = $com->getNextWord();
213
                    if (preg_match('/^=([^=]+)=(.*)$/sS', $word, $matches)) {
214
                        $this->setAttribute($matches[1], $matches[2]);
215
                    } elseif (preg_match('/^\.tag=(.*)$/sS', $word, $matches)) {
216
                        $this->setTag($matches[1]);
217
                    } else {
218
                        $this->unrecognizedWords[] = $word;
219
                    }
220
                } else {
221
                    $word = $com->getNextWordAsStream();
222
                    $ind = fread($word, 1);
223
                    if ('=' === $ind || '.' === $ind) {
224
                        $prefix = stream_get_line($word, null, '=');
225
                    }
226
                    if ('=' === $ind) {
227
                        $value = fopen('php://temp', 'r+b');
228
                        $bytesCopied = ftell($word);
229
                        while (!feof($word)) {
230
                            $bytesCopied += stream_copy_to_stream(
231
                                $word,
232
                                $value,
233
                                0xFFFFF,
234
                                $bytesCopied
235
                            );
236
                        }
237
                        rewind($value);
238
                        $this->setAttribute($prefix, $value);
239
                        continue;
240
                    }
241
                    if ('.' === $ind && 'tag' === $prefix) {
242
                        $this->setTag(stream_get_contents($word, -1, -1));
243
                        continue;
244
                    }
245
                    rewind($word);
246
                    $this->unrecognizedWords[] = $word;
247
                }
248
            }
249
            $com->getNextWord();
250
        }
251
    }
252
253
    /**
254
     * Sets the response type.
255
     *
256
     * Sets the response type. Valid values are the TYPE_* constants.
257
     *
258
     * @param string $type The new response type.
259
     *
260
     * @return $this The response object.
261
     *
262
     * @see getType()
263
     */
264
    protected function setType($type)
265
    {
266
        switch ($type) {
267
        case self::TYPE_FINAL:
268
        case self::TYPE_DATA:
269
        case self::TYPE_ERROR:
270
        case self::TYPE_FATAL:
271
            $this->_type = $type;
272
            return $this;
273
        default:
274
            throw new UnexpectedValueException(
275
                'Unrecognized response type.',
276
                UnexpectedValueException::CODE_RESPONSE_TYPE_UNKNOWN,
277
                null,
278
                $type
279
            );
280
        }
281
    }
282
283
    /**
284
     * Gets the response type.
285
     *
286
     * @return string The response type.
287
     *
288
     * @see setType()
289
     */
290
    public function getType()
291
    {
292
        return $this->_type;
293
    }
294
295
    /**
296
     * Gets the value of an argument.
297
     *
298
     * @param string $name The name of the argument.
299
     *
300
     * @return string|resource|null The value of the specified argument.
301
     *     Returns NULL if such an argument is not set.
302
     *
303
     * @deprecated         1.0.0b5 Use {@link static::getProperty()} instead.
304
     *     This method will be removed upon final release, and is currently
305
     *     left standing merely because it can't be easily search&replaced in
306
     *     existing code, due to the fact the name "getArgument()" is shared
307
     *     with {@link Request::getArgument()}, which is still valid.
308
     * @codeCoverageIgnore
309
     */
310
    public function getArgument($name)
311
    {
312
        trigger_error(
313
            'Response::getArgument() is deprecated in favor of ' .
314
            'Response::getProperty() (but note that Request::getArgument() ' .
315
            'is still valid)',
316
            E_USER_DEPRECATED
317
        );
318
        return $this->getAttribute($name);
319
    }
320
321
    /**
322
     * Gets the value of a property.
323
     *
324
     * @param string $name The name of the property.
325
     *
326
     * @return string|resource|null The value of the specified property.
327
     *     Returns NULL if such a property is not set.
328
     */
329
    public function getProperty($name)
330
    {
331
        return parent::getAttribute($name);
332
    }
333
334
    /**
335
     * Gets a list of unrecognized words.
336
     *
337
     * @return string[] The list of unrecognized words.
338
     */
339
    public function getUnrecognizedWords()
340
    {
341
        return $this->unrecognizedWords;
342
    }
343
}
344