Complex classes like ARC2_Reader 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 ARC2_Reader, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
14 | class ARC2_Reader extends ARC2_Class { |
||
15 | |||
16 | function __construct($a = '', &$caller) { |
||
19 | |||
20 | function ARC2_Reader($a = '', &$caller) { |
||
23 | |||
24 | function __init() {/* inc_path, proxy_host, proxy_port, proxy_skip, http_accept_header, http_user_agent_header, max_redirects */ |
||
25 | parent::__init(); |
||
26 | $this->http_method = $this->v('http_method', 'GET', $this->a); |
||
27 | $this->message_body = $this->v('message_body', '', $this->a);; |
||
28 | $this->http_accept_header = $this->v('http_accept_header', 'Accept: application/rdf+xml; q=0.9, */*; q=0.1', $this->a); |
||
29 | $this->http_user_agent_header = $this->v('http_user_agent_header', 'User-Agent: ARC Reader (http://arc.semsol.org/)', $this->a); |
||
30 | $this->http_custom_headers = $this->v('http_custom_headers', '', $this->a); |
||
31 | $this->max_redirects = $this->v('max_redirects', 3, $this->a); |
||
32 | $this->format = $this->v('format', false, $this->a); |
||
33 | $this->redirects = array(); |
||
34 | $this->stream_id = ''; |
||
35 | $this->timeout = $this->v('reader_timeout', 30, $this->a); |
||
36 | $this->response_headers = array(); |
||
37 | $this->digest_auth = 0; |
||
38 | $this->auth_infos = $this->v('reader_auth_infos', array(), $this->a); |
||
39 | } |
||
40 | |||
41 | /* */ |
||
42 | |||
43 | function setHTTPMethod($v) { |
||
46 | |||
47 | function setMessageBody($v) { |
||
50 | |||
51 | function setAcceptHeader($v) { |
||
54 | |||
55 | function setCustomHeaders($v) { |
||
58 | |||
59 | function addCustomHeaders($v) { |
||
63 | |||
64 | /* */ |
||
65 | |||
66 | function activate($path, $data = '', $ping_only = 0, $timeout = 0) { |
||
67 | $this->setCredentials($path); |
||
68 | $this->ping_only = $ping_only; |
||
69 | if ($timeout) $this->timeout = $timeout; |
||
70 | $id = md5($path . ' ' . $data); |
||
71 | if ($this->stream_id != $id) { |
||
72 | $this->stream_id = $id; |
||
73 | /* data uri? */ |
||
74 | if (!$data && preg_match('/^data\:([^\,]+)\,(.*)$/', $path, $m)) { |
||
75 | $path = ''; |
||
76 | $data = preg_match('/base64/', $m[1]) ? base64_decode($m[2]) : rawurldecode($m[2]); |
||
77 | } |
||
78 | $this->base = $this->calcBase($path); |
||
79 | $this->uri = $this->calcURI($path, $this->base); |
||
80 | $this->stream = ($data) ? $this->getDataStream($data) : $this->getSocketStream($this->base, $ping_only); |
||
81 | if ($this->stream && !$this->ping_only) { |
||
82 | $this->getFormat(); |
||
83 | } |
||
84 | } |
||
85 | } |
||
86 | |||
87 | /* |
||
88 | * HTTP Basic/Digest + Proxy authorization can be defined in the |
||
89 | * arc_reader_credentials config setting: |
||
90 | |||
91 | 'arc_reader_credentials' => array( |
||
92 | 'http://basic.example.com/' => 'user:pass', // shortcut for type=basic |
||
93 | 'http://digest.example.com/' => 'user::pass', // shortcut for type=digest |
||
94 | 'http://proxy.example.com/' => array('type' => 'basic', 'proxy', 'user' => 'user', 'pass' => 'pass'), |
||
95 | ), |
||
96 | |||
97 | */ |
||
98 | |||
99 | function setCredentials($url) { |
||
100 | if (!$creds = $this->v('arc_reader_credentials', array(), $this->a)) return 0; |
||
101 | foreach ($creds as $pattern => $creds) { |
||
102 | /* digest shortcut (user::pass) */ |
||
103 | if (!is_array($creds) && preg_match('/^(.+)\:\:(.+)$/', $creds, $m)) { |
||
104 | $creds = array('type' => 'digest', 'user' => $m[1], 'pass' => $m[2]); |
||
105 | } |
||
106 | /* basic shortcut (user:pass) */ |
||
107 | if (!is_array($creds) && preg_match('/^(.+)\:(.+)$/', $creds, $m)) { |
||
108 | $creds = array('type' => 'basic', 'user' => $m[1], 'pass' => $m[2]); |
||
109 | } |
||
110 | if (!is_array($creds)) return 0; |
||
111 | $regex = '/' . preg_replace('/([\:\/\.\?])/', '\\\\\1', $pattern) . '/'; |
||
112 | if (!preg_match($regex, $url)) continue; |
||
113 | $mthd = 'set' . $this->camelCase($creds['type']) . 'AuthCredentials'; |
||
114 | if (method_exists($this, $mthd)) $this->$mthd($creds, $url); |
||
115 | } |
||
116 | } |
||
117 | |||
118 | function setBasicAuthCredentials($creds) { |
||
119 | $auth = 'Basic ' . base64_encode($creds['user'] . ':' . $creds['pass']); |
||
120 | $h = in_array('proxy', $creds) ? 'Proxy-Authorization' : 'Authorization'; |
||
121 | $this->addCustomHeaders($h . ': ' . $auth); |
||
122 | //echo $h . ': ' . $auth . print_r($creds, 1); |
||
123 | } |
||
124 | |||
125 | function setDigestAuthCredentials($creds, $url) { |
||
126 | $path = $this->v1('path', '/', parse_url($url)); |
||
|
|||
127 | $auth = ''; |
||
128 | $hs = $this->getResponseHeaders(); |
||
129 | /* initial 401 */ |
||
130 | $h = $this->v('www-authenticate', '', $hs); |
||
131 | if ($h && preg_match('/Digest/i', $h)) { |
||
132 | $auth = 'Digest '; |
||
133 | /* Digest realm="$realm", nonce="$nonce", qop="auth", opaque="$opaque" */ |
||
134 | $ks = array('realm', 'nonce', 'opaque');/* skipping qop, assuming "auth" */ |
||
135 | foreach ($ks as $i => $k) { |
||
136 | $$k = preg_match('/' . $k . '=\"?([^\"]+)\"?/i', $h, $m) ? $m[1] : ''; |
||
137 | $auth .= ($i ? ', ' : '') . $k . '="' . $$k . '"'; |
||
138 | $this->auth_infos[$k] = $$k; |
||
139 | } |
||
140 | $this->auth_infos['auth'] = $auth; |
||
141 | $this->auth_infos['request_count'] = 1; |
||
142 | } |
||
143 | /* initial 401 or repeated request */ |
||
144 | if ($this->v('auth', 0, $this->auth_infos)) { |
||
145 | $qop = 'auth'; |
||
146 | $auth = $this->auth_infos['auth']; |
||
147 | $rc = $this->auth_infos['request_count']; |
||
148 | $realm = $this->auth_infos['realm']; |
||
149 | $nonce = $this->auth_infos['nonce']; |
||
150 | $ha1 = md5($creds['user'] . ':' . $realm . ':' . $creds['pass']); |
||
151 | $ha2 = md5($this->http_method . ':' . $path); |
||
152 | $nc = dechex($rc); |
||
153 | $cnonce = dechex($rc * 2); |
||
154 | $resp = md5($ha1 . ':' . $nonce . ':' . $nc . ':' . $cnonce . ':' . $qop . ':' . $ha2); |
||
155 | $auth .= ', username="' . $creds['user'] . '"' . |
||
156 | ', uri="' . $path . '"' . |
||
157 | ', qop=' . $qop . '' . |
||
158 | ', nc=' . $nc . |
||
159 | ', cnonce="' . $cnonce . '"' . |
||
160 | ', uri="' . $path . '"' . |
||
161 | ', response="' . $resp . '"' . |
||
162 | ''; |
||
163 | $this->auth_infos['request_count'] = $rc + 1; |
||
164 | } |
||
165 | if (!$auth) return 0; |
||
166 | $h = in_array('proxy', $creds) ? 'Proxy-Authorization' : 'Authorization'; |
||
167 | $this->addCustomHeaders($h . ': ' . $auth); |
||
168 | } |
||
169 | |||
170 | /* */ |
||
171 | |||
172 | function useProxy($url) { |
||
184 | |||
185 | /* */ |
||
186 | |||
187 | function createStream($path, $data = '') { |
||
191 | |||
192 | function getDataStream($data) { |
||
195 | |||
196 | function getSocketStream($url) { |
||
197 | if ($url == 'file://') { |
||
198 | return $this->addError('Error: file does not exists or is not accessible'); |
||
199 | } |
||
200 | $parts = parse_url($url); |
||
201 | $mappings = array('file' => 'File', 'http' => 'HTTP', 'https' => 'HTTP'); |
||
202 | if ($scheme = $this->v(strtolower($parts['scheme']), '', $mappings)) { |
||
203 | return $this->m('get' . $scheme . 'Socket', $url, $this->getDataStream('')); |
||
204 | } |
||
205 | } |
||
206 | |||
207 | function getFileSocket($url) { |
||
208 | $parts = parse_url($url); |
||
209 | $s = file_exists($parts['path']) ? @fopen($parts['path'], 'rb') : false; |
||
210 | if (!$s) { |
||
211 | return $this->addError('Socket error: Could not open "' . $parts['path'] . '"'); |
||
212 | } |
||
213 | return array('type' => 'socket', 'socket' =>& $s, 'headers' => array(), 'pos' => 0, 'size' => filesize($parts['path']), 'buffer' => ''); |
||
214 | } |
||
215 | |||
216 | function getHTTPSocket($url, $redirs = 0, $prev_parts = '') { |
||
338 | |||
339 | function readStream($buffer_xml = true, $d_size = 1024) { |
||
340 | //if (!$s = $this->v('stream')) return ''; |
||
341 | if (!$s = $this->v('stream')) return $this->addError('missing stream in "readStream" ' . $this->uri); |
||
342 | $s_type = $this->v('type', '', $s); |
||
343 | $r = $s['buffer']; |
||
344 | $s['buffer'] = ''; |
||
345 | if ($s['size']) $d_size = min($d_size, $s['size'] - $s['pos']); |
||
346 | /* data */ |
||
347 | if ($s_type == 'data') { |
||
348 | $d = ($d_size > 0) ? substr($s['data'], $s['pos'], $d_size) : ''; |
||
349 | } |
||
350 | /* socket */ |
||
351 | elseif ($s_type == 'socket') { |
||
352 | $d = ($d_size > 0) && !feof($s['socket']) ? fread($s['socket'], $d_size) : ''; |
||
353 | } |
||
354 | $eof = $d ? false : true; |
||
355 | /* chunked despite HTTP 1.0 request */ |
||
356 | if (isset($s['headers']) && isset($s['headers']['transfer-encoding']) && ($s['headers']['transfer-encoding'] == 'chunked')) { |
||
357 | $d = preg_replace('/(^|[\r\n]+)[0-9a-f]{1,4}[\r\n]+/', '', $d); |
||
358 | } |
||
359 | $s['pos'] += strlen($d); |
||
360 | if ($buffer_xml) {/* stop after last closing xml tag (if available) */ |
||
361 | if (preg_match('/^(.*\>)([^\>]*)$/s', $d, $m)) { |
||
362 | $d = $m[1]; |
||
363 | $s['buffer'] = $m[2]; |
||
364 | } |
||
365 | elseif (!$eof) { |
||
366 | $s['buffer'] = $r . $d; |
||
367 | $this->stream = $s; |
||
368 | return $this->readStream(true, $d_size); |
||
369 | } |
||
370 | } |
||
371 | $this->stream = $s; |
||
372 | return $r . $d; |
||
373 | } |
||
374 | |||
375 | function closeStream() { |
||
376 | if (isset($this->stream)) { |
||
377 | if ($this->v('type', 0, $this->stream) == 'socket') { |
||
378 | @fclose($this->stream['socket']); |
||
379 | } |
||
380 | unset($this->stream); |
||
381 | } |
||
382 | } |
||
383 | |||
384 | /* */ |
||
385 | |||
386 | function getFormat() { |
||
399 | |||
400 | /* */ |
||
401 | |||
402 | function getResponseHeaders() { |
||
403 | if (isset($this->stream) && isset($this->stream['headers'])) { |
||
404 | return $this->stream['headers']; |
||
405 | } |
||
406 | return $this->response_headers; |
||
407 | } |
||
408 | |||
409 | function getEncoding($default = 'UTF-8') { |
||
412 | |||
413 | function getRedirects() { |
||
416 | |||
417 | function getAuthInfos() { |
||
420 | |||
421 | /* */ |
||
422 | |||
423 | } |
||
424 |
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.