Passed
Push — master ( ad15ea...7afef2 )
by Darío
02:13
created

MySQL::autocommit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
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 resource
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
        if (property_exists($this->dbconn, 'num_rows'))
168
            $this->numRows = $this->dbconn->num_rows;
169
170
        if (property_exists($this->dbconn, 'field_count'))
171
            $this->numFields = $this->dbconn->field_count;
172
173
        if (property_exists($this->dbconn, 'affected_rows'))
174
            $this->rowsAffected = $this->dbconn->affected_rows;
175
176
        if ($this->transac_mode)
177
            $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...
178
179
        /*
180
         * Because mysqli_query() returns FALSE on failure, a mysqli_result object for SELECT, SHOW, DESCRIBE or EXPLAIN queries, * and TRUE for other successful queries, it should be handled to return only objects or resources.
181
         *
182
         * Ref: http://php.net/manual/en/mysqli.query.php
183
         */
184
        return is_bool($this->result) ? $this->dbconn : $this->result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return is_bool($this->re...>dbconn : $this->result also could return the type object which is incompatible with the documented return type resource.
Loading history...
185
    }
186
187
    /**
188
     * {@inheritDoc}
189
     */
190
    public function commit()
191
    {
192
        return $this->dbconn->commit();
193
    }
194
195
    /**
196
     * {@inheritDoc}
197
     */
198
    public function rollback()
199
    {
200
        return $this->dbconn->rollback();
201
    }
202
203
    /**
204
     * {@inheritDoc}
205
     */
206
    public function disconnect()
207
    {
208
        parent::disconnect();
209
210
        if ($this->dbconn->close())
211
        {
212
            $this->dbconn = null;
213
            return true;
214
        }
215
216
        return false;
217
    }
218
219
    /**
220
     * {@inheritDoc}
221
     */
222
    public function beginTransaction()
223
    {
224
        parent::beginTransaction();
225
        $this->dbconn->autocommit(false);
226
    }
227
228
    /**
229
     * {@inheritDoc}
230
     */
231
    public function autocommit($value)
232
    {
233
        parent::autocommit($value);
234
        $this->dbconn->autocommit($value);
235
    }
236
237
    /**
238
     * Returns an array with the rows fetched
239
     *
240
     * @throws LogicException
241
     *
242
     * @return array
243
     */
244
    protected function toArray()
245
    {
246
        $data = [];
247
248
        if ($this->result && !is_bool($this->result))
249
        {
250
            while ($row = $this->result->fetch_array(MYSQLI_BOTH))
251
            {
252
                $data[] = $row;
253
            }
254
        }
255
        else
256
            /*
257
             * "This kind of exception should lead directly to a fix in your code"
258
             * So much production tests tell us this error is throwed because developers
259
             * execute toArray() before execute().
260
             *
261
             * Ref: http://php.net/manual/en/class.logicexception.php
262
             */
263
            throw new \LogicException('There are not data in the buffer!');
264
265
        $this->arrayResult = $data;
266
267
        return $data;
268
    }
269
270
    /**
271
     * By default __destruct() disconnects to database
272
     *
273
     * @return null
274
     */
275
    public function __destruct()
276
    {
277
        # prevent "Property access is not allowed yet" with @ on failure connections
278
        if ($this->dbconn !== false && !is_null($this->dbconn))
279
            @$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

279
            /** @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...
280
    }
281
}