Passed
Push — develop ( ead434...0a8e77 )
by Vasil
03:53
created

Response::__debugInfo()   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 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
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|resource)[]
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)
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type string; however, parameter $handle of stream_get_contents() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

136
                        stream_get_contents(/** @scrutinizer ignore-type */ $value)
Loading history...
137
                    );
138
                }
139
                foreach ($response->unrecognizedWords as $i => $value) {
140
                    $this->unrecognizedWords[$i] = stream_get_contents($value);
141
                }
142
            } elseif (0 !== $streamOn) {
143
                foreach ($response->attributes as $name => $value) {
144
                    $valueLength = $com::seekableStreamLength($value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type string; however, parameter $stream of PEAR2\Net\RouterOS\Commu...:seekableStreamLength() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

144
                    $valueLength = $com::seekableStreamLength(/** @scrutinizer ignore-type */ $value);
Loading history...
145
                    if ((strlen($name) + 2 + $valueLength) < $streamOn) {
146
                        $this->setAttribute(
147
                            $name,
148
                            stream_get_contents($value)
149
                        );
150
                    }
151
                }
152
                foreach ($response->unrecognizedWords as $i => $value) {
153
                    $valueLength = $com::seekableStreamLength($value);
154
                    if ($valueLength < $streamOn) {
155
                        $this->unrecognizedWords[$i] = stream_get_contents(
156
                            $value
157
                        );
158
                    }
159
                }
160
            }
161
        }
162
    }
163
164
    /**
165
     * Extracts a new response from a communicator.
166
     *
167
     * This is the function that performs the actual receiving, while the
168
     * constructor is also involved in locks and registry sync.
169
     *
170
     * @param Communicator $com       The communicator from which to extract
171
     *     the new response.
172
     * @param int|null     $streamOn  Threshold after which to stream
173
     *     a word. NULL to disable streaming altogether.
174
     * @param int          $sTimeout  If a response is not immediately
175
     *     available, wait this many seconds. If NULL, wait indefinitely.
176
     *     Note that if an empty sentence is received, the timeout will be
177
     *     reset for another sentence receiving.
178
     * @param int|null     $usTimeout Microseconds to add to the waiting time.
179
     *
180
     * @return void
181
     */
182
    private function _receive(
183
        Communicator $com,
184
        $streamOn = null,
185
        $sTimeout = 0,
186
        $usTimeout = null
187
    ) {
188
        do {
189
            if (!$com->getTransmitter()->isDataAwaiting(
190
                $sTimeout,
191
                $usTimeout
192
            )
193
            ) {
194
                throw new SocketException(
195
                    'No data within the time limit',
196
                    SocketException::CODE_NO_DATA
197
                );
198
            }
199
            $type = $com->getNextWord();
200
        } while ('' === $type);
201
        $this->setType($type);
202
        if (null === $streamOn) {
203
            for ($word = $com->getNextWord(); '' !== $word;
204
                $word = $com->getNextWord()) {
205
                if (preg_match('/^=([^=]+)=(.*)$/sS', $word, $matches)) {
206
                    $this->setAttribute($matches[1], $matches[2]);
207
                } elseif (preg_match('/^\.tag=(.*)$/sS', $word, $matches)) {
208
                    $this->setTag($matches[1]);
209
                } else {
210
                    $this->unrecognizedWords[] = $word;
211
                }
212
            }
213
        } else {
214
            while ($com->getNextWordLength() !== 0) {
215
                if ($com->getNextWordLength() < $streamOn) {
216
                    $word = $com->getNextWord();
217
                    if (preg_match('/^=([^=]+)=(.*)$/sS', $word, $matches)) {
218
                        $this->setAttribute($matches[1], $matches[2]);
219
                    } elseif (preg_match('/^\.tag=(.*)$/sS', $word, $matches)) {
220
                        $this->setTag($matches[1]);
221
                    } else {
222
                        $this->unrecognizedWords[] = $word;
223
                    }
224
                } else {
225
                    $word = $com->getNextWordAsStream();
226
                    $ind = fread($word, 1);
227
                    if ('=' === $ind || '.' === $ind) {
228
                        $prefix = stream_get_line($word, null, '=');
229
                    }
230
                    if ('=' === $ind) {
231
                        $value = fopen('php://temp', 'r+b');
232
                        $bytesCopied = ftell($word);
233
                        while (!feof($word)) {
234
                            $bytesCopied += stream_copy_to_stream(
235
                                $word,
236
                                $value,
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type false; however, parameter $dest of stream_copy_to_stream() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

236
                                /** @scrutinizer ignore-type */ $value,
Loading history...
237
                                0xFFFFF,
238
                                $bytesCopied
239
                            );
240
                        }
241
                        rewind($value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type false; however, parameter $handle of rewind() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

241
                        rewind(/** @scrutinizer ignore-type */ $value);
Loading history...
242
                        $this->setAttribute($prefix, $value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type false; however, parameter $value of PEAR2\Net\RouterOS\Message::setAttribute() does only seem to accept resource|null|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

242
                        $this->setAttribute($prefix, /** @scrutinizer ignore-type */ $value);
Loading history...
Comprehensibility Best Practice introduced by
The variable $prefix does not seem to be defined for all execution paths leading up to this point.
Loading history...
243
                        continue;
244
                    }
245
                    if ('.' === $ind && 'tag' === $prefix) {
246
                        $this->setTag(stream_get_contents($word, -1, -1));
247
                        continue;
248
                    }
249
                    rewind($word);
250
                    $this->unrecognizedWords[] = $word;
251
                }
252
            }
253
            $com->getNextWord();
254
        }
255
    }
256
257
    /**
258
     * Sets the response type.
259
     *
260
     * Sets the response type. Valid values are the TYPE_* constants.
261
     *
262
     * @param string $type The new response type.
263
     *
264
     * @return $this The response object.
265
     *
266
     * @see getType()
267
     */
268
    protected function setType($type)
269
    {
270
        switch ($type) {
271
        case self::TYPE_FINAL:
272
        case self::TYPE_DATA:
273
        case self::TYPE_ERROR:
274
        case self::TYPE_FATAL:
275
            $this->_type = $type;
276
            return $this;
277
        default:
278
            throw new UnexpectedValueException(
279
                'Unrecognized response type.',
280
                UnexpectedValueException::CODE_RESPONSE_TYPE_UNKNOWN,
281
                null,
282
                $type
283
            );
284
        }
285
    }
286
287
    /**
288
     * Gets the response type.
289
     *
290
     * @return string The response type.
291
     *
292
     * @see setType()
293
     */
294
    public function getType()
295
    {
296
        return $this->_type;
297
    }
298
299
    /**
300
     * Gets the value of an argument.
301
     *
302
     * @param string $name The name of the argument.
303
     *
304
     * @return string|resource|null The value of the specified argument.
305
     *     Returns NULL if such an argument is not set.
306
     *
307
     * @deprecated         1.0.0b5 Use {@link static::getProperty()} instead.
308
     *     This method will be removed upon final release, and is currently
309
     *     left standing merely because it can't be easily search&replaced in
310
     *     existing code, due to the fact the name "getArgument()" is shared
311
     *     with {@link Request::getArgument()}, which is still valid.
312
     * @codeCoverageIgnore
313
     */
314
    public function getArgument($name)
315
    {
316
        trigger_error(
317
            'Response::getArgument() is deprecated in favor of ' .
318
            'Response::getProperty() (but note that Request::getArgument() ' .
319
            'is still valid)',
320
            E_USER_DEPRECATED
321
        );
322
        return $this->getAttribute($name);
323
    }
324
325
    /**
326
     * Gets the value of a property.
327
     *
328
     * @param string $name The name of the property.
329
     *
330
     * @return string|resource|null The value of the specified property.
331
     *     Returns NULL if such a property is not set.
332
     */
333
    public function getProperty($name)
334
    {
335
        return parent::getAttribute($name);
336
    }
337
338
    /**
339
     * Gets a list of unrecognized words.
340
     *
341
     * @return string[] The list of unrecognized words.
342
     */
343
    public function getUnrecognizedWords()
344
    {
345
        return $this->unrecognizedWords;
346
    }
347
348
    /**
349
     * Get actionable debug info.
350
     *
351
     * This is a magic method available to PHP 5.6 and above, due to which
352
     * output of var_dump() will be more actionable.
353
     *
354
     * You can still call it in earlier versions to get the object as a plain array.
355
     *
356
     * @return array The info, as an associative array.
357
     */
358
    public function __debugInfo()
359
    {
360
        return parent::__debugInfo() + array(
361
            'unrecognized' => $this->unrecognizedWords
362
        );
363
    }
364
}
365