| Total Complexity | 41 |
| Total Lines | 320 |
| Duplicated Lines | 0 % |
| Changes | 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 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) |
||
|
|
|||
| 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); |
||
| 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, |
||
| 237 | 0xFFFFF, |
||
| 238 | $bytesCopied |
||
| 239 | ); |
||
| 240 | } |
||
| 241 | rewind($value); |
||
| 242 | $this->setAttribute($prefix, $value); |
||
| 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() |
||
| 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() |
||
| 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() |
||
| 365 |