Completed
Push — authpdo ( c27579 )
by Andreas
05:24
created

auth_plugin_authpdo::_selectUser()   D

Complexity

Conditions 9
Paths 34

Size

Total Lines 34
Code Lines 23

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 34
rs 4.909
cc 9
eloc 23
nc 34
nop 1
1
<?php
2
/**
3
 * DokuWiki Plugin authpdo (Auth Component)
4
 *
5
 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6
 * @author  Andreas Gohr <[email protected]>
7
 */
8
9
// must be run within Dokuwiki
10
if(!defined('DOKU_INC')) die();
11
12
/**
13
 * Class auth_plugin_authpdo
14
 */
15
class auth_plugin_authpdo extends DokuWiki_Auth_Plugin {
16
17
    /** @var PDO */
18
    protected $pdo;
19
20
    /**
21
     * Constructor.
22
     */
23
    public function __construct() {
24
        parent::__construct(); // for compatibility
25
26
        if(!class_exists('PDO')) {
27
            $this->_debug('PDO extension for PHP not found.', -1, __LINE__);
28
            $this->success = false;
29
            return;
30
        }
31
32
        if(!$this->getConf('dsn')) {
33
            $this->_debug('No DSN specified', -1, __LINE__);
34
            $this->success = false;
35
            return;
36
        }
37
38
        try {
39
            $this->pdo = new PDO(
40
                $this->getConf('dsn'),
41
                $this->getConf('user'),
42
                $this->getConf('pass'),
43
                array(
44
                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // always fetch as array
45
                    PDO::ATTR_EMULATE_PREPARES => true, // emulating prepares allows us to reuse param names
46
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // we want exceptions, not error codes
47
                )
48
            );
49
        } catch(PDOException $e) {
50
            $this->_debug($e);
51
            msg($this->getLang('connectfail'), -1);
52
            $this->success = false;
53
            return;
54
        }
55
56
        // can Users be created?
57
        $this->cando['addUser'] = $this->_chkcnf(
58
            array(
59
                'select-user',
60
                'select-user-groups',
61
                'select-groups',
62
                'insert-user',
63
                'insert-group',
64
                'join-group'
65
            )
66
        );
67
68
        // can Users be deleted?
69
        $this->cando['delUser'] = $this->_chkcnf(
70
            array(
71
                'select-user',
72
                'select-user-groups',
73
                'select-groups',
74
                'leave-group'
75
            )
76
        );
77
78
        // can login names be changed?
79
        $this->cando['modLogin'] = $this->_chkcnf(
80
            array(
81
                'select-user',
82
                'select-user-groups',
83
                'update-user-login'
84
            )
85
        );
86
87
        // can passwords be changed?
88
        $this->cando['modPass'] = $this->_chkcnf(
89
            array(
90
                'select-user',
91
                'select-user-groups',
92
                'update-user-pass'
93
            )
94
        );
95
96
        // can real names and emails be changed?
97
        $this->cando['modName'] = $this->cando['modMail'] = $this->_chkcnf(
98
            array(
99
                'select-user',
100
                'select-user-groups',
101
                'update-user-info'
102
            )
103
        );
104
105
        // can groups be changed?
106
        $this->cando['modGroups'] = $this->_chkcnf(
107
            array(
108
                'select-user',
109
                'select-user-groups',
110
                'select-groups',
111
                'leave-group',
112
                'join-group',
113
                'insert-group'
114
            )
115
        );
116
117
        // can a filtered list of users be retrieved?
118
        $this->cando['getUsers'] = $this->_chkcnf(
119
            array(
120
                'list-users'
121
            )
122
        );
123
124
        // can the number of users be retrieved?
125
        $this->cando['getUserCount'] = $this->_chkcnf(
126
            array(
127
                'count-users'
128
            )
129
        );
130
131
        // can a list of available groups be retrieved?
132
        $this->cando['getGroups'] = $this->_chkcnf(
133
            array(
134
                'select-groups'
135
            )
136
        );
137
138
        $this->success = true;
139
    }
140
141
    /**
142
     * Check user+password
143
     *
144
     * @param   string $user the user name
145
     * @param   string $pass the clear text password
146
     * @return  bool
147
     */
148
    public function checkPass($user, $pass) {
149
150
        $data = $this->_selectUser($user);
151
        if($data == false) return false;
152
153
        if(isset($data['hash'])) {
154
            // hashed password
155
            $passhash = new PassHash();
156
            return $passhash->verify_hash($pass, $data['hash']);
157
        } else {
158
            // clear text password in the database O_o
159
            return ($pass == $data['clear']);
160
        }
161
    }
162
163
    /**
164
     * Return user info
165
     *
166
     * Returns info about the given user needs to contain
167
     * at least these fields:
168
     *
169
     * name string  full name of the user
170
     * mail string  email addres of the user
171
     * grps array   list of groups the user is in
172
     *
173
     * @param   string $user the user name
174
     * @param   bool $requireGroups whether or not the returned data must include groups
175
     * @return array containing user data or false
176
     */
177
    public function getUserData($user, $requireGroups = true) {
178
        $data = $this->_selectUser($user);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->_selectUser($user); of type boolean|array adds the type array to the return on line 189 which is incompatible with the return type of the parent method DokuWiki_Auth_Plugin::getUserData of type boolean.
Loading history...
179
        if($data == false) return false;
180
181
        if(isset($data['hash'])) unset($data['hash']);
182
        if(isset($data['clean'])) unset($data['clean']);
183
184
        if($requireGroups) {
185
            $data['grps'] = $this->_selectUserGroups($data);
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->_selectUser($user) on line 178 can also be of type boolean; however, auth_plugin_authpdo::_selectUserGroups() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
186
            if($data['grps'] === false) return false;
187
        }
188
189
        return $data;
190
    }
191
192
    /**
193
     * Create a new User [implement only where required/possible]
194
     *
195
     * Returns false if the user already exists, null when an error
196
     * occurred and true if everything went well.
197
     *
198
     * The new user HAS TO be added to the default group by this
199
     * function!
200
     *
201
     * Set addUser capability when implemented
202
     *
203
     * @param  string $user
204
     * @param  string $clear
205
     * @param  string $name
206
     * @param  string $mail
207
     * @param  null|array $grps
208
     * @return bool|null
209
     */
210
    public function createUser($user, $clear, $name, $mail, $grps = null) {
211
        global $conf;
212
213
        if(($info = $this->getUserData($user, false)) !== false) {
214
            msg($this->getLang('userexists'), -1);
215
            return false; // user already exists
216
        }
217
218
        // prepare data
219
        if($grps == null) $grps = array();
220
        $grps[] = $conf['defaultgroup'];
221
        $grps = array_unique($grps);
222
        $hash = auth_cryptPassword($clear);
223
        $userdata = compact('user', 'clear', 'hash', 'name', 'mail');
224
225
        // action protected by transaction
226
        $this->pdo->beginTransaction();
227
        {
228
            // insert the user
229
            $ok = $this->_query($this->getConf('insert-user'), $userdata);
230
            if($ok === false) goto FAIL;
231
            $userdata = $this->getUserData($user, false);
232
            if($userdata === false) goto FAIL;
233
234
            // create all groups that do not exist, the refetch the groups
235
            $allgroups = $this->_selectGroups();
236
            foreach($grps as $group) {
237
                if(!isset($allgroups[$group])) {
238
                    $ok = $this->addGroup($group);
239
                    if($ok === false) goto FAIL;
240
                }
241
            }
242
            $allgroups = $this->_selectGroups();
243
244
            // add user to the groups
245
            foreach($grps as $group) {
246
                $ok = $this->_joinGroup($userdata, $allgroups[$group]);
0 ignored issues
show
Bug introduced by
It seems like $userdata defined by $this->getUserData($user, false) on line 231 can also be of type boolean; however, auth_plugin_authpdo::_joinGroup() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
247
                if($ok === false) goto FAIL;
248
            }
249
        }
250
        $this->pdo->commit();
251
        return true;
252
253
        // something went wrong, rollback
254
        FAIL:
255
        $this->pdo->rollBack();
256
        $this->_debug('Transaction rolled back', 0, __LINE__);
257
        msg($this->getLang('writefail'), -1);
258
        return null; // return error
259
    }
260
261
    /**
262
     * Modify user data
263
     *
264
     * @param   string $user nick of the user to be changed
265
     * @param   array $changes array of field/value pairs to be changed (password will be clear text)
266
     * @return  bool
267
     */
268
    public function modifyUser($user, $changes) {
269
        // secure everything in transaction
270
        $this->pdo->beginTransaction();
271
        {
272
            $olddata = $this->getUserData($user);
273
            $oldgroups = $olddata['grps'];
274
            unset($olddata['grps']);
275
276
            // changing the user name?
277
            if(isset($changes['user'])) {
278
                if($this->getUserData($changes['user'], false)) goto FAIL;
279
                $params = $olddata;
280
                $params['newlogin'] = $changes['user'];
281
282
                $ok = $this->_query($this->getConf('update-user-login'), $params);
0 ignored issues
show
Bug introduced by
It seems like $params defined by $olddata on line 279 can also be of type boolean; however, auth_plugin_authpdo::_query() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
283
                if($ok === false) goto FAIL;
284
            }
285
286
            // changing the password?
287
            if(isset($changes['pass'])) {
288
                $params = $olddata;
289
                $params['clear'] = $changes['pass'];
290
                $params['hash'] = auth_cryptPassword($changes['pass']);
291
292
                $ok = $this->_query($this->getConf('update-user-pass'), $params);
0 ignored issues
show
Bug introduced by
It seems like $params defined by $olddata on line 288 can also be of type boolean; however, auth_plugin_authpdo::_query() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
293
                if($ok === false) goto FAIL;
294
            }
295
296
            // changing info?
297
            if(isset($changes['mail']) || isset($changes['name'])) {
298
                $params = $olddata;
299
                if(isset($changes['mail'])) $params['mail'] = $changes['mail'];
300
                if(isset($changes['name'])) $params['name'] = $changes['name'];
301
302
                $ok = $this->_query($this->getConf('update-user-info'), $params);
0 ignored issues
show
Bug introduced by
It seems like $params defined by $olddata on line 298 can also be of type boolean; however, auth_plugin_authpdo::_query() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
303
                if($ok === false) goto FAIL;
304
            }
305
306
            // changing groups?
307
            if(isset($changes['grps'])) {
308
                $allgroups = $this->_selectGroups();
309
310
                // remove membership for previous groups
311
                foreach($oldgroups as $group) {
312
                    if(!in_array($group, $changes['grps'])) {
313
                        $ok = $this->_leaveGroup($olddata, $allgroups[$group]);
0 ignored issues
show
Bug introduced by
It seems like $olddata defined by $this->getUserData($user) on line 272 can also be of type boolean; however, auth_plugin_authpdo::_leaveGroup() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
314
                        if($ok === false) goto FAIL;
315
                    }
316
                }
317
318
                // create all new groups that are missing
319
                $added = 0;
320
                foreach($changes['grps'] as $group) {
321
                    if(!isset($allgroups[$group])) {
322
                        $ok = $this->addGroup($group);
323
                        if($ok === false) goto FAIL;
324
                        $added++;
325
                    }
326
                }
327
                // reload group info
328
                if($added > 0) $allgroups = $this->_selectGroups();
329
330
                // add membership for new groups
331
                foreach($changes['grps'] as $group) {
332
                    if(!in_array($group, $oldgroups)) {
333
                        $ok = $this->_joinGroup($olddata, $allgroups[$group]);
0 ignored issues
show
Bug introduced by
It seems like $olddata defined by $this->getUserData($user) on line 272 can also be of type boolean; however, auth_plugin_authpdo::_joinGroup() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
334
                        if($ok === false) goto FAIL;
335
                    }
336
                }
337
            }
338
339
        }
340
        $this->pdo->commit();
341
        return true;
342
343
        // something went wrong, rollback
344
        FAIL:
345
        $this->pdo->rollBack();
346
        $this->_debug('Transaction rolled back', 0, __LINE__);
347
        msg($this->getLang('writefail'), -1);
348
        return false; // return error
349
    }
350
351
    /**
352
     * Delete one or more users
353
     *
354
     * Set delUser capability when implemented
355
     *
356
     * @param   array $users
357
     * @return  int    number of users deleted
358
     */
359
    public function deleteUsers($users) {
360
        $count = 0;
361
        foreach($users as $user) {
362
            if($this->_deleteUser($user)) $count++;
363
        }
364
        return $count;
365
    }
366
367
    /**
368
     * Bulk retrieval of user data [implement only where required/possible]
369
     *
370
     * Set getUsers capability when implemented
371
     *
372
     * @param   int $start index of first user to be returned
373
     * @param   int $limit max number of users to be returned
374
     * @param   array $filter array of field/pattern pairs, null for no filter
375
     * @return  array list of userinfo (refer getUserData for internal userinfo details)
376
     */
377
    public function retrieveUsers($start = 0, $limit = -1, $filter = null) {
378
        if($limit < 0) $limit = 10000; // we don't support no limit
379
        if(is_null($filter)) $filter = array();
380
381
        foreach(array('user', 'name', 'mail', 'group') as $key) {
382
            if(!isset($filter[$key])) {
383
                $filter[$key] = '%';
384
            } else {
385
                $filter[$key] = '%' . $filter[$key] . '%';
386
            }
387
        }
388
        $filter['start'] = $start;
389
        $filter['end'] = $start + $limit;
390
        $filter['limit'] = $limit;
391
392
        $result = $this->_query($this->getConf('list-users'), $filter);
393
        if(!$result) return array();
394
        $users = array();
395
        foreach($result as $row) {
396
            if(!isset($row['user'])) {
397
                $this->_debug("Statement did not return 'user' attribute", -1, __LINE__);
398
                return array();
399
            }
400
            $users[] = $row['user'];
401
        }
402
        return $users;
403
    }
404
405
    /**
406
     * Return a count of the number of user which meet $filter criteria
407
     *
408
     * @param  array $filter array of field/pattern pairs, empty array for no filter
409
     * @return int
410
     */
411
    public function getUserCount($filter = array()) {
412
        if(is_null($filter)) $filter = array();
413
414
        foreach(array('user', 'name', 'mail', 'group') as $key) {
415
            if(!isset($filter[$key])) {
416
                $filter[$key] = '%';
417
            } else {
418
                $filter[$key] = '%' . $filter[$key] . '%';
419
            }
420
        }
421
422
        $result = $this->_query($this->getConf('count-users'), $filter);
423
        if(!$result || !isset($result[0]['count'])) {
424
            $this->_debug("Statement did not return 'count' attribute", -1, __LINE__);
425
        }
426
        return isset($result[0]['count']);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return isset($result[0]['count']); (boolean) is incompatible with the return type of the parent method DokuWiki_Auth_Plugin::getUserCount of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
427
    }
428
429
    /**
430
     * Create a new group with the given name
431
     *
432
     * @param string $group
433
     * @return bool
434
     */
435
    public function addGroup($group) {
436
        $sql = $this->getConf('insert-group');
437
438
        $result = $this->_query($sql, array(':group' => $group));
439
        if($result === false) return false;
440
        return true;
441
    }
442
443
    /**
444
     * Retrieve groups
445
     *
446
     * Set getGroups capability when implemented
447
     *
448
     * @param   int $start
449
     * @param   int $limit
450
     * @return  array
451
     */
452
    public function retrieveGroups($start = 0, $limit = 0) {
453
        $groups = array_keys($this->_selectGroups());
454
        if($groups === false) return array();
455
456
        if(!$limit) {
457
            return array_splice($groups, $start);
458
        } else {
459
            return array_splice($groups, $start, $limit);
460
        }
461
    }
462
463
    /**
464
     * Select data of a specified user
465
     *
466
     * @param string $user the user name
467
     * @return bool|array user data, false on error
468
     */
469
    protected function _selectUser($user) {
470
        $sql = $this->getConf('select-user');
471
472
        $result = $this->_query($sql, array(':user' => $user));
473
        if(!$result) return false;
474
475
        if(count($result) > 1) {
476
            $this->_debug('Found more than one matching user', -1, __LINE__);
477
            return false;
478
        }
479
480
        $data = array_shift($result);
481
        $dataok = true;
482
483
        if(!isset($data['user'])) {
484
            $this->_debug("Statement did not return 'user' attribute", -1, __LINE__);
485
            $dataok = false;
486
        }
487
        if(!isset($data['hash']) && !isset($data['clear'])) {
488
            $this->_debug("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__);
489
            $dataok = false;
490
        }
491
        if(!isset($data['name'])) {
492
            $this->_debug("Statement did not return 'name' attribute", -1, __LINE__);
493
            $dataok = false;
494
        }
495
        if(!isset($data['mail'])) {
496
            $this->_debug("Statement did not return 'mail' attribute", -1, __LINE__);
497
            $dataok = false;
498
        }
499
500
        if(!$dataok) return false;
501
        return $data;
502
    }
503
504
    /**
505
     * Delete a user after removing all their group memberships
506
     *
507
     * @param string $user
508
     * @return bool true when the user was deleted
509
     */
510
    protected function _deleteUser($user) {
511
        $this->pdo->beginTransaction();
512
        {
513
            $userdata = $this->getUserData($user);
514
            if($userdata === false) goto FAIL;
515
            $allgroups = $this->_selectGroups();
516
517
            // remove group memberships (ignore errors)
518
            foreach($userdata['grps'] as $group) {
519
                $this->_leaveGroup($userdata, $allgroups[$group]);
0 ignored issues
show
Bug introduced by
It seems like $userdata defined by $this->getUserData($user) on line 513 can also be of type boolean; however, auth_plugin_authpdo::_leaveGroup() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
520
            }
521
522
            $ok = $this->_query($this->getConf('delete-user'), $userdata);
0 ignored issues
show
Bug introduced by
It seems like $userdata defined by $this->getUserData($user) on line 513 can also be of type boolean; however, auth_plugin_authpdo::_query() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
523
            if($ok === false) goto FAIL;
524
        }
525
        $this->pdo->commit();
526
        return true;
527
528
        FAIL:
529
        $this->pdo->rollBack();
530
        return false;
531
    }
532
533
    /**
534
     * Select all groups of a user
535
     *
536
     * @param array $userdata The userdata as returned by _selectUser()
537
     * @return array|bool list of group names, false on error
538
     */
539
    protected function _selectUserGroups($userdata) {
540
        global $conf;
541
        $sql = $this->getConf('select-user-groups');
542
        $result = $this->_query($sql, $userdata);
543
        if($result === false) return false;
544
545
        $groups = array($conf['defaultgroup']); // always add default config
546
        foreach($result as $row) {
547
            if(!isset($row['group'])) {
548
                $this->_debug("No 'group' field returned in select-user-groups statement");
549
                return false;
550
            }
551
            $groups[] = $row['group'];
552
        }
553
554
        $groups = array_unique($groups);
555
        sort($groups);
556
        return $groups;
557
    }
558
559
    /**
560
     * Select all available groups
561
     *
562
     * @todo this should be cached
563
     * @return array|bool list of all available groups and their properties
564
     */
565
    protected function _selectGroups() {
566
        $sql = $this->getConf('select-groups');
567
        $result = $this->_query($sql);
568
        if($result === false) return false;
569
570
        $groups = array();
571
        foreach($result as $row) {
572
            if(!isset($row['group'])) {
573
                $this->_debug("No 'group' field returned from select-groups statement", -1, __LINE__);
574
                return false;
575
            }
576
577
            // relayout result with group name as key
578
            $group = $row['group'];
579
            $groups[$group] = $row;
580
        }
581
582
        ksort($groups);
583
        return $groups;
584
    }
585
586
    /**
587
     * Adds the user to the group
588
     *
589
     * @param array $userdata all the user data
590
     * @param array $groupdata all the group data
591
     * @return bool
592
     */
593
    protected function _joinGroup($userdata, $groupdata) {
594
        $data = array_merge($userdata, $groupdata);
595
        $sql = $this->getConf('join-group');
596
        $result = $this->_query($sql, $data);
597
        if($result === false) return false;
598
        return true;
599
    }
600
601
    /**
602
     * Removes the user from the group
603
     *
604
     * @param array $userdata all the user data
605
     * @param array $groupdata all the group data
606
     * @return bool
607
     */
608
    protected function _leaveGroup($userdata, $groupdata) {
609
        $data = array_merge($userdata, $groupdata);
610
        $sql = $this->getConf('leave-group');
611
        $result = $this->_query($sql, $data);
612
        if($result === false) return false;
613
        return true;
614
    }
615
616
    /**
617
     * Executes a query
618
     *
619
     * @param string $sql The SQL statement to execute
620
     * @param array $arguments Named parameters to be used in the statement
621
     * @return array|bool The result as associative array, false on error
622
     */
623
    protected function _query($sql, $arguments = array()) {
624
        if(empty($sql)) {
625
            $this->_debug('No SQL query given', -1, __LINE__);
626
            return false;
627
        }
628
629
        // prepare parameters - we only use those that exist in the SQL
630
        $params = array();
631
        foreach($arguments as $key => $value) {
632
            if(is_array($value)) continue;
633
            if(is_object($value)) continue;
634
            if($key[0] != ':') $key = ":$key"; // prefix with colon if needed
635
            if(strpos($sql, $key) !== false) $params[$key] = $value;
636
        }
637
638
        // execute
639
        $sth = $this->pdo->prepare($sql);
640
        try {
641
            $sth->execute($params);
642
            $result = $sth->fetchAll();
643
        } catch(Exception $e) {
644
            // report the caller's line
645
            $trace = debug_backtrace();
646
            $line = $trace[0]['line'];
647
            $dsql = $this->_debugSQL($sql, $params, !defined('DOKU_UNITTEST'));
648
            $this->_debug($e, -1, $line);
649
            $this->_debug("SQL: <pre>$dsql</pre>", -1, $line);
650
            $result = false;
651
        } finally {
652
            $sth->closeCursor();
653
            $sth = null;
0 ignored issues
show
Unused Code introduced by
$sth 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...
654
        }
655
656
        return $result;
0 ignored issues
show
Bug introduced by
The variable $result 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
659
    /**
660
     * Wrapper around msg() but outputs only when debug is enabled
661
     *
662
     * @param string|Exception $message
663
     * @param int $err
664
     * @param int $line
665
     */
666
    protected function _debug($message, $err = 0, $line = 0) {
667
        if(!$this->getConf('debug')) return;
668
        if(is_a($message, 'Exception')) {
669
            $err = -1;
670
            $msg = $message->getMessage();
0 ignored issues
show
Bug introduced by
It seems like $message is not always an object, but can also be of type string. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
671
            if(!$line) $line = $message->getLine();
672
        } else {
673
            $msg = $message;
674
        }
675
676
        if(defined('DOKU_UNITTEST')) {
677
            printf("\n%s, %s:%d\n", $msg, __FILE__, $line);
678
        } else {
679
            msg('authpdo: ' . $msg, $err, $line, __FILE__);
680
        }
681
    }
682
683
    /**
684
     * Check if the given config strings are set
685
     *
686
     * @author  Matthias Grimm <[email protected]>
687
     *
688
     * @param   string[] $keys
689
     * @return  bool
690
     */
691
    protected function _chkcnf($keys) {
692
        foreach($keys as $key) {
693
            if(!$this->getConf($key)) return false;
694
        }
695
696
        return true;
697
    }
698
699
    /**
700
     * create an approximation of the SQL string with parameters replaced
701
     *
702
     * @param string $sql
703
     * @param array $params
704
     * @param bool $htmlescape Should the result be escaped for output in HTML?
705
     * @return string
706
     */
707
    protected function _debugSQL($sql, $params, $htmlescape = true) {
708
        foreach($params as $key => $val) {
709
            if(is_int($val)) {
710
                $val = $this->pdo->quote($val, PDO::PARAM_INT);
711
            } elseif(is_bool($val)) {
712
                $val = $this->pdo->quote($val, PDO::PARAM_BOOL);
713
            } elseif(is_null($val)) {
714
                $val = 'NULL';
715
            } else {
716
                $val = $this->pdo->quote($val);
717
            }
718
            $sql = str_replace($key, $val, $sql);
719
        }
720
        if($htmlescape) $sql = hsc($sql);
721
        return $sql;
722
    }
723
}
724
725
// vim:ts=4:sw=4:et:
726