PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.14.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.14.1
5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Db / Adapter / Pdo / Mysql.php
matomo / app / core / Db / Adapter / Pdo Last commit date
Mysql.php 3 years ago
Mysql.php
345 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9 namespace Piwik\Db\Adapter\Pdo;
10
11 use Exception;
12 use PDO;
13 use PDOException;
14 use Piwik\Config;
15 use Piwik\Db;
16 use Piwik\Db\AdapterInterface;
17 use Piwik\Piwik;
18 use Zend_Config;
19 use Zend_Db_Adapter_Pdo_Mysql;
20 use Zend_Db_Select;
21 use Zend_Db_Statement_Interface;
22
23 /**
24 */
25 class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
26 {
27 /**
28 * Constructor
29 *
30 * @param array|Zend_Config $config database configuration
31 */
32
33 // this is used for indicate TransactionLevel Cache
34 public $supportsUncommitted;
35
36 public function __construct($config)
37 {
38 // Enable LOAD DATA INFILE
39 if (defined('PDO::MYSQL_ATTR_LOCAL_INFILE')) {
40 $config['driver_options'][PDO::MYSQL_ATTR_LOCAL_INFILE] = true;
41 }
42 if ($config['enable_ssl']) {
43 if (!empty($config['ssl_key'])) {
44 $config['driver_options'][PDO::MYSQL_ATTR_SSL_KEY] = $config['ssl_key'];
45 }
46 if (!empty($config['ssl_cert'])) {
47 $config['driver_options'][PDO::MYSQL_ATTR_SSL_CERT] = $config['ssl_cert'];
48 }
49 if (!empty($config['ssl_ca'])) {
50 $config['driver_options'][PDO::MYSQL_ATTR_SSL_CA] = $config['ssl_ca'];
51 }
52 if (!empty($config['ssl_ca_path'])) {
53 $config['driver_options'][PDO::MYSQL_ATTR_SSL_CAPATH] = $config['ssl_ca_path'];
54 }
55 if (!empty($config['ssl_cipher'])) {
56 $config['driver_options'][PDO::MYSQL_ATTR_SSL_CIPHER] = $config['ssl_cipher'];
57 }
58 if (!empty($config['ssl_no_verify'])
59 && defined('PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT')
60 ) {
61 $config['driver_options'][PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = false;
62 }
63 }
64 parent::__construct($config);
65 }
66
67 /**
68 * Returns connection handle
69 *
70 * @return resource
71 */
72 public function getConnection()
73 {
74 if ($this->_connection) {
75 return $this->_connection;
76 }
77
78 $this->_connect();
79
80 /**
81 * Before MySQL 5.1.17, server-side prepared statements
82 * do not use the query cache.
83 * @see http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html
84 *
85 * MySQL also does not support preparing certain DDL and SHOW
86 * statements.
87 * @see http://framework.zend.com/issues/browse/ZF-1398
88 */
89 $this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
90
91 return $this->_connection;
92 }
93
94 protected function _connect()
95 {
96 if ($this->_connection) {
97 return;
98 }
99
100 $sql = 'SET sql_mode = "' . Db::SQL_MODE . '"';
101
102 try {
103 parent::_connect();
104 $this->_connection->exec($sql);
105 } catch (Exception $e) {
106 if ($this->isErrNo($e, \Piwik\Updater\Migration\Db::ERROR_CODE_MYSQL_SERVER_HAS_GONE_AWAY)) {
107 // mysql may return a MySQL server has gone away error when trying to establish the connection.
108 // in that case we want to retry establishing the connection once after a short sleep
109 $this->_connection = null; // we need to unset, otherwise parent connect won't retry
110 usleep(400 * 1000);
111 parent::_connect();
112 $this->_connection->exec($sql);
113 } else {
114 throw $e;
115 }
116 }
117 }
118
119 /**
120 * Reset the configuration variables in this adapter.
121 */
122 public function resetConfig()
123 {
124 $this->_config = array();
125 }
126
127 /**
128 * Return default port.
129 *
130 * @return int
131 */
132 public static function getDefaultPort()
133 {
134 return 3306;
135 }
136
137 /**
138 * Check MySQL version
139 *
140 * @throws Exception
141 */
142 public function checkServerVersion()
143 {
144 $serverVersion = $this->getServerVersion();
145 $requiredVersion = Config::getInstance()->General['minimum_mysql_version'];
146
147 if (version_compare($serverVersion, $requiredVersion) === -1) {
148 throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
149 }
150 }
151
152 /**
153 * Returns the MySQL server version
154 *
155 * @return null|string
156 */
157 public function getServerVersion()
158 {
159 // prioritizing SELECT @@VERSION in case the connection version string is incorrect (which can
160 // occur on Azure)
161 $versionInfo = $this->fetchAll('SELECT @@VERSION');
162
163 if (count($versionInfo)) {
164 return $versionInfo[0]['@@VERSION'];
165 }
166
167 return parent::getServerVersion();
168 }
169
170 /**
171 * Check client version compatibility against database server
172 *
173 * @throws Exception
174 */
175 public function checkClientVersion()
176 {
177 $serverVersion = $this->getServerVersion();
178 $clientVersion = $this->getClientVersion();
179
180 // incompatible change to DECIMAL implementation in 5.0.3
181 if (version_compare($serverVersion, '5.0.3') >= 0
182 && version_compare($clientVersion, '5.0.3') < 0
183 ) {
184 throw new Exception(Piwik::translate('General_ExceptionIncompatibleClientServerVersions', array('MySQL', $clientVersion, $serverVersion)));
185 }
186 }
187
188 /**
189 * Returns true if this adapter's required extensions are enabled
190 *
191 * @return bool
192 */
193 public static function isEnabled()
194 {
195 return extension_loaded('PDO') && extension_loaded('pdo_mysql') && in_array('mysql', PDO::getAvailableDrivers());
196 }
197
198 /**
199 * Returns true if this adapter supports blobs as fields
200 *
201 * @return bool
202 */
203 public function hasBlobDataType()
204 {
205 return true;
206 }
207
208 /**
209 * Returns true if this adapter supports bulk loading
210 *
211 * @return bool
212 */
213 public function hasBulkLoader()
214 {
215 return true;
216 }
217
218 /**
219 * Test error number
220 *
221 * @param Exception $e
222 * @param string $errno
223 * @return bool
224 */
225 public function isErrNo($e, $errno)
226 {
227 return self::isPdoErrorNumber($e, $errno);
228 }
229
230
231 /**
232 * Test error number
233 *
234 * @param Exception $e
235 * @param string $errno
236 * @return bool
237 */
238 public static function isPdoErrorNumber($e, $errno)
239 {
240 if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) {
241 return $match[1] == $errno;
242 }
243
244 return false;
245 }
246
247 /**
248 * Is the connection character set equal to utf8?
249 *
250 * @return bool
251 */
252 public function isConnectionUTF8()
253 {
254 $charsetInfo = $this->fetchAll('SHOW VARIABLES LIKE ?', array('character_set_connection'));
255
256 if (empty($charsetInfo)) {
257 return false;
258 }
259
260 $charset = $charsetInfo[0]['Value'];
261 return strpos($charset, 'utf8') === 0;
262 }
263
264
265 /**
266 * Return number of affected rows in last query
267 *
268 * @param mixed $queryResult Result from query()
269 * @return int
270 */
271 public function rowCount($queryResult)
272 {
273 return $queryResult->rowCount();
274 }
275
276 /**
277 * Retrieve client version in PHP style
278 *
279 * @return string
280 */
281 public function getClientVersion()
282 {
283 $this->_connect();
284 try {
285 $version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
286 $matches = null;
287 if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
288 return $matches[1];
289 }
290 } catch (PDOException $e) {
291 // In case of the driver doesn't support getting attributes
292 }
293 return null;
294 }
295
296 /**
297 * @var \Zend_Db_Statement_Pdo[]
298 */
299 private $cachePreparedStatement = array();
300
301 /**
302 * Prepares and executes an SQL statement with bound data.
303 * Caches prepared statements to avoid preparing the same query more than once
304 *
305 * @param string|Zend_Db_Select $sql The SQL statement with placeholders.
306 * @param array $bind An array of data to bind to the placeholders.
307 * @return Zend_Db_Statement_Interface
308 */
309 public function query($sql, $bind = array())
310 {
311 if (!is_string($sql)) {
312 return parent::query($sql, $bind);
313 }
314
315 if (isset($this->cachePreparedStatement[$sql])) {
316 if (!is_array($bind)) {
317 $bind = array($bind);
318 }
319
320 $stmt = $this->cachePreparedStatement[$sql];
321 $stmt->execute($bind);
322 return $stmt;
323 }
324
325 $stmt = parent::query($sql, $bind);
326 $this->cachePreparedStatement[$sql] = $stmt;
327 return $stmt;
328 }
329
330 /**
331 * Override _dsn() to ensure host and port to not be passed along
332 * if unix_socket is set since setting both causes unexpected behaviour
333 * @see http://php.net/manual/en/ref.pdo-mysql.connection.php
334 */
335 protected function _dsn()
336 {
337 if (!empty($this->_config['unix_socket'])) {
338 unset($this->_config['host']);
339 unset($this->_config['port']);
340 }
341
342 return parent::_dsn();
343 }
344 }
345