Completed
Push — master ( 0f8fe9...4a0a0c )
by Sébastien
06:30
created

src/Soluble/FlexStore/Source/Zend/SqlSource.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 67
    public function __construct(Adapter $adapter, Select $select = null)
78
    {
79 67
        $this->adapter = $adapter;
80 67
        $this->sql = new Sql($this->adapter);
81 67
        if ($select !== null) {
82 58
            $this->setSelect($select);
83
        }
84 67
    }
85
86
    /**
87
     * @param Select $select
88
     *
89
     * @return SqlSource
90
     */
91 62
    public function setSelect(Select $select)
92
    {
93 62
        $this->select = $select;
94
95 62
        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
        }
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
            }
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) {
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
                    }
145 1
                    $select->quantifier($quant_state);
146
                } else {
147 6
                    $select->quantifier(new Expression($calc_found_rows));
148
                }
149
            }
150
        }
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
        }
168
169 38
        $select = $this->assignOptions(clone $this->getSelect(), $options);
170
171 38
        $sql = new Sql($this->adapter);
172
173 38
        $sql_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 *'], true)) {
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);
0 ignored issues
show
$results is of type object<Zend\Db\Adapter\D...Driver\ResultInterface>, but the function expects a object<Zend\Db\ResultSet\ResultSet>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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
            } else {
198 36
                $r->setTotalRows($r->count());
199
            }
200
        } catch (\Exception $e) {
201
            throw new Exception\ErrorException(__METHOD__ . ': Cannot retrieve data (' . $e->getMessage() . ')');
202
        }
203
204 36
        return $r;
205
    }
206
207 29
    public function loadDefaultColumnModel()
208
    {
209 29
        $sql = new Sql($this->adapter);
210 29
        $select = clone $this->select;
211 29
        $select->limit(0);
212 29
        $sql_string = $sql->getSqlStringForSqlObject($select);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
213 29
        $metadata_columns = $this->getMetadataReader()->getColumnsMetadata($sql_string);
214 29
        $this->setColumnModel(MetadataMapper::getColumnModelFromMetadata($metadata_columns));
215 29
    }
216
217
    /**
218
     * {@inheritdoc}
219
     *
220
     * @throws Exception\UnsupportedFeatureException
221
     */
222 31
    public function getMetadataReader()
223
    {
224 31
        if ($this->metadataReader === null) {
225 31
            $this->setMetadataReader($this->getDefaultMetadataReader());
226
        }
227
228 31
        return $this->metadataReader;
229
    }
230
231
    /**
232
     * @throws Exception\UnsupportedFeatureException
233
     */
234 31
    protected function getDefaultMetadataReader()
235
    {
236 31
        $conn = $this->adapter->getDriver()->getConnection()->getResource();
237 31
        $class = strtolower(get_class($conn));
238
        switch ($class) {
239 31
            case 'pdo':
240
                return new MetadataReader\PdoMysqlMetadataReader($conn);
241 31
            case 'mysqli':
242 31
                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
        } elseif ($this->select !== null) {
276
            $sql = $this->sql->getSqlStringForSqlObject($this->select);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
277
        } else {
278
            throw new Exception\InvalidUsageException(__METHOD__ . ': No select given.');
279
        }
280
281 2
        return $sql;
282
    }
283
}
284