Completed
Push — tp71 ( 8607f4 )
by Richard
06:09
created

ProfileField::getVar()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 5
Ratio 50 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 2
nop 2
dl 5
loc 10
rs 9.2
c 0
b 0
f 0
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
use Xoops\Core\Database\Connection;
13
use Xoops\Core\Kernel\Dtype;
14
use Xoops\Core\Kernel\Handlers\XoopsUser;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, XoopsUser.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
15
use Xoops\Core\Kernel\XoopsObject;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, XoopsObject.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
16
use Xoops\Core\Kernel\XoopsPersistableObjectHandler;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, XoopsPersistableObjectHandler.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
17
18
/**
19
 * Extended User Profile
20
 *
21
 * @package         Profile
22
 * @author          Jan Pedersen
23
 * @author          Taiwen Jiang <[email protected]>
24
 * @copyright       2000-2016 XOOPS Project (www.xoops.org)
25
 * @license         GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
26
 * @since           2.3.0
27
 */
28
class ProfileField extends XoopsObject
29
{
30
    /**
31
     * Constructor
32
     */
33
    public function __construct()
34
    {
35
        $this->initVar('field_id', Dtype::TYPE_INTEGER, null);
36
        $this->initVar('cat_id', Dtype::TYPE_INTEGER, null, true);
37
        $this->initVar('field_type', Dtype::TYPE_TEXT_BOX);
38
        $this->initVar('field_valuetype', Dtype::TYPE_INTEGER, null, true);
39
        $this->initVar('field_name', Dtype::TYPE_TEXT_BOX, null, true);
40
        $this->initVar('field_title', Dtype::TYPE_TEXT_BOX);
41
        $this->initVar('field_description', Dtype::TYPE_TEXT_AREA);
42
        $this->initVar('field_required', Dtype::TYPE_INTEGER, 0); //0 = no, 1 = yes
43
        $this->initVar('field_maxlength', Dtype::TYPE_INTEGER, 0);
44
        $this->initVar('field_weight', Dtype::TYPE_INTEGER, 0);
45
        $this->initVar('field_default', Dtype::TYPE_TEXT_AREA, "");
46
        $this->initVar('field_notnull', Dtype::TYPE_INTEGER, 1);
47
        $this->initVar('field_edit', Dtype::TYPE_INTEGER, 0);
48
        $this->initVar('field_show', Dtype::TYPE_INTEGER, 0);
49
        $this->initVar('field_config', Dtype::TYPE_INTEGER, 0);
50
        $this->initVar('field_options', Dtype::TYPE_ARRAY, array());
51
        $this->initVar('step_id', Dtype::TYPE_INTEGER, 0);
52
    }
53
54
    /**
55
     * Extra treatment dealing with non latin encoding
56
     * Tricky solution
57
     *
58
     * @param string $key
59
     * @param mixed  $value
60
     *
61
     * @return void
62
     *
63
     * @todo evaluate removing this. New considerations: full UTF-8 system, new Dtype::TYPE_JSON
64
     */
65
    public function setVar($key, $value)
66
    {
67 View Code Duplication
        if ($key === 'field_options' && is_array($value)) {
68
            foreach (array_keys($value) as $idx) {
69
                $value[$idx] = base64_encode($value[$idx]);
70
            }
71
        }
72
        parent::setVar($key, $value);
73
    }
74
75
    /**
76
     * @param string $key
77
     * @param string $format
78
     * @return array|mixed
79
     */
80
    public function getVar($key, $format = 's')
81
    {
82
        $value = parent::getVar($key, $format);
83 View Code Duplication
        if ($key === 'field_options' && !empty($value)) {
84
            foreach (array_keys($value) as $idx) {
85
                $value[$idx] = base64_decode($value[$idx]);
86
            }
87
        }
88
        return $value;
89
    }
90
91
    /**
92
     * Returns a {@link Xoops\Form\Element} for editing the value of this field
93
     *
94
     * @param XoopsUser $user {@link XoopsUser} object to edit the value of
95
     * @param ProfileProfile $profile {@link ProfileProfile} object to edit the value of
96
     *
97
     * @return Xoops\Form\Element
98
     **/
99
    public function getEditElement(XoopsUser $user, ProfileProfile $profile)
100
    {
101
        $xoops = Xoops::getInstance();
102
        $value = in_array($this->getVar('field_name'), $this->getUserVars())
103
                ? $user->getVar($this->getVar('field_name'), 'e') : $profile->getVar($this->getVar('field_name'), 'e');
104
105
        $caption = $this->getVar('field_title');
106
        $caption = defined($caption) ? constant($caption) : $caption;
107
        $name = $this->getVar('field_name', 'e');
108
        $options = $this->getVar('field_options');
109
        if (is_array($options)) {
110
            //asort($options);
111
112
            foreach (array_keys($options) as $key) {
113
                $optval = defined($options[$key]) ? constant($options[$key]) : $options[$key];
114
                $optkey = defined($key) ? constant($key) : $key;
115
                unset($options[$key]);
116
                $options[$optkey] = $optval;
117
            }
118
        }
119
        switch ($this->getVar('field_type')) {
120
            default:
121
            case "autotext":
122
                //autotext is not for editing
123
                $element = new Xoops\Form\Label($caption, $this->getOutputValue($user, $profile));
124
                break;
125
126
            case "textbox":
127
                $element = new Xoops\Form\Text($caption, $name, 35, $this->getVar('field_maxlength'), $value);
128
                break;
129
130
            case "textarea":
131
                $element = new Xoops\Form\TextArea($caption, $name, $value, 4, 30);
132
                break;
133
134
            case "dhtml":
135
                $element = new Xoops\Form\DhtmlTextArea($caption, $name, $value, 10, 30);
136
                break;
137
138
            case "select":
139
                $element = new Xoops\Form\Select($caption, $name, $value);
140
                // If options do not include an empty element, then add a blank option to prevent any default selection
141
                if (!in_array('', array_keys($options))) {
142
                    $element->addOption('', XoopsLocale::NONE);
143
144
                    $eltmsg = empty($caption) ? sprintf(XoopsLocale::F_ENTER, $name) : sprintf(XoopsLocale::F_ENTER, $caption);
145
                    $eltmsg = str_replace('"', '\"', stripslashes($eltmsg));
146
                    $element->addCustomValidationCode("\nvar hasSelected = false; var selectBox = myform.{$name};" . "for (i = 0; i < selectBox.options.length; i++  ) { if ( selectBox.options[i].selected == true && selectBox.options[i].value != '' ) { hasSelected = true; break; } }" . "if ( !hasSelected ) { window.alert(\"{$eltmsg}\"); selectBox.focus(); return false; }");
147
                }
148
                $element->addOptionArray($options);
149
                break;
150
151
            case "select_multi":
152
                $element = new Xoops\Form\Select($caption, $name, $value, 5, true);
153
                $element->addOptionArray($options);
154
                break;
155
156
            case "radio":
157
                $element = new Xoops\Form\Radio($caption, $name, $value);
158
                $element->addOptionArray($options);
159
                break;
160
161
            case "checkbox":
162
                $element = new Xoops\Form\Checkbox($caption, $name, $value);
163
                $element->addOptionArray($options);
164
                break;
165
166
            case "yesno":
167
                $element = new Xoops\Form\RadioYesNo($caption, $name, $value);
168
                break;
169
170
            case "group":
171
                $element = new Xoops\Form\SelectGroup($caption, $name, true, $value);
172
                break;
173
174
            case "group_multi":
175
                $element = new Xoops\Form\SelectGroup($caption, $name, true, $value, 5, true);
176
                break;
177
178
            case "language":
179
                $element = new Xoops\Form\SelectLanguage($caption, $name, $value);
180
                break;
181
182
            case "date":
183
                $element = new Xoops\Form\DateSelect($caption, $name, $value);
184
                break;
185
186
            case "longdate":
187
                $element = new Xoops\Form\DateSelect($caption, $name, str_replace("-", "/", $value));
188
                break;
189
190
            case "datetime":
191
                $element = new Xoops\Form\DateTime($caption, $name, $value);
192
                break;
193
194
            case "timezone":
195
                $element = new Xoops\Form\SelectTimeZone($caption, $name, $value);
196
                break;
197
198
            case "rank":
199
                $ranklist = $xoops->service('userrank')->getAssignableUserRankList()->getValue();
200
                if ($ranklist !== null) {
201
                    $element = new Xoops\Form\Select($caption, $name, $value);
202
                    $element->addOption(0, "--------------");
203
                    $element->addOptionArray($ranklist);
204
                } else {
205
                    $element = new Xoops\Form\Hidden($name, $value);
206
                }
207
                break;
208
209
            case 'theme':
210
                $element = new Xoops\Form\Select($caption, $name, $value);
211
                $element->addOption("0", _PROFILE_MA_SITEDEFAULT);
212
                $handle = opendir(\XoopsBaseConfig::get('themes-path') . '/');
213
                $dirlist = array();
214
                while (false !== ($file = readdir($handle))) {
215
                    if (is_dir(\XoopsBaseConfig::get('themes-path') . '/' . $file) && !preg_match("/^[.]{1,2}$/", $file) && strtolower($file) !== 'cvs') {
216
                        if (XoopsLoad::fileExists(\XoopsBaseConfig::get('themes-path') . "/" . $file . "/theme.tpl") && in_array($file, $xoops->getConfig('theme_set_allowed'))) {
217
                            $dirlist[$file] = $file;
218
                        }
219
                    }
220
                }
221
                closedir($handle);
222
                if (!empty($dirlist)) {
223
                    asort($dirlist);
224
                    $element->addOptionArray($dirlist);
225
                }
226
                break;
227
        }
228
        if ($this->getVar('field_description') != "") {
229
            $element->setDescription($this->getVar('field_description'));
230
        }
231
        return $element;
232
    }
233
234
    /**
235
     * Returns a value for output of this field
236
     *
237
     * @param XoopsUser $user {@link XoopsUser} object to get the value of
238
     * @param profileProfile $profile object to get the value of
239
     *
240
     * @return string
241
     **/
242
    public function getOutputValue(XoopsUser $user, ProfileProfile $profile)
243
    {
244
        $xoops = Xoops::getInstance();
245
        $xoops->loadLanguage('modinfo', 'profile');
246
247
        $value = in_array($this->getVar('field_name'), $this->getUserVars())
248
                ? $user->getVar($this->getVar('field_name')) : $profile->getVar($this->getVar('field_name'));
249
250
        switch ($this->getVar('field_type')) {
251
            default:
252
            case "textbox":
253
                if ($this->getVar('field_name') === 'url' && $value != '') {
254
                    return '<a href="' . $xoops->formatURL($value) . '" rel="external">' . $value . '</a>';
255
                } else {
256
                    return $value;
257
                }
258
                break;
0 ignored issues
show
Unused Code introduced by
break; 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...
259
            case "textarea":
260
            case "dhtml":
261
            case 'theme':
262
            case "language":
263
            case "list":
264
                return $value;
265
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
266
267
            case "select":
268
            case "radio":
269
                $options = $this->getVar('field_options');
270 View Code Duplication
                if (isset($options[$value])) {
271
                    $value = htmlspecialchars(
272
                        defined($options[$value]) ? constant($options[$value]) : $options[$value]
273
                    );
274
                } else {
275
                    $value = "";
276
                }
277
                return $value;
278
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
279
280
            case "select_multi":
281
            case "checkbox":
282
                $options = $this->getVar('field_options');
283
                $ret = array();
284
                if (count($options) > 0) {
285
                    foreach (array_keys($options) as $key) {
286 View Code Duplication
                        if (in_array($key, $value)) {
287
                            $ret[$key] = htmlspecialchars(
288
                                defined($options[$key]) ? constant($options[$key]) : $options[$key]
289
                            );
290
                        }
291
                    }
292
                }
293
                return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $ret; (array) is incompatible with the return type documented by ProfileField::getOutputValue of type string.

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...
294
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
295
296
            case "group":
297
                $member_handler = $xoops->getHandlerMember();
298
                $options = $member_handler->getGroupList();
299
                $ret = isset($options[$value]) ? $options[$value] : '';
300
                return $ret;
301
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
302
303
            case "group_multi":
304
                $member_handler = $xoops->getHandlerMember();
305
                $options = $member_handler->getGroupList();
306
                $ret = array();
307
                foreach (array_keys($options) as $key) {
308
                    if (in_array($key, $value)) {
309
                        $ret[$key] = htmlspecialchars($options[$key]);
310
                    }
311
                }
312
                return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $ret; (array) is incompatible with the return type documented by ProfileField::getOutputValue of type string.

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...
313
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
314
315
            case "longdate":
316
                //return YYYY/MM/DD format - not optimal as it is not using local date format, but how do we do that
317
                //when we cannot convert it to a UNIX timestamp?
318
                return str_replace("-", "/", $value);
319
320
            case "date":
321
                return XoopsLocale::formatTimestamp($value, 's');
322
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
323
324
            case "datetime":
325
                if (!empty($value)) {
326
                    return XoopsLocale::formatTimestamp($value, 'm');
327
                } else {
328
                    return _PROFILE_MI_NEVER_LOGGED_IN;
329
                }
330
                break;
0 ignored issues
show
Unused Code introduced by
break; 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...
331
332
            case "autotext":
333
                $value = $user->getVar($this->getVar('field_name'), 'n'); //autotext can have HTML in it
334
                $value = str_replace("{X_UID}", $user->getVar("uid"), $value);
335
                $value = str_replace("{X_URL}", \XoopsBaseConfig::get('url'), $value);
336
                $value = str_replace("{X_UNAME}", $user->getVar("uname"), $value);
337
                return $value;
338
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
339
340
            case "rank":
341
                $userrank = $user->rank();
342
                $user_rankimage = "";
343 View Code Duplication
                if (isset($userrank['image']) && $userrank['image'] != "") {
344
                    $user_rankimage = '<img src="' . $userrank['image'] . '" alt="' . $userrank['title'] . '" /><br />';
345
                }
346
                return $user_rankimage . $userrank['title'];
347
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
348
349
            case "yesno":
350
                return $value ? XoopsLocale::YES : XoopsLocale::NO;
351
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
352
353
            case "timezone":
354
                return $value->getName();
355
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
356
        }
357
    }
358
359
    /**
360
     * Returns a value ready to be saved in the database
361
     *
362
     * @param mixed $value Value to format
363
     *
364
     * @return mixed
365
     */
366
    public function getValueForSave($value)
367
    {
368
        switch ($this->getVar('field_type')) {
369
            default:
370
            case "textbox":
371
            case "textarea":
372
            case "dhtml":
373
            case "yesno":
374
            case 'theme':
375
            case "language":
376
            case "list":
377
            case "select":
378
            case "radio":
379
            case "select_multi":
380
            case "group":
381
            case "group_multi":
382
            case "longdate":
383
                return $value;
384
385
            case "timezone":
386
                return $value;
387
388
            case "checkbox":
389
                return (array)$value;
390
391
            case "date":
392
                if ($value != "") {
393
                    return strtotime($value);
394
                }
395
                return $value;
396
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
397
398
            case "datetime":
399
                if (!empty($value)) {
400
                    return strtotime($value['date']) + (int)($value['time']);
401
                }
402
                return $value;
403
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
404
        }
405
    }
406
407
    /**
408
     * Get names of user variables
409
     *
410
     * @return array
411
     */
412
    public function getUserVars()
413
    {
414
        /* @var $profile_handler ProfileProfileHandler */
415
        $profile_handler = \Xoops::getModuleHelper('profile')->getHandler('profile');
416
        return $profile_handler->getUserVars();
417
    }
418
}
419
420
class ProfileFieldHandler extends XoopsPersistableObjectHandler
421
{
422
    /**
423
     * @param null|Connection $db database
424
     */
425
    public function __construct(Connection $db = null)
426
    {
427
        parent::__construct($db, 'profile_field', 'ProfileField', 'field_id', 'field_title');
428
    }
429
430
    /**
431
     * Read field information from cached storage
432
     *
433
     * @param bool   $force_update   read fields from database and not cached storage
434
     *
435
     * @return array
436
     */
437
    public function loadFields($force_update = false)
438
    {
439
        static $fields = array();
440
        if (!empty($force_update) || count($fields) == 0) {
441
            $this->table_link = $this->db2->prefix('profile_category');
442
            $criteria = new Criteria('o.field_id', 0, "!=");
443
            $criteria->setSort('l.cat_weight ASC, o.field_weight');
444
            $field_objs = $this->getByLink($criteria, array('o.*'), true, 'cat_id', 'cat_id');
445
            /* @var ProfileField $field */
446
            foreach ($field_objs as $field) {
0 ignored issues
show
Bug introduced by
The expression $field_objs of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
447
                $fields[$field->getVar('field_name')] = $field;
448
            }
449
        }
450
        return $fields;
451
    }
452
453
    /**
454
     * save a profile field in the database
455
     *
456
     * @param XoopsObject|ProfileField $obj reference to the object
457
     * @param bool $force whether to force the query execution despite security settings
458
     * @return bool FALSE if failed, TRUE if already present and unchanged or successful
459
     */
460
    public function insertFields(XoopsObject $obj, $force = false)
461
    {
462
        $profile_handler = \Xoops::getModuleHelper('profile')->getHandler('profile');
463
        $obj->setVar('field_name', str_replace(' ', '_', $obj->getVar('field_name')));
464
        $obj->cleanVars(); //Don't quote
465
        switch ($obj->getVar('field_type')) {
466
            case "datetime":
467
            case "date":
468
                $obj->setVar('field_valuetype', Dtype::TYPE_INTEGER);
469
                $obj->setVar('field_maxlength', 10);
470
                break;
471
472
            case "longdate":
473
                $obj->setVar('field_valuetype', Dtype::TYPE_MEDIUM_TIME);
474
                break;
475
476
            case "yesno":
477
                $obj->setVar('field_valuetype', Dtype::TYPE_INTEGER);
478
                $obj->setVar('field_maxlength', 1);
479
                break;
480
481
            case "textbox":
482
                if ($obj->getVar('field_valuetype') != Dtype::TYPE_INTEGER) {
483
                    $obj->setVar('field_valuetype', Dtype::TYPE_TEXT_BOX);
484
                }
485
                break;
486
487
            case "autotext":
488
                if ($obj->getVar('field_valuetype') != Dtype::TYPE_INTEGER) {
489
                    $obj->setVar('field_valuetype', Dtype::TYPE_TEXT_AREA);
490
                }
491
                break;
492
493
            case "group_multi":
494
            case "select_multi":
495
            case "checkbox":
496
                $obj->setVar('field_valuetype', Dtype::TYPE_ARRAY);
497
                break;
498
499
            case "language":
500
            case "timezone":
501
            case "theme":
502
                $obj->setVar('field_valuetype', Dtype::TYPE_TEXT_BOX);
503
                break;
504
505
            case "dhtml":
506
            case "textarea":
507
                $obj->setVar('field_valuetype', Dtype::TYPE_TEXT_AREA);
508
                break;
509
        }
510
511
        if ($obj->getVar('field_valuetype') == "") {
512
            $obj->setVar('field_valuetype', Dtype::TYPE_TEXT_BOX);
513
        }
514
515
        if (!in_array($obj->getVar('field_name'), $this->getUserVars())) {
516
            if ($obj->isNew()) {
517
                //add column to table
518
                $changetype = "ADD";
519
            } else {
520
                //update column information
521
                $changetype = "CHANGE `" . $obj->getVar('field_name', 'n') . "`";
522
            }
523
            $maxlengthstring = $obj->getVar('field_maxlength') > 0 ? "(" . $obj->getVar('field_maxlength') . ")" : "";
524
525
            //set type
526
            switch ($obj->getVar('field_valuetype')) {
527
                default:
528
                case Dtype::TYPE_ARRAY:
529
                case Dtype::TYPE_EMAIL:
530
                case Dtype::TYPE_TEXT_BOX:
531
                case Dtype::TYPE_URL:
532
                    $type = "varchar";
533
                    // varchars must have a maxlength
534
                    if (!$maxlengthstring) {
535
                        //so set it to max if maxlength is not set - or should it fail?
536
                        $maxlengthstring = "(255)";
537
                        $obj->setVar('field_maxlength', 255);
538
                    }
539
                    break;
540
541
                case Dtype::TYPE_INTEGER:
542
                    $type = "int";
543
                    break;
544
545
                case Dtype::TYPE_DECIMAL:
546
                    $type = "decimal(14,6)";
547
                    break;
548
549
                case Dtype::TYPE_FLOAT:
550
                    $type = "float(15,9)";
551
                    break;
552
553
                case Dtype::TYPE_OTHER:
554
                case Dtype::TYPE_TEXT_AREA:
555
                    $type = "text";
556
                    $maxlengthstring = "";
557
                    break;
558
559
                case Dtype::TYPE_MEDIUM_TIME:
560
                    $type = "date";
561
                    $maxlengthstring = "";
562
                    break;
563
            }
564
565
            $sql = "ALTER TABLE `" . $profile_handler->table . "` " . $changetype . " `" . $obj->cleanVars['field_name'] . "` " . $type . $maxlengthstring . ' NULL';
566
            if ($force) {
567
                $this->db2->setForce(true);
568
            }
569
            if (!$this->db2->query($sql)) {
570
                return false;
571
            }
572
        }
573
574
        //change this to also update the cached field information storage
575
        $obj->setDirty();
576
        if (!parent::insert($obj, $force)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression parent::insert($obj, $force) of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (insert() instead of insertFields()). Are you sure this is correct? If so, you might want to change this to $this->insert().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
577
            return false;
578
        }
579
        return $obj->getVar('field_id');
580
581
    }
582
583
    /**
584
     * delete a profile field from the database
585
     *
586
     * @param XoopsObject|ProfileField $obj reference to the object to delete
587
     * @param bool $force
588
     * @return bool FALSE if failed.
589
     **/
590
    public function deleteFields(XoopsObject $obj, $force = false)
591
    {
592
        $xoops = Xoops::getInstance();
593
        $profile_handler = \Xoops::getModuleHelper('profile')->getHandler('profile');
594
        // remove column from table
595
        $sql = "ALTER TABLE " . $profile_handler->table . " DROP `" . $obj->getVar('field_name', 'n') . "`";
596
        if ($this->db2->query($sql)) {
597
            //change this to update the cached field information storage
598
            if (!parent::delete($obj, $force)) {
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (delete() instead of deleteFields()). Are you sure this is correct? If so, you might want to change this to $this->delete().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
599
                return false;
600
            }
601
602
            if ($obj->getVar('field_show') || $obj->getVar('field_edit')) {
603
                $profile_module = $xoops->getModuleByDirname('profile');
604
                if (is_object($profile_module)) {
605
                    // Remove group permissions
606
                    $groupperm_handler = $xoops->getHandlerGroupPermission();
607
                    $criteria = new CriteriaCompo(new Criteria('gperm_modid', $profile_module->getVar('mid')));
608
                    $criteria->add(new Criteria('gperm_itemid', $obj->getVar('field_id')));
609
                    return $groupperm_handler->deleteAll($criteria);
0 ignored issues
show
Bug introduced by
The method deleteAll does only exist in XoopsPersistableObjectHandler, but not in XoopsObjectHandler.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
610
                }
611
            }
612
        }
613
        return false;
614
    }
615
616
    /**
617
     * Get array of standard variable names (user table)
618
     *
619
     * @return string[]
620
     */
621
    public function getUserVars()
622
    {
623
        return array(
624
            'uid', 'uname', 'name', 'email', 'url', 'user_avatar', 'user_regdate', 'user_icq', 'user_from', 'user_sig',
625
            'user_viewemail', 'actkey', 'user_aim', 'user_yim', 'user_msnm', 'pass', 'posts', 'attachsig', 'rank',
626
            'level', 'theme', 'timezone', 'last_login', 'umode', 'uorder', 'notify_method', 'notify_mode',
627
            'user_occ', 'bio', 'user_intrest', 'user_mailok'
628
        );
629
    }
630
}
631