Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like ISBN 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 ISBN, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 121 | class ISBN |
||
| 122 | { |
||
| 123 | var $groups_csv = "data/groups.csv"; |
||
| 124 | |||
| 125 | /** |
||
| 126 | * @var string ISBN Registration Group |
||
| 127 | */ |
||
| 128 | var $isbn_group = ''; |
||
| 129 | /** |
||
| 130 | * @var string ISBN Publisher |
||
| 131 | */ |
||
| 132 | var $isbn_publisher = ''; |
||
| 133 | /** |
||
| 134 | * @var string ISBN Title |
||
| 135 | */ |
||
| 136 | var $isbn_title = ''; |
||
| 137 | |||
| 138 | /** |
||
| 139 | * @var mixed ISBN number version |
||
| 140 | */ |
||
| 141 | var $ver = ISBN_VERSION_NONE; |
||
| 142 | |||
| 143 | /** |
||
| 144 | * @var array ISBN Groups Data acting as cache |
||
| 145 | * @see _getISBN10Groups() |
||
| 146 | */ |
||
| 147 | var $varISBN10Groups = array(); |
||
| 148 | |||
| 149 | // {{{ __construct |
||
| 150 | /** |
||
| 151 | * Constructor |
||
| 152 | * |
||
| 153 | * @param array $isbn String of ISBN Value to use |
||
| 154 | * @param mixed $ver Optional Version Constant |
||
| 155 | * |
||
| 156 | * @access public |
||
| 157 | * |
||
| 158 | * @throws ISBN_Exception in case it fails |
||
| 159 | */ |
||
| 160 | function __construct($isbn = '', $ver = ISBN_DEFAULT_INPUTVERSION) |
||
| 211 | // }}} |
||
| 212 | |||
| 213 | // {{{ _extractCheckdigit() |
||
| 214 | /** |
||
| 215 | * extract Checkdigit of an ISBN-Number |
||
| 216 | * |
||
| 217 | * @param string $isbnn normalized ISBN string |
||
| 218 | * |
||
| 219 | * @return string|false ISBN-Body or false if failed |
||
| 220 | * |
||
| 221 | */ |
||
| 222 | function _extractCheckdigit($isbnn) |
||
| 228 | // }}} |
||
| 229 | |||
| 230 | // {{{ _extractEANPrefix() |
||
| 231 | /** |
||
| 232 | * extracts EAN-Prefix of a normalized isbn string |
||
| 233 | * |
||
| 234 | * @param string $isbnn normalized isbn string |
||
| 235 | * |
||
| 236 | * @return string|false Prefix or false if failed |
||
| 237 | */ |
||
| 238 | function _extractEANPrefix($isbnn) |
||
| 250 | // }}} |
||
| 251 | |||
| 252 | // {{{ _extractGroup() |
||
| 253 | /** |
||
| 254 | * extract Registration Group of an ISBN-Body |
||
| 255 | * |
||
| 256 | * @param string $isbnbody ISBN-Body |
||
| 257 | * |
||
| 258 | * @return integer|false Registration Group or false if failed |
||
| 259 | */ |
||
| 260 | function _extractGroup($isbnbody) |
||
| 271 | // }}} |
||
| 272 | |||
| 273 | // {{{ _extractISBNBody() |
||
| 274 | /** |
||
| 275 | * extract ISBN-Body of an ISBN-Number |
||
| 276 | * |
||
| 277 | * @param string $isbnn normalized ISBN string |
||
| 278 | * |
||
| 279 | * @return string|false ISBN-Body or false if failed |
||
| 280 | */ |
||
| 281 | function _extractISBNBody($isbnn) |
||
| 308 | // }}} |
||
| 309 | |||
| 310 | // {{{ _isbnBodyParts() |
||
| 311 | /** |
||
| 312 | * Get the 2 Parts of the ISBN-Body (ISBN-10/ISBN-13-978) |
||
| 313 | * |
||
| 314 | * @param string $isbnbody ISBN-Body |
||
| 315 | * @param string &$registrationgroup Registration Group |
||
| 316 | * @param string &$isbnsubbody ISBN-Subbody |
||
| 317 | * |
||
| 318 | * @return boolean False if failed, True on success |
||
| 319 | * |
||
| 320 | * @access private |
||
| 321 | */ |
||
| 322 | function _isbnBodyParts($isbnbody, |
||
| 371 | // }}} |
||
| 372 | |||
| 373 | // {{{ _isbnSubbodyParts() |
||
| 374 | /** |
||
| 375 | * Get the 2 Parts of the ISBN-Subbody (ISBN-10/ISBN-13) |
||
| 376 | * |
||
| 377 | * @param string $isbnsubbody ISBN-Subbody |
||
| 378 | * @param integer $groupid Registrationgroup |
||
| 379 | * @param string &$registrant Registrant |
||
| 380 | * @param string &$publication Publication |
||
| 381 | * |
||
| 382 | * @return boolean False if failed, true on success |
||
| 383 | * |
||
| 384 | * @access private |
||
| 385 | */ |
||
| 386 | function _isbnSubbodyParts($isbnsubbody, |
||
| 432 | // }}} |
||
| 433 | |||
| 434 | // {{{ _getRegistrantLength() |
||
| 435 | /** |
||
| 436 | * Return Length of Registrant part within an ISBNSubbody in a specific |
||
| 437 | * grouprange in this specific format: |
||
| 438 | * |
||
| 439 | * '00-09;10-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999' |
||
| 440 | * |
||
| 441 | * Info: This function is compatible with Groupranges formatted in the |
||
| 442 | * .js file and might become obsolete if new formats are more fitting. |
||
| 443 | * |
||
| 444 | * @param string $isbnsubbody ISBN-Subbody |
||
| 445 | * @param string $grouprange Grouprange in the Format '#a1-#z1;#a2-z2[...]' |
||
| 446 | * |
||
| 447 | * @return boolean|int False if failed or Length (in chars) of Registrant |
||
| 448 | * |
||
| 449 | * @access private |
||
| 450 | */ |
||
| 451 | function _getRegistrantLength($isbnsubbody, $grouprange) |
||
| 500 | // }}} |
||
| 501 | |||
| 502 | // {{{ _getISBN10Group() |
||
| 503 | /** |
||
| 504 | * Get ISBN-10 Registration Group Data by its numeric ID |
||
| 505 | * |
||
| 506 | * @param integer $id Registration Group Identifier |
||
| 507 | * |
||
| 508 | * @return mixed array: group array |
||
| 509 | * boolean: False if failed |
||
| 510 | */ |
||
| 511 | function _getISBN10Group($id) |
||
| 527 | // }}} |
||
| 528 | |||
| 529 | // {{{ _getISBN10Groups() |
||
| 530 | /** |
||
| 531 | * Get all ISBN-10 Registration Groups |
||
| 532 | * |
||
| 533 | * @return array groups array |
||
| 534 | * |
||
| 535 | * Info: This function connects outer world data into this class logic |
||
| 536 | * which can be generated with the supplied tools. |
||
| 537 | * A user should not alter the array data. This data should be altered |
||
| 538 | * together with the international ISBN Agency only. |
||
| 539 | */ |
||
| 540 | function _getISBN10Groups() |
||
| 587 | // }}} |
||
| 588 | |||
| 589 | // {{{ _getVersion() |
||
| 590 | /** |
||
| 591 | * Get the Version of am ISBN Number |
||
| 592 | * |
||
| 593 | * @param string $isbn ISBN Number ofwhich the version to get |
||
| 594 | * |
||
| 595 | * @return mixed false for no, or fully identifyable ISBN |
||
| 596 | * Version Constant |
||
| 597 | * |
||
| 598 | * @access private |
||
| 599 | */ |
||
| 600 | function _getVersion($isbn) |
||
| 606 | // }}} |
||
| 607 | |||
| 608 | // {{{ _checkdigitISBN10() |
||
| 609 | /** |
||
| 610 | * Calculate checkdigit of an ISBN-10 string (ISBN-Body) |
||
| 611 | * as documented on pp.4-5 2001 handbook. |
||
| 612 | * |
||
| 613 | * @param string $isbnbody ISBN-Body |
||
| 614 | * |
||
| 615 | * @return string|false Checkdigit [0-9,X] or false if failed |
||
| 616 | * |
||
| 617 | * @access private |
||
| 618 | */ |
||
| 619 | function _checkdigitISBN10($isbnbody) |
||
| 648 | // }}} |
||
| 649 | |||
| 650 | // {{{ _checkdigitISBN13() |
||
| 651 | /** |
||
| 652 | * Calculate checkdigit of an ISBN-13 string (Prefix + ISBN-Body) |
||
| 653 | * as documented on pp.10-11 2005 handbook. |
||
| 654 | * |
||
| 655 | * @param string $isbnbody ISBN-Body |
||
| 656 | * @param string $prefix EAN-Prefix (Default 978 for ISBN13-978) |
||
| 657 | * |
||
| 658 | * @return string|false Checkdigit [0-9] or false if failed |
||
| 659 | * |
||
| 660 | * @access private |
||
| 661 | */ |
||
| 662 | function _checkdigitISBN13($isbnbody, $prefix = '978') |
||
| 704 | // }}} |
||
| 705 | |||
| 706 | // {{{ _isIsbnValid() |
||
| 707 | /** |
||
| 708 | * Validate an ISBN value |
||
| 709 | * |
||
| 710 | * @param string $isbn Number to validate |
||
| 711 | * @param string $ver Version to validate against |
||
| 712 | * |
||
| 713 | * @return integer|false Returns the Version to signal validity or false if |
||
| 714 | * ISBN number is not valid |
||
| 715 | * |
||
| 716 | * @access private |
||
| 717 | */ |
||
| 718 | function _isIsbnValid($isbn, $ver = ISBN_DEFAULT_INPUTVERSION) |
||
| 841 | // }}} |
||
| 842 | |||
| 843 | // {{{ _isbnVersionGuess() |
||
| 844 | /** |
||
| 845 | * Guesses the version of an ISBN |
||
| 846 | * |
||
| 847 | * @param string $isbn ISBN Number of which the Version to guess |
||
| 848 | * |
||
| 849 | * @return integer|false Version Value or false (ISBN_VERSION_NONE) if failed |
||
| 850 | * @access private |
||
| 851 | */ |
||
| 852 | function _isbnVersionGuess($isbn) |
||
| 864 | // }}} |
||
| 865 | |||
| 866 | // {{{ _isbnVersionIs() |
||
| 867 | /** |
||
| 868 | * Validate an ISBN Version value |
||
| 869 | * |
||
| 870 | * @param mixed $ver version to be checked being a valid ISBN Version |
||
| 871 | * |
||
| 872 | * @return bool true if value is valid, false if not |
||
| 873 | * |
||
| 874 | * @access private |
||
| 875 | */ |
||
| 876 | function _isbnVersionIs($ver) |
||
| 895 | // }}} |
||
| 896 | |||
| 897 | // {{{ _isbnVersionIsValid() |
||
| 898 | /** |
||
| 899 | * Validate an ISBN value being a valid (identifyable -10 / -13) value |
||
| 900 | * |
||
| 901 | * @param mixed $ver value to be checked being a valid ISBN Version |
||
| 902 | * |
||
| 903 | * @return bool true if value is valid, false if not |
||
| 904 | * |
||
| 905 | * @access private |
||
| 906 | */ |
||
| 907 | function _isbnVersionIsValid($ver) |
||
| 922 | // }}} |
||
| 923 | |||
| 924 | // {{{ _normaliseISBN() |
||
| 925 | /** |
||
| 926 | * downformat "any" ISBN Number to the very basics |
||
| 927 | * an isbn number is a 10 or 13 digit. with the |
||
| 928 | * 10 digit string, the last digit can be 0-9 and |
||
| 929 | * X as well, all other are 0-9 only |
||
| 930 | * additionally this fucntion can be used to validate |
||
| 931 | * the isbn against correct length and chars |
||
| 932 | * |
||
| 933 | * @param string $isbn ISBN String to normalise |
||
| 934 | * |
||
| 935 | * @return string|false normalised ISBN Number or false if the function was |
||
| 936 | * not able to normalise the input |
||
| 937 | * |
||
| 938 | * @access private |
||
| 939 | */ |
||
| 940 | function _normaliseISBN($isbn) |
||
| 991 | // }}} |
||
| 992 | |||
| 993 | // {{{ _normaliseISBNremoveLangSpecific() |
||
| 994 | /** |
||
| 995 | * helper function for _normaliseISBN to |
||
| 996 | * remove lang sepcific ISBN prefix |
||
| 997 | * |
||
| 998 | * @param string $isbn ISBN String to check (partially normalised) |
||
| 999 | * |
||
| 1000 | * @return string input value passed through helper |
||
| 1001 | * |
||
| 1002 | * @access private |
||
| 1003 | */ |
||
| 1004 | function _normaliseISBNremoveLangSpecific($isbn) |
||
| 1015 | // }}} |
||
| 1016 | |||
| 1017 | // {{{ convert() |
||
| 1018 | /** |
||
| 1019 | * converts an ISBN number from one version to another |
||
| 1020 | * can convert ISBN-10 to ISBN-13 and ISBN-13 to ISBN-10 |
||
| 1021 | * |
||
| 1022 | * @param string $isbnin ISBN to convert, must be a valid ISBN Number |
||
| 1023 | * @param integer $verfrom version value of the input ISBN |
||
| 1024 | * @param integer $verto version value to convert to |
||
| 1025 | * |
||
| 1026 | * @return string|false converted ISBN Number or false if conversion failed |
||
| 1027 | */ |
||
| 1028 | function convert($isbnin, $verfrom = ISBN_VERSION_ISBN_10, |
||
| 1069 | // }}} |
||
| 1070 | |||
| 1071 | // {{{ getCheckdigit() |
||
| 1072 | /** |
||
| 1073 | * Get the Checkdigit Part of ISBN Number |
||
| 1074 | * |
||
| 1075 | * @return string|false Checkdigit or false if failed |
||
| 1076 | */ |
||
| 1077 | function getCheckdigit() |
||
| 1095 | // }}} |
||
| 1096 | |||
| 1097 | // {{{ getEAN() |
||
| 1098 | /** |
||
| 1099 | * Get the EAN Prefix of ISBN Number (ISBN-13) |
||
| 1100 | * |
||
| 1101 | * @return string|false EAN Prefix or false if failed |
||
| 1102 | */ |
||
| 1103 | function getEAN() |
||
| 1117 | // }}} |
||
| 1118 | |||
| 1119 | // {{{ getGroup() |
||
| 1120 | /** |
||
| 1121 | * Get the Registrationgroup Part of the ISBN Number |
||
| 1122 | * |
||
| 1123 | * @return string|false Group Identifier or false if failed |
||
| 1124 | */ |
||
| 1125 | function getGroup() |
||
| 1129 | // }}} |
||
| 1130 | |||
| 1131 | // {{{ _setGroup() |
||
| 1132 | /** |
||
| 1133 | * Setter for the Registrationgroup Part of the ISBN Number |
||
| 1134 | * |
||
| 1135 | * @param string $group Registrationsgroup to set |
||
| 1136 | * |
||
| 1137 | * @return void |
||
| 1138 | * |
||
| 1139 | * @throws ISBN_Exception in case it fails |
||
| 1140 | */ |
||
| 1141 | function _setGroup($group) |
||
| 1160 | |||
| 1161 | // {{{ getISBN() |
||
| 1162 | /** |
||
| 1163 | * Get whole ISBN Number |
||
| 1164 | * |
||
| 1165 | * @return string ISBN Number (unformatted); empty string if this is |
||
| 1166 | * not a valid ISBN |
||
| 1167 | */ |
||
| 1168 | function getISBN() |
||
| 1188 | // }}} |
||
| 1189 | |||
| 1190 | // {{{ getISBNDisplayable() |
||
| 1191 | /** |
||
| 1192 | * Get whole ISBN Number in a displayable fashion (see Handbook p. 15) |
||
| 1193 | * |
||
| 1194 | * @param string $format Formatstring 1-4 Chars: |
||
| 1195 | * each character is a control char: |
||
| 1196 | * #1 i or not: use international pre-prefix |
||
| 1197 | * #2 i or not: "ISBN" in front or v: incl. version |
||
| 1198 | * #3 : or not: insert a ":" |
||
| 1199 | * #4 - or not: use - after EAN (ISBN 13 only) |
||
| 1200 | * #4 or =: use - between each ISBN part |
||
| 1201 | * Example 1: |
||
| 1202 | * ' --' 978-0-385-33941-4 |
||
| 1203 | * classic displayable ISBN |
||
| 1204 | * Example 2: |
||
| 1205 | * ' v:-' ISBN-13: 978-0385339414 |
||
| 1206 | * ISBN-Format used by amazon |
||
| 1207 | * Example 3: |
||
| 1208 | * 'iv:=' ISBN-13: 978-0-385-33941-4 |
||
| 1209 | * full blown: more is more! |
||
| 1210 | * |
||
| 1211 | * @return string ISBN Number (formatted); empty string if this is |
||
| 1212 | * not a valid ISBN |
||
| 1213 | */ |
||
| 1214 | function getISBNDisplayable($format = '') |
||
| 1277 | // }}} |
||
| 1278 | |||
| 1279 | // {{{ setISBN() |
||
| 1280 | /** |
||
| 1281 | * Setter for ISBN |
||
| 1282 | * |
||
| 1283 | * @param string $isbn ISBN Number |
||
| 1284 | * this is a valid ISBN Number or it is an Empty string |
||
| 1285 | * which will reset the class |
||
| 1286 | * |
||
| 1287 | * @return void |
||
| 1288 | * |
||
| 1289 | * @throws ISBN_Exception in case it fails |
||
| 1290 | * |
||
| 1291 | */ |
||
| 1292 | function setISBN($isbn) |
||
| 1325 | // }}} |
||
| 1326 | |||
| 1327 | // {{{ _getISBNBody() |
||
| 1328 | /** |
||
| 1329 | * _getISBNBody() |
||
| 1330 | * |
||
| 1331 | * @return string ISBN Body (not an offical term) |
||
| 1332 | */ |
||
| 1333 | function _getISBNBody() |
||
| 1340 | // }}} |
||
| 1341 | |||
| 1342 | // {{{ _setISBNBody() |
||
| 1343 | /** |
||
| 1344 | * _setISBNBody() |
||
| 1345 | * |
||
| 1346 | * Setter for ISBNBody |
||
| 1347 | * |
||
| 1348 | * @param string $body ISBNBody |
||
| 1349 | * |
||
| 1350 | * @return void |
||
| 1351 | * |
||
| 1352 | * @throws ISBN_Exception in case it fails |
||
| 1353 | */ |
||
| 1354 | function _setISBNBody($body) |
||
| 1388 | // }}} |
||
| 1389 | |||
| 1390 | // {{{ _getISBNSubbody() |
||
| 1391 | /** |
||
| 1392 | * Get ISBNSubbody () |
||
| 1393 | * |
||
| 1394 | * @return ISBN Subbody |
||
| 1395 | */ |
||
| 1396 | function _getISBNSubbody() |
||
| 1403 | // }}} |
||
| 1404 | |||
| 1405 | // {{{ _setISBNSubbody() |
||
| 1406 | /** |
||
| 1407 | * _setISBNSubbody |
||
| 1408 | * |
||
| 1409 | * Setter for the ISBN Subbody |
||
| 1410 | * |
||
| 1411 | * @param string $subbody ISBN Subbody |
||
| 1412 | * |
||
| 1413 | * @return void |
||
| 1414 | * |
||
| 1415 | * @throws ISBN_Exception in case it fails |
||
| 1416 | */ |
||
| 1417 | function _setISBNSubbody($subbody) |
||
| 1440 | |||
| 1441 | // {{{ getPublisher() |
||
| 1442 | /** |
||
| 1443 | * Get the Publication Part of the ISBN Number |
||
| 1444 | * |
||
| 1445 | * @return string|false Publisher or false if failed |
||
| 1446 | */ |
||
| 1447 | function getPublisher() |
||
| 1451 | // }}} |
||
| 1452 | |||
| 1453 | // {{{ getTitle() |
||
| 1454 | /** |
||
| 1455 | * Get the Title Part of the ISBN Number |
||
| 1456 | * |
||
| 1457 | * @return string|false Title or false if failed |
||
| 1458 | */ |
||
| 1459 | function getTitle() |
||
| 1463 | // }}} |
||
| 1464 | |||
| 1465 | |||
| 1466 | // {{{ isValid() |
||
| 1467 | /** |
||
| 1468 | * Returns this ISBN validity |
||
| 1469 | * |
||
| 1470 | * @return boolean |
||
| 1471 | */ |
||
| 1472 | function isValid() |
||
| 1478 | |||
| 1479 | // {{{ validate() |
||
| 1480 | /** |
||
| 1481 | * Validates an ISBN |
||
| 1482 | * |
||
| 1483 | * @param string $isbn ISBN to validate |
||
| 1484 | * @param integer $ver ISBN-Version to validate against |
||
| 1485 | * |
||
| 1486 | * @return integer|false Version value of a valid ISBN or false |
||
| 1487 | * if it did not validate |
||
| 1488 | */ |
||
| 1489 | function validate($isbn, $ver = ISBN_DEFAULT_INPUTVERSION) |
||
| 1507 | // }}} |
||
| 1508 | |||
| 1509 | // {{{ getVersion() |
||
| 1510 | /** |
||
| 1511 | * Returns version of this objects ISBN |
||
| 1512 | * |
||
| 1513 | * @return integer|false Version value or ISBN_VERSION_NONE |
||
| 1514 | */ |
||
| 1515 | function getVersion() |
||
| 1519 | |||
| 1520 | |||
| 1521 | // {{{ guessVersion() |
||
| 1522 | /** |
||
| 1523 | * Guesses ISBN version of passed string |
||
| 1524 | * |
||
| 1525 | * Note: This is not Validation. To get the validated |
||
| 1526 | * version of an ISBN Number use ISBN::validate(); |
||
| 1527 | * |
||
| 1528 | * @param string $isbn ISBN Number to guess Version of |
||
| 1529 | * |
||
| 1530 | * @return integer|false Version Value or false if failed |
||
| 1531 | * |
||
| 1532 | * @see validate(); |
||
| 1533 | */ |
||
| 1534 | function guessVersion($isbn) |
||
| 1539 | // }}} |
||
| 1540 | |||
| 1541 | } |
||
| 1542 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: