Test Failed
Push — master ( af80e6...de37f4 )
by Tomasz
21:17
created

IdPlist   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 362
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
wmc 54
eloc 185
c 8
b 0
f 0
dl 0
loc 362
rs 6.4799

11 Methods

Rating   Name   Duplication   Size   Complexity  
A orderIdentityProviders() 0 17 3
B listIdentityProvidersWithProfiles() 0 48 9
A setIdentityProviderAttributes() 0 26 5
B getIdpDistance() 0 23 7
A setAllIdentyProvidersQuery() 0 21 3
A setCurrentLocation() 0 10 3
A setAllProfileQuery() 0 16 1
B listAllIdentityProviders() 0 48 10
B setProfileAttributes() 0 34 7
A geoDistance() 0 7 1
A setKeywords() 0 20 5

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
        while ($queryResult = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allIDPs)) {
72
            $options = IdPlist::setIdentityProviderAttributes($queryResult);
73
            $oneInstitutionResult = [];
74
            $oneInstitutionResult['entityID'] = $queryResult->inst_id;
75
            $oneInstitutionResult['country'] = strtoupper($queryResult->country);
76
            if ($options['icon'] > 0) {
77
                $oneInstitutionResult['icon'] = $options['icon'];
78
            }
79
            $name = _("Unnamed Entity");
80
            if (count($options['names']) != 0) {
81
                $langObject = new \core\common\Language();
82
                $name = $langObject->getLocalisedValue($options['names']);
83
            }          
84
            $oneInstitutionResult['title'] = $name;
85
            $keywords = [];
86
            foreach ($options['names'] as $keyword) {
87
                $value = $keyword['value'];
88
                $keywords[$keyword['lang']] = $keyword['value'];
89
                $keywords[$keyword['lang'].'_7'] =
90
                        iconv('UTF-8', 'ASCII//TRANSLIT', $value);
91
            }
92
            
93
            if (\config\ConfAssistant::USE_KEYWORDS) {
94
                $keywords_final = array_unique($keywords);
95
96
                if (!empty($keywords_final)) {
97
                    $oneInstitutionResult['keywords'] = [];
98
                    foreach (array_keys($keywords_final) as $key) {
99
                    $oneInstitutionResult['keywords'][] = [$keywords_final[$key]];
100
                    }
101
                }
102
            }
103
            if (count($options['geo']) > 0) {
104
                $oneInstitutionResult['geo'] = $options['geo'];
105
            }
106
            $idpArray[] = $oneInstitutionResult;
107
        }
108
        common\Entity::outOfThePotatoes();
109
        return $idpArray;
110
    }
111
    
112
    /**
113
     * outputs a full list of IdPs containing the fllowing data:
114
     * institution_is, institution name in all available languages,
115
     * list of production profiles.
116
     * For eache profile the profile identifier, profile name in all languages
117
     * and redirect values (empty rediret value means that no redirect has been
118
     * set).
119
     * 
120
     * @return array of identity providers with attributes
121
     */
122
    public static function listIdentityProvidersWithProfiles() {
123
        $handle = DBConnection::handle("INST");
124
        $handle->exec("SET SESSION group_concat_max_len=10000");
125
        $idpQuery = IdPlist::setAllIdentyProvidersQuery(1);
126
        $allIDPs = $handle->exec($idpQuery);
127
        $idpArray = [];
128
        while ($queryResult = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allIDPs)) {
129
            $options = IdPlist::setIdentityProviderAttributes($queryResult);
130
            $oneInstitutionResult = [];
131
            $oneInstitutionResult['country'] = strtoupper($queryResult->country);
132
            $oneInstitutionResult['entityID'] = (int) $queryResult->inst_id;
133
            if ($options['icon'] > 0) {
134
                $oneInstitutionResult['icon'] = $options['icon'];
135
            }
136
            $oneInstitutionResult['names'] = $options['names'];
137
            if (count($options['geo']) > 0) {
138
                $geoArray = [];
139
                foreach ($options['geo'] as $coords) {
140
                    $geoArray[] = ['lon' => (float) $coords['lon'],
141
                        'lat' => (float) $coords['lat']];
142
                }
143
                $oneInstitutionResult['geo'] = $geoArray;
144
            }
145
146
            $idpArray[$queryResult->inst_id] = $oneInstitutionResult;
147
        }
148
        
149
        $profileQuery = IdPlist::setAllProfileQuery();
150
        $allProfiles = $handle->exec($profileQuery);
151
        while ($queryResult = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allProfiles)) {
152
            $profileOptions = IdPlist::setProfileAttributes($queryResult);
153
            $idpId = $queryResult->inst_id;
154
            if (empty($idpArray[$idpId])) {
155
                continue;
156
            }
157
            if (empty($idpArray[$idpId]['profiles'])) {
158
                $idpArray[$idpId]['profiles'] = [];
159
            }
160
            if (!$profileOptions['production']) {
161
                continue;
162
            }
163
            $idpArray[$idpId]['profiles'][] = [
164
                'id'=> (int) $queryResult->profile_id,
165
                'names'=> $profileOptions['profileNames'],
166
                'redirect'=>$profileOptions['redirect']
167
                ];
168
        }       
169
        return $idpArray;
170
    }
171
172
    /**
173
     * sets the current location
174
     * 
175
     * @param array $currentLocation the location to set
176
     * @return array
177
     */
178
    private static function setCurrentLocation($currentLocation)
179
    {
180
        if (is_null($currentLocation)) {
181
            $currentLocation = ['lat' => "90", 'lon' => "0"];
182
            $userLocation = DeviceLocation::locateDevice();
183
            if ($userLocation['status'] == 'ok') {
184
                $currentLocation = $userLocation['geo'];
185
            }
186
        }
187
        return $currentLocation;
188
    }
189
190
    /**
191
     * calculate surface distance from user location to IdP location
192
     * @param array $idp      the IdP in question
193
     * @param array $location user location
194
     * @return string
195
     */
196
    private static function getIdpDistance($idp, $location)
197
    {
198
        $dist = 10000;
199
        if (isset($idp['geo'])) {
200
            $G = $idp['geo'];
201
            if (isset($G['lon'])) {
202
                $d1 = self::geoDistance($location, $G);
203
                if ($d1 < $dist) {
204
                    $dist = $d1;
205
                }
206
            } else {
207
                foreach ($G as $g) {
208
                    $d1 = self::geoDistance($location, $g);
209
                    if ($d1 < $dist) {
210
                        $dist = $d1;
211
                    }
212
                }
213
            }
214
        }
215
        if ($dist > 100) {
216
            $dist = 10000;
217
        }
218
        return(sprintf("%06d", $dist));
219
    }
220
    
221
    /**
222
     * set the IdP query string for listAllIdentityProviders and 
223
     * listIdentityProvidersWithProfiles
224
     * @param int    $activeOnly if set to non-zero will cause listing of only those institutions which have some valid profiles defined.
225
     * @param string $country    if set, only list IdPs in a specific country
226
     * 
227
     * @return string the query
228
     */
229
    private static function setAllIdentyProvidersQuery($activeOnly = 0, $country = "")
230
    {
231
        $query = "SELECT distinct institution.inst_id AS inst_id,
232
            institution.country AS country,
233
            group_concat(concat_ws('===',institution_option.option_name,
234
                LEFT(institution_option.option_value,200),
235
                institution_option.option_lang) separator '---') AS options
236
            FROM institution ";
237
        if ($activeOnly == 1) {
238
            $query .= "JOIN v_active_inst ON institution.inst_id = v_active_inst.inst_id ";
239
        }
240
        $query .= 
241
            "JOIN institution_option ON institution.inst_id = institution_option.institution_id
242
            WHERE (institution_option.option_name = 'general:instname' 
243
                OR institution_option.option_name = 'general:geo_coordinates'
244
                OR institution_option.option_name = 'general:logo_file') ";
245
246
        $query .= ($country != "" ? "AND institution.country = ? " : "");
247
248
        $query .= "GROUP BY institution.inst_id ORDER BY inst_id";
249
        return $query;
250
    }
251
252
    /**
253
     * set the Profile query string for listIdentityProvidersWithProfiles
254
     * 
255
     * @return string query
256
     */
257
    private static function setAllProfileQuery() {
258
        $query = "SELECT profile.inst_id AS inst_id,
259
            profile.profile_id,
260
            group_concat(concat_ws('===',profile_option.option_name, 
261
                LEFT(profile_option.option_value, 200),
262
                profile_option.option_lang) separator '---') AS profile_options
263
            FROM profile
264
            JOIN profile_option ON profile.profile_id = profile_option.profile_id
265
            WHERE profile.sufficient_config = 1
266
                AND profile_option.eap_method_id = 0
267
                AND (profile_option.option_name = 'profile:name'
268
                OR (profile_option.option_name = 'device-specific:redirect'
269
                    AND isnull(profile_option.device_id))
270
                OR profile_option.option_name = 'profile:production')        
271
            GROUP BY profile.profile_id ORDER BY inst_id";
272
        return $query;
273
    }
274
    
275
    /**
276
     * Extract IdP attributes for listAllIdentityProviders and 
277
     * listIdentityProvidersWithProfiles
278
     * @param  $idp object - the row object returned by the IdP search
279
     * @return array the IdP attributes
280
     */
281
    private static function setIdentityProviderAttributes($idp) {
282
        $options = explode('---', $idp->options);
283
        $names = [];
284
        $geo = [];
285
        $icon = 0;
286
        foreach ($options as $option) {
287
            $opt = explode('===', $option);
288
            switch ($opt[0]) {
289
                case 'general:logo_file':
290
                    $icon = $idp->inst_id;
291
                    break;
292
                case 'general:geo_coordinates':
293
                    $at1 = json_decode($opt[1], true);
294
                    $geo[] = $at1;
295
                    break;
296
                case 'general:instname':
297
                    $names[] = [
298
                        'lang' => $opt[2],
299
                        'value' => $opt[1]
300
                    ];
301
                    break;
302
                default:
303
                    break;
304
            }
305
        }
306
        return ['names' => $names, 'geo' => $geo, 'icon' => $icon];       
307
    }
308
    
309
    /**
310
     * Extract Profie attributes for listIdentityProvidersWithProfiles
311
     * 
312
     * @param object $profile - the row object returned by the profile search
313
     * @return array the profile attributes
314
     */
315
    private static function setProfileAttributes($profile)
316
    {
317
        $profileOptions = explode('---', $profile->profile_options);
318
        $productionProfile = false;
319
        $profileNames = [];
320
        $redirect = '';
321
322
        foreach ($profileOptions as $profileOption) {
323
            $opt = explode('===', $profileOption);
324
            switch ($opt[0]) {
325
                case 'profile:production':
326
                    if ($opt[1] == 'on') {
327
                        $productionProfile = true;
328
                    }
329
                    break;
330
                case 'device-specific:redirect':
331
                    $redirect = $opt[1];
332
                    if (!empty($profile->device_id)) {
333
                        $redirect .= ':' . $profile->device_id;
334
                    }
335
                    break;
336
                case 'profile:name': 
337
                    $profileNames[] = [
338
                        'lang' => $opt[2],
339
                        'value' => $opt[1]
340
                    ];
341
                    break;
342
                default:
343
                    break; 
344
            }
345
        }
346
        return ['production' => $productionProfile,
347
            'profileNames' => $profileNames,
348
            'redirect' => $redirect];
349
    }
350
    
351
    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...
352
    {
353
        if (!\config\ConfAssistant::USE_KEYWORDS) {
354
            return null;
355
        }
356
        $keywords = [];
357
        $returnArray = [];
358
        foreach ($names as $keyword) {
359
            $value = $keyword['value'];
360
            $keywords[$keyword['lang']] = $keyword['value'];
361
            $keywords[$keyword['lang'].'_7'] =
362
                    iconv('UTF-8', 'ASCII//TRANSLIT', $value);
363
        }
364
        $keywords_final = array_unique($keywords);
365
        if (!empty($keywords_final)) {
366
            foreach (array_keys($keywords_final) as $key) {
367
            $returnArray[] = [$keywords_final[$key]];
368
            }
369
        }
370
        return $returnArray;
371
    }
372
373
    /**
374
     * Calculate the distance in km between two points given their
375
     * geo coordinates.
376
     * @param array $point1   first point as an 'lat', 'lon' array 
377
     * @param array $profile1 second point as an 'lat', 'lon' array 
378
     * @return float distance in km
379
     */
380
    public static function geoDistance($point1, $profile1)
381
    {
382
383
        $distIntermediate = sin(deg2rad($point1['lat'])) * sin(deg2rad($profile1['lat'])) +
384
            cos(deg2rad($point1['lat'])) * cos(deg2rad($profile1['lat'])) * cos(deg2rad($point1['lon'] - $profile1['lon']));
385
        $dist = rad2deg(acos($distIntermediate)) * 60 * 1.1852;
386
        return(round($dist));
387
    }
388
}