MySQLDatabase::__construct()   C
last analyzed

Complexity

Conditions 8
Paths 65

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 24
cts 24
cp 1
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 16
nc 65
nop 1
crap 8
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         = 'localhost';
44
    private $port         = 3306;
45
    private $username     = 'root';
46
    private $password     = '';
47
    private $databaseName = '';
48
    private $options      = [];
49
50
    /**
51
     * Initalizes this class, so a connection can be made to the database. The first argument is a
52
     * url. If url is not null, the 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
        parent::__construct();
89 10
    }
90 10
91 16
    /**
92 10
     * @return mysqli|null
93 10
     */
94 16
    public function getConnection()
95 15
    {
96 15
        $this->initalizeConnection();
97 16
98 11
        return $this->connection;
99 11
    }
100 16
101 10
    /**
102 10
     * Returns the host address for the MySQL client.
103 16
     *
104 16
     * @return string
105 16
     */
106
    public function getHost()
107
    {
108
        return $this->host;
109
    }
110 3
111
    /**
112 3
     * @param string $value
113
     * @return void
114 2
     */
115
    public function setHost($value)
116
    {
117
        $this->host = $value;
118
    }
119
120 3
    /**
121
     * Returns the port number for the MySQL client.
122 3
     *
123
     * @return int
124
     */
125
    public function getPort()
126
    {
127
        return $this->port;
128
    }
129
130 2
    /**
131
     * @param int $value
132 2
     *
133 2
     * @return void
134
     */
135
    public function setPort($value)
136
    {
137
        $this->port = $value;
138 3
    }
139
140 3
    /**
141
     * Returns the username for the MySQL client.
142
     *
143
     * @return string
144
     */
145
    public function getUsername()
146
    {
147
        return $this->username;
148 1
    }
149
150 1
    /**
151 1
     * Sets the username for the MySQL client.
152
     *
153
     * @param string $newUsername
154
     */
155
    public function setUsername($newUsername)
156 3
    {
157
        $this->username = $newUsername;
158 3
    }
159
160
    /**
161
     * Returns the password for the MySQL client.
162
     *
163
     * @return string
164 1
     */
165
    public function getPassword()
166 1
    {
167 1
        return $this->password;
168
    }
169
170
    /**
171
     * Sets the password for the MySQL client.
172 3
     *
173
     * @param string $newPassword
174 3
     */
175
    public function setPassword($newPassword)
176
    {
177
        $this->password = $newPassword;
178
    }
179
180 1
    /**
181
     * Returns the database's name for the MySQL client.
182 1
     *
183 1
     * @return string
184
     */
185
    public function getDatabaseName()
186
    {
187
        return $this->databaseName;
188 3
    }
189
190 3
    /**
191
     * Sets the database's name for the MySQL client.
192
     *
193
     * @param string $databaseName
194
     */
195
    public function setDatabaseName($databaseName)
196 1
    {
197
        $this->databaseName = $databaseName;
198 1
    }
199 1
200
    /**
201
     * @return array
202
     */
203
    public function getAllOptions()
204 1
    {
205
        return $this->options;
206 1
    }
207
208
    /**
209
     * @return mixed
210
     */
211
    public function getOption($key)
212 2
    {
213
        return isset($this->options[$key]) ? $this->options[$key] : null;
214 2
    }
215
216
    public function setOption($key, $newOption)
217 1
    {
218
        $this->options[$key] = $newOption;
219 1
    }
220 1
221
    /**
222
     * @return integer
223
     */
224
    public function lastIdCreated()
225 1
    {
226
        return (int) $this->getConnection()->insert_id;
227 1
    }
228
229
    /**
230
     * @return array[]
231
     */
232
    private static function processResult(mysqli_result $result)
233
    {
234
        $data = [];
235
        while (null !== ($row = $result->fetch_assoc())) {
236
            $data[] = $row;
237
        }
238
        $result->free();
239
        return $data;
240
    }
241
242
    /**
243
     * @param SqlStatementBuilder $statement
244
     * @param array               $options
245
     * @param bool                $returnResult
246
     * @return array[]|null
247
     *
248
     * @throws DatabaseException If there was an error connecting to the database.
249
     */
250
    protected function sendQueryToDatabase(SqlStatementBuilder $statement, array $options, $returnResult)
251 4
    {
252
        $compiled = self::compileQueriesAndBindings($statement, $options);
253 4
        $query    = $compiled[0];
254 4
        $bindings = $compiled[1];
255 4
256
        $this->initalizeConnection();
257 4
        if (count($bindings) !== 0) {
258 4
            // We are using prepared statements
259
260
            // Prepare
261
            $statement = $this->connection->prepare($query);
262 4
            if ($statement === false) {
263 4
                throw $this->logException(new DatabaseException(
264
                    'Unable to execute query! Error: ' . $this->connection->error
265
                ));
266
            }
267
268
            // Bind all of the variables
269
            self::bindToStatement($statement, $bindings);
270 4
271
            // Execute
272
            if (!$statement->execute()) {
273 4
                $message = 'Unable to execute statement! Error: ' . $this->connection->error;
274
                throw $this->logException(new DatabaseException($message));
275
            }
276
277
            if (!$returnResult) {
278 4
                return;
279 4
            }
280
            // Get result
281
            $result = $statement->get_result();
282
        } else {
283
            // There are no prepared values to bind.
284
            if (!$returnResult) {
285
                $this->connection->query($query);
286
                return;
287
            }
288
            $result = $this->connection->query($query);
289
        }
290
        if (!($result instanceof mysqli_result)) {
291
            $message = 'Unable to get result! Error: ' . $this->connection->error;
292
            throw $this->logException(new DatabaseException($message));
293
        }
294
        return self::processResult($result);
295
    }
296
297
    private function initalizeConnection()
298 6
    {
299
        if ($this->connection === null) {
300 6
            /* Instructs mysqli to throw exceptions instead of using errors. */
301
            mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
302 6
            try {
303
                $this->connection = new mysqli(
304 6
                    $this->host,
305 6
                    $this->username,
306 6
                    $this->password,
307 6
                    $this->databaseName,
308 6
                    $this->port
309 6
                );
310 6
                if ($this->connection->connect_errno) {
311 5
                    $error            = $this->connection->connect_error;
312
                    $this->connection = null;
313
                    throw new DatabaseException('Unable to connect to database! Error: ' . $error);
314
                }
315
                foreach ($this->options as $key => $value) {
316 5
                    $this->connection->options($key, $value);
317
                }
318 5
            } catch (mysqli_sql_exception $e) {
319 6
                throw $this->logException(
320 1
                    new DatabaseException(
321 1
                        'Error while connecting to the database!',
322 1
                        0,
323 1
                        $e
324
                    )
325 1
                );
326 1
            }
327
        }
328 5
    }
329
}
330