Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Channels 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 Channels, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
37 | class Channels extends MediaBase |
||
38 | { |
||
39 | const AST_STATE_DOWN = 'Down'; // Channel is down and available |
||
40 | const AST_STATE_RESERVED = 'Rsrvd'; // Channel is down, but reserved |
||
41 | const AST_STATE_OFFHOOK = 'OffHook'; // Channel is off hook |
||
42 | const AST_STATE_DIALING = 'Dialing'; // Digits (or equivalent) have been dialed |
||
43 | const AST_STATE_RING = 'Ring'; // Line is ringing |
||
44 | const AST_STATE_RINGING = 'Ringing'; // Remote end is ringing |
||
45 | const AST_STATE_UP = 'Up'; // Line is up |
||
46 | const AST_STATE_BUSY = 'Busy'; // Line is busy |
||
47 | const AST_STATE_DIALING_OFFHOOK = 'Dialing Offhook'; // Digits (or equivalent) have been dialed while offhook |
||
48 | const AST_STATE_PRERING = 'Pre-ring'; // Channel has detected an incoming call and is waiting for ring |
||
49 | const AST_STATE_MUTE = 'Mute'; // Do not transmit voice data |
||
50 | const AST_STATE_UNKNOWN = 'Unknown'; |
||
51 | |||
52 | /** |
||
53 | * List all active channels in Asterisk. |
||
54 | * |
||
55 | * @return Channel[] |
||
56 | */ |
||
57 | public function getChannels() |
||
69 | |||
70 | /** |
||
71 | * Create a new channel (originate). The new channel is created immediately and a snapshot of it |
||
72 | * returned. If a Stasis application is provided it will be automatically subscribed to the originated |
||
73 | * channel for further events and updates. |
||
74 | * |
||
75 | * @param string $endpoint (required) Endpoint to call. |
||
76 | * @param string $extension The extension to dial after the endpoint answers |
||
77 | * @param string $context The context to dial after the endpoint answers. If omitted, uses 'default' |
||
78 | * @param int $priority The priority to dial after the endpoint answers. If omitted, uses 1 |
||
79 | * @param string $label Asterisk 13+ The label to dial after the endpoint answers. Will supersede 'priority' if provided. Mutually exclusive with 'app'. |
||
80 | * @param string $app The application that is subscribed to the originated channel. When the channel is answered, it will be passed to this Stasis application. Mutually exclusive with 'context', 'extension', 'priority', and 'label'. |
||
81 | * @param string $appArgs The application arguments to pass to the Stasis application. |
||
82 | * @param string $callerId CallerID to use when dialing the endpoint or extension. |
||
83 | * @param int $timeout (default 30) Timeout (in seconds) before giving up dialing, or -1 for no timeout. |
||
84 | * @param string $channelId The unique id to assign the channel on creation. |
||
85 | * @param string $otherChannelId The unique id to assign the second channel when using local channels. |
||
86 | * @param array $variables The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice" } } |
||
87 | * @return Channel |
||
88 | * @throws InvalidParameterException |
||
89 | * @throws ServerException |
||
90 | */ |
||
91 | 38 | public function createChannel( |
|
92 | $endpoint, |
||
93 | $extension = null, |
||
94 | $context = null, |
||
95 | $priority = null, |
||
96 | $label = null, |
||
97 | $app = null, |
||
98 | $appArgs = null, |
||
99 | $callerId = null, |
||
100 | $timeout = null, |
||
101 | $channelId = null, |
||
102 | $otherChannelId = null, |
||
103 | $variables = array() |
||
104 | ) { |
||
105 | 38 | $uri = '/channels'; |
|
106 | try { |
||
107 | 38 | $response = $this->client->getEndpoint()->post($uri, array( |
|
108 | 38 | 'endpoint' => $endpoint, |
|
109 | 38 | 'extension' => $extension, |
|
110 | 38 | 'context' => $context, |
|
111 | 38 | 'priority' => $priority, |
|
112 | 38 | 'label' => $label, |
|
113 | 38 | 'app' => $app, |
|
114 | 38 | 'appArgs' => $appArgs, |
|
115 | 38 | 'callerId' => $callerId, |
|
116 | 38 | 'timeout' => $timeout, |
|
117 | 38 | 'channelId' => $channelId, |
|
118 | 38 | 'otherChannelId' => $otherChannelId, |
|
119 | 38 | 'variables' => array_map('strval', $variables), |
|
120 | 38 | )); |
|
121 | 38 | } catch (Pest_BadRequest $e) { // Invalid parameters for originating a channel. |
|
122 | throw new InvalidParameterException($e); |
||
123 | 1 | } catch (Pest_ServerError $e) { |
|
124 | 1 | throw new ServerException($e); // Couldn't create the channel. |
|
125 | } |
||
126 | |||
127 | 37 | return new Channel($this->client, $response); |
|
128 | } |
||
129 | |||
130 | /** |
||
131 | * Channel details. |
||
132 | * |
||
133 | * @param string $channelId |
||
134 | * @return Channel |
||
135 | * @throws NotFoundException |
||
136 | */ |
||
137 | public function getChannel($channelId) |
||
148 | |||
149 | /** |
||
150 | * Create a new channel (originate). The new channel is created immediately and a snapshot of it |
||
151 | * returned. If a Stasis application is provided it will be automatically subscribed to the originated |
||
152 | * channel for further events and updates. |
||
153 | * |
||
154 | * @param string $endpoint (required) Endpoint to call. |
||
155 | * @param string $extension The extension to dial after the endpoint answers |
||
156 | * @param string $context The context to dial after the endpoint answers. If omitted, uses 'default' |
||
157 | * @param int $priority The priority to dial after the endpoint answers. If omitted, uses 1 |
||
158 | * @param string $label Asterisk 13+ The label to dial after the endpoint answers. Will supersede 'priority' if provided. Mutually exclusive with 'app'. |
||
159 | * @param string $app The application that is subscribed to the originated channel, and passed to the Stasis application. |
||
160 | * @param string $appArgs The application arguments to pass to the Stasis application. |
||
161 | * @param string $callerId CallerID to use when dialing the endpoint or extension. |
||
162 | * @param int $timeout (default 30) Timeout (in seconds) before giving up dialing, or -1 for no timeout. |
||
163 | * @param string $channelId The unique id to assign the channel on creation. |
||
164 | * @param string $otherChannelId The unique id to assign the second channel when using local channels. |
||
165 | * @param array $variables The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice" } } |
||
166 | * @return Channel |
||
167 | * @throws InvalidParameterException |
||
168 | */ |
||
169 | public function createChannelWithId( |
||
204 | |||
205 | /** |
||
206 | * Delete (i.e. hangup) a channel. |
||
207 | * |
||
208 | * @param string $channelId Channel's id |
||
209 | * @throws NotFoundException |
||
210 | */ |
||
211 | 1 | View Code Duplication | public function deleteChannel($channelId) |
220 | |||
221 | /** |
||
222 | * Hangup a channel if it still exists. |
||
223 | * |
||
224 | * @param string $channelId Channel's id |
||
225 | */ |
||
226 | 1 | public function hangup($channelId) |
|
234 | |||
235 | /** |
||
236 | * Exit application; continue execution in the dialplan. |
||
237 | * |
||
238 | * @param string $channelId Channel's id |
||
239 | * @param string $context The context to continue to. |
||
240 | * @param string $extension The extension to continue to. |
||
241 | * @param int $priority The priority to continue to. |
||
242 | * @throws NotFoundException |
||
243 | * @throws ConflictException |
||
244 | */ |
||
245 | public function continueDialplan($channelId, $context, $extension, $priority) |
||
260 | |||
261 | /** |
||
262 | * Answer a channel. |
||
263 | * |
||
264 | * @param string $channelId Channel's id |
||
265 | * @throws NotFoundException |
||
266 | * @throws ConflictException |
||
267 | */ |
||
268 | 36 | View Code Duplication | public function answer($channelId) |
279 | |||
280 | /** |
||
281 | * Indicate ringing to a channel. |
||
282 | * |
||
283 | * @param string $channelId |
||
284 | * @throws NotFoundException |
||
285 | * @throws ConflictException |
||
286 | */ |
||
287 | View Code Duplication | public function startRinging($channelId) |
|
298 | |||
299 | /** |
||
300 | * Stop ringing indication on a channel if locally generated. |
||
301 | * |
||
302 | * @param string $channelId |
||
303 | * @throws NotFoundException |
||
304 | * @throws ConflictException |
||
305 | */ |
||
306 | View Code Duplication | public function stopRinging($channelId) |
|
317 | |||
318 | /** |
||
319 | * Send provided DTMF to a given channel. |
||
320 | * |
||
321 | * @param string $channelId |
||
322 | * @param string $dtmf DTMF To send. |
||
323 | * @param int $before Amount of time to wait before DTMF digits (specified in milliseconds) start. |
||
324 | * @param int $between Amount of time in between DTMF digits (specified in milliseconds). Default: 100 |
||
325 | * @param int $duration Length of each DTMF digit (specified in milliseconds). Default: 100 |
||
326 | * @param int $after Amount of time to wait after DTMF digits (specified in milliseconds) end. |
||
327 | * @throws InvalidParameterException |
||
328 | * @throws NotFoundException |
||
329 | * @throws ConflictException |
||
330 | */ |
||
331 | public function sendDtmf($channelId, $dtmf, $before = null, $between = null, $duration = null, $after = null) |
||
350 | |||
351 | /** |
||
352 | * Mute a channel. |
||
353 | * |
||
354 | * @param string $channelId Channel's id |
||
355 | * @param string $direction (default both) Direction in which to mute audio. Allowed values: both, in, out |
||
356 | * @throws NotFoundException |
||
357 | * @throws ConflictException |
||
358 | */ |
||
359 | public function mute($channelId, $direction) |
||
372 | |||
373 | /** |
||
374 | * Unmute a channel. |
||
375 | * |
||
376 | * @param string $channelId Channel's id |
||
377 | * @param string $direction (default both) Direction in which to unmute audio |
||
378 | * @throws NotFoundException |
||
379 | * @throws ConflictException |
||
380 | */ |
||
381 | View Code Duplication | public function unmute($channelId, $direction) |
|
392 | |||
393 | /** |
||
394 | * Hold a channel. |
||
395 | * |
||
396 | * @param string $channelId Channel's id |
||
397 | * @throws NotFoundException |
||
398 | * @throws ConflictException |
||
399 | */ |
||
400 | View Code Duplication | public function hold($channelId) |
|
411 | |||
412 | /** |
||
413 | * Remove a channel from hold. |
||
414 | * |
||
415 | * @param string $channelId Channel's id |
||
416 | * @throws NotFoundException |
||
417 | * @throws ConflictException |
||
418 | */ |
||
419 | View Code Duplication | public function unhold($channelId) |
|
430 | |||
431 | /** |
||
432 | * Play silence to a channel. Using media operations such as /play on a channel playing silence in this manner will suspend silence without resuming automatically. |
||
433 | * |
||
434 | * @param string $channelId Channel's id |
||
435 | * @throws NotFoundException |
||
436 | * @throws ConflictException |
||
437 | */ |
||
438 | View Code Duplication | public function startSilence($channelId) |
|
449 | |||
450 | /** |
||
451 | * Stop playing silence to a channel. |
||
452 | * |
||
453 | * @param string $channelId Channel's id |
||
454 | * @throws NotFoundException |
||
455 | * @throws ConflictException |
||
456 | */ |
||
457 | View Code Duplication | public function stopSilence($channelId) |
|
468 | |||
469 | /** |
||
470 | * Get the value of a channel variable or function. |
||
471 | * |
||
472 | * @param string $channelId |
||
473 | * @param string $variable |
||
474 | * @param null|string $default The value to return if the variable does not exist |
||
475 | * @return string|Variable |
||
476 | * @throws ConflictException |
||
477 | * @throws InvalidParameterException |
||
478 | * @throws NotFoundException |
||
479 | */ |
||
480 | public function getVariable($channelId, $variable, $default = null) |
||
500 | |||
501 | /** |
||
502 | * Set the value of a channel variable or function. |
||
503 | * |
||
504 | * @param string $channelId |
||
505 | * @param string $variable |
||
506 | * @param string $value |
||
507 | * @return Variable |
||
508 | * @throws InvalidParameterException |
||
509 | * @throws NotFoundException |
||
510 | * @throws ConflictException |
||
511 | */ |
||
512 | public function setVariable($channelId, $variable, $value) |
||
530 | |||
531 | /** |
||
532 | * Start snooping. Snoop (spy/whisper) on a specific channel. |
||
533 | * |
||
534 | * @param string $channelId Channel's id |
||
535 | * @param string $spy (default none) Direction of audio to spy on |
||
536 | * @param string $whisper (default none) Direction of audio to whisper into |
||
537 | * @param string $app (required) Application the snooping channel is placed into |
||
538 | * @param string $appArgs The application arguments to pass to the Stasis application |
||
539 | * @param string $snoopId Unique ID to assign to snooping channel |
||
540 | * @return Channel |
||
541 | * @throws InvalidParameterException |
||
542 | * @throws NotFoundException |
||
543 | */ |
||
544 | View Code Duplication | public function startSnoop($channelId, $spy, $whisper, $app, $appArgs, $snoopId) |
|
563 | |||
564 | /** |
||
565 | * Start snooping. Snoop (spy/whisper) on a specific channel. |
||
566 | * |
||
567 | * @param string $channelId Channel's id |
||
568 | * @param string $spy (default none) Direction of audio to spy on |
||
569 | * @param string $whisper (default none) Direction of audio to whisper into |
||
570 | * @param string $app (required) Application the snooping channel is placed into |
||
571 | * @param string $appArgs The application arguments to pass to the Stasis application |
||
572 | * @param string $snoopId Unique ID to assign to snooping channel |
||
573 | * @return Channel |
||
574 | * @throws InvalidParameterException |
||
575 | * @throws NotFoundException |
||
576 | */ |
||
577 | View Code Duplication | public function startSnoopWithId($channelId, $spy, $whisper, $app, $appArgs, $snoopId) |
|
595 | |||
596 | /** |
||
597 | * @return string |
||
598 | */ |
||
599 | 6 | public function getType() |
|
603 | } |
||
604 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.