MysqliConnection.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the MIT license. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\DBAL\Driver\Mysqli;
  20. use Doctrine\DBAL\Driver\Connection as Connection;
  21. use Doctrine\DBAL\Driver\PingableConnection;
  22. use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
  23. /**
  24. * @author Kim Hemsø Rasmussen <kimhemsoe@gmail.com>
  25. * @author Till Klampaeckel <till@php.net>
  26. */
  27. class MysqliConnection implements Connection, PingableConnection, ServerInfoAwareConnection
  28. {
  29. /**
  30. * Name of the option to set connection flags
  31. */
  32. const OPTION_FLAGS = 'flags';
  33. /**
  34. * @var \mysqli
  35. */
  36. private $_conn;
  37. /**
  38. * @param array $params
  39. * @param string $username
  40. * @param string $password
  41. * @param array $driverOptions
  42. *
  43. * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException
  44. */
  45. public function __construct(array $params, $username, $password, array $driverOptions = array())
  46. {
  47. $port = isset($params['port']) ? $params['port'] : ini_get('mysqli.default_port');
  48. // Fallback to default MySQL port if not given.
  49. if ( ! $port) {
  50. $port = 3306;
  51. }
  52. $socket = isset($params['unix_socket']) ? $params['unix_socket'] : ini_get('mysqli.default_socket');
  53. $dbname = isset($params['dbname']) ? $params['dbname'] : null;
  54. $flags = isset($driverOptions[static::OPTION_FLAGS]) ? $driverOptions[static::OPTION_FLAGS] : null;
  55. $this->_conn = mysqli_init();
  56. $this->setDriverOptions($driverOptions);
  57. set_error_handler(function () {});
  58. if ( ! $this->_conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) {
  59. restore_error_handler();
  60. throw new MysqliException($this->_conn->connect_error, @$this->_conn->sqlstate ?: 'HY000', $this->_conn->connect_errno);
  61. }
  62. restore_error_handler();
  63. if (isset($params['charset'])) {
  64. $this->_conn->set_charset($params['charset']);
  65. }
  66. }
  67. /**
  68. * Retrieves mysqli native resource handle.
  69. *
  70. * Could be used if part of your application is not using DBAL.
  71. *
  72. * @return \mysqli
  73. */
  74. public function getWrappedResourceHandle()
  75. {
  76. return $this->_conn;
  77. }
  78. /**
  79. * {@inheritdoc}
  80. */
  81. public function getServerVersion()
  82. {
  83. $majorVersion = floor($this->_conn->server_version / 10000);
  84. $minorVersion = floor(($this->_conn->server_version - $majorVersion * 10000) / 100);
  85. $patchVersion = floor($this->_conn->server_version - $majorVersion * 10000 - $minorVersion * 100);
  86. return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
  87. }
  88. /**
  89. * {@inheritdoc}
  90. */
  91. public function requiresQueryForServerVersion()
  92. {
  93. return false;
  94. }
  95. /**
  96. * {@inheritdoc}
  97. */
  98. public function prepare($prepareString)
  99. {
  100. return new MysqliStatement($this->_conn, $prepareString);
  101. }
  102. /**
  103. * {@inheritdoc}
  104. */
  105. public function query()
  106. {
  107. $args = func_get_args();
  108. $sql = $args[0];
  109. $stmt = $this->prepare($sql);
  110. $stmt->execute();
  111. return $stmt;
  112. }
  113. /**
  114. * {@inheritdoc}
  115. */
  116. public function quote($input, $type=\PDO::PARAM_STR)
  117. {
  118. return "'". $this->_conn->escape_string($input) ."'";
  119. }
  120. /**
  121. * {@inheritdoc}
  122. */
  123. public function exec($statement)
  124. {
  125. if (false === $this->_conn->query($statement)) {
  126. throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno);
  127. }
  128. return $this->_conn->affected_rows;
  129. }
  130. /**
  131. * {@inheritdoc}
  132. */
  133. public function lastInsertId($name = null)
  134. {
  135. return $this->_conn->insert_id;
  136. }
  137. /**
  138. * {@inheritdoc}
  139. */
  140. public function beginTransaction()
  141. {
  142. $this->_conn->query('START TRANSACTION');
  143. return true;
  144. }
  145. /**
  146. * {@inheritdoc}
  147. */
  148. public function commit()
  149. {
  150. return $this->_conn->commit();
  151. }
  152. /**
  153. * {@inheritdoc}non-PHPdoc)
  154. */
  155. public function rollBack()
  156. {
  157. return $this->_conn->rollback();
  158. }
  159. /**
  160. * {@inheritdoc}
  161. */
  162. public function errorCode()
  163. {
  164. return $this->_conn->errno;
  165. }
  166. /**
  167. * {@inheritdoc}
  168. */
  169. public function errorInfo()
  170. {
  171. return $this->_conn->error;
  172. }
  173. /**
  174. * Apply the driver options to the connection.
  175. *
  176. * @param array $driverOptions
  177. *
  178. * @throws MysqliException When one of of the options is not supported.
  179. * @throws MysqliException When applying doesn't work - e.g. due to incorrect value.
  180. */
  181. private function setDriverOptions(array $driverOptions = array())
  182. {
  183. $supportedDriverOptions = array(
  184. \MYSQLI_OPT_CONNECT_TIMEOUT,
  185. \MYSQLI_OPT_LOCAL_INFILE,
  186. \MYSQLI_INIT_COMMAND,
  187. \MYSQLI_READ_DEFAULT_FILE,
  188. \MYSQLI_READ_DEFAULT_GROUP,
  189. );
  190. if (defined('MYSQLI_SERVER_PUBLIC_KEY')) {
  191. $supportedDriverOptions[] = \MYSQLI_SERVER_PUBLIC_KEY;
  192. }
  193. $exceptionMsg = "%s option '%s' with value '%s'";
  194. foreach ($driverOptions as $option => $value) {
  195. if ($option === static::OPTION_FLAGS) {
  196. continue;
  197. }
  198. if (!in_array($option, $supportedDriverOptions, true)) {
  199. throw new MysqliException(
  200. sprintf($exceptionMsg, 'Unsupported', $option, $value)
  201. );
  202. }
  203. if (@mysqli_options($this->_conn, $option, $value)) {
  204. continue;
  205. }
  206. $msg = sprintf($exceptionMsg, 'Failed to set', $option, $value);
  207. $msg .= sprintf(', error: %s (%d)', mysqli_error($this->_conn), mysqli_errno($this->_conn));
  208. throw new MysqliException(
  209. $msg,
  210. $this->_conn->sqlstate,
  211. $this->_conn->errno
  212. );
  213. }
  214. }
  215. /**
  216. * Pings the server and re-connects when `mysqli.reconnect = 1`
  217. *
  218. * @return bool
  219. */
  220. public function ping()
  221. {
  222. return $this->_conn->ping();
  223. }
  224. }