Passed
Push — master ( 9b30b3...3078fb )
by Darío
01:46
created

MySQL::__destruct()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 2
nc 2
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * DronePHP (http://www.dronephp.com)
4
 *
5
 * @link      http://github.com/Pleets/DronePHP
6
 * @copyright Copyright (c) 2016-2018 Pleets. (http://www.pleets.org)
7
 * @license   http://www.dronephp.com/license
8
 * @author    Darío Rivera <[email protected]>
9
 */
10
11
namespace Drone\Db\Driver;
12
13
/**
14
 * MySQL class
15
 *
16
 * This is a database driver class to connect to MySQL
17
 */
18
class MySQL extends AbstractDriver implements DriverInterface
19
{
20
    /**
21
     * {@inheritDoc}
22
     *
23
     * @var object
24
     */
25
    protected $dbconn;
26
27
    /**
28
     * {@inheritDoc}
29
     *
30
     * @param array $options
31
     */
32
    public function __construct($options)
33
    {
34
        if (!array_key_exists("dbchar", $options))
35
            $options["dbchar"] = "utf8";
36
37
        parent::__construct($options);
38
39
        $auto_connect = array_key_exists('auto_connect', $options) ? $options["auto_connect"] : true;
40
41
        if ($auto_connect)
42
            $this->connect();
43
    }
44
45
    /**
46
     * Connects to database
47
     *
48
     * @throws RuntimeException
49
     * @throws Exception\ConnectionException
50
     *
51
     * @return \mysqli
52
     */
53
    public function connect()
54
    {
55
        if (!extension_loaded('mysqli'))
56
            throw new \RuntimeException("The Mysqli extension is not loaded");
57
58
        if (!is_null($this->dbport) && !empty($this->dbport))
0 ignored issues
show
introduced by
The condition is_null($this->dbport) is always false.
Loading history...
59
            $conn = @new \mysqli($this->dbhost, $this->dbuser, $this->dbpass, $this->dbname, $this->dbport);
60
        else
61
            $conn = @new \mysqli($this->dbhost, $this->dbuser, $this->dbpass, $this->dbname);
62
63
        if ($conn->connect_errno)
64
        {
65
            /*
66
             * Use ever mysqli_connect_errno() and mysqli_connect_error()
67
             * over $this->dbconn->errno and $this->dbconn->error to prevent
68
             * the warning message "Property access is not allowed yet".
69
             */
70
            throw new Exception\ConnectionException(mysqli_connect_error(), mysqli_connect_errno());
71
        }
72
        else
73
        {
74
            $this->dbconn = $conn;
75
            $this->dbconn->set_charset($this->dbchar);
76
        }
77
78
        return $this->dbconn;
79
    }
80
81
    /**
82
     * Excecutes a statement
83
     *
84
     * @param string $sql
85
     * @param array $params
86
     *
87
     * @throws RuntimeException
88
     * @throws Exception\InvalidQueryException
89
     *
90
     * @return mysqli_result
0 ignored issues
show
Bug introduced by
The type Drone\Db\Driver\mysqli_result was not found. Did you mean mysqli_result? If so, make sure to prefix the type with \.
Loading history...
91
     */
92
    public function execute($sql, Array $params = [])
93
    {
94
        $this->numRows = 0;
95
        $this->numFields = 0;
96
        $this->rowsAffected = 0;
97
98
        $this->arrayResult = null;
99
100
        # Bound variables
101
        if (count($params))
102
        {
103
            $this->result = $stmt = @$this->dbconn->prepare($sql);
104
105
            if (!$stmt)
106
            {
107
                $this->error($this->dbconn->errno, $this->dbconn->error);
108
                throw new Exception\InvalidQueryException($this->dbconn->error, $this->dbconn->errno);
109
            }
110
111
            $param_values = array_values($params);
112
113
            $n_params = count($param_values);
114
            $bind_values = [];
115
            $bind_types = "";
116
117
            for ($i = 0; $i < $n_params; $i++)
118
            {
119
                if (is_string($param_values[$i]))
120
                    $bind_types .= 's';
121
                else if(is_float($param_values[$i]))
122
                    $bind_types .= 'd';
123
                # [POSSIBLE BUG] - To Future revision (What about non-string and non-decimal types ?)
124
                else
125
                    $bind_types .= 's';
126
127
                $bind_values[] = '$param_values[' . $i . ']';
128
            }
129
130
            $values = implode(', ', $bind_values);
131
            eval('$stmt->bind_param(\'' . $bind_types . '\', ' . $values . ');');
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
132
133
            $r = $stmt->execute();
134
135
            if ($r)
136
            {
137
                if (is_object($stmt) && get_class($stmt) == 'mysqli_stmt')
138
                {
139
                    $res = $this->result->get_result();
140
141
                    /*
142
                     * if $res is false then there aren't results.
143
                     * It is useful to prevent rollback transactions on insert statements because
144
                     * insert statement do not free results.
145
                     */
146
                    if ($res)
147
                        $this->result = $res;
148
                }
149
            }
150
        }
151
        else
152
        {
153
            $prev_error_handler = set_error_handler(['\Drone\Error\ErrorHandler', 'errorControlOperator'], E_ALL);
154
155
            // may be throw a Fatal error (Ex: Maximum execution time)
156
            $r = $this->result = $this->dbconn->query($sql);
157
158
            set_error_handler($prev_error_handler);
159
        }
160
161
        if (!$r)
162
        {
163
            $this->error($this->dbconn->errno, $this->dbconn->error);
164
            throw new Exception\InvalidQueryException($this->dbconn->error, $this->dbconn->errno);
165
        }
166
167
        # identify SELECT, SHOW, DESCRIBE or EXPLAIN queries
168
        if (is_object($this->result) && property_exists($this->result, 'num_rows'))
169
            $this->numRows = $this->result->num_rows;
170
        else
171
        {
172
            # affected_rows return the same of num_rows on select statements!
173
            if (property_exists($this->dbconn, 'affected_rows'))
174
                $this->rowsAffected = $this->dbconn->affected_rows;
175
        }
176
177
        if (property_exists($this->dbconn, 'field_count'))
178
            $this->numFields = $this->dbconn->field_count;
179
180
        if ($this->transac_mode)
181
            $this->transac_result = is_null($this->transac_result) ? $this->result: $this->transac_result && $this->result;
0 ignored issues
show
introduced by
The condition is_null($this->transac_result) is always false.
Loading history...
182
183
        /*
184
         * Because mysqli_query() returns FALSE on failure, a mysqli_result object for SELECT, SHOW, DESCRIBE or EXPLAIN queries,
185
         * and TRUE for other successful queries, it should be handled to return only objects or resources.
186
         *
187
         * Ref: http://php.net/manual/en/mysqli.query.php
188
         */
189
        return is_bool($this->result) ? $this->dbconn : $this->result;
190
    }
191
192
    /**
193
     * {@inheritDoc}
194
     */
195
    public function commit()
196
    {
197
        return $this->dbconn->commit();
198
    }
199
200
    /**
201
     * {@inheritDoc}
202
     */
203
    public function rollback()
204
    {
205
        return $this->dbconn->rollback();
206
    }
207
208
    /**
209
     * {@inheritDoc}
210
     */
211
    public function disconnect()
212
    {
213
        parent::disconnect();
214
215
        if ($this->dbconn->close())
216
        {
217
            $this->dbconn = null;
218
            return true;
219
        }
220
221
        return false;
222
    }
223
224
    /**
225
     * {@inheritDoc}
226
     */
227
    public function autocommit($value)
228
    {
229
        parent::autocommit($value);
230
        $this->dbconn->autocommit($value);
231
    }
232
233
    /**
234
     * Returns an array with the rows fetched
235
     *
236
     * @throws LogicException
237
     *
238
     * @return array
239
     */
240
    protected function toArray()
241
    {
242
        $data = [];
243
244
        if ($this->result && !is_bool($this->result))
245
        {
246
            while ($row = $this->result->fetch_array(MYSQLI_BOTH))
247
            {
248
                $data[] = $row;
249
            }
250
        }
251
        else
252
            /*
253
             * "This kind of exception should lead directly to a fix in your code"
254
             * So much production tests tell us this error is throwed because developers
255
             * execute toArray() before execute().
256
             *
257
             * Ref: http://php.net/manual/en/class.logicexception.php
258
             */
259
            throw new \LogicException('There are not data in the buffer!');
260
261
        $this->arrayResult = $data;
262
263
        return $data;
264
    }
265
266
    /**
267
     * By default __destruct() disconnects to database
268
     *
269
     * @return null
270
     */
271
    public function __destruct()
272
    {
273
        # prevent "Property access is not allowed yet" with @ on failure connections
274
        if ($this->dbconn !== false && !is_null($this->dbconn))
275
            @$this->dbconn->close();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for close(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

275
            /** @scrutinizer ignore-unhandled */ @$this->dbconn->close();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
276
    }
277
}