Passed
Push — master ( 430f2d...9af89a )
by Agel_Nash
02:14
created

DocLister::parseSource()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 3
c 1
b 1
f 0
dl 0
loc 7
ccs 0
cts 5
cp 0
rs 10
cc 2
nc 2
nop 1
crap 6
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;
52
53
    /**
54
     * Шаблонизатор чанков
55
     * @var DLTemplate
56
     * @access protected
57
     */
58
    protected $DLTemplate;
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;
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
    /** @var \Helpers\FS */
164
    public $FS;
165
    /** @var string результатирующая строка которая была последний раз сгенирирована
166
     *               вызовами методов DocLister::render и DocLister::getJSON
167
     */
168
    protected $outData = '';
169
170
    /** @var int Число документов, которые были отфильтрованы через prepare при выводе */
171
    public $skippedDocs = 0;
172
173
    /** @var string Имя таблицы */
174
    protected $table = '';
175
    /** @var string alias таблицы */
176
    protected $alias = '';
177
178
    /** @var null|paginate_DL_Extender */
179
    protected $extPaginate;
180
181
    /** @var null|Helpers\Config */
182
    public $config;
183
184
    /**
185
     * @var cache_DL_Extender
186
     */
187
    protected $extCache;
188
189
    /**
190
     * Конструктор контроллеров DocLister
191
     *
192
     * @param DocumentParser $modx объект DocumentParser - основной класс MODX
193
     * @param mixed $cfg массив параметров сниппета
194
     * @param int $startTime время запуска сниппета
195
     * @throws Exception
196
     */
197 58
    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...
198
    {
199 58
        $this->setTimeStart($startTime);
200
201 58
        if (extension_loaded('mbstring')) {
202 58
            mb_internal_encoding("UTF-8");
203 58
        } else {
204
            throw new Exception('Not found php extension mbstring');
205
        }
206
207 58
        if ($modx instanceof DocumentParser) {
0 ignored issues
show
introduced by
$modx is always a sub-type of DocumentParser.
Loading history...
208 58
            $this->modx = $modx;
209 58
            $this->setDebug(1);
210
211 58
            if (! is_array($cfg) || empty($cfg)) {
212
                $cfg = $this->modx->Event->params;
0 ignored issues
show
Bug introduced by
The property Event does not seem to exist on DocumentParser.
Loading history...
213
            }
214 58
        } else {
215
            throw new Exception('MODX var is not instaceof DocumentParser');
216
        }
217
218 58
        $this->FS = \Helpers\FS::getInstance();
219 58
        $this->config = new \Helpers\Config($cfg);
220
221 58
        if (isset($cfg['config'])) {
222
            $this->config->setPath(dirname(__DIR__))->loadConfig($cfg['config']);
223
        }
224
225 58
        if ($this->config->setConfig($cfg) === false) {
226
            throw new Exception('no parameters to run DocLister');
227
        }
228
229 58
        $this->loadLang(array('core', 'json'));
230 58
        $this->setDebug($this->getCFGDef('debug', 0));
231
232 58
        if ($this->checkDL()) {
233 58
            $cfg = array();
234 58
            $idType = $this->getCFGDef('idType', '');
235 58
            if (empty($idType) && $this->getCFGDef('documents', '') != '') {
236
                $idType = 'documents';
237
            }
238
            switch ($idType) {
239 58
                case 'documents':
240
                    $IDs = $this->getCFGDef('documents');
241
                    $cfg['idType'] = "documents";
242
                    break;
243 58
                case 'parents':
244 58
                default:
245 58
                    $cfg['idType'] = "parents";
246 58
                    if (($IDs = $this->getCFGDef('parents', '')) === '') {
247 58
                        $IDs = $this->getCurrentMODXPageID();
248 58
                    }
249 58
                    break;
250 58
            }
251 58
            $this->config->setConfig($cfg);
252 58
            $this->alias = empty($this->alias) ? $this->getCFGDef(
253 58
                'tableAlias',
254
                'c'
255 58
            ) : $this->alias;
256 58
            $this->table = $this->getTable(empty($this->table) ? $this->getCFGDef(
257 58
                'table',
258
                'site_content'
259 58
            ) : $this->table, $this->alias);
260
261 58
            $this->idField = $this->getCFGDef('idField', 'id');
262 58
            $this->parentField = $this->getCFGDef('parentField', 'parent');
263 58
            $this->extCache = $this->getExtender('cache', true);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getExtender('cache', true) can also be of type xNop. However, the property $extCache is declared as type cache_DL_Extender. 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...
264 58
            $this->extCache->init($this, array(
0 ignored issues
show
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

264
            $this->extCache->/** @scrutinizer ignore-call */ 
265
                             init($this, array(
Loading history...
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

264
            $this->extCache->/** @scrutinizer ignore-call */ 
265
                             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...
265 58
                'cache'         => $this->getCFGDef('cache', 1),
266 58
                'cacheKey'      => $this->getCFGDef('cacheKey'),
267 58
                'cacheLifetime' => $this->getCFGDef('cacheLifetime', 0),
268 58
                'cacheStrategy' => $this->getCFGDef('cacheStrategy')
269 58
            ));
270 58
            $this->setIDs($IDs);
271 58
        }
272
273 58
        $this->setLocate();
274
275 58
        if ($this->getCFGDef("customLang")) {
276
            $this->getCustomLang();
277
        }
278 58
        $this->loadExtender($this->getCFGDef("extender", ""));
279
280 58
        if ($this->checkExtender('request')) {
281
            $this->extender['request']->init($this, $this->getCFGDef("requestActive", ""));
282
        }
283 58
        $this->_filters = $this->getFilters($this->getCFGDef('filters', ''));
284 58
        $this->ownerTPL = $this->getCFGDef("ownerTPL", "");
285 58
        $DLTemplate = DLTemplate::getInstance($modx);
286 58
        if ($path = $this->getCFGDef('templatePath')) {
287
            $DLTemplate->setTemplatePath($path);
288
        }
289 58
        if ($ext = $this->getCFGDef('templateExtension')) {
290
            $DLTemplate->setTemplateExtension($ext);
291
        }
292 58
        $this->DLTemplate = $DLTemplate->setTemplateData(array('DocLister' => $this));
293 58
    }
294
295
    /**
296
     * Разбиение фильтра на субфильтры с учётом вложенности
297
     * @param string $str строка с фильтром
298
     * @return array массив субфильтров
299
     */
300 10
    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...
301
    {
302 10
        $res = array();
303 10
        $cur = '';
304 10
        $open = 0;
305 10
        $strlen = mb_strlen($str, 'UTF-8');
306 10
        for ($i = 0; $i <= $strlen; $i++) {
307 10
            $e = mb_substr($str, $i, 1, 'UTF-8');
308
            switch ($e) {
309 10
                case '\\':
310
                    $cur .= $e;
311
                    $cur .= mb_substr($str, ++$i, 1, 'UTF-8');
312
                    break;
313 10
                case ')':
314 10
                    $open--;
315 10
                    if ($open === 0) {
316 4
                        $res[] = $cur . ')';
317 4
                        $cur = '';
318 4
                    } else {
319 10
                        $cur .= $e;
320
                    }
321 10
                    break;
322 10
                case '(':
323 4
                    $open++;
324 4
                    $cur .= $e;
325 4
                    break;
326 10
                case ';':
327 6
                    if ($open === 0) {
328 5
                        $res[] = $cur;
329 5
                        $cur = '';
330 5
                    } else {
331 1
                        $cur .= $e;
332
                    }
333 6
                    break;
334 10
                default:
335 10
                    $cur .= $e;
336 10
            }
337 10
        }
338 10
        $cur = preg_replace("/(\))$/u", '', $cur);
339 10
        if ($cur !== '') {
340 10
            $res[] = $cur;
341 10
        }
342
343 10
        return array_reverse($res);
344
    }
345
346
    /**
347
     * Трансформация объекта в строку
348
     * @return string последний ответ от DocLister'а
349
     */
350
    public function __toString()
351
    {
352
        return $this->outData;
353
    }
354
355
    /**
356
     * Установить время запуска сниппета
357
     * @param float|null $time
358
     */
359 58
    public function setTimeStart($time = null)
360
    {
361 58
        $this->_timeStart = is_null($time) ? microtime(true) : $time;
362 58
    }
363
364
    /**
365
     * Время запуска сниппета
366
     *
367
     * @return int
368
     */
369 38
    public function getTimeStart()
370
    {
371 38
        return $this->_timeStart;
372
    }
373
374
    /**
375
     * Установка режима отладки
376
     * @param int $flag режим отладки
377
     */
378 58
    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...
379
    {
380 58
        $flag = abs((int)$flag);
381 58
        if ($this->_debugMode != $flag) {
382 58
            $this->_debugMode = $flag;
383 58
            $this->debug = null;
384 58
            if ($this->_debugMode > 0) {
385 58
                if (isset($_SESSION['usertype']) && $_SESSION['usertype'] == 'manager') {
386
                    error_reporting(E_ALL ^ E_NOTICE);
387
                    ini_set('display_errors', 1);
388
                }
389 58
                $dir = dirname(dirname(__FILE__));
390 58
                if (file_exists($dir . "/lib/DLdebug.class.php")) {
391 58
                    include_once($dir . "/lib/DLdebug.class.php");
392 58
                    if (class_exists("DLdebug", false)) {
393 58
                        $this->debug = new DLdebug($this);
394 58
                    }
395 58
                }
396 58
            }
397
398 58
            if (is_null($this->debug)) {
399 58
                $this->debug = new xNop();
400 58
                $this->_debugMode = 0;
401 58
                error_reporting(0);
402 58
                ini_set('display_errors', 0);
403 58
            }
404 58
        }
405 58
    }
406
407
    /**
408
     * Информация о режиме отладки
409
     */
410 58
    public function getDebug()
411
    {
412 58
        return $this->_debugMode;
413
    }
414
415
    /**
416
     * Генерация имени таблицы с префиксом и алиасом
417
     *
418
     * @param string $name имя таблицы
419
     * @param string $alias желаемый алиас таблицы
420
     * @return string имя таблицы с префиксом и алиасом
421
     */
422 58
    public function getTable($name, $alias = '')
423
    {
424 58
        if (!isset($this->_table[$name])) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
425 58
            $this->_table[$name] = $this->modx->getFullTableName($name);
426 58
        }
427 58
        $table = $this->_table[$name];
428 58
        if (! empty($alias) && is_scalar($alias)) {
429 58
            $table .= " as `" . $alias . "`";
430 58
        }
431
432 58
        return $table;
433
    }
434
435
    /**
436
     * @param $name
437
     * @param $table
438
     * @param $alias
439
     * @return mixed
440
     */
441 26
    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...
442
    {
443 26
        if (!$this->checkTableAlias($name, $table)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
444 26
            $this->AddTable[$table][$name] = $alias;
445 26
        }
446
447 26
        return $this->AddTable[$table][$name];
448
    }
449
450
    /**
451
     * @param $name
452
     * @param $table
453
     * @return bool
454
     */
455 26
    public function checkTableAlias($name, $table)
456
    {
457 26
        return isset($this->AddTable[$table][$name]);
458
    }
459
460
    /**
461
     * Разбор JSON строки при помощи json_decode
462
     *
463
     * @param $json string строка c JSON
464
     * @param array $config ассоциативный массив с настройками для json_decode
465
     * @param bool $nop создавать ли пустой объект запрашиваемого типа
466
     * @return array|mixed|xNop
467
     */
468
    public function jsonDecode($json, $config = array(), $nop = false)
469
    {
470
        $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

470
        $this->debug->/** @scrutinizer ignore-call */ 
471
                      debug(
Loading history...
471
            '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

471
            'Decode JSON: ' . $this->debug->/** @scrutinizer ignore-call */ dumpData($json) . "\r\nwith config: " . $this->debug->dumpData($config),
Loading history...
472
            'jsonDecode',
473
            2
474
        );
475
        $config = jsonHelper::jsonDecode($json, $config, $nop);
476
        $this->isErrorJSON($json);
477
        $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

477
        $this->debug->/** @scrutinizer ignore-call */ 
478
                      debugEnd("jsonDecode");
Loading history...
478
479
        return $config;
480
    }
481
482
    /**
483
     * Были ли ошибки во время работы с JSON
484
     *
485
     * @param $json string строка с JSON для записи в лог при отладке
486
     * @return bool|string
487
     */
488
    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...
489
    {
490
        $error = jsonHelper::json_last_error_msg();
491
        if (!in_array($error, array('error_none', 'other'))) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
492
            $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

492
            $this->debug->/** @scrutinizer ignore-call */ 
493
                          error($this->getMsg('json.' . $error) . ": " . $this->debug->dumpData($json, 'code'), 'JSON');
Loading history...
493
            $error = true;
494
        }
495
496
        return $error;
497
    }
498
499
    /**
500
     * Проверка параметров и загрузка необходимых экстендеров
501
     * return boolean статус загрузки
502
     */
503 58
    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...
504
    {
505 58
        $this->debug->debug('Check DocLister parameters', 'checkDL', 2);
506 58
        $flag = true;
507 58
        $extenders = $this->getCFGDef('extender', '');
508 58
        $extenders = explode(",", $extenders);
509 58
        $tmp = $this->getCFGDef('requestActive', '') != '' || in_array('request', $extenders);
510 58
        if ($tmp && !$this->_loadExtender('request')) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
511
            //OR request in extender's parameter
512
            throw new Exception('Error load request extender');
513
        }
514
515 58
        $tmp = $this->getCFGDef('summary', '') != '' || in_array('summary', $extenders);
516 58
        if ($tmp && !$this->_loadExtender('summary')) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
517
            //OR summary in extender's parameter
518
            throw new Exception('Error load summary extender');
519
        }
520
521 58
        if ((int)$this->getCFGDef('display', 0) > 0 && ( //OR paginate in extender's parameter
522 27
                in_array('paginate', $extenders) || $this->getCFGDef('paginate', '') != '' ||
523
                $this->getCFGDef('TplPrevP', '') != '' || $this->getCFGDef('TplPage', '') != '' ||
524
                $this->getCFGDef('TplCurrentPage', '') != '' || $this->getCFGDef('TplWrapPaginate', '') != '' ||
525
                $this->getCFGDef('pageLimit', '') != '' || $this->getCFGDef('pageAdjacents', '') != '' ||
526
                $this->getCFGDef('PaginateClass', '') != '' || $this->getCFGDef('TplNextP', '') != ''
527 58
            ) && !$this->_loadExtender('paginate')
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
528 58
        ) {
529
            throw new Exception('Error load paginate extender');
530
        } else {
531 58
            if ((int)$this->getCFGDef('display', 0) == 0) {
532 58
                $extenders = $this->unsetArrayVal($extenders, 'paginate');
533 58
            }
534
        }
535
536 58
        if ($this->getCFGDef('prepare', '') != '' || $this->getCFGDef('prepareWrap') != '') {
537
            $this->_loadExtender('prepare');
538
        }
539
540 58
        $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

540
        $this->config->/** @scrutinizer ignore-call */ 
541
                       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...
541 58
        $this->debug->debugEnd("checkDL");
542
543 58
        return $flag;
544
    }
545
546
    /**
547
     * Удаление определенных данных из массива
548
     *
549
     * @param array $data массив с данными
550
     * @param mixed $val значение которые необходимо удалить из массива
551
     * @return array отчищеный массив с данными
552
     */
553 58
    private function unsetArrayVal($data, $val)
554
    {
555 58
        $out = array();
556 58
        if (is_array($data)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always true.
Loading history...
557 58
            foreach ($data as $item) {
558 58
                if ($item != $val) {
559 58
                    $out[] = $item;
560 58
                } else {
561
                    continue;
562
                }
563 58
            }
564 58
        }
565
566 58
        return $out;
567
    }
568
569
    /**
570
     * Генерация URL страницы
571
     *
572
     * @param int $id уникальный идентификатор страницы
573
     * @return string URL страницы
574
     */
575
    public function getUrl($id = 0)
576
    {
577
        $id = ((int)$id > 0) ? (int)$id : $this->getCurrentMODXPageID();
578
579
        $link = $this->checkExtender('request') ? $this->extender['request']->getLink() : $this->getRequest();
580
        if ($id == $this->modx->config['site_start']) {
581
            $url = $this->modx->config['site_url'] . ($link != '' ? "?{$link}" : "");
582
        } else {
583
            $url = $this->modx->makeUrl($id, '', $link, $this->getCFGDef('urlScheme', ''));
0 ignored issues
show
Bug introduced by
The method makeUrl() does not exist on DocumentParser. ( Ignorable by Annotation )

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

583
            /** @scrutinizer ignore-call */ 
584
            $url = $this->modx->makeUrl($id, '', $link, $this->getCFGDef('urlScheme', ''));

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...
584
        }
585
586
        return $url;
587
    }
588
589
    /**
590
     * Получение массива документов из базы
591
     * @param mixed $tvlist дополнительные параметры выборки
592
     * @return array Массив документов выбранных из базы
593
     */
594
    abstract public function getDocs($tvlist = '');
595
596
    /**
597
     * Подготовка результатов к отображению.
598
     *
599
     * @param string $tpl шаблон
600
     * @return mixed подготовленный к отображению результат выборки
601
     */
602
    abstract public function _render($tpl = '');
603
604
    /**
605
     * Подготовка результатов к отображению в соответствии с настройками
606
     *
607
     * @param string $tpl шаблон
608
     * @return string
609
     */
610
    public function render($tpl = '')
611
    {
612
        $this->debug->debug(array('Render data with template ' => $tpl), 'render', 2, array('html'));
613
        $out = '';
614
        if (1 == $this->getCFGDef('tree', '0')) {
615
            foreach ($this->_tree as $item) {
616
                $out .= $this->renderTree($item);
617
            }
618
            $out = $this->renderWrap($out);
619
        } else {
620
            $out = $this->_render($tpl);
621
        }
622
623
        if ($out) {
624
            $this->outData = $this->parseSource($out);
625
        }
626
        $this->debug->debugEnd('render');
627
628
        return $this->outData;
629
    }
630
631
    /**
632
     * @param string $out
633
     * @return string
634
     */
635
    public function parseSource($out = '')
636
    {
637
        if($this->getCFGDef('parseDocumentSource', 1)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
638
            $out = DLTemplate::getInstance($this->modx)->parseDocumentSource($out);
639
        }
640
641
        return $out;
642
    }
643
644
    /***************************************************
645
     ****************** CORE Block *********************
646
     ***************************************************/
647
648
    /**
649
     * Определение ID страницы открытой во фронте
650
     *
651
     * @return int
652
     */
653 58
    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...
654
    {
655 58
        $id = isset($this->modx->documentIdentifier) ? (int)$this->modx->documentIdentifier : 0;
656 58
        $docData = isset($this->modx->documentObject) ? $this->modx->documentObject : array();
657
658 58
        return empty($id) ? \APIHelpers::getkey($docData, 'id', 0) : $id;
659
    }
660
661
    /**
662
     * Display and save error information
663
     *
664
     * @param string $message error message
665
     * @param integer $code error number
666
     * @param string $file error on file
667
     * @param integer $line error on line
668
     * @param array $trace stack trace
669
     */
670
    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...
671
    {
672
        if (abs($this->getCFGDef('debug', '0')) == '1') {
673
            $out = "CODE #" . $code . "<br />";
674
            $out .= "on file: " . $file . ":" . $line . "<br />";
675
            $out .= "<pre>";
676
            $out .= print_r($trace, 1);
677
            $out .= "</pre>";
678
679
            $message = $out . $message;
680
        }
681
        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...
682
    }
683
684
    /**
685
     * Получение объекта DocumentParser
686
     *
687
     * @return DocumentParser
688
     */
689 58
    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...
690
    {
691 58
        return $this->modx;
692
    }
693
694
    /**
695
     * load extenders
696
     *
697
     * @param string $ext name extender separated by ,
698
     * @return boolean status load extenders
699
     * @throws Exception
700
     */
701 58
    public function loadExtender($ext = '')
702
    {
703 58
        $out = true;
704 58
        if ($ext != '') {
705
            $ext = explode(",", $ext);
706
            foreach ($ext as $item) {
707
                if ($item != '' && !$this->_loadExtender($item)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
708
                    throw new Exception('Error load ' . APIHelpers::e($item) . ' extender');
709
                }
710
            }
711
        }
712
713 58
        return $out;
714
    }
715
716
    /**
717
     * Получение информации из конфига
718
     *
719
     * @param string $name имя параметра в конфиге
720
     * @param mixed $def значение по умолчанию, если в конфиге нет искомого параметра
721
     * @return mixed значение из конфига
722
     */
723 58
    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...
724
    {
725 58
        return $this->config->getCFGDef($name, $def);
726
    }
727
728
    /**
729
     * Сохранение данных в массив плейсхолдеров
730
     *
731
     * @param mixed $data данные
732
     * @param int $set устанавливать ли глобальнй плейсхолдер MODX
733
     * @param string $key ключ локального плейсхолдера
734
     * @return string
735
     */
736
    public function toPlaceholders($data, $set = 0, $key = 'contentPlaceholder')
737
    {
738
        $this->debug->debug(null, 'toPlaceholders', 2);
739
        if ($set == 0) {
740
            $set = $this->getCFGDef('contentPlaceholder', 0);
741
        }
742
        $this->_plh[$key] = $data;
743
        $id = $this->getCFGDef('id', '');
744
        if ($id != '') {
745
            $id .= ".";
746
        }
747
        $out = DLTemplate::getInstance($this->getMODX())->toPlaceholders($data, $set, $key, $id);
748
749
        $this->debug->debugEnd(
750
            "toPlaceholders",
751
            array($key . " placeholder" => $data),
752
            array('html')
753
        );
754
755
        return $out;
756
    }
757
758
    /**
759
     * Предварительная обработка данных перед вставкой в SQL запрос вида IN
760
     * Если данные в виде строки, то происходит попытка сформировать массив из этой строки по разделителю $sep
761
     * Точно по тому, по которому потом данные будут собраны обратно
762
     *
763
     * @param integer|string|array $data данные для обработки
764
     * @param string $sep разделитель
765
     * @param boolean $quote заключать ли данные на выходе в кавычки
766
     * @return string обработанная строка
767
     */
768 28
    public function sanitarIn($data, $sep = ',', $quote = true)
769
    {
770 28
        if (is_scalar($data)) {
771 1
            $data = explode($sep, $data);
772 1
        }
773 28
        if (!is_array($data)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
introduced by
The condition is_array($data) is always true.
Loading history...
774
            $data = array(); //@TODO: throw
775
        }
776
777 28
        $out = array();
778 28
        foreach ($data as $item) {
779 28
            if ($item !== '') {
780 28
                $out[] = $this->modx->db->escape($item);
781 28
            }
782 28
        }
783 28
        $q = $quote ? "'" : "";
784 28
        $out = $q . implode($q . "," . $q, $out) . $q;
785
786 28
        return $out;
787
    }
788
789
    /**
790
     * Загрузка кастомного лексикона
791
     *
792
     * В файле с кастомным лексиконом ключи в массиве дожны быть полные
793
     * Например:
794
     *      - core.bla-bla
795
     *      - paginate.next
796
     *
797
     * @param string $lang имя языкового пакета
798
     * @return array
799
     */
800
    public function getCustomLang($lang = '')
801
    {
802
        if (empty($lang)) {
803
            $lang = $this->getCFGDef('lang', $this->modx->config['manager_language']);
804
        }
805
        if (file_exists(dirname(dirname(__FILE__)) . "/lang/" . $lang . ".php")) {
806
            $tmp = include(dirname(__FILE__) . "/lang/" . $lang . ".php");
807
            $this->_customLang = is_array($tmp) ? $tmp : array();
808
        }
809
810
        return $this->_customLang;
811
    }
812
813
    /**
814
     * Загрузка языкового пакета
815
     *
816
     * @param array|string $name ключ языкового пакета
817
     * @param string $lang имя языкового пакета
818
     * @param boolean $rename Переименовывать ли элементы массива
819
     * @return array массив с лексиконом
820
     */
821 58
    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...
822
    {
823 58
        if (empty($lang)) {
824 58
            $lang = $this->getCFGDef('lang', $this->modx->config['manager_language']);
825 58
        }
826
827 58
        $this->debug->debug(
828 58
            'Load language ' . $this->debug->dumpData($name) . "." . $this->debug->dumpData($lang),
829 58
            'loadlang',
830
            2
831 58
        );
832 58
        if (is_scalar($name)) {
833 27
            $name = array($name);
834 27
        }
835 58
        foreach ($name as $n) {
836 58
            if (file_exists(dirname(__FILE__) . "/lang/" . $lang . "/" . $n . ".inc.php")) {
837 58
                $tmp = include(dirname(__FILE__) . "/lang/" . $lang . "/" . $n . ".inc.php");
838 58
                if (is_array($tmp)) {
839
                    /**
840
                     * Переименовыываем элементы массива из array('test'=>'data') в array('name.test'=>'data')
841
                     */
842 58
                    if ($rename) {
843 58
                        $tmp = $this->renameKeyArr($tmp, $n, '', '.');
844 58
                    }
845 58
                    $this->_lang = array_merge($this->_lang, $tmp);
846 58
                }
847 58
            }
848 58
        }
849 58
        $this->debug->debugEnd("loadlang");
850
851 58
        return $this->_lang;
852
    }
853
854
    /**
855
     * Получение строки из языкового пакета
856
     *
857
     * @param string $name имя записи в языковом пакете
858
     * @param string $def Строка по умолчанию, если запись в языковом пакете не будет обнаружена
859
     * @return string строка в соответствии с текущими языковыми настройками
860
     */
861
    public function getMsg($name, $def = '')
862
    {
863
        if (isset($this->_customLang[$name])) {
864
            $say = $this->_customLang[$name];
865
        } else {
866
            $say = \APIHelpers::getkey($this->_lang, $name, $def);
867
        }
868
869
        return $say;
870
    }
871
872
    /**
873
     * Переменовывание элементов массива
874
     *
875
     * @param array $data массив с данными
876
     * @param string $prefix префикс ключей
877
     * @param string $suffix суффикс ключей
878
     * @param string $sep разделитель суффиксов, префиксов и ключей массива
879
     * @return array массив с переименованными ключами
880
     */
881 58
    public function renameKeyArr($data, $prefix = '', $suffix = '', $sep = '.')
882
    {
883 58
        return \APIHelpers::renameKeyArr($data, $prefix, $suffix, $sep);
884
    }
885
886
    /**
887
     * Установка локали
888
     *
889
     * @param string $locale локаль
890
     * @return string имя установленной локали
891
     */
892 58
    public function setLocate($locale = '')
893
    {
894 58
        if ('' == $locale) {
895 58
            $locale = $this->getCFGDef('locale', '');
896 58
        }
897 58
        if ('' != $locale) {
898
            setlocale(LC_ALL, $locale);
899
        }
900
901 58
        return $locale;
902
    }
903
904
    /**
905
     * Шаблонизация дерева.
906
     * Перевод из массива в HTML в соответствии с указанным шаблоном
907
     *
908
     * @param array $data массив сформированный как дерево
909
     * @return string строка для отображения пользователю
910
     */
911
    protected function renderTree($data)
912
    {
913
        $out = '';
914
        if (!empty($data['#childNodes'])) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
915
            foreach ($data['#childNodes'] as $item) {
916
                $out .= $this->renderTree($item);
917
            }
918
        }
919
920
        $data[$this->getCFGDef("sysKey", "dl") . ".wrap"] = $this->renderWrap($out);
921
        $out = $this->parseChunk($this->getCFGDef('tpl', ''), $data);
922
923
        return $out;
924
    }
925
926
    /**
927
     * refactor $modx->getChunk();
928
     *
929
     * @param string $name Template: chunk name || @CODE: template || @FILE: file with template
930
     * @return string html template with placeholders without data
931
     */
932
    private function _getChunk($name)
933
    {
934
        $this->debug->debug(array('Get chunk by name' => $name), "getChunk", 2, array('html'));
935
        //without trim
936
        $tpl = DLTemplate::getInstance($this->getMODX())->getChunk($name);
937
        $tpl = $this->parseLang($tpl);
938
939
        $this->debug->debugEnd("getChunk");
940
941
        return $tpl;
942
    }
943
944
    /**
945
     * Замена в шаблоне фраз из лексикона
946
     *
947
     * @param string $tpl HTML шаблон
948
     * @return string
949
     */
950
    public function parseLang($tpl)
951
    {
952
        $this->debug->debug(array("parseLang" => $tpl), "parseLang", 2, array('html'));
953
        if (is_scalar($tpl) && !empty($tpl)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
954
            if (preg_match_all("/\[\%([a-zA-Z0-9\.\_\-]+)\%\]/", $tpl, $match)) {
955
                $langVal = array();
956
                foreach ($match[1] as $item) {
957
                    $langVal[] = $this->getMsg($item);
958
                }
959
                $tpl = str_replace($match[0], $langVal, $tpl);
960
            }
961
        } else {
962
            $tpl = '';
963
        }
964
        $this->debug->debugEnd("parseLang");
965
966
        return $tpl;
967
    }
968
969
    /**
970
     * refactor $modx->parseChunk();
971
     *
972
     * @param string $name Template: chunk name || @CODE: template || @FILE: file with template
973
     * @param array $data paceholder
974
     * @param bool $parseDocumentSource render html template via DocumentParser::parseDocumentSource()
975
     * @return string html template with data without placeholders
976
     */
977
    public function parseChunk($name, $data = array(), $parseDocumentSource = false)
978
    {
979
        $this->debug->debug(
980
            array("parseChunk" => $name, "With data" => print_r($data, 1)),
981
            "parseChunk",
982
            2,
983
            array('html', null)
984
        );
985
        $disablePHx = $this->getCFGDef('disablePHx', 0);
986
        $out = $this->DLTemplate->parseChunk($name, $data, $parseDocumentSource, (bool)$disablePHx);
987
        $out = $this->parseLang($out);
988
        if (empty($out)) {
989
            $this->debug->debug("Empty chunk: " . $this->debug->dumpData($name), '', 2);
990
        }
991
        $this->debug->debugEnd("parseChunk");
992
993
        return $out;
994
    }
995
996
    /**
997
     * Get full template from parameter name
998
     *
999
     * @param string $name param name
1000
     * @param string $val default value
1001
     *
1002
     * @return string html template from parameter
1003
     */
1004
    public function getChunkByParam($name, $val = '')
1005
    {
1006
        $data = $this->getCFGDef($name, $val);
1007
        $data = $this->_getChunk($data);
1008
1009
        return $data;
1010
    }
1011
1012
    /**
1013
     * Помещение html кода в какой-то блок обертку
1014
     *
1015
     * @param string $data html код который нужно обернуть в ownerTPL
1016
     * @return string результатирующий html код
1017
     */
1018
    public function renderWrap($data)
1019
    {
1020
        $out = $data;
1021
        $docs = count($this->_docs) - $this->skippedDocs;
1022
        $wrap = $this->getCFGDef('prepareWrap');
1023
        if ((($this->getCFGDef("noneWrapOuter", "1") && $docs == 0) || $docs > 0) && !empty($this->ownerTPL) || !empty($wrap)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($this->getCFGDef('noneW...TPL)) || ! empty($wrap), Probably Intended Meaning: $this->getCFGDef('noneWr...TPL) || ! empty($wrap))
Loading history...
1024
            $this->debug->debug("", "renderWrapTPL", 2);
1025
            $parse = true;
1026
            $plh = array($this->getCFGDef("sysKey", "dl") . ".wrap" => $data);
1027
            /**
1028
             * @var $extPrepare prepare_DL_Extender
1029
             */
1030
            $extPrepare = $this->getExtender('prepare');
1031
            if ($extPrepare) {
0 ignored issues
show
introduced by
$extPrepare is of type prepare_DL_Extender, thus it always evaluated to true.
Loading history...
1032
                $params = $extPrepare->init($this, array(
1033
                    'data'      => array(
1034
                        'docs'         => $this->_docs,
1035
                        'placeholders' => $plh
1036
                    ),
1037
                    'nameParam' => 'prepareWrap',
1038
                    'return'    => 'placeholders'
1039
                ));
1040
                if ($params === false) {
1041
                    $out = $data;
1042
                    $parse = false;
1043
                }
1044
                $plh = $params;
1045
            }
1046
            if ($parse && !empty($this->ownerTPL)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1047
                $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

1047
                $this->debug->/** @scrutinizer ignore-call */ 
1048
                              updateMessage(
Loading history...
1048
                    array("render ownerTPL" => $this->ownerTPL, "With data" => print_r($plh, 1)),
1049
                    "renderWrapTPL",
1050
                    array('html', null)
1051
                );
1052
                $out = $this->parseChunk($this->ownerTPL, $plh);
1053
            }
1054
            if (empty($this->ownerTPL)) {
1055
                $this->debug->updateMessage("empty ownerTPL", "renderWrapTPL");
1056
            }
1057
            $this->debug->debugEnd("renderWrapTPL");
1058
        }
1059
1060
        return $out;
1061
    }
1062
1063
    /**
1064
     * Единые обработки массива с данными о документе для всех контроллеров
1065
     *
1066
     * @param array $data массив с данными о текущем документе
1067
     * @param int $i номер итерации в цикле
1068
     * @return array массив с данными которые можно использовать в цикле render метода
1069
     */
1070
    protected function uniformPrepare(&$data, $i = 0)
1071
    {
1072
        $class = array();
1073
1074
        $iterationName = ($i % 2 == 1) ? 'Odd' : 'Even';
1075
        $tmp = strtolower($iterationName);
1076
        $class[] = $this->getCFGDef($tmp . 'Class', $tmp);
1077
1078
        $this->renderTPL = $this->getCFGDef('tplId' . $i, $this->renderTPL);
1079
        $this->renderTPL = $this->getCFGDef('tpl' . $iterationName, $this->renderTPL);
1080
        $iteration = $i;
1081
1082
        if ($this->extPaginate) {
1083
            $offset = $this->getCFGDef(
1084
                'reversePagination',
1085
                0
1086
            ) && $this->extPaginate->currentPage() > 1 ? $this->extPaginate->totalPage() * $this->getCFGDef(
1087
                'display',
1088
                0
1089
            ) - $this->extPaginate->totalDocs() : 0;
1090
            if ($this->getCFGDef('maxDocs', 0) && !$this->getCFGDef(
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1091
                'reversePagination',
1092
                0
1093
            ) && $this->extPaginate->currentPage() == $this->extPaginate->totalPage()
1094
            ) {
1095
                $iteration += $this->getCFGDef('display', 0);
1096
            }
1097
            $iteration += $this->getCFGDef(
1098
                'display',
1099
                0
1100
            ) * ($this->extPaginate->currentPage() - 1) - $offset;
1101
        }
1102
1103
        $data[$this->getCFGDef(
1104
            "sysKey",
1105
            "dl"
1106
        ) . '.full_iteration'] = $iteration;
1107
1108
        if ($i == 1) {
1109
            $this->renderTPL = $this->getCFGDef('tplFirst', $this->renderTPL);
1110
            $class[] = $this->getCFGDef('firstClass', 'first');
1111
        }
1112
        if ($i == (count($this->_docs) - $this->skippedDocs)) {
1113
            $this->renderTPL = $this->getCFGDef('tplLast', $this->renderTPL);
1114
            $class[] = $this->getCFGDef('lastClass', 'last');
1115
        }
1116
        if ($this->modx->documentIdentifier == $data['id']) {
1117
            $this->renderTPL = $this->getCFGDef('tplCurrent', $this->renderTPL);
1118
            $data[$this->getCFGDef(
1119
                "sysKey",
1120
                "dl"
1121
            ) . '.active'] = 1; //[+active+] - 1 if $modx->documentIdentifer equal ID this element
1122
            $class[] = $this->getCFGDef('currentClass', 'current');
1123
        } else {
1124
            $data[$this->getCFGDef("sysKey", "dl") . '.active'] = 0;
1125
        }
1126
1127
        $class = implode(" ", $class);
1128
        $data[$this->getCFGDef("sysKey", "dl") . '.class'] = $class;
1129
1130
        /**
1131
         * @var $extE e_DL_Extender
1132
         */
1133
        $extE = $this->getExtender('e', true, true);
1134
        if ($out = $extE->init($this, compact('data'))) {
1135
            if (is_array($out)) {
1136
                $data = $out;
1137
            }
1138
        }
1139
1140
        return compact('class', 'iterationName');
1141
    }
1142
1143
    /**
1144
     * Формирование JSON ответа
1145
     *
1146
     * @param array $data массив данных которые подготавливаются к выводу в JSON
1147
     * @param mixed $fields список полей учавствующих в JSON ответе. может быть либо массив, либо строка с разделителем , (запятая)
1148
     * @param array $array данные которые необходимо примешать к ответу на каждой записи $data
1149
     * @return string JSON строка
1150
     */
1151
    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...
1152
    {
1153
        $out = array();
1154
        $fields = is_array($fields) ? $fields : explode(",", $fields);
1155
        if (is_array($array) && count($array) > 0) {
1156
            $tmp = array();
1157
            foreach ($data as $i => $v) { //array_merge not valid work with integer index key
1158
                $tmp[$i] = (isset($array[$i]) ? array_merge($v, $array[$i]) : $v);
1159
            }
1160
            $data = $tmp;
1161
        }
1162
1163
        foreach ($data as $num => $doc) {
1164
            $tmp = array();
1165
            foreach ($doc as $name => $value) {
1166
                if (in_array($name, $fields) || (isset($fields[0]) && $fields[0] == '1')) {
1167
                    $tmp[str_replace(".", "_", $name)] = $value; //JSON element name without dot
1168
                }
1169
            }
1170
            $out[$num] = $tmp;
1171
        }
1172
1173
        if ('new' == $this->getCFGDef('JSONformat', 'old')) {
1174
            $return = array();
1175
1176
            $return['rows'] = array();
1177
            foreach ($out as $key => $item) {
1178
                $return['rows'][] = $item;
1179
            }
1180
            $return['total'] = $this->getChildrenCount();
1181
        } elseif ('simple' == $this->getCFGDef('JSONformat', 'old')) {
1182
            $return = array();
1183
            foreach ($out as $key => $item) {
1184
                $return[] = $item;
1185
            }
1186
        } else {
1187
            $return = $out;
1188
        }
1189
        $this->outData = json_encode($return);
1190
        $this->isErrorJSON($return);
1191
1192
        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...
1193
    }
1194
1195
    /**
1196
     * @param array $item
1197
     * @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...
1198
     * @param string $introField
1199
     * @param string $contentField
1200
     * @return mixed|string
1201
     */
1202
    protected function getSummary(array $item = array(), $extSummary = null, $introField = '', $contentField = '')
1203
    {
1204
        $out = '';
1205
1206
        if (is_null($extSummary)) {
0 ignored issues
show
introduced by
The condition is_null($extSummary) is always true.
Loading history...
1207
            /**
1208
             * @var $extSummary summary_DL_Extender
1209
             */
1210
            $extSummary = $this->getExtender('summary');
1211
        }
1212
        $introField = $this->getCFGDef("introField", $introField);
1213
        $contentField = $this->getCFGDef("contentField", $contentField);
1214
1215
        if (!empty($introField) && !empty($item[$introField]) && mb_strlen($item[$introField], 'UTF-8') > 0) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1216
            $out = $item[$introField];
1217
        } else {
1218
            if (!empty($contentField) && !empty($item[$contentField]) && mb_strlen($item[$contentField], 'UTF-8') > 0) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1219
                $out = $extSummary->init($this, array(
1220
                    "content"      => $item[$contentField],
1221
                    "action"       => $this->getCFGDef("summary", ""),
1222
                    "cutSummary"   => $this->getCFGDef('cutSummary'),
1223
                    "dotSummary"   => $this->getCFGDef('dotSummary'),
1224
                    'breakSummary' => $this->getCFGDef('breakSummary')
1225
                ));
1226
            }
1227
        }
1228
1229
        return $out;
1230
    }
1231
1232
    /**
1233
     * @param string $name extender name
1234
     * @return boolean status extender load
1235
     */
1236 58
    public function checkExtender($name)
1237
    {
1238 58
        return (isset($this->extender[$name]) && $this->extender[$name] instanceof $name . "_DL_Extender");
1239
    }
1240
1241
    /**
1242
     * @param $name
1243
     * @param $obj
1244
     */
1245
    public function setExtender($name, $obj)
1246
    {
1247
        $this->extender[$name] = $obj;
1248
    }
1249
1250
    /**
1251
     * Вытащить экземпляр класса экстендера из общего массива экстендеров
1252
     *
1253
     * @param string $name имя экстендера
1254
     * @param bool $autoload Если экстендер не загружен, то пытаться ли его загрузить
1255
     * @param bool $nop если экстендер не загружен, то загружать ли xNop
1256
     * @return null|xNop
1257
     */
1258 58
    public function getExtender($name, $autoload = false, $nop = false)
1259
    {
1260 58
        $out = null;
1261 58
        if ((is_scalar($name) && $this->checkExtender($name)) || ($autoload && $this->_loadExtender($name))) {
1262 58
            $out = $this->extender[$name];
1263 58
        }
1264 58
        if ($nop && is_null($out)) {
1265
            $out = new xNop();
1266
        }
1267
1268 58
        return $out;
1269
    }
1270
1271
    /**
1272
     * load extender
1273
     *
1274
     * @param string $name name extender
1275
     * @return boolean $flag status load extender
1276
     */
1277 58
    protected function _loadExtender($name)
1278
    {
1279 58
        $this->debug->debug('Load Extender ' . $this->debug->dumpData($name), 'LoadExtender', 2);
1280 58
        $flag = false;
1281
1282 58
        $classname = ($name != '') ? $name . "_DL_Extender" : "";
1283 58
        if ($classname != '' && isset($this->extender[$name]) && $this->extender[$name] instanceof $classname) {
1284
            $flag = true;
1285
        } else {
1286 58
            if (!class_exists($classname, false) && $classname != '') {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1287 2
                if (file_exists(dirname(__FILE__) . "/extender/" . $name . ".extender.inc")) {
1288 2
                    include_once(dirname(__FILE__) . "/extender/" . $name . ".extender.inc");
1289 2
                }
1290 2
            }
1291 58
            if (class_exists($classname, false) && $classname != '') {
1292 58
                $this->extender[$name] = new $classname($this, $name);
1293 58
                $flag = true;
1294 58
            }
1295
        }
1296 58
        if (!$flag) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1297
            $this->debug->debug("Error load Extender " . $this->debug->dumpData($name));
1298
        }
1299 58
        $this->debug->debugEnd('LoadExtender');
1300
1301 58
        return $flag;
1302
    }
1303
1304
    /*************************************************
1305
     ****************** IDs BLOCK ********************
1306
     ************************************************/
1307
1308
    /**
1309
     * Очистка массива $IDs по которому потом будет производиться выборка документов
1310
     *
1311
     * @param mixed $IDs список id документов по которым необходима выборка
1312
     * @return array очищенный массив
1313
     */
1314 58
    public function setIDs($IDs)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (4) exceeds 3; consider refactoring the function
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...
1315
    {
1316 58
        $this->debug->debug('set ID list ' . $this->debug->dumpData($IDs), 'setIDs', 2);
1317 58
        $IDs = $this->cleanIDs($IDs);
1318 58
        $type = $this->getCFGDef('idType', 'parents');
1319 58
        $depth = $this->getCFGDef('depth', '');
1320 58
        if ($type == 'parents' && $depth > 0) {
1321
            $out = $this->extCache->load('children');
1322
            if ($out === false) {
0 ignored issues
show
introduced by
The condition $out === false is always false.
Loading history...
1323
                $tmp = $IDs;
1324
                do {
1325
                    if (count($tmp) > 0) {
1326
                        $tmp = $this->getChildrenFolder($tmp);
1327
                        $IDs = array_merge($IDs, $tmp);
1328
                    }
1329
                } while ((--$depth) > 0);
1330
                $this->extCache->save($IDs, 'children');
1331
            } else {
1332
                $IDs = $out;
1333
            }
1334
        }
1335 58
        $this->debug->debugEnd("setIDs");
1336
1337 58
        return ($this->IDs = $IDs);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->IDs = $IDs also could return the type string which is incompatible with the documented return type array.
Loading history...
1338
    }
1339
1340
    /**
1341
     * @return int
1342
     */
1343
    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...
1344
    {
1345
        return $this->IDs;
1346
    }
1347
1348
    /**
1349
     * Очистка данных и уникализация списка цифр.
1350
     * Если был $IDs был передан как строка, то эта строка будет преобразована в массив по разделителю $sep
1351
     * @param mixed $IDs данные для обработки
1352
     * @param string $sep разделитель
1353
     * @return array очищенный массив с данными
1354
     */
1355 58
    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...
1356
    {
1357 58
        $this->debug->debug(
1358 58
            'clean IDs ' . $this->debug->dumpData($IDs) . ' with separator ' . $this->debug->dumpData($sep),
1359 58
            'cleanIDs',
1360
            2
1361 58
        );
1362 58
        $out = array();
1363 58
        if (!is_array($IDs)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1364 58
            $IDs = explode($sep, $IDs);
1365 58
        }
1366 58
        foreach ($IDs as $item) {
1367 58
            $item = trim($item);
1368 58
            if (is_numeric($item) && (int)$item >= 0) { //Fix 0xfffffffff
1369 58
                $out[] = (int)$item;
1370 58
            }
1371 58
        }
1372 58
        $out = array_unique($out);
1373 58
        $this->debug->debugEnd("cleanIDs");
1374
1375 58
        return $out;
1376
    }
1377
1378
    /**
1379
     * Проверка массива с id-шниками документов для выборки
1380
     * @return boolean пригодны ли данные для дальнейшего использования
1381
     */
1382
    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...
1383
    {
1384
        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...
1385
    }
1386
1387
    /**
1388
     * Get all field values from array documents
1389
     *
1390
     * @param string $userField field name
1391
     * @param boolean $uniq Only unique values
1392
     * @global array $_docs all documents
1393
     * @return array all field values
1394
     */
1395
    public function getOneField($userField, $uniq = false)
1396
    {
1397
        $out = array();
1398
        foreach ($this->_docs as $doc => $val) {
1399
            if (isset($val[$userField]) && (($uniq && !in_array($val[$userField], $out)) || !$uniq)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1400
                $out[$doc] = $val[$userField];
1401
            }
1402
        }
1403
1404
        return $out;
1405
    }
1406
1407
    /**
1408
     * @return DLCollection
1409
     */
1410
    public function docsCollection()
1411
    {
1412
        return new DLCollection($this->modx, $this->_docs);
1413
    }
1414
1415
    /**********************************************************
1416
     ********************** SQL BLOCK *************************
1417
     *********************************************************/
1418
1419
    /**
1420
     * Подсчет документов удовлетворящиюх выборке
1421
     *
1422
     * @return int Число дочерних документов
1423
     */
1424
    abstract public function getChildrenCount();
1425
1426
    /**
1427
     * Выборка документов которые являются дочерними относительно $id документа и в тоже время
1428
     * являются родителями для каких-нибудь других документов
1429
     *
1430
     * @param string|array $id значение PrimaryKey родителя
1431
     * @return array массив документов
1432
     */
1433
    abstract public function getChildrenFolder($id);
1434
1435
    /**
1436
     * @param string $group
1437
     * @return string
1438
     */
1439 27
    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...
1440
    {
1441 27
        $out = '';
1442 27
        if ($group != '') {
1443 27
            $out = 'GROUP BY ' . $group;
1444 27
        }
1445
1446 27
        return $out;
1447
    }
1448
1449
    /**
1450
     *    Sorting method in SQL queries
1451
     *
1452
     * @global string $order
1453
     * @global string $orderBy
1454
     * @global string $sortBy
1455
     *
1456
     * @param string $sortName default sort field
1457
     * @param string $orderDef default order (ASC|DESC)
1458
     *
1459
     * @return string Order by for SQL
1460
     */
1461 27
    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...
1462
    {
1463 27
        $this->debug->debug('', 'sortORDER', 2);
1464
1465 27
        $sort = '';
1466 27
        switch ($this->getCFGDef('sortType', '')) {
1467 27
            case 'none':
1468
                break;
1469 27
            case 'doclist':
1470
                $idList = $this->sanitarIn($this->IDs, ',', false);
1471
                $out = array('orderBy' => "FIND_IN_SET({$this->getCFGDef('sortBy', $this->getPK())}, '{$idList}')");
1472
                $this->config->setConfig($out); //reload config;
1473
                $sort = "ORDER BY " . $out['orderBy'];
1474
                break;
1475 27
            default:
1476 27
                $out = array('orderBy' => '', 'order' => '', 'sortBy' => '');
1477 27
                if (($tmp = $this->getCFGDef('orderBy', '')) != '') {
1478
                    $out['orderBy'] = $tmp;
1479
                } else {
1480
                    switch (true) {
1481 27
                        case ('' != ($tmp = $this->getCFGDef('sortDir', ''))): //higher priority than order
1482
                            $out['order'] = $tmp;
1483
                        // no break
1484 27
                        case ('' != ($tmp = $this->getCFGDef('order', ''))):
1485
                            $out['order'] = $tmp;
1486
                        // no break
1487
                    }
1488 27
                    if ('' == $out['order'] || !in_array(strtoupper($out['order']), array('ASC', 'DESC'))) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1489 27
                        $out['order'] = $orderDef; //Default
1490 27
                    }
1491
1492 27
                    $out['sortBy'] = (($tmp = $this->getCFGDef('sortBy', '')) != '') ? $tmp : $sortName;
1493 27
                    $out['orderBy'] = $out['sortBy'] . " " . $out['order'];
1494
                }
1495 27
                $this->config->setConfig($out); //reload config;
1496 27
                $sort = "ORDER BY " . $out['orderBy'];
1497 27
                break;
1498 27
        }
1499 27
        $this->debug->debugEnd("sortORDER", 'Get sort order for SQL: ' . $this->debug->dumpData($sort));
1500
1501 27
        return $sort;
1502
    }
1503
1504
    /**
1505
     * Получение LIMIT вставки в SQL запрос
1506
     *
1507
     * @return string LIMIT вставка в SQL запрос
1508
     */
1509 27
    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...
1510
    {
1511 27
        $this->debug->debug('', 'limitSQL', 2);
1512 27
        $ret = '';
1513 27
        if ($limit == 0) {
1514 27
            $limit = $this->getCFGDef('display', 0);
1515 27
        }
1516 27
        $maxDocs = $this->getCFGDef('maxDocs', 0);
1517 27
        if ($maxDocs > 0 && $limit > $maxDocs) {
1518
            $limit = $maxDocs;
1519
        }
1520 27
        if ($offset == 0) {
1521 27
            $offset = $this->getCFGDef('offset', 0);
1522 27
        }
1523 27
        $offset += $this->getCFGDef('start', 0);
1524 27
        $total = $this->getCFGDef('total', 0);
1525 27
        if ($limit < ($total - $limit)) {
1526
            $limit = $total - $offset;
1527
        }
1528
1529 27
        if ($limit != 0) {
1530 27
            $ret = "LIMIT " . (int)$offset . "," . (int)$limit;
1531 27
        } else {
1532
            if ($offset != 0) {
1533
                /**
1534
                 * 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
1535
                 * @see http://dev.mysql.com/doc/refman/5.0/en/select.html
1536
                 */
1537
                $ret = "LIMIT " . (int)$offset . ",18446744073709551615";
1538
            }
1539
        }
1540 27
        $this->debug->debugEnd("limitSQL", "Get limit for SQL: " . $this->debug->dumpData($ret));
1541
1542 27
        return $ret;
1543
    }
1544
1545
    /**
1546
     * Clean up the modx and html tags
1547
     *
1548
     * @param string $data String for cleaning
1549
     * @param string $charset
1550
     * @return string Clear string
1551
     */
1552 58
    public function sanitarData($data, $charset = 'UTF-8')
1553
    {
1554 58
        return APIHelpers::sanitarTag($data, $charset);
1555
    }
1556
1557
    /**
1558
     * run tree build
1559
     *
1560
     * @param string $idField default name id field
1561
     * @param string $parentField default name parent field
1562
     * @return array
1563
     */
1564
    public function treeBuild($idField = 'id', $parentField = 'parent')
1565
    {
1566
        return $this->_treeBuild(
1567
            $this->_docs,
1568
            $this->getCFGDef('idField', $idField),
1569
            $this->getCFGDef('parentField', $parentField)
1570
        );
1571
    }
1572
1573
    /**
1574
     * @see: https://github.com/DmitryKoterov/DbSimple/blob/master/lib/DbSimple/Generic.php#L986
1575
     *
1576
     * @param array $data Associative data array
1577
     * @param string $idName name ID field in associative data array
1578
     * @param string $pidName name parent field in associative data array
1579
     * @return array
1580
     */
1581
    private function _treeBuild($data, $idName, $pidName)
1582
    {
1583
        $children = array(); // children of each ID
1584
        $ids = array();
1585
        foreach ($data as $i => $r) {
1586
            $row =& $data[$i];
1587
            $id = $row[$idName];
1588
            $pid = $row[$pidName];
1589
            $children[$pid][$id] =& $row;
1590
            if (!isset($children[$id])) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1591
                $children[$id] = array();
1592
            }
1593
            $row['#childNodes'] =& $children[$id];
1594
            $ids[$row[$idName]] = true;
1595
        }
1596
        // Root elements are elements with non-found PIDs.
1597
        $this->_tree = array();
1598
        foreach ($data as $i => $r) {
1599
            $row =& $data[$i];
1600
            if (!isset($ids[$row[$pidName]])) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1601
                $this->_tree[$row[$idName]] = $row;
1602
            }
1603
        }
1604
1605
        return $this->_tree;
1606
    }
1607
1608
    /**
1609
     * Получение PrimaryKey основной таблицы.
1610
     * По умолчанию это id. Переопределить можно в контроллере присвоив другое значение переменной idField
1611
     * @param bool $full если true то возвращается значение для подстановки в запрос
1612
     * @return string PrimaryKey основной таблицы
1613
     */
1614 27
    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...
1615
    {
1616 27
        $idField = isset($this->idField) ? $this->idField: 'id';
1617 27
        if ($full) {
1618 27
            $idField = '`' . $idField . '`';
1619 27
            if (!empty($this->alias)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1620 27
                $idField = '`' . $this->alias . '`.' . $idField;
1621 27
            }
1622 27
        }
1623
1624 27
        return $idField;
1625
    }
1626
1627
    /**
1628
     * Получение Parent key
1629
     * По умолчанию это parent. Переопределить можно в контроллере присвоив другое значение переменной parentField
1630
     * @param bool $full если true то возвращается значение для подстановки в запрос
1631
     * @return string Parent Key основной таблицы
1632
     */
1633 9
    public function getParentField($full = true)
1634
    {
1635 9
        $parentField = isset($this->parentField) ? $this->parentField : '';
1636 9
        if ($full && !empty($parentField)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1637 9
            $parentField = '`' . $parentField . '`';
1638 9
            if (!empty($this->alias)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1639 9
                $parentField = '`' . $this->alias . '`.' . $parentField;
1640 9
            }
1641 9
        }
1642
1643 9
        return $parentField;
1644
    }
1645
1646
    /**
1647
     * Разбор фильтров
1648
     * OR(AND(filter:field:operator:value;filter2:field:oerpator:value);(...)), etc.
1649
     *
1650
     * @param string $filter_string строка со всеми фильтрами
1651
     * @return mixed результат разбора фильтров
1652
     */
1653 58
    protected function getFilters($filter_string)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (5) exceeds 3; consider refactoring the function
Loading history...
1654
    {
1655 58
        $this->debug->debug("getFilters: " . $this->debug->dumpData($filter_string), 'getFilter', 1);
1656
        // the filter parameter tells us, which filters can be used in this query
1657 58
        $filter_string = ltrim(trim($filter_string, ';'));
1658 58
        if (!$filter_string) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1659 58
            return;
1660
        }
1661 19
        $output = array('join' => '', 'where' => '');
1662 19
        $logic_op_found = false;
1663 19
        $joins = $wheres = array();
1664 19
        foreach ($this->_logic_ops as $op => $sql) {
1665 19
            if (strpos($filter_string, $op) === 0) {
1666 10
                $logic_op_found = true;
1667 10
                $subfilters = mb_substr($filter_string, strlen($op) + 1, mb_strlen($filter_string, "UTF-8"), "UTF-8");
1668 10
                $subfilters = $this->smartSplit($subfilters);
1669 10
                $lastFilter = '';
1670 10
                foreach ($subfilters as $filter) {
1671
                    /**
1672
                     * С правой стороны не выполняется trim, т.к. там находятся значения. А они могу быть чувствительны к пробелам
1673
                     */
1674 10
                    $subfilter = $this->getFilters(ltrim($filter) . ltrim($lastFilter));
1675 10
                    if (!$subfilter) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1676 6
                        $lastFilter = explode(';', $filter, 2);
1677 6
                        $subfilter = isset($lastFilter[1]) ? $this->getFilters($lastFilter[1]) : '';
1678 6
                        $lastFilter = $lastFilter[0];
1679 6
                        if (!$subfilter) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1680 5
                            continue;
1681
                        }
1682 1
                    }
1683 10
                    if ($subfilter['join']) {
1684 10
                        $joins[] = $subfilter['join'];
1685 10
                    }
1686 10
                    if ($subfilter['where']) {
1687 10
                        $wheres[] = $subfilter['where'];
1688 10
                    }
1689 10
                }
1690 10
                $output['join'] = !empty($joins) ? implode(' ', array_reverse($joins)) : '';
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1691 10
                $output['where'] = !empty($wheres) ? '(' . implode($sql, array_reverse($wheres)) . ')' : '';
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1692 10
            }
1693 19
        }
1694
1695 19
        if (!$logic_op_found) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1696 19
            $filter = $this->loadFilter($filter_string);
1697 19
            if (!$filter) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1698 5
                $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

1698
                $this->debug->/** @scrutinizer ignore-call */ 
1699
                              warning('Error while loading DocLister filter "' . $this->debug->dumpData($filter_string) . '": check syntax!');
Loading history...
1699 5
                $output = false;
1700 5
            } else {
1701 18
                $output['join'] = $filter->get_join();
1702 18
                $output['where'] = $filter->get_where();
1703
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
1704
            }
1705 19
        }
1706 19
        $this->debug->debug('getFilter');
1707
1708 19
        return $output;
1709
    }
1710
1711
    /**
1712
     * @return mixed
1713
     */
1714
    public function filtersWhere()
1715
    {
1716
        return APIHelpers::getkey($this->_filters, 'where', '');
1717
    }
1718
1719
    /**
1720
     * @return mixed
1721
     */
1722
    public function filtersJoin()
1723
    {
1724
        return APIHelpers::getkey($this->_filters, 'join', '');
1725
    }
1726
1727
    /**
1728
     * @param string $join
1729
     * @return $this
1730
     */
1731
    public function setFiltersJoin($join = '')
1732
    {
1733
        if (!empty($join)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1734
            if (!empty($this->_filters['join'])) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1735
                $this->_filters['join'] .= ' ' . $join;
1736
            } else {
1737
                $this->_filters['join'] = $join;
1738
            }
1739
        }
1740
1741
        return $this;
1742
    }
1743
1744
    /**
1745
     * Приведение типа поля
1746
     *
1747
     * @param $field string имя поля
1748
     * @param $type string тип фильтрации
1749
     * @return string имя поля с учетом приведения типа
1750
     */
1751 8
    public function changeSortType($field, $type)
1752
    {
1753 8
        $type = trim($type);
1754 8
        switch (strtoupper($type)) {
1755 8
            case 'DECIMAL':
1756 1
                $field = 'CAST(' . $field . ' as DECIMAL(10,2))';
1757 1
                break;
1758 8
            case 'UNSIGNED':
1759 1
                $field = 'CAST(' . $field . ' as UNSIGNED)';
1760 1
                break;
1761 8
            case 'BINARY':
1762 1
                $field = 'CAST(' . $field . ' as BINARY)';
1763 1
                break;
1764 7
            case 'DATETIME':
1765 1
                $field = 'CAST(' . $field . ' as DATETIME)';
1766 1
                break;
1767 6
            case 'SIGNED':
1768
                $field = 'CAST(' . $field . ' as SIGNED)';
1769
                break;
1770 8
        }
1771
1772 8
        return $field;
1773
    }
1774
1775
    /**
1776
     * Загрузка фильтра
1777
     * @param string $filter срока с параметрами фильтрации
1778
     * @return bool
1779
     */
1780 19
    protected function loadFilter($filter)
1781
    {
1782 19
        $this->debug->debug('Load filter ' . $this->debug->dumpData($filter), 'loadFilter', 2);
1783 19
        $out = false;
1784 19
        $fltr_params = explode(':', $filter, 2);
1785 19
        $fltr = APIHelpers::getkey($fltr_params, 0, null);
1786
        /**
1787
        * @var tv_DL_filter|content_DL_filter $fltr_class
1788
        */
1789 19
        $fltr_class = $fltr . '_DL_filter';
1790
        // check if the filter is implemented
1791 19
        if (!is_null($fltr)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1792 19
            if (!class_exists($fltr_class) && file_exists(__DIR__ . '/filter/' . $fltr . '.filter.php')) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after 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

1792
            if (!class_exists(/** @scrutinizer ignore-type */ $fltr_class) && file_exists(__DIR__ . '/filter/' . $fltr . '.filter.php')) {
Loading history...
1793
                require_once dirname(__FILE__) . '/filter/' . $fltr . '.filter.php';
1794
            }
1795 19
            if (class_exists($fltr_class)) {
1796 18
                $this->totalFilters++;
1797 18
                $fltr_obj = new $fltr_class();
1798 18
                if ($fltr_obj->init($this, $filter)) {
1799 18
                    $out = $fltr_obj;
1800 18
                } else {
1801
                    $this->debug->error("Wrong filter parameter: '{$this->debug->dumpData($filter)}'", 'Filter');
1802
                }
1803 18
            }
1804 19
        }
1805 19
        if (!$out) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after NOT operator; 0 found
Loading history...
1806 5
            $this->debug->error("Error load Filter: '{$this->debug->dumpData($filter)}'", 'Filter');
1807 5
        }
1808
1809 19
        $this->debug->debugEnd("loadFilter");
1810
1811 19
        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...
1812
    }
1813
1814
    /**
1815
     * Общее число фильтров
1816
     * @return int
1817
     */
1818 18
    public function getCountFilters()
1819
    {
1820 18
        return (int)$this->totalFilters;
1821
    }
1822
1823
    /**
1824
     * Выполнить SQL запрос
1825
     * @param string $q SQL запрос
1826
     */
1827 27
    public function dbQuery($q)
1828
    {
1829 27
        $this->debug->debug($q, "query", 1, 'sql');
1830 27
        $out = $this->modx->db->query($q);
1831 27
        $this->debug->debugEnd("query");
1832
1833 27
        return $out;
1834
    }
1835
1836
    /**
1837
     * Экранирование строки в SQL запросе LIKE
1838
     * @see: http://stackoverflow.com/a/3683868/2323306
1839
     *
1840
     * @param string $field поле по которому осуществляется поиск
1841
     * @param string $value искомое значение
1842
     * @param string $escape экранирующий символ
1843
     * @param string $tpl шаблон подстановки значения в SQL запрос
1844
     * @return string строка для подстановки в SQL запрос
1845
     */
1846 3
    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...
1847
    {
1848 3
        return sqlHelper::LikeEscape($this->modx, $field, $value, $escape, $tpl);
1849
    }
1850
1851
    /**
1852
     * Получение REQUEST_URI без GET-ключа с
1853
     * @return string
1854
     */
1855
    public function getRequest()
1856
    {
1857
        $URL = null;
1858
        parse_str(parse_url(MODX_SITE_URL . $_SERVER['REQUEST_URI'], PHP_URL_QUERY), $URL);
1859
1860
        return http_build_query(array_merge($URL, array(DocLister::AliasRequest => null)));
1861
    }
1862
}
1863