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 DeviceConfig 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 DeviceConfig, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 48 | abstract class DeviceConfig { |
||
| 49 | /** |
||
| 50 | * stores the path to the temporary working directory for a module instance |
||
| 51 | * @var string $FPATH |
||
| 52 | */ |
||
| 53 | public $FPATH; |
||
| 54 | |||
| 55 | /** |
||
| 56 | * array of specialities - will be displayed on the admin download as "footnote" |
||
| 57 | * @var array specialities |
||
| 58 | */ |
||
| 59 | public $specialities; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * device module constructor should be defined by each module, but if it is not, then here is a default one |
||
| 63 | */ |
||
| 64 | |||
| 65 | public function __construct() { |
||
| 70 | |||
| 71 | |||
| 72 | /** |
||
| 73 | * Set up working environment for a device module |
||
| 74 | * |
||
| 75 | * Sets up the device module environment taking into account the actual profile |
||
| 76 | * selected by the user in the GUI. The selected profile is passed as the |
||
| 77 | * Profile $profile argumant. |
||
| 78 | * |
||
| 79 | * This method needs to be called after the device instance has been created (the GUI class does that) |
||
| 80 | * |
||
| 81 | * setup performs the following tasks: |
||
| 82 | * - collect profile attributes and pass them as the attributes property; |
||
| 83 | * - create the temporary working directory |
||
| 84 | * - process CA certificates and store them as 'internal:CAs' attribute |
||
| 85 | * - process and save optional info files and store references to them in |
||
| 86 | * 'internal:info_file' attribute |
||
| 87 | * @param Profile $profile the profile object which will be passed by the caller |
||
| 88 | * @final not to be redefined |
||
| 89 | */ |
||
| 90 | final public function setup(Profile $profile) { |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Selects the preferred eap method based on profile EAP configuration and device EAP capabilities |
||
| 140 | * |
||
| 141 | * @param array eap_array an array of eap methods supported by a given device |
||
| 142 | * @return the best matching EAP type for the profile; or 0 if no match was found |
||
| 143 | */ |
||
| 144 | public function getPreferredEapType($eap_array) { |
||
| 155 | /** |
||
| 156 | * prepare usage information for the installer |
||
| 157 | * every device module should override this method |
||
| 158 | * |
||
| 159 | * @return String HTML text to be displayed |
||
| 160 | */ |
||
| 161 | public function writeDeviceInfo() { |
||
| 164 | |||
| 165 | /** |
||
| 166 | * Copy a file from the module location to the temporary directory. |
||
| 167 | * |
||
| 168 | * If the second argument is provided then the file will be saved under the name |
||
| 169 | * taken form this argument. If only one parameter is given, source and destination |
||
| 170 | * filenames are the same |
||
| 171 | * Source file can be located either in the Files subdirectory or in the sibdirectory of Files |
||
| 172 | * named the same as device_id. The second option takes precedence. |
||
| 173 | * |
||
| 174 | * @param string $source_name The source file name |
||
| 175 | * @param string $output_name The destination file name |
||
| 176 | * |
||
| 177 | * @return bool result of the copy operation |
||
| 178 | * @final not to be redefined |
||
| 179 | */ |
||
| 180 | final protected function copyFile($source_name, $output_name = 0) { |
||
| 199 | |||
| 200 | |||
| 201 | /** |
||
| 202 | * Copy a file from the module location to the temporary directory aplying transcoding. |
||
| 203 | * |
||
| 204 | * Transcoding is only required for Windows installers, and no Unicode support |
||
| 205 | * in NSIS (NSIS version below 3) |
||
| 206 | * Trancoding is only applied if the third optional parameter is set and nonzero |
||
| 207 | * If Config::$NSIS_VERSION is set to 3 or more, no transcoding will be applied |
||
| 208 | * regardless of the third parameter value. |
||
| 209 | * If the second argument is provided and is not equal to 0, then the file will be |
||
| 210 | * saved under the name taken from this argument. |
||
| 211 | * If only one parameter is given or the second is equal to 0, source and destination |
||
| 212 | * filenames are the same. |
||
| 213 | * The third optional parameter, if nonzero, should be the character set understood by iconv |
||
| 214 | * This is required by the Windows installer and is expected to go away in the future. |
||
| 215 | * Source file can be located either in the Files subdirectory or in the sibdirectory of Files |
||
| 216 | * named the same as device_id. The second option takes precedence. |
||
| 217 | * |
||
| 218 | * @param string $source_name The source file name |
||
| 219 | * @param string $output_name The destination file name |
||
| 220 | * @param int $use_win_cp Set Windows charset if non-zero |
||
| 221 | * |
||
| 222 | * @final not to be redefined |
||
| 223 | */ |
||
| 224 | |||
| 225 | final protected function translateFile($source_name, $output_name = 0, $encoding = 0) { |
||
| 252 | |||
| 253 | |||
| 254 | /** |
||
| 255 | * Transcode a string adding double quotes escaping |
||
| 256 | * |
||
| 257 | * Transcoding is only required for Windows installers, and no Unicode support |
||
| 258 | * in NSIS (NSIS version below 3) |
||
| 259 | * Trancoding is only applied if the third optional parameter is set and nonzero |
||
| 260 | * If Config::$NSIS_VERSION is set to 3 or more, no transcoding will be applied |
||
| 261 | * regardless of the second parameter value. |
||
| 262 | * The second optional parameter, if nonzero, should be the character set understood by iconv |
||
| 263 | * This is required by the Windows installer and is expected to go away in the future. |
||
| 264 | * |
||
| 265 | * @param string $source_name The source file name |
||
| 266 | * @param int $use_win_cp Set Windows charset if non-zero |
||
| 267 | * |
||
| 268 | * @final not to be redefined |
||
| 269 | */ |
||
| 270 | |||
| 271 | final protected function translateString($source_string,$encoding = 0) { |
||
| 284 | |||
| 285 | |||
| 286 | /** |
||
| 287 | * Save certificate files in either DER or PEM format |
||
| 288 | * |
||
| 289 | * Certificate files will be saved in the module working directory. |
||
| 290 | * @param string $format only "der" and "pem" are currently allowed |
||
| 291 | * @return array an array of arrays or FALSE on error |
||
| 292 | * saved certificate file names are avalable under the 'file' index |
||
| 293 | * additional array entries are indexed as 'sha1', 'md5', and 'root'. |
||
| 294 | * sha1 and md5 are correcponding certificate hashes |
||
| 295 | * root is set to 1 for the CA roor certicicate and 0 otherwise |
||
| 296 | */ |
||
| 297 | final protected function saveCertificateFiles($format) { |
||
| 327 | |||
| 328 | /** |
||
| 329 | * Generate installer filename base. |
||
| 330 | * Device module should use this name adding an extension. |
||
| 331 | * Normally the device identifier follows the Consortium name. |
||
| 332 | * The sting taken for the device identifier equals (by default) to the index in the listDevices array, |
||
| 333 | * but can be overriden with the 'device_id' device option. |
||
| 334 | */ |
||
| 335 | private function getInstallerBasename() { |
||
| 357 | |||
| 358 | private function getDeviceId() { |
||
| 366 | |||
| 367 | |||
| 368 | private function getSSIDs() { |
||
| 399 | |||
| 400 | private function getConsortia() { |
||
| 408 | |||
| 409 | /** |
||
| 410 | * An array with shorthand definitions for MIME types |
||
| 411 | * @var array |
||
| 412 | */ |
||
| 413 | private $mime_extensions = [ |
||
| 414 | 'text/plain' => 'txt', |
||
| 415 | 'text/rtf' => 'rtf', |
||
| 416 | 'application/pdf' =>'pdf', |
||
| 417 | ]; |
||
| 418 | |||
| 419 | private function saveLogoFile($Logos) { |
||
| 443 | |||
| 444 | |||
| 445 | private function saveInfoFile($blob) { |
||
| 456 | |||
| 457 | private function getProfileAttributes(Profile $profile) { |
||
| 469 | /** |
||
| 470 | * dumps attributes for debugging purposes |
||
| 471 | * |
||
| 472 | * dumpAttibutes method is supplied for debuging purposes, it simply dumps the attribute array |
||
| 473 | * to a file with name passed in the attribute. |
||
| 474 | * @param string $file the output file name |
||
| 475 | */ |
||
| 476 | protected function dumpAttibutes($file) { |
||
| 484 | /** |
||
| 485 | * placeholder for the main device method |
||
| 486 | * |
||
| 487 | */ |
||
| 488 | |||
| 489 | protected function writeInstaller() { |
||
| 492 | |||
| 493 | /** |
||
| 494 | * Array passing all options to the device module. |
||
| 495 | * |
||
| 496 | * $attrbutes array contains option values defined for the institution and a particular |
||
| 497 | * profile (possibly overriding one another) ready for the device module to consume. |
||
| 498 | * |
||
| 499 | * For each of the options the value is another array of vales (even if only one value is present). |
||
| 500 | * Some attributes may be missing if they have not been configured for a viven institution or profile. |
||
| 501 | * |
||
| 502 | * The following attributes are meant to be used by device modules: |
||
| 503 | * - <b>general:geo_coordinates</b> - geographical coordinates of the institution or a campus |
||
| 504 | * - <b>support:info_file</b> - consent file displayed to the users |
||
| 505 | * - <b>general:logo_file</b> - file data containing institution logo |
||
| 506 | * - <b>support:eap_types</b> - URL to a local support page for a specific eap methiod, not to be confused with general:url |
||
| 507 | * - <b>support:email</b> - email for users to contact for local instructions |
||
| 508 | * - <b>support:phone</b> - telephone number for users to contact for local instructions |
||
| 509 | * - <b>support:url</b> - URL where the user will find local instructions |
||
| 510 | * - <b>internal:info_file</b> - the pathname of the info_file saved in the working directory |
||
| 511 | * - <b>internal:logo_file</b> - array of pathnames of logo_files saved in the working directory |
||
| 512 | * - <b>internal:CAs</b> - the value is an array produced by X509::processCertificate() with the following filds |
||
| 513 | * - <b>internal:SSID</b> - an array indexed by SSID strings with values either TKIP or AES; if TKIP is set the both WPA/TKIP and WPA2/AES should be set if AES is set the this is a WPA2/AES only SSID; the consortium's defined SSIDs are always set as the first array elements. |
||
| 514 | * -<b>internal:profile_count</b> - the number of profiles for the associated IdP |
||
| 515 | * |
||
| 516 | * |
||
| 517 | * these attributes are available and can be used, but the "internal" attributes are better suited for modules |
||
| 518 | * - eap:ca_file - certificate of the CA signing the RADIUS server key |
||
| 519 | * - <b>media:SSID</b> - additional SSID to configure, WPA2/AES only (device modules should use internal:SSID) |
||
| 520 | * - <b>media:SSID_with_legacy</b> - additional SSID to configure, WPA2/AES and WPA/TKIP (device modules should use internal:SSID) |
||
| 521 | * |
||
| 522 | * @see X509::processCertificate() |
||
| 523 | * @var array $attributes |
||
| 524 | */ |
||
| 525 | public $attributes; |
||
| 526 | /** |
||
| 527 | * stores the path to the module source location and is used |
||
| 528 | * by copyFile and translateFile |
||
| 529 | * the only reason for it to be a public variable ies that it is set by the DeviceFactory class |
||
| 530 | * module_path should not be used by module drivers. |
||
| 531 | * @var string |
||
| 532 | */ |
||
| 533 | public $module_path; |
||
| 534 | |||
| 535 | /** |
||
| 536 | * The optimal EAP type |
||
| 537 | * |
||
| 538 | */ |
||
| 539 | /** |
||
| 540 | * optimal EAP method selected given profile and device |
||
| 541 | * @var EAP::constant |
||
| 542 | */ |
||
| 543 | public $selected_eap; |
||
| 544 | /** |
||
| 545 | * the path to the profile signing program |
||
| 546 | * device modules which require signing should use this property to exec the signer |
||
| 547 | * the signer program must accept two arguments - input and output file names |
||
| 548 | * the signer program mus operate in the local directory and filenames are relative to this |
||
| 549 | * directory |
||
| 550 | * |
||
| 551 | *@var string |
||
| 552 | */ |
||
| 553 | public $sign; |
||
| 554 | public $signer; |
||
| 555 | /** |
||
| 556 | * the string referencing the language (index ot the Config::$LANGUAGES array). |
||
| 557 | * It is set to the current language and may be used by the device module to |
||
| 558 | * set its language |
||
| 559 | * |
||
| 560 | *@var string |
||
| 561 | */ |
||
| 562 | public $lang_index; |
||
| 563 | /** |
||
| 564 | * The string identifier of the device (don't show this to users) |
||
| 565 | * @var string |
||
| 566 | */ |
||
| 567 | public $device_id; |
||
| 568 | |||
| 569 | /** |
||
| 570 | * See devices-template.php for a list of available options |
||
| 571 | * @var array |
||
| 572 | */ |
||
| 573 | public $options; |
||
| 574 | |||
| 575 | /** |
||
| 576 | * This string will be shown if no support email was configured by the admin |
||
| 577 | * |
||
| 578 | * @var string |
||
| 579 | */ |
||
| 580 | public static $support_email_substitute; |
||
| 581 | |||
| 582 | /** |
||
| 583 | * This string will be shown if no support URL was configured by the admin |
||
| 584 | * |
||
| 585 | * @var string |
||
| 586 | */ |
||
| 587 | public static $support_url_substitute; |
||
| 588 | |||
| 589 | /** |
||
| 590 | * This string should be used by all installer modules to set the |
||
| 591 | * installer file basename. |
||
| 592 | * |
||
| 593 | * @var string |
||
| 594 | */ |
||
| 595 | public static $installerBasename; |
||
| 596 | } |
||
| 597 |
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: