Complex classes like Rule_UserName 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 Rule_UserName, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
324 | class Rule_UserName |
||
325 | extends Rule { |
||
326 | |||
327 | const RESERVED_PREFIX = 'forge__'; |
||
328 | |||
329 | /** |
||
330 | * Test if value is a name on underlying OS. |
||
331 | * |
||
332 | * @param String $val Value to test |
||
333 | * |
||
334 | * @return Boolean |
||
335 | */ |
||
336 | public function isSystemName($val) { |
||
337 | $backend = $this->_getBackend(); |
||
338 | if ($backend->unixUserExists($val) || $backend->unixGroupExists($val)) { |
||
339 | $this->error = $this->_getErrorExists(); |
||
340 | return true; |
||
341 | } |
||
342 | return false; |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * Test is the value is Codendi username |
||
347 | * |
||
348 | * @param String $val Value to test |
||
349 | * |
||
350 | * @return Boolean |
||
351 | */ |
||
352 | public function isAlreadyUserName($val) { |
||
353 | $um = $this->_getUserManager(); |
||
354 | if ($um->getUserByUserName($val) !== null) { |
||
355 | $this->error = $this->_getErrorExists(); |
||
356 | return true; |
||
357 | } |
||
358 | return false; |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * Test if the value is a project name |
||
363 | * |
||
364 | * @param String $val Value to test |
||
365 | * |
||
366 | * @return Boolean |
||
367 | */ |
||
368 | public function isAlreadyProjectName($val) { |
||
369 | $pm = $this->_getProjectManager(); |
||
370 | if ($pm->getProjectByUnixName($val) !== null) { |
||
371 | $this->error = $this->_getErrorExists(); |
||
372 | return true; |
||
373 | } |
||
374 | return false; |
||
375 | } |
||
376 | |||
377 | /** |
||
378 | * Test if the value contains spaces |
||
379 | * |
||
380 | * @param String $val Value to test |
||
381 | * |
||
382 | * @return Boolean |
||
383 | */ |
||
384 | public function noSpaces($val) { |
||
385 | if (strrpos($val,' ') !== false) { |
||
386 | $this->error = $this->_getErrorNoSpaces(); |
||
387 | return false; |
||
388 | } |
||
389 | return true; |
||
390 | } |
||
391 | |||
392 | /** |
||
393 | * Needs to check the name start by a char |
||
394 | * |
||
395 | * @param String $val |
||
396 | * |
||
397 | * @return Boolean |
||
398 | */ |
||
399 | public function atLeastOneChar($val) { |
||
400 | if (strspn($val,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0) { |
||
401 | $this->error = $GLOBALS['Language']->getText('include_account','char_err'); |
||
402 | return false; |
||
403 | } |
||
404 | return true; |
||
405 | } |
||
406 | |||
407 | /** |
||
408 | * Test if the name contains illegal chars |
||
409 | * |
||
410 | * @param String $val Value to test |
||
411 | * |
||
412 | * @return Boolean |
||
413 | */ |
||
414 | public function containsIllegalChars($val) { |
||
415 | if (strspn($val,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.") != strlen($val)) { |
||
416 | $this->error = $GLOBALS['Language']->getText('include_account','illegal_char'); |
||
417 | return true; |
||
418 | } |
||
419 | return false; |
||
420 | } |
||
421 | |||
422 | /** |
||
423 | * Test if the name is already reserved |
||
424 | * |
||
425 | * @param String $val Value to test |
||
426 | * |
||
427 | * @return Boolean |
||
428 | */ |
||
429 | public function isReservedName($val) { |
||
430 | $is_reserved_name = preg_match('/^('. |
||
431 | '(www[0-9]?)|(cvs[0-9]?)|(shell[0-9]?)|(ftp[0-9]?)|(irc[0-9]?)|(news[0-9]?)'. |
||
432 | '|(mail[0-9]?)|(ns[0-9]?)|(download[0-9]?)|(pub)|(users)|(compile)|(lists)'. |
||
433 | '|(slayer)|(orbital)|(tokyojoe)|(webdev)|(projects)|(cvs)|(monitor)|(mirrors?)'. |
||
434 | '|(root)|(bin)|(daemon)|(adm)|(lp)|(sync)|(shutdown)|(halt)|(mail)'. |
||
435 | '|(uucp)|(operator)|(games)|(mysql)|(httpd)|(nobody)|(dummy)|(debian)'. |
||
436 | '|(munin)|(mailman)|(ftpadmin)|(codendiadm)|(imadmin-bot)|(apache)|(nscd)'. |
||
437 | '|(git)|(gitolite)'. |
||
438 | ')$/i', $val); |
||
439 | $is_reserved_prefix = $this->isReservedPrefix($val); |
||
440 | |||
441 | if ($is_reserved_name || $is_reserved_prefix) { |
||
442 | $this->error = $GLOBALS['Language']->getText('include_account','reserved'); |
||
443 | return true; |
||
444 | } |
||
445 | return false; |
||
446 | } |
||
447 | |||
448 | /** |
||
449 | * Test if the name begins with a reserved prefix |
||
450 | * |
||
451 | * @param string $val Value to test |
||
452 | * |
||
453 | * @return bool |
||
454 | */ |
||
455 | private function isReservedPrefix($val) { |
||
456 | if (strpos($val, self::RESERVED_PREFIX) === 0) { |
||
457 | return true; |
||
458 | } |
||
459 | return false; |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * Test if the name corresponds to a CVS user account |
||
464 | * |
||
465 | * @param String $val Value to test |
||
466 | * |
||
467 | * @return Boolean |
||
468 | */ |
||
469 | public function isCvsAccount($val) { |
||
470 | if (preg_match('/^anoncvs_/i', $val)) { |
||
471 | $this->error = $GLOBALS['Language']->getText('include_account','reserved_cvs'); |
||
472 | return true; |
||
473 | } |
||
474 | return false; |
||
475 | } |
||
476 | |||
477 | /** |
||
478 | * Test minimal length of name |
||
479 | * |
||
480 | * @param String $val Value to test |
||
481 | * |
||
482 | * @return Boolean |
||
483 | */ |
||
484 | public function lessThanMin($val) { |
||
485 | if (strlen($val) < 3) { |
||
486 | $this->error = $GLOBALS['Language']->getText('include_account','name_too_short'); |
||
487 | return true; |
||
488 | } |
||
489 | return false; |
||
490 | } |
||
491 | |||
492 | /** |
||
493 | * Test maximal length of name |
||
494 | * |
||
495 | * @param String $val Value to test |
||
496 | * @param Integer $max maximal length (default = 30) |
||
497 | * |
||
498 | * @return Boolean |
||
499 | */ |
||
500 | public function greaterThanMax($val, $max = 30) { |
||
501 | if (strlen($val) > $max) { |
||
502 | $this->error = $GLOBALS['Language']->getText('include_account','name_too_long', $max); |
||
503 | return true; |
||
504 | } |
||
505 | return false; |
||
506 | } |
||
507 | /** |
||
508 | * Prevent from renaming two users on the same name |
||
509 | * before that the rename is performed by the system |
||
510 | * |
||
511 | * @param String $val |
||
512 | */ |
||
513 | public function getPendingUserRename($val) { |
||
514 | $sm = $this->_getSystemEventManager(); |
||
515 | if (!$sm->isUserNameAvailable($val)) { |
||
516 | $this->error = $GLOBALS['Language']->getText('rule_user_name', 'error_event_reserved', array($val)); |
||
517 | return false; |
||
518 | } |
||
519 | return true; |
||
520 | } |
||
521 | /** |
||
522 | * Test if name is valid |
||
523 | * |
||
524 | * @param String $val Value to test |
||
525 | * |
||
526 | * @return Boolean |
||
527 | */ |
||
528 | public function isValid($val) { |
||
529 | return $this->noSpaces($val) |
||
530 | && $this->atLeastOneChar($val) |
||
531 | && !$this->isReservedName($val) |
||
532 | && !$this->isCvsAccount($val) |
||
533 | && !$this->lessThanMin($val) |
||
534 | && !$this->greaterThanMax($val) |
||
535 | && !$this->containsIllegalChars($val) |
||
536 | && !$this->isAlreadyUserName($val) |
||
537 | && !$this->isAlreadyProjectName($val) |
||
538 | && !$this->isSystemName($val) |
||
539 | && $this->getPendingUserRename($val); |
||
540 | } |
||
541 | |||
542 | /** |
||
543 | * Error message |
||
544 | * |
||
545 | * @return String |
||
546 | */ |
||
547 | public function getErrorMessage() { |
||
550 | |||
551 | /** |
||
552 | * Returns error message when the username already exists |
||
553 | * |
||
554 | * Dedicate a method to be able to override it in descendent classes |
||
555 | * |
||
556 | * @param String $val Value to test |
||
557 | * |
||
558 | * @return Boolean |
||
559 | */ |
||
560 | protected function _getErrorExists() { |
||
563 | |||
564 | /** |
||
565 | * Returns error message when name contains a space |
||
566 | * |
||
567 | * Dedicate a method to be able to override it in descendent classes |
||
568 | * |
||
569 | * @param String $val Value to test |
||
570 | * |
||
571 | * @return Boolean |
||
572 | */ |
||
573 | protected function _getErrorNoSpaces() { |
||
576 | |||
577 | /** |
||
578 | * Wrapper |
||
579 | * |
||
580 | * @return ProjectManager |
||
581 | */ |
||
582 | protected function _getProjectManager() { |
||
585 | |||
586 | /** |
||
587 | * Wrapper |
||
588 | * |
||
589 | * @return UserManager |
||
590 | */ |
||
591 | protected function _getUserManager() { |
||
594 | |||
595 | /** |
||
596 | * Wrapper |
||
597 | * |
||
598 | * @return Backend |
||
599 | */ |
||
600 | protected function _getBackend($type='') { |
||
603 | |||
604 | /** |
||
605 | * Wrapper |
||
606 | * |
||
607 | * @return SystemEventManager |
||
608 | */ |
||
609 | protected function _getSystemEventManager() { |
||
612 | } |
||
613 | |||
614 | /** |
||
615 | * Check if a project name is valid |
||
616 | * |
||
617 | * This extends the user name validation |
||
618 | */ |
||
619 | class Rule_ProjectName |
||
620 | extends Rule_UserName { |
||
621 | |||
622 | /** |
||
623 | * Group name cannot contain underscore or dots for DNS reasons. |
||
624 | * |
||
625 | * @param String $val |
||
877 |
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.
Consider the following example. The parameter
$italy
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was removed, but the annotation was not.