| Total Complexity | 53 |
| Total Lines | 361 |
| Duplicated Lines | 3.32 % |
| Changes | 0 | ||
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 Federation 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.
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 Federation, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 44 | class Federation extends EntityWithDBProperties { |
||
| 45 | |||
| 46 | /** |
||
| 47 | * the handle to the FRONTEND database (only needed for some stats access) |
||
| 48 | * |
||
| 49 | * @var DBConnection |
||
| 50 | */ |
||
| 51 | private $frontendHandle; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * retrieve the statistics from the database in an internal array representation |
||
| 55 | * |
||
| 56 | * @return array |
||
| 57 | */ |
||
| 58 | private function downloadStatsCore() { |
||
| 93 | } |
||
| 94 | |||
| 95 | /** |
||
| 96 | * NOOP on Federations, but have to override the abstract parent method |
||
| 97 | */ |
||
| 98 | public function updateFreshness() { |
||
| 99 | // Federation is always fresh |
||
| 100 | } |
||
| 101 | |||
| 102 | /** |
||
| 103 | * gets the download statistics for the federation |
||
| 104 | * @param string $format either as an html *table* or *XML* |
||
| 105 | * @return string |
||
| 106 | */ |
||
| 107 | public function downloadStats($format) { |
||
| 108 | $data = $this->downloadStatsCore(); |
||
| 109 | $retstring = ""; |
||
| 110 | |||
| 111 | switch ($format) { |
||
| 112 | case "table": |
||
| 113 | View Code Duplication | foreach ($data as $device => $numbers) { |
|
|
|
|||
| 114 | if ($device == "TOTAL") { |
||
| 115 | continue; |
||
| 116 | } |
||
| 117 | $retstring .= "<tr><td>$device</td><td>" . $numbers['ADMIN'] . "</td><td>" . $numbers['SILVERBULLET'] . "</td><td>" . $numbers['USER'] . "</td></tr>"; |
||
| 118 | } |
||
| 119 | $retstring .= "<tr><td><strong>TOTAL</strong></td><td><strong>" . $data['TOTAL']['ADMIN'] . "</strong></td><td><strong>" . $data['TOTAL']['SILVERBULLET'] . "</strong></td><td><strong>" . $data['TOTAL']['USER'] . "</strong></td></tr>"; |
||
| 120 | break; |
||
| 121 | case "XML": |
||
| 122 | // the calls to date() operate on current date, so there is no chance for a FALSE to be returned. Silencing scrutinizer. |
||
| 123 | $retstring .= "<federation id='$this->identifier' ts='" . /** @scrutinizer ignore-type */ date("Y-m-d") . "T" . /** @scrutinizer ignore-type */ date("H:i:s") . "'>\n"; |
||
| 124 | View Code Duplication | foreach ($data as $device => $numbers) { |
|
| 125 | if ($device == "TOTAL") { |
||
| 126 | continue; |
||
| 127 | } |
||
| 128 | $retstring .= " <device name='" . $device . "'>\n <downloads group='admin'>" . $numbers['ADMIN'] . "</downloads>\n <downloads group='managed_idp'>" . $numbers['SILVERBULLET'] . "</downloads>\n <downloads group='user'>" . $numbers['USER'] . "</downloads>\n </device>"; |
||
| 129 | } |
||
| 130 | $retstring .= "<total>\n <downloads group='admin'>" . $data['TOTAL']['ADMIN'] . "</downloads>\n <downloads group='managed_idp'>" . $data['TOTAL']['SILVERBULLET'] . "</downloads>\n <downloads group='user'>" . $data['TOTAL']['USER'] . "</downloads>\n</total>\n"; |
||
| 131 | $retstring .= "</federation>"; |
||
| 132 | break; |
||
| 133 | default: |
||
| 134 | throw new Exception("Statistics can be requested only in 'table' or 'XML' format!"); |
||
| 135 | } |
||
| 136 | |||
| 137 | return $retstring; |
||
| 138 | } |
||
| 139 | |||
| 140 | /** |
||
| 141 | * |
||
| 142 | * Constructs a Federation object. |
||
| 143 | * |
||
| 144 | * @param string $fedname - textual representation of the Federation object |
||
| 145 | * Example: "lu" (for Luxembourg) |
||
| 146 | */ |
||
| 147 | public function __construct($fedname = "") { |
||
| 148 | |||
| 149 | // initialise the superclass variables |
||
| 150 | |||
| 151 | $this->databaseType = "INST"; |
||
| 152 | $this->entityOptionTable = "federation_option"; |
||
| 153 | $this->entityIdColumn = "federation_id"; |
||
| 154 | |||
| 155 | $cat = new CAT(); |
||
| 156 | if (!isset($cat->knownFederations[$fedname])) { |
||
| 157 | throw new Exception("This federation is not known to the system!"); |
||
| 158 | } |
||
| 159 | $this->identifier = $fedname; |
||
| 160 | $this->name = $cat->knownFederations[$this->identifier]; |
||
| 161 | |||
| 162 | parent::__construct(); // we now have access to our database handle |
||
| 163 | |||
| 164 | $this->frontendHandle = DBConnection::handle("FRONTEND"); |
||
| 165 | |||
| 166 | // fetch attributes from DB; populates $this->attributes array |
||
| 167 | $this->attributes = $this->retrieveOptionsFromDatabase("SELECT DISTINCT option_name, option_lang, option_value, row |
||
| 168 | FROM $this->entityOptionTable |
||
| 169 | WHERE $this->entityIdColumn = ? |
||
| 170 | ORDER BY option_name", "FED", "s", $this->identifier); |
||
| 171 | |||
| 172 | |||
| 173 | $this->attributes[] = array("name" => "internal:country", |
||
| 174 | "lang" => NULL, |
||
| 175 | "value" => $this->identifier, |
||
| 176 | "level" => "FED", |
||
| 177 | "row" => 0, |
||
| 178 | "flag" => NULL); |
||
| 179 | } |
||
| 180 | |||
| 181 | /** |
||
| 182 | * Creates a new IdP inside the federation. |
||
| 183 | * |
||
| 184 | * @param string $ownerId Persistent identifier of the user for whom this IdP is created (first administrator) |
||
| 185 | * @param string $level Privilege level of the first administrator (was he blessed by a federation admin or a peer?) |
||
| 186 | * @param string $mail e-mail address with which the user was invited to administer (useful for later user identification if the user chooses a "funny" real name) |
||
| 187 | * @return int identifier of the new IdP |
||
| 188 | */ |
||
| 189 | public function newIdP($ownerId, $level, $mail) { |
||
| 190 | $this->databaseHandle->exec("INSERT INTO institution (country) VALUES('$this->identifier')"); |
||
| 191 | $identifier = $this->databaseHandle->lastID(); |
||
| 192 | |||
| 193 | if ($identifier == 0 || !$this->loggerInstance->writeAudit($ownerId, "NEW", "IdP $identifier")) { |
||
| 194 | $text = "<p>Could not create a new " . CONFIG_CONFASSISTANT['CONSORTIUM']['nomenclature_inst'] . "!</p>"; |
||
| 195 | echo $text; |
||
| 196 | throw new Exception($text); |
||
| 197 | } |
||
| 198 | |||
| 199 | if ($ownerId != "PENDING") { |
||
| 200 | $this->databaseHandle->exec("INSERT INTO ownership (user_id,institution_id, blesslevel, orig_mail) VALUES(?,?,?,?)", "siss", $ownerId, $identifier, $level, $mail); |
||
| 201 | } |
||
| 202 | return $identifier; |
||
| 203 | } |
||
| 204 | |||
| 205 | /** |
||
| 206 | * Lists all Identity Providers in this federation |
||
| 207 | * |
||
| 208 | * @param int $activeOnly if set to non-zero will list only those institutions which have some valid profiles defined. |
||
| 209 | * @return array (Array of IdP instances) |
||
| 210 | * |
||
| 211 | */ |
||
| 212 | public function listIdentityProviders($activeOnly = 0) { |
||
| 213 | // default query is: |
||
| 214 | $allIDPs = $this->databaseHandle->exec("SELECT inst_id FROM institution |
||
| 215 | WHERE country = '$this->identifier' ORDER BY inst_id"); |
||
| 216 | // the one for activeOnly is much more complex: |
||
| 217 | if ($activeOnly) { |
||
| 218 | $allIDPs = $this->databaseHandle->exec("SELECT distinct institution.inst_id AS inst_id |
||
| 219 | FROM institution |
||
| 220 | JOIN profile ON institution.inst_id = profile.inst_id |
||
| 221 | WHERE institution.country = '$this->identifier' |
||
| 222 | AND profile.showtime = 1 |
||
| 223 | ORDER BY inst_id"); |
||
| 224 | } |
||
| 225 | |||
| 226 | $returnarray = []; |
||
| 227 | // SELECT -> resource, not boolean |
||
| 228 | while ($idpQuery = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allIDPs)) { |
||
| 229 | $idp = new IdP($idpQuery->inst_id); |
||
| 230 | $name = $idp->name; |
||
| 231 | $idpInfo = ['entityID' => $idp->identifier, |
||
| 232 | 'title' => $name, |
||
| 233 | 'country' => strtoupper($idp->federation), |
||
| 234 | 'instance' => $idp]; |
||
| 235 | $returnarray[$idp->identifier] = $idpInfo; |
||
| 236 | } |
||
| 237 | return $returnarray; |
||
| 238 | } |
||
| 239 | |||
| 240 | /** |
||
| 241 | * returns an array with information about the authorised administrators of the federation |
||
| 242 | * |
||
| 243 | * @return array |
||
| 244 | */ |
||
| 245 | public function listFederationAdmins() { |
||
| 246 | $returnarray = []; |
||
| 247 | $query = "SELECT user_id FROM user_options WHERE option_name = 'user:fedadmin' AND option_value = ?"; |
||
| 248 | if (CONFIG_CONFASSISTANT['CONSORTIUM']['name'] == "eduroam" && isset(CONFIG_CONFASSISTANT['CONSORTIUM']['deployment-voodoo']) && CONFIG_CONFASSISTANT['CONSORTIUM']['deployment-voodoo'] == "Operations Team") { // SW: APPROVED |
||
| 249 | $query = "SELECT eptid as user_id FROM view_admin WHERE role = 'fedadmin' AND realm = ?"; |
||
| 250 | } |
||
| 251 | $userHandle = DBConnection::handle("USER"); // we need something from the USER database for a change |
||
| 252 | $upperFed = strtoupper($this->identifier); |
||
| 253 | // SELECT -> resource, not boolean |
||
| 254 | $admins = $userHandle->exec($query, "s", $upperFed); |
||
| 255 | |||
| 256 | while ($fedAdminQuery = mysqli_fetch_object(/** @scrutinizer ignore-type */ $admins)) { |
||
| 257 | $returnarray[] = $fedAdminQuery->user_id; |
||
| 258 | } |
||
| 259 | return $returnarray; |
||
| 260 | } |
||
| 261 | |||
| 262 | /** |
||
| 263 | * cross-checks in the EXTERNAL customer DB which institutions exist there for the federations |
||
| 264 | * |
||
| 265 | * @param bool $unmappedOnly if set to TRUE, only returns those which do not have a known mapping to our internally known institutions |
||
| 266 | * @return array |
||
| 267 | */ |
||
| 268 | public function listExternalEntities($unmappedOnly) { |
||
| 337 | } |
||
| 338 | |||
| 339 | const UNKNOWN_IDP = -1; |
||
| 340 | const AMBIGUOUS_IDP = -2; |
||
| 341 | |||
| 342 | /** |
||
| 343 | * for a MySQL list of institutions, find an institution or find out that |
||
| 344 | * there is no single best match |
||
| 345 | * |
||
| 346 | * @param \mysqli_result $dbResult |
||
| 347 | * @param string $country used to return the country of the inst, if can be found out |
||
| 348 | * @return int the identifier of the inst, or one of the special return values if unsuccessful |
||
| 349 | */ |
||
| 350 | private static function findCandidates(\mysqli_result $dbResult, &$country) { |
||
| 366 | } |
||
| 367 | |||
| 368 | /** |
||
| 369 | * If we are running diagnostics, our input from the user is the realm. We |
||
| 370 | * need to find out which IdP this realm belongs to. |
||
| 371 | * @param string $realm the realm to search for |
||
| 372 | * @return array an array with two entries, CAT ID and DB ID, with either the respective ID of the IdP in the system, or UNKNOWN_IDP or AMBIGUOUS_IDP |
||
| 373 | */ |
||
| 374 | public static function determineIdPIdByRealm($realm) { |
||
| 395 | } |
||
| 396 | |||
| 397 | /** |
||
| 398 | * helper function to sort institutions by their name |
||
| 399 | * @param array $a an array with institution a's information |
||
| 400 | * @param array $b an array with institution b's information |
||
| 401 | * @return int the comparison result |
||
| 402 | */ |
||
| 403 | private function usortInstitution($a, $b) { |
||
| 405 | } |
||
| 406 | |||
| 407 | } |
||
| 408 |
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.