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

Soluble/FlexStore/Source/DbWrapper/QuerySource.php (1 issue)

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\DbWrapper;
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 ArrayObject;
21
use Soluble\DbWrapper\Adapter\AdapterInterface;
22
use Soluble\DbWrapper\Result\Resultset as DbWrapperResultset;
23
use Soluble\FlexStore\Column\ColumnModel;
24
use Soluble\FlexStore\Column\Type\MetadataMapper;
25
use Soluble\Metadata\Reader as MetadataReader;
26
27
class QuerySource extends AbstractSource implements QueryableSourceInterface
28
{
29
    /**
30
     * @var string
31
     */
32
    protected $query;
33
34
    /**
35
     * @var AdapterInterface
36
     */
37
    protected $adapter;
38
39
    /**
40
     * Initial params received in the constructor.
41
     *
42
     * @var ArrayObject
43
     */
44
    protected $params;
45
46
    /**
47
     * The query string contains the query as it has been crafted (with options, limits...).
48
     *
49
     * @var string
50
     */
51
    protected $query_string;
52
53
    /**
54
     * @var ColumnModel
55
     */
56
    protected $columnModel;
57
58
    /**
59
     * @param AdapterInterface $adapter
60
     * @param string           $query
61
     */
62 10
    public function __construct(AdapterInterface $adapter, $query = null)
63
    {
64 10
        $this->adapter = $adapter;
65
66 10
        if ($query !== null) {
67 7
            $this->setQuery($query);
68
        }
69 10
    }
70
71
    /**
72
     * @param string $query
73
     */
74 10
    public function setQuery($query)
75
    {
76 10
        $this->query = $query;
77 10
    }
78
79
    /**
80
     * @return string
81
     */
82
    public function getQuery()
83
    {
84
        return $this->query;
85
    }
86
87
    /**
88
     * @param string  $query
89
     * @param Options $options
90
     *
91
     * @return string query
92
     */
93 6
    protected function assignOptions($query, Options $options)
94
    {
95 6
        if ($options->hasLimit()) {
96
            /*
97
             * For mysql queries, to allow counting rows we must prepend
98
             * SQL_CALC_FOUND_ROWS to the select quantifiers
99
             */
100 4
            if ($options->getLimit() > 0) {
101 3
                $calc_found_rows = 'SQL_CALC_FOUND_ROWS';
102 3
                if (!preg_match("/$calc_found_rows/", $query)) {
103 3
                    $q = trim($query);
104 3
                    $query = preg_replace('/^select\b/i', "SELECT $calc_found_rows", $q);
105
                }
106
            }
107
108
            // mysql only, @todo make it rule everything (use traits)
109 4
            $replace_regexp = "LIMIT[\s]+[\d]+((\s*,\s*\d+)|(\s+OFFSET\s+\d+)){0,1}";
110
111 4
            $search_regexp = "$replace_regexp";
112 4
            if (!preg_match("/$search_regexp$/i", $query)) {
113
                // Limit is not already present
114 3
                $query .= ' LIMIT ' . $options->getLimit();
115
            } else {
116 1
                $query = preg_replace("/($replace_regexp)/i", 'LIMIT ' . $options->getLimit(), $query);
117
            }
118
119 4
            if ($options->hasOffset()) {
120 3
                $query .= ' OFFSET ' . $options->getOffset();
121
            }
122
        }
123
124 6
        return $query;
125
    }
126
127
    /**
128
     * @param Options $options
129
     *
130
     * @throws Exception\EmptyQueryException
131
     * @throws Exception\ErrorException
132
     *
133
     * @return ResultSet
134
     */
135 6
    public function getData(Options $options = null)
136
    {
137 6
        if ($options === null) {
138 2
            $options = $this->getOptions();
139
        }
140
141 6
        $this->query_string = $this->assignOptions($this->query, $options);
142
143
        try {
144 6
            $results = $this->adapter->query($this->query_string, DbWrapperResultset::TYPE_ARRAYOBJECT);
145
146 6
            $r = new ResultSet($results);
0 ignored issues
show
$results is of type object<Soluble\DbWrapper\Result\Resultset>, 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...
147 6
            $r->setSource($this);
148 6
            $r->setHydrationOptions($options->getHydrationOptions());
149
150 6
            if ($options->hasLimit() && $options->getLimit() > 0) {
151
                //$row = $this->adapter->query('select FOUND_ROWS() as total_count')->execute()->current();
152 3
                $row = $this->adapter->query('select FOUND_ROWS() as total_count')->current();
153 3
                $r->setTotalRows($row['total_count']);
154
            } else {
155 6
                $r->setTotalRows($r->count());
156
            }
157
        } catch (\Exception $e) {
158
            throw new Exception\ErrorException(__METHOD__ . ': Cannot retrieve data (' . $e->getMessage() . ')');
159
        }
160
161 6
        return $r;
162
    }
163
164 2
    public function loadDefaultColumnModel()
165
    {
166 2
        $metadata_columns = $this->getMetadataReader()->getColumnsMetadata($this->query);
167 2
        $this->setColumnModel(MetadataMapper::getColumnModelFromMetadata($metadata_columns));
168 2
    }
169
170
    /**
171
     * {@inheritdoc}
172
     *
173
     * @throws Exception\UnsupportedFeatureException
174
     */
175 4
    public function getMetadataReader()
176
    {
177 4
        if ($this->metadataReader === null) {
178 4
            $this->setMetadataReader($this->getDefaultMetadataReader());
179
        }
180
181 4
        return $this->metadataReader;
182
    }
183
184
    /**
185
     * @throws Exception\UnsupportedFeatureException
186
     */
187 4
    protected function getDefaultMetadataReader()
188
    {
189 4
        $conn = $this->adapter->getConnection()->getResource();
190 4
        $class = strtolower(get_class($conn));
191
        switch ($class) {
192 4
            case 'pdo':
193
                return new MetadataReader\PdoMysqlMetadataReader($conn);
194 4
            case 'mysqli':
195 4
                return new MetadataReader\MysqliMetadataReader($conn);
196
            default:
197
                throw new Exception\UnsupportedFeatureException(__METHOD__ . " Does not support default metadata reader for driver '$class'");
198
        }
199
    }
200
201
    /**
202
     * Return the query string that was executed with options etc.
203
     *
204
     * @throws Exception\InvalidUsageException
205
     *
206
     * @return string
207
     */
208 2
    public function getQueryString()
209
    {
210 2
        if ($this->query_string == '') {
211
            throw new Exception\InvalidUsageException(__METHOD__ . ': Invalid usage, getQueryString must be called after data has been loaded (performance reason).');
212
        }
213
214 2
        return str_replace("\n", ' ', $this->query_string);
215
    }
216
217
    /**
218
     * Return the query string.
219
     *
220
     * @throws Exception\InvalidUsageException
221
     *
222
     * @return string
223
     */
224
    public function __toString()
225
    {
226
        if (trim($this->query) == '') {
227
            throw new Exception\InvalidUsageException(__METHOD__ . ': Empty query given.');
228
        }
229
230
        return $this->query;
231
    }
232
}
233