Complex classes like RemoteAPICore 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 RemoteAPICore, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
12 | class RemoteAPICore { |
||
13 | |||
14 | private $api; |
||
15 | |||
16 | /** |
||
17 | * @param RemoteAPI $api |
||
18 | */ |
||
19 | public function __construct(RemoteAPI $api) { |
||
22 | |||
23 | /** |
||
24 | * Returns details about the core methods |
||
25 | * |
||
26 | * @return array |
||
27 | */ |
||
28 | public function __getRemoteInfo() { |
||
172 | |||
173 | /** |
||
174 | * @return string |
||
175 | */ |
||
176 | public function getVersion() { |
||
177 | return getVersion(); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * @return int unix timestamp |
||
182 | */ |
||
183 | public function getTime() { |
||
186 | |||
187 | /** |
||
188 | * Return a raw wiki page |
||
189 | * |
||
190 | * @param string $id wiki page id |
||
191 | * @param int|string $rev revision timestamp of the page or empty string |
||
192 | * @return string page text. |
||
193 | * @throws RemoteAccessDeniedException if no permission for page |
||
194 | */ |
||
195 | public function rawPage($id,$rev=''){ |
||
207 | |||
208 | /** |
||
209 | * Return a media file |
||
210 | * |
||
211 | * @author Gina Haeussge <[email protected]> |
||
212 | * |
||
213 | * @param string $id file id |
||
214 | * @return mixed media file |
||
215 | * @throws RemoteAccessDeniedException no permission for media |
||
216 | * @throws RemoteException not exist |
||
217 | */ |
||
218 | public function getAttachment($id){ |
||
232 | |||
233 | /** |
||
234 | * Return info about a media file |
||
235 | * |
||
236 | * @author Gina Haeussge <[email protected]> |
||
237 | * |
||
238 | * @param string $id page id |
||
239 | * @return array |
||
240 | */ |
||
241 | public function getAttachmentInfo($id){ |
||
265 | |||
266 | /** |
||
267 | * Return a wiki page rendered to html |
||
268 | * |
||
269 | * @param string $id page id |
||
270 | * @param string|int $rev revision timestamp or empty string |
||
271 | * @return null|string html |
||
272 | * @throws RemoteAccessDeniedException no access to page |
||
273 | */ |
||
274 | public function htmlPage($id,$rev=''){ |
||
281 | |||
282 | /** |
||
283 | * List all pages - we use the indexer list here |
||
284 | * |
||
285 | * @return array |
||
286 | */ |
||
287 | public function listPages(){ |
||
288 | $list = array(); |
||
289 | $pages = idx_get_indexer()->getPages(); |
||
290 | $pages = array_filter(array_filter($pages,'isVisiblePage'),'page_exists'); |
||
291 | |||
292 | foreach(array_keys($pages) as $idx) { |
||
293 | $perm = auth_quickaclcheck($pages[$idx]); |
||
294 | if($perm < AUTH_READ) { |
||
295 | continue; |
||
296 | } |
||
297 | $page = array(); |
||
298 | $page['id'] = trim($pages[$idx]); |
||
299 | $page['perms'] = $perm; |
||
300 | $page['size'] = @filesize(wikiFN($pages[$idx])); |
||
301 | $page['lastModified'] = $this->api->toDate(@filemtime(wikiFN($pages[$idx]))); |
||
302 | $list[] = $page; |
||
303 | } |
||
304 | |||
305 | return $list; |
||
306 | } |
||
307 | |||
308 | /** |
||
309 | * List all pages in the given namespace (and below) |
||
310 | * |
||
311 | * @param string $ns |
||
312 | * @param array $opts |
||
313 | * $opts['depth'] recursion level, 0 for all |
||
314 | * $opts['hash'] do md5 sum of content? |
||
315 | * @return array |
||
316 | */ |
||
317 | public function readNamespace($ns,$opts){ |
||
318 | global $conf; |
||
319 | |||
320 | if(!is_array($opts)) $opts=array(); |
||
321 | |||
322 | $ns = cleanID($ns); |
||
323 | $dir = utf8_encodeFN(str_replace(':', '/', $ns)); |
||
324 | $data = array(); |
||
325 | $opts['skipacl'] = 0; // no ACL skipping for XMLRPC |
||
326 | search($data, $conf['datadir'], 'search_allpages', $opts, $dir); |
||
327 | return $data; |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * List all pages in the given namespace (and below) |
||
332 | * |
||
333 | * @param string $query |
||
334 | * @return array |
||
335 | */ |
||
336 | public function search($query){ |
||
337 | $regex = array(); |
||
338 | $data = ft_pageSearch($query,$regex); |
||
339 | $pages = array(); |
||
340 | |||
341 | // prepare additional data |
||
342 | $idx = 0; |
||
343 | foreach($data as $id => $score){ |
||
344 | $file = wikiFN($id); |
||
345 | |||
346 | if($idx < FT_SNIPPET_NUMBER){ |
||
347 | $snippet = ft_snippet($id,$regex); |
||
348 | $idx++; |
||
349 | }else{ |
||
350 | $snippet = ''; |
||
351 | } |
||
352 | |||
353 | $pages[] = array( |
||
354 | 'id' => $id, |
||
355 | 'score' => intval($score), |
||
356 | 'rev' => filemtime($file), |
||
357 | 'mtime' => filemtime($file), |
||
358 | 'size' => filesize($file), |
||
359 | 'snippet' => $snippet, |
||
360 | 'title' => useHeading('navigation') ? p_get_first_heading($id) : $id |
||
361 | ); |
||
362 | } |
||
363 | return $pages; |
||
364 | } |
||
365 | |||
366 | /** |
||
367 | * Returns the wiki title. |
||
368 | * |
||
369 | * @return string |
||
370 | */ |
||
371 | public function getTitle(){ |
||
375 | |||
376 | /** |
||
377 | * List all media files. |
||
378 | * |
||
379 | * Available options are 'recursive' for also including the subnamespaces |
||
380 | * in the listing, and 'pattern' for filtering the returned files against |
||
381 | * a regular expression matching their name. |
||
382 | * |
||
383 | * @author Gina Haeussge <[email protected]> |
||
384 | * |
||
385 | * @param string $ns |
||
386 | * @param array $options |
||
387 | * $options['depth'] recursion level, 0 for all |
||
388 | * $options['showmsg'] shows message if invalid media id is used |
||
389 | * $options['pattern'] check given pattern |
||
390 | * $options['hash'] add hashes to result list |
||
391 | * @return array |
||
392 | * @throws RemoteAccessDeniedException no access to the media files |
||
393 | */ |
||
394 | public function listAttachments($ns, $options = array()) { |
||
421 | |||
422 | /** |
||
423 | * Return a list of backlinks |
||
424 | * |
||
425 | * @param string $id page id |
||
426 | * @return array |
||
427 | */ |
||
428 | function listBackLinks($id){ |
||
|
|||
429 | return ft_backlinks($this->resolvePageId($id)); |
||
430 | } |
||
431 | |||
432 | /** |
||
433 | * Return some basic data about a page |
||
434 | * |
||
435 | * @param string $id page id |
||
436 | * @param string|int $rev revision timestamp or empty string |
||
437 | * @return array |
||
438 | * @throws RemoteAccessDeniedException no access for page |
||
439 | * @throws RemoteException page not exist |
||
440 | */ |
||
441 | public function pageInfo($id,$rev=''){ |
||
442 | $id = $this->resolvePageId($id); |
||
443 | if(auth_quickaclcheck($id) < AUTH_READ){ |
||
444 | throw new RemoteAccessDeniedException('You are not allowed to read this page', 111); |
||
445 | } |
||
446 | $file = wikiFN($id,$rev); |
||
447 | $time = @filemtime($file); |
||
448 | if(!$time){ |
||
449 | throw new RemoteException('The requested page does not exist', 121); |
||
450 | } |
||
451 | |||
452 | $pagelog = new PageChangeLog($id, 1024); |
||
453 | $info = $pagelog->getRevisionInfo($time); |
||
454 | |||
455 | $data = array( |
||
456 | 'name' => $id, |
||
457 | 'lastModified' => $this->api->toDate($time), |
||
458 | 'author' => (($info['user']) ? $info['user'] : $info['ip']), |
||
459 | 'version' => $time |
||
460 | ); |
||
461 | |||
462 | return ($data); |
||
463 | } |
||
464 | |||
465 | /** |
||
466 | * Save a wiki page |
||
467 | * |
||
468 | * @author Michael Klier <[email protected]> |
||
469 | * |
||
470 | * @param string $id page id |
||
471 | * @param string $text wiki text |
||
472 | * @param array $params parameters: summary, minor edit |
||
473 | * @return bool |
||
474 | * @throws RemoteAccessDeniedException no write access for page |
||
475 | * @throws RemoteException no id, empty new page or locked |
||
476 | */ |
||
477 | public function putPage($id, $text, $params) { |
||
478 | global $TEXT; |
||
479 | global $lang; |
||
480 | |||
481 | $id = $this->resolvePageId($id); |
||
482 | $TEXT = cleanText($text); |
||
483 | $sum = $params['sum']; |
||
484 | $minor = $params['minor']; |
||
485 | |||
486 | if(empty($id)) { |
||
487 | throw new RemoteException('Empty page ID', 131); |
||
488 | } |
||
489 | |||
490 | if(!page_exists($id) && trim($TEXT) == '' ) { |
||
491 | throw new RemoteException('Refusing to write an empty new wiki page', 132); |
||
492 | } |
||
493 | |||
494 | if(auth_quickaclcheck($id) < AUTH_EDIT) { |
||
495 | throw new RemoteAccessDeniedException('You are not allowed to edit this page', 112); |
||
496 | } |
||
497 | |||
498 | // Check, if page is locked |
||
499 | if(checklock($id)) { |
||
500 | throw new RemoteException('The page is currently locked', 133); |
||
501 | } |
||
502 | |||
503 | // SPAM check |
||
504 | if(checkwordblock()) { |
||
505 | throw new RemoteException('Positive wordblock check', 134); |
||
506 | } |
||
507 | |||
508 | // autoset summary on new pages |
||
509 | if(!page_exists($id) && empty($sum)) { |
||
510 | $sum = $lang['created']; |
||
511 | } |
||
512 | |||
513 | // autoset summary on deleted pages |
||
514 | if(page_exists($id) && empty($TEXT) && empty($sum)) { |
||
515 | $sum = $lang['deleted']; |
||
516 | } |
||
517 | |||
518 | lock($id); |
||
519 | |||
520 | saveWikiText($id,$TEXT,$sum,$minor); |
||
521 | |||
522 | unlock($id); |
||
523 | |||
524 | // run the indexer if page wasn't indexed yet |
||
525 | idx_addPage($id); |
||
526 | |||
527 | return true; |
||
528 | } |
||
529 | |||
530 | /** |
||
531 | * Appends text to a wiki page. |
||
532 | * |
||
533 | * @param string $id page id |
||
534 | * @param string $text wiki text |
||
535 | * @param array $params such as summary,minor |
||
536 | * @return bool|string |
||
537 | */ |
||
538 | public function appendPage($id, $text, $params) { |
||
539 | $currentpage = $this->rawPage($id); |
||
540 | if (!is_string($currentpage)) { |
||
541 | return $currentpage; |
||
542 | } |
||
543 | return $this->putPage($id, $currentpage.$text, $params); |
||
544 | } |
||
545 | |||
546 | /** |
||
547 | * Uploads a file to the wiki. |
||
548 | * |
||
549 | * Michael Klier <[email protected]> |
||
550 | * |
||
551 | * @param string $id page id |
||
552 | * @param string $file |
||
553 | * @param array $params such as overwrite |
||
554 | * @return false|string |
||
555 | * @throws RemoteException |
||
556 | */ |
||
557 | public function putAttachment($id, $file, $params) { |
||
558 | $id = cleanID($id); |
||
559 | $auth = auth_quickaclcheck(getNS($id).':*'); |
||
560 | |||
561 | if(!isset($id)) { |
||
562 | throw new RemoteException('Filename not given.', 231); |
||
563 | } |
||
564 | |||
565 | global $conf; |
||
566 | |||
567 | $ftmp = $conf['tmpdir'] . '/' . md5($id.clientIP()); |
||
568 | |||
569 | // save temporary file |
||
570 | @unlink($ftmp); |
||
1 ignored issue
–
show
|
|||
571 | io_saveFile($ftmp, $file); |
||
572 | |||
573 | $res = media_save(array('name' => $ftmp), $id, $params['ow'], $auth, 'rename'); |
||
574 | if (is_array($res)) { |
||
575 | throw new RemoteException($res[0], -$res[1]); |
||
576 | } else { |
||
577 | return $res; |
||
578 | } |
||
579 | } |
||
580 | |||
581 | /** |
||
582 | * Deletes a file from the wiki. |
||
583 | * |
||
584 | * @author Gina Haeussge <[email protected]> |
||
585 | * |
||
586 | * @param string $id page id |
||
587 | * @return int |
||
588 | * @throws RemoteAccessDeniedException no permissions |
||
589 | * @throws RemoteException file in use or not deleted |
||
590 | */ |
||
591 | public function deleteAttachment($id){ |
||
592 | $id = cleanID($id); |
||
593 | $auth = auth_quickaclcheck(getNS($id).':*'); |
||
594 | $res = media_delete($id, $auth); |
||
595 | if ($res & DOKU_MEDIA_DELETED) { |
||
596 | return 0; |
||
597 | } elseif ($res & DOKU_MEDIA_NOT_AUTH) { |
||
598 | throw new RemoteAccessDeniedException('You don\'t have permissions to delete files.', 212); |
||
599 | } elseif ($res & DOKU_MEDIA_INUSE) { |
||
600 | throw new RemoteException('File is still referenced', 232); |
||
601 | } else { |
||
602 | throw new RemoteException('Could not delete file', 233); |
||
603 | } |
||
604 | } |
||
605 | |||
606 | /** |
||
607 | * Returns the permissions of a given wiki page for the current user or another user |
||
608 | * |
||
609 | * @param string $id page id |
||
610 | * @param string|null $user username |
||
611 | * @param array|null $groups array of groups |
||
612 | * @return int permission level |
||
613 | */ |
||
614 | public function aclCheck($id, $user = null, $groups = null) { |
||
633 | |||
634 | /** |
||
635 | * Lists all links contained in a wiki page |
||
636 | * |
||
637 | * @author Michael Klier <[email protected]> |
||
638 | * |
||
639 | * @param string $id page id |
||
640 | * @return array |
||
641 | * @throws RemoteAccessDeniedException no read access for page |
||
642 | */ |
||
643 | public function listLinks($id) { |
||
644 | $id = $this->resolvePageId($id); |
||
645 | if(auth_quickaclcheck($id) < AUTH_READ){ |
||
646 | throw new RemoteAccessDeniedException('You are not allowed to read this page', 111); |
||
647 | } |
||
648 | $links = array(); |
||
649 | |||
650 | // resolve page instructions |
||
651 | $ins = p_cached_instructions(wikiFN($id)); |
||
652 | |||
653 | // instantiate new Renderer - needed for interwiki links |
||
654 | $Renderer = new Doku_Renderer_xhtml(); |
||
655 | $Renderer->interwiki = getInterwiki(); |
||
656 | |||
657 | // parse parse instructions |
||
658 | foreach($ins as $in) { |
||
659 | $link = array(); |
||
660 | switch($in[0]) { |
||
661 | case 'internallink': |
||
662 | $link['type'] = 'local'; |
||
663 | $link['page'] = $in[1][0]; |
||
664 | $link['href'] = wl($in[1][0]); |
||
665 | array_push($links,$link); |
||
666 | break; |
||
667 | case 'externallink': |
||
668 | $link['type'] = 'extern'; |
||
669 | $link['page'] = $in[1][0]; |
||
670 | $link['href'] = $in[1][0]; |
||
671 | array_push($links,$link); |
||
672 | break; |
||
673 | case 'interwikilink': |
||
674 | $url = $Renderer->_resolveInterWiki($in[1][2],$in[1][3]); |
||
675 | $link['type'] = 'extern'; |
||
676 | $link['page'] = $url; |
||
677 | $link['href'] = $url; |
||
678 | array_push($links,$link); |
||
679 | break; |
||
680 | } |
||
681 | } |
||
682 | |||
683 | return ($links); |
||
684 | } |
||
685 | |||
686 | /** |
||
687 | * Returns a list of recent changes since give timestamp |
||
688 | * |
||
689 | * @author Michael Hamann <[email protected]> |
||
690 | * @author Michael Klier <[email protected]> |
||
691 | * |
||
692 | * @param int $timestamp unix timestamp |
||
693 | * @return array |
||
694 | * @throws RemoteException no valid timestamp |
||
695 | */ |
||
696 | public function getRecentChanges($timestamp) { |
||
697 | if(strlen($timestamp) != 10) { |
||
698 | throw new RemoteException('The provided value is not a valid timestamp', 311); |
||
699 | } |
||
700 | |||
701 | $recents = getRecentsSince($timestamp); |
||
702 | |||
703 | $changes = array(); |
||
704 | |||
705 | foreach ($recents as $recent) { |
||
706 | $change = array(); |
||
707 | $change['name'] = $recent['id']; |
||
708 | $change['lastModified'] = $this->api->toDate($recent['date']); |
||
709 | $change['author'] = $recent['user']; |
||
710 | $change['version'] = $recent['date']; |
||
711 | $change['perms'] = $recent['perms']; |
||
712 | $change['size'] = @filesize(wikiFN($recent['id'])); |
||
713 | array_push($changes, $change); |
||
714 | } |
||
715 | |||
716 | if (!empty($changes)) { |
||
717 | return $changes; |
||
718 | } else { |
||
719 | // in case we still have nothing at this point |
||
720 | throw new RemoteException('There are no changes in the specified timeframe', 321); |
||
721 | } |
||
722 | } |
||
723 | |||
724 | /** |
||
725 | * Returns a list of recent media changes since give timestamp |
||
726 | * |
||
727 | * @author Michael Hamann <[email protected]> |
||
728 | * @author Michael Klier <[email protected]> |
||
729 | * |
||
730 | * @param int $timestamp unix timestamp |
||
731 | * @return array |
||
732 | * @throws RemoteException no valid timestamp |
||
733 | */ |
||
734 | public function getRecentMediaChanges($timestamp) { |
||
760 | |||
761 | /** |
||
762 | * Returns a list of available revisions of a given wiki page |
||
763 | * Number of returned pages is set by $conf['recent'] |
||
764 | * However not accessible pages are skipped, so less than $conf['recent'] could be returned |
||
765 | * |
||
766 | * @author Michael Klier <[email protected]> |
||
767 | * |
||
768 | * @param string $id page id |
||
769 | * @param int $first skip the first n changelog lines (0 = from current(if exists), 1 = from 1st old rev, 2 = from 2nd old rev, etc) |
||
770 | * @return array |
||
771 | * @throws RemoteAccessDeniedException no read access for page |
||
772 | * @throws RemoteException empty id |
||
773 | */ |
||
774 | public function pageVersions($id, $first) { |
||
827 | |||
828 | /** |
||
829 | * The version of Wiki RPC API supported |
||
830 | */ |
||
831 | public function wiki_RPCVersion(){ |
||
832 | return 2; |
||
833 | } |
||
834 | |||
835 | |||
836 | /** |
||
837 | * Locks or unlocks a given batch of pages |
||
838 | * |
||
839 | * Give an associative array with two keys: lock and unlock. Both should contain a |
||
840 | * list of pages to lock or unlock |
||
841 | * |
||
842 | * Returns an associative array with the keys locked, lockfail, unlocked and |
||
843 | * unlockfail, each containing lists of pages. |
||
844 | * |
||
845 | * @param array[] $set list pages with array('lock' => array, 'unlock' => array) |
||
846 | * @return array |
||
847 | */ |
||
848 | public function setLocks($set){ |
||
849 | $locked = array(); |
||
850 | $lockfail = array(); |
||
851 | $unlocked = array(); |
||
852 | $unlockfail = array(); |
||
853 | |||
854 | foreach((array) $set['lock'] as $id){ |
||
855 | $id = $this->resolvePageId($id); |
||
856 | if(auth_quickaclcheck($id) < AUTH_EDIT || checklock($id)){ |
||
857 | $lockfail[] = $id; |
||
858 | }else{ |
||
859 | lock($id); |
||
860 | $locked[] = $id; |
||
861 | } |
||
862 | } |
||
863 | |||
864 | foreach((array) $set['unlock'] as $id){ |
||
865 | $id = $this->resolvePageId($id); |
||
866 | if(auth_quickaclcheck($id) < AUTH_EDIT || !unlock($id)){ |
||
867 | $unlockfail[] = $id; |
||
868 | }else{ |
||
869 | $unlocked[] = $id; |
||
870 | } |
||
871 | } |
||
872 | |||
873 | return array( |
||
874 | 'locked' => $locked, |
||
875 | 'lockfail' => $lockfail, |
||
876 | 'unlocked' => $unlocked, |
||
877 | 'unlockfail' => $unlockfail, |
||
878 | ); |
||
879 | } |
||
880 | |||
881 | /** |
||
882 | * Return API version |
||
883 | * |
||
884 | * @return int |
||
885 | */ |
||
886 | public function getAPIVersion(){ |
||
889 | |||
890 | /** |
||
891 | * Login |
||
892 | * |
||
893 | * @param string $user |
||
894 | * @param string $pass |
||
895 | * @return int |
||
896 | */ |
||
897 | public function login($user,$pass){ |
||
898 | global $conf; |
||
899 | /** @var DokuWiki_Auth_Plugin $auth */ |
||
900 | global $auth; |
||
901 | |||
902 | if(!$conf['useacl']) return 0; |
||
903 | if(!$auth) return 0; |
||
904 | |||
905 | @session_start(); // reopen session for login |
||
1 ignored issue
–
show
|
|||
906 | if($auth->canDo('external')){ |
||
907 | $ok = $auth->trustExternal($user,$pass,false); |
||
908 | }else{ |
||
909 | $evdata = array( |
||
910 | 'user' => $user, |
||
911 | 'password' => $pass, |
||
912 | 'sticky' => false, |
||
913 | 'silent' => true, |
||
914 | ); |
||
915 | $ok = trigger_event('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper'); |
||
916 | } |
||
917 | session_write_close(); // we're done with the session |
||
918 | |||
919 | return $ok; |
||
920 | } |
||
921 | |||
922 | /** |
||
923 | * Log off |
||
924 | * |
||
925 | * @return int |
||
926 | */ |
||
927 | public function logoff(){ |
||
937 | |||
938 | /** |
||
939 | * Resolve page id |
||
940 | * |
||
941 | * @param string $id page id |
||
942 | * @return string |
||
943 | */ |
||
944 | private function resolvePageId($id) { |
||
945 | $id = cleanID($id); |
||
946 | if(empty($id)) { |
||
947 | global $conf; |
||
948 | $id = cleanID($conf['start']); |
||
949 | } |
||
950 | return $id; |
||
951 | } |
||
952 | |||
953 | } |
||
954 | |||
955 |
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.