Completed
Push — master ( 286460...c262d8 )
by Maxim
02:09
created

DocLister::renderWrap()   B

Complexity

Conditions 11
Paths 13

Size

Total Lines 42
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
eloc 28
c 0
b 0
f 0
dl 0
loc 42
rs 7.3166
ccs 0
cts 34
cp 0
cc 11
nc 13
nop 1
crap 132

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * DocLister class
4
 *
5
 * @license GNU General Public License (GPL), http://www.gnu.org/copyleft/gpl.html
6
 * @author Agel_Nash <[email protected]>
7
 */
8 1
include_once(MODX_BASE_PATH . 'assets/lib/APIHelpers.class.php');
9 1
include_once(MODX_BASE_PATH . 'assets/lib/Helpers/FS.php');
10 1
include_once(MODX_BASE_PATH . 'assets/lib/Helpers/Config.php');
11 1
require_once(dirname(dirname(__FILE__)) . "/lib/jsonHelper.class.php");
12 1
require_once(dirname(dirname(__FILE__)) . "/lib/sqlHelper.class.php");
13 1
require_once(dirname(dirname(__FILE__)) . "/lib/DLTemplate.class.php");
14 1
require_once(dirname(dirname(__FILE__)) . "/lib/DLCollection.class.php");
15 1
require_once(dirname(dirname(__FILE__)) . "/lib/xnop.class.php");
16
17
/**
18
 * Class DocLister
19
 */
20
abstract class DocLister
21
{
22
    /**
23
     * Ключ в массиве $_REQUEST в котором находится алиас запрашиваемого документа
24
     */
25
    const AliasRequest = 'q';
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected ALIASREQUEST).
Loading history...
26
    /**
27
     * Массив документов полученный в результате выборки из базы
28
     * @var array
29
     * @access protected
30
     */
31
    protected $_docs = array();
32
33
    /**
34
     * Массив документов self::$_docs собранный в виде дерева
35
     * @var array
36
     * @access protected
37
     */
38
    protected $_tree = array();
39
40
    /**
41
     * @var
42
     * @access protected
43
     */
44
    protected $IDs = 0;
45
46
    /**
47
     * Объект DocumentParser - основной класс MODX'а
48
     * @var DocumentParser
49
     * @access protected
50
     */
51
    protected $modx = null;
52
53
    /**
54
     * Шаблонизатор чанков
55
     * @var DLTemplate
56
     * @access protected
57
     */
58
    protected $DLTemplate = null;
59
60
    /**
61
     * Массив загруженных экстендеров
62
     * @var array
63
     * @access protected
64
     */
65
    protected $extender = array();
66
67
    /**
68
     * Массив плейсхолдеров доступных в шаблоне
69
     * @var array
70
     * @access protected
71
     */
72
    protected $_plh = array();
73
74
    /**
75
     * Языковой пакет
76
     * @var array
77
     * @access protected
78
     */
79
    protected $_lang = array();
80
81
    /**
82
     * Пользовательский языковой пакет
83
     * @var array
84
     * @access protected
85
     */
86
    protected $_customLang = array();
87
88
    /**
89
     * Список таблиц уже с префиксами MODX
90
     * @var array
91
     * @access private
92
     */
93
    private $_table = array();
94
95
    /**
96
     * PrimaryKey основной таблицы
97
     * @var string
98
     * @access protected
99
     */
100
    protected $idField = 'id';
101
102
    /**
103
     * Parent Key основной таблицы
104
     * @var string
105
     * @access protected
106
     */
107
    protected $parentField = 'parent';
108
109
    /**
110
     * Дополнительные условия для SQL запросов
111
     * @var array
112
     * @access protected
113
     */
114
    protected $_filters = array('where' => '', 'join' => '');
115
116
    /**
117
     * Список доступных логических операторов для фильтрации
118
     * @var array
119
     * @access protected
120
     */
121
    protected $_logic_ops = array('AND' => ' AND ', 'OR' => ' OR '); // logic operators currently supported
122
123
    /**
124
     * Режим отладки
125
     * @var int
126
     * @access private
127
     */
128
    private $_debugMode = 0;
129
130
    /**
131
     * Отладчик
132
     *
133
     * @var DLdebug|xNop
134
     * @access public
135
     */
136
    public $debug = null;
137
138
    /**
139
     * Массив дополнительно подключаемых таблиц с псевдонимами
140
     * @var array
141
     */
142
    public $AddTable = array();
143
144
    /**
145
     * Время запуска сниппета
146
     * @var int
147
     */
148
    private $_timeStart = 0;
149
150
    /**
151
     * Номер фильтра в общем списке фильтров
152
     * @var int
153
     * @access protected
154
     */
155
    protected $totalFilters = 0;
156
157
    /** @var string имя шаблона для вывода записи */
158
    public $renderTPL = '';
159
160
    /** @var string имя шаблона обертки для записей */
161
    public $ownerTPL = '';
162
163
    public $FS = null;
164
    /** @var string результатирующая строка которая была последний раз сгенирирована
165
     *               вызовами методов DocLister::render и DocLister::getJSON
166
     */
167
    protected $outData = '';
168
169
    /** @var int Число документов, которые были отфильтрованы через prepare при выводе */
170
    public $skippedDocs = 0;
171
172
    /** @var string Имя таблицы */
173
    protected $table = '';
174
    /** @var string alias таблицы */
175
    protected $alias = '';
176
177
    /** @var null|paginate_DL_Extender */
178
    protected $extPaginate = null;
179
180
    /** @var null|Helpers\Config */
181
    public $config = null;
182
183
    /**
184
     * Конструктор контроллеров DocLister
185
     *
186
     * @param DocumentParser $modx объект DocumentParser - основной класс MODX
187
     * @param array $cfg массив параметров сниппета
188
     * @param int $startTime время запуска сниппета
189
     * @throws Exception
190
     */
191 48
    public function __construct($modx, $cfg = array(), $startTime = null)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (4) exceeds 3; consider refactoring the function
Loading history...
192
    {
193 48
        $this->setTimeStart($startTime);
194
195 48
        if (extension_loaded('mbstring')) {
196 48
            mb_internal_encoding("UTF-8");
197 48
        } else {
198
            throw new Exception('Not found php extension mbstring');
199
        }
200
201 48
        if ($modx instanceof DocumentParser) {
202
            $this->modx = $modx;
203
            $this->setDebug(1);
204
205
            if (!is_array($cfg) || empty($cfg)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
introduced by
The condition is_array($cfg) is always true.
Loading history...
206
                $cfg = $this->modx->Event->params;
207
            }
208
        } else {
209 48
            throw new Exception('MODX var is not instaceof DocumentParser');
210
        }
211
212
        $this->FS = \Helpers\FS::getInstance();
213
        $this->config = new \Helpers\Config($cfg);
214
215
        if (isset($cfg['config'])) {
216
            $this->config->setPath(dirname(__DIR__))->loadConfig($cfg['config']);
217
        }
218
219
        if ($this->config->setConfig($cfg) === false) {
0 ignored issues
show
introduced by
The condition $this->config->setConfig($cfg) === false is always false.
Loading history...
220
            throw new Exception('no parameters to run DocLister');
221
        }
222
223
        $this->loadLang(array('core', 'json'));
224
        $this->setDebug($this->getCFGDef('debug', 0));
225
226
        if ($this->checkDL()) {
227
            $cfg = array();
228
            $idType = $this->getCFGDef('idType', '');
229
            if (empty($idType) && $this->getCFGDef('documents', '') != '') {
230
                $idType = 'documents';
231
            }
232
            switch ($idType) {
233
                case 'documents':
234
                    $IDs = $this->getCFGDef('documents');
235
                    $cfg['idType'] = "documents";
236
                    break;
237
                case 'parents':
238
                default:
239
                    $cfg['idType'] = "parents";
240
                    if (($IDs = $this->getCFGDef('parents', '')) === '') {
241
                        $IDs = $this->getCurrentMODXPageID();
242
                    }
243
                    break;
244
            }
245
            $this->config->setConfig($cfg);
246
            $this->alias = empty($this->alias) ? $this->getCFGDef(
247
                'tableAlias',
248
                'c'
249
            ) : $this->alias;
250
            $this->table = $this->getTable(empty($this->table) ? $this->getCFGDef(
251
                'table',
252
                'site_content'
253
            ) : $this->table, $this->alias);
254
255
            $this->idField = $this->getCFGDef('idField', 'id');
256
            $this->parentField = $this->getCFGDef('parentField', 'parent');
257
            $this->extCache = $this->getExtender('cache', true);
0 ignored issues
show
Bug Best Practice introduced by
The property extCache does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
258
            $this->extCache->init($this, array(
0 ignored issues
show
Bug introduced by
The method init() does not exist on null. ( Ignorable by Annotation )

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

258
            $this->extCache->/** @scrutinizer ignore-call */ 
259
                             init($this, array(

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...
Bug introduced by
The method init() does not exist on xNop. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

258
            $this->extCache->/** @scrutinizer ignore-call */ 
259
                             init($this, array(
Loading history...
259
                'cache'         => $this->getCFGDef('cache', 1),
260
                'cacheKey'      => $this->getCFGDef('cacheKey'),
261
                'cacheLifetime' => $this->getCFGDef('cacheLifetime', 0),
262
                'cacheStrategy' => $this->getCFGDef('cacheStrategy')
263
            ));
264
            $this->setIDs($IDs);
265
        }
266
267
        $this->setLocate();
268
269
        if ($this->getCFGDef("customLang")) {
270
            $this->getCustomLang();
271
        }
272
        $this->loadExtender($this->getCFGDef("extender", ""));
273
274
        if ($this->checkExtender('request')) {
275
            $this->extender['request']->init($this, $this->getCFGDef("requestActive", ""));
276
        }
277
        $this->_filters = $this->getFilters($this->getCFGDef('filters', ''));
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getFilters($this-...tCFGDef('filters', '')) can also be of type false. However, the property $_filters is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
278
        $this->ownerTPL = $this->getCFGDef("ownerTPL", "");
279
        $DLTemplate = DLTemplate::getInstance($modx);
280
        if ($path = $this->getCFGDef('templatePath')) {
281
            $DLTemplate->setTemplatePath($path);
282
        }
283
        if ($ext = $this->getCFGDef('templateExtension')) {
284
            $DLTemplate->setTemplateExtension($ext);
285
        }
286
        $this->DLTemplate = $DLTemplate->setTemplateData(array('DocLister' => $this));
287
    }
288
289
    /**
290
     * Разбиение фильтра на субфильтры с учётом вложенности
291
     * @param string $str строка с фильтром
292
     * @return array массив субфильтров
293
     */
294
    public function smartSplit($str)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (4) exceeds 3; consider refactoring the function
Loading history...
295
    {
296
        $res = array();
297
        $cur = '';
298
        $open = 0;
299
        $strlen = mb_strlen($str, 'UTF-8');
300
        for ($i = 0; $i <= $strlen; $i++) {
301
            $e = mb_substr($str, $i, 1, 'UTF-8');
302
            switch ($e) {
303
                case '\\':
304
                    $cur .= $e;
305
                    $cur .= mb_substr($str, ++$i, 1, 'UTF-8');
306
                    break;
307
                case ')':
308
                    $open--;
309
                    if ($open == 0) {
310
                        $res[] = $cur . ')';
311
                        $cur = '';
312
                    } else {
313
                        $cur .= $e;
314
                    }
315
                    break;
316
                case '(':
317
                    $open++;
318
                    $cur .= $e;
319
                    break;
320
                case ';':
321
                    if ($open == 0) {
322
                        $res[] = $cur;
323
                        $cur = '';
324
                    } else {
325
                        $cur .= $e;
326
                    }
327
                    break;
328
                default:
329
                    $cur .= $e;
330
            }
331
        }
332
        $cur = preg_replace("/(\))$/u", '', $cur);
333
        if ($cur != '') {
334
            $res[] = $cur;
335
        }
336
337
        return $res;
338
    }
339
340
    /**
341
     * Трансформация объекта в строку
342
     * @return string последний ответ от DocLister'а
343
     */
344
    public function __toString()
345
    {
346
        return $this->outData;
347
    }
348
349
    /**
350
     * Установить время запуска сниппета
351
     * @param float|null $time
352
     */
353 48
    public function setTimeStart($time = null)
354
    {
355 48
        $this->_timeStart = is_null($time) ? microtime(true) : $time;
356 48
    }
357
358
    /**
359
     * Время запуска сниппета
360
     *
361
     * @return int
362
     */
363
    public function getTimeStart()
364
    {
365
        return $this->_timeStart;
366
    }
367
368
    /**
369
     * Установка режима отладки
370
     * @param int $flag режим отладки
371
     */
372
    public function setDebug($flag = 0)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (4) exceeds 3; consider refactoring the function
Loading history...
373
    {
374
        $flag = abs((int)$flag);
375
        if ($this->_debugMode != $flag) {
376
            $this->_debugMode = $flag;
377
            $this->debug = null;
378
            if ($this->_debugMode > 0) {
379
                if (isset($_SESSION['usertype']) && $_SESSION['usertype'] == 'manager') {
380
                    error_reporting(E_ALL ^ E_NOTICE);
381
                    ini_set('display_errors', 1);
382
                }
383
                $dir = dirname(dirname(__FILE__));
384
                if (file_exists($dir . "/lib/DLdebug.class.php")) {
385
                    include_once($dir . "/lib/DLdebug.class.php");
386
                    if (class_exists("DLdebug", false)) {
387
                        $this->debug = new DLdebug($this);
388
                    }
389
                }
390
            }
391
392
            if (is_null($this->debug)) {
393
                $this->debug = new xNop();
394
                $this->_debugMode = 0;
395
                error_reporting(0);
396
                ini_set('display_errors', 0);
397
            }
398
        }
399
    }
400
401
    /**
402
     * Информация о режиме отладки
403
     */
404
    public function getDebug()
405
    {
406
        return $this->_debugMode;
407
    }
408
409
    /**
410
     * Генерация имени таблицы с префиксом и алиасом
411
     *
412
     * @param string $name имя таблицы
413
     * @param string $alias желаемый алиас таблицы
414
     * @return string имя таблицы с префиксом и алиасом
415
     */
416
    public function getTable($name, $alias = '')
417
    {
418
        if (!isset($this->_table[$name])) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
419
            $this->_table[$name] = $this->modx->getFullTableName($name);
420
        }
421
        $table = $this->_table[$name];
422
        if (!empty($alias) && is_scalar($alias)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
423
            $table .= " as `" . $alias . "`";
424
        }
425
426
        return $table;
427
    }
428
429
    /**
430
     * @param $name
431
     * @param $table
432
     * @param $alias
433
     * @return mixed
434
     */
435
    public function TableAlias($name, $table, $alias)
0 ignored issues
show
Coding Style introduced by
Method name "DocLister::TableAlias" is not in camel caps format
Loading history...
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
436
    {
437
        if (!$this->checkTableAlias($name, $table)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
438
            $this->AddTable[$table][$name] = $alias;
439
        }
440
441
        return $this->AddTable[$table][$name];
442
    }
443
444
    /**
445
     * @param $name
446
     * @param $table
447
     * @return bool
448
     */
449
    public function checkTableAlias($name, $table)
450
    {
451
        return isset($this->AddTable[$table][$name]);
452
    }
453
454
    /**
455
     * Разбор JSON строки при помощи json_decode
456
     *
457
     * @param $json string строка c JSON
458
     * @param array $config ассоциативный массив с настройками для json_decode
459
     * @param bool $nop создавать ли пустой объект запрашиваемого типа
460
     * @return array|mixed|xNop
461
     */
462
    public function jsonDecode($json, $config = array(), $nop = false)
463
    {
464
        $this->debug->debug(
0 ignored issues
show
Bug introduced by
The method debug() does not exist on xNop. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

464
        $this->debug->/** @scrutinizer ignore-call */ 
465
                      debug(
Loading history...
465
            'Decode JSON: ' . $this->debug->dumpData($json) . "\r\nwith config: " . $this->debug->dumpData($config),
0 ignored issues
show
Bug introduced by
The method dumpData() does not exist on xNop. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

465
            'Decode JSON: ' . $this->debug->/** @scrutinizer ignore-call */ dumpData($json) . "\r\nwith config: " . $this->debug->dumpData($config),
Loading history...
466
            'jsonDecode',
467
            2
468
        );
469
        $config = jsonHelper::jsonDecode($json, $config, $nop);
470
        $this->isErrorJSON($json);
471
        $this->debug->debugEnd("jsonDecode");
0 ignored issues
show
Bug introduced by
The method debugEnd() does not exist on xNop. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

471
        $this->debug->/** @scrutinizer ignore-call */ 
472
                      debugEnd("jsonDecode");
Loading history...
472
473
        return $config;
474
    }
475
476
    /**
477
     * Были ли ошибки во время работы с JSON
478
     *
479
     * @param $json string строка с JSON для записи в лог при отладке
480
     * @return bool|string
481
     */
482
    public function isErrorJSON($json)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
483
    {
484
        $error = jsonHelper::json_last_error_msg();
485
        if (!in_array($error, array('error_none', 'other'))) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
486
            $this->debug->error($this->getMsg('json.' . $error) . ": " . $this->debug->dumpData($json, 'code'), 'JSON');
0 ignored issues
show
Bug introduced by
The method error() does not exist on xNop. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

486
            $this->debug->/** @scrutinizer ignore-call */ 
487
                          error($this->getMsg('json.' . $error) . ": " . $this->debug->dumpData($json, 'code'), 'JSON');
Loading history...
487
            $error = true;
488
        }
489
490
        return $error;
491
    }
492
493
    /**
494
     * Проверка параметров и загрузка необходимых экстендеров
495
     * return boolean статус загрузки
496
     */
497
    public function checkDL()
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
498
    {
499
        $this->debug->debug('Check DocLister parameters', 'checkDL', 2);
500
        $flag = true;
501
        $extenders = $this->getCFGDef('extender', '');
502
        $extenders = explode(",", $extenders);
503
        $tmp = $this->getCFGDef('requestActive', '') != '' || in_array('request', $extenders);
504
        if ($tmp && !$this->_loadExtender('request')) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
505
            //OR request in extender's parameter
506
            throw new Exception('Error load request extender');
507
        }
508
509
        $tmp = $this->getCFGDef('summary', '') != '' || in_array('summary', $extenders);
510
        if ($tmp && !$this->_loadExtender('summary')) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
511
            //OR summary in extender's parameter
512
            throw new Exception('Error load summary extender');
513
        }
514
515
        if ((int)$this->getCFGDef('display', 0) > 0 && ( //OR paginate in extender's parameter
516
                in_array('paginate', $extenders) || $this->getCFGDef('paginate', '') != '' ||
517
                $this->getCFGDef('TplPrevP', '') != '' || $this->getCFGDef('TplPage', '') != '' ||
518
                $this->getCFGDef('TplCurrentPage', '') != '' || $this->getCFGDef('TplWrapPaginate', '') != '' ||
519
                $this->getCFGDef('pageLimit', '') != '' || $this->getCFGDef('pageAdjacents', '') != '' ||
520
                $this->getCFGDef('PaginateClass', '') != '' || $this->getCFGDef('TplNextP', '') != ''
521
            ) && !$this->_loadExtender('paginate')
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
522
        ) {
523
            throw new Exception('Error load paginate extender');
524
        } else {
525
            if ((int)$this->getCFGDef('display', 0) == 0) {
526
                $extenders = $this->unsetArrayVal($extenders, 'paginate');
527
            }
528
        }
529
530
        if ($this->getCFGDef('prepare', '') != '' || $this->getCFGDef('prepareWrap') != '') {
531
            $this->_loadExtender('prepare');
532
        }
533
534
        $this->config->setConfig(array('extender' => implode(",", $extenders)));
0 ignored issues
show
Bug introduced by
The method setConfig() does not exist on null. ( Ignorable by Annotation )

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

534
        $this->config->/** @scrutinizer ignore-call */ 
535
                       setConfig(array('extender' => implode(",", $extenders)));

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...
535
        $this->debug->debugEnd("checkDL");
536
537
        return $flag;
538
    }
539
540
    /**
541
     * Удаление определенных данных из массива
542
     *
543
     * @param array $data массив с данными
544
     * @param mixed $val значение которые необходимо удалить из массива
545
     * @return array отчищеный массив с данными
546
     */
547
    private function unsetArrayVal($data, $val)
548
    {
549
        $out = array();
550
        if (is_array($data)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always true.
Loading history...
551
            foreach ($data as $item) {
552
                if ($item != $val) {
553
                    $out[] = $item;
554
                } else {
555
                    continue;
556
                }
557
            }
558
        }
559
560
        return $out;
561
    }
562
563
    /**
564
     * Генерация URL страницы
565
     *
566
     * @param int $id уникальный идентификатор страницы
567
     * @return string URL страницы
568
     */
569
    public function getUrl($id = 0)
570
    {
571
        $id = ((int)$id > 0) ? (int)$id : $this->getCurrentMODXPageID();
572
573
        $link = $this->checkExtender('request') ? $this->extender['request']->getLink() : $this->getRequest();
574
        if ($id == $this->modx->config['site_start']) {
575
            $url = $this->modx->config['site_url'] . ($link != '' ? "?{$link}" : "");
576
        } else {
577
            $url = $this->modx->makeUrl($id, '', $link, $this->getCFGDef('urlScheme', ''));
578
        }
579
580
        return $url;
581
    }
582
583
    /**
584
     * Получение массива документов из базы
585
     * @param mixed $tvlist дополнительные параметры выборки
586
     * @return array Массив документов выбранных из базы
587
     */
588
    abstract public function getDocs($tvlist = '');
589
590
    /**
591
     * Подготовка результатов к отображению.
592
     *
593
     * @param string $tpl шаблон
594
     * @return mixed подготовленный к отображению результат выборки
595
     */
596
    abstract public function _render($tpl = '');
597
598
    /**
599
     * Подготовка результатов к отображению в соответствии с настройками
600
     *
601
     * @param string $tpl шаблон
602
     * @return string
603
     */
604
    public function render($tpl = '')
605
    {
606
        $this->debug->debug(array('Render data with template ' => $tpl), 'render', 2, array('html'));
607
        $out = '';
608
        if (1 == $this->getCFGDef('tree', '0')) {
609
            foreach ($this->_tree as $item) {
610
                $out .= $this->renderTree($item);
611
            }
612
            $out = $this->renderWrap($out);
613
        } else {
614
            $out = $this->_render($tpl);
615
        }
616
617
        if ($out) {
618
            $this->outData = DLTemplate::getInstance($this->modx)->parseDocumentSource($out);
619
        }
620
        $this->debug->debugEnd('render');
621
622
        return $this->outData;
623
    }
624
625
    /***************************************************
626
     ****************** CORE Block *********************
627
     ***************************************************/
628
629
    /**
630
     * Определение ID страницы открытой во фронте
631
     *
632
     * @return int
633
     */
634
    public function getCurrentMODXPageID()
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
635
    {
636
        $id = isset($this->modx->documentIdentifier) ? (int)$this->modx->documentIdentifier : 0;
637
        $docData = isset($this->modx->documentObject) ? $this->modx->documentObject : array();
638
639
        return empty($id) ? \APIHelpers::getkey($docData, 'id', 0) : $id;
640
    }
641
642
    /**
643
     * Display and save error information
644
     *
645
     * @param string $message error message
646
     * @param integer $code error number
647
     * @param string $file error on file
648
     * @param integer $line error on line
649
     * @param array $trace stack trace
650
     */
651
    public function ErrorLogger($message, $code, $file, $line, $trace)
0 ignored issues
show
Coding Style introduced by
Method name "DocLister::ErrorLogger" is not in camel caps format
Loading history...
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
652
    {
653
        if (abs($this->getCFGDef('debug', '0')) == '1') {
654
            $out = "CODE #" . $code . "<br />";
655
            $out .= "on file: " . $file . ":" . $line . "<br />";
656
            $out .= "<pre>";
657
            $out .= print_r($trace, 1);
658
            $out .= "</pre>";
659
660
            $message = $out . $message;
661
        }
662
        die($message);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
663
    }
664
665
    /**
666
     * Получение объекта DocumentParser
667
     *
668
     * @return DocumentParser
669
     */
670
    public function getMODX()
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
671
    {
672
        return $this->modx;
673
    }
674
675
    /**
676
     * load extenders
677
     *
678
     * @param string $ext name extender separated by ,
679
     * @return boolean status load extenders
680
     * @throws Exception
681
     */
682
    public function loadExtender($ext = '')
683
    {
684
        $out = true;
685
        if ($ext != '') {
686
            $ext = explode(",", $ext);
687
            foreach ($ext as $item) {
688
                if ($item != '' && !$this->_loadExtender($item)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
689
                    throw new Exception('Error load ' . APIHelpers::e($item) . ' extender');
690
                }
691
            }
692
        }
693
694
        return $out;
695
    }
696
697
    /**
698
     * Получение информации из конфига
699
     *
700
     * @param string $name имя параметра в конфиге
701
     * @param mixed $def значение по умолчанию, если в конфиге нет искомого параметра
702
     * @return mixed значение из конфига
703
     */
704
    public function getCFGDef($name, $def = null)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
705
    {
706
        return $this->config->getCFGDef($name, $def);
707
    }
708
709
    /**
710
     * Сохранение данных в массив плейсхолдеров
711
     *
712
     * @param mixed $data данные
713
     * @param int $set устанавливать ли глобальнй плейсхолдер MODX
714
     * @param string $key ключ локального плейсхолдера
715
     * @return string
716
     */
717
    public function toPlaceholders($data, $set = 0, $key = 'contentPlaceholder')
718
    {
719
        $this->debug->debug(null, 'toPlaceholders', 2);
720
        if ($set == 0) {
721
            $set = $this->getCFGDef('contentPlaceholder', 0);
722
        }
723
        $this->_plh[$key] = $data;
724
        $id = $this->getCFGDef('id', '');
725
        if ($id != '') {
726
            $id .= ".";
727
        }
728
        $out = DLTemplate::getInstance($this->getMODX())->toPlaceholders($data, $set, $key, $id);
729
730
        $this->debug->debugEnd(
731
            "toPlaceholders",
732
            array($key . " placeholder" => $data),
733
            array('html')
734
        );
735
736
        return $out;
737
    }
738
739
    /**
740
     * Предварительная обработка данных перед вставкой в SQL запрос вида IN
741
     * Если данные в виде строки, то происходит попытка сформировать массив из этой строки по разделителю $sep
742
     * Точно по тому, по которому потом данные будут собраны обратно
743
     *
744
     * @param integer|string|array $data данные для обработки
745
     * @param string $sep разделитель
746
     * @param boolean $quote заключать ли данные на выходе в кавычки
747
     * @return string обработанная строка
748
     */
749
    public function sanitarIn($data, $sep = ',', $quote = true)
750
    {
751
        if (is_scalar($data)) {
752
            $data = explode($sep, $data);
753
        }
754
        if (!is_array($data)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
introduced by
The condition is_array($data) is always true.
Loading history...
755
            $data = array(); //@TODO: throw
756
        }
757
758
        $out = array();
759
        foreach ($data as $item) {
760
            if ($item !== '') {
761
                $out[] = $this->modx->db->escape($item);
762
            }
763
        }
764
        $q = $quote ? "'" : "";
765
        $out = $q . implode($q . "," . $q, $out) . $q;
766
767
        return $out;
768
    }
769
770
    /**
771
     * Загрузка кастомного лексикона
772
     *
773
     * В файле с кастомным лексиконом ключи в массиве дожны быть полные
774
     * Например:
775
     *      - core.bla-bla
776
     *      - paginate.next
777
     *
778
     * @param string $lang имя языкового пакета
779
     * @return array
780
     */
781
    public function getCustomLang($lang = '')
782
    {
783
        if (empty($lang)) {
784
            $lang = $this->getCFGDef('lang', $this->modx->config['manager_language']);
785
        }
786
        if (file_exists(dirname(dirname(__FILE__)) . "/lang/" . $lang . ".php")) {
787
            $tmp = include(dirname(__FILE__) . "/lang/" . $lang . ".php");
788
            $this->_customLang = is_array($tmp) ? $tmp : array();
789
        }
790
791
        return $this->_customLang;
792
    }
793
794
    /**
795
     * Загрузка языкового пакета
796
     *
797
     * @param array|string $name ключ языкового пакета
798
     * @param string $lang имя языкового пакета
799
     * @param boolean $rename Переименовывать ли элементы массива
800
     * @return array массив с лексиконом
801
     */
802
    public function loadLang($name = 'core', $lang = '', $rename = true)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (4) exceeds 3; consider refactoring the function
Loading history...
803
    {
804
        if (empty($lang)) {
805
            $lang = $this->getCFGDef('lang', $this->modx->config['manager_language']);
806
        }
807
808
        $this->debug->debug(
809
            'Load language ' . $this->debug->dumpData($name) . "." . $this->debug->dumpData($lang),
810
            'loadlang',
811
            2
812
        );
813
        if (is_scalar($name)) {
814
            $name = array($name);
815
        }
816
        foreach ($name as $n) {
817
            if (file_exists(dirname(__FILE__) . "/lang/" . $lang . "/" . $n . ".inc.php")) {
818
                $tmp = include(dirname(__FILE__) . "/lang/" . $lang . "/" . $n . ".inc.php");
819
                if (is_array($tmp)) {
820
                    /**
821
                     * Переименовыываем элементы массива из array('test'=>'data') в array('name.test'=>'data')
822
                     */
823
                    if ($rename) {
824
                        $tmp = $this->renameKeyArr($tmp, $n, '', '.');
825
                    }
826
                    $this->_lang = array_merge($this->_lang, $tmp);
827
                }
828
            }
829
        }
830
        $this->debug->debugEnd("loadlang");
831
832
        return $this->_lang;
833
    }
834
835
    /**
836
     * Получение строки из языкового пакета
837
     *
838
     * @param string $name имя записи в языковом пакете
839
     * @param string $def Строка по умолчанию, если запись в языковом пакете не будет обнаружена
840
     * @return string строка в соответствии с текущими языковыми настройками
841
     */
842
    public function getMsg($name, $def = '')
843
    {
844
        if (isset($this->_customLang[$name])) {
845
            $say = $this->_customLang[$name];
846
        } else {
847
            $say = \APIHelpers::getkey($this->_lang, $name, $def);
848
        }
849
850
        return $say;
851
    }
852
853
    /**
854
     * Переменовывание элементов массива
855
     *
856
     * @param array $data массив с данными
857
     * @param string $prefix префикс ключей
858
     * @param string $suffix суффикс ключей
859
     * @param string $sep разделитель суффиксов, префиксов и ключей массива
860
     * @return array массив с переименованными ключами
861
     */
862
    public function renameKeyArr($data, $prefix = '', $suffix = '', $sep = '.')
863
    {
864
        return \APIHelpers::renameKeyArr($data, $prefix, $suffix, $sep);
865
    }
866
867
    /**
868
     * Установка локали
869
     *
870
     * @param string $locale локаль
871
     * @return string имя установленной локали
872
     */
873
    public function setLocate($locale = '')
874
    {
875
        if ('' == $locale) {
876
            $locale = $this->getCFGDef('locale', '');
877
        }
878
        if ('' != $locale) {
879
            setlocale(LC_ALL, $locale);
880
        }
881
882
        return $locale;
883
    }
884
885
    /**
886
     * Шаблонизация дерева.
887
     * Перевод из массива в HTML в соответствии с указанным шаблоном
888
     *
889
     * @param array $data массив сформированный как дерево
890
     * @return string строка для отображения пользователю
891
     */
892
    protected function renderTree($data)
893
    {
894
        $out = '';
895
        if (!empty($data['#childNodes'])) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
896
            foreach ($data['#childNodes'] as $item) {
897
                $out .= $this->renderTree($item);
898
            }
899
        }
900
901
        $data[$this->getCFGDef("sysKey", "dl") . ".wrap"] = $this->renderWrap($out);
902
        $out = $this->parseChunk($this->getCFGDef('tpl', ''), $data);
903
904
        return $out;
905
    }
906
907
    /**
908
     * refactor $modx->getChunk();
909
     *
910
     * @param string $name Template: chunk name || @CODE: template || @FILE: file with template
911
     * @return string html template with placeholders without data
912
     */
913
    private function _getChunk($name)
914
    {
915
        $this->debug->debug(array('Get chunk by name' => $name), "getChunk", 2, array('html'));
916
        //without trim
917
        $tpl = DLTemplate::getInstance($this->getMODX())->getChunk($name);
918
        $tpl = $this->parseLang($tpl);
919
920
        $this->debug->debugEnd("getChunk");
921
922
        return $tpl;
923
    }
924
925
    /**
926
     * Замена в шаблоне фраз из лексикона
927
     *
928
     * @param string $tpl HTML шаблон
929
     * @return string
930
     */
931
    public function parseLang($tpl)
932
    {
933
        $this->debug->debug(array("parseLang" => $tpl), "parseLang", 2, array('html'));
934
        if (is_scalar($tpl) && !empty($tpl)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
935
            if (preg_match_all("/\[\%([a-zA-Z0-9\.\_\-]+)\%\]/", $tpl, $match)) {
936
                $langVal = array();
937
                foreach ($match[1] as $item) {
938
                    $langVal[] = $this->getMsg($item);
939
                }
940
                $tpl = str_replace($match[0], $langVal, $tpl);
941
            }
942
        } else {
943
            $tpl = '';
944
        }
945
        $this->debug->debugEnd("parseLang");
946
947
        return $tpl;
948
    }
949
950
    /**
951
     * refactor $modx->parseChunk();
952
     *
953
     * @param string $name Template: chunk name || @CODE: template || @FILE: file with template
954
     * @param array $data paceholder
955
     * @param bool $parseDocumentSource render html template via DocumentParser::parseDocumentSource()
956
     * @return string html template with data without placeholders
957
     */
958
    public function parseChunk($name, $data = array(), $parseDocumentSource = false)
959
    {
960
        $this->debug->debug(
961
            array("parseChunk" => $name, "With data" => print_r($data, 1)),
962
            "parseChunk",
963
            2,
964
            array('html', null)
965
        );
966
        $disablePHx = $this->getCFGDef('disablePHx', 0);
967
        $out = $this->DLTemplate->parseChunk($name, $data, $parseDocumentSource, (bool)$disablePHx);
968
        $out = $this->parseLang($out);
969
        if (empty($out)) {
970
            $this->debug->debug("Empty chunk: " . $this->debug->dumpData($name), '', 2);
971
        }
972
        $this->debug->debugEnd("parseChunk");
973
974
        return $out;
975
    }
976
977
    /**
978
     * Get full template from parameter name
979
     *
980
     * @param string $name param name
981
     * @param string $val default value
982
     *
983
     * @return string html template from parameter
984
     */
985
    public function getChunkByParam($name, $val = '')
986
    {
987
        $data = $this->getCFGDef($name, $val);
988
        $data = $this->_getChunk($data);
989
990
        return $data;
991
    }
992
993
    /**
994
     * Помещение html кода в какой-то блок обертку
995
     *
996
     * @param string $data html код который нужно обернуть в ownerTPL
997
     * @return string результатирующий html код
998
     */
999
    public function renderWrap($data)
1000
    {
1001
        $out = $data;
1002
        $docs = count($this->_docs) - $this->skippedDocs;
1003
        if ((($this->getCFGDef("noneWrapOuter", "1") && $docs == 0) || $docs > 0) && !empty($this->ownerTPL) || !empty($this->getCFGDef('prepareWrap'))) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($this->getCFGDef('noneW...tCFGDef('prepareWrap')), Probably Intended Meaning: $this->getCFGDef('noneWr...CFGDef('prepareWrap')))
Loading history...
1004
            $this->debug->debug("", "renderWrapTPL", 2);
1005
            $parse = true;
1006
            $plh = array($this->getCFGDef("sysKey", "dl") . ".wrap" => $data);
1007
            /**
1008
             * @var $extPrepare prepare_DL_Extender
1009
             */
1010
            $extPrepare = $this->getExtender('prepare');
1011
            if ($extPrepare) {
0 ignored issues
show
introduced by
$extPrepare is of type prepare_DL_Extender, thus it always evaluated to true.
Loading history...
1012
                $params = $extPrepare->init($this, array(
1013
                    'data'      => array(
1014
                        'docs'         => $this->_docs,
1015
                        'placeholders' => $plh
1016
                    ),
1017
                    'nameParam' => 'prepareWrap',
1018
                    'return'    => 'placeholders'
1019
                ));
1020
                if ($params === false) {
1021
                    $out = $data;
1022
                    $parse = false;
1023
                }
1024
                $plh = $params;
1025
            }
1026
            if ($parse && !empty($this->ownerTPL)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1027
                $this->debug->updateMessage(
0 ignored issues
show
Bug introduced by
The method updateMessage() does not exist on xNop. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

1027
                $this->debug->/** @scrutinizer ignore-call */ 
1028
                              updateMessage(
Loading history...
1028
                    array("render ownerTPL" => $this->ownerTPL, "With data" => print_r($plh, 1)),
1029
                    "renderWrapTPL",
1030
                    array('html', null)
1031
                );
1032
                $out = $this->parseChunk($this->ownerTPL, $plh);
1033
            }
1034
            if (empty($this->ownerTPL)) {
1035
                $this->debug->updateMessage("empty ownerTPL", "renderWrapTPL");
1036
            }
1037
            $this->debug->debugEnd("renderWrapTPL");
1038
        }
1039
1040
        return $out;
1041
    }
1042
1043
    /**
1044
     * Единые обработки массива с данными о документе для всех контроллеров
1045
     *
1046
     * @param array $data массив с данными о текущем документе
1047
     * @param int $i номер итерации в цикле
1048
     * @return array массив с данными которые можно использовать в цикле render метода
1049
     */
1050
    protected function uniformPrepare(&$data, $i = 0)
1051
    {
1052
        $class = array();
1053
1054
        $iterationName = ($i % 2 == 1) ? 'Odd' : 'Even';
1055
        $tmp = strtolower($iterationName);
1056
        $class[] = $this->getCFGDef($tmp . 'Class', $tmp);
1057
1058
        $this->renderTPL = $this->getCFGDef('tplId' . $i, $this->renderTPL);
1059
        $this->renderTPL = $this->getCFGDef('tpl' . $iterationName, $this->renderTPL);
1060
        $iteration = $i;
1061
1062
        if ($this->extPaginate) {
1063
            $offset = $this->getCFGDef(
1064
                'reversePagination',
1065
                0
1066
            ) && $this->extPaginate->currentPage() > 1 ? $this->extPaginate->totalPage() * $this->getCFGDef(
1067
                'display',
1068
                0
1069
            ) - $this->extPaginate->totalDocs() : 0;
1070
            if ($this->getCFGDef('maxDocs', 0) && !$this->getCFGDef(
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1071
                'reversePagination',
1072
                0
1073
            ) && $this->extPaginate->currentPage() == $this->extPaginate->totalPage()
1074
            ) {
1075
                $iteration += $this->getCFGDef('display', 0);
1076
            }
1077
            $iteration += $this->getCFGDef(
1078
                'display',
1079
                0
1080
            ) * ($this->extPaginate->currentPage() - 1) - $offset;
1081
        }
1082
1083
        $data[$this->getCFGDef(
1084
            "sysKey",
1085
            "dl"
1086
        ) . '.full_iteration'] = $iteration;
1087
1088
        if ($i == 1) {
1089
            $this->renderTPL = $this->getCFGDef('tplFirst', $this->renderTPL);
1090
            $class[] = $this->getCFGDef('firstClass', 'first');
1091
        }
1092
        if ($i == (count($this->_docs) - $this->skippedDocs)) {
1093
            $this->renderTPL = $this->getCFGDef('tplLast', $this->renderTPL);
1094
            $class[] = $this->getCFGDef('lastClass', 'last');
1095
        }
1096
        if ($this->modx->documentIdentifier == $data['id']) {
1097
            $this->renderTPL = $this->getCFGDef('tplCurrent', $this->renderTPL);
1098
            $data[$this->getCFGDef(
1099
                "sysKey",
1100
                "dl"
1101
            ) . '.active'] = 1; //[+active+] - 1 if $modx->documentIdentifer equal ID this element
1102
            $class[] = $this->getCFGDef('currentClass', 'current');
1103
        } else {
1104
            $data[$this->getCFGDef("sysKey", "dl") . '.active'] = 0;
1105
        }
1106
1107
        $class = implode(" ", $class);
1108
        $data[$this->getCFGDef("sysKey", "dl") . '.class'] = $class;
1109
1110
        /**
1111
         * @var $extE e_DL_Extender
1112
         */
1113
        $extE = $this->getExtender('e', true, true);
1114
        if ($out = $extE->init($this, compact('data'))) {
1115
            if (is_array($out)) {
1116
                $data = $out;
1117
            }
1118
        }
1119
1120
        return compact('class', 'iterationName');
1121
    }
1122
1123
    /**
1124
     * Формирование JSON ответа
1125
     *
1126
     * @param array $data массив данных которые подготавливаются к выводу в JSON
1127
     * @param mixed $fields список полей учавствующих в JSON ответе. может быть либо массив, либо строка с разделителем , (запятая)
1128
     * @param array $array данные которые необходимо примешать к ответу на каждой записи $data
1129
     * @return string JSON строка
1130
     */
1131
    public function getJSON($data, $fields, $array = array())
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1132
    {
1133
        $out = array();
1134
        $fields = is_array($fields) ? $fields : explode(",", $fields);
1135
        if (is_array($array) && count($array) > 0) {
1136
            $tmp = array();
1137
            foreach ($data as $i => $v) { //array_merge not valid work with integer index key
1138
                $tmp[$i] = (isset($array[$i]) ? array_merge($v, $array[$i]) : $v);
1139
            }
1140
            $data = $tmp;
1141
        }
1142
1143
        foreach ($data as $num => $doc) {
1144
            $tmp = array();
1145
            foreach ($doc as $name => $value) {
1146
                if (in_array($name, $fields) || (isset($fields[0]) && $fields[0] == '1')) {
1147
                    $tmp[str_replace(".", "_", $name)] = $value; //JSON element name without dot
1148
                }
1149
            }
1150
            $out[$num] = $tmp;
1151
        }
1152
1153
        if ('new' == $this->getCFGDef('JSONformat', 'old')) {
1154
            $return = array();
1155
1156
            $return['rows'] = array();
1157
            foreach ($out as $key => $item) {
1158
                $return['rows'][] = $item;
1159
            }
1160
            $return['total'] = $this->getChildrenCount();
1161
        } elseif ('simple' == $this->getCFGDef('JSONformat', 'old')) {
1162
            $return = array();
1163
            foreach ($out as $key => $item) {
1164
                $return[] = $item;
1165
            }
1166
        } else {
1167
            $return = $out;
1168
        }
1169
        $this->outData = json_encode($return);
1170
        $this->isErrorJSON($return);
1171
1172
        return jsonHelper::json_format($this->outData);
0 ignored issues
show
Bug Best Practice introduced by
The expression return jsonHelper::json_format($this->outData) could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
1173
    }
1174
1175
    /**
1176
     * @param array $item
1177
     * @param null $extSummary
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $extSummary is correct as it would always require null to be passed?
Loading history...
1178
     * @param string $introField
1179
     * @param string $contentField
1180
     * @return mixed|string
1181
     */
1182
    protected function getSummary(array $item = array(), $extSummary = null, $introField = '', $contentField = '')
1183
    {
1184
        $out = '';
1185
1186
        if (is_null($extSummary)) {
0 ignored issues
show
introduced by
The condition is_null($extSummary) is always true.
Loading history...
1187
            /**
1188
             * @var $extSummary summary_DL_Extender
1189
             */
1190
            $extSummary = $this->getExtender('summary');
1191
        }
1192
        $introField = $this->getCFGDef("introField", $introField);
1193
        $contentField = $this->getCFGDef("contentField", $contentField);
1194
1195
        if (!empty($introField) && !empty($item[$introField]) && mb_strlen($item[$introField], 'UTF-8') > 0) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1196
            $out = $item[$introField];
1197
        } else {
1198
            if (!empty($contentField) && !empty($item[$contentField]) && mb_strlen($item[$contentField], 'UTF-8') > 0) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1199
                $out = $extSummary->init($this, array(
1200
                    "content"      => $item[$contentField],
1201
                    "action"       => $this->getCFGDef("summary", ""),
1202
                    "cutSummary"   => $this->getCFGDef('cutSummary'),
1203
                    "dotSummary"   => $this->getCFGDef('dotSummary'),
1204
                    'breakSummary' => $this->getCFGDef('breakSummary')
1205
                ));
1206
            }
1207
        }
1208
1209
        return $out;
1210
    }
1211
1212
    /**
1213
     * @param string $name extender name
1214
     * @return boolean status extender load
1215
     */
1216
    public function checkExtender($name)
1217
    {
1218
        return (isset($this->extender[$name]) && $this->extender[$name] instanceof $name . "_DL_Extender");
1219
    }
1220
1221
    /**
1222
     * @param $name
1223
     * @param $obj
1224
     */
1225
    public function setExtender($name, $obj)
1226
    {
1227
        $this->extender[$name] = $obj;
1228
    }
1229
1230
    /**
1231
     * Вытащить экземпляр класса экстендера из общего массива экстендеров
1232
     *
1233
     * @param string $name имя экстендера
1234
     * @param bool $autoload Если экстендер не загружен, то пытаться ли его загрузить
1235
     * @param bool $nop если экстендер не загружен, то загружать ли xNop
1236
     * @return null|xNop
1237
     */
1238
    public function getExtender($name, $autoload = false, $nop = false)
1239
    {
1240
        $out = null;
1241
        if ((is_scalar($name) && $this->checkExtender($name)) || ($autoload && $this->_loadExtender($name))) {
1242
            $out = $this->extender[$name];
1243
        }
1244
        if ($nop && is_null($out)) {
1245
            $out = new xNop();
1246
        }
1247
1248
        return $out;
1249
    }
1250
1251
    /**
1252
     * load extender
1253
     *
1254
     * @param string $name name extender
1255
     * @return boolean $flag status load extender
1256
     */
1257
    protected function _loadExtender($name)
1258
    {
1259
        $this->debug->debug('Load Extender ' . $this->debug->dumpData($name), 'LoadExtender', 2);
1260
        $flag = false;
1261
1262
        $classname = ($name != '') ? $name . "_DL_Extender" : "";
1263
        if ($classname != '' && isset($this->extender[$name]) && $this->extender[$name] instanceof $classname) {
1264
            $flag = true;
1265
        } else {
1266
            if (!class_exists($classname, false) && $classname != '') {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1267
                if (file_exists(dirname(__FILE__) . "/extender/" . $name . ".extender.inc")) {
1268
                    include_once(dirname(__FILE__) . "/extender/" . $name . ".extender.inc");
1269
                }
1270
            }
1271
            if (class_exists($classname, false) && $classname != '') {
1272
                $this->extender[$name] = new $classname($this, $name);
1273
                $flag = true;
1274
            }
1275
        }
1276
        if (!$flag) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1277
            $this->debug->debug("Error load Extender " . $this->debug->dumpData($name));
1278
        }
1279
        $this->debug->debugEnd('LoadExtender');
1280
1281
        return $flag;
1282
    }
1283
1284
    /*************************************************
1285
     ****************** IDs BLOCK ********************
1286
     ************************************************/
1287
1288
    /**
1289
     * Очистка массива $IDs по которому потом будет производиться выборка документов
1290
     *
1291
     * @param mixed $IDs список id документов по которым необходима выборка
1292
     * @return array очищенный массив
1293
     */
1294
    public function setIDs($IDs)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
Coding Style introduced by
Function's nesting level (4) exceeds 3; consider refactoring the function
Loading history...
1295
    {
1296
        $this->debug->debug('set ID list ' . $this->debug->dumpData($IDs), 'setIDs', 2);
1297
        $IDs = $this->cleanIDs($IDs);
1298
        $type = $this->getCFGDef('idType', 'parents');
1299
        $depth = $this->getCFGDef('depth', '');
1300
        if ($type == 'parents' && $depth > 0) {
1301
            $out = $this->extCache->load('children');
1302
            if ($out === false) {
1303
                $tmp = $IDs;
1304
                do {
1305
                    if (count($tmp) > 0) {
1306
                        $tmp = $this->getChildrenFolder($tmp);
1307
                        $IDs = array_merge($IDs, $tmp);
1308
                    }
1309
                } while ((--$depth) > 0);
1310
                $this->extCache->save($IDs, 'children');
1311
            } else {
1312
                $IDs = $out;
1313
            }
1314
        }
1315
        $this->debug->debugEnd("setIDs");
1316
1317
        return ($this->IDs = $IDs);
1318
    }
1319
1320
    /**
1321
     * @return int
1322
     */
1323
    public function getIDs()
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1324
    {
1325
        return $this->IDs;
1326
    }
1327
1328
    /**
1329
     * Очистка данных и уникализация списка цифр.
1330
     * Если был $IDs был передан как строка, то эта строка будет преобразована в массив по разделителю $sep
1331
     * @param mixed $IDs данные для обработки
1332
     * @param string $sep разделитель
1333
     * @return array очищенный массив с данными
1334
     */
1335
    public function cleanIDs($IDs, $sep = ',')
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1336
    {
1337
        $this->debug->debug(
1338
            'clean IDs ' . $this->debug->dumpData($IDs) . ' with separator ' . $this->debug->dumpData($sep),
1339
            'cleanIDs',
1340
            2
1341
        );
1342
        $out = array();
1343
        if (!is_array($IDs)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1344
            $IDs = explode($sep, $IDs);
1345
        }
1346
        foreach ($IDs as $item) {
1347
            $item = trim($item);
1348
            if (is_numeric($item) && (int)$item >= 0) { //Fix 0xfffffffff
1349
                $out[] = (int)$item;
1350
            }
1351
        }
1352
        $out = array_unique($out);
1353
        $this->debug->debugEnd("cleanIDs");
1354
1355
        return $out;
1356
    }
1357
1358
    /**
1359
     * Проверка массива с id-шниками документов для выборки
1360
     * @return boolean пригодны ли данные для дальнейшего использования
1361
     */
1362
    protected function checkIDs()
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1363
    {
1364
        return (is_array($this->IDs) && count($this->IDs) > 0) ? true : false;
0 ignored issues
show
introduced by
The condition is_array($this->IDs) is always false.
Loading history...
1365
    }
1366
1367
    /**
1368
     * Get all field values from array documents
1369
     *
1370
     * @param string $userField field name
1371
     * @param boolean $uniq Only unique values
1372
     * @global array $_docs all documents
1373
     * @return array all field values
1374
     */
1375
    public function getOneField($userField, $uniq = false)
1376
    {
1377
        $out = array();
1378
        foreach ($this->_docs as $doc => $val) {
1379
            if (isset($val[$userField]) && (($uniq && !in_array($val[$userField], $out)) || !$uniq)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1380
                $out[$doc] = $val[$userField];
1381
            }
1382
        }
1383
1384
        return $out;
1385
    }
1386
1387
    /**
1388
     * @return DLCollection
1389
     */
1390
    public function docsCollection()
1391
    {
1392
        return new DLCollection($this->modx, $this->_docs);
1393
    }
1394
1395
    /**********************************************************
1396
     ********************** SQL BLOCK *************************
1397
     *********************************************************/
1398
1399
    /**
1400
     * Подсчет документов удовлетворящиюх выборке
1401
     *
1402
     * @return int Число дочерних документов
1403
     */
1404
    abstract public function getChildrenCount();
1405
1406
    /**
1407
     * Выборка документов которые являются дочерними относительно $id документа и в тоже время
1408
     * являются родителями для каких-нибудь других документов
1409
     *
1410
     * @param string|array $id значение PrimaryKey родителя
1411
     * @return array массив документов
1412
     */
1413
    abstract public function getChildrenFolder($id);
1414
1415
    /**
1416
     * @param string $group
1417
     * @return string
1418
     */
1419
    protected function getGroupSQL($group = '')
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1420
    {
1421
        $out = '';
1422
        if ($group != '') {
1423
            $out = 'GROUP BY ' . $group;
1424
        }
1425
1426
        return $out;
1427
    }
1428
1429
    /**
1430
     *    Sorting method in SQL queries
1431
     *
1432
     * @global string $order
1433
     * @global string $orderBy
1434
     * @global string $sortBy
1435
     *
1436
     * @param string $sortName default sort field
1437
     * @param string $orderDef default order (ASC|DESC)
1438
     *
1439
     * @return string Order by for SQL
1440
     */
1441
    protected function SortOrderSQL($sortName, $orderDef = 'DESC')
0 ignored issues
show
Coding Style introduced by
Function's nesting level (5) exceeds 3; consider refactoring the function
Loading history...
Coding Style introduced by
Method name "DocLister::SortOrderSQL" is not in camel caps format
Loading history...
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1442
    {
1443
        $this->debug->debug('', 'sortORDER', 2);
1444
1445
        $sort = '';
1446
        switch ($this->getCFGDef('sortType', '')) {
1447
            case 'none':
1448
                break;
1449
            case 'doclist':
1450
                $idList = $this->sanitarIn($this->IDs, ',', false);
1451
                $out = array('orderBy' => "FIND_IN_SET({$this->getCFGDef('sortBy', $this->getPK())}, '{$idList}')");
1452
                $this->config->setConfig($out); //reload config;
1453
                $sort = "ORDER BY " . $out['orderBy'];
1454
                break;
1455
            default:
1456
                $out = array('orderBy' => '', 'order' => '', 'sortBy' => '');
1457
                if (($tmp = $this->getCFGDef('orderBy', '')) != '') {
1458
                    $out['orderBy'] = $tmp;
1459
                } else {
1460
                    switch (true) {
1461
                        case ('' != ($tmp = $this->getCFGDef('sortDir', ''))): //higher priority than order
1462
                            $out['order'] = $tmp;
1463
                        // no break
1464
                        case ('' != ($tmp = $this->getCFGDef('order', ''))):
1465
                            $out['order'] = $tmp;
1466
                        // no break
1467
                    }
1468
                    if ('' == $out['order'] || !in_array(strtoupper($out['order']), array('ASC', 'DESC'))) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1469
                        $out['order'] = $orderDef; //Default
1470
                    }
1471
1472
                    $out['sortBy'] = (($tmp = $this->getCFGDef('sortBy', '')) != '') ? $tmp : $sortName;
1473
                    $out['orderBy'] = $out['sortBy'] . " " . $out['order'];
1474
                }
1475
                $this->config->setConfig($out); //reload config;
1476
                $sort = "ORDER BY " . $out['orderBy'];
1477
                break;
1478
        }
1479
        $this->debug->debugEnd("sortORDER", 'Get sort order for SQL: ' . $this->debug->dumpData($sort));
1480
1481
        return $sort;
1482
    }
1483
1484
    /**
1485
     * Получение LIMIT вставки в SQL запрос
1486
     *
1487
     * @return string LIMIT вставка в SQL запрос
1488
     */
1489
    protected function LimitSQL($limit = 0, $offset = 0)
0 ignored issues
show
Coding Style introduced by
Method name "DocLister::LimitSQL" is not in camel caps format
Loading history...
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1490
    {
1491
        $this->debug->debug('', 'limitSQL', 2);
1492
        $ret = '';
1493
        if ($limit == 0) {
1494
            $limit = $this->getCFGDef('display', 0);
1495
        }
1496
        $maxDocs = $this->getCFGDef('maxDocs', 0);
1497
        if ($maxDocs > 0 && $limit > $maxDocs) {
1498
            $limit = $maxDocs;
1499
        }
1500
        if ($offset == 0) {
1501
            $offset = $this->getCFGDef('offset', 0);
1502
        }
1503
        $offset += $this->getCFGDef('start', 0);
1504
        $total = $this->getCFGDef('total', 0);
1505
        if ($limit < ($total - $limit)) {
1506
            $limit = $total - $offset;
1507
        }
1508
1509
        if ($limit != 0) {
1510
            $ret = "LIMIT " . (int)$offset . "," . (int)$limit;
1511
        } else {
1512
            if ($offset != 0) {
1513
                /**
1514
                 * To retrieve all rows from a certain offset up to the end of the result set, you can use some large number for the second parameter
1515
                 * @see http://dev.mysql.com/doc/refman/5.0/en/select.html
1516
                 */
1517
                $ret = "LIMIT " . (int)$offset . ",18446744073709551615";
1518
            }
1519
        }
1520
        $this->debug->debugEnd("limitSQL", "Get limit for SQL: " . $this->debug->dumpData($ret));
1521
1522
        return $ret;
1523
    }
1524
1525
    /**
1526
     * Clean up the modx and html tags
1527
     *
1528
     * @param string $data String for cleaning
1529
     * @param string $charset
1530
     * @return string Clear string
1531
     */
1532
    public function sanitarData($data, $charset = 'UTF-8')
1533
    {
1534
        return APIHelpers::sanitarTag($data, $charset);
1535
    }
1536
1537
    /**
1538
     * run tree build
1539
     *
1540
     * @param string $idField default name id field
1541
     * @param string $parentField default name parent field
1542
     * @return array
1543
     */
1544
    public function treeBuild($idField = 'id', $parentField = 'parent')
1545
    {
1546
        return $this->_treeBuild(
1547
            $this->_docs,
1548
            $this->getCFGDef('idField', $idField),
1549
            $this->getCFGDef('parentField', $parentField)
1550
        );
1551
    }
1552
1553
    /**
1554
     * @see: https://github.com/DmitryKoterov/DbSimple/blob/master/lib/DbSimple/Generic.php#L986
1555
     *
1556
     * @param array $data Associative data array
1557
     * @param string $idName name ID field in associative data array
1558
     * @param string $pidName name parent field in associative data array
1559
     * @return array
1560
     */
1561
    private function _treeBuild($data, $idName, $pidName)
1562
    {
1563
        $children = array(); // children of each ID
1564
        $ids = array();
1565
        foreach ($data as $i => $r) {
1566
            $row =& $data[$i];
1567
            $id = $row[$idName];
1568
            $pid = $row[$pidName];
1569
            $children[$pid][$id] =& $row;
1570
            if (!isset($children[$id])) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1571
                $children[$id] = array();
1572
            }
1573
            $row['#childNodes'] =& $children[$id];
1574
            $ids[$row[$idName]] = true;
1575
        }
1576
        // Root elements are elements with non-found PIDs.
1577
        $this->_tree = array();
1578
        foreach ($data as $i => $r) {
1579
            $row =& $data[$i];
1580
            if (!isset($ids[$row[$pidName]])) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1581
                $this->_tree[$row[$idName]] = $row;
1582
            }
1583
        }
1584
1585
        return $this->_tree;
1586
    }
1587
1588
    /**
1589
     * Получение PrimaryKey основной таблицы.
1590
     * По умолчанию это id. Переопределить можно в контроллере присвоив другое значение переменной idField
1591
     * @param bool $full если true то возвращается значение для подстановки в запрос
1592
     * @return string PrimaryKey основной таблицы
1593
     */
1594
    public function getPK($full = true)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1595
    {
1596
        $idField = isset($this->idField) ? $this->idField: 'id';
1597
        if ($full) {
1598
            $idField = '`' . $idField . '`';
1599
            if (!empty($this->alias)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1600
                $idField = '`' . $this->alias . '`.' . $idField;
1601
            }
1602
        }
1603
1604
        return $idField;
1605
    }
1606
1607
    /**
1608
     * Получение Parent key
1609
     * По умолчанию это parent. Переопределить можно в контроллере присвоив другое значение переменной parentField
1610
     * @param bool $full если true то возвращается значение для подстановки в запрос
1611
     * @return string Parent Key основной таблицы
1612
     */
1613
    public function getParentField($full = true)
1614
    {
1615
        $parentField = isset($this->parentField) ? $this->parentField : '';
1616
        if ($full && !empty($parentField)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1617
            $parentField = '`' . $parentField . '`';
1618
            if (!empty($this->alias)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1619
                $parentField = '`' . $this->alias . '`.' . $parentField;
1620
            }
1621
        }
1622
1623
        return $parentField;
1624
    }
1625
1626
    /**
1627
     * Разбор фильтров
1628
     * OR(AND(filter:field:operator:value;filter2:field:oerpator:value);(...)), etc.
1629
     *
1630
     * @param string $filter_string строка со всеми фильтрами
1631
     * @return mixed результат разбора фильтров
1632
     */
1633
    protected function getFilters($filter_string)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (4) exceeds 3; consider refactoring the function
Loading history...
1634
    {
1635
        $this->debug->debug("getFilters: " . $this->debug->dumpData($filter_string), 'getFilter', 1);
1636
        // the filter parameter tells us, which filters can be used in this query
1637
        $filter_string = trim($filter_string, ' ;');
1638
        if (!$filter_string) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1639
            return;
1640
        }
1641
        $output = array('join' => '', 'where' => '');
1642
        $logic_op_found = false;
1643
        $joins = $wheres = array();
1644
        foreach ($this->_logic_ops as $op => $sql) {
1645
            if (strpos($filter_string, $op) === 0) {
1646
                $logic_op_found = true;
1647
                $subfilters = mb_substr($filter_string, strlen($op) + 1, mb_strlen($filter_string, "UTF-8"), "UTF-8");
1648
                $subfilters = $this->smartSplit($subfilters);
1649
                foreach ($subfilters as $subfilter) {
1650
                    $subfilter = $this->getFilters(trim($subfilter));
1651
                    if (!$subfilter) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1652
                        continue;
1653
                    }
1654
                    if ($subfilter['join']) {
1655
                        $joins[] = $subfilter['join'];
1656
                    }
1657
                    if ($subfilter['where']) {
1658
                        $wheres[] = $subfilter['where'];
1659
                    }
1660
                }
1661
                $output['join'] = !empty($joins) ? implode(' ', $joins) : '';
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1662
                $output['where'] = !empty($wheres) ? '(' . implode($sql, $wheres) . ')' : '';
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1663
            }
1664
        }
1665
1666
        if (!$logic_op_found) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1667
            $filter = $this->loadFilter($filter_string);
1668
            if (!$filter) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1669
                $this->debug->warning('Error while loading DocLister filter "' . $this->debug->dumpData($filter_string) . '": check syntax!');
0 ignored issues
show
Bug introduced by
The method warning() does not exist on xNop. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

1669
                $this->debug->/** @scrutinizer ignore-call */ 
1670
                              warning('Error while loading DocLister filter "' . $this->debug->dumpData($filter_string) . '": check syntax!');
Loading history...
1670
                $output = false;
1671
            } else {
1672
                $output['join'] = $filter->get_join();
1673
                $output['where'] = stripslashes($filter->get_where());
1674
            }
1675
        }
1676
        $this->debug->debug('getFilter');
1677
1678
        return $output;
1679
    }
1680
1681
    /**
1682
     * @return mixed
1683
     */
1684
    public function filtersWhere()
1685
    {
1686
        return APIHelpers::getkey($this->_filters, 'where', '');
1687
    }
1688
1689
    /**
1690
     * @return mixed
1691
     */
1692
    public function filtersJoin()
1693
    {
1694
        return APIHelpers::getkey($this->_filters, 'join', '');
1695
    }
1696
1697
    /**
1698
     * @param string $join
1699
     * @return $this
1700
     */
1701
    public function setFiltersJoin($join = '')
1702
    {
1703
        if (!empty($join)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1704
            if (!empty($this->_filters['join'])) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1705
                $this->_filters['join'] .= ' ' . $join;
1706
            } else {
1707
                $this->_filters['join'] = $join;
1708
            }
1709
        }
1710
1711
        return $this;
1712
    }
1713
1714
    /**
1715
     * Приведение типа поля
1716
     *
1717
     * @param $field string имя поля
1718
     * @param $type string тип фильтрации
1719
     * @return string имя поля с учетом приведения типа
1720
     */
1721
    public function changeSortType($field, $type)
1722
    {
1723
        $type = trim($type);
1724
        switch (strtoupper($type)) {
1725
            case 'DECIMAL':
1726
                $field = 'CAST(' . $field . ' as DECIMAL(10,2))';
1727
                break;
1728
            case 'UNSIGNED':
1729
                $field = 'CAST(' . $field . ' as UNSIGNED)';
1730
                break;
1731
            case 'BINARY':
1732
                $field = 'CAST(' . $field . ' as BINARY)';
1733
                break;
1734
            case 'DATETIME':
1735
                $field = 'CAST(' . $field . ' as DATETIME)';
1736
                break;
1737
            case 'SIGNED':
1738
                $field = 'CAST(' . $field . ' as SIGNED)';
1739
                break;
1740
        }
1741
1742
        return $field;
1743
    }
1744
1745
    /**
1746
     * Загрузка фильтра
1747
     * @param string $filter срока с параметрами фильтрации
1748
     * @return bool
1749
     */
1750
    protected function loadFilter($filter)
1751
    {
1752
        $this->debug->debug('Load filter ' . $this->debug->dumpData($filter), 'loadFilter', 2);
1753
        $out = false;
1754
        $fltr_params = explode(':', $filter, 2);
1755
        $fltr = APIHelpers::getkey($fltr_params, 0, null);
1756
        /**
1757
        * @var tv_DL_filter|content_DL_filter $fltr_class
1758
        */
1759
        $fltr_class = $fltr . '_DL_filter';
1760
        // check if the filter is implemented
1761
        if (!is_null($fltr)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1762
            if (!class_exists($fltr_class) && file_exists(__DIR__ . '/filter/' . $fltr . '.filter.php')) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
Bug introduced by
$fltr_class of type content_DL_filter|tv_DL_filter is incompatible with the type string expected by parameter $class_name of class_exists(). ( Ignorable by Annotation )

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

1762
            if (!class_exists(/** @scrutinizer ignore-type */ $fltr_class) && file_exists(__DIR__ . '/filter/' . $fltr . '.filter.php')) {
Loading history...
1763
                require_once dirname(__FILE__) . '/filter/' . $fltr . '.filter.php';
1764
            }
1765
            if (class_exists($fltr_class)) {
1766
                $this->totalFilters++;
1767
                $fltr_obj = new $fltr_class();
1768
                if ($fltr_obj->init($this, $filter)) {
1769
                    $out = $fltr_obj;
1770
                } else {
1771
                    $this->debug->error("Wrong filter parameter: '{$this->debug->dumpData($filter)}'", 'Filter');
1772
                }
1773
            }
1774
        }
1775
        if (!$out) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
1776
            $this->debug->error("Error load Filter: '{$this->debug->dumpData($filter)}'", 'Filter');
1777
        }
1778
1779
        $this->debug->debugEnd("loadFilter");
1780
1781
        return $out;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $out also could return the type object which is incompatible with the documented return type boolean.
Loading history...
1782
    }
1783
1784
    /**
1785
     * Общее число фильтров
1786
     * @return int
1787
     */
1788
    public function getCountFilters()
1789
    {
1790
        return (int)$this->totalFilters;
1791
    }
1792
1793
    /**
1794
     * Выполнить SQL запрос
1795
     * @param string $q SQL запрос
1796
     */
1797
    public function dbQuery($q)
1798
    {
1799
        $this->debug->debug($q, "query", 1, 'sql');
1800
        $out = $this->modx->db->query($q);
1801
        $this->debug->debugEnd("query");
1802
1803
        return $out;
1804
    }
1805
1806
    /**
1807
     * Экранирование строки в SQL запросе LIKE
1808
     * @see: http://stackoverflow.com/a/3683868/2323306
1809
     *
1810
     * @param string $field поле по которому осуществляется поиск
1811
     * @param string $value искомое значение
1812
     * @param string $escape экранирующий символ
1813
     * @param string $tpl шаблон подстановки значения в SQL запрос
1814
     * @return string строка для подстановки в SQL запрос
1815
     */
1816
    public function LikeEscape($field, $value, $escape = '=', $tpl = '%[+value+]%')
0 ignored issues
show
Coding Style introduced by
Method name "DocLister::LikeEscape" is not in camel caps format
Loading history...
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1817
    {
1818
        return sqlHelper::LikeEscape($this->modx, $field, $value, $escape, $tpl);
1819
    }
1820
1821
    /**
1822
     * Получение REQUEST_URI без GET-ключа с
1823
     * @return string
1824
     */
1825
    public function getRequest()
1826
    {
1827
        $URL = null;
1828
        parse_str(parse_url(MODX_SITE_URL . $_SERVER['REQUEST_URI'], PHP_URL_QUERY), $URL);
1829
1830
        return http_build_query(array_merge($URL, array(DocLister::AliasRequest => null)));
1831
    }
1832
}
1833