User_Options_Model   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 279
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 13.92%

Importance

Changes 0
Metric Value
dl 0
loc 279
ccs 11
cts 79
cp 0.1392
rs 10
c 0
b 0
f 0
wmc 30
lcom 1
cbo 5

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A get() 0 5 2
A set() 0 29 5
A parse_value() 0 22 5
A generate_radio_array() 0 25 4
B get_by_userid() 0 22 6
A get_db() 0 24 3
A set_db() 0 11 4
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
				4 => 'unread_latest'
77
			)
78
		),
79
80
		'list_sort_order' => array(
81
			'default' => 'asc',
82
			'type' => 'int',
83
			'valid_options' => array(
84
				0 => 'asc',
85
				1 => 'desc'
86
			)
87
		),
88
89
		'theme' => array(
90
			'default' => 'light',
91
			'type' => 'int',
92
			'valid_options' => array(
93
				0 => 'light',
94
				1 => 'dark'
95
			)
96
		),
97
98
		'enable_public_list' => array(
99
			'default' => 'disabled',
100
			'type' => 'int',
101
			'valid_options' => array(
102
				0 => 'disabled',
103
				1 => 'enabled'
104
			)
105
		),
106
107
		'mal_sync' => array(
108
			'default' => 'disabled',
109
			'type' => 'int',
110
			'valid_options' => array(
111
				0 => 'disabled',
112
				1 => 'csrf',
113
				2 => 'api'
114
			)
115
		),
116
	);
117
118 96
	public function __construct() {
119 96
		parent::__construct();
120 96
	}
121
122
	/**
123
	 * Get user option, or default option if it does not exist.
124
	 *
125
	 * @param string   $option
126
	 * @param int|null $userID
127
	 *
128
	 * @return mixed Returns option value as STRING, or FALSE if option does not exist.
129
	 */
130 96
	public function get(string $option, ?int $userID = NULL) {
131 96
		$userID = (is_null($userID) ? (int) $this->User->id : $userID);
132
133 96
		return $this->get_by_userid($option, $userID);
134
	}
135
136
	/**
137
	 * @param string     $option
138
	 * @param string|int $value
139
	 *
140
	 * @return bool
141
	 */
142
	public function set(string $option, $value) : bool {
143
		//Check if user is logged in & set ID if so
144
		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...
145
			//Check if option is valid
146
			if(array_key_exists($option, $this->options)) {
147
				//option is valid
148
149
				$idData = array(
150
					'user_id' => $this->User->id,
151
					'name'    => $option
152
				);
153
154
				$valueData = array();
155
				if($this->options[$option]['type'] == 'int') {
156
					$valueData['value_int'] = array_search($value, $this->options[$option]['valid_options']);
157
				} else {
158
					$valueData['value_str'] = (string) $value;
159
				}
160
161
				$success = $this->set_db($idData, $valueData);
162
				if($success) $this->session->unset_tempdata("option_{$option}");
163
			} else {
164
				$success = FALSE;
165
			}
166
		} else {
167
			$success = FALSE;
168
		}
169
		return $success;
170
	}
171
172 96
	private function get_by_userid(string $option, int $userID) {
173
		//Check if option is valid
174 96
		if(array_key_exists($option, $this->options)) {
175
			//Check if userID is > 0
176 96
			if($userID) {
177
				//Check if user has option set.
178
				if($row = $this->get_db($option, $userID)) {
179
					//User has option set, get proper value.
180
					if($userValue = $this->parse_value($option, $row['value_str'], $row['value_int'])) {
181
						//Value is valid. Everything is good.
182
						$value = $userValue;
183
					}
184
				}
185
			}
186
187
			//Overall fallback method.
188 96
			if(!isset($value)) $value = $this->options[$option]['default'];
189
		} else {
190
			$value = FALSE;
191
		}
192 96
		return $value;
193
	}
194
195
	private function get_db(string $option, int $userID) {
196
		//This function assumes we've already done some basic validation.
197
198
		//FIXME: Query duplication.
199
		if($this->User->id !== $userID) {
200
			$query = $this->db->select('value_str, value_int')
201
			                  ->from('user_options')
202
			                  ->where('user_id', $userID)
203
			                  ->where('name',    $option)
204
			                  ->limit(1);
205
			$data = $query->get()->row_array();
206
		} else {
207
			if(!($data = $this->session->tempdata("option_{$option}"))) {
208
				$query = $this->db->select('value_str, value_int')
209
				                  ->from('user_options')
210
				                  ->where('user_id', $userID)
211
				                  ->where('name',    $option)
212
				                  ->limit(1);
213
				$data = $query->get()->row_array();
214
				$this->session->set_tempdata("option_{$option}", $data, 3600);
215
			}
216
		}
217
		return $data;
218
	}
219
220
	private function set_db(array $idData, array $valueData) : bool {
221
		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...
222
			$idData['type'] = (isset($a['value_int']) ? 0 : (isset($a['value_str']) == 'string' ? 1 : 2)); //FIXME: How does this work now?
0 ignored issues
show
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...
223
			$success = $this->db->insert('user_options', array_merge($idData, $valueData));
224
		} else {
225
			$this->db->where($idData);
226
			$success = $this->db->update('user_options', $valueData);
227
		}
228
229
		return $success;
230
	}
231
232
	private function parse_value(string $option, $value_str, $value_int) {
233
		$type = $this->options[$option]['type'];
234
235
		switch($type) {
236
			case 'int':
237
				//TODO: How exactly should we handle INT? Just DB side?
238
				if(in_array($value_int, array_keys($this->options[$option]['valid_options']))) {
239
					$value = $this->options[$option]['valid_options'][$value_int];
240
				}
241
				break;
242
			case 'string':
243
				//TODO: We should have some basically XSS checking here?
244
				$value = (string) $value_str;
245
				break;
246
			default:
247
				//This should never happen.
248
				break;
249
		}
250
		if(!isset($value)) $value = FALSE; //FIXME: This won't play nice with BOOL type false?
251
252
		return $value;
253
	}
254
255
	//Used to quickly generate an array used with form_radio.
256
	public function generate_radio_array(string $option, string $selected_option) {
257
		if(array_key_exists($option, $this->options)) {
258
			$base_attributes = array(
259
				'name' => $option,
260
				'id'   => $option
261
			);
262
			//FIXME: Get a better solution than str_replace for removing special characters
263
			$elements = array();
264
			foreach (array_values($this->options[$option]['valid_options']) as $valid_option) {
265
				$elements[$option.'_'.str_replace(',', '_', $valid_option)] = array_merge($base_attributes, array(
266
					'value' => $valid_option
267
				));
268
			}
269
			if(isset($elements[$option.'_'.str_replace(',', '_', $selected_option)])) {
270
				$elements[$option.'_'.str_replace(',', '_', $selected_option)]['checked'] = TRUE;
271
			} else {
272
				//This should never occur, but fallbacks are always a good idea..
273
				$elements[$option.'_'.$this->options[$option]['default']]['checked'] = TRUE;
274
			}
275
			//CHECK: Should we attach this to body_data here?
276
			return $elements;
277
		} else {
278
			return FALSE;
279
		}
280
	}
281
}
282