PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/library/phpRack/Adapters/Db/Mysql.php

http://github.com/michael-romer/zf-boilerplate
PHP | 326 lines | 165 code | 34 blank | 127 comment | 28 complexity | 4ef539cda905d28b895675fbb346e728 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0
  1. <?php
  2. /**
  3. * phpRack: Integration Testing Framework
  4. *
  5. * This source file is subject to the new BSD license that is bundled
  6. * with this package in the file LICENSE.txt. It is also available
  7. * through the world-wide-web at this URL: http://www.phprack.com/LICENSE.txt
  8. * If you did not receive a copy of the license and are unable to
  9. * obtain it through the world-wide-web, please send an email
  10. * to license@phprack.com so we can send you a copy immediately.
  11. *
  12. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  13. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  14. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  15. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  16. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  17. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  18. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  19. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  20. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  21. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  22. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  23. * POSSIBILITY OF SUCH DAMAGE.
  24. *
  25. * @copyright Copyright (c) phpRack.com
  26. * @version $Id: Mysql.php 611 2010-07-12 14:23:40Z yegor256@yahoo.com $
  27. * @category phpRack
  28. */
  29. /**
  30. * @see phpRack_Adapters_Db_Abstract
  31. */
  32. require_once PHPRACK_PATH . '/Adapters/Db/Abstract.php';
  33. /**
  34. * MySQL adapter
  35. *
  36. * The class is using native PHP mysql_ methods, without any specific
  37. * extensions like PDO or Mysqli.
  38. *
  39. * @package Adapters
  40. * @subpackage Db
  41. */
  42. class phpRack_Adapters_Db_Mysql extends phpRack_Adapters_Db_Abstract
  43. {
  44. /**
  45. * Current mysql connection link identifier
  46. *
  47. * @var int Result of mysql_connect()
  48. * @see connect()
  49. */
  50. private $_connection;
  51. /**
  52. * Destructor automatically close opened connection
  53. *
  54. * @return void
  55. */
  56. public function __destruct()
  57. {
  58. $this->closeConnection();
  59. }
  60. /**
  61. * Connect to the server
  62. *
  63. * @param string JDBC URL to connect to the server
  64. * @return void
  65. * @see http://java.sun.com/docs/books/tutorial/jdbc/basics/connecting.html
  66. * @throws Exception If MySQL extension is not loaded
  67. * @throws Exception If any of the required params are missed in the URL
  68. */
  69. public function connect($url)
  70. {
  71. // Parse JDBC URl, and throw exception if it is invalid
  72. $jdbcUrlParts = $this->_parseJdbcUrl($url);
  73. if (!extension_loaded('mysql')) {
  74. throw new Exception('MySQL extension is not loaded');
  75. }
  76. $server = $jdbcUrlParts['host'];
  77. // Check whether server port was set in JDBC URL
  78. if (isset($jdbcUrlParts['port'])) {
  79. $server .= ':' . $jdbcUrlParts['port'];
  80. }
  81. // Check whether username was set in JDBC URL
  82. if (isset($jdbcUrlParts['params']['username'])) {
  83. $username = $jdbcUrlParts['params']['username'];
  84. } else {
  85. $username = ini_get('mysql.default_user');
  86. }
  87. // Check whether password was set in JDBC URL
  88. if (isset($jdbcUrlParts['params']['password'])) {
  89. $password = $jdbcUrlParts['params']['password'];
  90. } else {
  91. $password = ini_get('mysql.default_password');
  92. }
  93. // Try to connect with MySQL server
  94. $this->_connection = @mysql_connect($server, $username, $password);
  95. if (!$this->_connection) {
  96. throw new Exception("Can't connect to MySQL server: '{$server}'");
  97. }
  98. // Check whether database was set in JDBC URL
  99. if (!empty($jdbcUrlParts['database'])) {
  100. // Try to set this database as current
  101. if (!@mysql_select_db($jdbcUrlParts['database'], $this->_connection)) {
  102. throw new Exception("Can't select database '{$jdbcUrlParts['database']}'");
  103. }
  104. }
  105. }
  106. /**
  107. * Execute SQL query on the server
  108. *
  109. * @param string SQL query
  110. * @return string Raw result from the server, in text
  111. * @throws Exception If something wrong happens there
  112. * @see mysql_query()
  113. */
  114. public function query($sql)
  115. {
  116. if (!$this->_connection) {
  117. throw new Exception('connect() method should be called before');
  118. }
  119. $result = mysql_query($sql, $this->_connection);
  120. // INSERT, UPDATE, DELETE, DROP, USE etc type queries
  121. // on success return just true
  122. if ($result === true) {
  123. return '';
  124. }
  125. // Something goes wrong
  126. if ($result === false) {
  127. throw new Exception('MySQL query error: ' . mysql_error());
  128. }
  129. // SELECT, SHOW type queries
  130. // if MySQL server returned some rows, format them for return
  131. if (mysql_num_rows($result)) {
  132. /**
  133. * @see phpRack_Adapters_Db_Mysql_Result_Formatter
  134. */
  135. require_once PHPRACK_PATH . '/Adapters/Db/Mysql/Result/Formatter.php';
  136. $response = phpRack_Adapters_Db_Mysql_Result_Formatter::formatResult($result);
  137. } else {
  138. $response = '';
  139. }
  140. return $response;
  141. }
  142. /**
  143. * Show database schema
  144. *
  145. * @return string Raw result from the server, in text
  146. * @throws Exception If connect() method wasn't executed earlier
  147. * @throws Exception If no database was selected as current
  148. * @throws Exception Passed from query()
  149. * @see phpRack_Package_Db_Mysql::showSchema()
  150. */
  151. public function showSchema()
  152. {
  153. if (!$this->isConnected()) {
  154. throw new Exception('You must call connect() method before');
  155. }
  156. if (!$this->isDatabaseSelected()) {
  157. throw new Exception('No database selected yet');
  158. }
  159. $response = '';
  160. $queries = array('SHOW TABLES', 'SHOW TRIGGERS', 'SHOW PROCEDURE STATUS');
  161. foreach ($queries as $query) {
  162. $response .= sprintf(
  163. "'%s' returns:\n%s\n",
  164. $query,
  165. $result = $this->query($query) // Exception is possible here
  166. );
  167. if ($query == 'SHOW TABLES') {
  168. // foreach table show CREATE TABLE
  169. foreach (array_slice(explode("\n", $result), 1, -1) as $tableName) {
  170. $query = sprintf("SHOW CREATE TABLE `%s`", addcslashes(trim($tableName), '`'));
  171. $response .= sprintf(
  172. "'%s' returns:\n%s\n",
  173. $query,
  174. $this->query($query) // Exception is possible
  175. );
  176. }
  177. }
  178. }
  179. return $response;
  180. }
  181. /**
  182. * Show connections and their status
  183. *
  184. * @return string Raw result from the server, in text
  185. * @throws Exception If connect() method wasn't executed earlier
  186. * @see phpRack_Package_Db_Mysql::showConnections()
  187. */
  188. public function showConnections()
  189. {
  190. if (!$this->isConnected()) {
  191. throw new Exception('You must call connect() method before');
  192. }
  193. $answer = $this->query('SHOW GRANTS FOR CURRENT_USER');
  194. if (!preg_match('~GRANT (PROCESS|ALL)~', $answer)) {
  195. return false;
  196. }
  197. return $this->query('SHOW FULL PROCESSLIST');
  198. }
  199. /**
  200. * Show server info
  201. *
  202. * @return string Raw result from the server, in text
  203. * @throws Exception If connect() method wasn't executed earlier
  204. * @see phpRack_Package_Db_Mysql::showServerInfo()
  205. */
  206. public function showServerInfo()
  207. {
  208. $out = '';
  209. // Users privileges. We must check grants for it
  210. $privileges = $this->query('SHOW GRANTS FOR CURRENT_USER');
  211. if (preg_match('~GRANT (ALL|SELECT ON (`mysql`|\*)\.\*)~', $privileges)) {
  212. $dbUsers = $this->query(
  213. 'SELECT CONCAT(User, "\'@\'", Host) FROM `mysql`.`user`'
  214. );
  215. $dbUsers = explode("\n", rtrim($dbUsers, "\n"));
  216. $dbUsers = array_map('trim', $dbUsers);
  217. for ($i=1; $i<count($dbUsers); $i++) {
  218. $priv = $this->query("SHOW GRANTS FOR '{$dbUsers[$i]}'");
  219. $out .= preg_replace('~GRANT (.+?) TO.*~', '\1', trim($priv));
  220. $out .= "\n\n";
  221. }
  222. }
  223. // Table stats. We do not need to check privileges here
  224. $dbList = $this->query('SHOW DATABASES');
  225. $dbList = explode("\n", rtrim($dbList, "\n"));
  226. if (count($dbList) > 2) {
  227. $dbList = array_map('trim', $dbList);
  228. for ($i=2; $i<count($dbList); $i++) {
  229. $out .= "Database: {$dbList[$i]}\n";
  230. $out .= $this->query(
  231. "SELECT COUNT(TABLE_NAME) AS 'Count Of Tables',
  232. SUM(TABLE_ROWS) AS 'Count Of Rows',
  233. SUM(DATA_LENGTH) AS 'Size Of Data',
  234. SUM(INDEX_LENGTH) AS 'Index Size'
  235. FROM `information_schema`.`TABLES`
  236. WHERE TABLE_SCHEMA = '{$dbList[$i]}'"
  237. );
  238. $out .= "\n";
  239. }
  240. }
  241. // Mysql version. We do not need to check privileges here
  242. $out .= $this->query("SHOW VARIABLES LIKE 'version'") . "\n";
  243. // Mysql variables. We do not need to check privileges here
  244. $out .= $this->query('SHOW GLOBAL VARIABLES') . "\n";
  245. return $out;
  246. }
  247. /**
  248. * Return true if adapter is connected with database
  249. *
  250. * @return boolean
  251. * @see $this->_connection
  252. */
  253. public function isConnected()
  254. {
  255. if ($this->_connection) {
  256. return true;
  257. } else {
  258. return false;
  259. }
  260. }
  261. /**
  262. * Return true if some database was selected for use
  263. *
  264. * @return boolean
  265. */
  266. public function isDatabaseSelected()
  267. {
  268. $result = $this->query('SELECT DATABASE()');
  269. /**
  270. * @see phpRack_Adapters_Db_Mysql_Result_Formatter
  271. */
  272. require_once PHPRACK_PATH . '/Adapters/Db/Mysql/Result/Formatter.php';
  273. if (trim(phpRack_Adapters_Db_Mysql_Result_Formatter::removeColumnHeadersLine($result)) == '') {
  274. return false;
  275. } else {
  276. return true;
  277. }
  278. }
  279. /**
  280. * Close connection to database, if was earlier opened
  281. *
  282. * @return void
  283. */
  284. public function closeConnection()
  285. {
  286. if (is_resource($this->_connection)) {
  287. mysql_close($this->_connection);
  288. $this->_connection = null;
  289. }
  290. }
  291. }