Completed
Pull Request — master (#169)
by Richard
11:35
created

ProfileProfileHandler::get()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 9
rs 9.6666
1
<?php
2
/**
3
 * Extended User Profile
4
 *
5
 * You may not change or alter any portion of this comment or credits
6
 * of supporting developers from this source code or any supporting source code
7
 * which is considered copyrighted (c) material of the original comment or credit authors.
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
13
 * @license             GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package             profile
15
 * @since               2.3.0
16
 * @author              Jan Pedersen
17
 * @author              Taiwen Jiang <[email protected]>
18
 */
19
20
// defined('XOOPS_ROOT_PATH') || exit("XOOPS root path not defined");
21
22
/**
23
 * @package             kernel
24
 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
25
 */
26
class ProfileProfile extends XoopsObject
27
{
28
    /**
29
     * @param $fields
30
     */
31
    public function __construct($fields)
32
    {
33
        $this->initVar('profile_id', XOBJ_DTYPE_INT, null, true);
34
        $this->init($fields);
35
    }
36
37
    /**
38
     * Initiate variables
39
     * @param array $fields field information array of {@link XoopsProfileField} objects
40
     */
41
    public function init($fields)
42
    {
43
        if (is_array($fields) && count($fields) > 0) {
44
            foreach (array_keys($fields) as $key) {
45
                $this->initVar($key, $fields[$key]->getVar('field_valuetype'), $fields[$key]->getVar('field_default', 'n'), $fields[$key]->getVar('field_required'), $fields[$key]->getVar('field_maxlength'));
46
            }
47
        }
48
    }
49
}
50
51
/**
52
 * @package             kernel
53
 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
54
 */
55
class ProfileProfileHandler extends XoopsPersistableObjectHandler
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
56
{
57
    /**
58
     * holds reference to {@link profileFieldHandler} object
59
     */
60
    public $_fHandler;
61
62
    /**
63
     * Array of {@link XoopsProfileField} objects
64
     * @var array
65
     */
66
    public $_fields = array();
67
68
    /**
69
     * @param null|XoopsDatabase $db
70
     */
71
    public function __construct(XoopsDatabase $db)
72
    {
73
        parent::__construct($db, 'profile_profile', 'profileprofile', 'profile_id');
74
        $this->_fHandler = xoops_getModuleHandler('field', 'profile');
75
    }
76
77
    /**
78
     * create a new {@link ProfileProfile}
79
     *
80
     * @param bool $isNew Flag the new objects as "new"?
81
     *
82
     * @return object {@link ProfileProfile}
83
     */
84
    public function create($isNew = true)
85
    {
86
        $obj          = new $this->className($this->loadFields());
87
        $obj->handler = $this;
88
        if ($isNew === true) {
89
            $obj->setNew();
90
        }
91
92
        return $obj;
93
    }
94
95
    /**
96
     * Get a ProfileProfile object for a user id.
97
     *
98
     * We will create an empty profile if none exists. This behavior allows user objects
99
     * created outside of profile to be edited correctly in the profile module.
100
     *
101
     * @param integer|null  $uid
102
     * @param string[]|null $fields array of field names to fetch, null for all
103
     *
104
     * @return object {@link ProfileProfile}
105
     *
106
     * @internal This was get($uid, $createOnFailure = true). No callers found using the extra parameter.
107
     * @internal Modified to match parent signature.
108
     */
109
    public function get($uid = null, $fields = null)
110
    {
111
        $obj = parent::get($uid, $fields);
112
        if (!is_object($obj)) {
113
            $obj = $this->create();
114
        }
115
116
        return $obj;
117
    }
118
119
    /**
120
     * Create new {@link ProfileField} object
121
     *
122
     * @param bool $isNew
123
     *
124
     * @return ProfileField
125
     */
126
    public function createField($isNew = true)
127
    {
128
        $return = $this->_fHandler->create($isNew);
0 ignored issues
show
Unused Code introduced by
The call to XoopsObjectHandler::create() has too many arguments starting with $isNew.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
129
130
        return $return;
131
    }
132
133
    /**
134
     * Load field information
135
     *
136
     * @return array
137
     */
138
    public function loadFields()
139
    {
140
        if (count($this->_fields) == 0) {
141
            $this->_fields = $this->_fHandler->loadFields();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class XoopsObjectHandler as the method loadFields() does only exist in the following sub-classes of XoopsObjectHandler: ProfileFieldHandler, ProfileProfileHandler. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
142
        }
143
144
        return $this->_fields;
145
    }
146
147
    /**
148
     * Fetch fields
149
     *
150
     * @param CriteriaElement $criteria  {@link CriteriaElement} object
151
     * @param bool            $id_as_key return array with field IDs as key?
152
     * @param bool            $as_object return array of objects?
153
     *
154
     * @return array
155
     **/
156
    public function getFields(CriteriaElement $criteria, $id_as_key = true, $as_object = true)
157
    {
158
        return $this->_fHandler->getObjects($criteria, $id_as_key, $as_object);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class XoopsObjectHandler as the method getObjects() does only exist in the following sub-classes of XoopsObjectHandler: ArtObjectHandler, PmMessageHandler, ProfileCategoryHandler, ProfileFieldHandler, ProfileProfileHandler, ProfileRegstepHandler, ProfileVisibilityHandler, SystemAvatarHandler, SystemBannerHandler, SystemBannerclientHandler, SystemBannerfinishHandler, SystemBlockHandler, SystemBlockLinkModuleHandler, SystemGroupHandler, SystemUsersHandler, XoopsAvatarHandler, XoopsBlockHandler, XoopsCacheModelHandler, XoopsCommentHandler, XoopsConfigCategoryHandler, XoopsConfigItemHandler, XoopsConfigOptionHandler, XoopsGroupHandler, XoopsGroupPermHandler, XoopsImageHandler, XoopsImagecategoryHandler, XoopsImagesetHandler, XoopsImagesetimgHandler, XoopsMembershipHandler, XoopsModuleHandler, XoopsNotificationHandler, XoopsPersistableObjectHandler, XoopsPrivmessageHandler, XoopsTplfileHandler, XoopsTplsetHandler, XoopsUserHandler, systemsmiliesHandler, systemuserrankHandler. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
159
    }
160
161
    /**
162
     * Insert a field in the database
163
     *
164
     * @param ProfileField $field
165
     * @param bool         $force
166
     *
167
     * @return bool
168
     */
169
    public function insertField(ProfileField $field, $force = false)
170
    {
171
        return $this->_fHandler->insert($field, $force);
0 ignored issues
show
Unused Code introduced by
The call to XoopsObjectHandler::insert() has too many arguments starting with $force.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
172
    }
173
174
    /**
175
     * Delete a field from the database
176
     *
177
     * @param ProfileField $field
178
     * @param bool         $force
179
     *
180
     * @return bool
181
     */
182
    public function deleteField(ProfileField $field, $force = false)
183
    {
184
        return $this->_fHandler->delete($field, $force);
0 ignored issues
show
Unused Code introduced by
The call to XoopsObjectHandler::delete() has too many arguments starting with $force.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
185
    }
186
187
    /**
188
     * Save a new field in the database
189
     *
190
     * @param array $vars array of variables, taken from $module->loadInfo('profile')['field']
191
     * @param int   $weight
192
     *
193
     * @internal param int $categoryid ID of the category to add it to
194
     * @internal param int $type valuetype of the field
195
     * @internal param int $moduleid ID of the module, this field belongs to
196
     * @return string
197
     */
198
    public function saveField($vars, $weight = 0)
199
    {
200
        $field = $this->createField();
201
        $field->setVar('field_name', $vars['name']);
202
        $field->setVar('field_valuetype', $vars['valuetype']);
203
        $field->setVar('field_type', $vars['type']);
204
        $field->setVar('field_weight', $weight);
205
        if (isset($vars['title'])) {
206
            $field->setVar('field_title', $vars['title']);
207
        }
208
        if (isset($vars['description'])) {
209
            $field->setVar('field_description', $vars['description']);
210
        }
211
        if (isset($vars['required'])) {
212
            $field->setVar('field_required', $vars['required']); //0 = no, 1 = yes
213
        }
214
        if (isset($vars['maxlength'])) {
215
            $field->setVar('field_maxlength', $vars['maxlength']);
216
        }
217
        if (isset($vars['default'])) {
218
            $field->setVar('field_default', $vars['default']);
219
        }
220
        if (isset($vars['notnull'])) {
221
            $field->setVar('field_notnull', $vars['notnull']);
222
        }
223
        if (isset($vars['show'])) {
224
            $field->setVar('field_show', $vars['show']);
225
        }
226
        if (isset($vars['edit'])) {
227
            $field->setVar('field_edit', $vars['edit']);
228
        }
229
        if (isset($vars['config'])) {
230
            $field->setVar('field_config', $vars['config']);
231
        }
232
        if (isset($vars['options'])) {
233
            $field->setVar('field_options', $vars['options']);
234
        } else {
235
            $field->setVar('field_options', array());
236
        }
237
        if ($this->insertField($field)) {
238
            $msg = '&nbsp;&nbsp;Field <strong>' . $vars['name'] . '</strong> added to the database';
239
        } else {
240
            $msg = '&nbsp;&nbsp;<span class="red">ERROR: Could not insert field <strong>' . $vars['name'] . '</strong> into the database. ' . implode(' ', $field->getErrors()) . $this->db->error() . '</span>';
241
        }
242
        unset($field);
243
244
        return $msg;
245
    }
246
247
    /**
248
     * insert a new object in the database
249
     *
250
     * @param XoopsObject|ProfileProfile $obj   reference to the object
251
     * @param bool                       $force whether to force the query execution despite security settings
252
     *
253
     * @return bool FALSE if failed, TRUE if already present and unchanged or successful
254
     */
255
    public function insert(XoopsObject $obj, $force = false)
256
    {
257
        if (!($obj instanceof $this->className)) {
258
            return false;
259
        }
260
        $uservars = $this->getUserVars();
261
        foreach ($uservars as $var) {
262
            unset($obj->vars[$var]);
263
        }
264
        if (count($obj->vars) == 0) {
265
            return true;
266
        }
267
268
        return parent::insert($obj, $force);
269
    }
270
271
    /**
272
     * Get array of standard variable names (user table)
273
     *
274
     * @return array
275
     */
276
    public function getUserVars()
277
    {
278
        return $this->_fHandler->getUserVars();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class XoopsObjectHandler as the method getUserVars() does only exist in the following sub-classes of XoopsObjectHandler: ProfileFieldHandler, ProfileProfileHandler. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
279
    }
280
281
    /**
282
     * Search profiles and users
283
     *
284
     * @param CriteriaElement $criteria   CriteriaElement
285
     * @param array           $searchvars Fields to be fetched
286
     * @param array           $groups     for Usergroups is selected (only admin!)
287
     *
288
     * @return array
289
     */
290
    public function search(CriteriaElement $criteria, $searchvars = array(), $groups = null)
291
    {
292
        $uservars = $this->getUserVars();
293
294
        $searchvars_user    = array_intersect($searchvars, $uservars);
295
        $searchvars_profile = array_diff($searchvars, $uservars);
296
        $sv                 = array('u.uid, u.uname, u.email, u.user_viewemail');
297
        if (!empty($searchvars_user)) {
298
            $sv[0] .= ',u.' . implode(', u.', $searchvars_user);
299
        }
300
        if (!empty($searchvars_profile)) {
301
            $sv[] = 'p.' . implode(', p.', $searchvars_profile);
302
        }
303
304
        $sql_select = 'SELECT ' . (empty($searchvars) ? 'u.*, p.*' : implode(', ', $sv));
305
        $sql_from   = ' FROM ' . $this->db->prefix('users') . ' AS u LEFT JOIN ' . $this->table . ' AS p ON u.uid=p.profile_id' . (empty($groups) ? '' : ' LEFT JOIN ' . $this->db->prefix('groups_users_link') . ' AS g ON u.uid=g.uid');
306
        $sql_clause = ' WHERE 1=1';
307
        $sql_order  = '';
308
309
        $limit = $start = 0;
310
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
311
            $sql_clause .= ' AND ' . $criteria->render();
312
            if ($criteria->getSort() !== '') {
313
                $sql_order = ' ORDER BY ' . $criteria->getSort() . ' ' . $criteria->getOrder();
314
            }
315
            $limit = $criteria->getLimit();
316
            $start = $criteria->getStart();
317
        }
318
319
        if (!empty($groups)) {
320
            $sql_clause .= ' AND g.groupid IN (' . implode(', ', $groups) . ')';
321
        }
322
323
        $sql_users = $sql_select . $sql_from . $sql_clause . $sql_order;
324
        $result    = $this->db->query($sql_users, $limit, $start);
325
326
        if (!$result) {
327
            return array(array(), array(), 0);
328
        }
329
        $user_handler = xoops_getHandler('user');
330
        $uservars     = $this->getUserVars();
331
        $users        = array();
332
        $profiles     = array();
333
        while ($myrow = $this->db->fetchArray($result)) {
334
            $profile = $this->create(false);
335
            $user    = $user_handler->create(false);
336
337
            foreach ($myrow as $name => $value) {
338
                if (in_array($name, $uservars)) {
339
                    $user->assignVar($name, $value);
340
                } else {
341
                    $profile->assignVar($name, $value);
342
                }
343
            }
344
            $profiles[$myrow['uid']] = $profile;
345
            $users[$myrow['uid']]    = $user;
346
        }
347
348
        $count = count($users);
349
        if ((!empty($limit) && $count >= $limit) || !empty($start)) {
350
            $sql_count = 'SELECT COUNT(*)' . $sql_from . $sql_clause;
351
            $result    = $this->db->query($sql_count);
352
            list($count) = $this->db->fetchRow($result);
353
        }
354
355
        return array($users, $profiles, (int)$count);
356
    }
357
}
358