XoopsObject::toArray()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
1
<?php
2
/**
3
 * XOOPS Kernel Object
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-2025 XOOPS Project (https://xoops.org)
13
 * @license             GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package             kernel
15
 * @since               2.0.0
16
 * @author              Kazumi Ono (AKA onokazu) http://www.myweb.ne.jp/, http://jp.xoops.org/
17
 * @author              Taiwen Jiang <[email protected]>
18
 */
19
20
defined('XOOPS_ROOT_PATH') || exit('Restricted access');
21
/**
22
 * YOU SHOULD NOT USE ANY OF THE UNICODE TYPES, THEY WILL BE REMOVED
23
 */
24
25
/**
26
 * *#@+
27
 * Xoops object datatype
28
 */
29
define('XOBJ_DTYPE_TXTBOX', 1);
30
define('XOBJ_DTYPE_TXTAREA', 2);
31
define('XOBJ_DTYPE_INT', 3);
32
define('XOBJ_DTYPE_URL', 4);
33
define('XOBJ_DTYPE_EMAIL', 5);
34
define('XOBJ_DTYPE_ARRAY', 6);
35
define('XOBJ_DTYPE_OTHER', 7);
36
define('XOBJ_DTYPE_SOURCE', 8);
37
define('XOBJ_DTYPE_STIME', 9);
38
define('XOBJ_DTYPE_MTIME', 10);
39
define('XOBJ_DTYPE_LTIME', 11);
40
define('XOBJ_DTYPE_FLOAT', 13);
41
define('XOBJ_DTYPE_DECIMAL', 14);
42
define('XOBJ_DTYPE_ENUM', 15);
43
// YOU SHOULD NEVER USE THE FOLLOWING TYPES, THEY WILL BE REMOVED
44
define('XOBJ_DTYPE_UNICODE_TXTBOX', 16);
45
define('XOBJ_DTYPE_UNICODE_TXTAREA', 17);
46
define('XOBJ_DTYPE_UNICODE_URL', 18);
47
define('XOBJ_DTYPE_UNICODE_EMAIL', 19);
48
define('XOBJ_DTYPE_UNICODE_ARRAY', 20);
49
define('XOBJ_DTYPE_UNICODE_OTHER', 21);
50
// Addition for 2.5.5
51
define('XOBJ_DTYPE_DATE', 22);
52
define('XOBJ_DTYPE_TIME', 23);
53
define('XOBJ_DTYPE_TIMESTAMP', 24);
54
55
/**
56
 * Base class for all objects in the Xoops kernel (and beyond)
57
 */
58
class XoopsObject
59
{
60
    /**
61
     * holds all variables(properties) of an object
62
     *
63
     * @var array
64
     * @access protected
65
     */
66
    public $vars = [];
67
68
    /**
69
     * variables cleaned for store in DB
70
     *
71
     * @var array
72
     * @access protected
73
     */
74
    public $cleanVars = [];
75
76
    /**
77
     * is it a newly created object?
78
     *
79
     * @var bool
80
     * @access private
81
     */
82
    public $_isNew = false;
83
84
    /**
85
     * has any of the values been modified?
86
     *
87
     * @var bool
88
     * @access private
89
     */
90
    public $_isDirty = false;
91
92
    /**
93
     * errors
94
     *
95
     * @var array
96
     * @access private
97
     */
98
    public $_errors = [];
99
100
    /**
101
     * additional filters registered dynamically by a child class object
102
     *
103
     * @access private
104
     */
105
    public $_filters = [];
106
107
    /**
108
     * constructor
109
     *
110
     * normally, this is called from child classes only
111
     *
112
     * @access public
113
     */
114
    public function __construct() {}
115
116
    /**
117
     * PHP 4 style constructor compatibility shim
118
     * @deprecated all callers should be using parent::__construct()
119
     */
120
    public function XoopsObject()
121
    {
122
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
123
        trigger_error("Should call parent::__construct in {$trace[0]['file']} line {$trace[0]['line']},", E_USER_DEPRECATED);
124
        self::__construct();
0 ignored issues
show
Bug Best Practice introduced by
The method XoopsObject::__construct() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

124
        self::/** @scrutinizer ignore-call */ 
125
              __construct();
Loading history...
125
    }
126
127
    /**
128
     * *#@+
129
     * used for new/clone objects
130
     *
131
     * @access public
132
     */
133
    public function setNew()
134
    {
135
        $this->_isNew = true;
136
    }
137
138
    public function unsetNew()
139
    {
140
        $this->_isNew = false;
141
    }
142
143
    /**
144
     * @return bool
145
     */
146
    public function isNew()
147
    {
148
        return $this->_isNew;
149
    }
150
151
    /**
152
     * *#@+
153
     * mark modified objects as dirty
154
     *
155
     * used for modified objects only
156
     *
157
     * @access public
158
     */
159
    public function setDirty()
160
    {
161
        $this->_isDirty = true;
162
    }
163
164
    public function unsetDirty()
165
    {
166
        $this->_isDirty = false;
167
    }
168
169
    /**
170
     * @return bool
171
     */
172
    public function isDirty()
173
    {
174
        return $this->_isDirty;
175
    }
176
177
    /**
178
     * initialize variables for the object
179
     *
180
     * YOU SHOULD NOT USE THE $enumeration PARAMETER
181
     *
182
     * @access   public
183
     *
184
     * @param string   $key
185
     * @param int      $data_type set to one of XOBJ_DTYPE_XXX constants (set to XOBJ_DTYPE_OTHER if no data type checking nor text sanitizing is required)
186
     * @param mixed    $value
187
     * @param bool     $required  require html form input?
188
     * @param int|null $maxlength for XOBJ_DTYPE_TXTBOX type only
189
     * @param string   $options
190
     * @param string   $enumerations
191
     *
192
     * @return void
193
     */
194
    public function initVar($key, $data_type, $value = null, $required = false, $maxlength = null, $options = '', $enumerations = '')
195
    {
196
        $this->vars[$key] = [
197
            'value'       => $value,
198
            'required'    => $required,
199
            'data_type'   => $data_type,
200
            'maxlength'   => $maxlength,
201
            'changed'     => false,
202
            'options'     => $options,
203
            'enumeration' => $enumerations,
204
        ];
205
    }
206
207
    /**
208
     * assign a value to a variable
209
     *
210
     * @access public
211
     * @param string $key   name of the variable to assign
212
     * @param mixed  $value value to assign
213
     */
214
    public function assignVar($key, $value)
215
    {
216
        if (isset($key) && isset($this->vars[$key])) {
217
            switch ($this->vars[$key]['data_type']) {
218
                case XOBJ_DTYPE_UNICODE_ARRAY:
219
                    if (is_array($value)) {
220
                        $temp = $value;
221
                        array_walk($temp, 'xoops_aw_decode');
222
                        $value = $temp;
223
                        $this->vars[$key]['value'] = $value;
224
                    } else {
225
                        $this->vars[$key]['value'] = xoops_convert_decode($value);
226
                    }
227
                    break;
228
                case XOBJ_DTYPE_UNICODE_URL:
229
                case XOBJ_DTYPE_UNICODE_EMAIL:
230
                case XOBJ_DTYPE_UNICODE_OTHER:
231
                case XOBJ_DTYPE_UNICODE_TXTBOX:
232
                case XOBJ_DTYPE_UNICODE_TXTAREA:
233
                    $this->vars[$key]['value'] = xoops_convert_decode($value);
234
                    break;
235
                case XOBJ_DTYPE_DATE:
236
                    if (!is_string($value) && is_numeric($value)) {
237
                        $this->vars[$key]['value'] = date(_DBDATESTRING, $value);
238
                    } else {
239
                        $this->vars[$key]['value'] = date(_DBDATESTRING, strtotime($value));
240
                    }
241
                    break;
242
                case XOBJ_DTYPE_TIME:
243
                    if (!is_string($value) && is_numeric($value)) {
244
                        $this->vars[$key]['value'] = date(_DBTIMESTRING, $value);
245
                    } else {
246
                        $this->vars[$key]['value'] = date(_DBTIMESTRING, strtotime($value));
247
                    }
248
                    break;
249
                case XOBJ_DTYPE_TIMESTAMP:
250
                    if (!is_string($value) && is_numeric($value)) {
251
                        $this->vars[$key]['value'] = date(_DBTIMESTAMPSTRING, $value);
252
                    } else {
253
                        $this->vars[$key]['value'] = date(_DBTIMESTAMPSTRING, strtotime($value));
254
                    }
255
                    break;
256
                    // YOU SHOULD NOT USE THE ABOVE TYPES, THEY WILL BE REMOVED
257
                default:
258
                    $this->vars[$key]['value'] = & $value;
259
            }
260
        }
261
    }
262
263
    /**
264
     * assign values to multiple variables in a batch
265
     *
266
     * @access   private
267
     * @param array $var_arr associative array of values to assign
268
     */
269
    public function assignVars($var_arr)
270
    {
271
        if (is_array($var_arr)) {
0 ignored issues
show
introduced by
The condition is_array($var_arr) is always true.
Loading history...
272
            foreach ($var_arr as $key => $value) {
273
                $this->assignVar($key, $value);
274
            }
275
        }
276
    }
277
278
    /**
279
     * assign a value to a variable
280
     *
281
     * @access public
282
     * @param string $key   name of the variable to assign
283
     * @param mixed  $value value to assign
284
     * @param bool   $not_gpc
285
     */
286
    public function setVar($key, $value, $not_gpc = false)
287
    {
288
        if (!empty($key) && isset($value) && isset($this->vars[$key])) {
289
            $this->vars[$key]['value']   = & $value;
290
            $this->vars[$key]['not_gpc'] = $not_gpc;
291
            $this->vars[$key]['changed'] = true;
292
            $this->setDirty();
293
        }
294
    }
295
296
    /**
297
     * assign values to multiple variables in a batch
298
     *
299
     * @access private
300
     * @param array $var_arr associative array of values to assign
301
     * @param bool  $not_gpc
302
     */
303
    public function setVars($var_arr, $not_gpc = false)
304
    {
305
        if (is_array($var_arr)) {
0 ignored issues
show
introduced by
The condition is_array($var_arr) is always true.
Loading history...
306
            foreach ($var_arr as $key => $value) {
307
                $this->setVar($key, $value, $not_gpc);
308
            }
309
        }
310
    }
311
312
    /**
313
     * unset variable(s) for the object
314
     *
315
     * @access public
316
     *
317
     * @param mixed $var
318
     *
319
     * @return bool
320
     */
321
    public function destroyVars($var)
322
    {
323
        if (empty($var)) {
324
            return true;
325
        }
326
        $var = !is_array($var) ? [$var] : $var;
327
        foreach ($var as $key) {
328
            if (!isset($this->vars[$key])) {
329
                continue;
330
            }
331
            $this->vars[$key]['changed'] = null;
332
        }
333
334
        return true;
335
    }
336
337
    /**
338
     * @param mixed $var
339
     * @return bool
340
     * @deprecated use destroyVars() instead,  destoryVars() will be removed in the next major release
341
     */
342
    public function destoryVars($var)
343
    {
344
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
345
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . "() is deprecated, called from {$trace[0]['file']}line {$trace[0]['line']}");
346
        return $this->destroyVars($var);
347
    }
348
349
    /**
350
     * Assign values to multiple variables in a batch
351
     *
352
     * Meant for a CGI context:
353
     * - prefixed CGI args are considered save
354
     * - avoids polluting of namespace with CGI args
355
     *
356
     * @param array  $var_arr associative array of values to assign
357
     * @param string $pref    prefix (only keys starting with the prefix will be set)
358
     * @param bool   $not_gpc
359
     *
360
     * @return void
361
     *
362
     * @deprecated This method will be removed in the next major XOOPS version
363
     */
364
    public function setFormVars($var_arr = null, $pref = 'xo_', $not_gpc = false)
365
    {
366
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
367
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . "() is deprecated, called from {$trace[0]['file']}line {$trace[0]['line']}");
368
369
        $len = strlen($pref);
370
        if (is_array($var_arr)) {
371
            foreach ($var_arr as $key => $value) {
372
                if ($pref == substr($key, 0, $len)) {
373
                    $this->setVar(substr($key, $len), $value, $not_gpc);
374
                }
375
            }
376
        }
377
    }
378
379
    /**
380
     * returns all variables for the object
381
     *
382
     * @access public
383
     * @return array associative array of key->value pairs
384
     */
385
    public function &getVars()
386
    {
387
        return $this->vars;
388
    }
389
390
    /**
391
     * Returns the values of the specified variables
392
     *
393
     * @param  mixed  $keys     An array containing the names of the keys to retrieve, or null to get all of them
394
     * @param  string $format   Format to use (see getVar)
395
     * @param  int    $maxDepth Maximum level of recursion to use if some vars are objects themselves
396
     * @return array  associative array of key->value pairs
397
     */
398
    public function getValues($keys = null, $format = 's', $maxDepth = 1)
399
    {
400
        if (!isset($keys)) {
401
            $keys = array_keys($this->vars);
402
        }
403
        $vars = [];
404
        foreach ($keys as $key) {
405
            if (isset($this->vars[$key])) {
406
                if (is_object($this->vars[$key]) && is_a($this->vars[$key], 'XoopsObject')) {
407
                    if ($maxDepth) {
408
                        $vars[$key] = $this->vars[$key]->getValues(null, $format, $maxDepth - 1);
409
                    }
410
                } else {
411
                    $vars[$key] = $this->getVar($key, $format);
412
                }
413
            }
414
        }
415
416
        return $vars;
417
    }
418
419
    /**
420
     * returns a specific variable for the object in a proper format
421
     *
422
     * YOU SHOULD NOT USE ANY OF THE UNICODE TYPES, THEY WILL BE REMOVED
423
     *
424
     * @access public
425
     * @param string      $key    key of the object's variable to be returned
426
     * @param string|null $format format to use for the output
427
     * @return mixed  formatted value of the variable
428
     */
429
    public function getVar($key, ?string $format = null)
430
    {
431
        $format = (null === $format) ? 's' : (string) $format;
432
        $ret = null;
433
        if (!isset($this->vars[$key])) {
434
            return $ret;
435
        }
436
        $ret = $this->vars[$key]['value'];
437
        $myts  = \MyTextSanitizer::getInstance();
438
        switch ($this->vars[$key]['data_type']) {
439
            case XOBJ_DTYPE_INT:
440
                $ret = (null === $ret) ? '' : (int) $ret;
441
                break;
442
            case XOBJ_DTYPE_UNICODE_TXTBOX:
443
            case XOBJ_DTYPE_TXTBOX:
444
                switch (strtolower($format)) {
445
                    case 's':
446
                    case 'show':
447
                    case 'e':
448
                    case 'edit':
449
                        return $myts->htmlSpecialChars((string)$ret);
450
                        break 1;
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...
451
                    case 'p':
452
                    case 'preview':
453
                    case 'f':
454
                    case 'formpreview':
455
                        return $myts->htmlSpecialChars((string)$ret);
456
                        break 1;
457
                    case 'n':
458
                    case 'none':
459
                    default:
460
                        break 1;
461
                }
462
                break;
463
            case XOBJ_DTYPE_UNICODE_TXTAREA:
464
            case XOBJ_DTYPE_TXTAREA:
465
                switch (strtolower($format)) {
466
                    case 's':
467
                    case 'show':
468
                        $html   = !empty($this->vars['dohtml']['value']) ? 1 : 0;
469
                        $xcode  = (!isset($this->vars['doxcode']['value']) || $this->vars['doxcode']['value'] == 1) ? 1 : 0;
470
                        $smiley = (!isset($this->vars['dosmiley']['value']) || $this->vars['dosmiley']['value'] == 1) ? 1 : 0;
471
                        $image  = (!isset($this->vars['doimage']['value']) || $this->vars['doimage']['value'] == 1) ? 1 : 0;
472
                        $br     = (!isset($this->vars['dobr']['value']) || $this->vars['dobr']['value'] == 1) ? 1 : 0;
473
474
                        return $myts->displayTarea($ret, $html, $smiley, $xcode, $image, $br);
475
                        break 1;
476
                    case 'e':
477
                    case 'edit':
478
                        return htmlspecialchars((string) $ret, ENT_QUOTES | ENT_HTML5);
479
                        break 1;
480
                    case 'p':
481
                    case 'preview':
482
                        $html   = !empty($this->vars['dohtml']['value']) ? 1 : 0;
483
                        $xcode  = (!isset($this->vars['doxcode']['value']) || $this->vars['doxcode']['value'] == 1) ? 1 : 0;
484
                        $smiley = (!isset($this->vars['dosmiley']['value']) || $this->vars['dosmiley']['value'] == 1) ? 1 : 0;
485
                        $image  = (!isset($this->vars['doimage']['value']) || $this->vars['doimage']['value'] == 1) ? 1 : 0;
486
                        $br     = (!isset($this->vars['dobr']['value']) || $this->vars['dobr']['value'] == 1) ? 1 : 0;
487
488
                        return $myts->previewTarea($ret, $html, $smiley, $xcode, $image, $br);
489
                        break 1;
490
                    case 'f':
491
                    case 'formpreview':
492
                        return htmlspecialchars((string)$ret, ENT_QUOTES | ENT_HTML5);
493
                        break 1;
494
                    case 'n':
495
                    case 'none':
496
                    default:
497
                        break 1;
498
                }
499
                break;
500
            case XOBJ_DTYPE_UNICODE_ARRAY:
501
                switch (strtolower($format)) {
502
                    case 'n':
503
                    case 'none':
504
                        break 1;
505
                    default:
506
                        if (!is_array($ret)) {
507
                            if ($ret != '') {
508
                                $ret = unserialize($ret);
509
                            }
510
                            $ret = is_array($ret) ? $ret : [];
511
                            if (is_array($ret)) {
0 ignored issues
show
introduced by
The condition is_array($ret) is always true.
Loading history...
512
                                $ret = array_walk($ret, 'xoops_aw_decode');
513
                            }
514
                        }
515
516
                        return $ret;
517
                        break 1;
518
                }
519
                break;
520
            case XOBJ_DTYPE_ARRAY:
521
                switch (strtolower($format)) {
522
                    case 'n':
523
                    case 'none':
524
                        break 1;
525
                    default:
526
                        if (!is_array($ret)) {
527
                            if ($ret != '') {
528
                                $ret = unserialize($ret);
529
                            }
530
                            $ret = is_array($ret) ? $ret : [];
531
                        }
532
533
                        return $ret;
534
                        break 1;
535
                }
536
                break;
537
            case XOBJ_DTYPE_SOURCE:
538
                switch (strtolower($format)) {
539
                    case 's':
540
                    case 'show':
541
                        break 1;
542
                    case 'e':
543
                    case 'edit':
544
                        return htmlspecialchars((string)$ret, ENT_QUOTES | ENT_HTML5);
545
                        break 1;
546
                    case 'p':
547
                    case 'preview':
548
                        return $ret;
549
                        break 1;
550
                    case 'f':
551
                    case 'formpreview':
552
                        return htmlspecialchars((string)$ret, ENT_QUOTES | ENT_HTML5);
553
                        break 1;
554
                    case 'n':
555
                    case 'none':
556
                    default:
557
                        break 1;
558
                }
559
                break;
560
            case XOBJ_DTYPE_DATE:
561
                switch (strtolower($format)) {
562
                    case 's':
563
                    case 'show':
564
                        if (is_string($ret) && !is_numeric($ret)) {
565
                            return date(_DBDATESTRING, strtotime($ret));
566
                        } else {
567
                            return date(_DBDATESTRING, $ret);
568
                        }
569
                        break 1;
570
                    case 'e':
571
                    case 'edit':
572
                        if (is_string($ret) && !is_numeric($ret)) {
573
                            return htmlspecialchars(date(_DBDATESTRING, strtotime($ret)), ENT_QUOTES | ENT_HTML5);
574
                        } else {
575
                            return htmlspecialchars(date(_DBDATESTRING, $ret), ENT_QUOTES | ENT_HTML5);
576
                        }
577
                        break 1;
578
                    case 'p':
579
                    case 'preview':
580
                        if (is_string($ret) && !is_numeric($ret)) {
581
                            return date(_DBDATESTRING, strtotime($ret));
582
                        } else {
583
                            return date(_DBDATESTRING, $ret);
584
                        }
585
                        break 1;
586
                    case 'f':
587
                    case 'formpreview':
588
                        if (is_string($ret) && !is_numeric($ret)) {
589
                            return htmlspecialchars(date(_DBDATESTRING, strtotime($ret)), ENT_QUOTES | ENT_HTML5);
590
                        } else {
591
                            return htmlspecialchars(date(_DBDATESTRING, $ret), ENT_QUOTES | ENT_HTML5);
592
                        }
593
                        break 1;
594
                    case 'n':
595
                    case 'none':
596
                    default:
597
                        break 1;
598
                }
599
                break;
600
            case XOBJ_DTYPE_TIME:
601
                switch (strtolower($format)) {
602
                    case 's':
603
                    case 'show':
604
                        if (is_string($ret) && !is_numeric($ret)) {
605
                            return date(_DBTIMESTRING, strtotime($ret));
606
                        } else {
607
                            return date(_DBTIMESTRING, $ret);
608
                        }
609
                        break 1;
610
                    case 'e':
611
                    case 'edit':
612
                        if (is_string($ret) && !is_numeric($ret)) {
613
                            return htmlspecialchars(date(_DBTIMESTRING, strtotime($ret)), ENT_QUOTES | ENT_HTML5);
614
                        } else {
615
                            return htmlspecialchars(date(_DBTIMESTRING, $ret), ENT_QUOTES | ENT_HTML5);
616
                        }
617
                        break 1;
618
                    case 'p':
619
                    case 'preview':
620
                        if (is_string($ret) && !is_numeric($ret)) {
621
                            return date(_DBTIMESTRING, strtotime($ret));
622
                        } else {
623
                            return date(_DBTIMESTRING, $ret);
624
                        }
625
                        break 1;
626
                    case 'f':
627
                    case 'formpreview':
628
                        if (is_string($ret) && !is_numeric($ret)) {
629
                            return htmlspecialchars(date(_DBTIMESTRING, strtotime($ret)), ENT_QUOTES | ENT_HTML5);
630
                        } else {
631
                            return htmlspecialchars(date(_DBTIMESTRING, $ret), ENT_QUOTES | ENT_HTML5);
632
                        }
633
                        break 1;
634
                    case 'n':
635
                    case 'none':
636
                    default:
637
                        break 1;
638
                }
639
                break;
640
            case XOBJ_DTYPE_TIMESTAMP:
641
                switch (strtolower($format)) {
642
                    case 's':
643
                    case 'show':
644
                        if (is_string($ret) && !is_numeric($ret)) {
645
                            return date(_DBTIMESTAMPSTRING, strtotime($ret));
646
                        } else {
647
                            return date(_DBTIMESTAMPSTRING, $ret);
648
                        }
649
                        break 1;
650
                    case 'e':
651
                    case 'edit':
652
                        if (is_string($ret) && !is_numeric($ret)) {
653
                            return htmlspecialchars(date(_DBTIMESTAMPSTRING, strtotime($ret)), ENT_QUOTES | ENT_HTML5);
654
                        } else {
655
                            return htmlspecialchars(date(_DBTIMESTAMPSTRING, $ret), ENT_QUOTES | ENT_HTML5);
656
                        }
657
                        break 1;
658
                    case 'p':
659
                    case 'preview':
660
                        if (is_string($ret) && !is_numeric($ret)) {
661
                            return date(_DBDATESTRING, strtotime($ret));
662
                        } else {
663
                            return date(_DBDATESTRING, $ret);
664
                        }
665
                        break 1;
666
                    case 'f':
667
                    case 'formpreview':
668
                        if (is_string($ret) && !is_numeric($ret)) {
669
                            return htmlspecialchars(date(_DBTIMESTAMPSTRING, strtotime($ret)), ENT_QUOTES | ENT_HTML5);
670
                        } else {
671
                            return htmlspecialchars(date(_DBTIMESTAMPSTRING, $ret), ENT_QUOTES | ENT_HTML5);
672
                        }
673
                        break 1;
674
                    case 'n':
675
                    case 'none':
676
                    default:
677
                        break 1;
678
                }
679
                break;
680
            default:
681
                if ($this->vars[$key]['options'] != '' && $ret != '') {
682
                    switch (strtolower($format)) {
683
                        case 's':
684
                        case 'show':
685
                            $selected = explode('|', $ret);
686
                            $options  = explode('|', $this->vars[$key]['options']);
687
                            $i        = 1;
688
                            $ret      = [];
689
                            foreach ($options as $op) {
690
                                if (in_array($i, $selected)) {
691
                                    $ret[] = $op;
692
                                }
693
                                ++$i;
694
                            }
695
696
                            return implode(', ', $ret);
697
                        case 'e':
698
                        case 'edit':
699
                            $ret = explode('|', $ret);
700
                            break 1;
701
                        default:
702
                            break 1;
703
                    }
704
                }
705
                break;
706
        }
707
708
        return $ret;
709
    }
710
711
    /**
712
     * clean values of all variables of the object for storage.
713
     * also add slashes wherever needed
714
     *
715
     * YOU SHOULD NOT USE ANY OF THE UNICODE TYPES, THEY WILL BE REMOVED
716
     *
717
     * @return bool true if successful
718
     * @access public
719
     */
720
    public function cleanVars()
721
    {
722
        $myts              = \MyTextSanitizer::getInstance();
723
        $existing_errors = $this->getErrors();
724
        $this->_errors   = [];
725
        foreach ($this->vars as $k => $v) {
726
            $cleanv = $v['value'];
727
            if (!$v['changed']) {
728
            } else {
729
                $cleanv = is_string($cleanv) ? trim($cleanv) : $cleanv;
730
                switch ($v['data_type']) {
731
                    case XOBJ_DTYPE_TIMESTAMP:
732
                        $cleanv = !is_string($cleanv) && is_numeric($cleanv) ? date(_DBTIMESTAMPSTRING, $cleanv) : date(_DBTIMESTAMPSTRING, strtotime($cleanv));
733
                        break;
734
                    case XOBJ_DTYPE_TIME:
735
                        $cleanv = !is_string($cleanv) && is_numeric($cleanv) ? date(_DBTIMESTRING, $cleanv) : date(_DBTIMESTRING, strtotime($cleanv));
736
                        break;
737
                    case XOBJ_DTYPE_DATE:
738
                        $cleanv = !is_string($cleanv) && is_numeric($cleanv) ? date(_DBDATESTRING, $cleanv) : date(_DBDATESTRING, strtotime($cleanv));
739
                        break;
740
                    case XOBJ_DTYPE_TXTBOX:
741
                        if ($v['required'] && $cleanv != '0' && $cleanv == '') {
742
                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
743
                            continue 2;
744
                        }
745
                        if (isset($v['maxlength']) && strlen($cleanv) > (int) $v['maxlength']) {
746
                            $this->setErrors(sprintf(_XOBJ_ERR_SHORTERTHAN, $k, (int) $v['maxlength']));
747
                            continue 2;
748
                        }
749
                        $cleanv = $myts->censorString($cleanv);
750
                        break;
751
                    case XOBJ_DTYPE_TXTAREA:
752
                        if ($v['required'] && $cleanv != '0' && $cleanv == '') {
753
                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
754
                            continue 2;
755
                        }
756
                        $cleanv = $myts->censorString($cleanv);
757
                        break;
758
                    case XOBJ_DTYPE_SOURCE:
759
                        // Perform any necessary operations for XOBJ_DTYPE_SOURCE, if needed
760
                        break;
761
                    case XOBJ_DTYPE_INT:
762
                        $cleanv = (int) $cleanv;
763
                        break;
764
765
                    case XOBJ_DTYPE_EMAIL:
766
                        if ($v['required'] && $cleanv == '') {
767
                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
768
                            continue 2;
769
                        }
770
                        if ($cleanv != '' && !preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+([\.][a-z0-9-]+)+$/i", $cleanv)) {
771
                            $this->setErrors('Invalid Email'); //_XOBJ_ERR_INVALID_EMAIL
772
                            continue 2;
773
                        }
774
                        break;
775
                    case XOBJ_DTYPE_URL:
776
                        if ($v['required'] && $cleanv == '') {
777
                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
778
                            continue 2;
779
                        }
780
                        if ($cleanv != '' && !preg_match("/^http[s]*:\/\//i", $cleanv)) {
781
                            $cleanv = XOOPS_PROT . $cleanv;
782
                        }
783
                        break;
784
                    case XOBJ_DTYPE_ARRAY:
785
                        $cleanv = (array) $cleanv;
786
                        $cleanv = serialize($cleanv);
787
                        break;
788
                    case XOBJ_DTYPE_STIME:
789
                    case XOBJ_DTYPE_MTIME:
790
                    case XOBJ_DTYPE_LTIME:
791
                        $cleanv = !is_string($cleanv) ? (int) $cleanv : strtotime($cleanv);
792
                        break;
793
                    case XOBJ_DTYPE_FLOAT:
794
                        $cleanv = (float) $cleanv;
795
                        break;
796
                    case XOBJ_DTYPE_DECIMAL:
797
                        $cleanv = (float) $cleanv;
798
                        break;
799
                    case XOBJ_DTYPE_ENUM:
800
                        if (!in_array($cleanv, $v['enumeration'])) {
801
                            $this->setErrors('Invalid Enumeration');//_XOBJ_ERR_INVALID_ENUMERATION
802
                            continue 2;
803
                        }
804
                        break;
805
                    case XOBJ_DTYPE_UNICODE_TXTBOX:
806
                        if ($v['required'] && $cleanv != '0' && $cleanv == '') {
807
                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
808
                            continue 2;
809
                        }
810
                        $cleanv = xoops_convert_encode($cleanv);
811
                        if (isset($v['maxlength']) && strlen($cleanv) > (int) $v['maxlength']) {
812
                            $this->setErrors(sprintf(_XOBJ_ERR_SHORTERTHAN, $k, (int) $v['maxlength']));
813
                            continue 2;
814
                        }
815
                        $cleanv = $myts->censorString($cleanv);
816
                        break;
817
                    case XOBJ_DTYPE_UNICODE_TXTAREA:
818
                        if ($v['required'] && $cleanv != '0' && $cleanv == '') {
819
                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
820
                            continue 2;
821
                        }
822
                        $cleanv = xoops_convert_encode($cleanv);
823
                        $cleanv = $myts->censorString($cleanv);
824
                        break;
825
                    case XOBJ_DTYPE_UNICODE_EMAIL:
826
                        if ($v['required'] && $cleanv == '') {
827
                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
828
                            continue 2;
829
                        }
830
                        if ($cleanv != '' && !preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+([\.][a-z0-9-]+)+$/i", $cleanv)) {
831
                            $this->setErrors('Invalid Email');
832
                            continue 2;
833
                        }
834
                        $cleanv = xoops_convert_encode($cleanv);
835
                        break;
836
                    case XOBJ_DTYPE_UNICODE_URL:
837
                        if ($v['required'] && $cleanv == '') {
838
                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
839
                            continue 2;
840
                        }
841
                        if ($cleanv != '' && !preg_match("/^http[s]*:\/\//i", $cleanv)) {
842
                            $cleanv = XOOPS_PROT . $cleanv;
843
                        }
844
                        $cleanv = xoops_convert_encode($cleanv);
845
                        break;
846
                    case XOBJ_DTYPE_UNICODE_ARRAY:
847
                        $cleanv = serialize(array_walk($cleanv, 'xoops_aw_encode'));
0 ignored issues
show
Bug introduced by
It seems like $cleanv can also be of type string; however, parameter $array of array_walk() does only seem to accept array|object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

847
                        $cleanv = serialize(array_walk(/** @scrutinizer ignore-type */ $cleanv, 'xoops_aw_encode'));
Loading history...
848
                        break;
849
                    default:
850
                        break;
851
852
                }
853
            }
854
            $this->cleanVars[$k] = str_replace('\\"', '"', (string) $cleanv);
855
            unset($cleanv);
856
        }
857
        if (count($this->_errors) > 0) {
858
            $this->_errors = array_merge($existing_errors, $this->_errors);
859
860
            return false;
861
        }
862
        $this->_errors = array_merge($existing_errors, $this->_errors);
863
        $this->unsetDirty();
864
865
        return true;
866
    }
867
868
    /**
869
     * dynamically register additional filter for the object
870
     *
871
     * @param string $filtername name of the filter
872
     *
873
     * @deprecated \XoopsObject::registerFilter is deprecated since XOOPS 2.5.8 and will be removed in the next major release
874
     */
875
    public function registerFilter($filtername)
876
    {
877
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
878
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . "() is deprecated, called from {$trace[0]['file']}line {$trace[0]['line']}");
879
        $this->_filters[] = $filtername;
880
    }
881
882
    /**
883
     * load all additional filters that have been registered to the object
884
     *
885
     * @access private
886
     */
887
    public function _loadFilters()
888
    {
889
        static $loaded;
890
        if (isset($loaded)) {
891
            return null;
892
        }
893
        $loaded = 1;
894
895
        $path = empty($this->plugin_path) ? __DIR__ . '/filters' : $this->plugin_path;
896
        if (file_exists($file = $path . '/filter.php')) {
897
            include_once $file;
898
            foreach ($this->_filters as $f) {
899
                if (file_exists($file = $path . '/' . strtolower($f) . 'php')) {
900
                    include_once $file;
901
                }
902
            }
903
        }
904
    }
905
906
    /**
907
     * load all local filters for the object
908
     *
909
     * Filter distribution:
910
     * In each module folder there is a folder "filter" containing filter files with,
911
     * filename: [name_of_target_class][.][function/action_name][.php];
912
     * function name: [dirname][_][name_of_target_class][_][function/action_name];
913
     * parameter: the target object
914
     *
915
     * @param string $method function or action name
916
     *
917
     * @deprecated \XoopsObject::loadFilters is deprecated since XOOPS 2.5.8 and will be removed in the next major release
918
     */
919
    public function loadFilters($method)
920
    {
921
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
922
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . "() is deprecated, called from {$trace[0]['file']}line {$trace[0]['line']}");
923
924
        $this->_loadFilters();
925
926
        xoops_load('XoopsCache');
927
        $class = get_class($this);
928
        if (!$modules_active = XoopsCache::read('system_modules_active')) {
929
            /** @var XoopsModuleHandler $module_handler */
930
            $module_handler = xoops_getHandler('module');
931
            $modules_obj    = $module_handler->getObjects(new Criteria('isactive', 1));
932
            $modules_active = [];
933
            foreach (array_keys($modules_obj) as $key) {
934
                $modules_active[] = $modules_obj[$key]->getVar('dirname');
935
            }
936
            unset($modules_obj);
937
            XoopsCache::write('system_modules_active', $modules_active);
938
        }
939
        foreach ($modules_active as $dirname) {
940
            if (file_exists($file = XOOPS_ROOT_PATH . '/modules/' . $dirname . '/filter/' . $class . '.' . $method . '.php')) {
941
                include_once $file;
942
                if (function_exists($class . '_' . $method)) {
943
                    call_user_func_array($dirname . '_' . $class . '_' . $method, [&$this]);
944
                }
945
            }
946
        }
947
    }
948
949
    /**
950
     * create a clone(copy) of the current object
951
     *
952
     * @access public
953
     * @return object clone
954
     */
955
    public function xoopsClone()
956
    {
957
        $class = get_class($this);
958
        $clone = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $clone is dead and can be removed.
Loading history...
959
        $clone = new $class();
960
        foreach ($this->vars as $k => $v) {
961
            $clone->assignVar($k, $v['value']);
962
        }
963
        // need this to notify the handler class that this is a newly created object
964
        $clone->setNew();
965
966
        return $clone;
967
    }
968
969
    /**
970
     * Adjust a newly cloned object
971
     */
972
    public function __clone()
973
    {
974
        // need this to notify the handler class that this is a newly created object
975
        $this->setNew();
976
    }
977
978
    /**
979
     * add an error
980
     *
981
     * @param array|string $err_str
982
     * @internal param string $value error to add
983
     * @access   public
984
     */
985
    public function setErrors($err_str)
986
    {
987
        if (is_array($err_str)) {
988
            $this->_errors = array_merge($this->_errors, $err_str);
989
        } else {
990
            $this->_errors[] = trim($err_str);
991
        }
992
    }
993
994
    /**
995
     * return the errors for this object as an array
996
     *
997
     * @return array an array of errors
998
     * @access public
999
     */
1000
    public function getErrors()
1001
    {
1002
        return $this->_errors;
1003
    }
1004
1005
    /**
1006
     * return the errors for this object as html
1007
     *
1008
     * @return string html listing the errors
1009
     * @access public
1010
     */
1011
    public function getHtmlErrors()
1012
    {
1013
        $ret = '<h4>Errors</h4>';
1014
        if (!empty($this->_errors)) {
1015
            foreach ($this->_errors as $error) {
1016
                $ret .= $error . '<br>';
1017
            }
1018
        } else {
1019
            $ret .= 'None<br>';
1020
        }
1021
1022
        return $ret;
1023
    }
1024
1025
    /**
1026
     * Returns an array representation of the object
1027
     *
1028
     * Deprecated, use getValues() directly
1029
     *
1030
     * @return array
1031
     */
1032
    public function toArray()
1033
    {
1034
        return $this->getValues();
1035
    }
1036
}
1037
1038
/**
1039
 * XOOPS object handler class.
1040
 * This class is an abstract class of handler classes that are responsible for providing
1041
 * data access mechanisms to the data source of its corresponding data objects
1042
 *
1043
 * @package             kernel
1044
 * @abstract
1045
 * @author              Kazumi Ono <[email protected]>
1046
 * @copyright       (c) 2000-2025 XOOPS Project (https://xoops.org)
1047
 */
1048
class XoopsObjectHandler
1049
{
1050
    /**
1051
     * XoopsDatabase holds referenced to {@link XoopsDatabase} class object
1052
     *
1053
     * @var XoopsDatabase
1054
     */
1055
    public $db;
1056
1057
    /**
1058
     * called from child classes only
1059
     *
1060
     * @param XoopsDatabase $db reference to the {@link XoopsDatabase} object
1061
     * @access protected
1062
     */
1063
    public function __construct(XoopsDatabase $db)
1064
    {
1065
        /** @var XoopsMySQLDatabase $db */
1066
        $this->db = $db;
1067
    }
1068
1069
    /**
1070
     * PHP 4 style constructor compatibility shim
1071
     *
1072
     * @param XoopsDatabase $db database object
1073
     * @deprecated all callers should be using parent::__construct()
1074
     */
1075
    public function XoopsObjectHandler($db)
1076
    {
1077
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
1078
        trigger_error("Should call parent::__construct in {$trace[0]['file']} line {$trace[0]['line']},", E_USER_DEPRECATED);
1079
        self::__construct($db);
0 ignored issues
show
Bug Best Practice introduced by
The method XoopsObjectHandler::__construct() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1079
        self::/** @scrutinizer ignore-call */ 
1080
              __construct($db);
Loading history...
1080
    }
1081
1082
    /**
1083
     * creates a new object
1084
     *
1085
     * @abstract
1086
     * @return XoopsObject
1087
     */
1088
    public function create() {}
1089
1090
    /**
1091
     * gets a value object
1092
     *
1093
     * @param int $id
1094
     * @abstract
1095
     * @return XoopsObject
1096
     */
1097
    public function get($id) {}
1098
1099
    /**
1100
     * insert/update object
1101
     *
1102
     * @param XoopsObject $object
1103
     * @abstract
1104
     */
1105
    public function insert(XoopsObject $object) {}
1106
1107
    /**
1108
     * delete object from database
1109
     *
1110
     * @param XoopsObject $object
1111
     * @abstract
1112
     */
1113
    public function delete(XoopsObject $object) {}
1114
}
1115
1116
/**
1117
 * Persistable Object Handler class.
1118
 *
1119
 * @author              Taiwen Jiang <[email protected]>
1120
 * @author              Jan Keller Pedersen <[email protected]>
1121
 * @copyright       (c) 2000-2025 XOOPS Project (https://xoops.org)
1122
 * @package             Kernel
1123
 */
1124
class XoopsPersistableObjectHandler extends XoopsObjectHandler
1125
{
1126
    //PHP 8.2 Dynamic properties deprecated
1127
    public $table_link;
1128
1129
    /**
1130
     * holds reference to custom extended object handler
1131
     *
1132
     * var object
1133
     *
1134
     * @access private
1135
     */
1136
    /**
1137
     * static protected
1138
     */
1139
    public $handler;
1140
1141
    /**
1142
     * holds reference to predefined extended object handlers: read, stats, joint, write, sync
1143
     *
1144
     * The handlers hold methods for different purposes, which could be all put together inside the current class.
1145
     * However, load codes only if they are necessary, thus they are now split out.
1146
     *
1147
     * var array of objects
1148
     *
1149
     * @access private
1150
     */
1151
    /**
1152
     * static protected
1153
     */
1154
    public $handlers = ['read' => null, 'stats' => null, 'joint' => null, 'write' => null, 'sync' => null];
1155
1156
    /**
1157
     * Information about the class, the handler is managing
1158
     *
1159
     * @var string
1160
     */
1161
    public $table;
1162
1163
    /**
1164
     * @var string
1165
     */
1166
    public $keyName;
1167
1168
    /**
1169
     * @var string
1170
     */
1171
    public $className;
1172
1173
    /**
1174
     * @var string
1175
     */
1176
    public $identifierName;
1177
1178
    /**
1179
     * @var string
1180
     */
1181
    public $field_link;
1182
1183
    /**
1184
     * @var string
1185
     */
1186
    public $field_object;
1187
1188
    /**
1189
     * Constructor
1190
     *
1191
     * @param null|XoopsDatabase $db             database connection
1192
     * @param string             $table          Name of database table
1193
     * @param string             $className      Name of the XoopsObject class this handler manages
1194
     * @param string             $keyName        Name of the property holding the key
1195
     * @param string             $identifierName Name of the property holding an identifier
1196
     *                                            name (title, name ...), used on getList()
1197
     */
1198
    public function __construct(?\XoopsDatabase $db = null, $table = '', $className = '', $keyName = '', $identifierName = '')
1199
    {
1200
        $db    = XoopsDatabaseFactory::getDatabaseConnection();
1201
        $table = $db->prefix($table);
1202
        parent::__construct($db);
1203
        $this->table     = $table;
1204
        $this->keyName   = $keyName;
1205
        $this->className = $className;
1206
        if ($identifierName) {
1207
            $this->identifierName = $identifierName;
1208
        }
1209
    }
1210
1211
    /**
1212
     * PHP 4 style constructor compatibility shim
1213
     *
1214
     * @param null|XoopsDatabase $db             database connection
1215
     * @param string             $table          Name of database table
1216
     * @param string             $className      Name of the XoopsObject class this handler manages
1217
     * @param string             $keyName        Name of the property holding the key
1218
     * @param string             $identifierName Name of the property holding an identifier
1219
     *                                            name (title, name ...), used on getList()
1220
     *
1221
     * @deprecated all callers should be using parent::__construct()
1222
     */
1223
    public function XoopsPersistableObjectHandler(?\XoopsDatabase $db = null, $table = '', $className = '', $keyName = '', $identifierName = '')
1224
    {
1225
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
1226
        trigger_error("Should call parent::__construct in {$trace[0]['file']} line {$trace[0]['line']},", E_USER_DEPRECATED);
1227
        self::__construct($db, $table, $className, $keyName, $identifierName);
0 ignored issues
show
Bug Best Practice introduced by
The method XoopsPersistableObjectHandler::__construct() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1227
        self::/** @scrutinizer ignore-call */ 
1228
              __construct($db, $table, $className, $keyName, $identifierName);
Loading history...
1228
    }
1229
1230
    /**
1231
     * Set custom handler
1232
     *
1233
     * @access   protected
1234
     * @param null|string   $handler
1235
     * @param null   $args
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $args is correct as it would always require null to be passed?
Loading history...
1236
     * @param string $path path to class
1237
     * @internal param object $handler
1238
     * @internal param mixed  $args
1239
     * @return object of handler
1240
     */
1241
    public function setHandler($handler = null, $args = null, $path = null)
1242
    {
1243
        $this->handler = null;
1244
        if (is_object($handler)) {
1245
            $this->handler = $handler;
1246
        } elseif (is_string($handler)) {
1247
            xoops_load('XoopsModelFactory');
1248
            $this->handler = XoopsModelFactory::loadHandler($this, $handler, $args);
1249
        }
1250
1251
        return $this->handler;
1252
    }
1253
1254
    /**
1255
     * Load predefined handler
1256
     *
1257
     * @access protected
1258
     * @param  string $name handler name
1259
     * @param  mixed  $args args
1260
     * @return XoopsModelAbstract of handler {@link XoopsModelAbstract}
1261
     */
1262
    public function loadHandler($name, $args = null)
1263
    {
1264
        static $handlers;
1265
        if (!isset($handlers[$name])) {
1266
            xoops_load('XoopsModelFactory');
1267
            $handlers[$name] = XoopsModelFactory::loadHandler($this, $name, $args);
1268
        } else {
1269
            $handlers[$name]->setHandler($this);
1270
            $handlers[$name]->setVars($args);
1271
        }
1272
1273
        return $handlers[$name];
1274
1275
        /**
1276
         * // Following code just kept as placeholder for PHP5
1277
         * if (!isset(self::$handlers[$name])) {
1278
         * self::$handlers[$name] = XoopsModelFactory::loadHandler($this, $name, $args);
1279
         * } else {
1280
         * self::$handlers[$name]->setHandler($this);
1281
         * self::$handlers[$name]->setVars($args);
1282
         * }
1283
         *
1284
         * return self::$handlers[$name];
1285
         */
1286
    }
1287
1288
    /**
1289
     * Magic method for overloading of delegation
1290
     *
1291
     * To be enabled in XOOPS 3.0 with PHP 5
1292
     *
1293
     * @access protected
1294
     * @param  string $name method name
1295
     * @param  array  $args arguments
1296
     * @return mixed
1297
     */
1298
    public function __call($name, $args)
1299
    {
1300
        if (is_object($this->handler) && is_callable([$this->handler, $name])) {
1301
            return call_user_func_array([$this->handler, $name], $args);
1302
        }
1303
        foreach (array_keys($this->handlers) as $_handler) {
1304
            $handler = $this->loadHandler($_handler);
1305
            if (is_callable([$handler, $name])) {
1306
                return call_user_func_array([$handler, $name], $args);
1307
            }
1308
        }
1309
1310
        return null;
1311
    }
1312
1313
    /**
1314
     * *#@+
1315
     * Methods of native handler {@link XoopsPersistableObjectHandler}
1316
     */
1317
    /**
1318
     * create a new object
1319
     *
1320
     * @param  bool $isNew Flag the new objects as new
1321
     * @return XoopsObject {@link XoopsObject}
1322
     */
1323
    public function create($isNew = true)
1324
    {
1325
        $obj = new $this->className();
1326
        if ($isNew === true) {
1327
            $obj->setNew();
1328
        }
1329
1330
        return $obj;
1331
    }
1332
1333
    /**
1334
     * Load a {@link XoopsObject} object from the database
1335
     *
1336
     * @access protected
1337
     * @param  mixed $id     ID
1338
     * @param array|null $fields fields to fetch
1339
     * @return XoopsObject|null {@link XoopsObject}
1340
     */
1341
    public function get($id = null, $fields = null)
1342
    {
1343
        $object = null;
1344
        if (empty($id)) {
1345
            $object = $this->create();
1346
1347
            return $object;
1348
        }
1349
        if (!empty($fields) && \is_array($fields)) {
1350
            $select = implode(',', $fields);
1351
            if (!in_array($this->keyName, $fields)) {
1352
                $select .= ', ' . $this->keyName;
1353
            }
1354
        } else {
1355
            $select = '*';
1356
        }
1357
        $sql = sprintf('SELECT %s FROM %s WHERE %s = %s', $select, $this->table, $this->keyName, $this->db->quote($id));
0 ignored issues
show
Bug introduced by
The method quote() does not exist on XoopsDatabase. Since it exists in all sub-types, consider adding an abstract or default implementation to XoopsDatabase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1357
        $sql = sprintf('SELECT %s FROM %s WHERE %s = %s', $select, $this->table, $this->keyName, $this->db->/** @scrutinizer ignore-call */ quote($id));
Loading history...
1358
        //$sql = "SELECT {$select} FROM {$this->table} WHERE {$this->keyName} = " . $this->db->quote($id);
1359
        $result = $this->db->query($sql);
0 ignored issues
show
Bug introduced by
The method query() does not exist on XoopsDatabase. Since it exists in all sub-types, consider adding an abstract or default implementation to XoopsDatabase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1359
        /** @scrutinizer ignore-call */ 
1360
        $result = $this->db->query($sql);
Loading history...
1360
        if (!$this->db->isResultSet($result)) {
1361
            return $object;
1362
        }
1363
        if (!$this->db->getRowsNum($result)) {
0 ignored issues
show
Bug introduced by
The method getRowsNum() does not exist on XoopsDatabase. Since it exists in all sub-types, consider adding an abstract or default implementation to XoopsDatabase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1363
        if (!$this->db->/** @scrutinizer ignore-call */ getRowsNum($result)) {
Loading history...
1364
            return $object;
1365
        }
1366
        $object = $this->create(false);
1367
        $object->assignVars($this->db->fetchArray($result));
0 ignored issues
show
Bug introduced by
The method fetchArray() does not exist on XoopsDatabase. Since it exists in all sub-types, consider adding an abstract or default implementation to XoopsDatabase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1367
        $object->assignVars($this->db->/** @scrutinizer ignore-call */ fetchArray($result));
Loading history...
1368
1369
        return $object;
1370
    }
1371
    /**
1372
     * *#@-
1373
     */
1374
1375
    /**
1376
     * *#@+
1377
     * Methods of write handler {@link XoopsObjectWrite}
1378
     */
1379
    /**
1380
     * insert an object into the database
1381
     *
1382
     * @param  XoopsObject $object {@link XoopsObject} reference to object
1383
     * @param  bool        $force  flag to force the query execution despite security settings
1384
     * @return mixed       object ID
1385
     */
1386
    public function insert(XoopsObject $object, $force = true)
1387
    {
1388
        $handler = $this->loadHandler('write');
1389
1390
        return $handler->insert($object, $force);
0 ignored issues
show
Bug introduced by
The method insert() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelWrite. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1390
        return $handler->/** @scrutinizer ignore-call */ insert($object, $force);
Loading history...
1391
    }
1392
1393
    /**
1394
     * delete an object from the database
1395
     *
1396
     * @param  XoopsObject $object {@link XoopsObject} reference to the object to delete
1397
     * @param  bool        $force
1398
     * @return bool        FALSE if failed.
1399
     */
1400
    public function delete(XoopsObject $object, $force = false)
1401
    {
1402
        $handler = $this->loadHandler('write');
1403
1404
        return $handler->delete($object, $force);
0 ignored issues
show
Bug introduced by
The method delete() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelWrite. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1404
        return $handler->/** @scrutinizer ignore-call */ delete($object, $force);
Loading history...
1405
    }
1406
1407
    /**
1408
     * delete all objects matching the conditions
1409
     *
1410
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} with conditions to meet
1411
     * @param  bool            $force    force to delete
1412
     * @param  bool            $asObject delete in object way: instantiate all objects and delete one by one
1413
     * @return bool|int
1414
     */
1415
    public function deleteAll(?CriteriaElement $criteria = null, $force = true, $asObject = false)
1416
    {
1417
        $handler = $this->loadHandler('write');
1418
1419
        return $handler->deleteAll($criteria, $force, $asObject);
0 ignored issues
show
Bug introduced by
The method deleteAll() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelWrite. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1419
        return $handler->/** @scrutinizer ignore-call */ deleteAll($criteria, $force, $asObject);
Loading history...
1420
    }
1421
1422
    /**
1423
     * Change a field for objects with a certain criteria
1424
     *
1425
     * @param  string          $fieldname  Name of the field
1426
     * @param  mixed           $fieldvalue Value to write
1427
     * @param \CriteriaElement|null $criteria   {@link CriteriaElement}
1428
     * @param  bool            $force      force to query
1429
     * @return bool
1430
     */
1431
    public function updateAll($fieldname, $fieldvalue, ?CriteriaElement $criteria = null, $force = false)
1432
    {
1433
        $handler = $this->loadHandler('write');
1434
1435
        return $handler->updateAll($fieldname, $fieldvalue, $criteria, $force);
0 ignored issues
show
Bug introduced by
The method updateAll() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelWrite. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1435
        return $handler->/** @scrutinizer ignore-call */ updateAll($fieldname, $fieldvalue, $criteria, $force);
Loading history...
1436
    }
1437
    /**
1438
     * *#@-
1439
     */
1440
1441
    /**
1442
     * *#@+
1443
     * Methods of read handler {@link XoopsObjectRead}
1444
     */
1445
    /**
1446
     * Retrieve objects from the database
1447
     *
1448
     * @param \CriteriaElement|null $criteria  {@link CriteriaElement} conditions to be met
1449
     * @param  bool            $id_as_key use the ID as key for the array
1450
     * @param  bool            $as_object return an array of objects
1451
     * @return array
1452
     */
1453
    public function &getObjects(?CriteriaElement $criteria = null, $id_as_key = false, $as_object = true)
1454
    {
1455
        $handler = $this->loadHandler('read');
1456
        $ret     = $handler->getObjects($criteria, $id_as_key, $as_object);
0 ignored issues
show
Bug introduced by
The method getObjects() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelRead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1456
        /** @scrutinizer ignore-call */ 
1457
        $ret     = $handler->getObjects($criteria, $id_as_key, $as_object);
Loading history...
1457
1458
        return $ret;
1459
    }
1460
1461
    /**
1462
     * get all objects matching a condition
1463
     *
1464
     * @param \CriteriaElement|null $criteria  {@link CriteriaElement} to match
1465
     * @param array|null            $fields    variables to fetch
1466
     * @param  bool            $asObject  flag indicating as object, otherwise as array
1467
     * @param  bool            $id_as_key use the ID as key for the array
1468
     * @return array           of objects/array {@link XoopsObject}
1469
     */
1470
    public function &getAll(?CriteriaElement $criteria = null, $fields = null, $asObject = true, $id_as_key = true)
1471
    {
1472
        $handler = $this->loadHandler('read');
1473
        $ret     = $handler->getAll($criteria, $fields, $asObject, $id_as_key);
0 ignored issues
show
Bug introduced by
The method getAll() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelRead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1473
        /** @scrutinizer ignore-call */ 
1474
        $ret     = $handler->getAll($criteria, $fields, $asObject, $id_as_key);
Loading history...
1474
1475
        return $ret;
1476
    }
1477
1478
    /**
1479
     * Retrieve a list of objects data
1480
     *
1481
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} conditions to be met
1482
     * @param  int             $limit    Max number of objects to fetch
1483
     * @param  int             $start    Which record to start at
1484
     * @return array
1485
     */
1486
    public function getList(?CriteriaElement $criteria = null, $limit = 0, $start = 0)
1487
    {
1488
        $handler = $this->loadHandler('read');
1489
        $ret     = $handler->getList($criteria, $limit, $start);
0 ignored issues
show
Bug introduced by
The method getList() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelRead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1489
        /** @scrutinizer ignore-call */ 
1490
        $ret     = $handler->getList($criteria, $limit, $start);
Loading history...
1490
1491
        return $ret;
1492
    }
1493
1494
    /**
1495
     * get IDs of objects matching a condition
1496
     *
1497
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} to match
1498
     * @return array           of object IDs
1499
     */
1500
    public function &getIds(?CriteriaElement $criteria = null)
1501
    {
1502
        $handler = $this->loadHandler('read');
1503
        $ret     = $handler->getIds($criteria);
0 ignored issues
show
Bug introduced by
The method getIds() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelRead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1503
        /** @scrutinizer ignore-call */ 
1504
        $ret     = $handler->getIds($criteria);
Loading history...
1504
1505
        return $ret;
1506
    }
1507
1508
    /**
1509
     * get a limited list of objects matching a condition
1510
     *
1511
     * {@link CriteriaCompo}
1512
     *
1513
     * @param  int             $limit    Max number of objects to fetch
1514
     * @param  int             $start    Which record to start at
1515
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} to match
1516
     * @param array|null            $fields   variables to fetch
1517
     * @param  bool            $asObject flag indicating as object, otherwise as array
1518
     * @return array           of objects     {@link XoopsObject}
1519
     */
1520
    public function &getByLimit($limit = 0, $start = 0, ?CriteriaElement $criteria = null, $fields = null, $asObject = true)
1521
    {
1522
        $handler = $this->loadHandler('read');
1523
        $ret     = $handler->getByLimit($limit, $start, $criteria, $fields, $asObject);
0 ignored issues
show
Bug introduced by
The method getByLimit() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelRead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1523
        /** @scrutinizer ignore-call */ 
1524
        $ret     = $handler->getByLimit($limit, $start, $criteria, $fields, $asObject);
Loading history...
1524
1525
        return $ret;
1526
    }
1527
    /**
1528
     * *#@-
1529
     */
1530
1531
    /**
1532
     * *#@+
1533
     * Methods of stats handler {@link XoopsObjectStats}
1534
     */
1535
    /**
1536
     * count objects matching a condition
1537
     *
1538
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} to match
1539
     * @return int             count of objects
1540
     */
1541
    public function getCount(?CriteriaElement $criteria = null)
1542
    {
1543
        $handler = $this->loadHandler('stats');
1544
1545
        return $handler->getCount($criteria);
0 ignored issues
show
Bug introduced by
The method getCount() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelStats. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1545
        return $handler->/** @scrutinizer ignore-call */ getCount($criteria);
Loading history...
1546
    }
1547
1548
    /**
1549
     * Get counts of objects matching a condition
1550
     *
1551
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} to match
1552
     * @return array           of counts
1553
     */
1554
    public function getCounts(?CriteriaElement $criteria = null)
1555
    {
1556
        $handler = $this->loadHandler('stats');
1557
1558
        return $handler->getCounts($criteria);
0 ignored issues
show
Bug introduced by
The method getCounts() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelStats. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1558
        return $handler->/** @scrutinizer ignore-call */ getCounts($criteria);
Loading history...
1559
    }
1560
    /**
1561
     * *#@-
1562
     */
1563
1564
    /**
1565
     * *#@+
1566
     * Methods of joint handler {@link XoopsObjectJoint}
1567
     */
1568
    /**
1569
     * get a list of objects matching a condition joint with another related object
1570
     *
1571
     * @param \CriteriaElement|null $criteria     {@link CriteriaElement} to match
1572
     * @param array|null            $fields       variables to fetch
1573
     * @param bool                  $asObject     flag indicating as object, otherwise as array
1574
     * @param string|null           $field_link   field of linked object for JOIN
1575
     * @param string|null           $field_object field of current object for JOIN
1576
     * @return array           of objects {@link XoopsObject}
1577
     */
1578
    public function getByLink(?CriteriaElement $criteria = null, $fields = null, $asObject = true, $field_link = null, $field_object = null)
1579
    {
1580
        $handler = $this->loadHandler('joint');
1581
        $ret     = $handler->getByLink($criteria, $fields, $asObject, $field_link, $field_object);
0 ignored issues
show
Bug introduced by
The method getByLink() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelJoint. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1581
        /** @scrutinizer ignore-call */ 
1582
        $ret     = $handler->getByLink($criteria, $fields, $asObject, $field_link, $field_object);
Loading history...
1582
1583
        return $ret;
1584
    }
1585
1586
    /**
1587
     * Count of objects matching a condition
1588
     *
1589
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} to match
1590
     * @return int             count of objects
1591
     */
1592
    public function getCountByLink(?CriteriaElement $criteria = null)
1593
    {
1594
        $handler = $this->loadHandler('joint');
1595
        $ret     = $handler->getCountByLink($criteria);
0 ignored issues
show
Bug introduced by
The method getCountByLink() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelJoint. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1595
        /** @scrutinizer ignore-call */ 
1596
        $ret     = $handler->getCountByLink($criteria);
Loading history...
1596
1597
        return $ret;
1598
    }
1599
1600
    /**
1601
     * array of count of objects matching a condition of, groupby linked object keyname
1602
     *
1603
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} to match
1604
     * @return int             count of objects
1605
     */
1606
    public function getCountsByLink(?CriteriaElement $criteria = null)
1607
    {
1608
        $handler = $this->loadHandler('joint');
1609
        $ret     = $handler->getCountsByLink($criteria);
0 ignored issues
show
Bug introduced by
The method getCountsByLink() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelJoint. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1609
        /** @scrutinizer ignore-call */ 
1610
        $ret     = $handler->getCountsByLink($criteria);
Loading history...
1610
1611
        return $ret;
1612
    }
1613
1614
    /**
1615
     * update objects matching a condition against linked objects
1616
     *
1617
     * @param  array           $data     array of key => value
1618
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} to match
1619
     * @return int             count of objects
1620
     */
1621
    public function updateByLink($data, ?CriteriaElement $criteria = null)
1622
    {
1623
        $handler = $this->loadHandler('joint');
1624
        $ret     = $handler->updateByLink($data, $criteria);
0 ignored issues
show
Bug introduced by
The method updateByLink() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelJoint. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1624
        /** @scrutinizer ignore-call */ 
1625
        $ret     = $handler->updateByLink($data, $criteria);
Loading history...
1625
1626
        return $ret;
1627
    }
1628
1629
    /**
1630
     * Delete objects matching a condition against linked objects
1631
     *
1632
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} to match
1633
     * @return int|null             count of objects
1634
     */
1635
    public function deleteByLink(?CriteriaElement $criteria = null)
1636
    {
1637
        $handler = $this->loadHandler('joint');
1638
        $ret     = $handler->deleteByLink($criteria);
0 ignored issues
show
Bug introduced by
The method deleteByLink() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelJoint. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1638
        /** @scrutinizer ignore-call */ 
1639
        $ret     = $handler->deleteByLink($criteria);
Loading history...
1639
1640
        return $ret;
1641
    }
1642
    /**
1643
     * *#@-
1644
     */
1645
1646
    /**
1647
     * *#@+
1648
     * Methods of sync handler {@link XoopsObjectSync}
1649
     */
1650
    /**
1651
     * Clean orphan objects against linked objects
1652
     *
1653
     * @param  string $table_link   table of linked object for JOIN
1654
     * @param  string $field_link   field of linked object for JOIN
1655
     * @param  string $field_object field of current object for JOIN
1656
     * @return bool   true on success
1657
     */
1658
    public function cleanOrphan($table_link = '', $field_link = '', $field_object = '')
1659
    {
1660
        $handler = $this->loadHandler('sync');
1661
        $ret     = $handler->cleanOrphan($table_link, $field_link, $field_object);
0 ignored issues
show
Bug introduced by
The method cleanOrphan() does not exist on XoopsModelAbstract. It seems like you code against a sub-type of XoopsModelAbstract such as XoopsModelSync. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1661
        /** @scrutinizer ignore-call */ 
1662
        $ret     = $handler->cleanOrphan($table_link, $field_link, $field_object);
Loading history...
1662
1663
        return $ret;
1664
    }
1665
1666
    /**
1667
     * Synchronizing objects
1668
     *
1669
     * @return bool true on success
1670
     */
1671
    public function synchronization()
1672
    {
1673
        $retval = $this->cleanOrphan();
1674
1675
        return $retval;
1676
    }
1677
    /**
1678
     * *#@-
1679
     */
1680
1681
    /**#@+
1682
     * @deprecated
1683
     * @param      $result
1684
     * @param bool $id_as_key
1685
     * @param bool $as_object
1686
     * @return bool
1687
     */
1688
    public function convertResultSet($result, $id_as_key = false, $as_object = true)
1689
    {
1690
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1691
1692
        return false;
1693
    }
1694
    /**#@-*/
1695
}
1696