Completed
Push — master ( 9ec4d8...8355c8 )
by Sébastien
08:31 queued 03:59
created

SqlSource   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 255
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 91.58%

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 8
dl 0
loc 255
ccs 87
cts 95
cp 0.9158
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getSelect() 0 4 1
B getData() 0 43 6
A __construct() 0 8 2
A setSelect() 0 6 1
A select() 0 8 2
C assignOptions() 0 30 7
A loadDefaultColumnModel() 0 9 1
A getMetadataReader() 0 8 2
A getDefaultMetadataReader() 0 13 3
A getQueryString() 0 8 2
A __toString() 0 12 3
1
<?php
2
3
/*
4
 * soluble-flexstore library
5
 *
6
 * @author    Vanvelthem Sébastien
7
 * @link      https://github.com/belgattitude/soluble-flexstore
8
 * @copyright Copyright (c) 2016-2017 Vanvelthem Sébastien
9
 * @license   MIT License https://github.com/belgattitude/soluble-flexstore/blob/master/LICENSE.md
10
 *
11
 */
12
13
namespace Soluble\FlexStore\Source\Zend;
14
15
use Soluble\FlexStore\Source\AbstractSource;
16
use Soluble\FlexStore\Source\QueryableSourceInterface;
17
use Soluble\FlexStore\ResultSet\ResultSet;
18
use Soluble\FlexStore\Exception;
19
use Soluble\FlexStore\Options;
20
use Zend\Db\Adapter\Adapter;
21
use Zend\Db\Sql\Sql;
22
use Zend\Db\Sql\Select;
23
use Zend\Db\Sql\Expression;
24
use ArrayObject;
25
use Soluble\FlexStore\Column\ColumnModel;
26
use Soluble\FlexStore\Column\Type\MetadataMapper;
27
use Soluble\Metadata\Reader as MetadataReader;
28
29
class SqlSource extends AbstractSource implements QueryableSourceInterface
30
{
31
    /**
32
     * @var Sql
33
     */
34
    protected $sql;
35
36
    /**
37
     * @var Select
38
     */
39
    protected $select;
40
41
    /**
42
     * @var Adapter
43
     */
44
    protected $adapter;
45
46
    /**
47
     * Initial params received in the constructor.
48
     *
49
     * @var ArrayObject
50
     */
51
    protected $params;
52
53
    /**
54
     * @var string
55
     */
56
    protected $query_string;
57
58
    /**
59
     * @var \Zend\Db\Adapter\Driver\Mysqli\Statement
60
     */
61
    protected static $cache_stmt_prototype;
62
63
    /**
64
     * @var \Zend\Db\Adapter\Driver\ResultInterface
65
     */
66
    protected static $cache_result_prototype;
67
68
    /**
69
     * @var ColumnModel
70
     */
71
    protected $columnModel;
72
73
    /**
74
     * @param Adapter $adapter
75
     * @param Select  $select
76
     */
77 69
    public function __construct(Adapter $adapter, Select $select = null)
78
    {
79 69
        $this->adapter = $adapter;
80 69
        $this->sql = new Sql($this->adapter);
81 69
        if ($select !== null) {
82 60
            $this->setSelect($select);
83 60
        }
84 69
    }
85
86
    /**
87
     * @param Select $select
88
     *
89
     * @return SqlSource
90
     */
91 64
    public function setSelect(Select $select)
92
    {
93 64
        $this->select = $select;
94
95 64
        return $this;
96
    }
97
98
    /**
99
     * @return Select
100
     */
101 38
    public function getSelect()
102
    {
103 38
        return $this->select();
104
    }
105
106
    /**
107
     * @return Select
108
     */
109 43
    public function select()
110
    {
111 43
        if ($this->select === null) {
112 14
            $this->select = $this->sql->select();
113 14
        }
114
115 43
        return $this->select;
116
    }
117
118
    /**
119
     * @param Select  $select
120
     * @param Options $options
121
     *
122
     * @return Select
123
     */
124 38
    protected function assignOptions(Select $select, Options $options)
125
    {
126 38
        if ($options->hasLimit()) {
127 8
            $select->limit($options->getLimit());
128 8
            if ($options->hasOffset()) {
129 3
                $select->offset($options->getOffset());
130 3
            }
131
132 8
            if ($options->getLimit() > 0) {
133
                /**
134
                 * For mysql queries, to allow counting rows we must prepend
135
                 * SQL_CALC_FOUND_ROWS to the select quantifiers.
136
                 */
137 6
                $calc_found_rows = 'SQL_CALC_FOUND_ROWS';
138 6
                $quant_state = $select->getRawState($select::QUANTIFIER);
139 6
                if ($quant_state !== null) {
140 1
                    if ($quant_state instanceof Expression) {
1 ignored issue
show
Bug introduced by
The class Zend\Db\Sql\Expression does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
141 1
                        $quant_state->setExpression($calc_found_rows . ' ' . $quant_state->getExpression());
142 1
                    } elseif (is_string($quant_state)) {
143 1
                        $quant_state = $calc_found_rows . ' ' . $quant_state;
144 1
                    }
145 1
                    $select->quantifier($quant_state);
146 1
                } else {
147 6
                    $select->quantifier(new Expression($calc_found_rows));
148
                }
149 6
            }
150 8
        }
151
152 38
        return $select;
153
    }
154
155
    /**
156
     * @param Options $options
157
     *
158
     * @throws Exception\EmptyQueryException
159
     * @throws Exception\ErrorException
160
     *
161
     * @return ResultSet
162
     */
163 38
    public function getData(Options $options = null)
164
    {
165 38
        if ($options === null) {
166 21
            $options = $this->getOptions();
167 21
        }
168
169 38
        $select = $this->assignOptions(clone $this->getSelect(), $options);
170
171 38
        $sql = new Sql($this->adapter);
172
173 38
        $sql_string = (string) $sql->buildSqlString($select);
174
        //echo $this->select->getSqlString($this->adapter->getPlatform());
175
        //echo "----" . var_dump($sql_string) . "----\n";
176
        // In ZF 2.3.0 an empty query will return SELECT .*
177
        // In ZF 2.4.0 and empty query will return SELECT *
178 38
        if (in_array($sql_string, ['', 'SELECT .*', 'SELECT *'])) {
179 2
            throw new Exception\EmptyQueryException(__METHOD__ . ': Cannot return data of an empty query');
180
        }
181 36
        $this->query_string = $sql_string;
182
183
        try {
184 36
            $results = $this->adapter->query($sql_string, Adapter::QUERY_MODE_EXECUTE);
185
            //$stmt = $sql->prepareStatementForSqlObject( $select );
186
            //$results = $stmt->execute();
187
            //var_dump(get_class($results));
188
189 36
            $r = new ResultSet($results);
190 36
            $r->setSource($this);
191 36
            $r->setHydrationOptions($options->getHydrationOptions());
192
193 36
            if ($options->hasLimit() && $options->getLimit() > 0) {
194
                //$row = $this->adapter->query('select FOUND_ROWS() as total_count')->execute()->current();
195 6
                $row = $this->adapter->createStatement('select FOUND_ROWS() as total_count')->execute()->current();
196 6
                $r->setTotalRows($row['total_count']);
197 6
            } else {
198 32
                $r->setTotalRows($r->count());
199
            }
200 36
        } catch (\Exception $e) {
201
            throw new Exception\ErrorException(__METHOD__ . ': Cannot retrieve data (' . $e->getMessage() . ')');
202
        }
203
204 36
        return $r;
205
    }
206
207 32
    public function loadDefaultColumnModel()
208
    {
209 31
        $sql = new Sql($this->adapter);
210 31
        $select = clone $this->select;
211 31
        $select->limit(0);
212 31
        $sql_string = $sql->getSqlStringForSqlObject($select);
213 32
        $metadata_columns = $this->getMetadataReader()->getColumnsMetadata($sql_string);
214 31
        $this->setColumnModel(MetadataMapper::getColumnModelFromMetadata($metadata_columns));
215 31
    }
216
217
    /**
218
     * {@inheritdoc}
219
     *
220
     * @throws Exception\UnsupportedFeatureException
221
     */
222 33
    public function getMetadataReader()
223
    {
224 33
        if ($this->metadataReader === null) {
225 33
            $this->setMetadataReader($this->getDefaultMetadataReader());
226 33
        }
227
228 33
        return $this->metadataReader;
229
    }
230
231
    /**
232
     * @throws Exception\UnsupportedFeatureException
233
     */
234 33
    protected function getDefaultMetadataReader()
235
    {
236 33
        $conn = $this->adapter->getDriver()->getConnection()->getResource();
237 33
        $class = strtolower(get_class($conn));
238
        switch ($class) {
239 33
            case 'pdo':
240
                return new MetadataReader\PdoMysqlMetadataReader($conn);
241 33
            case 'mysqli':
242 33
                return new MetadataReader\MysqliMetadataReader($conn);
243
            default:
244
                throw new Exception\UnsupportedFeatureException(__METHOD__ . " Does not support default metadata reader for driver '$class'");
245
        }
246
    }
247
248
    /**
249
     * Return the query string that was executed.
250
     *
251
     * @throws Exception\InvalidUsageException
252
     *
253
     * @return string
254
     */
255 5
    public function getQueryString()
256
    {
257 5
        if ($this->query_string == '') {
258 1
            throw new Exception\InvalidUsageException(__METHOD__ . ': Invalid usage, getQueryString must be called after data has been loaded (performance reason).');
259
        }
260
261 4
        return str_replace("\n", ' ', $this->query_string);
262
    }
263
264
    /**
265
     * Return the query string.
266
     *
267
     * @throws Exception\InvalidUsageException
268
     *
269
     * @return string
270
     */
271 2
    public function __toString()
272
    {
273 2
        if (trim($this->query_string) != '') {
274 2
            $sql = str_replace("\n", ' ', $this->query_string);
275 2
        } elseif ($this->select !== null) {
276
            $sql = $this->sql->getSqlStringForSqlObject($this->select);
277
        } else {
278
            throw new Exception\InvalidUsageException(__METHOD__ . ': No select given.');
279
        }
280
281 2
        return $sql;
282
    }
283
}
284