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'){ |
||
| 555 | |||
| 556 | /** |
||
| 557 | * Tries to establish a CONNECT tunnel via Proxy |
||
| 558 | * |
||
| 559 | * Protocol, Servername and Port will be stripped from the request URL when a successful CONNECT happened |
||
| 560 | * |
||
| 561 | * @param resource &$socket |
||
| 562 | * @param string &$requesturl |
||
| 563 | * @throws HTTPClientException when a tunnel is needed but could not be established |
||
| 564 | * @return bool true if a tunnel was established |
||
| 565 | */ |
||
| 566 | function _ssltunnel(&$socket, &$requesturl){ |
||
| 567 | if(!$this->proxy_host) return false; |
||
| 568 | $requestinfo = parse_url($requesturl); |
||
| 569 | if($requestinfo['scheme'] != 'https') return false; |
||
| 570 | if(!$requestinfo['port']) $requestinfo['port'] = 443; |
||
| 571 | |||
| 572 | // build request |
||
| 573 | $request = "CONNECT {$requestinfo['host']}:{$requestinfo['port']} HTTP/1.0".HTTP_NL; |
||
| 574 | $request .= "Host: {$requestinfo['host']}".HTTP_NL; |
||
| 575 | if($this->proxy_user) { |
||
| 576 | $request .= 'Proxy-Authorization: Basic '.base64_encode($this->proxy_user.':'.$this->proxy_pass).HTTP_NL; |
||
| 577 | } |
||
| 578 | $request .= HTTP_NL; |
||
| 579 | |||
| 580 | $this->_debug('SSL Tunnel CONNECT',$request); |
||
| 581 | $this->_sendData($socket, $request, 'SSL Tunnel CONNECT'); |
||
| 582 | |||
| 583 | // read headers from socket |
||
| 584 | $r_headers = ''; |
||
| 585 | do{ |
||
| 586 | $r_line = $this->_readLine($socket, 'headers'); |
||
| 587 | $r_headers .= $r_line; |
||
| 588 | }while($r_line != "\r\n" && $r_line != "\n"); |
||
| 589 | |||
| 590 | $this->_debug('SSL Tunnel Response',$r_headers); |
||
| 591 | if(preg_match('/^HTTP\/1\.[01] 200/i',$r_headers)){ |
||
| 592 | // set correct peer name for verification (enabled since PHP 5.6) |
||
| 593 | stream_context_set_option($socket, 'ssl', 'peer_name', $requestinfo['host']); |
||
| 594 | |||
| 595 | // because SSLv3 is mostly broken, we try TLS connections here first. |
||
| 596 | // according to https://github.com/splitbrain/dokuwiki/commit/c05ef534 we had problems with certain |
||
| 597 | // setups with this solution before, but we have no usable test for that and TLS should be the more |
||
| 598 | // common crypto by now |
||
| 599 | if (@stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { |
||
| 600 | $requesturl = $requestinfo['path']. |
||
| 601 | (!empty($requestinfo['query'])?'?'.$requestinfo['query']:''); |
||
| 602 | return true; |
||
| 603 | } |
||
| 604 | |||
| 605 | // if the above failed, this will most probably not work either, but we can try |
||
| 606 | if (@stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_SSLv3_CLIENT)) { |
||
| 607 | $requesturl = $requestinfo['path']. |
||
| 608 | (!empty($requestinfo['query'])?'?'.$requestinfo['query']:''); |
||
| 609 | return true; |
||
| 610 | } |
||
| 611 | |||
| 612 | throw new HTTPClientException('Failed to set up crypto for secure connection to '.$requestinfo['host'], -151); |
||
| 613 | } |
||
| 614 | |||
| 615 | throw new HTTPClientException('Failed to establish secure proxy connection', -150); |
||
| 616 | } |
||
| 617 | |||
| 618 | /** |
||
| 619 | * Safely write data to a socket |
||
| 620 | * |
||
| 621 | * @param resource $socket An open socket handle |
||
| 622 | * @param string $data The data to write |
||
| 623 | * @param string $message Description of what is being read |
||
| 624 | * @throws HTTPClientException |
||
| 625 | * |
||
| 626 | * @author Tom N Harris <[email protected]> |
||
| 627 | */ |
||
| 628 | function _sendData($socket, $data, $message) { |
||
| 629 | // send request |
||
| 630 | $towrite = strlen($data); |
||
| 631 | $written = 0; |
||
| 632 | while($written < $towrite){ |
||
| 633 | // check timeout |
||
| 634 | $time_used = $this->_time() - $this->start; |
||
| 635 | if($time_used > $this->timeout) |
||
| 636 | throw new HTTPClientException(sprintf('Timeout while sending %s (%.3fs)',$message, $time_used), -100); |
||
| 637 | if(feof($socket)) |
||
| 638 | throw new HTTPClientException("Socket disconnected while writing $message"); |
||
| 639 | |||
| 640 | // select parameters |
||
| 641 | $sel_r = null; |
||
| 642 | $sel_w = array($socket); |
||
| 643 | $sel_e = null; |
||
| 644 | // wait for stream ready or timeout (1sec) |
||
| 645 | if(@stream_select($sel_r,$sel_w,$sel_e,1) === false){ |
||
| 646 | usleep(1000); |
||
| 647 | continue; |
||
| 648 | } |
||
| 649 | |||
| 650 | // write to stream |
||
| 651 | $nbytes = fwrite($socket, substr($data,$written,4096)); |
||
| 652 | if($nbytes === false) |
||
| 653 | throw new HTTPClientException("Failed writing to socket while sending $message", -100); |
||
| 654 | $written += $nbytes; |
||
| 655 | } |
||
| 656 | } |
||
| 657 | |||
| 658 | /** |
||
| 659 | * Safely read data from a socket |
||
| 660 | * |
||
| 661 | * Reads up to a given number of bytes or throws an exception if the |
||
| 662 | * response times out or ends prematurely. |
||
| 663 | * |
||
| 664 | * @param resource $socket An open socket handle in non-blocking mode |
||
| 665 | * @param int $nbytes Number of bytes to read |
||
| 666 | * @param string $message Description of what is being read |
||
| 667 | * @param bool $ignore_eof End-of-file is not an error if this is set |
||
| 668 | * @throws HTTPClientException |
||
| 669 | * @return string |
||
| 670 | * |
||
| 671 | * @author Tom N Harris <[email protected]> |
||
| 672 | */ |
||
| 673 | function _readData($socket, $nbytes, $message, $ignore_eof = false) { |
||
| 674 | $r_data = ''; |
||
| 675 | // Does not return immediately so timeout and eof can be checked |
||
| 676 | if ($nbytes < 0) $nbytes = 0; |
||
| 677 | $to_read = $nbytes; |
||
| 678 | do { |
||
| 679 | $time_used = $this->_time() - $this->start; |
||
| 680 | if ($time_used > $this->timeout) |
||
| 681 | throw new HTTPClientException( |
||
| 682 | sprintf('Timeout while reading %s after %d bytes (%.3fs)', $message, |
||
| 683 | strlen($r_data), $time_used), -100); |
||
| 684 | if(feof($socket)) { |
||
| 685 | if(!$ignore_eof) |
||
| 686 | throw new HTTPClientException("Premature End of File (socket) while reading $message"); |
||
| 687 | break; |
||
| 688 | } |
||
| 689 | |||
| 690 | if ($to_read > 0) { |
||
| 691 | // select parameters |
||
| 692 | $sel_r = array($socket); |
||
| 693 | $sel_w = null; |
||
| 694 | $sel_e = null; |
||
| 695 | // wait for stream ready or timeout (1sec) |
||
| 696 | if(@stream_select($sel_r,$sel_w,$sel_e,1) === false){ |
||
| 697 | usleep(1000); |
||
| 698 | continue; |
||
| 699 | } |
||
| 700 | |||
| 701 | $bytes = fread($socket, $to_read); |
||
| 702 | if($bytes === false) |
||
| 703 | throw new HTTPClientException("Failed reading from socket while reading $message", -100); |
||
| 704 | $r_data .= $bytes; |
||
| 705 | $to_read -= strlen($bytes); |
||
| 706 | } |
||
| 707 | } while ($to_read > 0 && strlen($r_data) < $nbytes); |
||
| 708 | return $r_data; |
||
| 709 | } |
||
| 710 | |||
| 711 | /** |
||
| 712 | * Safely read a \n-terminated line from a socket |
||
| 713 | * |
||
| 714 | * Always returns a complete line, including the terminating \n. |
||
| 715 | * |
||
| 716 | * @param resource $socket An open socket handle in non-blocking mode |
||
| 717 | * @param string $message Description of what is being read |
||
| 718 | * @throws HTTPClientException |
||
| 719 | * @return string |
||
| 720 | * |
||
| 721 | * @author Tom N Harris <[email protected]> |
||
| 722 | */ |
||
| 723 | function _readLine($socket, $message) { |
||
| 724 | $r_data = ''; |
||
| 725 | do { |
||
| 726 | $time_used = $this->_time() - $this->start; |
||
| 727 | if ($time_used > $this->timeout) |
||
| 728 | throw new HTTPClientException( |
||
| 729 | sprintf('Timeout while reading %s (%.3fs) >%s<', $message, $time_used, $r_data), |
||
| 730 | -100); |
||
| 731 | if(feof($socket)) |
||
| 732 | throw new HTTPClientException("Premature End of File (socket) while reading $message"); |
||
| 733 | |||
| 734 | // select parameters |
||
| 735 | $sel_r = array($socket); |
||
| 736 | $sel_w = null; |
||
| 737 | $sel_e = null; |
||
| 738 | // wait for stream ready or timeout (1sec) |
||
| 739 | if(@stream_select($sel_r,$sel_w,$sel_e,1) === false){ |
||
| 740 | usleep(1000); |
||
| 741 | continue; |
||
| 742 | } |
||
| 743 | |||
| 744 | $r_data = fgets($socket, 1024); |
||
| 745 | } while (!preg_match('/\n$/',$r_data)); |
||
| 746 | return $r_data; |
||
| 747 | } |
||
| 748 | |||
| 749 | /** |
||
| 750 | * print debug info |
||
| 751 | * |
||
| 752 | * Uses _debug_text or _debug_html depending on the SAPI name |
||
| 753 | * |
||
| 754 | * @author Andreas Gohr <[email protected]> |
||
| 755 | * |
||
| 756 | * @param string $info |
||
| 757 | * @param mixed $var |
||
| 758 | */ |
||
| 759 | function _debug($info,$var=null){ |
||
| 760 | if(!$this->debug) return; |
||
| 761 | if(php_sapi_name() == 'cli'){ |
||
| 762 | $this->_debug_text($info, $var); |
||
| 763 | }else{ |
||
| 764 | $this->_debug_html($info, $var); |
||
| 765 | } |
||
| 766 | } |
||
| 767 | |||
| 768 | /** |
||
| 769 | * print debug info as HTML |
||
| 770 | * |
||
| 771 | * @param string $info |
||
| 772 | * @param mixed $var |
||
| 773 | */ |
||
| 774 | function _debug_html($info, $var=null){ |
||
| 775 | print '<b>'.$info.'</b> '.($this->_time() - $this->start).'s<br />'; |
||
| 776 | if(!is_null($var)){ |
||
| 777 | ob_start(); |
||
| 778 | print_r($var); |
||
| 779 | $content = htmlspecialchars(ob_get_contents()); |
||
| 780 | ob_end_clean(); |
||
| 781 | print '<pre>'.$content.'</pre>'; |
||
| 782 | } |
||
| 783 | } |
||
| 784 | |||
| 785 | /** |
||
| 786 | * prints debug info as plain text |
||
| 787 | * |
||
| 788 | * @param string $info |
||
| 789 | * @param mixed $var |
||
| 790 | */ |
||
| 791 | function _debug_text($info, $var=null){ |
||
| 792 | print '*'.$info.'* '.($this->_time() - $this->start)."s\n"; |
||
| 793 | if(!is_null($var)) print_r($var); |
||
| 794 | print "\n-----------------------------------------------\n"; |
||
| 795 | } |
||
| 796 | |||
| 797 | /** |
||
| 798 | * Return current timestamp in microsecond resolution |
||
| 799 | * |
||
| 800 | * @return float |
||
| 801 | */ |
||
| 802 | static function _time(){ |
||
| 806 | |||
| 807 | /** |
||
| 808 | * convert given header string to Header array |
||
| 809 | * |
||
| 810 | * All Keys are lowercased. |
||
| 811 | * |
||
| 812 | * @author Andreas Gohr <[email protected]> |
||
| 813 | * |
||
| 814 | * @param string $string |
||
| 815 | * @return array |
||
| 816 | */ |
||
| 817 | function _parseHeaders($string){ |
||
| 818 | $headers = array(); |
||
| 819 | $lines = explode("\n",$string); |
||
| 820 | array_shift($lines); //skip first line (status) |
||
| 821 | foreach($lines as $line){ |
||
| 822 | @list($key, $val) = explode(':',$line,2); |
||
|
1 ignored issue
–
show
|
|||
| 823 | $key = trim($key); |
||
| 824 | $val = trim($val); |
||
| 825 | $key = strtolower($key); |
||
| 826 | if(!$key) continue; |
||
| 827 | if(isset($headers[$key])){ |
||
| 828 | if(is_array($headers[$key])){ |
||
| 829 | $headers[$key][] = $val; |
||
| 830 | }else{ |
||
| 831 | $headers[$key] = array($headers[$key],$val); |
||
| 832 | } |
||
| 833 | }else{ |
||
| 834 | $headers[$key] = $val; |
||
| 835 | } |
||
| 836 | } |
||
| 837 | return $headers; |
||
| 838 | } |
||
| 839 | |||
| 840 | /** |
||
| 841 | * convert given header array to header string |
||
| 842 | * |
||
| 843 | * @author Andreas Gohr <[email protected]> |
||
| 844 | * |
||
| 845 | * @param array $headers |
||
| 846 | * @return string |
||
| 847 | */ |
||
| 848 | function _buildHeaders($headers){ |
||
| 849 | $string = ''; |
||
| 850 | foreach($headers as $key => $value){ |
||
| 851 | if($value === '') continue; |
||
| 852 | $string .= $key.': '.$value.HTTP_NL; |
||
| 853 | } |
||
| 854 | return $string; |
||
| 855 | } |
||
| 856 | |||
| 857 | /** |
||
| 858 | * get cookies as http header string |
||
| 859 | * |
||
| 860 | * @author Andreas Goetz <[email protected]> |
||
| 861 | * |
||
| 862 | * @return string |
||
| 863 | */ |
||
| 864 | function _getCookies(){ |
||
| 865 | $headers = ''; |
||
| 866 | foreach ($this->cookies as $key => $val){ |
||
| 867 | $headers .= "$key=$val; "; |
||
| 868 | } |
||
| 869 | $headers = substr($headers, 0, -2); |
||
| 870 | if ($headers) $headers = "Cookie: $headers".HTTP_NL; |
||
| 871 | return $headers; |
||
| 872 | } |
||
| 873 | |||
| 874 | /** |
||
| 875 | * Encode data for posting |
||
| 876 | * |
||
| 877 | * @author Andreas Gohr <[email protected]> |
||
| 878 | * |
||
| 879 | * @param array $data |
||
| 880 | * @return string |
||
| 881 | */ |
||
| 882 | function _postEncode($data){ |
||
| 885 | |||
| 886 | /** |
||
| 887 | * Encode data for posting using multipart encoding |
||
| 888 | * |
||
| 889 | * @fixme use of urlencode might be wrong here |
||
| 890 | * @author Andreas Gohr <[email protected]> |
||
| 891 | * |
||
| 892 | * @param array $data |
||
| 893 | * @return string |
||
| 894 | */ |
||
| 895 | function _postMultipartEncode($data){ |
||
| 896 | $boundary = '--'.$this->boundary; |
||
| 897 | $out = ''; |
||
| 898 | foreach($data as $key => $val){ |
||
| 899 | $out .= $boundary.HTTP_NL; |
||
| 900 | if(!is_array($val)){ |
||
| 901 | $out .= 'Content-Disposition: form-data; name="'.urlencode($key).'"'.HTTP_NL; |
||
| 902 | $out .= HTTP_NL; // end of headers |
||
| 903 | $out .= $val; |
||
| 904 | $out .= HTTP_NL; |
||
| 905 | }else{ |
||
| 906 | $out .= 'Content-Disposition: form-data; name="'.urlencode($key).'"'; |
||
| 907 | if($val['filename']) $out .= '; filename="'.urlencode($val['filename']).'"'; |
||
| 908 | $out .= HTTP_NL; |
||
| 909 | if($val['mimetype']) $out .= 'Content-Type: '.$val['mimetype'].HTTP_NL; |
||
| 910 | $out .= HTTP_NL; // end of headers |
||
| 911 | $out .= $val['body']; |
||
| 912 | $out .= HTTP_NL; |
||
| 913 | } |
||
| 914 | } |
||
| 915 | $out .= "$boundary--".HTTP_NL; |
||
| 916 | return $out; |
||
| 917 | } |
||
| 918 | |||
| 919 | /** |
||
| 920 | * Generates a unique identifier for a connection. |
||
| 921 | * |
||
| 922 | * @param string $server |
||
| 923 | * @param string $port |
||
| 924 | * @return string unique identifier |
||
| 925 | */ |
||
| 926 | function _uniqueConnectionId($server, $port) { |
||
| 929 | } |
||
| 930 | |||
| 931 | //Setup VIM: ex: et ts=4 : |
||
| 932 |
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.