Passed
Push — master ( b1fefe...1b2e02 )
by Michael
05:24
created

MySQLDatabase::sendQueryToDatabase()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 46
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 15.4039

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 46
ccs 12
cts 27
cp 0.4444
rs 6.7273
cc 7
eloc 26
nc 8
nop 3
crap 15.4039
1
<?php
2
namespace DAL;
3
4
use \Exception;
5
use \mysqli;
6
use \mysqli_result;
7
use \mysqli_sql_exception;
8
use \mysqli_stmt;
9
use \DAL\Exceptions\DatabaseException;
10
11
/**
12
 * Defines a connection to a database using the mysqli extension.
13
 * Broken
14
 *
15
 * Please use PDO if you are able to.
16
 */
17
class MySQLDatabase extends AbstractSQLDatabase
18
{
19
20 4
    private static function bindToStatement(mysqli_stmt $statement, array $bindings)
21
    {
22 4
        $types = '';
23 4
        foreach ($bindings as $binding) {
24 4
            if (is_int($binding) || is_long($binding)) {
25 1
                $types .= 'i';
26 4
            } elseif (is_double($binding) || is_float($binding)) {
27
                $types .= 'd';
28 4
            } elseif (is_string($binding)) {
29 4
                $types .= 's';
30 4
            } else {
31
                $types .= 'b';
32
            }
33 4
        }
34
35 4
        $arguments = [$types];
36 4
        foreach ($bindings as &$binding) {
37 4
            $arguments[] =& $binding;
38 4
        }
39 4
        call_user_func_array([$statement, 'bind_param'], $arguments);
40 4
    }
41
42
    private $connection;
43
    private $host;
44
    private $port;
45
    private $username;
46
    private $password;
47
    private $databaseName;
48
    private $options;
49
50
    /**
51
     * Initalizes this class, so a connection can be made to the database. If url is not null, the
52
     * connection parameters will be decoded from the url.
53
     *
54
     * The provided url will contain the username, password, hostname, port, and the database name.
55
     * Take the following url for example.
56
     *
57
     * root:[email protected]:3603/administration
58
     *
59
     * It will connect to mysql.example.com at port 3603. It will use the username root, and use
60
     * the password secret. The database is named administration.
61
     * Please note that the scheme and fragment will be ignored.
62
     *
63
     * @param string|null $url
64
     */
65 16
    public function __construct($url = null)
66
    {
67 16
        if (null !== $url) {
68 6
            if (!filter_var($url, \FILTER_VALIDATE_URL)) {
69 6
                $url = 'mysql://' . $url;
70 6
            }
71 6
            $parsed = parse_url($url);
72 6
            if (isset($parsed['host'])) {
73 6
                $this->host = $parsed['host'];
74 6
            }
75 6
            if (isset($parsed['port'])) {
76 5
                $this->port = $parsed['port'];
77 5
            }
78 6
            if (isset($parsed['user'])) {
79 6
                $this->username = $parsed['user'];
80 6
            }
81 6
            if (isset($parsed['pass'])) {
82 1
                $this->password = $parsed['pass'];
83 1
            }
84 6
            if (isset($parsed['path'])) {
85 6
                $this->databaseName = trim($parsed['path'], '/');
86 6
            }
87 6
        }
88 16
        if (null === $this->host) {
89 10
            $this->host = 'localhost';
90 10
        }
91 16
        if (null === $this->username) {
92 10
            $this->username = 'root';
93 10
        }
94 16
        if (null === $this->password) {
95 15
            $this->password = '';
96 15
        }
97 16
        if (null === $this->port) {
98 11
            $this->port = 3306;
99 11
        }
100 16
        if (null === $this->databaseName) {
101 10
            $this->databaseName = '';
102 10
        }
103 16
        $this->options = [];
104 16
        parent::__construct();
105 16
    }
106
107
    /**
108
     * @return mysqli|null
109
     */
110 3
    public function getConnection()
111
    {
112 3
        $this->initalizeConnection();
113
114 2
        return $this->connection;
115
    }
116
117
    /**
118
     * @return string
119
     */
120 3
    public function getHost()
121
    {
122 3
        return $this->host;
123
    }
124
125
    /**
126
     * @param string $value
127
     *
128
     * @return void
129
     */
130 2
    public function setHost($value)
131
    {
132 2
        $this->host = $value;
133 2
    }
134
135
    /**
136
     * @return int
137
     */
138 3
    public function getPort()
139
    {
140 3
        return $this->port;
141
    }
142
143
    /**
144
     * @param int $value
145
     *
146
     * @return void
147
     */
148 1
    public function setPort($value)
149
    {
150 1
        $this->port = $value;
151 1
    }
152
153
    /**
154
     * @return string
155
     */
156 3
    public function getUsername()
157
    {
158 3
        return $this->username;
159
    }
160
161
    /**
162
     * @param string $newUsername
163
     */
164 1
    public function setUsername($newUsername)
165
    {
166 1
        $this->username = $newUsername;
167 1
    }
168
169
    /**
170
     * @return string
171
     */
172 3
    public function getPassword()
173
    {
174 3
        return $this->password;
175
    }
176
177
    /**
178
     * @param string $newPassword
179
     */
180 1
    public function setPassword($newPassword)
181
    {
182 1
        $this->password = $newPassword;
183 1
    }
184
185
    /**
186
     * @return string
187
     */
188 3
    public function getDatabaseName()
189
    {
190 3
        return $this->databaseName;
191
    }
192
193
    /**
194
     * @param string $databaseName
195
     */
196 1
    public function setDatabaseName($databaseName)
197
    {
198 1
        $this->databaseName = $databaseName;
199 1
    }
200
201
    /**
202
     * @return array
203
     */
204 1
    public function getAllOptions()
205
    {
206 1
        return $this->options;
207
    }
208
209
    /**
210
     * @return mixed
211
     */
212 2
    public function getOption($key)
213
    {
214 2
        return isset($this->options[$key]) ? $this->options[$key] : null;
215
    }
216
217 1
    public function setOption($key, $newOption)
218
    {
219 1
        $this->options[$key] = $newOption;
220 1
    }
221
222
    /**
223
     * @return integer
224
     */
225 1
    public function lastIdCreated()
226
    {
227 1
        return (int) $this->getConnection()->insert_id;
228
    }
229
230
    /**
231
     * @return array[]
232
     */
233
    private static function processResult(mysqli_result $result)
234
    {
235
        $data = [];
236
        while (null !== ($row = $result->fetch_assoc())) {
237
            $data[] = $row;
238
        }
239
        $result->free();
240
        return $data;
241
    }
242
243
    /**
244
     * @param SqlStatementBuilder $statement
245
     * @param array               $options
246
     * @param bool                $returnResult
247
     * @return array[]|null
248
     *
249
     * @throws DatabaseException If there was an error connecting to the database.
250
     */
251 4
    protected function sendQueryToDatabase(SqlStatementBuilder $statement, array $options, $returnResult)
252
    {
253 4
        $compiled = self::compileQueriesAndBindings($statement, $options);
254 4
        $query    = $compiled[0];
255 4
        $bindings = $compiled[1];
256
257 4
        $this->initalizeConnection();
258 4
        if (count($bindings) !== 0) {
259
            // We are using prepared statements
260
261
            // Prepare
262 4
            $statement = $this->connection->prepare($query);
263 4
            if ($statement === false) {
264
                throw $this->logException(new DatabaseException(
265
                    'Unable to execute query! Error: ' . $this->connection->error
266
                ));
267
            }
268
269
            // Bind all of the variables
270 4
            self::bindToStatement($statement, $bindings);
271
272
            // Execute
273 4
            if (!$statement->execute()) {
274
                $message = 'Unable to execute statement! Error: ' . $this->connection->error;
275
                throw $this->logException(new DatabaseException($message));
276
            }
277
278 4
            if (!$returnResult) {
279 4
                return;
280
            }
281
            // Get result
282
            $result = $statement->get_result();
283
        } else {
284
            // There are no prepared values to bind.
285
            if (!$returnResult) {
286
                $this->connection->query($query);
287
                return;
288
            }
289
            $result = $this->connection->query($query);
290
        }
291
        if (!($result instanceof mysqli_result)) {
292
            $message = 'Unable to get result! Error: ' . $this->connection->error;
293
            throw $this->logException(new DatabaseException($message));
294
        }
295
        return self::processResult($result);
296
    }
297
298 6
    private function initalizeConnection()
299
    {
300 6
        if ($this->connection === null) {
301
            /* Instructs mysqli to throw exceptions instead of using errors. */
302 6
            mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
303
            try {
304 6
                $this->connection = new mysqli(
305 6
                    $this->host,
306 6
                    $this->username,
307 6
                    $this->password,
308 6
                    $this->databaseName,
309 6
                    $this->port
310 6
                );
311 5
                if ($this->connection->connect_errno) {
312
                    $error            = $this->connection->connect_error;
313
                    $this->connection = null;
314
                    throw new DatabaseException('Unable to connect to database! Error: ' . $error);
315
                }
316 5
                foreach ($this->options as $key => $value) {
317
                    $this->connection->options($key, $value);
318 5
                }
319 6
            } catch (mysqli_sql_exception $e) {
320 1
                throw $this->logException(
321 1
                    new DatabaseException(
322 1
                        'Error while connecting to the database!',
323 1
                        0,
324
                        $e
325 1
                    )
326 1
                );
327
            }
328 5
        }
329 5
    }
330
}
331