QueryBridge   F
last analyzed

Complexity

Total Complexity 121

Size/Duplication

Total Lines 665
Duplicated Lines 0 %

Coupling/Cohesion

Dependencies 8

Importance

Changes 13
Bugs 5 Features 1
Metric Value
wmc 121
c 13
b 5
f 1
cbo 8
dl 0
loc 665
rs 1.3736

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A execute() 0 12 2
C prepare() 0 73 16
B selectQuery() 0 14 6
A deleteQuery() 0 15 4
A updateQuery() 0 15 4
A insertQuery() 0 14 2
A getOrderBy() 0 13 2
B getSelectJoins() 0 33 6
A getInsertValues() 0 13 2
A getCleanInsertValue() 0 19 4
A getUpdateSet() 0 6 2
C getSelectColumns() 0 25 8
A getColumnsAliases() 0 4 2
A getColumnAlias() 0 11 3
D getSelectFrom() 0 62 15
A getTableAlias() 0 10 3
C getSelectColumnsFromTables() 0 46 12
D getSelectColumnsFromString() 0 86 18
B getWhere() 0 19 5
A getLimit() 0 8 2
A getGroupBy() 0 4 1
A getQueryString() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like QueryBridge often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use QueryBridge, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Fwk
4
 *
5
 * Copyright (c) 2011-2012, Julien Ballestracci <[email protected]>.
6
 * All rights reserved.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
12
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
13
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
15
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
16
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
17
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
21
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
 * POSSIBILITY OF SUCH DAMAGE.
23
 *
24
 * PHP Version 5.3
25
 *
26
 * @package    Fwk
27
 * @subpackage Db
28
 * @author     Julien Ballestracci <[email protected]>
29
 * @copyright  2011-2012 Julien Ballestracci <[email protected]>
30
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
31
 * @link       http://www.phpfwk.com
32
 */
33
namespace Fwk\Db;
34
35
use Fwk\Db\Connection;
36
use Fwk\Db\Query;
37
use Doctrine\DBAL\Query\QueryBuilder;
38
39
/**
40
 * This class transform a Fwk\Db\Query object into a SQL query string
41
 *
42
 */
43
class QueryBridge
44
{
45
    const STATE_INIT    = 0;
46
    const STATE_READY   = 1;
47
    const STATE_ERROR   = 2;
48
49
    /**
50
     * The Connection
51
     *
52
     * @var Connection
53
     */
54
    protected $connection;
55
56
    /**
57
     *
58
     * @var array
59
     */
60
    protected $tablesAliases;
61
62
    /**
63
     *
64
     * @var array
65
     */
66
    protected $columnsAliases;
67
68
    /**
69
     *
70
     * @var QueryBuilder
71
     */
72
    protected $handle;
73
74
    protected $state = self::STATE_INIT;
75
76
    protected $queryString;
77
78
    public function __construct(Connection $connection)
79
    {
80
        $this->connection = $connection;
81
    }
82
83
    /**
84
     *
85
     * @param  Query                             $query
86
     * @param  array                             $params
87
     * @param  array                             $options
88
     * @return \Doctrine\DBAL\Query\QueryBuilder
89
     */
90
    public function execute(Query $query, array $params = array(), array $options = array())
91
    {
92
        $this->queryString = $sql = $this->prepare($query, $options);
0 ignored issues
show
Unused Code introduced by
The call to QueryBridge::prepare() has too many arguments starting with $options.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
93
94
        if ($query->getType() == Query::TYPE_INSERT) {
95
            return $this->connection->getDriver()->executeUpdate($sql, $params);
96
        }
97
98
        $this->handle->setParameters($params);
99
100
        return $this->handle->execute();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->handle->execute(); (Doctrine\DBAL\Driver\Statement|integer) is incompatible with the return type documented by Fwk\Db\QueryBridge::execute of type Doctrine\DBAL\Query\QueryBuilder.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
101
    }
102
103
    /**
104
     *
105
     * @param Query $query
106
     *
107
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
108
     */
109
    public function prepare(Query $query)
110
    {
111
        if ($this->state !== self::STATE_INIT) {
112
            return null;
113
        }
114
115
        $this->handle   = $this->connection->getDriver()->createQueryBuilder();
116
        $type           = $query->getType();
117
        switch ($type) {
118
            case Query::TYPE_DELETE:
119
                $parts  = array(
120
                    'delete' => true
121
                );
122
                $call = array($this, 'deleteQuery');
123
                break;
124
125
            case Query::TYPE_INSERT:
126
                $parts  = array(
127
                    'insert' => true,
128
                    'values' => true
129
                );
130
                $call = array($this, 'insertQuery');
131
                break;
132
133
            case Query::TYPE_SELECT:
134
                $parts  = array(
135
                    'select' => true,
136
                    'from' => true
137
                );
138
                $call = array($this, 'selectQuery');
139
                break;
140
141
            case Query::TYPE_UPDATE:
142
                $parts = array(
143
                    'update' => true
144
                );
145
                $call = array($this, 'updateQuery');
146
                break;
147
148
            default:
149
                return null;
150
        }
151
152
        foreach ($parts as $part => $required) {
153
            if ($required == true && !$query->offsetExists($part)) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
154
                throw new Exception(sprintf('Missing required query part "%s"', $part));
155
            }
156
        }
157
158
        $table = $query['from'];
159
        if (!empty($table)) {
160
            if(strpos($table, ' ')) {
161
                list($table, ) = explode(' ', $table);
162
            }
163
164
            $decl = $this->connection->table($table)->getDefaultEntity($table);
0 ignored issues
show
Unused Code introduced by
The call to Table::getDefaultEntity() has too many arguments starting with $table.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
165
            if(empty($query['entity']) || $query['entity'] == "\stdClass") {
166
                $query->entity($decl);
167
            }
168
        }
169
170
        if (!empty($query['entity']) && $query['entity'] != "\stdClass") {
171
            $obj        = new $query['entity'];
172
            $access     = new Accessor($obj);
173
            $relations  = $access->getRelations();
174
175
            foreach ($relations as $colName => $relation) {
176
                $relation->prepare($query, $colName);
177
            }
178
        }
179
180
        return trim(\call_user_func($call, $query));
181
    }
182
183
    /**
184
     *
185
     * @return void
0 ignored issues
show
Documentation introduced by
Should the return type not be string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
186
     */
187
    public function selectQuery(Query $query)
188
    {
189
        $queryJoins = $query['joins'];
190
191
        $this->getSelectFrom($query['from'], $queryJoins);
192
        $this->getSelectColumns($query['select'], $query['from'], $query, $queryJoins);
193
        if (isset($query['joins'])) { $this->getSelectJoins($queryJoins); }
194
        if (isset($query['where'])) { $this->getWhere($query); }
195
        if (isset($query['groupBy'])) { $this->getGroupBy($query['groupBy']); }
196
        if (isset($query['orderBy'])) { $this->getOrderBy($query['orderBy']); }
197
        if (isset($query['limit'])) { $this->getLimit($query['limit']); }
198
199
        return $this->handle->getSQL();
200
    }
201
202
    /**
203
     *
204
     * @return string
205
     */
206
    public function deleteQuery(Query $query)
207
    {
208
        $from = $query['delete'];
209
        if (strpos($from,' ')) {
210
            list($from, $alias) = explode(' ', $from);
211
        } else {
212
            $alias = null;
213
        }
214
215
        $this->handle->delete($from, $alias);
216
        if (isset($query['where'])) { $this->getWhere($query); }
217
        if (isset($query['limit'])) { $this->getLimit($query['limit']); }
218
219
        return $this->handle->getSQL();
220
    }
221
222
    /**
223
     *
224
     * @return string
225
     */
226
    public function updateQuery(Query $query)
227
    {
228
        $update = $query['update'];
229
        if (strpos($update,' ')) {
230
            list($update, $alias) = explode(' ', $update);
231
        } else {
232
            $alias = null;
233
        }
234
        $this->handle->update($update, $alias);
235
        $this->getUpdateSet($query['values']);
236
        if (isset($query['where'])) { $this->getWhere($query); }
237
        if (isset($query['limit'])) { $this->getLimit($query['limit']); }
238
239
        return $this->handle->getSQL();
240
    }
241
242
    /**
243
     *
244
     * @return string
245
     */
246
    public function insertQuery(Query $query)
247
    {
248
        $str = "INSERT INTO";
249
250
        $table = $query['insert'];
251
        if($table instanceof Table)
0 ignored issues
show
Coding Style introduced by
Please always use braces to surround the code block of IF statements.
Loading history...
252
            $table = $table->getName();
253
254
        $vals = $this->getInsertValues($query['values']);
255
256
        $query = implode(' ', array($str, $table, $vals));
257
258
        return $query;
259
    }
260
261
    protected function getOrderBy(array $orderBy)
262
    {
263
        $column = $orderBy['column'];
264
        $order = $orderBy['order'];
265
266
        if (strpos($column, '.') !== false) {
267
            list(, $column) = \explode('.', $column);
268
        }
269
270
        $col = $this->getColumnAlias($column);
271
272
        $this->handle->orderBy($col, $order);
273
    }
274
275
    protected function getSelectJoins(array $joins)
276
    {
277
        foreach ($joins as $join) {
278
279
            $type = $join['type'];
280
            $table = $join['table'];
281
            if (strpos($table, ' ') !== false) {
282
                list($table, ) = explode(' ', $table);
283
            }
284
285
            $keys = array_keys($this->tablesAliases);
286
            $first = array_shift($keys);
287
            $fromAlias = $this->getTableAlias($first);
288
            $alias = $this->getTableAlias($table);
289
            $local = $join['local'];
290
            $foreign = $join['foreign'];
291
292
            if (\strpos($foreign, '.') === false) {
293
                $foreign = $alias .'.'. $foreign;
294
            }
295
296
            if (\strpos($local, '.') === false) {
297
                $local = $fromAlias .'.'. $local;
298
            }
299
300
            $cond = sprintf('%s = %s', $local, $foreign);
301
            if ($type == Query::JOIN_LEFT) {
302
                $this->handle->leftJoin($fromAlias, $table, $alias, $cond);
303
            } else {
304
                $this->handle->join($fromAlias, $table, $alias, $cond);
305
            }
306
        }
307
    }
308
309
    protected function getInsertValues(array $values)
310
    {
311
        $cols = array_keys($values);
312
        $vals = array_values($values);
313
        $str = '(`'. implode('`, `', $cols) .'`) VALUES (';
314
        $final = array();
315
        foreach ($vals as $value) {
316
            $value = $this->getCleanInsertValue($value);
317
            array_push($final, $value);
318
        }
319
320
        return $str . implode(', ', $final) .')';
321
    }
322
323
    protected function getCleanInsertValue($value)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
324
    {
325
        $value = trim($value);
326
        if ($value === '?' || strpos($value, ':') === 0) {
327
            return $value;
328
        }
329
330
        /*
331
        if($value instanceof Expression)
332
333
            return (string) $value;
334
        */
335
336
        if ($value === null) {
337
            return 'NULL';
338
        }
339
340
        return $this->connection->getDriver()->quote((string) $value);
341
    }
342
343
    /**
344
     *
345
     * @param Array $values
346
     *
347
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
348
     */
349
    protected function getUpdateSet(array $values)
350
    {
351
        foreach ($values as $key => $value) {
352
            $this->handle->set($key, $value);
353
        }
354
    }
355
356
    /**
357
     *
358
     * @param mixed $columns
359
     *
360
     * @param mixed $tables
361
     *
362
     * @return void
363
     */
364
    protected function getSelectColumns($columns, $tables, Query $query, $joins = null)
365
    {
366
        if ($query->getFetchMode() != Query::FETCH_SPECIAL) {
367
            $this->handle->select((empty($query['select']) ? '*' : $query['select']));
368
            return;
369
        }
370
371
        if (!$columns || $columns == '*') {
372
            $columns = $this->columnsAliases = $this->getSelectColumnsFromTables($tables, $joins);
373
        } elseif (is_string($columns)) {
374
            $columns = $this->columnsAliases = $this->getSelectColumnsFromString($columns, $tables);
375
        }
376
377
        $final = array();
378
        foreach ($columns as $alias => $column) {
0 ignored issues
show
Bug introduced by
The expression $columns of type object|integer|double|null|array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
379
            if (!$column['function']) {
380
                array_push($final, $this->getTableAlias($column['table']) .'.'. trim($column['column']) .' AS '. $alias);
381
            } else {
382
                // it's a function
383
                array_push($final, $column['column'] .' AS '. $alias);
384
            }
385
        }
386
387
        $this->handle->select(\implode(', ', $final));
388
    }
389
390
    /**
391
     *
392
     * @return array
393
     */
394
    public function getColumnsAliases()
395
    {
396
        return (is_array($this->columnsAliases) ? $this->columnsAliases : array());
397
    }
398
399
    /**
400
     * @param string $colName
401
     *
402
     * @return int|string
403
     */
404
    public function getColumnAlias($colName)
405
    {
406
        $columns = $this->getColumnsAliases();
407
        foreach ($columns as $alias => $column) {
408
            if ($column['column'] == $colName) {
409
                return $alias;
410
            }
411
        }
412
413
        return $colName;
414
    }
415
416
    /**
417
     *
418
     * @param mixed $tables
419
     * @param array $joins
0 ignored issues
show
Documentation introduced by
Should the type for parameter $joins not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
420
     *
421
     * @return void
422
     */
423
    protected function getSelectFrom($tables, $joins = null)
424
    {
425
        if (is_string($tables) && \strpos($tables, ',') !== false) {
426
            $tables = \explode(',', $tables);
427
        }
428
429
        if (!is_array($tables)) {
430
            $tables = array($tables);
431
        }
432
433
        $joinned = array();
434
        if (is_array($joins)) {
435
            $js = $joins;
436
            foreach ($joins as $k => $join) {
437
                array_push($tables, $join['table']);
438
439
                if (strpos($join['table'], ' ') !== false) {
440
                    list($tble,$alias) = explode(' ', $join['table']);
441
                    $join['table'] = $tble;
442
                } else {
443
                    $tble = $join['table'];
444
                    $alias = 'j'. (is_array($this->tablesAliases) ? count($this->tablesAliases) : '0');
445
                }
446
447
                $js[$k]['table'] = $tble;
448
                $js[$k]['alias'] = $alias;
449
450
                array_push($joinned, $join['table']);
451
            }
452
        }
453
454
        $tbls = array();
455
        foreach ($tables as $table) {
456
            if ($table instanceof Table) {
457
                $table = $table->getName();
458
            }
459
460
            $table = trim($table);
461
            if (\strpos($table, ' ') !== false) {
462
                list($table, $alias) = \explode(' ', $table);
463
            } else {
464
                if (\is_array($this->tablesAliases)) {
465
                    $alias = array_search($table, $this->tablesAliases);
466
                    if (!$alias) {
467
                        $alias = 't'. count($this->tablesAliases);
468
                    }
469
                } else {
470
                    $alias = 't0';
471
                }
472
            }
473
474
            $this->tablesAliases[trim($alias)] = trim($table);
475
476
            if (!in_array($table, $joinned)) {
477
                \array_push($tbls, array('table' => $table, 'alias' => $alias));
478
            }
479
        }
480
481
        foreach ($tbls as $infos) {
482
            $this->handle->from($infos['table'], $infos['alias']);
483
        }
484
    }
485
486
487
    /**
488
     *
489
     * @return string
490
     */
491
    protected function getTableAlias($tableName)
492
    {
493
        if (!is_array($this->tablesAliases)) {
494
           return $tableName;
495
        }
496
497
        $k = \array_search($tableName, $this->tablesAliases);
498
499
        return (false === $k ? $tableName : $k);
500
    }
501
502
503
    /**
504
     *
505
     * @param mixed $tables
506
     *
507
     * @return array
508
     */
509
    protected function getSelectColumnsFromTables($tables, $joins = null)
510
    {
511
        srand();
512
513
        if (is_string($tables) && \strpos($tables, ',') !== false) {
514
            $tables = \explode(',', $tables);
515
        }
516
517
        if (!is_array($tables)) {
518
            $tables = array($tables);
519
        }
520
521
        if (is_array($joins) && count($joins)) {
522
            foreach ($joins as $join) {
523
                array_push($tables, $join['table']);
524
            }
525
        }
526
527
        $columns = array();
528
        foreach ($tables as $table) {
529
            $table = trim($table);
530
531
            if (is_string($table) && \strpos($table, ' ') !== false) {
532
                list($table, ) = \explode(' ', $table);
533
            }
534
535
            $cols = $this->connection->table($table)->getColumns();
536
            foreach ($cols as $column) {
537
                $colName = $column->getName();
538
                $asName = 'c'. \rand(1000, 9999);
539
                if (isset($columns[$asName])) {
540
                    srand();
541
                    $asName = 'c'. \rand(1000, 9999);
542
                }
543
544
                $columns[$asName] = array(
545
                    'table'        => $table,
546
                    'column'       => $colName,
547
                    'function'     => false,
548
                    'alias'        => false
549
                );
550
            }
551
        }
552
553
        return $columns;
554
    }
555
556
    /**
557
     *
558
     * @param string $str
559
     *
560
     * @return array
561
     */
562
    protected function getSelectColumnsFromString($str, $tables)
563
    {
564
        $str = trim($str);
565
        $current = null;
566
        $funcLevel = 0;
567
        $currentIsFunc = false;
568
        $columns = array();
569
        for ($x = 0; $x < strlen($str); $x++) {
0 ignored issues
show
Performance Best Practice introduced by
Consider avoiding function calls on each iteration of the for loop.

If you have a function call in the test part of a for loop, this function is executed on each iteration. Often such a function, can be moved to the initialization part and be cached.

// count() is called on each iteration
for ($i=0; $i < count($collection); $i++) { }

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
570
            $letter = $str{$x};
571
            $current .= $letter;
572
573
            if ($current == '*') {
574
                $wasStar = true;
575
                continue;
576
            }
577
578
            if ($letter == '(') {
579
                $funcLevel++;
580
            } elseif ($letter == ')') {
581
                $funcLevel--;
582
                $currentIsFunc = true;
583
            }
584
585
            $tbls = \array_values ($this->tablesAliases);
586
            $defaultTable = \array_shift ($tbls);
587
588
            if (($letter == ',' || $x == strlen($str) -1) && $funcLevel == 0) {
589
                $column = ($letter == ',' ? substr($current, 0, strlen($current)-1) : $current);
590
591
                if (!$currentIsFunc) {
592
                    if (\strpos($column, '.') !== false) {
593
                        list($table, $column) = \explode ('.', $column);
594
                        if (isset($this->tablesAliases[trim($table)])) {
595
                                $table = $this->tablesAliases[trim($table)];
596
                        }
597
                        if ($column == '*') {
598
                            $columns = array_merge($columns, $this->getSelectColumnsFromTables($table));
599
                            $x++;
600
                            $current = null;
601
                            continue;
602
                        }
603
                    } else {
604
                        $table = $defaultTable;
605
                    }
606
607
                    if (\stripos($column, 'AS') !== false) {
608
                        list($column, $asName) = explode((\strpos($column, 'as') !== false ? ' as ' : ' AS '), $column);
609
                    } else {
610
                        $asName = $column . '__'. \rand(1000, 9999);
611
                    }
612
613
                    $columns[$asName] = array(
614
                        'table'        => trim($table),
615
                        'column'       => trim($column),
616
                        'function'     => false,
617
                        'alias'        => true
618
                    );
619
                } else {
620
                    if (\stripos($column, 'AS') !== false
621
                        && \preg_match_all('/\) AS ([A-Za-z0-9_]+)/i', $column, $matchesarray)
622
                    ) {
623
                        $asName = $matchesarray[1][0];
624
                        $column = \substr($column, 0, strlen($column) - strlen($asName) - 4);
625
                    } else {
626
                        $asName = 'func__'. \rand(1000, 9999);
627
                    }
628
629
                    $columns[$asName] = array(
630
                        'table'        => $defaultTable,
631
                        'column'       => trim($column),
632
                        'function'     => true,
633
                        'alias'        => true
634
                    );
635
                }
636
637
                $current = null;
638
                $currentIsFunc = false;
639
            }
640
        }
641
642
        if (isset($wasStar)) {
643
            $columns = array_merge($columns, $this->getSelectColumnsFromTables($tables));
644
        }
645
646
        return $columns;
647
    }
648
649
    /**
650
     *
651
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
652
     */
653
    protected function getWhere(Query $query)
654
    {
655
        $where = $query['where'];
656
        $wheres = $query['wheres'];
657
658
        $this->handle->where($where);
659
660
        if(!is_array($wheres) OR !count($wheres))
0 ignored issues
show
Coding Style introduced by
Please always use braces to surround the code block of IF statements.
Loading history...
661
662
            return;
663
664
        foreach ($wheres as $w) {
665
            if ($w['type'] == Query::WHERE_OR) {
666
                $this->handle->orWhere($w['condition']);
667
            } else {
668
                $this->handle->andWhere($w['condition']);
669
            }
670
        }
671
    }
672
673
    /**
674
     *
675
     * @param mixed $limit
676
     *
677
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
678
     */
679
    protected function getLimit(array $limit)
680
    {
681
        if ($limit['first'] !== null) {
682
            $this->handle->setFirstResult($limit['first']);
683
        }
684
685
        $this->handle->setMaxResults($limit['max']);
686
    }
687
688
    /**
689
     *
690
     * @param mixed $groupBy
691
     *
692
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
693
     */
694
    protected function getGroupBy($groupBy)
695
    {
696
        $this->handle->groupBy($groupBy);
697
    }
698
699
    /**
700
     *
701
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
702
     */
703
    public function getQueryString()
704
    {
705
        return $this->queryString;
706
    }
707
}
708