Complex classes like Connection 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 Connection, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 26 | class Connection implements ConnectionInterface | ||
| 27 | { | ||
| 28 | /** | ||
| 29 | * @var Factory | ||
| 30 | */ | ||
| 31 | private $factory; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * @var string | ||
| 35 | */ | ||
| 36 | private $host; | ||
| 37 | |||
| 38 | /** | ||
| 39 | * @var int | ||
| 40 | */ | ||
| 41 | private $port; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * @var bool | ||
| 45 | */ | ||
| 46 | private $secure; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * @var Socket | ||
| 50 | */ | ||
| 51 | private $socket; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * @var int | ||
| 55 | */ | ||
| 56 | private $timeout; | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Constructor. | ||
| 60 | * | ||
| 61 | * @param string $host The hostname of the NNTP server. | ||
| 62 | * @param int $port The port of the NNTP server. | ||
| 63 | * @param bool $secure A bool indicating if a secure connection should be established. | ||
| 64 | * @param int $timeout The socket timeout in seconds. | ||
| 65 | * @param Factory $factory The socket client factory. | ||
| 66 | */ | ||
| 67 | 2 | public function __construct($host, $port, $secure = false, $timeout = 15, Factory $factory = null) | |
| 75 | |||
| 76 | /** | ||
| 77 |      * {@inheritdoc} | ||
| 78 | */ | ||
| 79 | 2 | public function connect() | |
| 99 | |||
| 100 | /** | ||
| 101 |      * {@inheritdoc} | ||
| 102 | */ | ||
| 103 | public function disconnect() | ||
| 111 | |||
| 112 | /** | ||
| 113 |      * {@inheritdoc} | ||
| 114 | */ | ||
| 115 | public function sendCommand(CommandInterface $command) | ||
| 116 |     { | ||
| 117 | $commandString = $command->execute(); | ||
| 118 | |||
| 119 | // NNTP/RFC977 only allows command up to 512 (-2 \r\n) chars. | ||
| 120 |         if (!strlen($commandString) > 510) { | ||
| 121 |             throw new InvalidArgumentException('Failed to write to socket: command exceeded 510 characters'); | ||
| 122 | } | ||
| 123 | |||
| 124 |         if (!$this->socket->selectWrite() || strlen($commandString."\r\n") !== $this->socket->write($commandString."\r\n")) { | ||
| 125 |             throw new RuntimeException('Failed to write to socket'); | ||
| 126 | } | ||
| 127 | |||
| 128 | $response = $this->getResponse(); | ||
| 129 | |||
| 130 |         if ($command->isMultiLine() && ($response->getStatusCode() >= 200 && $response->getStatusCode() <= 399)) { | ||
| 131 | $response = $command->isCompressed() ? $this->getCompressedResponse($response) : $this->getMultiLineResponse($response); | ||
| 132 | } | ||
| 133 | |||
| 134 |         if (in_array($response->getStatusCode(), [Response::COMMAND_UNKNOWN, Response::COMMAND_UNAVAILABLE])) { | ||
| 135 |             throw new RuntimeException('Sent command is either unknown or unavailable on server'); | ||
| 136 | } | ||
| 137 | |||
| 138 | $expectedResponseCodes = $command->getExpectedResponseCodes(); | ||
| 139 | |||
| 140 | // Check if we received a response expected by the command. | ||
| 141 |         if (!isset($expectedResponseCodes[$response->getStatusCode()])) { | ||
| 142 | throw new RuntimeException(sprintf( | ||
| 143 | 'Unexpected response received: [%d] %s', | ||
| 144 | $response->getStatusCode(), | ||
| 145 | $response->getMessage() | ||
| 146 | )); | ||
| 147 | } | ||
| 148 | |||
| 149 | $expectedResponseHandler = $expectedResponseCodes[$response->getStatusCode()]; | ||
| 150 |         if (!is_callable([$command, $expectedResponseHandler])) { | ||
| 151 |             throw new RuntimeException(sprintf('Response handler (%s) is not callable method on given command object', $expectedResponseHandler)); | ||
| 152 | } | ||
| 153 | |||
| 154 | $command->setResponse($response); | ||
| 155 | $command->$expectedResponseHandler($response); | ||
| 156 | |||
| 157 | return $command; | ||
| 158 | } | ||
| 159 | |||
| 160 | public function sendArticle(CommandInterface $command) | ||
| 191 | |||
| 192 | 1 | protected function getResponse() | |
| 211 | |||
| 212 | public function getMultiLineResponse(Response $response) | ||
| 239 | |||
| 240 | public function getCompressedResponse(Response $response) | ||
| 276 | |||
| 277 | /** | ||
| 278 | * @param string $address | ||
| 279 | */ | ||
| 280 | 2 | protected function getSocketUrl($address) | |
| 289 | } | ||
| 290 |