Completed
Push — master ( f3d796...336862 )
by Angus
06:04
created

User_Options_Model::get_db()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

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