Test Failed
Push — release_2_1 ( ff55e9...bc6859 )
by Tomasz
14:08 queued 01:00
created

IdPlist   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 382
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 58
eloc 199
c 3
b 0
f 0
dl 0
loc 382
rs 4.5599

11 Methods

Rating   Name   Duplication   Size   Complexity  
A orderIdentityProviders() 0 17 3
B listIdentityProvidersWithProfiles() 0 50 9
A geoDistance() 0 7 1
A setIdentityProviderAttributes() 0 26 5
B getIdpDistance() 0 23 7
A setKeywords() 0 20 5
A setAllIdentyProvidersQuery() 0 21 3
A setCurrentLocation() 0 10 3
A setAllProfileQuery() 0 18 1
C listAllIdentityProviders() 0 58 13
B setProfileAttributes() 0 39 8

How to fix   Complexity   

Complex Class

Complex classes like IdPlist 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 IdPlist, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * *****************************************************************************
5
 * Contributions to this work were made on behalf of the GÉANT project, a 
6
 * project that has received funding from the European Union’s Framework 
7
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
8
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
9
 * 691567 (GN4-1) and No. 731122 (GN4-2).
10
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
11
 * of the copyright in all material which was developed by a member of the GÉANT
12
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
13
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
14
 * UK as a branch of GÉANT Vereniging.
15
 * 
16
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
17
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
18
 *
19
 * License: see the web/copyright.inc.php file in the file structure or
20
 *          <base_url>/copyright.php after deploying the software
21
 */
22
23
namespace core;
24
25
class IdPlist extends common\Entity
26
{
27
28
    /**
29
     * Order active identity providers according to their distance and name
30
     * @param string $country         the country from which to list IdPs
31
     * @param array  $currentLocation current location
32
     * @return array $IdPs -  list of arrays ('id', 'name');
33
     */
34
    public static function orderIdentityProviders($country, $currentLocation)
35
    {
36
        $idps = self::listAllIdentityProviders(1, $country);
37
        $here = self::setCurrentLocation($currentLocation);
38
        $idpTitle = [];
39
        $resultSet = [];
40
        foreach ($idps as $idp) {
41
            $idpTitle[$idp['entityID']] = $idp['title'];
42
            $d = self::getIdpDistance($idp, $here);
43
            $resultSet[$idp['entityID']] = $d." ".$idp['title'];
44
        }
45
        asort($resultSet);
46
        $outarray = [];
47
        foreach (array_keys($resultSet) as $r) {
48
            $outarray[] = ['idp' => $r, 'title' => $idpTitle[$r]];
49
        }
50
        return($outarray);
51
    }
52
53
    /**
54
     * Lists all identity providers in the database
55
     * adding information required by DiscoJuice.
56
     * 
57
     * @param int    $activeOnly if set to non-zero will cause listing of only those institutions which have some valid profiles defined.
58
     * @param string $country    if set, only list IdPs in a specific country
59
     * @return array the list of identity providers
60
     *
61
     */
62
    public static function listAllIdentityProviders($activeOnly = 0, $country = "")
63
    {
64
        common\Entity::intoThePotatoes();
65
        $handle = DBConnection::handle("INST");
66
        $handle->exec("SET SESSION group_concat_max_len=10000");
67
        $query = IdPlist::setAllIdentyProvidersQuery($activeOnly, $country);
68
        $allIDPs = ($country != "" ? $handle->exec($query, "s", $country) : $handle->exec($query));
69
        $idpArray = [];
70
        // SELECTs never return a booleans, always an object
71
        $allIdPresults = [];
72
        while ($queryResult = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allIDPs)) {
73
            $allIdPresults[] = $queryResult;
74
        }
75
        foreach ($allIdPresults as $queryResult) {
76
            $options = IdPlist::setIdentityProviderAttributes($queryResult);
77
            $oneInstitutionResult = [];
78
            $oneInstitutionResult['entityID'] = $queryResult->inst_id;
79
            $oneInstitutionResult['country'] = strtoupper($queryResult->country);
80
            if ($options['icon'] > 0) {
81
                $oneInstitutionResult['icon'] = $options['icon'];
82
            }
83
            $name = _("Unnamed Entity");
84
            if (count($options['names']) != 0) {
85
                $langObject = new \core\common\Language();
86
                $name = $langObject->getLocalisedValue($options['names']);
87
            }          
88
            $oneInstitutionResult['title'] = $name;
89
            
90
            if (\config\ConfAssistant::USE_KEYWORDS) {
91
                $keywords = [];
92
                foreach ($options['names'] as $keyword) {
93
                    $value = $keyword['value'];
94
                    $keywords[$keyword['lang']] = $keyword['value'];
95
                    $keywords[$keyword['lang'].'_7'] =
96
                        iconv('UTF-8', 'ASCII//TRANSLIT', $value);
97
                }
98
                $q = "SELECT DISTINCT realm FROM profile WHERE inst_id=? AND realm NOT LIKE '%hosted.eduroam.org'";
99
                $realms = $handle->exec($q, 'i', $queryResult->inst_id);
100
                while ($outerId = mysqli_fetch_row(/** @scrutinizer ignore-type */ $realms)) {
101
                    if (preg_match('/.*@(.*)$/', $outerId[0], $matches)) {
102
                        $keywords[] = $matches[1];
103
                    }
104
                }
105
                $keywords_final = array_unique($keywords);
106
                if (!empty($keywords_final)) {
107
                    $oneInstitutionResult['keywords'] = [];
108
                    foreach (array_keys($keywords_final) as $key) {
109
                    $oneInstitutionResult['keywords'][] = [$keywords_final[$key]];
110
                    }
111
                }
112
            }
113
            if (count($options['geo']) > 0) {
114
                $oneInstitutionResult['geo'] = $options['geo'];
115
            }
116
            $idpArray[] = $oneInstitutionResult;
117
        }
118
        common\Entity::outOfThePotatoes();
119
        return $idpArray;
120
    }
121
    
122
    /**
123
     * outputs a full list of IdPs containing the fllowing data:
124
     * institution_is, institution name in all available languages,
125
     * list of production profiles.
126
     * For eache profile the profile identifier, profile name in all languages
127
     * and redirect values (empty rediret value means that no redirect has been
128
     * set).
129
     * 
130
     * @return array of identity providers with attributes
131
     */
132
    public static function listIdentityProvidersWithProfiles() {
133
        $handle = DBConnection::handle("INST");
134
        $handle->exec("SET SESSION group_concat_max_len=10000");
135
        $idpQuery = IdPlist::setAllIdentyProvidersQuery(1);
136
        $allIDPs = $handle->exec($idpQuery);
137
        $idpArray = [];
138
        while ($queryResult = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allIDPs)) {
139
            $options = IdPlist::setIdentityProviderAttributes($queryResult);
140
            $oneInstitutionResult = [];
141
            $oneInstitutionResult['country'] = strtoupper($queryResult->country);
142
            $oneInstitutionResult['entityID'] = (int) $queryResult->inst_id;
143
            if ($options['icon'] > 0) {
144
                $oneInstitutionResult['icon'] = $options['icon'];
145
            }
146
            $oneInstitutionResult['names'] = $options['names'];
147
            if (count($options['geo']) > 0) {
148
                $geoArray = [];
149
                foreach ($options['geo'] as $coords) {
150
                    $geoArray[] = ['lon' => (float) $coords['lon'],
151
                        'lat' => (float) $coords['lat']];
152
                }
153
                $oneInstitutionResult['geo'] = $geoArray;
154
            }
155
156
            $idpArray[$queryResult->inst_id] = $oneInstitutionResult;
157
        }
158
        
159
        $profileQuery = IdPlist::setAllProfileQuery();
160
        $allProfiles = $handle->exec($profileQuery);
161
        while ($queryResult = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allProfiles)) {
162
            $profileOptions = IdPlist::setProfileAttributes($queryResult);
163
            $idpId = $queryResult->inst_id;
164
            if (empty($idpArray[$idpId])) {
165
                continue;
166
            }
167
            if (empty($idpArray[$idpId]['profiles'])) {
168
                $idpArray[$idpId]['profiles'] = [];
169
            }
170
            if (!$profileOptions['production']) {
171
                continue;
172
            }
173
            $idpArray[$idpId]['profiles'][] = [
174
                'id'=> (int) $queryResult->profile_id,
175
                'names' => $profileOptions['profileNames'],
176
                'redirect' => $profileOptions['redirect'],
177
                'openroaming' => $profileOptions['openroaming'],
178
//                'options' => $profileOptions['options'],
179
                ];
180
        }       
181
        return $idpArray;
182
    }
183
184
    /**
185
     * sets the current location
186
     * 
187
     * @param array $currentLocation the location to set
188
     * @return array
189
     */
190
    private static function setCurrentLocation($currentLocation)
191
    {
192
        if (is_null($currentLocation)) {
193
            $currentLocation = ['lat' => "90", 'lon' => "0"];
194
            $userLocation = DeviceLocation::locateDevice();
195
            if ($userLocation['status'] == 'ok') {
196
                $currentLocation = $userLocation['geo'];
197
            }
198
        }
199
        return $currentLocation;
200
    }
201
202
    /**
203
     * calculate surface distance from user location to IdP location
204
     * @param array $idp      the IdP in question
205
     * @param array $location user location
206
     * @return string
207
     */
208
    private static function getIdpDistance($idp, $location)
209
    {
210
        $dist = 10000;
211
        if (isset($idp['geo'])) {
212
            $G = $idp['geo'];
213
            if (isset($G['lon'])) {
214
                $d1 = self::geoDistance($location, $G);
215
                if ($d1 < $dist) {
216
                    $dist = $d1;
217
                }
218
            } else {
219
                foreach ($G as $g) {
220
                    $d1 = self::geoDistance($location, $g);
221
                    if ($d1 < $dist) {
222
                        $dist = $d1;
223
                    }
224
                }
225
            }
226
        }
227
        if ($dist > 100) {
228
            $dist = 10000;
229
        }
230
        return(sprintf("%06d", $dist));
231
    }
232
    
233
    /**
234
     * set the IdP query string for listAllIdentityProviders and 
235
     * listIdentityProvidersWithProfiles
236
     * @param int    $activeOnly if set to non-zero will cause listing of only those institutions which have some valid profiles defined.
237
     * @param string $country    if set, only list IdPs in a specific country
238
     * 
239
     * @return string the query
240
     */
241
    private static function setAllIdentyProvidersQuery($activeOnly = 0, $country = "")
242
    {
243
        $query = "SELECT distinct institution.inst_id AS inst_id,
244
            institution.country AS country,
245
            group_concat(concat_ws('===',institution_option.option_name,
246
                LEFT(institution_option.option_value,200),
247
                institution_option.option_lang) separator '---') AS options
248
            FROM institution ";
249
        if ($activeOnly == 1) {
250
            $query .= "JOIN v_active_inst ON institution.inst_id = v_active_inst.inst_id ";
251
        }
252
        $query .= 
253
            "JOIN institution_option ON institution.inst_id = institution_option.institution_id
254
            WHERE (institution_option.option_name = 'general:instname' 
255
                OR institution_option.option_name = 'general:geo_coordinates'
256
                OR institution_option.option_name = 'general:logo_file') ";
257
258
        $query .= ($country != "" ? "AND institution.country = ? " : "");
259
260
        $query .= "GROUP BY institution.inst_id ORDER BY inst_id";
261
        return $query;
262
    }
263
264
    /**
265
     * set the Profile query string for listIdentityProvidersWithProfiles
266
     * 
267
     * @return string query
268
     */
269
    private static function setAllProfileQuery() {
270
        $query = "SELECT profile.inst_id AS inst_id,
271
            profile.profile_id,
272
            group_concat(concat_ws('===',profile_option.option_name, 
273
                LEFT(profile_option.option_value, 200),
274
                profile_option.option_lang) separator '---') AS profile_options,
275
                profile.realm AS realm
276
            FROM profile
277
            JOIN profile_option ON profile.profile_id = profile_option.profile_id
278
            WHERE profile.sufficient_config = 1
279
                AND profile_option.eap_method_id = 0
280
                AND (profile_option.option_name = 'profile:name'
281
                OR (profile_option.option_name = 'device-specific:redirect'
282
                    AND isnull(profile_option.device_id))
283
                OR profile_option.option_name = 'media:openroaming'
284
                OR profile_option.option_name = 'profile:production')        
285
            GROUP BY profile.profile_id ORDER BY inst_id";
286
        return $query;
287
    }
288
    
289
    /**
290
     * Extract IdP attributes for listAllIdentityProviders and 
291
     * listIdentityProvidersWithProfiles
292
     * @param  $idp object - the row_id object returned by the IdP search
293
     * @return array the IdP attributes
294
     */
295
    private static function setIdentityProviderAttributes($idp) {
296
        $options = explode('---', $idp->options);
297
        $names = [];
298
        $geo = [];
299
        $icon = 0;
300
        foreach ($options as $option) {
301
            $opt = explode('===', $option);
302
            switch ($opt[0]) {
303
                case 'general:logo_file':
304
                    $icon = $idp->inst_id;
305
                    break;
306
                case 'general:geo_coordinates':
307
                    $at1 = json_decode($opt[1], true);
308
                    $geo[] = $at1;
309
                    break;
310
                case 'general:instname':
311
                    $names[] = [
312
                        'lang' => $opt[2],
313
                        'value' => $opt[1]
314
                    ];
315
                    break;
316
                default:
317
                    break;
318
            }
319
        }
320
        return ['names' => $names, 'geo' => $geo, 'icon' => $icon];       
321
    }
322
    
323
    /**
324
     * Extract Profile attributes for listIdentityProvidersWithProfiles
325
     * 
326
     * @param object $profile - the row_id object returned by the profile search
327
     * @return array the profile attributes
328
     */
329
    private static function setProfileAttributes($profile)
330
    {
331
        $profileOptions = explode('---', $profile->profile_options);
332
        $productionProfile = false;
333
        $profileNames = [];
334
        $redirect = '';
335
        $openroaming = 'none';
336
337
        foreach ($profileOptions as $profileOption) {
338
            $opt = explode('===', $profileOption);
339
            switch ($opt[0]) {
340
                case 'profile:production':
341
                    if ($opt[1] == 'on') {
342
                        $productionProfile = true;
343
                    }
344
                    break;
345
                case 'device-specific:redirect':
346
                    $redirect = $opt[1];
347
                    if (!empty($profile->device_id)) {
348
                        $redirect .= ':'.$profile->device_id;
349
                    }
350
                    break;
351
                case 'profile:name': 
352
                    $profileNames[] = [
353
                        'lang' => $opt[2],
354
                        'value' => $opt[1]
355
                    ];
356
                    break;
357
                case 'media:openroaming':
358
                    $openroaming = $opt[1];
359
                    break;
360
                default:
361
                    break; 
362
            }
363
        }
364
        return ['production' => $productionProfile,
365
            'profileNames' => $profileNames,
366
            'redirect' => $redirect,
367
            'openroaming' => $openroaming,
368
            ];
369
    }
370
    
371
    private static function setKeywords($names)
0 ignored issues
show
Unused Code introduced by
The method setKeywords() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
372
    {
373
        if (!\config\ConfAssistant::USE_KEYWORDS) {
374
            return null;
375
        }
376
        $keywords = [];
377
        $returnArray = [];
378
        foreach ($names as $keyword) {
379
            $value = $keyword['value'];
380
            $keywords[$keyword['lang']] = $keyword['value'];
381
            $keywords[$keyword['lang'].'_7'] =
382
                    iconv('UTF-8', 'ASCII//TRANSLIT', $value);
383
        }
384
        $keywords_final = array_unique($keywords);
385
        if (!empty($keywords_final)) {
386
            foreach (array_keys($keywords_final) as $key) {
387
            $returnArray[] = [$keywords_final[$key]];
388
            }
389
        }
390
        return $returnArray;
391
    }
392
393
    /**
394
     * Calculate the distance in km between two points given their
395
     * geo coordinates.
396
     * @param array $point1   first point as an 'lat', 'lon' array 
397
     * @param array $profile1 second point as an 'lat', 'lon' array 
398
     * @return float distance in km
399
     */
400
    public static function geoDistance($point1, $profile1)
401
    {
402
403
        $distIntermediate = sin(deg2rad($point1['lat'])) * sin(deg2rad($profile1['lat'])) +
404
            cos(deg2rad($point1['lat'])) * cos(deg2rad($profile1['lat'])) * cos(deg2rad($point1['lon'] - $profile1['lon']));
405
        $dist = rad2deg(acos($distIntermediate)) * 60 * 1.1852;
406
        return(round($dist));
407
    }
408
}
409