Complex classes like HTTPClient 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 HTTPClient, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
100 | class HTTPClient { |
||
101 | //set these if you like |
||
102 | var $agent; // User agent |
||
103 | var $http; // HTTP version defaults to 1.0 |
||
104 | var $timeout; // read timeout (seconds) |
||
105 | var $cookies; |
||
106 | var $referer; |
||
107 | var $max_redirect; |
||
108 | var $max_bodysize; |
||
109 | var $max_bodysize_abort = true; // if set, abort if the response body is bigger than max_bodysize |
||
110 | var $header_regexp; // if set this RE must match against the headers, else abort |
||
111 | var $headers; |
||
112 | var $debug; |
||
113 | var $start = 0.0; // for timings |
||
114 | var $keep_alive = true; // keep alive rocks |
||
115 | |||
116 | // don't set these, read on error |
||
117 | var $error; |
||
118 | var $redirect_count; |
||
119 | |||
120 | // read these after a successful request |
||
121 | var $status; |
||
122 | var $resp_body; |
||
123 | var $resp_headers; |
||
124 | |||
125 | // set these to do basic authentication |
||
126 | var $user; |
||
127 | var $pass; |
||
128 | |||
129 | // set these if you need to use a proxy |
||
130 | var $proxy_host; |
||
131 | var $proxy_port; |
||
132 | var $proxy_user; |
||
133 | var $proxy_pass; |
||
134 | var $proxy_ssl; //boolean set to true if your proxy needs SSL |
||
135 | var $proxy_except; // regexp of URLs to exclude from proxy |
||
136 | |||
137 | // list of kept alive connections |
||
138 | static $connections = array(); |
||
139 | |||
140 | // what we use as boundary on multipart/form-data posts |
||
141 | var $boundary = '---DokuWikiHTTPClient--4523452351'; |
||
142 | |||
143 | /** |
||
144 | * Constructor. |
||
145 | * |
||
146 | * @author Andreas Gohr <[email protected]> |
||
147 | */ |
||
148 | function __construct(){ |
||
149 | $this->agent = 'Mozilla/4.0 (compatible; DokuWiki HTTP Client; '.PHP_OS.')'; |
||
150 | $this->timeout = 15; |
||
151 | $this->cookies = array(); |
||
152 | $this->referer = ''; |
||
153 | $this->max_redirect = 3; |
||
154 | $this->redirect_count = 0; |
||
155 | $this->status = 0; |
||
156 | $this->headers = array(); |
||
157 | $this->http = '1.0'; |
||
158 | $this->debug = false; |
||
159 | $this->max_bodysize = 0; |
||
160 | $this->header_regexp= ''; |
||
161 | if(extension_loaded('zlib')) $this->headers['Accept-encoding'] = 'gzip'; |
||
162 | $this->headers['Accept'] = 'text/xml,application/xml,application/xhtml+xml,'. |
||
163 | 'text/html,text/plain,image/png,image/jpeg,image/gif,*/*'; |
||
164 | $this->headers['Accept-Language'] = 'en-us'; |
||
165 | } |
||
166 | |||
167 | |||
168 | /** |
||
169 | * Simple function to do a GET request |
||
170 | * |
||
171 | * Returns the wanted page or false on an error; |
||
172 | * |
||
173 | * @param string $url The URL to fetch |
||
174 | * @param bool $sloppy304 Return body on 304 not modified |
||
175 | * @return false|string response body, false on error |
||
176 | * |
||
177 | * @author Andreas Gohr <[email protected]> |
||
178 | */ |
||
179 | function get($url,$sloppy304=false){ |
||
180 | if(!$this->sendRequest($url)) return false; |
||
181 | if($this->status == 304 && $sloppy304) return $this->resp_body; |
||
182 | if($this->status < 200 || $this->status > 206) return false; |
||
183 | return $this->resp_body; |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * Simple function to do a GET request with given parameters |
||
188 | * |
||
189 | * Returns the wanted page or false on an error. |
||
190 | * |
||
191 | * This is a convenience wrapper around get(). The given parameters |
||
192 | * will be correctly encoded and added to the given base URL. |
||
193 | * |
||
194 | * @param string $url The URL to fetch |
||
195 | * @param array $data Associative array of parameters |
||
196 | * @param bool $sloppy304 Return body on 304 not modified |
||
197 | * @return false|string response body, false on error |
||
198 | * |
||
199 | * @author Andreas Gohr <[email protected]> |
||
200 | */ |
||
201 | function dget($url,$data,$sloppy304=false){ |
||
202 | if(strpos($url,'?')){ |
||
203 | $url .= '&'; |
||
204 | }else{ |
||
205 | $url .= '?'; |
||
206 | } |
||
207 | $url .= $this->_postEncode($data); |
||
208 | return $this->get($url,$sloppy304); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Simple function to do a POST request |
||
213 | * |
||
214 | * Returns the resulting page or false on an error; |
||
215 | * |
||
216 | * @param string $url The URL to fetch |
||
217 | * @param array $data Associative array of parameters |
||
218 | * @return false|string response body, false on error |
||
219 | * @author Andreas Gohr <[email protected]> |
||
220 | */ |
||
221 | function post($url,$data){ |
||
226 | |||
227 | /** |
||
228 | * Send an HTTP request |
||
229 | * |
||
230 | * This method handles the whole HTTP communication. It respects set proxy settings, |
||
231 | * builds the request headers, follows redirects and parses the response. |
||
232 | * |
||
233 | * Post data should be passed as associative array. When passed as string it will be |
||
234 | * sent as is. You will need to setup your own Content-Type header then. |
||
235 | * |
||
236 | * @param string $url - the complete URL |
||
237 | * @param mixed $data - the post data either as array or raw data |
||
238 | * @param string $method - HTTP Method usually GET or POST. |
||
239 | * @return bool - true on success |
||
240 | * |
||
241 | * @author Andreas Goetz <[email protected]> |
||
242 | * @author Andreas Gohr <[email protected]> |
||
243 | */ |
||
244 | function sendRequest($url,$data='',$method='GET'){ |
||
557 | |||
558 | /** |
||
559 | * Tries to establish a CONNECT tunnel via Proxy |
||
560 | * |
||
561 | * Protocol, Servername and Port will be stripped from the request URL when a successful CONNECT happened |
||
562 | * |
||
563 | * @param resource &$socket |
||
564 | * @param string &$requesturl |
||
565 | * @throws HTTPClientException when a tunnel is needed but could not be established |
||
566 | * @return bool true if a tunnel was established |
||
567 | */ |
||
568 | function _ssltunnel(&$socket, &$requesturl){ |
||
569 | if(!$this->proxy_host) return false; |
||
570 | $requestinfo = parse_url($requesturl); |
||
571 | if($requestinfo['scheme'] != 'https') return false; |
||
572 | if(!$requestinfo['port']) $requestinfo['port'] = 443; |
||
573 | |||
574 | // build request |
||
575 | $request = "CONNECT {$requestinfo['host']}:{$requestinfo['port']} HTTP/1.0".HTTP_NL; |
||
576 | $request .= "Host: {$requestinfo['host']}".HTTP_NL; |
||
577 | if($this->proxy_user) { |
||
578 | $request .= 'Proxy-Authorization: Basic '.base64_encode($this->proxy_user.':'.$this->proxy_pass).HTTP_NL; |
||
579 | } |
||
580 | $request .= HTTP_NL; |
||
581 | |||
582 | $this->_debug('SSL Tunnel CONNECT',$request); |
||
583 | $this->_sendData($socket, $request, 'SSL Tunnel CONNECT'); |
||
584 | |||
585 | // read headers from socket |
||
586 | $r_headers = ''; |
||
587 | do{ |
||
588 | $r_line = $this->_readLine($socket, 'headers'); |
||
589 | $r_headers .= $r_line; |
||
590 | }while($r_line != "\r\n" && $r_line != "\n"); |
||
591 | |||
592 | $this->_debug('SSL Tunnel Response',$r_headers); |
||
593 | if(preg_match('/^HTTP\/1\.[01] 200/i',$r_headers)){ |
||
594 | // set correct peer name for verification (enabled since PHP 5.6) |
||
595 | stream_context_set_option($socket, 'ssl', 'peer_name', $requestinfo['host']); |
||
596 | |||
597 | // because SSLv3 is mostly broken, we try TLS connections here first. |
||
598 | // according to https://github.com/splitbrain/dokuwiki/commit/c05ef534 we had problems with certain |
||
599 | // setups with this solution before, but we have no usable test for that and TLS should be the more |
||
600 | // common crypto by now |
||
601 | if (@stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { |
||
602 | $requesturl = $requestinfo['path']. |
||
603 | (!empty($requestinfo['query'])?'?'.$requestinfo['query']:''); |
||
604 | return true; |
||
605 | } |
||
606 | |||
607 | // if the above failed, this will most probably not work either, but we can try |
||
608 | if (@stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_SSLv3_CLIENT)) { |
||
609 | $requesturl = $requestinfo['path']. |
||
610 | (!empty($requestinfo['query'])?'?'.$requestinfo['query']:''); |
||
611 | return true; |
||
612 | } |
||
613 | |||
614 | throw new HTTPClientException('Failed to set up crypto for secure connection to '.$requestinfo['host'], -151); |
||
615 | } |
||
616 | |||
617 | throw new HTTPClientException('Failed to establish secure proxy connection', -150); |
||
618 | } |
||
619 | |||
620 | /** |
||
621 | * Safely write data to a socket |
||
622 | * |
||
623 | * @param resource $socket An open socket handle |
||
624 | * @param string $data The data to write |
||
625 | * @param string $message Description of what is being read |
||
626 | * @throws HTTPClientException |
||
627 | * |
||
628 | * @author Tom N Harris <[email protected]> |
||
629 | */ |
||
630 | function _sendData($socket, $data, $message) { |
||
659 | |||
660 | /** |
||
661 | * Safely read data from a socket |
||
662 | * |
||
663 | * Reads up to a given number of bytes or throws an exception if the |
||
664 | * response times out or ends prematurely. |
||
665 | * |
||
666 | * @param resource $socket An open socket handle in non-blocking mode |
||
667 | * @param int $nbytes Number of bytes to read |
||
668 | * @param string $message Description of what is being read |
||
669 | * @param bool $ignore_eof End-of-file is not an error if this is set |
||
670 | * @throws HTTPClientException |
||
671 | * @return string |
||
672 | * |
||
673 | * @author Tom N Harris <[email protected]> |
||
674 | */ |
||
675 | function _readData($socket, $nbytes, $message, $ignore_eof = false) { |
||
712 | |||
713 | /** |
||
714 | * Safely read a \n-terminated line from a socket |
||
715 | * |
||
716 | * Always returns a complete line, including the terminating \n. |
||
717 | * |
||
718 | * @param resource $socket An open socket handle in non-blocking mode |
||
719 | * @param string $message Description of what is being read |
||
720 | * @throws HTTPClientException |
||
721 | * @return string |
||
722 | * |
||
723 | * @author Tom N Harris <[email protected]> |
||
724 | */ |
||
725 | function _readLine($socket, $message) { |
||
750 | |||
751 | /** |
||
752 | * print debug info |
||
753 | * |
||
754 | * Uses _debug_text or _debug_html depending on the SAPI name |
||
755 | * |
||
756 | * @author Andreas Gohr <[email protected]> |
||
757 | * |
||
758 | * @param string $info |
||
759 | * @param mixed $var |
||
760 | */ |
||
761 | function _debug($info,$var=null){ |
||
769 | |||
770 | /** |
||
771 | * print debug info as HTML |
||
772 | * |
||
773 | * @param string $info |
||
774 | * @param mixed $var |
||
775 | */ |
||
776 | function _debug_html($info, $var=null){ |
||
786 | |||
787 | /** |
||
788 | * prints debug info as plain text |
||
789 | * |
||
790 | * @param string $info |
||
791 | * @param mixed $var |
||
792 | */ |
||
793 | function _debug_text($info, $var=null){ |
||
798 | |||
799 | /** |
||
800 | * Return current timestamp in microsecond resolution |
||
801 | * |
||
802 | * @return float |
||
803 | */ |
||
804 | static function _time(){ |
||
808 | |||
809 | /** |
||
810 | * convert given header string to Header array |
||
811 | * |
||
812 | * All Keys are lowercased. |
||
813 | * |
||
814 | * @author Andreas Gohr <[email protected]> |
||
815 | * |
||
816 | * @param string $string |
||
817 | * @return array |
||
818 | */ |
||
819 | function _parseHeaders($string){ |
||
841 | |||
842 | /** |
||
843 | * convert given header array to header string |
||
844 | * |
||
845 | * @author Andreas Gohr <[email protected]> |
||
846 | * |
||
847 | * @param array $headers |
||
848 | * @return string |
||
849 | */ |
||
850 | function _buildHeaders($headers){ |
||
858 | |||
859 | /** |
||
860 | * get cookies as http header string |
||
861 | * |
||
862 | * @author Andreas Goetz <[email protected]> |
||
863 | * |
||
864 | * @return string |
||
865 | */ |
||
866 | function _getCookies(){ |
||
875 | |||
876 | /** |
||
877 | * Encode data for posting |
||
878 | * |
||
879 | * @author Andreas Gohr <[email protected]> |
||
880 | * |
||
881 | * @param array $data |
||
882 | * @return string |
||
883 | */ |
||
884 | function _postEncode($data){ |
||
887 | |||
888 | /** |
||
889 | * Encode data for posting using multipart encoding |
||
890 | * |
||
891 | * @fixme use of urlencode might be wrong here |
||
892 | * @author Andreas Gohr <[email protected]> |
||
893 | * |
||
894 | * @param array $data |
||
895 | * @return string |
||
896 | */ |
||
897 | function _postMultipartEncode($data){ |
||
920 | |||
921 | /** |
||
922 | * Generates a unique identifier for a connection. |
||
923 | * |
||
924 | * @param string $server |
||
925 | * @param string $port |
||
926 | * @return string unique identifier |
||
927 | */ |
||
928 | function _uniqueConnectionId($server, $port) { |
||
931 | } |
||
932 | |||
934 |
Adding explicit visibility (
private
,protected
, orpublic
) is generally recommend to communicate to other developers how, and from where this method is intended to be used.