| Total Complexity | 42 |
| Total Lines | 327 |
| Duplicated Lines | 0 % |
| Changes | 4 | ||
| Bugs | 1 | Features | 0 |
Complex classes like Response often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Response, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 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 with empty data (from RouterOS 7.18). |
||
| 57 | */ |
||
| 58 | const TYPE_EMPTY = '!empty'; |
||
| 59 | |||
| 60 | /** |
||
| 61 | * A response signifying error. |
||
| 62 | */ |
||
| 63 | const TYPE_ERROR = '!trap'; |
||
| 64 | |||
| 65 | /** |
||
| 66 | * A response signifying a fatal error, due to which the connection would be |
||
| 67 | * terminated. |
||
| 68 | */ |
||
| 69 | const TYPE_FATAL = '!fatal'; |
||
| 70 | |||
| 71 | /** |
||
| 72 | * An array of unrecognized words in network order. |
||
| 73 | * |
||
| 74 | * @var (string|resource)[] |
||
| 75 | */ |
||
| 76 | protected $unrecognizedWords = array(); |
||
| 77 | |||
| 78 | /** |
||
| 79 | * The response type. |
||
| 80 | * |
||
| 81 | * @var string |
||
| 82 | */ |
||
| 83 | private $_type; |
||
| 84 | |||
| 85 | /** |
||
| 86 | * Extracts a new response from a communicator. |
||
| 87 | * |
||
| 88 | * @param Communicator $com The communicator from which to extract |
||
| 89 | * the new response. |
||
| 90 | * @param int|null $streamOn Threshold after which to stream |
||
| 91 | * a word. NULL to disable streaming altogether. |
||
| 92 | * @param int $sTimeout If a response is not immediately |
||
| 93 | * available, wait this many seconds. If NULL, wait indefinitely. |
||
| 94 | * @param int|null $usTimeout Microseconds to add to the waiting time. |
||
| 95 | * @param Registry|null $reg An optional registry to sync the |
||
| 96 | * response with. |
||
| 97 | * |
||
| 98 | * @see getType() |
||
| 99 | * @see getArgument() |
||
| 100 | */ |
||
| 101 | public function __construct( |
||
| 102 | Communicator $com, |
||
| 103 | $streamOn = null, |
||
| 104 | $sTimeout = 0, |
||
| 105 | $usTimeout = null, |
||
| 106 | Registry $reg = null |
||
| 107 | ) { |
||
| 108 | if (null === $reg) { |
||
| 109 | if ($com->getTransmitter()->isPersistent()) { |
||
| 110 | $old = $com->getTransmitter() |
||
| 111 | ->lock(T\Stream::DIRECTION_RECEIVE); |
||
| 112 | try { |
||
| 113 | $this->_receive($com, $streamOn, $sTimeout, $usTimeout); |
||
| 114 | } catch (E $e) { |
||
| 115 | $com->getTransmitter()->lock($old, true); |
||
| 116 | throw $e; |
||
| 117 | } |
||
| 118 | $com->getTransmitter()->lock($old, true); |
||
| 119 | } else { |
||
| 120 | $this->_receive($com, $streamOn, $sTimeout, $usTimeout); |
||
| 121 | } |
||
| 122 | } else { |
||
| 123 | while (null === ($response = $reg->getNextResponse())) { |
||
| 124 | $newResponse = new self($com, 0, $sTimeout, $usTimeout); |
||
| 125 | $tagInfo = $reg::parseTag($newResponse->getTag()); |
||
| 126 | $newResponse->setTag($tagInfo[1]); |
||
| 127 | if (!$reg->add($newResponse, $tagInfo[0])) { |
||
| 128 | $response = $newResponse; |
||
| 129 | break; |
||
| 130 | } |
||
| 131 | } |
||
| 132 | |||
| 133 | $this->_type = $response->_type; |
||
| 134 | $this->setTag($response->getTag()); |
||
| 135 | $this->attributes = $response->attributes; |
||
| 136 | $this->unrecognizedWords = $response->unrecognizedWords; |
||
| 137 | if (null === $streamOn) { |
||
| 138 | foreach ($response->attributes as $name => $value) { |
||
| 139 | $this->setAttribute( |
||
| 140 | $name, |
||
| 141 | stream_get_contents($value) |
||
|
|
|||
| 142 | ); |
||
| 143 | } |
||
| 144 | foreach ($response->unrecognizedWords as $i => $value) { |
||
| 145 | $this->unrecognizedWords[$i] = stream_get_contents($value); |
||
| 146 | } |
||
| 147 | } elseif (0 !== $streamOn) { |
||
| 148 | foreach ($response->attributes as $name => $value) { |
||
| 149 | $valueLength = $com::seekableStreamLength($value); |
||
| 150 | if ((strlen($name) + 2 + $valueLength) < $streamOn) { |
||
| 151 | $this->setAttribute( |
||
| 152 | $name, |
||
| 153 | stream_get_contents($value) |
||
| 154 | ); |
||
| 155 | } |
||
| 156 | } |
||
| 157 | foreach ($response->unrecognizedWords as $i => $value) { |
||
| 158 | $valueLength = $com::seekableStreamLength($value); |
||
| 159 | if ($valueLength < $streamOn) { |
||
| 160 | $this->unrecognizedWords[$i] = stream_get_contents( |
||
| 161 | $value |
||
| 162 | ); |
||
| 163 | } |
||
| 164 | } |
||
| 165 | } |
||
| 166 | } |
||
| 167 | } |
||
| 168 | |||
| 169 | /** |
||
| 170 | * Extracts a new response from a communicator. |
||
| 171 | * |
||
| 172 | * This is the function that performs the actual receiving, while the |
||
| 173 | * constructor is also involved in locks and registry sync. |
||
| 174 | * |
||
| 175 | * @param Communicator $com The communicator from which to extract |
||
| 176 | * the new response. |
||
| 177 | * @param int|null $streamOn Threshold after which to stream |
||
| 178 | * a word. NULL to disable streaming altogether. |
||
| 179 | * @param int $sTimeout If a response is not immediately |
||
| 180 | * available, wait this many seconds. If NULL, wait indefinitely. |
||
| 181 | * Note that if an empty sentence is received, the timeout will be |
||
| 182 | * reset for another sentence receiving. |
||
| 183 | * @param int|null $usTimeout Microseconds to add to the waiting time. |
||
| 184 | * |
||
| 185 | * @return void |
||
| 186 | */ |
||
| 187 | private function _receive( |
||
| 188 | Communicator $com, |
||
| 189 | $streamOn = null, |
||
| 190 | $sTimeout = 0, |
||
| 191 | $usTimeout = null |
||
| 192 | ) { |
||
| 193 | do { |
||
| 194 | if (!$com->getTransmitter()->isDataAwaiting( |
||
| 195 | $sTimeout, |
||
| 196 | $usTimeout |
||
| 197 | ) |
||
| 198 | ) { |
||
| 199 | throw new SocketException( |
||
| 200 | 'No data within the time limit', |
||
| 201 | SocketException::CODE_NO_DATA |
||
| 202 | ); |
||
| 203 | } |
||
| 204 | $type = $com->getNextWord(); |
||
| 205 | } while ('' === $type); |
||
| 206 | $this->setType($type); |
||
| 207 | if (null === $streamOn) { |
||
| 208 | for ($word = $com->getNextWord(); '' !== $word; $word = $com-> |
||
| 209 | getNextWord()) { |
||
| 210 | if (preg_match('/^=([^=]+)=(.*)$/sS', $word, $matches)) { |
||
| 211 | $this->setAttribute($matches[1], $matches[2]); |
||
| 212 | } elseif (preg_match('/^\.tag=(.*)$/sS', $word, $matches)) { |
||
| 213 | $this->setTag($matches[1]); |
||
| 214 | } else { |
||
| 215 | $this->unrecognizedWords[] = $word; |
||
| 216 | } |
||
| 217 | } |
||
| 218 | } else { |
||
| 219 | while ($com->getNextWordLength() !== 0) { |
||
| 220 | if ($com->getNextWordLength() < $streamOn) { |
||
| 221 | $word = $com->getNextWord(); |
||
| 222 | if (preg_match('/^=([^=]+)=(.*)$/sS', $word, $matches)) { |
||
| 223 | $this->setAttribute($matches[1], $matches[2]); |
||
| 224 | } elseif (preg_match('/^\.tag=(.*)$/sS', $word, $matches)) { |
||
| 225 | $this->setTag($matches[1]); |
||
| 226 | } else { |
||
| 227 | $this->unrecognizedWords[] = $word; |
||
| 228 | } |
||
| 229 | } else { |
||
| 230 | $word = $com->getNextWordAsStream(); |
||
| 231 | $ind = fread($word, 1); |
||
| 232 | if ('=' === $ind || '.' === $ind) { |
||
| 233 | $prefix = stream_get_line($word, 0, '='); |
||
| 234 | } |
||
| 235 | if ('=' === $ind) { |
||
| 236 | $value = fopen('php://temp', 'r+b'); |
||
| 237 | $bytesCopied = ftell($word); |
||
| 238 | while (!feof($word)) { |
||
| 239 | $bytesCopied += stream_copy_to_stream( |
||
| 240 | $word, |
||
| 241 | $value, |
||
| 242 | 0xFFFFF, |
||
| 243 | $bytesCopied |
||
| 244 | ); |
||
| 245 | } |
||
| 246 | rewind($value); |
||
| 247 | $this->setAttribute($prefix, $value); |
||
| 248 | continue; |
||
| 249 | } |
||
| 250 | if ('.' === $ind && 'tag' === $prefix) { |
||
| 251 | $this->setTag(stream_get_contents($word, -1, -1)); |
||
| 252 | continue; |
||
| 253 | } |
||
| 254 | rewind($word); |
||
| 255 | $this->unrecognizedWords[] = $word; |
||
| 256 | } |
||
| 257 | } |
||
| 258 | $com->getNextWord(); |
||
| 259 | } |
||
| 260 | } |
||
| 261 | |||
| 262 | /** |
||
| 263 | * Sets the response type. |
||
| 264 | * |
||
| 265 | * Sets the response type. Valid values are the TYPE_* constants. |
||
| 266 | * |
||
| 267 | * @param string $type The new response type. |
||
| 268 | * |
||
| 269 | * @return $this The response object. |
||
| 270 | * |
||
| 271 | * @see getType() |
||
| 272 | */ |
||
| 273 | protected function setType($type) |
||
| 274 | { |
||
| 275 | switch ($type) { |
||
| 276 | case self::TYPE_FINAL: |
||
| 277 | case self::TYPE_DATA: |
||
| 278 | case self::TYPE_EMPTY: |
||
| 279 | case self::TYPE_ERROR: |
||
| 280 | case self::TYPE_FATAL: |
||
| 281 | $this->_type = $type; |
||
| 282 | return $this; |
||
| 283 | default: |
||
| 284 | throw new UnexpectedValueException( |
||
| 285 | 'Unrecognized response type.', |
||
| 286 | UnexpectedValueException::CODE_RESPONSE_TYPE_UNKNOWN, |
||
| 287 | null, |
||
| 288 | $type |
||
| 289 | ); |
||
| 290 | } |
||
| 291 | } |
||
| 292 | |||
| 293 | /** |
||
| 294 | * Gets the response type. |
||
| 295 | * |
||
| 296 | * @return string The response type. |
||
| 297 | * |
||
| 298 | * @see setType() |
||
| 299 | */ |
||
| 300 | public function getType() |
||
| 301 | { |
||
| 302 | return $this->_type; |
||
| 303 | } |
||
| 304 | |||
| 305 | /** |
||
| 306 | * Gets the value of an argument. |
||
| 307 | * |
||
| 308 | * @param string $name The name of the argument. |
||
| 309 | * |
||
| 310 | * @return string|resource|null The value of the specified argument. |
||
| 311 | * Returns NULL if such an argument is not set. |
||
| 312 | * |
||
| 313 | * @deprecated 1.0.0b5 Use {@link static::getProperty()} instead. |
||
| 314 | * This method will be removed upon final release, and is currently |
||
| 315 | * left standing merely because it can't be easily search&replaced in |
||
| 316 | * existing code, due to the fact the name "getArgument()" is shared |
||
| 317 | * with {@link Request::getArgument()}, which is still valid. |
||
| 318 | * @codeCoverageIgnore |
||
| 319 | */ |
||
| 320 | public function getArgument($name) |
||
| 329 | } |
||
| 330 | |||
| 331 | /** |
||
| 332 | * Gets the value of a property. |
||
| 333 | * |
||
| 334 | * @param string $name The name of the property. |
||
| 335 | * |
||
| 336 | * @return string|resource|null The value of the specified property. |
||
| 337 | * Returns NULL if such a property is not set. |
||
| 338 | */ |
||
| 339 | public function getProperty($name) |
||
| 340 | { |
||
| 341 | return $this->getAttribute($name); |
||
| 342 | } |
||
| 343 | |||
| 344 | /** |
||
| 345 | * Gets a list of unrecognized words. |
||
| 346 | * |
||
| 347 | * @return string[] The list of unrecognized words. |
||
| 348 | */ |
||
| 349 | public function getUnrecognizedWords() |
||
| 350 | { |
||
| 351 | return $this->unrecognizedWords; |
||
| 352 | } |
||
| 353 | |||
| 354 | /** |
||
| 355 | * Get actionable debug info. |
||
| 356 | * |
||
| 357 | * This is a magic method available to PHP 5.6 and above, due to which |
||
| 358 | * output of var_dump() will be more actionable. |
||
| 359 | * |
||
| 360 | * You can still call it in earlier versions to get the object as a |
||
| 361 | * plain array. |
||
| 362 | * |
||
| 363 | * @return array The info, as an associative array. |
||
| 364 | */ |
||
| 365 | public function __debugInfo() |
||
| 369 | ); |
||
| 370 | } |
||
| 371 | } |
||
| 372 |