Completed
Push — master ( 03ec64...ee79b5 )
by
unknown
07:25
created

Profile   F

Complexity

Total Complexity 150

Size/Duplication

Total Lines 786
Duplicated Lines 3.82 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 3
Bugs 1 Features 0
Metric Value
dl 30
loc 786
rs 3.9999
c 3
b 1
f 0
wmc 150
lcom 1
cbo 11

25 Methods

Rating   Name   Duplication   Size   Complexity  
F __construct() 20 236 34
A profileFromRealm() 0 7 2
A updateFreshness() 0 3 1
A getFreshness() 0 6 2
B testCache() 0 15 5
A updateCache() 0 7 1
A incrementDownloadStats() 0 8 3
B getUserDownloadStats() 0 19 6
A addAttribute() 0 8 3
A addSupportedEapMethod() 0 7 1
A destroy() 0 6 1
A flushSupportedEapMethods() 0 4 1
A setAnonymousIDSupport() 0 3 2
A setRealmCheckUser() 0 6 3
A setInputVerificationPreference() 0 6 3
A setRealm() 0 5 1
A getEapMethodsinOrderOfPreference() 0 12 4
C getAttributes() 0 32 13
C isEapTypeDefinitionComplete() 0 37 13
C listDevices() 0 45 13
D getCollapsedAttributes() 10 45 18
A getSufficientConfig() 0 18 3
B readyForShowtime() 0 20 9
B prepShowtime() 0 18 6
A getShowtime() 0 11 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

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 Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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

1
<?php
2
3
/* * ********************************************************************************
4
 * (c) 2011-15 GÉANT on behalf of the GN3, GN3plus and GN4 consortia
5
 * License: see the LICENSE file in the root directory
6
 * ********************************************************************************* */
7
?>
8
<?php
9
10
/**
11
 * This file contains the Profile class.
12
 *
13
 * @author Stefan Winter <[email protected]>
14
 * @author Tomasz Wolniewicz <[email protected]>
15
 *
16
 * @package Developer
17
 *
18
 */
19
/**
20
 * necessary includes
21
 */
22
require_once('Helper.php');
23
require_once('IdP.php');
24
require_once('EAP.php');
25
require_once('X509.php');
26
require_once('EntityWithDBProperties.php');
27
require_once('devices/devices.php');
28
29
define("HIDDEN", -1);
30
define("AVAILABLE", 0);
31
define("UNAVAILABLE", 1);
32
define("INCOMPLETE", 2);
33
define("NOTCONFIGURED", 3);
34
35
/**
36
 * This class represents an EAP Profile.
37
 * Profiles can inherit attributes from their IdP, if the IdP has some. Otherwise,
38
 * one can set attribute in the Profile directly. If there is a conflict between
39
 * IdP-wide and Profile-wide attributes, the more specific ones (i.e. Profile) win.
40
 *
41
 * @author Stefan Winter <[email protected]>
42
 * @author Tomasz Wolniewicz <[email protected]>
43
 *
44
 * @license see LICENSE file in root directory
45
 *
46
 * @package Developer
47
 */
48
class Profile extends EntityWithDBProperties {
49
50
    /**
51
     * This array holds the supported EAP types (in "array" OUTER/INNER representation). They are not synced against the DB after instantiation.
52
     * 
53
     * @var array
54
     */
55
    private $priv_eaptypes;
56
    
57
    /**
58
     * Class constructor for existing profiles (use IdP::newProfile() to actually create one). Retrieves all attributes and 
59
     * supported EAP types from the DB and stores them in the priv_ arrays.
60
     * 
61
     * @param int $p_id identifier of the profile in the DB
62
     * @param IdP $idp_object optionally, the institution to which this Profile belongs. Saves the construction of the IdP instance. If omitted, an extra query and instantiation is executed to find out.
63
     */
64
    public function __construct($p_id, $idp_object = 0) {
65
        debug(3, "--- BEGIN Constructing new Profile object ... ---\n");
66
        
67
        $this->databaseType = "INST";
68
        $this->entityOptionTable = "profile_option";
69
        $this->entityIdColumn = "profile_id";
70
        $this->identifier = $p_id;
71
        $this->attributes = [];
72
        
73
        $profile = DBConnection::exec($this->databaseType, "SELECT inst_id, realm, use_anon_outer, checkuser_outer, checkuser_value, verify_userinput_suffix as verify, hint_userinput_suffix as hint FROM profile WHERE profile_id = $p_id");
74
        debug(4, $profile);
75
        if (!$profile || $profile->num_rows == 0) {
76
            debug(2, "Profile $p_id not found in database!\n");
77
            throw new Exception("Profile $p_id not found in database!");
78
            return;
0 ignored issues
show
Unused Code introduced by
return; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
79
        }
80
        $a = mysqli_fetch_object($profile);
81
        if (!($idp_object instanceof IdP)) {
82
            $this->institution = $a->inst_id;
83
            $idp = new IdP($this->institution);
84
        } else {
85
            $idp = $idp_object;
86
            $this->institution = $idp->name;
87
        }
88
        $temparray = [];
89
        $optioninstance = Options::instance();
90
91
        $this->realm = $a->realm;
92
        $this->use_anon_outer = $a->use_anon_outer;
93
        $this->lang_index = CAT::get_lang();
94
        $this->inst_name = $idp->name;
95
96
        $this->checkuser_outer = $a->checkuser_outer;
0 ignored issues
show
Bug introduced by
The property checkuser_outer does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
97
        $this->checkuser_value = $a->checkuser_value;
0 ignored issues
show
Bug introduced by
The property checkuser_value does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
98
        $this->verify = $a->verify;
0 ignored issues
show
Bug introduced by
The property verify does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
99
        $this->hint = $a->hint;
0 ignored issues
show
Bug introduced by
The property hint does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
100
        
101
        // fetch all atributes from this profile from DB
102
103
        $AllAttributes = DBConnection::exec($this->databaseType, "SELECT option_name, option_value, device_id, eap_method_id as method, row 
104
                FROM $this->entityOptionTable
105
                WHERE $this->entityIdColumn = $this->identifier");
106
107
        while ($a = mysqli_fetch_object($AllAttributes)) {
108
109
            $optinfo = $optioninstance->optionType($a->option_name);
110
            $lang = "";
111
            if ($optinfo['type'] != "file") {
112
                $temparray[] = [
113
                    "name" => $a->option_name,
114
                    "value" => $a->option_value,
115
                    "level" => ($a->device_id == NULL && $a->method == 0 ? "Profile" : "Method" ),
116
                    "row" => $a->row,
117
                    "device" => $a->device_id,
118
                    "flag" => $optinfo['flag'],
119
                    "eapmethod" => EAP::EAPMethodArrayFromId($a->method)];
120
            } else {
121
                // suppress E_NOTICE on the following... we are testing *if*
122
                // we have a serialized value - so not having one is fine and
123
                // shouldn't throw E_NOTICE
124
                if (@unserialize($a->option_value) !== FALSE) { // multi-lang
125
                    $content = unserialize($a->option_value);
126
                    $lang = $content['lang'];
127
                    $content = $content['content'];
128
                } else { // single lang, direct content
129
                    $content = $a->option_value;
130
                }
131
132
                $content = base64_decode($content);
133
134
                $temparray[] = [
135
                    "name" => $a->option_name,
136
                    "value" => ( $lang == "" ? $content : serialize(['lang' => $lang, 'content' => $content])),
137
                    "level" => ($a->device_id == NULL && $a->method == 0 ? "Profile" : "Method" ),
138
                    "row" => $a->row,
139
                    "flag" => $optinfo['flag'],
140
                    "device" => $a->device_id,
141
                    "eapmethod" => EAP::EAPMethodArrayFromId($a->method)];
142
            }
143
144
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
145
              "name" => $a->option_name,
146
              "row" => $a->row,
147
              "level" => ($a->device_id == NULL && $a->method == NULL ? "Profile" : "Method" ),
148
              "device" => $a->device_id,
149
              "eapmethod" => $a->method);
150
151
             */
152
        }
153
        // add internal attributes
154
155
        $temparray[] = ["name" => "internal:profile_count",
156
            "value" => $idp->profileCount(),
157
            "level" => "Profile",
158
            "row" => 0,
159
            "flag" => NULL,
160
            "device" => NULL,
161
            "eapmethod" => NULL];
162
        
163
        $temparray[] = ["name" => "internal:checkuser_outer",
164
            "value" => $this->checkuser_outer,
165
            "level" => "Profile",
166
            "row" => 0,
167
            "flag" => NULL,
168
            "device" => NULL,
169
            "eapmethod" => NULL];
170
        
171
        $temparray[] = ["name" => "internal:checkuser_value",
172
            "value" => $this->checkuser_value,
173
            "level" => "Profile",
174
            "row" => 0,
175
            "flag" => NULL,
176
            "device" => NULL,
177
            "eapmethod" => NULL];
178
        
179
        $temparray[] = ["name" => "internal:verify_userinput_suffix",
180
            "value" => $this->verify,
181
            "level" => "Profile",
182
            "row" => 0,
183
            "flag" => NULL,
184
            "device" => NULL,
185
            "eapmethod" => NULL];
186
        
187
        $temparray[] = ["name" => "internal:hint_userinput_suffix",
188
            "value" => $this->hint,
189
            "level" => "Profile",
190
            "row" => 0,
191
            "flag" => NULL,
192
            "device" => NULL,
193
            "eapmethod" => NULL];
194
        
195
        // strip local@ off of the realm value
196
        $strippedrealm = preg_replace('/^.*@/', '', $this->realm);
197
        $temparray[] = ["name" => "internal:realm",
198
            "value" => $strippedrealm,
199
            "level" => "Profile",
200
            "row" => 0,
201
            "flag" => NULL,
202
            "device" => NULL,
203
            "eapmethod" => NULL];
204
        // FALSE or TRUE
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
205
        $temparray[] = ["name" => "internal:use_anon_outer",
206
            "value" => $this->use_anon_outer,
207
            "level" => "Profile",
208
            "row" => 0,
209
            "flag" => NULL,
210
            "device" => NULL,
211
            "eapmethod" => NULL];
212
        // the local part, if set (otherwise use default value)
213
        if (preg_match('/@/', $this->realm)) {
214
            $temparray[] = ["name" => "internal:anon_local_value",
215
                "value" => substr($this->realm, 0, strpos($this->realm, '@')),
216
                "level" => "Profile",
217
                "row" => 0,
218
                "flag" => NULL,
219
                "device" => NULL,
220
                "eapmethod" => NULL];
221
        } else {
222
            $temparray[] = ["name" => "internal:anon_local_value",
223
                "value" => "anonymous",
224
                "level" => "Profile",
225
                "row" => 0,
226
                "flag" => NULL,
227
                "device" => NULL,
228
                "eapmethod" => NULL];
229
        }
230
231
        // now, fetch IdP-wide attributes
232
233
        $idpoptions = $idp->getAttributes();
234
235
        foreach ($idpoptions as $the_attr)
236
            $temparray[] = [
237
                "name" => $the_attr["name"],
238
                "value" => $the_attr["value"],
239
                "level" => $the_attr["level"],
240
                "row" => $the_attr["row"],
241
                "flag" => $the_attr["flag"],
242
                "device" => NULL,
243
                "eapmethod" => NULL,
244
            ];
245
246
        // check sanity (device and eapmethod are mutually exclusive) and first batch of adding (method level)
247
248
        foreach ($temparray as $attrib) {
249
            if ($attrib["device"] != NULL && $attrib["eapmethod"] != NULL)
250
                debug(2, "Sanity check failed - device and eapmethod are set!\n");
251
        }
252
253
        foreach ($temparray as $attrib) {
254
            if ($attrib["device"] != NULL || $attrib["eapmethod"] != NULL)
255
                $this->attributes[] = $attrib;
256
        }
257
        // pick all attributes which are profile specific and place into final array if no eap/device-specific exists
258
259 View Code Duplication
        foreach ($temparray as $attrib) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
260
            if ($attrib["level"] == "Profile") {
261
                $ignore = "";
262
                foreach ($this->attributes as $approved_attrib)
263
                    if ($attrib["name"] == $approved_attrib["name"] && $approved_attrib["level"] != "IdP" && $approved_attrib["level"] != "Profile")
264
                        $ignore = "YES";
265
                if ($ignore != "YES")
266
                    $this->attributes[] = $attrib;
267
            }
268
        }
269
270
        // now, add IdP-wide attribs
271
272 View Code Duplication
        foreach ($temparray as $attrib) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
273
            if ($attrib["level"] == "IdP") {
274
                $ignore = "";
275
                foreach ($this->attributes as $approved_attrib)
276
                    if ($attrib["name"] == $approved_attrib["name"] && $approved_attrib["level"] != "IdP")
277
                        $ignore = "YES";
278
                if ($ignore != "YES")
279
                    $this->attributes[] = $attrib;
280
            }
281
        }
282
283
        $this->name = getLocalisedValue($this->getAttributes('profile:name', 0, 0), $this->lang_index);
284
285
        $eap_m = DBConnection::exec($this->databaseType, "SELECT eap_method_id 
286
                                                        FROM supported_eap supp 
287
                                                        WHERE supp.profile_id = $this->identifier 
288
                                                        ORDER by preference");
289
        $returnarray = [];
290
        while ($eap = (mysqli_fetch_object($eap_m))) {
291
            $eaptype = EAP::EAPMethodArrayFromId($eap->eap_method_id);
292
            $returnarray[] = $eaptype;
293
        }
294
        debug(4, "Looks like this profile supports the following EAP types: ");
295
        debug(4, $returnarray);
296
        $this->priv_eaptypes = $returnarray;
297
298
        debug(3, "--- END Constructing new Profile object ... ---\n");
299
    }
300
301
    /**
302
     * find a profile, given its realm
303
     */
304
    public static function profileFromRealm($realm) {
305
        $exec_query = DBConnection::exec($this->databaseType, "SELECT profile_id FROM profile WHERE realm LIKE '%@$realm'");
0 ignored issues
show
Bug introduced by
The variable $this does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
306
        if ($a = mysqli_fetch_object($exec_query)) {
307
            return $a->profile_id;
308
        } else
309
            return FALSE;
310
    }
311
    
312
    /**
313
     * update the last_changed timestamp for this profile
314
     */
315
    public function updateFreshness() {
316
        DBConnection::exec($this->databaseType, "UPDATE profile SET last_change = CURRENT_TIMESTAMP WHERE profile_id = $this->identifier");
317
    }
318
319
    /**
320
     * gets the last-modified timestamp (useful for caching "dirty" check)
321
     */
322
    public function getFreshness() {
323
        $exec_update = DBConnection::exec($this->databaseType, "SELECT last_change FROM profile WHERE profile_id = $this->identifier");
324
        if ($a = mysqli_fetch_object($exec_update)) {
325
            return $a->last_change;
326
        }
327
    }
328
329
    /**
330
     * tests if the configurator needs to be regenerated
331
     * returns the configurator path or NULL if regeneration is required
332
     */
333
334
    /**
335
     * This function tests if the configurator needs to be regenerated (properties of the Profile may have changed since the last configurator generation).
336
     * 
337
     * @param string $device device ID to check
338
     * @return mixed a string with the path to the configurator download, or NULL if it needs to be regenerated
339
     */
340
    public function testCache($device) {
341
        $returnValue = NULL;
342
        $escapedDevice = DBConnection::escape_value($this->databaseType, $device);
343
        $result = DBConnection::exec($this->databaseType, "SELECT download_path, mime, UNIX_TIMESTAMP(installer_time) AS tm FROM downloads WHERE profile_id = $this->identifier AND device_id = '$escapedDevice' AND lang = '$this->lang_index'");
344
        if ($result && $cache = mysqli_fetch_object($result)) {
345
            $exec_update = DBConnection::exec($this->databaseType, "SELECT UNIX_TIMESTAMP(last_change) AS last_change FROM profile WHERE profile_id = $this->identifier");
346
            if ($lc = mysqli_fetch_object($exec_update)->last_change) {
347
                if ($lc < $cache->tm) {
348
                    debug(4, "Installer cached:$cache->download_path\n");
349
                    $returnValue = ['cache'=>$cache->download_path,'mime'=>$cache->mime];
350
                }
351
            }
352
        }
353
        return $returnValue;
354
    }
355
356
    /**
357
     * Updates database with new installler location
358
     * 
359
     * @param string device the device identifier string
360
     * @param string path the path where the new installer can be found
361
     */
362
    public function updateCache($device, $path,$mime) {
363
        $escapedDevice = DBConnection::escape_value($this->databaseType, $device);
364
        $escapedPath   = DBConnection::escape_value($this->databaseType, $path);
365
        DBConnection::exec($this->databaseType, "INSERT INTO downloads (profile_id,device_id,download_path,mime,lang,installer_time) 
366
                                        VALUES ($this->identifier, '$escapedDevice', '$escapedPath', '$mime', '$this->lang_index', CURRENT_TIMESTAMP ) 
367
                                        ON DUPLICATE KEY UPDATE download_path = '$escapedPath', mime = '$mime', installer_time = CURRENT_TIMESTAMP");
368
    }
369
370
    /**
371
     * Log a new download for our stats
372
     * 
373
     * @param device the device id string
374
     * @param area either admin or user
375
     * @return TRUE if incrementing worked, FALSE if not
376
     */
377
    public function incrementDownloadStats($device, $area) {
378
        $device = DBConnection::escape_value($this->databaseType, $device);
379
        if ($area == "admin" || $area == "user") {
380
            DBConnection::exec(Profile::$DB_TYPE, "INSERT INTO downloads (profile_id, device_id, lang, downloads_$area) VALUES ($this->identifier, '$device','$this->lang_index', 1) ON DUPLICATE KEY UPDATE downloads_$area = downloads_$area + 1");
381
            return TRUE;
382
        }
383
        return FALSE;
384
    }
385
386
    /**
387
     * Retrieve current download stats from database, either for one specific device or for all devices
388
     * @param string $device the device id string
389
     * @return mixed user downloads of this profile; if device is given, returns the counter as int, otherwise an array with devicename => counter
390
     */
391
    public function getUserDownloadStats($device = 0) {
392
        $returnarray = [];
393
        $numbers_q = DBConnection::exec($this->databaseType, "SELECT device_id, SUM(downloads_user) AS downloads_user FROM downloads WHERE profile_id = $this->identifier GROUP BY device_id");
394
        while ($a = mysqli_fetch_object($numbers_q))
395
            $returnarray[$a->device_id] = $a->downloads_user;
396
        if ($device !== 0) {
397
            if (isset($returnarray[$device]))
398
                return $returnarray[$device];
399
            else
400
                return 0;
401
        }
402
        // we should pretty-print the device names
403
        $finalarray = [];
404
        $devlist = Devices::listDevices();
405
        foreach ($returnarray as $dev_id => $count)
406
            if (isset($devlist[$dev_id]))
407
                    $finalarray[$devlist[$dev_id]['display']] = $count;
408
        return $finalarray;
409
    }
410
411
    /**
412
     * adds an attribute to this profile; not the usual function from EntityWithDBProperties
413
     * because this class also has per-EAP-type and per-device sub-settings
414
     *
415
     * @param string $attr_name name of the attribute to set
0 ignored issues
show
Bug introduced by
There is no parameter named $attr_name. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
416
     * @param string $attr_value value of the attribute to set
0 ignored issues
show
Bug introduced by
There is no parameter named $attr_value. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
417
     * @param int $eap_type identifier of the EAP type in the database. 0 if the attribute is valid for all EAP types.
0 ignored issues
show
Bug introduced by
There is no parameter named $eap_type. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
418
     * @param string $device identifier of the device in the databse. Omit the argument if attribute is valid for all devices.
419
     */
420
    public function addAttribute($attrName, $attrValue, $eapType, $device = 0) {
421
        $escapedAttrName = DBConnection::escape_value(Profile::$DB_TYPE, $attrName);
422
        $escapedAttrValue = DBConnection::escape_value(Profile::$DB_TYPE, $attrValue);
423
        
424
        DBConnection::exec($this->databaseType, "INSERT INTO $this->entityOptionTable ($this->entityIdColumn, option_name, option_value, eap_method_id" . ($device !== 0 ? ",device_id" : "") . ") 
425
                          VALUES(". $this->identifier . ", '$escapedAttrName', '$escapedAttrValue', $eapType" . ($device !== 0 ? ",'". DBConnection::escape_value($this->databaseType, $device) . "'" : "" ) . ")");
426
        $this->updateFreshness();
427
    }
428
429
    /**
430
     * register new supported EAP method for this profile
431
     *
432
     * @param array $type The EAP Type, as defined in class EAP
433
     * @param int $preference preference of this EAP Type. If a preference value is re-used, the order of EAP types of the same preference level is undefined.
434
     *
435
     */
436
    public function addSupportedEapMethod($type, $preference) {
437
        DBConnection::exec($this->databaseType, "INSERT INTO supported_eap (profile_id, eap_method_id, preference) VALUES ("
438
                . $this->identifier . ", "
439
                . EAP::EAPMethodIdFromArray($type) . ", "
440
                . $preference . ")");
441
        $this->updateFreshness();
442
    }
443
444
    /**
445
     * Deletes the profile from database and uninstantiates itself.
446
     *
447
     */
448
    public function destroy() {
449
        DBConnection::exec($this->databaseType, "DELETE FROM profile_option WHERE profile_id = $this->identifier");
450
        DBConnection::exec($this->databaseType, "DELETE FROM supported_eap WHERE profile_id = $this->identifier");
451
        DBConnection::exec($this->databaseType, "DELETE FROM profile WHERE profile_id = $this->identifier");
452
        unset($this);
453
    }
454
455
    /**
456
     * Removes all supported EAP methods
457
     */
458
    public function flushSupportedEapMethods() {
459
        DBConnection::exec($this->databaseType, "DELETE FROM supported_eap WHERE profile_id = $this->identifier");
460
        $this->updateFreshness();
461
    }
462
463
    /** Toggle anonymous outer ID support.
464
     *
465
     * @param boolean $shallwe TRUE to enable outer identities (needs valid $realm), FALSE to disable
466
     *
467
     */
468
    public function setAnonymousIDSupport($shallwe) {
469
        DBConnection::exec($this->databaseType, "UPDATE profile SET use_anon_outer = " . ($shallwe == true ? "1" : "0") . " WHERE profile_id = $this->identifier");
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
470
    }
471
    
472
    /** Toggle special username for realm checks
473
     *
474
     * @param boolean $shallwe TRUE to enable outer identities (needs valid $realm), FALSE to disable
475
     * @param string $localpart the username
476
     *
477
     */
478
    public function setRealmCheckUser($shallwe,$localpart = NULL) {
479
        DBConnection::exec($this->databaseType, 
480
                "UPDATE profile SET checkuser_outer = " . ($shallwe == true ? "1" : "0") . 
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
481
                ( $localpart !== NULL ? ", checkuser_value = '$localpart' " : "") .
482
                " WHERE profile_id = $this->identifier");
483
    }
484
485
    /** should username be verified or even prefilled?
486
     * 
487
     */
488
    public function setInputVerificationPreference($verify,$hint) {
489
        DBConnection::exec($this->databaseType, 
490
                "UPDATE profile SET verify_userinput_suffix = " . ($verify == true ? "1" : "0") . 
491
                ", hint_userinput_suffix = ". ($hint == true ? "1" : "0") .
492
                " WHERE profile_id = $this->identifier");
493
    }
494
    
495
    /**
496
     * Specifies the realm of this profile.
497
     * 
498
     * @param string $realm the realm (potentially with the local@ part that should be used for anonymous identities)
499
     */
500
    public function setRealm($realm) {
501
        $escapedRealm = DBConnection::escape_value($this->databaseType, $realm);
502
        DBConnection::exec(Profile::$DB_TYPE, "UPDATE profile SET realm = '$escapedRealm' WHERE profile_id = $this->identifier");
503
        $this->realm = $escapedRealm;
504
    }
505
506
    /**
507
     * Produces an array of EAP methods supported by this profile, ordered by preference
508
     * 
509
     * @param int $complete_only if set and non-zero limits the output to methods with complete information
510
     * @return array list of EAP methods, (in "array" OUTER/INNER representation)
511
     */
512
    public function getEapMethodsinOrderOfPreference($complete_only = 0) {
513
        $temparray = [];
514
515
        if ($complete_only == 0) {
516
            return $this->priv_eaptypes;
517
        } else {
518
            foreach ($this->priv_eaptypes as $type)
519
                if ($this->isEapTypeDefinitionComplete($type) === true)
520
                    $temparray[] = $type;
521
            return($temparray);
522
        }
523
    }
524
525
    /** Returns an array of the profile's attributes.
526
     * 
527
     * @param string option_name the name of a specific option. If set, only returns option values for this option name
528
     * @param eapmethod the EAP type, in array ("OUTER/INNER") notation. If set, returns only attributes which are specific to that EAP type
529
     * @param string device the device ID string. If set, returns only attributes which are specific to that device
530
     * @return array attributes of the profile
531
     */
532
    public function getAttributes($option_name = 0, $eapmethod = 0, $device = 0) {
533
534
        $outarray = [];
535
        $temparray = [];
536
        if ($eapmethod) {
537
            foreach ($this->attributes as $the_attr) 
538
                if ($the_attr["eapmethod"] == $eapmethod)
539
                    $temparray[] = $the_attr;
540
        } else
541
        if ($device) {
542
            foreach ($this->attributes as $the_attr)
543
                if ($the_attr["device"] == $device)
544
                    $temparray[] = $the_attr;
545
        };
546
547
        foreach ($this->attributes as $the_attr)
548
            if ($the_attr["device"] == NULL && $the_attr["eapmethod"] == NULL)
549
                $temparray[] = $the_attr;
550
551
552
        // return only options by one name, if asked for
553
554
        if ($option_name) {
555
            foreach ($temparray as $the_attr)
556
                if ($the_attr["name"] == $option_name)
557
                    $outarray[] = $the_attr;
558
        } else {
559
            $outarray = $temparray;
560
        }
561
562
        return $outarray;
563
    }
564
565
    /**
566
     * Performs a sanity check for a given EAP type - did the admin submit enough information to create installers for him?
567
     * 
568
     * @param array $eaptype the EAP type in "array" OUTER/INNER representation
569
     * @return mixed TRUE if the EAP type is complete; an array of missing attribues if it's incomplete; FALSE if it's incomplete for other reasons
570
     */
571
    public function isEapTypeDefinitionComplete($eaptype) {
572
        $missing = [];
573
        // TLS, TTLS, PEAP outer phase need a CA certficate and a Server Name
574
        if ($eaptype["OUTER"] == PEAP || $eaptype["OUTER"] == TLS || $eaptype["OUTER"] == TTLS || $eaptype["OUTER"] == FAST) {
575
576
            $cn_option = $this->getAttributes("eap:server_name", $eaptype);
0 ignored issues
show
Documentation introduced by
$eaptype is of type array<string,?,{"OUTER":"?"}>, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
577
            $ca_option = $this->getAttributes("eap:ca_file", $eaptype);
0 ignored issues
show
Documentation introduced by
$eaptype is of type array<string,?,{"OUTER":"?"}>, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
578
            /* echo "<pre>";
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
579
              print_r($options);
580
              echo "</pre>"; */
581
            if (count($ca_option) > 0 && count($cn_option) > 0) {// see if we have at least one root CA cert
582
                foreach ($ca_option as $one_ca) {
583
                    $x509 = new X509();
584
                    $ca_parsed = $x509->processCertificate($one_ca['value']);
585
                    if ($ca_parsed['root'] == 1)
586
                        return true;
587
                }
588
                $missing[] = "eap:ca_file";
589
            }
590
            if (count($ca_option) == 0)
591
                $missing[] = "eap:ca_file";
592
            if (count($cn_option) == 0)
593
                $missing[] = "eap:server_name";
594
            return $missing;
595
        } elseif ($eaptype["OUTER"] == PWD || $eaptype["INNER"] == NE_SILVERBULLET) {
596
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
597
              $cn_option = $this->getAttributes("eap:server_name", $eaptype);
598
              if (count($cn_option) > 0) */
599
            return true;
600
            /* $missing[] = "eap:server_name";
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
601
              return $missing; */
602
        }
603
604
        // we have no idea; let's say false
605
606
        return false;
607
    }
608
609
    /**
610
     * list all devices marking their availabiblity and possible redirects
611
     *
612
     * @param string $locale for text-based attributes, either returns values for the default value, or if specified here, in the locale specified
613
     * @return array of device ids display names and their status
614
     */
615
    public function listDevices($locale = 0) {
616
        if ($locale == 0)
617
            $locale = $this->lang_index;
618
        $redirect_url = 0;
0 ignored issues
show
Unused Code introduced by
$redirect_url is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
619
        $message = 0;
0 ignored issues
show
Unused Code introduced by
$message is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
620
        $returnarray = [];
621
        $redirect = $this->getAttributes("device-specific:redirect", 0, 0);
622
        if ($redirect) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $redirect of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
623
            $v = unserialize($redirect[0]['value']);
624
            return [['id' => '0', 'redirect' => $v['content']]];
625
        }
626
        $preferred_eap = $this->getEapMethodsinOrderOfPreference(1);
627
        $EAP_options = [];
628
        foreach (Devices::listDevices() as $d => $D) {
629
            $factory = new DeviceFactory($d);
630
            $dev = $factory->device;
631
            $redirect_url = getLocalisedValue($this->getAttributes("device-specific:redirect", 0, $d), $locale);
632
            $dev_status = AVAILABLE;
633
            if(isset($D['options']) && isset($D['options']['message']) && $D['options']['message']) 
634
               $message = $D['options']['message'];
635
            else
636
               $message = 0;
637
638
            if ($redirect_url === 0) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $redirect_url (string) and 0 (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
639
                if(isset($D['options']) && isset($D['options']['redirect']) && $D['options']['redirect']) {
640
                   $dev_status = HIDDEN;
641
                }  else {
642
                   $eap = $dev->getPreferredEapType($preferred_eap);
643
                   if ($eap) {
644
                       if (isset($EAP_options["eap-specific:customtext"][serialize($eap)]))
645
                           $eap_customtext = $EAP_options["eap-specific:customtext"][serialize($eap)];
646
                       else {
647
                           $eap_customtext = getLocalisedValue($this->getAttributes("eap-specific:customtext", $eap, 0), $locale);
648
                           $EAP_options["eap-specific:customtext"][serialize($eap)] = $eap_customtext;
649
                       }
650
                       $device_customtext = getLocalisedValue($this->getAttributes("device-specific:customtext", 0, $d), $locale);
651
                   } else {
652
                    $dev_status = UNAVAILABLE;
653
                   }
654
               }
655
            }
656
            $returnarray[] = ['id' => $d, 'display' => $D['display'], 'status' => $dev_status, 'redirect' => $redirect_url, 'eap_customtext' => $eap_customtext, 'device_customtext' => $device_customtext, 'message' => $message, 'options'=>$D['options']];
0 ignored issues
show
Bug introduced by
The variable $eap_customtext does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $device_customtext does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
657
        }
658
        return $returnarray;
659
    }
660
661
    /**
662
     * prepare profile attributes for device modules
663
     * Gets profile attributes taking into account the most specific level on which they may be defined
664
     * as wel as the chosen language.
665
     * can be called with an optional $eap argument
666
     * 
667
     * @param array $eap if specified, retrieves attributes specific to the given EAP type
668
     * @return array list of attributes in collapsed style (index is the attrib name, value is an array of different values)
669
     */
670
    public function getCollapsedAttributes($eap = 0) {
671
        $attr = $this->getAttributes(0, $eap);
672
        $temp1 = [];
673
        foreach ($attr as $b) {
674
            $name = $b['name'];
675
            $temp1[] = $name;
676
            $level = $b['level'];
677
//            $S[$l] = $z[$l];
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
678
            $value = $b['value'];
679
            if (!isset($temp[$name][$level]))
680
                $temp[$name][$level] = [];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$temp was never initialized. Although not strictly required by PHP, it is generally a good practice to add $temp = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
681
            if ($b['flag'] == 'ML') {
682
                $v = unserialize($value);
683
                $value = [$v['lang'] => $v['content']];
684
            }
685
            $temp[$name][$level][] = $value;
0 ignored issues
show
Bug introduced by
The variable $temp does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
686
            $flags[$name] = $b['flag'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$flags was never initialized. Although not strictly required by PHP, it is generally a good practice to add $flags = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
687
        }
688
        foreach ($temp1 as $name) {
689
            if ($flags[$name] == 'ML') {
0 ignored issues
show
Bug introduced by
The variable $flags does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
690
                $S = [];
691 View Code Duplication
                if (isset($temp[$name]['Profile'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
692
                    foreach ($temp[$name]['Profile'] as $z)
693
                        foreach ($z as $l => $w)
694
                            $S[$l] = $w;
695
                }
696 View Code Duplication
                if (!$S && isset($temp[$name]['IdP'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $S of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
697
                    foreach ($temp[$name]['IdP'] as $z)
698
                        foreach ($z as $l => $w)
699
                            $S[$l] = $w;
700
                }
701
                $out[$name]['langs'] = $S;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$out was never initialized. Although not strictly required by PHP, it is generally a good practice to add $out = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
702
                if (isset($S[$this->lang_index]) || isset($S['C']))
703
                    $out[$name][0] = (isset($S[$this->lang_index])) ? $S[$this->lang_index] : $S['C'];
0 ignored issues
show
Bug introduced by
The variable $out does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
704
            } else {
705
                if (isset($temp[$name]['Method']))
706
                    $out[$name] = $temp[$name]['Method'];
707
                elseif (isset($temp[$name]['Profile']))
708
                    $out[$name] = $temp[$name]['Profile'];
709
                else
710
                    $out[$name] = $temp[$name]['IdP'];
711
            }
712
        }
713
        return($out);
714
    }
715
716
    /**
717
     * 
718
     */
719
    public function getSufficientConfig() {
720
        $result = DBConnection::exec($this->databaseType, "SELECT sufficient_config FROM profile WHERE profile_id = " . $this->identifier);
721
        $r = mysqli_fetch_row($result);
722
        /* echo "<pre>";
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
723
          print_r($r);
724
          echo "</pre>"; */
725
        if ($r[0] === NULL) { // we have never recorded the state; do it now
726
            // this case only occurs during DB migration from
727
            // 1.0 to 1.1 until everybody has run their profile
728
            // view or updated once
729
            $this->prepShowtime();
730
            return $this->getSufficientConfig();
731
        } elseif ($r[0] == "0") {
732
            return FALSE;
733
        } else {
734
            return TRUE;
735
        }
736
    }
737
738
    /**
739
     * Checks if the profile has enough information to have something to show to end users. This does not necessarily mean
740
     * that there's a fully configured EAP type - it is sufficient if a redirect has been set for at least one device.
741
     * 
742
     * @return boolean TRUE if enough information for showtime is set; FALSE if not
743
     */
744
    public function readyForShowtime() {
745
        $proper_config = FALSE;
746
        $attribs = $this->getCollapsedAttributes();
747
        // do we have enough to go live? Check if any of the configured EAP methods is completely configured ...
748
        if (sizeof($this->getEapMethodsinOrderOfPreference(1)) > 0)
749
            $proper_config = TRUE;
750
        // if not, it could still be that general redirect has been set
751
        if (!$proper_config) {
752
            if (isset($attribs['device-specific:redirect']))
753
                $proper_config = TRUE;
754
            // TODO: or maybe just a per-device redirect? would be good enough...
755
        }
756
        // do we know at least one SSID to configure, or work with wired? If not, it's not ready...
757
        if (!isset($attribs['media:SSID']) &&
758
                !isset($attribs['media:SSID_with_legacy']) &&
759
                (!isset(Config::$CONSORTIUM['ssid']) || count(Config::$CONSORTIUM['ssid']) == 0) &&
760
                !isset($attribs['media:wired']))
761
            $proper_config = FALSE;
762
        return $proper_config;
763
    }
764
765
    /**
766
     * set the showtime and QR-user attributes if prepShowTime says that there is enough info *and* the admin flagged the profile for showing
767
     */
768
    public function prepShowtime() {
769
        $proper_config = $this->readyForShowtime();
770
        if ($proper_config)
771
            DBConnection::exec($this->databaseType, "UPDATE profile SET sufficient_config = TRUE WHERE profile_id = " . $this->identifier);
772
        else
773
            DBConnection::exec($this->databaseType, "UPDATE profile SET sufficient_config = FALSE WHERE profile_id = " . $this->identifier);
774
        $attribs = $this->getCollapsedAttributes();
775
        // if not enough info to go live, set FALSE
776
        // even if enough info is there, admin has the ultimate say: 
777
        //   if he doesn't want to go live, no further checks are needed, set FALSE as well
778
        if (!$proper_config || !isset($attribs['profile:production']) || (isset($attribs['profile:production']) && $attribs['profile:production'][0] != "on")) {
779
            DBConnection::exec($this->databaseType, "UPDATE profile SET showtime = FALSE WHERE profile_id = " . $this->identifier);
780
            return;
781
        } else { 
782
            DBConnection::exec($this->databaseType, "UPDATE profile SET showtime = TRUE WHERE profile_id = " . $this->identifier);
783
            return;
784
        }
785
    }
786
787
    /**
788
     * Checks if the profile is shown (showable) to end users
789
     * @return boolean TRUE if profile is shown; FALSE if not
790
     */
791
    public function getShowtime() {
792
        $result = DBConnection::exec($this->databaseType, "SELECT showtime FROM profile WHERE profile_id = " . $this->identifier);
793
        $r = mysqli_fetch_row($result);
794
        /* echo "<pre>";
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
795
          print_r($r);
796
          echo "</pre>"; */
797
        if ($r[0] == "0")
798
            return FALSE;
799
        else
800
            return TRUE;
801
    }
802
803
    /**
804
     * current language
805
     * @var string
806
     */
807
    private $lang_index;
808
809
    /**
810
     * DB identifier of the parent institution of this profile
811
     * @var int
812
     */
813
    public $institution;
814
815
    /**
816
     * name of the parent institution of this profile in the current language
817
     * @var string
818
     */
819
    public $inst_name;
820
821
    /**
822
     * realm of this profile (empty string if unset)
823
     * @var string
824
     */
825
    public $realm;
826
827
    /**
828
     * boolean value: should anonymous outer IDs be used or not?
829
     * @var boolean
830
     */
831
    public $use_anon_outer;
832
833
}