1: <?php
2: /**
3: * Object write handler class.
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 (https://www.gnu.org/licenses/gpl-2.0.html)
14: * @package kernel
15: * @subpackage model
16: * @since 2.3.0
17: * @author Taiwen Jiang <phppp@users.sourceforge.net>
18: */
19: defined('XOOPS_ROOT_PATH') || exit('Restricted access');
20:
21: /**
22: * Object write handler class.
23: *
24: * @author Taiwen Jiang <phppp@users.sourceforge.net>
25: * @author Simon Roberts <simon@xoops.org>
26: *
27: * {@link XoopsModelAbstract}
28: */
29: class XoopsModelWrite extends XoopsModelAbstract
30: {
31: /**
32: * Clean values of all variables of the object for storage.
33: * also add slashes and quote string wherever needed
34: *
35: * CleanVars only contains changed and cleaned variables
36: * Reference is used for PHP4 compliance
37: *
38: * @param $object
39: *
40: * @return bool true if successful
41: * @access public
42: */
43: public function cleanVars(&$object)
44: {
45: $myts = \MyTextSanitizer::getInstance();
46: $errors = array();
47:
48: $vars = $object->getVars();
49: $object->cleanVars = array();
50: foreach ($vars as $k => $v) {
51: if (!$v['changed']) {
52: continue;
53: }
54: $cleanv = $v['value'];
55: switch ($v['data_type']) {
56: case XOBJ_DTYPE_TIMESTAMP:
57: $cleanv = !is_string($cleanv) && is_numeric($cleanv) ? date(_DBTIMESTAMPSTRING, $cleanv) : date(_DBTIMESTAMPSTRING, strtotime($cleanv));
58: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
59: break;
60: case XOBJ_DTYPE_TIME:
61: $cleanv = !is_string($cleanv) && is_numeric($cleanv) ? date(_DBTIMESTRING, $cleanv) : date(_DBTIMESTRING, strtotime($cleanv));
62: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
63: break;
64: case XOBJ_DTYPE_DATE:
65: $cleanv = !is_string($cleanv) && is_numeric($cleanv) ? date(_DBDATESTRING, $cleanv) : date(_DBDATESTRING, strtotime($cleanv));
66: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
67: break;
68: case XOBJ_DTYPE_UNICODE_TXTBOX:
69: if ($v['required'] && $cleanv != '0' && $cleanv == '') {
70: $errors[] = sprintf(_XOBJ_ERR_REQUIRED, $k);
71: continue 2;
72: }
73: $cleanv = xoops_convert_encode($cleanv);
74: if (isset($v['maxlength']) && strlen($cleanv) > (int)$v['maxlength']) {
75: $errors[] = sprintf(_XOBJ_ERR_SHORTERTHAN, $k, (int)$v['maxlength']);
76: continue 2;
77: }
78: if (!$v['not_gpc']) {
79: $cleanv = $myts->stripSlashesGPC($myts->censorString($cleanv));
80: } else {
81: $cleanv = $myts->censorString($cleanv);
82: }
83: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
84: break;
85:
86: case XOBJ_DTYPE_UNICODE_TXTAREA:
87: if ($v['required'] && $cleanv != '0' && $cleanv == '') {
88: $errors[] = sprintf(_XOBJ_ERR_REQUIRED, $k);
89: continue 2;
90: }
91: $cleanv = xoops_convert_encode($cleanv);
92: if (!$v['not_gpc']) {
93: if (!empty($vars['dohtml']['value'])) {
94: $cleanv = $myts->textFilter($cleanv);
95: }
96: $cleanv = $myts->stripSlashesGPC($myts->censorString($cleanv));
97: } else {
98: $cleanv = $myts->censorString($cleanv);
99: }
100: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
101: break;
102:
103: case XOBJ_DTYPE_TXTBOX:
104: if ($v['required'] && $cleanv != '0' && $cleanv == '') {
105: $errors[] = sprintf(_XOBJ_ERR_REQUIRED, $k);
106: continue 2;
107: }
108: if (isset($v['maxlength']) && strlen($cleanv) > (int)$v['maxlength']) {
109: $errors[] = sprintf(_XOBJ_ERR_SHORTERTHAN, $k, (int)$v['maxlength']);
110: continue 2;
111: }
112: if (!$v['not_gpc']) {
113: $cleanv = $myts->stripSlashesGPC($myts->censorString($cleanv));
114: } else {
115: $cleanv = $myts->censorString($cleanv);
116: }
117: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
118: break;
119:
120: case XOBJ_DTYPE_TXTAREA:
121: if ($v['required'] && $cleanv != '0' && $cleanv == '') {
122: $errors[] = sprintf(_XOBJ_ERR_REQUIRED, $k);
123: continue 2;
124: }
125: if (!$v['not_gpc']) {
126: if (!empty($vars['dohtml']['value'])) {
127: $cleanv = $myts->textFilter($cleanv);
128: }
129: $cleanv = $myts->stripSlashesGPC($myts->censorString($cleanv));
130: } else {
131: $cleanv = $myts->censorString($cleanv);
132: }
133: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
134: break;
135:
136: case XOBJ_DTYPE_SOURCE:
137: $cleanv = trim($cleanv);
138: if (!$v['not_gpc']) {
139: $cleanv = $myts->stripSlashesGPC($cleanv);
140: } else {
141: $cleanv = $cleanv;
142: }
143: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
144: break;
145: // Should not be used!
146: case XOBJ_DTYPE_UNICODE_EMAIL:
147: $cleanv = trim($cleanv);
148: if ($v['required'] && $cleanv == '') {
149: $errors[] = sprintf(_XOBJ_ERR_REQUIRED, $k);
150: continue 2;
151: }
152: if (!$v['not_gpc']) {
153: $cleanv = $myts->stripSlashesGPC($cleanv);
154: }
155: $cleanv = str_replace('\\"', '"', $this->handler->db->quote(xoops_convert_encode($cleanv)));
156: break;
157:
158: case XOBJ_DTYPE_EMAIL:
159: $cleanv = trim($cleanv);
160: if ($v['required'] && $cleanv == '') {
161: $errors[] = sprintf(_XOBJ_ERR_REQUIRED, $k);
162: continue 2;
163: }
164: if ($cleanv != '' && !preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+([\.][a-z0-9-]+)+$/i", $cleanv)) {
165: $errors[] = 'Invalid Email';
166: continue 2;
167: }
168: if (!$v['not_gpc']) {
169: $cleanv = $myts->stripSlashesGPC($cleanv);
170: }
171: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
172: break;
173:
174: // Should not be used!
175: case XOBJ_DTYPE_UNICODE_URL:
176: $cleanv = trim($cleanv);
177: if ($v['required'] && $cleanv == '') {
178: $errors[] = sprintf(_XOBJ_ERR_REQUIRED, $k);
179: continue 2;
180: }
181: if ($cleanv != '' && !preg_match("/^http[s]*:\/\//i", $cleanv)) {
182: $cleanv = XOOPS_PROT . $cleanv;
183: }
184: if (!$v['not_gpc']) {
185: $cleanv = $myts->stripSlashesGPC($cleanv);
186: }
187: $cleanv = str_replace('\\"', '"', $this->handler->db->quote(xoops_convert_encode($cleanv)));
188: break;
189: case XOBJ_DTYPE_URL:
190: $cleanv = trim($cleanv);
191: if ($v['required'] && $cleanv == '') {
192: $errors[] = sprintf(_XOBJ_ERR_REQUIRED, $k);
193: continue 2;
194: }
195: if ($cleanv != '' && !preg_match("/^http[s]*:\/\//i", $cleanv)) {
196: $cleanv = XOOPS_PROT . $cleanv;
197: }
198: if (!$v['not_gpc']) {
199: $cleanv = $myts->stripSlashesGPC($cleanv);
200: }
201: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
202: break;
203:
204: // Should not be used!
205: case XOBJ_DTYPE_UNICODE_OTHER:
206: $cleanv = str_replace('\\"', '"', $this->handler->db->quote(xoops_convert_encode($cleanv)));
207: break;
208:
209: case XOBJ_DTYPE_OTHER:
210: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
211: break;
212:
213: case XOBJ_DTYPE_INT:
214: $cleanv = (int)$cleanv;
215: break;
216:
217: case XOBJ_DTYPE_FLOAT:
218: $cleanv = (float)$cleanv;
219: break;
220:
221: case XOBJ_DTYPE_DECIMAL:
222: $cleanv = (float)$cleanv;
223: break;
224:
225: // Should not be used!
226: case XOBJ_DTYPE_UNICODE_ARRAY:
227: if (!$v['not_gpc']) {
228: $cleanv = array_map(array(&$myts, 'stripSlashesGPC'), $cleanv);
229: }
230: foreach (array_keys($cleanv) as $key) {
231: $cleanv[$key] = str_replace('\\"', '"', addslashes($cleanv[$key]));
232: }
233: // TODO: Not encoding safe, should try base64_encode -- phppp
234: $cleanv = "'" . serialize(array_walk($cleanv, 'xoops_aw_encode')) . "'";
235: break;
236:
237: case XOBJ_DTYPE_ARRAY:
238: $cleanv = (array)$cleanv;
239: if (!$v['not_gpc']) {
240: $cleanv = array_map(array(&$myts, 'stripSlashesGPC'), $cleanv);
241: }
242: // TODO: Not encoding safe, should try base64_encode -- phppp
243: $cleanv = $this->handler->db->quote(serialize($cleanv));
244: break;
245:
246: case XOBJ_DTYPE_STIME:
247: case XOBJ_DTYPE_MTIME:
248: case XOBJ_DTYPE_LTIME:
249: $cleanv = !is_string($cleanv) ? (int)$cleanv : strtotime($cleanv);
250: break;
251:
252: default:
253: $cleanv = str_replace('\\"', '"', $this->handler->db->quote($cleanv));
254: break;
255: }
256: $object->cleanVars[$k] = $cleanv;
257: }
258: if (!empty($errors)) {
259: $object->setErrors($errors);
260: }
261: $object->unsetDirty();
262:
263: return empty($errors) ? true : false;
264: }
265:
266: /**
267: * insert an object into the database
268: *
269: * @param object $object {@link XoopsObject} reference to object
270: * @param bool $force flag to force the query execution despite security settings
271: * @return mixed object ID
272: */
273: public function insert(&$object, $force = true)
274: {
275: if (!$object->isDirty()) {
276: trigger_error("Data entry is not inserted - the object '" . get_class($object) . "' is not dirty", E_USER_NOTICE);
277:
278: return $object->getVar($this->handler->keyName);
279: }
280: if (!$this->cleanVars($object)) {
281: trigger_error("Insert failed in method 'cleanVars' of object '" . get_class($object) . "'", E_USER_WARNING);
282:
283: return $object->getVar($this->handler->keyName);
284: }
285: $queryFunc = empty($force) ? 'query' : 'queryF';
286:
287: if ($object->isNew()) {
288: $sql = 'INSERT INTO `' . $this->handler->table . '`';
289: if (!empty($object->cleanVars)) {
290: $keys = array_keys($object->cleanVars);
291: $vals = array_values($object->cleanVars);
292: $sql .= ' (`' . implode('`, `', $keys) . '`) VALUES (' . implode(',', $vals) . ')';
293: } else {
294: trigger_error("Data entry is not inserted - no variable is changed in object of '" . get_class($object) . "'", E_USER_NOTICE);
295:
296: return $object->getVar($this->handler->keyName);
297: }
298: if (!$result = $this->handler->db->{$queryFunc}($sql)) {
299: return false;
300: }
301: if (!$object->getVar($this->handler->keyName) && $object_id = $this->handler->db->getInsertId()) {
302: $object->assignVar($this->handler->keyName, $object_id);
303: }
304: } elseif (!empty($object->cleanVars)) {
305: $keys = array();
306: foreach ($object->cleanVars as $k => $v) {
307: $keys[] = " `{$k}` = {$v}";
308: }
309: $sql = 'UPDATE `' . $this->handler->table . '` SET ' . implode(',', $keys) . ' WHERE `' . $this->handler->keyName . '` = ' . $this->handler->db->quote($object->getVar($this->handler->keyName));
310: if (!$result = $this->handler->db->{$queryFunc}($sql)) {
311: return false;
312: }
313: }
314:
315: return $object->getVar($this->handler->keyName);
316: }
317:
318: /**
319: * delete an object from the database
320: *
321: * @param object $object {@link XoopsObject} reference to the object to delete
322: * @param bool $force
323: * @return bool FALSE if failed.
324: */
325: public function delete(&$object, $force = false)
326: {
327: if (is_array($this->handler->keyName)) {
328: $clause = array();
329: $thishandlerkeyNameCount = count($this->handler->keyName);
330: for ($i = 0; $i < $thishandlerkeyNameCount; ++$i) {
331: $clause[] = '`' . $this->handler->keyName[$i] . '` = ' . $this->handler->db->quote($object->getVar($this->handler->keyName[$i]));
332: }
333: $whereclause = implode(' AND ', $clause);
334: } else {
335: $whereclause = '`' . $this->handler->keyName . '` = ' . $this->handler->db->quote($object->getVar($this->handler->keyName));
336: }
337: $sql = 'DELETE FROM `' . $this->handler->table . '` WHERE ' . $whereclause;
338: $queryFunc = empty($force) ? 'query' : 'queryF';
339: $result = $this->handler->db->{$queryFunc}($sql);
340:
341: return empty($result) ? false : true;
342: }
343:
344: /**
345: * delete all objects matching the conditions
346: *
347: * @param CriteriaElement|CriteriaCompo $criteria {@link CriteriaElement} with conditions to meet
348: * @param bool $force force to delete
349: * @param bool $asObject delete in object way: instantiate all objects and delete one by one
350: * @return bool|int
351: */
352: public function deleteAll(CriteriaElement $criteria = null, $force = true, $asObject = false)
353: {
354: if ($asObject) {
355: $objects = $this->handler->getAll($criteria);
356: $num = 0;
357: foreach (array_keys($objects) as $key) {
358: $num += $this->delete($objects[$key], $force) ? 1 : 0;
359: }
360: unset($objects);
361:
362: return $num;
363: }
364: $queryFunc = empty($force) ? 'query' : 'queryF';
365: $sql = 'DELETE FROM ' . $this->handler->table;
366: if (!empty($criteria)) {
367: if (is_subclass_of($criteria, 'CriteriaElement')) {
368: $sql .= ' ' . $criteria->renderWhere();
369: } else {
370: return false;
371: }
372: }
373: if (!$this->handler->db->{$queryFunc}($sql)) {
374: return false;
375: }
376:
377: return $this->handler->db->getAffectedRows();
378: }
379:
380: /**
381: * Change a field for objects with a certain criteria
382: *
383: * @param string $fieldname Name of the field
384: * @param mixed $fieldvalue Value to write
385: * @param CriteriaElement|CriteriaCompo $criteria {@link CriteriaElement}
386: * @param bool $force force to query
387: * @return bool
388: */
389: public function updateAll($fieldname, $fieldvalue, CriteriaElement $criteria = null, $force = false)
390: {
391: $set_clause = "`{$fieldname}` = ";
392: if (is_numeric($fieldvalue)) {
393: $set_clause .= $fieldvalue;
394: } elseif (is_array($fieldvalue)) {
395: $set_clause .= $this->handler->db->quote(implode(',', $fieldvalue));
396: } else {
397: $set_clause .= $this->handler->db->quote($fieldvalue);
398: }
399: $sql = 'UPDATE `' . $this->handler->table . '` SET ' . $set_clause;
400: if (isset($criteria) && \method_exists($criteria, 'renderWhere')) {
401: $sql .= ' ' . $criteria->renderWhere();
402: }
403: $queryFunc = empty($force) ? 'query' : 'queryF';
404: $result = $this->handler->db->{$queryFunc}($sql);
405:
406: return empty($result) ? false : true;
407: }
408: }
409: