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