| Total Complexity | 48 |
| Total Lines | 373 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 1 |
Complex classes like ExternalEduroamDBData 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 ExternalEduroamDBData, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 51 | class ExternalEduroamDBData extends common\Entity implements ExternalLinkInterface { |
||
| 52 | |||
| 53 | /** |
||
| 54 | * List of all service providers. Fetched only once by allServiceProviders() |
||
| 55 | * and then stored in this property for efficiency |
||
| 56 | * |
||
| 57 | * @var array |
||
| 58 | */ |
||
| 59 | private $SPList = []; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * total number of hotspots, cached here for efficiency |
||
| 63 | * |
||
| 64 | * @var int |
||
| 65 | */ |
||
| 66 | private $counter = -1; |
||
| 67 | |||
| 68 | /** |
||
| 69 | * our handle to the external DB |
||
| 70 | * |
||
| 71 | * @var DBConnection |
||
| 72 | */ |
||
| 73 | private $db; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * our handle to the local DB |
||
| 77 | * |
||
| 78 | * @var DBConnection |
||
| 79 | */ |
||
| 80 | private $localDb; |
||
| 81 | |||
| 82 | |||
| 83 | /** |
||
| 84 | * constructor, gives us access to the DB handle we need for queries |
||
| 85 | */ |
||
| 86 | public function __construct() { |
||
| 87 | parent::__construct(); |
||
| 88 | $connHandle = DBConnection::handle("EXTERNAL"); |
||
| 89 | if (!$connHandle instanceof DBConnection) { |
||
| 90 | throw new Exception("Frontend DB is never an array, always a single DB object."); |
||
| 91 | } |
||
| 92 | $this->db = $connHandle; |
||
| 93 | $localConnHandle = DBConnection::handle("INST"); |
||
| 94 | if (!$localConnHandle instanceof DBConnection) { |
||
| 95 | throw new Exception("Frontend DB is never an array, always a single DB object."); |
||
| 96 | } |
||
| 97 | $this->localDb = $localConnHandle; |
||
| 98 | } |
||
| 99 | |||
| 100 | /** |
||
| 101 | * eduroam DB delivers a string with all name variants mangled in one. Pry |
||
| 102 | * it apart. |
||
| 103 | * |
||
| 104 | * @param string $nameRaw the string with all name variants coerced into one |
||
| 105 | * @return array language/name pair |
||
| 106 | * @throws Exception |
||
| 107 | */ |
||
| 108 | private function splitNames($nameRaw) { |
||
| 109 | $variants = explode('#', $nameRaw); |
||
| 110 | $submatches = []; |
||
| 111 | $returnArray = []; |
||
| 112 | foreach ($variants as $oneVariant) { |
||
| 113 | if ($oneVariant == NULL) { |
||
| 114 | continue; |
||
| 115 | } |
||
| 116 | if (!preg_match('/^(..):\ (.*)/', $oneVariant, $submatches) || !isset($submatches[2])) { |
||
| 117 | $this->loggerInstance->debug(2, "[$nameRaw] We expect 'xx: bla but found '$oneVariant'."); |
||
| 118 | continue; |
||
| 119 | } |
||
| 120 | $returnArray[$submatches[1]] = $submatches[2]; |
||
| 121 | } |
||
| 122 | return $returnArray; |
||
| 123 | } |
||
| 124 | |||
| 125 | /** |
||
| 126 | * retrieves the list of all service providers from the eduroam database |
||
| 127 | * |
||
| 128 | * @return array list of providers |
||
| 129 | */ |
||
| 130 | public function listAllServiceProviders() { |
||
| 131 | if (count($this->SPList) == 0) { |
||
| 132 | $query = $this->db->exec("SELECT country, inst_name, sp_location FROM view_active_SP_location_eduroamdb"); |
||
| 133 | while ($iterator = mysqli_fetch_object(/** @scrutinizer ignore-type */ $query)) { |
||
| 134 | $this->SPList[] = ["country" => $iterator->country, "instnames" => $this->splitNames($iterator->inst_name), "locnames" => $this->splitNames($iterator->sp_location)]; |
||
| 135 | } |
||
| 136 | } |
||
| 137 | return $this->SPList; |
||
| 138 | } |
||
| 139 | |||
| 140 | public function countAllServiceProviders() { |
||
| 141 | if ($this->counter > -1) { |
||
| 142 | return $this->counter; |
||
| 143 | } |
||
| 144 | |||
| 145 | $cachedNumber = @file_get_contents(ROOT . "/var/tmp/cachedSPNumber.serialised"); |
||
| 146 | if ($cachedNumber !== FALSE) { |
||
| 147 | $numberData = unserialize($cachedNumber); |
||
| 148 | $now = new \DateTime(); |
||
| 149 | $cacheDate = $numberData["timestamp"]; // this is a DateTime object |
||
| 150 | $diff = $now->diff($cacheDate); |
||
| 151 | if ($diff->y == 0 && $diff->m == 0 && $diff->d == 0) { |
||
| 152 | $this->counter = $numberData["number"]; |
||
| 153 | return $this->counter; |
||
| 154 | } |
||
| 155 | } else { // data in cache is too old or doesn't exist. We really need to ask the database |
||
| 156 | $list = $this->listAllServiceProviders(); |
||
| 157 | $this->counter = count($list); |
||
| 158 | file_put_contents(ROOT . "/var/tmp/cachedSPNumber.serialised", serialize(["number" => $this->counter, "timestamp" => new \DateTime()])); |
||
| 159 | return $this->counter; |
||
| 160 | } |
||
| 161 | } |
||
| 162 | |||
| 163 | public const TYPE_IDPSP = "3"; |
||
| 164 | public const TYPE_SP = "2"; |
||
| 165 | public const TYPE_IDP = "1"; |
||
| 166 | private const TYPE_MAPPING = [ |
||
| 167 | IdP::TYPE_IDP => ExternalEduroamDBData::TYPE_IDP, |
||
| 168 | IdP::TYPE_IDPSP => ExternalEduroamDBData::TYPE_IDPSP, |
||
| 169 | IdP::TYPE_SP => ExternalEduroamDBData::TYPE_SP, |
||
| 170 | ]; |
||
| 171 | |||
| 172 | /** |
||
| 173 | * separate institution names as written in the eduroam DB into array |
||
| 174 | * |
||
| 175 | * @param string $collapsed - '#' separated list of names - each name has |
||
| 176 | * two-letter language prefic followed by ':' |
||
| 177 | * @return array $nameList - tle list contains both separate per-lang entires |
||
| 178 | * and a joint one, just names no lang info - this used for comparison with CAT institution names |
||
| 179 | */ |
||
| 180 | public static function dissectCollapsedInstitutionNames($collapsed) { |
||
| 181 | $names = explode('#', $collapsed); |
||
| 182 | $nameList = [ |
||
| 183 | 'joint' => [], |
||
| 184 | 'perlang' => [], |
||
| 185 | ]; |
||
| 186 | foreach ($names as $name) { |
||
| 187 | $perlang = explode(': ', $name, 2); |
||
| 188 | $nameList['perlang'][$perlang[0]] = $perlang[1]; |
||
| 189 | if (!in_array($perlang[1], $nameList['joint'])) { |
||
| 190 | $nameList['joint'][] = mb_strtolower(preg_replace('/^..: /', '', $name), 'UTF-8'); |
||
| 191 | } |
||
| 192 | } |
||
| 193 | return $nameList; |
||
| 194 | } |
||
| 195 | |||
| 196 | /** |
||
| 197 | * separate institution realms as written in the eduroam DB into array |
||
| 198 | * |
||
| 199 | * @param type $collapsed |
||
| 200 | * @return array $realmList |
||
| 201 | */ |
||
| 202 | public static function dissectCollapsedInstitutionRealms($collapsed) { |
||
| 203 | if ($collapsed === '' || $collapsed === NULL) { |
||
| 204 | return []; |
||
| 205 | } |
||
| 206 | $realmList = explode(',', $collapsed); |
||
| 207 | return $realmList; |
||
| 208 | } |
||
| 209 | |||
| 210 | /** |
||
| 211 | * |
||
| 212 | * @param string $collapsed the blob with contact info from eduroam DB |
||
| 213 | * @return array of contacts represented as array name,mail,phone |
||
| 214 | */ |
||
| 215 | public static function dissectCollapsedContacts($collapsed) { |
||
| 216 | $contacts = explode('#', $collapsed); |
||
| 217 | $contactList = []; |
||
| 218 | foreach ($contacts as $contact) { |
||
| 219 | $matches = []; |
||
| 220 | preg_match("/^n: *([^ ].*), e: *([^ ].*), p: *([^ ].*)$/", $contact, $matches); |
||
| 221 | if (!isset($matches[1])) { |
||
| 222 | continue; |
||
| 223 | } |
||
| 224 | $contactList[] = [ |
||
| 225 | "name" => $matches[1], |
||
| 226 | "mail" => $matches[2], |
||
| 227 | "phone" => $matches[3] |
||
| 228 | ]; |
||
| 229 | } |
||
| 230 | return $contactList; |
||
| 231 | } |
||
| 232 | |||
| 233 | /** |
||
| 234 | * retrieves entity information from the eduroam database. Choose whether to get all entities with an SP role, an IdP role, or only those with both roles |
||
| 235 | * |
||
| 236 | * @param string $tld the top-level domain from which to fetch the entities |
||
| 237 | * @param string|NULL $type type of entity to retrieve |
||
| 238 | * @return array list of entities |
||
| 239 | */ |
||
| 240 | public function listExternalEntities($tld, $type) { |
||
| 241 | if ($type === NULL) { |
||
| 242 | $eduroamDbType = NULL; |
||
| 243 | } else { |
||
| 244 | $eduroamDbType = self::TYPE_MAPPING[$type]; // anything |
||
| 245 | } |
||
| 246 | $returnarray = []; |
||
| 247 | $query = "SELECT instid AS id, country, inst_realm as realmlist, name AS collapsed_name, contact AS collapsed_contact, type FROM view_active_institution WHERE country = ?"; |
||
| 248 | if ($eduroamDbType !== NULL) { |
||
| 249 | $query .= " AND ( type = '" . ExternalEduroamDBData::TYPE_IDPSP . "' OR type = '" . $eduroamDbType . "')"; |
||
| 250 | } |
||
| 251 | $externals = $this->db->exec($query, "s", $tld); |
||
| 252 | // was a SELECT query, so a resource and not a boolean |
||
| 253 | while ($externalQuery = mysqli_fetch_object(/** @scrutinizer ignore-type */ $externals)) { |
||
| 254 | $names = $this->splitNames($externalQuery->collapsed_name); |
||
| 255 | $thelanguage = $names[$this->languageInstance->getLang()] ?? $names["en"] ?? array_shift($names); |
||
| 256 | $contacts = $this::dissectCollapsedContacts($externalQuery->collapsed_contact); |
||
| 257 | $mails = []; |
||
| 258 | foreach ($contacts as $contact) { |
||
| 259 | // extracting real names is nice, but the <> notation |
||
| 260 | // really gets screwed up on POSTs and HTML safety |
||
| 261 | // so better not do this; use only mail addresses |
||
| 262 | $mails[] = $contact['mail']; |
||
| 263 | } |
||
| 264 | $convertedType = array_search($externalQuery->type, self::TYPE_MAPPING); |
||
| 265 | $returnarray[] = ["ID" => $externalQuery->id, "name" => $thelanguage, "contactlist" => implode(", ", $mails), "country" => $externalQuery->country, "realmlist" => $externalQuery->realmlist, "type" => $convertedType]; |
||
| 266 | } |
||
| 267 | usort($returnarray, array($this, "usortInstitution")); |
||
| 268 | return $returnarray; |
||
| 269 | } |
||
| 270 | |||
| 271 | /** |
||
| 272 | * retrieves entity information from the eduroam database having the given realm in the inst_realm field |
||
| 273 | * Choose which fields to get or get default |
||
| 274 | * |
||
| 275 | * @param string $realm the realm |
||
| 276 | * @param array $fields list of fields |
||
| 277 | * @return array list of entities |
||
| 278 | */ |
||
| 279 | public function listExternalEntitiesByRealm($realm, $fields = []) { |
||
| 299 | } |
||
| 300 | |||
| 301 | /* |
||
| 302 | * retrieves the list of identifiers (external and local) of all institutions |
||
| 303 | * which have the admin email listed in the externam DB, thos that are synced to an |
||
| 304 | * existing CAT institution will also have the local identifier (else NULL) |
||
| 305 | */ |
||
| 306 | |||
| 307 | public function listExternalEntitiesByUserEmail($userEmail){ |
||
| 334 | } |
||
| 335 | |||
| 336 | /** |
||
| 337 | * Test if a given external institution exists and if userEmail is provided also |
||
| 338 | * check if this mail is listed as the admin for this institutution |
||
| 339 | * |
||
| 340 | * @param type $ROid |
||
| 341 | * @param type $extId |
||
| 342 | * @param type $userEmail |
||
| 343 | * @return int 1 if found 0 if not |
||
| 344 | */ |
||
| 345 | public function verifyExternalEntity($ROid, $extId, $userEmail = NULL) { |
||
| 346 | $query = "SELECT * FROM view_institution_admins WHERE ROid='$ROid' AND instid='$extId'"; |
||
| 347 | if ($userEmail != NULL) { |
||
| 348 | $query .= " AND email='$userEmail'"; |
||
| 349 | } |
||
| 350 | $result = $this->db->exec($query); |
||
| 351 | if (mysqli_num_rows(/** @scrutinizer ignore-type */ $result) > 0) { |
||
| 352 | return 1; |
||
| 353 | } else { |
||
| 354 | return 0; |
||
| 355 | } |
||
| 356 | } |
||
| 357 | |||
| 358 | public function listExternalRealms() { |
||
| 359 | return $this->listExternalEntitiesByRealm(""); // leaing realm empty gets *all* |
||
| 360 | } |
||
| 361 | |||
| 362 | /** |
||
| 363 | * helper function to sort institutions by their name |
||
| 364 | * |
||
| 365 | * @param array $a an array with institution a's information |
||
| 366 | * @param array $b an array with institution b's information |
||
| 367 | * @return int the comparison result |
||
| 368 | */ |
||
| 369 | private function usortInstitution($a, $b) { |
||
| 370 | return strcasecmp($a["name"], $b["name"]); |
||
| 371 | } |
||
| 372 | |||
| 373 | /** |
||
| 374 | * get all RADIUS/TLS servers for a given federation, with contacts |
||
| 375 | * [ hostnames => contacts ] |
||
| 376 | * (hostnames are comma-separated) |
||
| 377 | * |
||
| 378 | * @return array |
||
| 379 | */ |
||
| 380 | public function listExternalTlsServersFederation($tld) { |
||
| 381 | $retval = []; |
||
| 382 | // this includes servers of type "staging", which is fine |
||
| 383 | $query = "SELECT servers, contacts FROM view_tls_ro WHERE country = ? AND servers IS NOT NULL AND contacts IS NOT NULL"; |
||
| 384 | $roTldServerTransaction = $this->db->exec($query, "s", $tld); |
||
| 385 | while ($roServerResponses = mysqli_fetch_object(/** @scrutinizer ignore-type */ $roTldServerTransaction)) { |
||
| 386 | // there is only one row_id |
||
| 387 | $retval[$roServerResponses->servers] = $this::dissectCollapsedContacts($roServerResponses->contacts); |
||
| 388 | } |
||
| 389 | return $retval; |
||
| 390 | } |
||
| 391 | |||
| 392 | /** |
||
| 393 | * get all RADIUS/TLS servers for all institutions within a given federation |
||
| 394 | * including their contact details |
||
| 395 | * |
||
| 396 | * "ROid-instid" => [type, inst_name, servers, contacts] |
||
| 397 | * |
||
| 398 | * (hostnames are comma-separated) |
||
| 399 | * |
||
| 400 | * @return array |
||
| 401 | */ |
||
| 402 | public function listExternalTlsServersInstitution($tld, $include_not_ready=FALSE) { |
||
| 424 | } |
||
| 425 | } |
||
| 426 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths