Completed
Push — develop ( 5ed1fa...10f0a8 )
by Vasil
06:12
created

Response::count()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 1
Metric Value
c 1
b 1
f 1
dl 0
loc 8
rs 9.4285
cc 2
eloc 5
nc 2
nop 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
     * @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);
1 ignored issue
show
Security Bug introduced by
It seems like $old defined by $com->getTransmitter()->...eam::DIRECTION_RECEIVE) on line 101 can also be of type false; however, PEAR2\Net\Transmitter\TcpClient::lock() does only seem to accept integer, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

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 returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
107
                    throw $e;
108
                }
109
                $com->getTransmitter()->lock($old, true);
1 ignored issue
show
Security Bug introduced by
It seems like $old defined by $com->getTransmitter()->...eam::DIRECTION_RECEIVE) on line 101 can also be of type false; however, PEAR2\Net\Transmitter\TcpClient::lock() does only seem to accept integer, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

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 returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
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