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 IdP 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 IdP, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 43 | class IdP extends EntityWithDBProperties { |
||
| 44 | |||
| 45 | /** |
||
| 46 | * |
||
| 47 | * @var int synchronisation state with external database, if any |
||
| 48 | */ |
||
| 49 | private $externalDbSyncstate; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * The shortname of this IdP's federation |
||
| 53 | * @var string |
||
| 54 | */ |
||
| 55 | public $federation; |
||
| 56 | |||
| 57 | /** |
||
| 58 | * Constructs an IdP object based on its details in the database. |
||
| 59 | * Cannot be used to define a new IdP in the database! This happens via Federation::newIdP() |
||
| 60 | * |
||
| 61 | * @param integer $instId the database row identifier |
||
| 62 | */ |
||
| 63 | public function __construct($instId) { |
||
| 64 | debug(3, "--- BEGIN Constructing new IdP object ... ---\n"); |
||
| 65 | |||
| 66 | $this->databaseType = "INST"; |
||
| 67 | $this->entityOptionTable = "institution_option"; |
||
| 68 | $this->entityIdColumn = "inst_id"; |
||
| 69 | $this->identifier = $instId; |
||
| 70 | $this->attributes = []; |
||
| 71 | |||
| 72 | $idp = DBConnection::exec($this->databaseType, "SELECT inst_id, country,external_db_syncstate FROM institution WHERE inst_id = $this->identifier"); |
||
| 73 | if (!$attributeQuery = mysqli_fetch_object($idp)) { |
||
| 74 | throw new Exception("IdP $this->identifier not found in database!"); |
||
| 75 | } |
||
| 76 | |||
| 77 | $this->federation = $attributeQuery->country; |
||
| 78 | |||
| 79 | $optioninstance = Options::instance(); |
||
| 80 | |||
| 81 | $this->externalDbSyncstate = $attributeQuery->externalDbSyncstate; |
||
| 82 | // fetch attributes from DB and keep them in priv_attributes |
||
| 83 | |||
| 84 | $idPAttributes = DBConnection::exec($this->databaseType, "SELECT DISTINCT option_name,option_value, row FROM institution_option |
||
| 85 | WHERE institution_id = $this->identifier ORDER BY option_name"); |
||
| 86 | |||
| 87 | View Code Duplication | while ($attributeQuery = mysqli_fetch_object($idPAttributes)) { |
|
|
|
|||
| 88 | // decode base64 for files (respecting multi-lang) |
||
| 89 | $optinfo = $optioninstance->optionType($attributeQuery->option_name); |
||
| 90 | $flag = $optinfo['flag']; |
||
| 91 | |||
| 92 | if ($optinfo['type'] != "file") { |
||
| 93 | $this->attributes[] = ["name" => $attributeQuery->option_name, "value" => $attributeQuery->option_value, "level" => "IdP", "row" => $attributeQuery->row, "flag" => $flag]; |
||
| 94 | } else { |
||
| 95 | $decodedAttribute = $this->decodeFileAttribute($attributeQuery->option_value); |
||
| 96 | |||
| 97 | $this->attributes[] = ["name" => $attributeQuery->option_name, "value" => ($decodedAttribute['lang'] == "" ? $decodedAttribute['content'] : serialize($decodedAttribute)), "level" => "IdP", "row" => $attributeQuery->row, "flag" => $flag]; |
||
| 98 | } |
||
| 99 | } |
||
| 100 | $this->attributes[] = ["name" => "internal:country", |
||
| 101 | "value" => $this->federation, |
||
| 102 | "level" => "IdP", |
||
| 103 | "row" => 0, |
||
| 104 | "flag" => NULL]; |
||
| 105 | |||
| 106 | $this->name = getLocalisedValue($this->getAttributes('general:instname'), CAT::get_lang()); |
||
| 107 | debug(3, "--- END Constructing new IdP object ... ---\n"); |
||
| 108 | } |
||
| 109 | |||
| 110 | /** |
||
| 111 | * This function retrieves all registered profiles for this IdP from the database |
||
| 112 | * |
||
| 113 | * @return array List of Profiles of this IdP |
||
| 114 | * @param int $activeOnly if and set to non-zero will |
||
| 115 | * cause listing of only those institutions which have some valid profiles defined. |
||
| 116 | */ |
||
| 117 | public function listProfiles($activeOnly = 0) { |
||
| 118 | $query = "SELECT profile_id FROM profile WHERE inst_id = $this->identifier" . ($activeOnly ? " AND showtime = 1" : ""); |
||
| 119 | $allProfiles = DBConnection::exec($this->databaseType, $query); |
||
| 120 | $returnarray = []; |
||
| 121 | while ($profileQuery = mysqli_fetch_object($allProfiles)) { |
||
| 122 | $oneProfile = new Profile($profileQuery->profile_id, $this); |
||
| 123 | $oneProfile->institution = $this->identifier; |
||
| 124 | $returnarray[] = $oneProfile; |
||
| 125 | } |
||
| 126 | return $returnarray; |
||
| 127 | } |
||
| 128 | |||
| 129 | View Code Duplication | public function isOneProfileConfigured() { |
|
| 136 | |||
| 137 | View Code Duplication | public function isOneProfileShowtime() { |
|
| 144 | |||
| 145 | public function getAllProfileStatusOverview() { |
||
| 146 | $allProfiles = DBConnection::exec($this->databaseType, "SELECT status_dns, status_cert, status_reachability, status_TLS, last_status_check FROM profile WHERE inst_id = $this->identifier AND sufficient_config = 1"); |
||
| 167 | |||
| 168 | /** This function retrieves an array of authorised users which can |
||
| 169 | * manipulate this institution. |
||
| 170 | * |
||
| 171 | * @return array owners of the institution; numbered array with members ID, MAIL and LEVEL |
||
| 172 | */ |
||
| 173 | public function owner() { |
||
| 181 | |||
| 182 | /** |
||
| 183 | * This function gets the profile count for a given IdP |
||
| 184 | * The count could be retreived from the listProfiles method |
||
| 185 | * but this is less expensive. |
||
| 186 | * |
||
| 187 | * @return int profile count |
||
| 188 | */ |
||
| 189 | public function profileCount() { |
||
| 194 | |||
| 195 | /** |
||
| 196 | * This function sets the timestamp of last modification of the child profiles to the current timestamp. This is needed |
||
| 197 | * for installer caching: all installers which are on disk must be re-created if an attribute changes. This timestamp here |
||
| 198 | * is used to determine if the installer on disk is still new enough. |
||
| 199 | */ |
||
| 200 | public function updateFreshness() { |
||
| 205 | |||
| 206 | /** |
||
| 207 | * Adds a new profile to this IdP. |
||
| 208 | * Only creates the DB entry for the Profile. If you want to add attributes later, see Profile::addAttribute(). |
||
| 209 | * |
||
| 210 | * @return object new Profile object if successful, or FALSE if an error occured |
||
| 211 | */ |
||
| 212 | public function newProfile() { |
||
| 221 | |||
| 222 | /** |
||
| 223 | * deletes the IdP and all its profiles |
||
| 224 | */ |
||
| 225 | public function destroy() { |
||
| 259 | |||
| 260 | /** |
||
| 261 | * Performs a lookup in an external database to determine matching entities to this IdP. The business logic of this function is |
||
| 262 | * roaming consortium specific; if no match algorithm is known for the consortium, FALSE is returned. |
||
| 263 | * |
||
| 264 | * @return array list of entities in external database that correspond to this IdP or FALSE if no consortium-specific matching function is defined |
||
| 265 | */ |
||
| 266 | public function getExternalDBSyncCandidates() { |
||
| 306 | |||
| 307 | public function getExternalDBSyncState() { |
||
| 313 | |||
| 314 | /** |
||
| 315 | * Retrieves the external DB identifier of this institution. Returns FALSE if no ID is known. |
||
| 316 | * |
||
| 317 | * @return int the external identifier; or FALSE if no external ID is known |
||
| 318 | */ |
||
| 319 | public function getExternalDBId() { |
||
| 331 | |||
| 332 | /** |
||
| 333 | * Fetches information from the external database about this IdP |
||
| 334 | * |
||
| 335 | * @return array details about that institution. Array may be empty if entity is not synced |
||
| 336 | */ |
||
| 337 | public function getExternalDBEntityDetails() { |
||
| 344 | |||
| 345 | public function setExternalDBId($identifier) { |
||
| 355 | } |
||
| 356 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.