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-2016 XOOPS Project (www.xoops.org)
13 * @license             GNU GPL 2 (http://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 <phppp@users.sourceforge.net>
18 */
19
20defined('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 */
29define('XOBJ_DTYPE_TXTBOX', 1);
30define('XOBJ_DTYPE_TXTAREA', 2);
31define('XOBJ_DTYPE_INT', 3);
32define('XOBJ_DTYPE_URL', 4);
33define('XOBJ_DTYPE_EMAIL', 5);
34define('XOBJ_DTYPE_ARRAY', 6);
35define('XOBJ_DTYPE_OTHER', 7);
36define('XOBJ_DTYPE_SOURCE', 8);
37define('XOBJ_DTYPE_STIME', 9);
38define('XOBJ_DTYPE_MTIME', 10);
39define('XOBJ_DTYPE_LTIME', 11);
40define('XOBJ_DTYPE_FLOAT', 13);
41define('XOBJ_DTYPE_DECIMAL', 14);
42define('XOBJ_DTYPE_ENUM', 15);
43// YOU SHOULD NEVER USE THE FOLLOWING TYPES, THEY WILL BE REMOVED
44define('XOBJ_DTYPE_UNICODE_TXTBOX', 16);
45define('XOBJ_DTYPE_UNICODE_TXTAREA', 17);
46define('XOBJ_DTYPE_UNICODE_URL', 18);
47define('XOBJ_DTYPE_UNICODE_EMAIL', 19);
48define('XOBJ_DTYPE_UNICODE_ARRAY', 20);
49define('XOBJ_DTYPE_UNICODE_OTHER', 21);
50// Addition for 2.5.5
51define('XOBJ_DTYPE_DATE', 22);
52define('XOBJ_DTYPE_TIME', 23);
53define('XOBJ_DTYPE_TIMESTAMP', 24);
54
55/**
56 * Base class for all objects in the Xoops kernel (and beyond)
57 */
58class XoopsObject
59{
60    /**
61     * holds all variables(properties) of an object
62     *
63     * @var array
64     * @access protected
65     */
66    public $vars = array();
67
68    /**
69     * variables cleaned for store in DB
70     *
71     * @var array
72     * @access protected
73     */
74    public $cleanVars = array();
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 = array();
99
100    /**
101     * additional filters registered dynamically by a child class object
102     *
103     * @access private
104     */
105    public $_filters = array();
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
118    /**
119     * PHP 4 style constructor compatibility shim
120     * @deprecated all callers should be using parent::__construct()
121     */
122    public function XoopsObject()
123    {
124        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
125        trigger_error("Should call parent::__construct in {$trace[0]['file']} line {$trace[0]['line']},");
126        self::__construct();
127    }
128
129    /**
130     * *#@+
131     * used for new/clone objects
132     *
133     * @access public
134     */
135    public function setNew()
136    {
137        $this->_isNew = true;
138    }
139
140    public function unsetNew()
141    {
142        $this->_isNew = false;
143    }
144
145    /**
146     * @return bool
147     */
148    public function isNew()
149    {
150        return $this->_isNew;
151    }
152
153    /**
154     * *#@+
155     * mark modified objects as dirty
156     *
157     * used for modified objects only
158     *
159     * @access public
160     */
161    public function setDirty()
162    {
163        $this->_isDirty = true;
164    }
165
166    public function unsetDirty()
167    {
168        $this->_isDirty = false;
169    }
170
171    /**
172     * @return bool
173     */
174    public function isDirty()
175    {
176        return $this->_isDirty;
177    }
178
179    /**
180     * initialize variables for the object
181     *
182     * YOU SHOULD NOT USE THE $enumeration PARAMETER
183     *
184     * @access   public
185     *
186     * @param string $key
187     * @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)
188     * @param null   $value
189     * @param bool   $required  require html form input?
190     * @param int    $maxlength for XOBJ_DTYPE_TXTBOX type only
191     * @param string $options
192     * @param string $enumerations
193     *
194     * @return void
195     */
196    public function initVar($key, $data_type, $value = null, $required = false, $maxlength = null, $options = '', $enumerations = '')
197    {
198        $this->vars[$key] = array(
199            'value'       => $value,
200            'required'    => $required,
201            'data_type'   => $data_type,
202            'maxlength'   => $maxlength,
203            'changed'     => false,
204            'options'     => $options,
205            'enumeration' => $enumerations);
206    }
207
208    /**
209     * assign a value to a variable
210     *
211     * @access public
212     * @param string $key   name of the variable to assign
213     * @param mixed  $value value to assign
214     */
215    public function assignVar($key, $value)
216    {
217        if (isset($key) && isset($this->vars[$key])) {
218            switch ($this->vars[$key]['data_type']) {
219                case XOBJ_DTYPE_UNICODE_ARRAY:
220                    if (is_array($value)) {
221                        $this->vars[$key]['value'] =& array_walk($value, 'xoops_aw_decode');
222                    } else {
223                        $this->vars[$key]['value'] =& xoops_convert_decode($value);
224                    }
225                    break;
226                case XOBJ_DTYPE_UNICODE_URL:
227                case XOBJ_DTYPE_UNICODE_EMAIL:
228                case XOBJ_DTYPE_UNICODE_OTHER:
229                case XOBJ_DTYPE_UNICODE_TXTBOX:
230                case XOBJ_DTYPE_UNICODE_TXTAREA:
231                    $this->vars[$key]['value'] = xoops_convert_decode($value);
232                    break;
233                case XOBJ_DTYPE_DATE:
234                    if (!is_string($value) && is_numeric($value)) {
235                        $this->vars[$key]['value'] = date(_DBDATESTRING, $value);
236                    } else {
237                        $this->vars[$key]['value'] = date(_DBDATESTRING, strtotime($value));
238                    }
239                    break;
240                case XOBJ_DTYPE_TIME:
241                    if (!is_string($value) && is_numeric($value)) {
242                        $this->vars[$key]['value'] = date(_DBTIMESTRING, $value);
243                    } else {
244                        $this->vars[$key]['value'] = date(_DBTIMESTRING, strtotime($value));
245                    }
246                    break;
247                case XOBJ_DTYPE_TIMESTAMP:
248                    if (!is_string($value) && is_numeric($value)) {
249                        $this->vars[$key]['value'] = date(_DBTIMESTAMPSTRING, $value);
250                    } else {
251                        $this->vars[$key]['value'] = date(_DBTIMESTAMPSTRING, strtotime($value));
252                    }
253                    break;
254                // YOU SHOULD NOT USE THE ABOVE TYPES, THEY WILL BE REMOVED
255                default:
256                    $this->vars[$key]['value'] =& $value;
257            }
258        }
259    }
260
261    /**
262     * assign values to multiple variables in a batch
263     *
264     * @access   private
265     * @param $var_arr
266     * @internal param array $var_array associative array of values to assign
267     */
268    public function assignVars($var_arr)
269    {
270        foreach ($var_arr as $key => $value) {
271            $this->assignVar($key, $value);
272        }
273    }
274
275    /**
276     * assign a value to a variable
277     *
278     * @access public
279     * @param string $key   name of the variable to assign
280     * @param mixed  $value value to assign
281     * @param bool   $not_gpc
282     */
283    public function setVar($key, $value, $not_gpc = false)
284    {
285        if (!empty($key) && isset($value) && isset($this->vars[$key])) {
286            $this->vars[$key]['value']   =& $value;
287            $this->vars[$key]['not_gpc'] = $not_gpc;
288            $this->vars[$key]['changed'] = true;
289            $this->setDirty();
290        }
291    }
292
293    /**
294     * assign values to multiple variables in a batch
295     *
296     * @access private
297     * @param array $var_arr associative array of values to assign
298     * @param bool  $not_gpc
299     */
300    public function setVars($var_arr, $not_gpc = false)
301    {
302        foreach ($var_arr as $key => $value) {
303            $this->setVar($key, $value, $not_gpc);
304        }
305    }
306
307    /**
308     * unset variable(s) for the object
309     *
310     * @access public
311     *
312     * @param mixed $var
313     *
314     * @return bool
315     */
316    public function destroyVars($var)
317    {
318        if (empty($var)) {
319            return true;
320        }
321        $var = !is_array($var) ? array($var) : $var;
322        foreach ($var as $key) {
323            if (!isset($this->vars[$key])) {
324                continue;
325            }
326            $this->vars[$key]['changed'] = null;
327        }
328
329        return true;
330    }
331
332    /**
333     * @param $var
334     * @return bool
335     * @deprecated use destroyVars() instead,  destoryVars() will be removed in the next major release
336     */
337    public function destoryVars($var)
338    {
339        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
340        trigger_error("XoopsObject::destoryVars() is deprecated, called from {$trace[0]['file']} line {$trace[0]['line']}");
341        return $this->destroyVars($var);
342    }
343
344    /**
345     * Assign values to multiple variables in a batch
346     *
347     * Meant for a CGI context:
348     * - prefixed CGI args are considered save
349     * - avoids polluting of namespace with CGI args
350     *
351     * @access private
352     * @param array  $var_arr associative array of values to assign
353     * @param string $pref    prefix (only keys starting with the prefix will be set)
354     * @param bool   $not_gpc
355     */
356    public function setFormVars($var_arr = null, $pref = 'xo_', $not_gpc = false)
357    {
358        $len = strlen($pref);
359        foreach ($var_arr as $key => $value) {
360            if ($pref == substr($key, 0, $len)) {
361                $this->setVar(substr($key, $len), $value, $not_gpc);
362            }
363        }
364    }
365
366    /**
367     * returns all variables for the object
368     *
369     * @access public
370     * @return array associative array of key->value pairs
371     */
372    public function &getVars()
373    {
374        return $this->vars;
375    }
376
377    /**
378     * Returns the values of the specified variables
379     *
380     * @param  mixed  $keys     An array containing the names of the keys to retrieve, or null to get all of them
381     * @param  string $format   Format to use (see getVar)
382     * @param  int    $maxDepth Maximum level of recursion to use if some vars are objects themselves
383     * @return array  associative array of key->value pairs
384     */
385    public function getValues($keys = null, $format = 's', $maxDepth = 1)
386    {
387        if (!isset($keys)) {
388            $keys = array_keys($this->vars);
389        }
390        $vars = array();
391        foreach ($keys as $key) {
392            if (isset($this->vars[$key])) {
393                if (is_object($this->vars[$key]) && is_a($this->vars[$key], 'XoopsObject')) {
394                    if ($maxDepth) {
395                        $vars[$key] = $this->vars[$key]->getValues(null, $format, $maxDepth - 1);
396                    }
397                } else {
398                    $vars[$key] = $this->getVar($key, $format);
399                }
400            }
401        }
402
403        return $vars;
404    }
405
406    /**
407     * returns a specific variable for the object in a proper format
408     *
409     * YOU SHOULD NOT USE ANY OF THE UNICODE TYPES, THEY WILL BE REMOVED
410     *
411     * @access public
412     * @param  string $key    key of the object's variable to be returned
413     * @param  string $format format to use for the output
414     * @return mixed  formatted value of the variable
415     */
416    public function getVar($key, $format = 's')
417    {
418        $ret = null;
419        if (!isset($this->vars[$key])) {
420            return $ret;
421        }
422        $ret = $this->vars[$key]['value'];
423        $ts  = MyTextSanitizer::getInstance();
424        switch ($this->vars[$key]['data_type']) {
425            case XOBJ_DTYPE_INT:
426                $ret = (int) $ret;
427                break;
428            case XOBJ_DTYPE_UNICODE_TXTBOX:
429            case XOBJ_DTYPE_TXTBOX:
430                switch (strtolower($format)) {
431                    case 's':
432                    case 'show':
433                    case 'e':
434                    case 'edit':
435                        return $ts->htmlSpecialChars($ret);
436                        break 1;
437                    case 'p':
438                    case 'preview':
439                    case 'f':
440                    case 'formpreview':
441                        return $ts->htmlSpecialChars($ts->stripSlashesGPC($ret));
442                        break 1;
443                    case 'n':
444                    case 'none':
445                    default:
446                        break 1;
447                }
448                break;
449            case XOBJ_DTYPE_UNICODE_TXTAREA:
450            case XOBJ_DTYPE_TXTAREA:
451                switch (strtolower($format)) {
452                    case 's':
453                    case 'show':
454                        $html   = !empty($this->vars['dohtml']['value']) ? 1 : 0;
455                        $xcode  = (!isset($this->vars['doxcode']['value']) || $this->vars['doxcode']['value'] == 1) ? 1 : 0;
456                        $smiley = (!isset($this->vars['dosmiley']['value']) || $this->vars['dosmiley']['value'] == 1) ? 1 : 0;
457                        $image  = (!isset($this->vars['doimage']['value']) || $this->vars['doimage']['value'] == 1) ? 1 : 0;
458                        $br     = (!isset($this->vars['dobr']['value']) || $this->vars['dobr']['value'] == 1) ? 1 : 0;
459
460                        return $ts->displayTarea($ret, $html, $smiley, $xcode, $image, $br);
461                        break 1;
462                    case 'e':
463                    case 'edit':
464                        return htmlspecialchars($ret, ENT_QUOTES);
465                        break 1;
466                    case 'p':
467                    case 'preview':
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 $ts->previewTarea($ret, $html, $smiley, $xcode, $image, $br);
475                        break 1;
476                    case 'f':
477                    case 'formpreview':
478                        return htmlspecialchars($ts->stripSlashesGPC($ret), ENT_QUOTES);
479                        break 1;
480                    case 'n':
481                    case 'none':
482                    default:
483                        break 1;
484                }
485                break;
486            case XOBJ_DTYPE_UNICODE_ARRAY:
487                switch (strtolower($format)) {
488                    case 'n':
489                    case 'none':
490                        break 1;
491                    default:
492                        if (!is_array($ret)) {
493                            if ($ret != '') {
494                                $ret = unserialize($ret);
495                            }
496                            $ret = is_array($ret) ? $ret : array();
497                            if (is_array($ret)) {
498                                $ret = array_walk($ret, 'xoops_aw_decode');
499                            }
500                        }
501
502                        return $ret;
503                        break 1;
504                }
505                break;
506            case XOBJ_DTYPE_ARRAY:
507                switch (strtolower($format)) {
508                    case 'n':
509                    case 'none':
510                        break 1;
511                    default:
512                        if (!is_array($ret)) {
513                            if ($ret != '') {
514                                $ret = unserialize($ret);
515                            }
516                            $ret = is_array($ret) ? $ret : array();
517                        }
518
519                        return $ret;
520                        break 1;
521                }
522                break;
523            case XOBJ_DTYPE_SOURCE:
524                switch (strtolower($format)) {
525                    case 's':
526                    case 'show':
527                        break 1;
528                    case 'e':
529                    case 'edit':
530                        return htmlspecialchars($ret, ENT_QUOTES);
531                        break 1;
532                    case 'p':
533                    case 'preview':
534                        return $ts->stripSlashesGPC($ret);
535                        break 1;
536                    case 'f':
537                    case 'formpreview':
538                        return htmlspecialchars($ts->stripSlashesGPC($ret), ENT_QUOTES);
539                        break 1;
540                    case 'n':
541                    case 'none':
542                    default:
543                        break 1;
544                }
545                break;
546            case XOBJ_DTYPE_DATE:
547                switch (strtolower($format)) {
548                    case 's':
549                    case 'show':
550                        if (is_string($ret) && !is_numeric($ret)) {
551                            return date(_DBDATESTRING, strtotime($ret));
552                        } else {
553                            return date(_DBDATESTRING, $ret);
554                        }
555                        break 1;
556                    case 'e':
557                    case 'edit':
558                        if (is_string($ret) && !is_numeric($ret)) {
559                            return htmlspecialchars(date(_DBDATESTRING, strtotime($ret)), ENT_QUOTES);
560                        } else {
561                            return htmlspecialchars(date(_DBDATESTRING, $ret), ENT_QUOTES);
562                        }
563                        break 1;
564                    case 'p':
565                    case 'preview':
566                        if (is_string($ret) && !is_numeric($ret)) {
567                            return $ts->stripSlashesGPC(date(_DBDATESTRING, strtotime($ret)));
568                        } else {
569                            return $ts->stripSlashesGPC(date(_DBDATESTRING, $ret));
570                        }
571                        break 1;
572                    case 'f':
573                    case 'formpreview':
574                        if (is_string($ret) && !is_numeric($ret)) {
575                            return htmlspecialchars($ts->stripSlashesGPC(date(_DBDATESTRING, strtotime($ret))), ENT_QUOTES);
576                        } else {
577                            return htmlspecialchars($ts->stripSlashesGPC(date(_DBDATESTRING, $ret)), ENT_QUOTES);
578                        }
579                        break 1;
580                    case 'n':
581                    case 'none':
582                    default:
583                        break 1;
584                }
585                break;
586            case XOBJ_DTYPE_TIME:
587                switch (strtolower($format)) {
588                    case 's':
589                    case 'show':
590                        if (is_string($ret) && !is_numeric($ret)) {
591                            return date(_DBTIMESTRING, strtotime($ret));
592                        } else {
593                            return date(_DBTIMESTRING, $ret);
594                        }
595                        break 1;
596                    case 'e':
597                    case 'edit':
598                        if (is_string($ret) && !is_numeric($ret)) {
599                            return htmlspecialchars(date(_DBTIMESTRING, strtotime($ret)), ENT_QUOTES);
600                        } else {
601                            return htmlspecialchars(date(_DBTIMESTRING, $ret), ENT_QUOTES);
602                        }
603                        break 1;
604                    case 'p':
605                    case 'preview':
606                        if (is_string($ret) && !is_numeric($ret)) {
607                            return $ts->stripSlashesGPC(date(_DBTIMESTRING, strtotime($ret)));
608                        } else {
609                            return $ts->stripSlashesGPC(date(_DBTIMESTRING, $ret));
610                        }
611                        break 1;
612                    case 'f':
613                    case 'formpreview':
614                        if (is_string($ret) && !is_numeric($ret)) {
615                            return htmlspecialchars($ts->stripSlashesGPC(date(_DBTIMESTRING, strtotime($ret))), ENT_QUOTES);
616                        } else {
617                            return htmlspecialchars($ts->stripSlashesGPC(date(_DBTIMESTRING, $ret)), ENT_QUOTES);
618                        }
619                        break 1;
620                    case 'n':
621                    case 'none':
622                    default:
623                        break 1;
624                }
625                break;
626            case XOBJ_DTYPE_TIMESTAMP:
627                switch (strtolower($format)) {
628                    case 's':
629                    case 'show':
630                        if (is_string($ret) && !is_numeric($ret)) {
631                            return date(_DBTIMESTAMPSTRING, strtotime($ret));
632                        } else {
633                            return date(_DBTIMESTAMPSTRING, $ret);
634                        }
635                        break 1;
636                    case 'e':
637                    case 'edit':
638                        if (is_string($ret) && !is_numeric($ret)) {
639                            return htmlspecialchars(date(_DBTIMESTAMPSTRING, strtotime($ret)), ENT_QUOTES);
640                        } else {
641                            return htmlspecialchars(date(_DBTIMESTAMPSTRING, $ret), ENT_QUOTES);
642                        }
643                        break 1;
644                    case 'p':
645                    case 'preview':
646                        if (is_string($ret) && !is_numeric($ret)) {
647                            return $ts->stripSlashesGPC(date(_DBTIMESTAMPSTRING, strtotime($ret)));
648                        } else {
649                            return $ts->stripSlashesGPC(date(_DBTIMESTAMPSTRING, $ret));
650                        }
651                        break 1;
652                    case 'f':
653                    case 'formpreview':
654                        if (is_string($ret) && !is_numeric($ret)) {
655                            return htmlspecialchars($ts->stripSlashesGPC(date(_DBTIMESTAMPSTRING, strtotime($ret))), ENT_QUOTES);
656                        } else {
657                            return htmlspecialchars($ts->stripSlashesGPC(date(_DBTIMESTAMPSTRING, $ret)), ENT_QUOTES);
658                        }
659                        break 1;
660                    case 'n':
661                    case 'none':
662                    default:
663                        break 1;
664                }
665                break;
666            default:
667                if ($this->vars[$key]['options'] != '' && $ret != '') {
668                    switch (strtolower($format)) {
669                        case 's':
670                        case 'show':
671                            $selected = explode('|', $ret);
672                            $options  = explode('|', $this->vars[$key]['options']);
673                            $i        = 1;
674                            $ret      = array();
675                            foreach ($options as $op) {
676                                if (in_array($i, $selected)) {
677                                    $ret[] = $op;
678                                }
679                                ++$i;
680                            }
681
682                            return implode(', ', $ret);
683                        case 'e':
684                        case 'edit':
685                            $ret = explode('|', $ret);
686                            break 1;
687                        default:
688                            break 1;
689                    }
690                }
691                break;
692        }
693
694        return $ret;
695    }
696
697    /**
698     * clean values of all variables of the object for storage.
699     * also add slashes wherever needed
700     *
701     * YOU SHOULD NOT USE ANY OF THE UNICODE TYPES, THEY WILL BE REMOVED
702     *
703     * @return bool true if successful
704     * @access public
705     */
706    public function cleanVars()
707    {
708        $ts              = MyTextSanitizer::getInstance();
709        $existing_errors = $this->getErrors();
710        $this->_errors   = array();
711        foreach ($this->vars as $k => $v) {
712            $cleanv = $v['value'];
713            if (!$v['changed']) {
714            } else {
715                $cleanv = is_string($cleanv) ? trim($cleanv) : $cleanv;
716                switch ($v['data_type']) {
717                    case XOBJ_DTYPE_TIMESTAMP:
718                        $cleanv = !is_string($cleanv) && is_numeric($cleanv) ? date(_DBTIMESTAMPSTRING, $cleanv) : date(_DBTIMESTAMPSTRING, strtotime($cleanv));
719                        break;
720                    case XOBJ_DTYPE_TIME:
721                        $cleanv = !is_string($cleanv) && is_numeric($cleanv) ? date(_DBTIMESTRING, $cleanv) : date(_DBTIMESTRING, strtotime($cleanv));
722                        break;
723                    case XOBJ_DTYPE_DATE:
724                        $cleanv = !is_string($cleanv) && is_numeric($cleanv) ? date(_DBDATESTRING, $cleanv) : date(_DBDATESTRING, strtotime($cleanv));
725                        break;
726                    case XOBJ_DTYPE_TXTBOX:
727                        if ($v['required'] && $cleanv != '0' && $cleanv == '') {
728                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
729                            continue 2;
730                        }
731                        if (isset($v['maxlength']) && strlen($cleanv) > (int)$v['maxlength']) {
732                            $this->setErrors(sprintf(_XOBJ_ERR_SHORTERTHAN, $k, (int)$v['maxlength']));
733                            continue 2;
734                        }
735                        if (!$v['not_gpc']) {
736                            $cleanv = $ts->stripSlashesGPC($ts->censorString($cleanv));
737                        } else {
738                            $cleanv = $ts->censorString($cleanv);
739                        }
740                        break;
741                    case XOBJ_DTYPE_TXTAREA:
742                        if ($v['required'] && $cleanv != '0' && $cleanv == '') {
743                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
744                            continue 2;
745                        }
746                        if (!$v['not_gpc']) {
747                            $cleanv = $ts->stripSlashesGPC($ts->censorString($cleanv));
748                        } else {
749                            $cleanv = $ts->censorString($cleanv);
750                        }
751                        break;
752                    case XOBJ_DTYPE_SOURCE:
753                        if (!$v['not_gpc']) {
754                            $cleanv = $ts->stripSlashesGPC($cleanv);
755                        }
756                        break;
757                    case XOBJ_DTYPE_INT:
758                        $cleanv = (int)$cleanv;
759                        break;
760
761                    case XOBJ_DTYPE_EMAIL:
762                        if ($v['required'] && $cleanv == '') {
763                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
764                            continue 2;
765                        }
766                        if ($cleanv != '' && !preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+([\.][a-z0-9-]+)+$/i", $cleanv)) {
767                            $this->setErrors('Invalid Email'); //_XOBJ_ERR_INVALID_EMAIL
768                            continue 2;
769                        }
770                        if (!$v['not_gpc']) {
771                            $cleanv = $ts->stripSlashesGPC($cleanv);
772                        }
773                        break;
774                    case XOBJ_DTYPE_URL:
775                        if ($v['required'] && $cleanv == '') {
776                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
777                            continue 2;
778                        }
779                        if ($cleanv != '' && !preg_match("/^http[s]*:\/\//i", $cleanv)) {
780                            $cleanv = XOOPS_PROT . $cleanv;
781                        }
782                        if (!$v['not_gpc']) {
783                            $cleanv =& $ts->stripSlashesGPC($cleanv);
784                        }
785                        break;
786                    case XOBJ_DTYPE_ARRAY:
787                        $cleanv = (array)$cleanv;
788                        $cleanv = serialize($cleanv);
789                        break;
790                    case XOBJ_DTYPE_STIME:
791                    case XOBJ_DTYPE_MTIME:
792                    case XOBJ_DTYPE_LTIME:
793                        $cleanv = !is_string($cleanv) ? (int)$cleanv : strtotime($cleanv);
794                        break;
795                    case XOBJ_DTYPE_FLOAT:
796                        $cleanv = (float)$cleanv;
797                        break;
798                    case XOBJ_DTYPE_DECIMAL:
799                        $cleanv = (float)$cleanv;
800                        break;
801                    case XOBJ_DTYPE_ENUM:
802                        if (!in_array($cleanv, $v['enumeration'])) {
803                            $this->setErrors('Invalid Enumeration');//_XOBJ_ERR_INVALID_ENUMERATION
804                            continue 2;
805                        }
806                        break;
807                    case XOBJ_DTYPE_UNICODE_TXTBOX:
808                        if ($v['required'] && $cleanv != '0' && $cleanv == '') {
809                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
810                            continue 2;
811                        }
812                        $cleanv = xoops_convert_encode($cleanv);
813                        if (isset($v['maxlength']) && strlen($cleanv) > (int)$v['maxlength']) {
814                            $this->setErrors(sprintf(_XOBJ_ERR_SHORTERTHAN, $k, (int)$v['maxlength']));
815                            continue 2;
816                        }
817                        if (!$v['not_gpc']) {
818                            $cleanv = $ts->stripSlashesGPC($ts->censorString($cleanv));
819                        } else {
820                            $cleanv = $ts->censorString($cleanv);
821                        }
822                        break;
823                    case XOBJ_DTYPE_UNICODE_TXTAREA:
824                        if ($v['required'] && $cleanv != '0' && $cleanv == '') {
825                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
826                            continue 2;
827                        }
828                        $cleanv = xoops_convert_encode($cleanv);
829                        if (!$v['not_gpc']) {
830                            $cleanv = $ts->stripSlashesGPC($ts->censorString($cleanv));
831                        } else {
832                            $cleanv = $ts->censorString($cleanv);
833                        }
834                        break;
835                    case XOBJ_DTYPE_UNICODE_EMAIL:
836                        if ($v['required'] && $cleanv == '') {
837                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
838                            continue 2;
839                        }
840                        if ($cleanv != '' && !preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+([\.][a-z0-9-]+)+$/i", $cleanv)) {
841                            $this->setErrors('Invalid Email');
842                            continue 2;
843                        }
844                        $cleanv = xoops_convert_encode($cleanv);
845                        if (!$v['not_gpc']) {
846                            $cleanv = $ts->stripSlashesGPC($cleanv);
847                        }
848                        break;
849                    case XOBJ_DTYPE_UNICODE_URL:
850                        if ($v['required'] && $cleanv == '') {
851                            $this->setErrors(sprintf(_XOBJ_ERR_REQUIRED, $k));
852                            continue 2;
853                        }
854                        if ($cleanv != '' && !preg_match("/^http[s]*:\/\//i", $cleanv)) {
855                            $cleanv = XOOPS_PROT . $cleanv;
856                        }
857                        $cleanv = xoops_convert_encode($cleanv);
858                        if (!$v['not_gpc']) {
859                            $cleanv =& $ts->stripSlashesGPC($cleanv);
860                        }
861                        break;
862                    case XOBJ_DTYPE_UNICODE_ARRAY:
863                        $cleanv = serialize(array_walk($cleanv, 'xoops_aw_encode'));
864                        break;
865                    default:
866                        break;
867
868                }
869            }
870            $this->cleanVars[$k] = str_replace('\\"', '"', $cleanv);
871            unset($cleanv);
872        }
873        if (count($this->_errors) > 0) {
874            $this->_errors = array_merge($existing_errors, $this->_errors);
875
876            return false;
877        }
878        $this->_errors = array_merge($existing_errors, $this->_errors);
879        $this->unsetDirty();
880
881        return true;
882    }
883
884    /**
885     * dynamically register additional filter for the object
886     *
887     * @param string $filtername name of the filter
888     *
889     * @deprecated \XoopsObject::registerFilter is deprecated since XOOPS 2.5.8 and will be removed in the next major release
890     */
891    public function registerFilter($filtername)
892    {
893        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
894        trigger_error("XoopsObject::registerFilter() is deprecated, called from {$trace[0]['file']} line {$trace[0]['line']}");
895        $this->_filters[] = $filtername;
896    }
897
898    /**
899     * load all additional filters that have been registered to the object
900     *
901     * @access private
902     */
903    public function _loadFilters()
904    {
905        static $loaded;
906        if (isset($loaded)) {
907            return null;
908        }
909        $loaded = 1;
910
911        $path = empty($this->plugin_path) ? __DIR__ . '/filters' : $this->plugin_path;
912        if (file_exists($file = $path . '/filter.php')) {
913            include_once $file;
914            foreach ($this->_filters as $f) {
915                if (file_exists($file = $path . '/' . strtolower($f) . 'php')) {
916                    include_once $file;
917                }
918            }
919        }
920    }
921
922    /**
923     * load all local filters for the object
924     *
925     * Filter distribution:
926     * In each module folder there is a folder "filter" containing filter files with,
927     * filename: [name_of_target_class][.][function/action_name][.php];
928     * function name: [dirname][_][name_of_target_class][_][function/action_name];
929     * parameter: the target object
930     *
931     * @param string $method function or action name
932     *
933     * @deprecated \XoopsObject::loadFilters is deprecated since XOOPS 2.5.8 and will be removed in the next major release
934     */
935    public function loadFilters($method)
936    {
937        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
938        trigger_error("XoopsObject::loadFilters() is deprecated, called from {$trace[0]['file']} line {$trace[0]['line']}");
939
940        $this->_loadFilters();
941
942        xoops_load('XoopsCache');
943        $class = get_class($this);
944        if (!$modules_active = XoopsCache::read('system_modules_active')) {
945            /* @var XoopsModuleHandler $module_handler */
946            $module_handler = xoops_getHandler('module');
947            $modules_obj    = $module_handler->getObjects(new Criteria('isactive', 1));
948            $modules_active = array();
949            foreach (array_keys($modules_obj) as $key) {
950                $modules_active[] = $modules_obj[$key]->getVar('dirname');
951            }
952            unset($modules_obj);
953            XoopsCache::write('system_modules_active', $modules_active);
954        }
955        foreach ($modules_active as $dirname) {
956            if (file_exists($file = XOOPS_ROOT_PATH . '/modules/' . $dirname . '/filter/' . $class . '.' . $method . '.php')) {
957                include_once $file;
958                if (function_exists($class . '_' . $method)) {
959                    call_user_func_array($dirname . '_' . $class . '_' . $method, array(&$this));
960                }
961            }
962        }
963    }
964
965    /**
966     * create a clone(copy) of the current object
967     *
968     * @access public
969     * @return object clone
970     */
971    public function xoopsClone()
972    {
973        $class = get_class($this);
974        $clone = null;
975        $clone = new $class();
976        foreach ($this->vars as $k => $v) {
977            $clone->assignVar($k, $v['value']);
978        }
979        // need this to notify the handler class that this is a newly created object
980        $clone->setNew();
981
982        return $clone;
983    }
984
985    /**
986     * Adjust a newly cloned object
987     */
988    public function __clone()
989    {
990        // need this to notify the handler class that this is a newly created object
991        $this->setNew();
992    }
993
994    /**
995     * add an error
996     *
997     * @param $err_str
998     * @internal param string $value error to add
999     * @access   public
1000     */
1001    public function setErrors($err_str)
1002    {
1003        if (is_array($err_str)) {
1004            $this->_errors = array_merge($this->_errors, $err_str);
1005        } else {
1006            $this->_errors[] = trim($err_str);
1007        }
1008    }
1009
1010    /**
1011     * return the errors for this object as an array
1012     *
1013     * @return array an array of errors
1014     * @access public
1015     */
1016    public function getErrors()
1017    {
1018        return $this->_errors;
1019    }
1020
1021    /**
1022     * return the errors for this object as html
1023     *
1024     * @return string html listing the errors
1025     * @access public
1026     */
1027    public function getHtmlErrors()
1028    {
1029        $ret = '<h4>Errors</h4>';
1030        if (!empty($this->_errors)) {
1031            foreach ($this->_errors as $error) {
1032                $ret .= $error . '<br>';
1033            }
1034        } else {
1035            $ret .= 'None<br>';
1036        }
1037
1038        return $ret;
1039    }
1040
1041    /**
1042     * Returns an array representation of the object
1043     *
1044     * Deprecated, use getValues() directly
1045     *
1046     * @return array
1047     */
1048    public function toArray()
1049    {
1050        return $this->getValues();
1051    }
1052}
1053
1054/**
1055 * XOOPS object handler class.
1056 * This class is an abstract class of handler classes that are responsible for providing
1057 * data access mechanisms to the data source of its corresponding data objects
1058 *
1059 * @package             kernel
1060 * @abstract
1061 * @author              Kazumi Ono <onokazu@xoops.org>
1062 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
1063 */
1064class XoopsObjectHandler
1065{
1066    /**
1067     * XoopsDatabase holds referenced to {@link XoopsDatabase} class object
1068     *
1069     * @var XoopsDatabase
1070     */
1071    public $db;
1072
1073    /**
1074     * called from child classes only
1075     *
1076     * @param XoopsDatabase $db reference to the {@link XoopsDatabase} object
1077     * @access protected
1078     */
1079    public function __construct(XoopsDatabase $db)
1080    {
1081        /* @var XoopsMySQLDatabase $db */
1082        $this->db = $db;
1083    }
1084
1085    /**
1086     * PHP 4 style constructor compatibility shim
1087     *
1088     * @param XoopsDatabase $db database object
1089     * @deprecated all callers should be using parent::__construct()
1090     */
1091    public function XoopsObjectHandler($db)
1092    {
1093        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
1094        trigger_error("Should call parent::__construct in {$trace[0]['file']} line {$trace[0]['line']},");
1095        self::__construct($db);
1096    }
1097
1098    /**
1099     * creates a new object
1100     *
1101     * @abstract
1102     * @return XoopsObject
1103     */
1104    public function create()
1105    {
1106    }
1107
1108    /**
1109     * gets a value object
1110     *
1111     * @param int $int_id
1112     * @abstract
1113     * @return XoopsObject
1114     */
1115    public function get($int_id)
1116    {
1117    }
1118
1119    /**
1120     * insert/update object
1121     *
1122     * @param XoopsObject $object
1123     * @abstract
1124     */
1125    public function insert(XoopsObject $object)
1126    {
1127    }
1128
1129    /**
1130     * delete object from database
1131     *
1132     * @param XoopsObject $object
1133     * @abstract
1134     */
1135    public function delete(XoopsObject $object)
1136    {
1137    }
1138}
1139
1140/**
1141 * Persistable Object Handler class.
1142 *
1143 * @author              Taiwen Jiang <phppp@users.sourceforge.net>
1144 * @author              Jan Keller Pedersen <mithrandir@xoops.org>
1145 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
1146 * @package             Kernel
1147 */
1148class XoopsPersistableObjectHandler extends XoopsObjectHandler
1149{
1150    /**
1151     * holds reference to custom extended object handler
1152     *
1153     * var object
1154     *
1155     * @access private
1156     */
1157    /**
1158     * static protected
1159     */
1160    public $handler;
1161
1162    /**
1163     * holds reference to predefined extended object handlers: read, stats, joint, write, sync
1164     *
1165     * The handlers hold methods for different purposes, which could be all put together inside of current class.
1166     * However, load codes only if they are necessary, thus they are now split out.
1167     *
1168     * var array of objects
1169     *
1170     * @access private
1171     */
1172    /**
1173     * static protected
1174     */
1175    public $handlers = array('read' => null, 'stats' => null, 'joint' => null, 'write' => null, 'sync' => null);
1176
1177    /**
1178     * Information about the class, the handler is managing
1179     *
1180     * @var string
1181     */
1182    public $table;
1183
1184    /**
1185     * @var string
1186     */
1187    public $keyName;
1188
1189    /**
1190     * @var string
1191     */
1192    public $className;
1193
1194    /**
1195     * @var string
1196     */
1197    public $identifierName;
1198
1199    /**
1200     * @var string
1201     */
1202    public $field_link;
1203
1204    /**
1205     * @var string
1206     */
1207    public $field_object;
1208
1209    /**
1210     * Constructor
1211     *
1212     * @param null|XoopsDatabase $db             database connection
1213     * @param string             $table          Name of database table
1214     * @param string             $className      Name of the XoopsObject class this handler manages
1215     * @param string             $keyName        Name of the property holding the key
1216     * @param string             $identifierName Name of the property holding an identifier
1217     *                                            name (title, name ...), used on getList()
1218     */
1219    public function __construct(XoopsDatabase $db = null, $table = '', $className = '', $keyName = '', $identifierName = '')
1220    {
1221        $db    = XoopsDatabaseFactory::getDatabaseConnection();
1222        $table = $db->prefix($table);
1223        parent::__construct($db);
1224        $this->table     = $table;
1225        $this->keyName   = $keyName;
1226        $this->className = $className;
1227        if ($identifierName) {
1228            $this->identifierName = $identifierName;
1229        }
1230    }
1231
1232    /**
1233     * PHP 4 style constructor compatibility shim
1234     *
1235     * @param null|XoopsDatabase $db             database connection
1236     * @param string             $table          Name of database table
1237     * @param string             $className      Name of the XoopsObject class this handler manages
1238     * @param string             $keyName        Name of the property holding the key
1239     * @param string             $identifierName Name of the property holding an identifier
1240     *                                            name (title, name ...), used on getList()
1241     *
1242     * @deprecated all callers should be using parent::__construct()
1243     */
1244    public function XoopsPersistableObjectHandler(XoopsDatabase $db = null, $table = '', $className = '', $keyName = '', $identifierName = '')
1245    {
1246        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
1247        trigger_error("Should call parent::__construct in {$trace[0]['file']} line {$trace[0]['line']},");
1248        self::__construct($db, $table, $className, $keyName, $identifierName);
1249    }
1250
1251    /**
1252     * Set custom handler
1253     *
1254     * @access   protected
1255     * @param null|string   $handler
1256     * @param null   $args
1257     * @param string $path path to class
1258     * @internal param object $handler
1259     * @internal param mixed  $args
1260     * @return object of handler
1261     */
1262    public function setHandler($handler = null, $args = null, $path = null)
1263    {
1264        $this->handler = null;
1265        if (is_object($handler)) {
1266            $this->handler = $handler;
1267        } elseif (is_string($handler)) {
1268            xoops_load('XoopsModelFactory');
1269            $this->handler = XoopsModelFactory::loadHandler($this, $handler, $args);
1270        }
1271
1272        return $this->handler;
1273    }
1274
1275    /**
1276     * Load predefined handler
1277     *
1278     * @access protected
1279     * @param  string $name handler name
1280     * @param  mixed  $args args
1281     * @return XoopsModelAbstract of handler {@link XoopsModelAbstract}
1282     */
1283    public function loadHandler($name, $args = null)
1284    {
1285        static $handlers;
1286        if (!isset($handlers[$name])) {
1287            xoops_load('XoopsModelFactory');
1288            $handlers[$name] = XoopsModelFactory::loadHandler($this, $name, $args);
1289        } else {
1290            $handlers[$name]->setHandler($this);
1291            $handlers[$name]->setVars($args);
1292        }
1293
1294        return $handlers[$name];
1295
1296        /**
1297         * // Following code just kept as placeholder for PHP5
1298         * if (!isset(self::$handlers[$name])) {
1299         * self::$handlers[$name] = XoopsModelFactory::loadHandler($this, $name, $args);
1300         * } else {
1301         * self::$handlers[$name]->setHandler($this);
1302         * self::$handlers[$name]->setVars($args);
1303         * }
1304         *
1305         * return self::$handlers[$name];
1306         */
1307    }
1308
1309    /**
1310     * Magic method for overloading of delegation
1311     *
1312     * To be enabled in XOOPS 3.0 with PHP 5
1313     *
1314     * @access protected
1315     * @param  string $name method name
1316     * @param  array  $args arguments
1317     * @return mixed
1318     */
1319    public function __call($name, $args)
1320    {
1321        if (is_object($this->handler) && is_callable(array($this->handler, $name))) {
1322            return call_user_func_array(array($this->handler, $name), $args);
1323        }
1324        foreach (array_keys($this->handlers) as $_handler) {
1325            $handler = $this->loadHandler($_handler);
1326            if (is_callable(array($handler, $name))) {
1327                return call_user_func_array(array($handler, $name), $args);
1328            }
1329        }
1330
1331        return null;
1332    }
1333
1334    /**
1335     * *#@+
1336     * Methods of native handler {@link XoopsPersistableObjectHandler}
1337     */
1338    /**
1339     * create a new object
1340     *
1341     * @param  bool $isNew Flag the new objects as new
1342     * @return XoopsObject {@link XoopsObject}
1343     */
1344    public function create($isNew = true)
1345    {
1346        $obj = new $this->className();
1347        if ($isNew === true) {
1348            $obj->setNew();
1349        }
1350
1351        return $obj;
1352    }
1353
1354    /**
1355     * Load a {@link XoopsObject} object from the database
1356     *
1357     * @access protected
1358     * @param  mixed $id     ID
1359     * @param  array $fields fields to fetch
1360     * @return XoopsObject {@link XoopsObject}
1361     */
1362    public function get($id = null, $fields = null)
1363    {
1364        $object = null;
1365        if (empty($id)) {
1366            $object = $this->create();
1367
1368            return $object;
1369        }
1370        if (is_array($fields) && count($fields) > 0) {
1371            $select = implode(',', $fields);
1372            if (!in_array($this->keyName, $fields)) {
1373                $select .= ', ' . $this->keyName;
1374            }
1375        } else {
1376            $select = '*';
1377        }
1378        $sql = sprintf('SELECT %s FROM %s WHERE %s = %s', $select, $this->table, $this->keyName, $this->db->quote($id));
1379        //$sql = "SELECT {$select} FROM {$this->table} WHERE {$this->keyName} = " . $this->db->quote($id);
1380        if (!$result = $this->db->query($sql)) {
1381            return $object;
1382        }
1383        if (!$this->db->getRowsNum($result)) {
1384            return $object;
1385        }
1386        $object = $this->create(false);
1387        $object->assignVars($this->db->fetchArray($result));
1388
1389        return $object;
1390    }
1391    /**
1392     * *#@-
1393     */
1394
1395    /**
1396     * *#@+
1397     * Methods of write handler {@link XoopsObjectWrite}
1398     */
1399    /**
1400     * insert an object into the database
1401     *
1402     * @param  XoopsObject $object {@link XoopsObject} reference to object
1403     * @param  bool        $force  flag to force the query execution despite security settings
1404     * @return mixed       object ID
1405     */
1406    public function insert(XoopsObject $object, $force = true)
1407    {
1408        $handler = $this->loadHandler('write');
1409
1410        return $handler->insert($object, $force);
1411    }
1412
1413    /**
1414     * delete an object from the database
1415     *
1416     * @param  XoopsObject $object {@link XoopsObject} reference to the object to delete
1417     * @param  bool        $force
1418     * @return bool        FALSE if failed.
1419     */
1420    public function delete(XoopsObject $object, $force = false)
1421    {
1422        $handler = $this->loadHandler('write');
1423
1424        return $handler->delete($object, $force);
1425    }
1426
1427    /**
1428     * delete all objects matching the conditions
1429     *
1430     * @param  CriteriaElement $criteria {@link CriteriaElement} with conditions to meet
1431     * @param  bool            $force    force to delete
1432     * @param  bool            $asObject delete in object way: instantiate all objects and delete one by one
1433     * @return bool
1434     */
1435    public function deleteAll(CriteriaElement $criteria = null, $force = true, $asObject = false)
1436    {
1437        $handler = $this->loadHandler('write');
1438
1439        return $handler->deleteAll($criteria, $force, $asObject);
1440    }
1441
1442    /**
1443     * Change a field for objects with a certain criteria
1444     *
1445     * @param  string          $fieldname  Name of the field
1446     * @param  mixed           $fieldvalue Value to write
1447     * @param  CriteriaElement $criteria   {@link CriteriaElement}
1448     * @param  bool            $force      force to query
1449     * @return bool
1450     */
1451    public function updateAll($fieldname, $fieldvalue, CriteriaElement $criteria = null, $force = false)
1452    {
1453        $handler = $this->loadHandler('write');
1454
1455        return $handler->updateAll($fieldname, $fieldvalue, $criteria, $force);
1456    }
1457    /**
1458     * *#@-
1459     */
1460
1461    /**
1462     * *#@+
1463     * Methods of read handler {@link XoopsObjectRead}
1464     */
1465    /**
1466     * Retrieve objects from the database
1467     *
1468     * @param  CriteriaElement $criteria  {@link CriteriaElement} conditions to be met
1469     * @param  bool            $id_as_key use the ID as key for the array
1470     * @param  bool            $as_object return an array of objects
1471     * @return array
1472     */
1473    public function &getObjects(CriteriaElement $criteria = null, $id_as_key = false, $as_object = true)
1474    {
1475        $handler = $this->loadHandler('read');
1476        $ret     = $handler->getObjects($criteria, $id_as_key, $as_object);
1477
1478        return $ret;
1479    }
1480
1481    /**
1482     * get all objects matching a condition
1483     *
1484     * @param  CriteriaElement $criteria  {@link CriteriaElement} to match
1485     * @param  array           $fields    variables to fetch
1486     * @param  bool            $asObject  flag indicating as object, otherwise as array
1487     * @param  bool            $id_as_key use the ID as key for the array
1488     * @return array           of objects/array {@link XoopsObject}
1489     */
1490    public function &getAll(CriteriaElement $criteria = null, $fields = null, $asObject = true, $id_as_key = true)
1491    {
1492        $handler = $this->loadHandler('read');
1493        $ret     = $handler->getAll($criteria, $fields, $asObject, $id_as_key);
1494
1495        return $ret;
1496    }
1497
1498    /**
1499     * Retrieve a list of objects data
1500     *
1501     * @param  CriteriaElement $criteria {@link CriteriaElement} conditions to be met
1502     * @param  int             $limit    Max number of objects to fetch
1503     * @param  int             $start    Which record to start at
1504     * @return array
1505     */
1506    public function getList(CriteriaElement $criteria = null, $limit = 0, $start = 0)
1507    {
1508        $handler = $this->loadHandler('read');
1509        $ret     = $handler->getList($criteria, $limit, $start);
1510
1511        return $ret;
1512    }
1513
1514    /**
1515     * get IDs of objects matching a condition
1516     *
1517     * @param  CriteriaElement $criteria {@link CriteriaElement} to match
1518     * @return array           of object IDs
1519     */
1520    public function &getIds(CriteriaElement $criteria = null)
1521    {
1522        $handler = $this->loadHandler('read');
1523        $ret     = $handler->getIds($criteria);
1524
1525        return $ret;
1526    }
1527
1528    /**
1529     * get a limited list of objects matching a condition
1530     *
1531     * {@link CriteriaCompo}
1532     *
1533     * @param  int             $limit    Max number of objects to fetch
1534     * @param  int             $start    Which record to start at
1535     * @param  CriteriaElement $criteria {@link CriteriaElement} to match
1536     * @param  array           $fields   variables to fetch
1537     * @param  bool            $asObject flag indicating as object, otherwise as array
1538     * @return array           of objects     {@link XoopsObject}
1539     */
1540    public function &getByLimit($limit = 0, $start = 0, CriteriaElement $criteria = null, $fields = null, $asObject = true)
1541    {
1542        $handler = $this->loadHandler('read');
1543        $ret     = $handler->getByLimit($limit, $start, $criteria, $fields, $asObject);
1544
1545        return $ret;
1546    }
1547    /**
1548     * *#@-
1549     */
1550
1551    /**
1552     * *#@+
1553     * Methods of stats handler {@link XoopsObjectStats}
1554     */
1555    /**
1556     * count objects matching a condition
1557     *
1558     * @param  CriteriaElement $criteria {@link CriteriaElement} to match
1559     * @return int             count of objects
1560     */
1561    public function getCount(CriteriaElement $criteria = null)
1562    {
1563        $handler = $this->loadHandler('stats');
1564
1565        return $handler->getCount($criteria);
1566    }
1567
1568    /**
1569     * Get counts of objects matching a condition
1570     *
1571     * @param  CriteriaElement $criteria {@link CriteriaElement} to match
1572     * @return array           of counts
1573     */
1574    public function getCounts(CriteriaElement $criteria = null)
1575    {
1576        $handler = $this->loadHandler('stats');
1577
1578        return $handler->getCounts($criteria);
1579    }
1580    /**
1581     * *#@-
1582     */
1583
1584    /**
1585     * *#@+
1586     * Methods of joint handler {@link XoopsObjectJoint}
1587     */
1588    /**
1589     * get a list of objects matching a condition joint with another related object
1590     *
1591     * @param  CriteriaElement $criteria     {@link CriteriaElement} to match
1592     * @param  array           $fields       variables to fetch
1593     * @param  bool            $asObject     flag indicating as object, otherwise as array
1594     * @param  string          $field_link   field of linked object for JOIN
1595     * @param  string          $field_object field of current object for JOIN
1596     * @return array           of objects {@link XoopsObject}
1597     */
1598    public function &getByLink(CriteriaElement $criteria = null, $fields = null, $asObject = true, $field_link = null, $field_object = null)
1599    {
1600        $handler = $this->loadHandler('joint');
1601        $ret     = $handler->getByLink($criteria, $fields, $asObject, $field_link, $field_object);
1602
1603        return $ret;
1604    }
1605
1606    /**
1607     * Count of objects matching a condition
1608     *
1609     * @param  CriteriaElement $criteria {@link CriteriaElement} to match
1610     * @return int             count of objects
1611     */
1612    public function getCountByLink(CriteriaElement $criteria = null)
1613    {
1614        $handler = $this->loadHandler('joint');
1615        $ret     = $handler->getCountByLink($criteria);
1616
1617        return $ret;
1618    }
1619
1620    /**
1621     * array of count of objects matching a condition of, groupby linked object keyname
1622     *
1623     * @param  CriteriaElement $criteria {@link CriteriaElement} to match
1624     * @return int             count of objects
1625     */
1626    public function getCountsByLink(CriteriaElement $criteria = null)
1627    {
1628        $handler = $this->loadHandler('joint');
1629        $ret     = $handler->getCountsByLink($criteria);
1630
1631        return $ret;
1632    }
1633
1634    /**
1635     * update objects matching a condition against linked objects
1636     *
1637     * @param  array           $data     array of key => value
1638     * @param  CriteriaElement $criteria {@link CriteriaElement} to match
1639     * @return int             count of objects
1640     */
1641    public function updateByLink($data, CriteriaElement $criteria = null)
1642    {
1643        $handler = $this->loadHandler('joint');
1644        $ret     = $handler->updateByLink($data, $criteria);
1645
1646        return $ret;
1647    }
1648
1649    /**
1650     * Delete objects matching a condition against linked objects
1651     *
1652     * @param  CriteriaElement $criteria {@link CriteriaElement} to match
1653     * @return int             count of objects
1654     */
1655    public function deleteByLink(CriteriaElement $criteria = null)
1656    {
1657        $handler = $this->loadHandler('joint');
1658        $ret     = $handler->deleteByLink($criteria);
1659
1660        return $ret;
1661    }
1662    /**
1663     * *#@-
1664     */
1665
1666    /**
1667     * *#@+
1668     * Methods of sync handler {@link XoopsObjectSync}
1669     */
1670    /**
1671     * Clean orphan objects against linked objects
1672     *
1673     * @param  string $table_link   table of linked object for JOIN
1674     * @param  string $field_link   field of linked object for JOIN
1675     * @param  string $field_object field of current object for JOIN
1676     * @return bool   true on success
1677     */
1678    public function cleanOrphan($table_link = '', $field_link = '', $field_object = '')
1679    {
1680        $handler = $this->loadHandler('sync');
1681        $ret     = $handler->cleanOrphan($table_link, $field_link, $field_object);
1682
1683        return $ret;
1684    }
1685
1686    /**
1687     * Synchronizing objects
1688     *
1689     * @return bool true on success
1690     */
1691    public function synchronization()
1692    {
1693        $retval = $this->cleanOrphan();
1694
1695        return $retval;
1696    }
1697    /**
1698     * *#@-
1699     */
1700
1701    /**#@+
1702     * @deprecated
1703     * @param      $result
1704     * @param bool $id_as_key
1705     * @param bool $as_object
1706     * @return bool
1707     */
1708    public function convertResultSet($result, $id_as_key = false, $as_object = true)
1709    {
1710        trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated', E_USER_WARNING);
1711
1712        return false;
1713    }
1714    /**#@-*/
1715}
1716