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