Issues (733)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

class/ExtcalPersistableObjectHandler.php (12 issues)

1
<?php
2
3
namespace XoopsModules\Extcal;
4
5
/*
6
 * You may not change or alter any portion of this comment or credits
7
 * of supporting developers from this source code or any supporting source code
8
 * which is considered copyrighted (c) material of the original comment or credit authors.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
 */
14
15
/**
16
 * @copyright    {@link https://xoops.org/ XOOPS Project}
17
 * @license      {@link https://www.gnu.org/licenses/gpl-2.0.html GNU GPL 2 or later}
18
 * @package      extcal
19
 * @since
20
 * @author       XOOPS Development Team,
21
 */
22
23
use XoopsModules\Extcal\{Helper
0 ignored issues
show
This use statement conflicts with another class in this namespace, XoopsModules\Extcal\Helper. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
24
};
25
26
/**
27
 * Persistable Object Handler class.
28
 * This class is responsible for providing data access mechanisms to the data source
29
 * of derived class objects.
30
 *
31
 * @author    Jan Keller Pedersen <[email protected]> - IDG Danmark A/S <www.idg.dk>
32
 * @copyright copyright (c) 2000-2004 XOOPS.org
33
 */
34
class ExtcalPersistableObjectHandler extends \XoopsPersistableObjectHandler //XoopsObjectHandler
35
{
36
    /**#@+
37
     * Information about the class, the handler is managing
38
     *
39
     * @var string
40
     */
41
    //    public $table;
42
    //    public $keyName;
43
    //    public $className;
44
    //    public $identifierName;
45
46
    /**#@-*/
47
48
    /**
49
     * Constructor - called from child classes.
50
     *
51
     * @param \XoopsDatabase|null $db        {@link XoopsDatabase}
52
     *                                       object
53
     * @param string              $tablename Name of database table
54
     * @param string              $classname Name of Class, this handler is managing
55
     * @param string              $keyname   Name of the property, holding the key
56
     * @param bool                $idenfierName
57
     */
58
    public function __construct(\XoopsDatabase $db, $tablename, $classname, $keyname, $idenfierName = false)
59
    {
60
        parent::__construct($db);
61
        $this->table     = $db->prefix($tablename);
62
        $this->keyName   = $keyname;
63
        $this->className = $classname;
64
        if (false !== $idenfierName) {
65
            $this->identifierName = $idenfierName;
0 ignored issues
show
Documentation Bug introduced by
The property $identifierName was declared of type string, but $idenfierName is of type true. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
66
        }
67
    }
68
69
    /**
70
     * Constructor.
71
     * @param mixed $isNew
72
     */
73
    //    public function ExtcalPersistableObjectHandler($db, $tablename, $classname, $keyname, $idenfierName = false)
74
    //    {
75
    //        $this->__construct($db, $tablename, $classname, $keyname, $idenfierName);
76
    //    }
77
78
    /**
79
     * create a new user.
80
     *
81
     * @param bool $isNew Flag the new objects as "new"?
82
     *
83
     * @return \XoopsObject
84
     */
85
    public function create($isNew = true)
86
    {
87
        $obj = new $this->className();
88
        if (true === $isNew) {
89
            $obj->setNew();
90
        }
91
92
        return $obj;
93
    }
94
95
    /**
96
     * retrieve an object.
97
     *
98
     * @param mixed      $id ID of the object - or array of ids for joint keys. Joint keys MUST be given in the same order as in the constructor
99
     * @param null|array $fields
100
     * @param bool       $as_object
101
     *
102
     * @return mixed reference to the object, FALSE if failed
103
     *
104
     * @internal param bool $asObject whether to return an object or an array
105
     */
106
    public function get($id = null, $fields = null, $as_object = true) //get($id, $as_object = true)
107
    {
108
        if (\is_array($this->keyName)) {
0 ignored issues
show
The condition is_array($this->keyName) is always false.
Loading history...
109
            $criteria = new \CriteriaCompo();
110
            for ($i = 0, $iMax = \count($this->keyName); $i < $iMax; ++$i) {
111
                $criteria->add(new \Criteria($this->keyName[$i], (int)$id[$i]));
112
            }
113
        } else {
114
            $criteria = new \Criteria($this->keyName, (int)$id);
115
        }
116
        $criteria->setLimit(1);
117
        $objectArray = $this->getObjects($criteria, false, true);
118
        if (1 != \count($objectArray)) {
119
            return $this->create();
120
        }
121
122
        return $objectArray[0];
123
    }
124
125
    /**
126
     * retrieve objects from the database.
127
     *
128
     * @param \CriteriaElement|null $criteria {@link CriteriaElement} conditions to be met
129
     * @param bool                  $idAsKey  use the ID as key for the array?
130
     * @param bool                  $asObject return an array of objects?
131
     *
132
     * @return array
133
     */
134
    public function &getObjects(\CriteriaElement $criteria = null, $idAsKey = false, $asObject = true)
135
    {
136
        $ret   = [];
137
        $limit = $start = 0;
138
        $sql   = 'SELECT * FROM ' . $this->table;
139
        if (\is_object($criteria) && is_subclass_of($criteria,  \CriteriaElement::class)) {
140
            $sql .= ' ' . $criteria->renderWhere();
0 ignored issues
show
The method renderWhere() does not exist on CriteriaElement. Did you maybe mean render()? ( Ignorable by Annotation )

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

140
            $sql .= ' ' . $criteria->/** @scrutinizer ignore-call */ renderWhere();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
141
            if ('' != $criteria->getSort()) {
142
                $sql .= ' ORDER BY ' . $criteria->getSort() . ' ' . $criteria->getOrder();
143
            }
144
            $limit = $criteria->getLimit();
145
            $start = $criteria->getStart();
146
        }
147
        $result = $this->db->query($sql, $limit, $start);
148
        if (!$result) {
149
            return $ret;
150
        }
151
152
        $ret = $this->convertResultSet($result, $idAsKey, $asObject);
153
154
        return $ret;
155
    }
156
157
    /**
158
     * Convert a database resultset to a returnable array.
159
     *
160
     * @param \mysqli_result $result  database resultset
161
     * @param bool         $idAsKey - should NOT be used with joint keys
162
     * @param bool         $asObject
163
     *
164
     * @return array
165
     */
166
    public function convertResultSet($result, $idAsKey = false, $asObject = true)
167
    {
168
        $ret = [];
169
        while (false !== ($myrow = $this->db->fetchArray($result))) {
170
            $obj = $this->create(false);
171
            $obj->assignVars($myrow);
172
            if (!$idAsKey) {
173
                if ($asObject) {
174
                    $ret[] = $obj;
175
                } else {
176
                    $row  = [];
177
                    $vars = &$obj->getVars();
178
                    foreach (\array_keys($vars) as $i) {
179
                        $row[$i] = $obj->getVar($i);
180
                    }
181
                    $ret[] = $row;
182
                }
183
            } else {
184
                if ($asObject) {
185
                    $ret[$myrow[$this->keyName]] = $obj;
186
                } else {
187
                    $row  = [];
188
                    $vars = &$obj->getVars();
189
                    foreach (\array_keys($vars) as $i) {
190
                        $row[$i] = $obj->getVar($i);
191
                    }
192
                    $ret[$myrow[$this->keyName]] = $row;
193
                }
194
            }
195
            unset($obj);
196
        }
197
198
        return $ret;
199
    }
200
201
    /**
202
     * Retrieve a list of objects as arrays - DON'T USE WITH JOINT KEYS.
203
     *
204
     * @param \CriteriaCompo|\CriteriaElement|null $criteria {@link CriteriaCompo|\CriteriaElement} conditions to be met
205
     * @param int                                  $limit    Max number of objects to fetch
206
     * @param int                                  $start    Which record to start at
207
     *
208
     * @return array
209
     */
210
    public function getList(\CriteriaElement $criteria = null, $limit = 0, $start = 0)
211
    {
212
        $ret = [];
213
        if (null === $criteria) {
214
            $criteria = new \CriteriaCompo();
215
        }
216
217
        if ('' == $criteria->getSort()) {
218
            $criteria->setSort($this->identifierName);
219
        }
220
221
        $sql = 'SELECT ' . $this->keyName;
222
        if (!empty($this->identifierName)) {
223
            $sql .= ', ' . $this->identifierName;
224
        }
225
        $sql .= ' FROM ' . $this->table;
226
        if (\is_object($criteria) && is_subclass_of($criteria,  \CriteriaElement::class)) {
227
            $sql .= ' ' . $criteria->renderWhere();
228
            if ('' != $criteria->getSort()) {
229
                $sql .= ' ORDER BY ' . $criteria->getSort() . ' ' . $criteria->getOrder();
230
            }
231
            $limit = $criteria->getLimit();
232
            $start = $criteria->getStart();
233
        }
234
        $result = $this->db->query($sql, $limit, $start);
235
        if ($result) {
236
            while (false !== ($myrow = $this->db->fetchArray($result))) {
237
                //identifiers should be textboxes, so sanitize them like that
238
                $ret[$myrow[$this->keyName]] = empty($this->identifierName) ? 1 : htmlspecialchars($myrow[$this->identifierName]);
239
            }
240
        }
241
        return $ret;
242
    }
243
244
    /**
245
     * count objects matching a condition.
246
     *
247
     * @param \CriteriaElement|null $criteria {@link \CriteriaCompo|\CriteriaElement} to match
248
     *
249
     * @return int|array count of objects
250
     */
251
    public function getCount(\CriteriaElement $criteria = null)
252
    {
253
        $field   = '';
254
        $groupby = false;
255
        if (isset($criteria) && null !== $criteria) {
256
            if ('' != $criteria->groupby) {
257
                $groupby = true;
258
                $field   = $criteria->groupby . ', '; //Not entirely secure unless you KNOW that no criteria's groupby clause is going to be mis-used
259
            }
260
        }
261
        $sql = 'SELECT ' . $field . 'COUNT(*) FROM ' . $this->table;
262
        if (\is_object($criteria) && is_subclass_of($criteria,  \CriteriaElement::class)) {
263
            $sql .= ' ' . $criteria->renderWhere();
264
            if ('' != $criteria->groupby) {
265
                $sql .= $criteria->getGroupby();
266
            }
267
        }
268
        $result = $this->db->query($sql);
269
        if (!$result) {
270
            return 0;
271
        }
272
        if (false === $groupby) {
273
            [$count] = $this->db->fetchRow($result);
274
275
            return $count;
276
        }
277
        $ret = [];
278
        while (list($id, $count) = $this->db->fetchRow($result)) {
279
            $ret[$id] = $count;
280
        }
281
282
        return $ret;
283
    }
284
285
    /**
286
     * delete an object from the database by id.
287
     *
288
     * @param mixed $id id of the object to delete
289
     * @param bool  $force
290
     *
291
     * @return bool FALSE if failed.
292
     */
293
    public function deleteById($id, $force = false) //delete(\XoopsObject $object, $force = false)
294
    {
295
        if (\is_array($this->keyName)) {
0 ignored issues
show
The condition is_array($this->keyName) is always false.
Loading history...
296
            $clause = [];
297
            for ($i = 0, $iMax = \count($this->keyName); $i < $iMax; ++$i) {
298
                $clause[] = $this->keyName[$i] . ' = ' . $id[$i];
299
            }
300
            $whereclause = \implode(' AND ', $clause);
301
        } else {
302
            $whereclause = $this->keyName . ' = ' . $id;
303
        }
304
        $sql = 'DELETE FROM ' . $this->table . ' WHERE ' . $whereclause;
305
        if (false !== $force) {
306
            $result = $this->db->queryF($sql);
307
        } else {
308
            $result = $this->db->query($sql);
309
        }
310
        if (!$result) {
311
            return false;
312
        }
313
314
        return true;
315
    }
316
317
    /**
318
     * insert a new object in the database.
319
     *
320
     * @param \XoopsObject $obj         reference to the object
321
     * @param bool         $force       whether to force the query execution despite security settings
322
     * @param bool         $checkObject check if the object is dirty and clean the attributes
323
     *
324
     * @return bool FALSE if failed, TRUE if already present and unchanged or successful
325
     */
326
    public function insert(\XoopsObject $obj, $force = false, $checkObject = true)
327
    {
328
        if (false !== $checkObject) {
329
            if (!\is_object($obj)) {
330
                //                var_dump($obj);
331
                return false;
332
            }
333
334
            if (!($obj instanceof $this->className && \class_exists($this->className))) {
335
                $obj->setErrors(\get_class($obj) . ' Differs from ' . $this->className);
336
337
                return false;
338
            }
339
        }
340
        if (!$obj->cleanVars()) {
341
            return false;
342
        }
343
344
        foreach ($obj->cleanVars as $k => $v) {
345
            if (\XOBJ_DTYPE_INT == $obj->vars[$k]['data_type']) {
346
                $cleanvars[$k] = (int)$v;
347
            } elseif (\is_array($v)) {
348
                $cleanvars[$k] = $this->db->quoteString(\implode(',', $v));
349
            } else {
350
                $cleanvars[$k] = $this->db->quoteString($v);
351
            }
352
        }
353
        if ($obj->isNew()) {
354
            if (!\is_array($this->keyName)) {
0 ignored issues
show
The condition is_array($this->keyName) is always false.
Loading history...
355
                if ($cleanvars[$this->keyName] < 1) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cleanvars seems to be defined by a foreach iteration on line 344. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
356
                    $cleanvars[$this->keyName] = $this->db->genId($this->table . '_' . $this->keyName . '_seq');
357
                }
358
            }
359
            $sql = 'INSERT INTO ' . $this->table . ' (' . \implode(',', \array_keys($cleanvars)) . ') VALUES (' . \implode(',', $cleanvars) . ')';
360
        } else {
361
            $sql = 'UPDATE ' . $this->table . ' SET';
362
            foreach ($cleanvars as $key => $value) {
363
                if ((!\is_array($this->keyName) && $key == $this->keyName)
364
                    || (\is_array($this->keyName)
365
                        && \in_array($key, $this->keyName))) {
366
                    continue;
367
                }
368
                if (isset($notfirst)) {
369
                    $sql .= ',';
370
                }
371
                $sql      .= ' ' . $key . ' = ' . $value;
372
                $notfirst = true;
373
            }
374
            if (\is_array($this->keyName)) {
0 ignored issues
show
The condition is_array($this->keyName) is always false.
Loading history...
375
                $whereclause = '';
376
                for ($i = 0, $iMax = \count($this->keyName); $i < $iMax; ++$i) {
377
                    if ($i > 0) {
378
                        $whereclause .= ' AND ';
379
                    }
380
                    $whereclause .= $this->keyName[$i] . ' = ' . $obj->getVar($this->keyName[$i]);
381
                }
382
            } else {
383
                $whereclause = $this->keyName . ' = ' . $obj->getVar($this->keyName);
384
            }
385
            $sql .= ' WHERE ' . $whereclause;
386
        }
387
        if (false !== $force) {
388
            $result = $this->db->queryF($sql);
389
        } else {
390
            $result = $this->db->query($sql);
391
        }
392
        if (!$result) {
393
            return false;
394
        }
395
        if (!\is_array($this->keyName) && $obj->isNew()) {
0 ignored issues
show
The condition is_array($this->keyName) is always false.
Loading history...
396
            $obj->assignVar($this->keyName, $this->db->getInsertId());
397
        }
398
399
        return true;
400
    }
401
402
    /**
403
     * Change a value for objects with a certain criteria.
404
     *
405
     * @param string                $fieldname  Name of the field
406
     * @param string|array          $fieldvalue Value to write
407
     * @param \CriteriaElement|null $criteria   {@link CriteriaElement}
408
     * @param bool                  $force
409
     *
410
     * @return bool
411
     */
412
    public function updateAll($fieldname, $fieldvalue, \CriteriaElement $criteria = null, $force = false)
413
    {
414
        $setClause = $fieldname . ' = ';
415
        if (\is_numeric($fieldvalue)) {
416
            $setClause .= $fieldvalue;
417
        } elseif (\is_array($fieldvalue)) {
418
            $setClause .= $this->db->quoteString(\implode(',', $fieldvalue));
419
        } else {
420
            $setClause .= $this->db->quoteString($fieldvalue);
421
        }
422
        $sql = 'UPDATE ' . $this->table . ' SET ' . $setClause;
423
        if (\is_object($criteria) && is_subclass_of($criteria,  \CriteriaElement::class)) {
424
            $sql .= ' ' . $criteria->renderWhere();
425
        }
426
        if (false !== $force) {
427
            $result = $this->db->queryF($sql);
428
        } else {
429
            $result = $this->db->query($sql);
430
        }
431
        if (!$result) {
432
            return false;
433
        }
434
435
        return true;
436
    }
437
438
    /**
439
     * @param      $fieldname
440
     * @param      $fieldvalue
441
     * @param null $criteria
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $criteria is correct as it would always require null to be passed?
Loading history...
442
     * @param bool $force
443
     *
444
     * @return bool
445
     */
446
    public function updateFieldValue($fieldname, $fieldvalue, $criteria = null, $force = true)
447
    {
448
        $sql = 'UPDATE ' . $this->table . ' SET ' . $fieldname . ' = ' . $fieldvalue;
449
        if (\is_object($criteria) && is_subclass_of($criteria,  \CriteriaElement::class)) {
450
            $sql .= ' ' . $criteria->renderWhere();
451
        }
452
        if (false !== $force) {
453
            $result = $this->db->queryF($sql);
454
        } else {
455
            $result = $this->db->query($sql);
456
        }
457
        if (!$result) {
458
            return false;
459
        }
460
461
        return true;
462
    }
463
464
    /**
465
     * delete all objects meeting the conditions.
466
     *
467
     * @param \CriteriaElement|null $criteria  {@link \CriteriaCompo|\CriteriaElement}
468
     *                                         with conditions to meet
469
     * @param bool                  $force
470
     * @param bool                  $asObject
471
     * @return bool
472
     */
473
    public function deleteAll(\CriteriaElement $criteria = null, $force = true, $asObject = false)
474
    {
475
        if (\is_object($criteria) && is_subclass_of($criteria,  \CriteriaElement::class)) {
476
            $sql = 'DELETE FROM ' . $this->table;
477
            $sql .= ' ' . $criteria->renderWhere();
478
            if (!$this->db->query($sql)) {
479
                return false;
480
            }
481
            $rows = $this->db->getAffectedRows();
482
483
            return $rows > 0 ? $rows : true;
484
        }
485
486
        return false;
487
    }
488
489
    /**
490
     * @param $data
491
     *
492
     * @return array|\XoopsObject
493
     */
494
    public function _toObject($data)
495
    {
496
        if (\is_array($data)) {
497
            $ret = [];
498
            foreach ($data as $v) {
499
                $object = new $this->className();
500
                $object->assignVars($v);
501
                $ret[] = $object;
502
            }
503
504
            return $ret;
505
        }
506
        $object = new $this->className();
507
        $object->assignVars($v);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $v seems to be never defined.
Loading history...
508
509
        return $object;
510
    }
511
512
    /**
513
     * @param        $objects
514
     * @param array  $externalKeys
515
     * @param string $format
516
     *
517
     * @return array
518
     */
519
    public function objectToArray($objects, $externalKeys = [], $format = 's')
520
    {
521
        static $cache;
522
        if (!\is_array($externalKeys)) {
0 ignored issues
show
The condition is_array($externalKeys) is always true.
Loading history...
523
            $externalKeys = [$externalKeys];
524
        } //JJD
525
526
        $ret = [];
527
        if (\is_array($objects)) {
528
            $i = 0;
529
            foreach ($objects as $object) {
530
                $vars = $object->getVars();
531
                foreach ($vars as $k => $v) {
532
                    $ret[$i][$k] = $object->getVar($k, $format);
533
                }
534
                foreach ($externalKeys as $key) {
535
                    // Replace external key by corresponding object
536
                    $externalKey = $object->getExternalKey($key);
537
                    if (0 != $ret[$i][$key]) {
538
                        // Retrieving data if isn't cached
539
                        if (!isset($cached[$externalKey['keyName']][$ret[$i][$key]])) {
540
                            if ($externalKey['core']) {
541
                                $handler = \xoops_getHandler($externalKey['className']);
542
                            } else {
543
                                $handler = Helper::getInstance()->getHandler($externalKey['className']);
544
                            }
545
                            $getMethod                                       = $externalKey['getMethodeName'];
546
                            $cached[$externalKey['keyName']][$ret[$i][$key]] = $this->objectToArrayWithoutExternalKey($handler->$getMethod($ret[$i][$key], true), $format);
547
                        }
548
                        $ret[$i][$externalKey['keyName']] = $cached[$externalKey['keyName']][$ret[$i][$key]];
549
                    }
550
                    unset($ret[$i][$key]);
551
                }
552
                ++$i;
553
            }
554
        } else {
555
            $vars = $objects->getVars();
556
            foreach ($vars as $k => $v) {
557
                $ret[$k] = $objects->getVar($k, $format);
558
            }
559
            foreach ($externalKeys as $key) {
560
                // Replace external key by corresponding object
561
                $externalKey = $objects->getExternalKey($key);
562
                if (0 != $ret[$key]) {
563
                    // Retriving data if isn't cached
564
                    if (!isset($cached[$externalKey['keyName']][$ret[$key]])) {
565
                        if ($externalKey['core']) {
566
                            $handler = \xoops_getHandler($externalKey['className']);
567
                        } else {
568
                            $handler = Helper::getInstance()->getHandler($externalKey['className']);
569
                        }
570
                        $getMethod                                   = $externalKey['getMethodeName'];
571
                        $cached[$externalKey['keyName']][$ret[$key]] = $this->objectToArrayWithoutExternalKey($handler->$getMethod($ret[$key], true), $format);
572
                    }
573
                    $ret[$externalKey['keyName']] = $cached[$externalKey['keyName']][$ret[$key]];
574
                }
575
                unset($ret[$key]);
576
            }
577
        }
578
579
        return $ret;
580
    }
581
582
    /**
583
     * @param        $object
584
     * @param string $format
585
     *
586
     * @return array
587
     */
588
    public function objectToArrayWithoutExternalKey($object, $format = 's')
589
    {
590
        $ret = [];
591
        if (!empty($object)) {
592
            $vars = $object->getVars();
593
            foreach ($vars as $k => $v) {
594
                $ret[$k] = $object->getVar($k, $format);
595
            }
596
        }
597
598
        return $ret;
599
    }
600
601
    /**
602
     * @param        $fieldname
603
     * @param        $criteria
604
     * @param string $op
605
     *
606
     * @return bool
607
     */
608
    public function updateCounter($fieldname, $criteria, $op = '+')
609
    {
610
        $sql    = 'UPDATE ' . $this->table . ' SET ' . $fieldname . ' = ' . $fieldname . $op . '1';
611
        $sql    .= ' ' . $criteria->renderWhere();
612
        $result = $this->db->queryF($sql);
613
        if (!$result) {
614
            return false;
615
        }
616
617
        return true;
618
    }
619
620
    /**
621
     * @param \CriteriaCompo|null $criteria
622
     * @param string              $sum
623
     *
624
     * @return array|string
625
     */
626
    public function getSum(\CriteriaCompo $criteria = null, $sum = '*')
627
    {
628
        $field   = '';
629
        $groupby = false;
630
        if (isset($criteria) && null !== $criteria) {
631
            if ('' != $criteria->groupby) {
632
                $groupby = true;
633
                $field   = $criteria->groupby . ', '; //Not entirely secure unless you KNOW that no criteria's groupby clause is going to be mis-used
634
            }
635
        }
636
        $sql = 'SELECT ' . $field . "SUM($sum) FROM " . $this->table;
637
        if (\is_object($criteria) && is_subclass_of($criteria,  \CriteriaElement::class)) {
638
            $sql .= ' ' . $criteria->renderWhere();
639
            if ('' != $criteria->groupby) {
640
                $sql .= $criteria->getGroupby();
641
            }
642
        }
643
        $result = $this->db->query($sql);
644
        if (!$result) {
645
            return 0;
646
        }
647
        if (false === $groupby) {
648
            [$sum] = $this->db->fetchRow($result);
649
650
            return $sum;
651
        }
652
        $ret = [];
653
        while (list($id, $sum) = $this->db->fetchRow($result)) {
654
            $ret[$id] = $sum;
655
        }
656
657
        return $ret;
658
    }
659
660
    /**
661
     * @param \CriteriaCompo|null $criteria
662
     * @param string              $max
663
     *
664
     * @return array|string
665
     */
666
    public function getMax(\CriteriaCompo $criteria = null, $max = '*')
667
    {
668
        $field   = '';
669
        $groupby = false;
670
        if (isset($criteria) && null !== $criteria) {
671
            if ('' != $criteria->groupby) {
672
                $groupby = true;
673
                $field   = $criteria->groupby . ', '; //Not entirely secure unless you KNOW that no criteria's groupby clause is going to be mis-used
674
            }
675
        }
676
        $sql = 'SELECT ' . $field . "MAX($max) FROM " . $this->table;
677
        if (\is_object($criteria) && is_subclass_of($criteria,  \CriteriaElement::class)) {
678
            $sql .= ' ' . $criteria->renderWhere();
679
            if ('' != $criteria->groupby) {
680
                $sql .= $criteria->getGroupby();
681
            }
682
        }
683
        $result = $this->db->query($sql);
684
        if (!$result) {
685
            return 0;
686
        }
687
        if (false === $groupby) {
688
            [$max] = $this->db->fetchRow($result);
689
690
            return $max;
691
        }
692
        $ret = [];
693
        while (list($id, $max) = $this->db->fetchRow($result)) {
694
            $ret[$id] = $max;
695
        }
696
697
        return $ret;
698
    }
699
700
    /**
701
     * @param null|\CriteriaCompo $criteria
702
     * @param string              $avg
703
     *
704
     * @return int
705
     */
706
    public function getAvg(\CriteriaCompo $criteria = null, $avg = '*')
707
    {
708
        $field = '';
709
710
        $sql = 'SELECT ' . $field . "AVG($avg) FROM " . $this->table;
711
        if (\is_object($criteria) && is_subclass_of($criteria,  \CriteriaElement::class)) {
712
            $sql .= ' ' . $criteria->renderWhere();
713
        }
714
        $result = $this->db->query($sql);
715
        if (!$result) {
716
            return 0;
717
        }
718
        [$sum] = $this->db->fetchRow($result);
719
720
        return $sum;
721
    }
722
723
    /**
724
     * @return mixed
725
     */
726
    public function getInsertId()
727
    {
728
        return $this->db->getInsertId();
729
    }
730
}
731