Completed
Push — master ( 305ec5...eac71e )
by Angus
08:34
created

User_Options_Model::set()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 10.3999

Importance

Changes 0
Metric Value
cc 5
eloc 18
nc 6
nop 2
dl 0
loc 32
ccs 2
cts 5
cp 0.4
crap 10.3999
rs 8.439
c 0
b 0
f 0
1
<?php declare(strict_types=1); defined('BASEPATH') or exit('No direct script access allowed');
2
3
class User_Options_Model extends CI_Model {
4
	public $options = array(
5
		/** GENERAL OPTIONS **/
6
		'category_custom_1' => array(
7
			'default' => 'disabled',
8
			'type' => 'int',
9
			'valid_options' => array(
10
				0 => 'disabled',
11
				1 => 'enabled'
12
			)
13
		),
14
		'category_custom_2' => array(
15
			'default' => 'disabled',
16
			'type' => 'int',
17
			'valid_options' => array(
18
				0 => 'disabled',
19
				1 => 'enabled'
20
			)
21
		),
22
		'category_custom_3' => array(
23
			'default' => 'disabled',
24
			'type' => 'int',
25
			'valid_options' => array(
26
				0 => 'disabled',
27
				1 => 'enabled'
28
			)
29
		),
30
		'category_custom_1_text' => array(
31
			'default' => 'Custom 1',
32
			'type' => 'string'
33
		),
34
		'category_custom_2_text' => array(
35
			'default' => 'Custom 2',
36
			'type' => 'string'
37
		),
38
		'category_custom_3_text' => array(
39
			'default' => 'Custom 3',
40
			'type' => 'string'
41
		),
42
43
		'enable_live_countdown_timer' => array(
44
			'default' => 'enabled',
45
			'type' => 'int',
46
			'valid_options' => array(
47
				0 => 'disabled',
48
				1 => 'enabled'
49
			)
50
		),
51
52
		'default_series_category' => array(
53
			'default' => 'reading',
54
			'type' => 'int',
55
			'valid_options' => array(
56
				0 => 'reading',
57
				1 => 'on-hold',
58
				2 => 'plan-to-read',
59
60
				//FIXME: (MAJOR) This should only be enabled if the custom categories are enabled
61
				// Problem is we can't easily check for this since the userscript uses it's own UserID, and not $this->User->id
62
				3 => 'custom1',
63
				4 => 'custom2',
64
				5 => 'custom3'
65
			)
66
		),
67
68
		'list_sort_type' => array(
69
			'default' => 'unread',
70
			'type' => 'int',
71
			'valid_options' => array(
72
				0 => 'unread',
73
				1 => 'alphabetical',
74
				2 => 'my_status',
75
				3 => 'latest'
76
			)
77
		),
78
79
		'list_sort_order' => array(
80
			'default' => 'asc',
81
			'type' => 'int',
82
			'valid_options' => array(
83
				0 => 'asc',
84
				1 => 'desc'
85
			)
86
		),
87
88
		'theme' => array(
89
			'default' => 'light',
90
			'type' => 'int',
91
			'valid_options' => array(
92
				0 => 'light',
93
				1 => 'dark'
94
			)
95
		),
96
	);
97
98 120
	public function __construct() {
99 120
		parent::__construct();
100 120
	}
101
102
	/**
103
	 * Get user option, or default option if it does not exist.
104
	 * @param string $option
105
	 * @return mixed Returns option value as STRING, or FALSE if option does not exist.
106
	 */
107 120
	public function get(string $option) {
108 120
		return $this->get_by_userid($option, (int) $this->User->id);
109
	}
110 120
111
	/**
112 120
	 * @param string     $option
113
	 * @param string|int $value
114 120
	 *
115
	 * @return bool
116 3
	 */
117
	public function set(string $option, $value) : bool {
118
119
120
		//TODO: Check if user is logged in, get ID
121
		//Check if user is logged in & set ID if so
122
		if($userID = $this->User->id) {
0 ignored issues
show
Unused Code introduced by
$userID 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...
123
			//Check if option is valid
124
			if(array_key_exists($option, $this->options)) {
125
				//option is valid
126 120
127
				$idData = array(
128
					'user_id' => $this->User->id,
129
					'name'    => $option,
130 120
				);
131
132
				$valueData = array();
133
				if($this->options[$option]['type'] == 'int') {
134
					$valueData['value_int'] = array_search($value, $this->options[$option]['valid_options']);
135
				} else {
136
					$valueData['value_str'] = (string) $value;
137
				}
138
139
				$success = $this->set_db($idData, $valueData);
140
				if($success) $this->session->unset_tempdata("option_{$option}");
141
			} else {
142
				$success = FALSE;
143
			}
144
		} else {
145
			$success = FALSE;
146
		}
147
		return $success;
148
	}
149
150
151
	public function get_by_userid(string $option, int $userID) {
152
		//Check if option is valid
153
		if(array_key_exists($option, $this->options)) {
154
			//Check if userID is > 0
155
			if($userID) {
156
				//Check if user has option set.
157
				if($row = $this->get_db($option, $userID)) {
158
					//User has option set, get proper value.
159
					if($userValue = $this->parse_value($option, $row['value_str'], $row['value_int'])) {
160
						//Value is valid. Everything is good.
161
						$value = $userValue;
162
					}
163
				}
164
			}
165
166
			//Overall fallback method.
167
			if(!isset($value)) $value = $this->options[$option]['default'];
168
		} else {
169 3
			$value = FALSE;
170
		}
171 3
		return $value;
172 3
	}
173 3
174 3
	private function get_db(string $option, int $userID) {
175 3
		//This function assumes we've already done some basic validation.
176 3
		if(!($data = $this->session->tempdata("option_{$option}"))) {
177 3
			$query = $this->db->select('value_str, value_int')
178 3
			                  ->from('user_options')
179
			                  ->where('user_id', $userID)
180 3
			                  ->where('name',    $option)
181
			                  ->limit(1);
182
			$data = $query->get()->row_array();
183
			$this->session->set_tempdata("option_{$option}", $data, 3600);
184
		}
185
		return $data;
186
	}
187
188
	private function set_db(array $idData, array $valueData) : bool {
189
		if($this->db->get_where('user_options', $idData)->num_rows() === 0) {
0 ignored issues
show
Documentation introduced by
$idData is of type array, but the function expects a string|null.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
190
			$data['type'] = (isset($a['value_int']) ? 0 : (isset($a['value_str']) == 'string' ? 1 : 2));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
Bug introduced by
The variable $a seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
191
			$success = $this->db->insert('user_options', array_merge($idData, $valueData));
192
		} else {
193
			$this->db->where($idData);
194
			$success = $this->db->update('user_options', $valueData);
195
		}
196
197
		return $success;
198
	}
199
200
	private function parse_value(string $option, $value_str, $value_int) {
201
		$type = $this->options[$option]['type'];
202
203
		switch($type) {
204
			case 'int':
205
				//TODO: How exactly should we handle INT? Just DB side?
206
				if(in_array($value_int, array_keys($this->options[$option]['valid_options']))) {
207
					$value = $this->options[$option]['valid_options'][$value_int];
208
				}
209
				break;
210
			case 'string':
211
				//TODO: We should have some basically XSS checking here?
212
				$value = (string) $value_str;
213
				break;
214
			default:
215
				//This should never happen.
216
				break;
217
		}
218
		if(!isset($value)) $value = FALSE; //FIXME: This won't play nice with BOOL type false?
219
220
		return $value;
221
	}
222
223
	//Used to quickly generate an array used with form_radio.
224
	public function generate_radio_array(string $option, string $selected_option) {
225
		if(array_key_exists($option, $this->options)) {
226
			$base_attributes = array(
227
				'name' => $option,
228
				'id'   => $option
229
			);
230
			//FIXME: Get a better solution than str_replace for removing special characters
231
			$elements = array();
232
			foreach (array_values($this->options[$option]['valid_options']) as $valid_option) {
233
				$elements[$option.'_'.str_replace(',', '_', $valid_option)] = array_merge($base_attributes, array(
234
					'value' => $valid_option
235
				));
236
			}
237
			if(isset($elements[$option.'_'.str_replace(',', '_', $selected_option)])) {
238
				$elements[$option.'_'.str_replace(',', '_', $selected_option)]['checked'] = TRUE;
239
			} else {
240
				//This should never occur, but fallbacks are always a good idea..
241
				$elements[$option.'_'.$this->options[$option]['default']]['checked'] = TRUE;
242
			}
243
			//CHECK: Should we attach this to body_data here?
244
			return $elements;
245
		} else {
246
			return FALSE;
247
		}
248
	}
249
}
250