xpdogenerator.class.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <?php
  2. /*
  3. * Copyright 2010-2015 by MODX, LLC.
  4. *
  5. * This file is part of xPDO.
  6. *
  7. * xPDO is free software; you can redistribute it and/or modify it under the
  8. * terms of the GNU General Public License as published by the Free Software
  9. * Foundation; either version 2 of the License, or (at your option) any later
  10. * version.
  11. *
  12. * xPDO is distributed in the hope that it will be useful, but WITHOUT ANY
  13. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  14. * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along with
  17. * xPDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
  18. * Suite 330, Boston, MA 02111-1307 USA
  19. */
  20. /**
  21. * MySQL classes for generating xPDOObject classes and maps from an xPDO schema.
  22. *
  23. * @package xpdo
  24. * @subpackage om.mysql
  25. */
  26. /**
  27. * Include the parent {@link xPDOGenerator} class.
  28. */
  29. include_once (dirname(__DIR__) . '/xpdogenerator.class.php');
  30. /**
  31. * An extension for generating {@link xPDOObject} class and map files for MySQL.
  32. *
  33. * A MySQL-specific extension to an {@link xPDOManager} instance that can
  34. * generate class stub and meta-data map files from a provided XML schema of a
  35. * database structure.
  36. *
  37. * @package xpdo
  38. * @subpackage om.mysql
  39. */
  40. class xPDOGenerator_mysql extends xPDOGenerator {
  41. public function compile($path = '') {
  42. return false;
  43. }
  44. public function getIndex($index) {
  45. switch ($index) {
  46. case 'PRI':
  47. $index= 'pk';
  48. break;
  49. case 'UNI':
  50. $index= 'unique';
  51. break;
  52. case 'MUL':
  53. $index= 'index';
  54. break;
  55. default:
  56. break;
  57. }
  58. if (!empty ($index)) {
  59. $index= ' index="' . $index . '"';
  60. }
  61. return $index;
  62. }
  63. /**
  64. * Write an xPDO XML Schema from your database.
  65. *
  66. * @param string $schemaFile The name (including path) of the schemaFile you
  67. * want to write.
  68. * @param string $package Name of the package to generate the classes in.
  69. * @param string $baseClass The class which all classes in the package will
  70. * extend; by default this is set to {@link xPDOObject} and any
  71. * auto_increment fields with the column name 'id' will extend {@link
  72. * xPDOSimpleObject} automatically.
  73. * @param string $tablePrefix The table prefix for the current connection,
  74. * which will be removed from all of the generated class and table names.
  75. * Specify a prefix when creating a new {@link xPDO} instance to recreate
  76. * the tables with the same prefix, but still use the generic class names.
  77. * @param boolean $restrictPrefix Only reverse-engineer tables that have the
  78. * specified tablePrefix; if tablePrefix is empty, this is ignored.
  79. * @return boolean True on success, false on failure.
  80. */
  81. public function writeSchema($schemaFile, $package= '', $baseClass= '', $tablePrefix= '', $restrictPrefix= false) {
  82. if (empty ($package))
  83. $package= $this->manager->xpdo->package;
  84. if (empty ($baseClass))
  85. $baseClass= 'xPDOObject';
  86. if (empty ($tablePrefix))
  87. $tablePrefix= $this->manager->xpdo->config[xPDO::OPT_TABLE_PREFIX];
  88. $schemaVersion = xPDO::SCHEMA_VERSION;
  89. $xmlContent = array();
  90. $xmlContent[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
  91. $xmlContent[] = "<model package=\"{$package}\" baseClass=\"{$baseClass}\" platform=\"mysql\" defaultEngine=\"InnoDB\" version=\"{$schemaVersion}\">";
  92. //read list of tables
  93. $dbname= $this->manager->xpdo->escape($this->manager->xpdo->config['dbname']);
  94. $tableLike= ($tablePrefix && $restrictPrefix) ? " LIKE '{$tablePrefix}%'" : '';
  95. $tablesStmt= $this->manager->xpdo->prepare("SHOW TABLES FROM {$dbname}{$tableLike}");
  96. $tstart = microtime(true);
  97. $tablesStmt->execute();
  98. $this->manager->xpdo->queryTime += microtime(true) - $tstart;
  99. $this->manager->xpdo->executedQueries++;
  100. $tables= $tablesStmt->fetchAll(PDO::FETCH_NUM);
  101. if ($this->manager->xpdo->getDebug() === true) $this->manager->xpdo->log(xPDO::LOG_LEVEL_DEBUG, print_r($tables, true));
  102. foreach ($tables as $table) {
  103. $xmlObject= array();
  104. $xmlFields= array();
  105. $xmlIndices= array();
  106. if (!$tableName= $this->getTableName($table[0], $tablePrefix, $restrictPrefix)) {
  107. continue;
  108. }
  109. $class= $this->getClassName($tableName);
  110. $extends= $baseClass;
  111. $fieldsStmt= $this->manager->xpdo->query('SHOW COLUMNS FROM ' . $this->manager->xpdo->escape($table[0]));
  112. if ($fieldsStmt) {
  113. $fields= $fieldsStmt->fetchAll(PDO::FETCH_ASSOC);
  114. if ($this->manager->xpdo->getDebug() === true) $this->manager->xpdo->log(xPDO::LOG_LEVEL_DEBUG, print_r($fields, true));
  115. if (!empty($fields)) {
  116. foreach ($fields as $field) {
  117. $Field= '';
  118. $Type= '';
  119. $Null= '';
  120. $Key= '';
  121. $Default= '';
  122. $Extra= '';
  123. extract($field, EXTR_OVERWRITE);
  124. $Type= xPDO :: escSplit(' ', $Type, "'", 2);
  125. $precisionPos= strpos($Type[0], '(');
  126. $dbType= $precisionPos? substr($Type[0], 0, $precisionPos): $Type[0];
  127. $dbType= strtolower($dbType);
  128. $Precision= $precisionPos? substr($Type[0], $precisionPos + 1, strrpos($Type[0], ')') - ($precisionPos + 1)): '';
  129. if (!empty ($Precision)) {
  130. $Precision= ' precision="' . trim($Precision) . '"';
  131. }
  132. $attributes= '';
  133. if (isset ($Type[1]) && !empty ($Type[1])) {
  134. $attributes= ' attributes="' . trim($Type[1]) . '"';
  135. }
  136. $PhpType= $this->manager->xpdo->driver->getPhpType($dbType);
  137. $Null= ' null="' . (($Null === 'NO') ? 'false' : 'true') . '"';
  138. $Key= $this->getIndex($Key);
  139. $Default= $this->getDefault($Default);
  140. if (!empty ($Extra)) {
  141. if ($Extra === 'auto_increment') {
  142. if ($baseClass === 'xPDOObject' && $Field === 'id') {
  143. $extends= 'xPDOSimpleObject';
  144. continue;
  145. } else {
  146. $Extra= ' generated="native"';
  147. }
  148. } else {
  149. $Extra= ' extra="' . strtolower($Extra) . '"';
  150. }
  151. $Extra= ' ' . $Extra;
  152. }
  153. $xmlFields[] = "\t\t<field key=\"{$Field}\" dbtype=\"{$dbType}\"{$Precision}{$attributes} phptype=\"{$PhpType}\"{$Null}{$Default}{$Key}{$Extra} />";
  154. }
  155. } else {
  156. $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'No columns were found in table ' . $table[0]);
  157. }
  158. } else {
  159. $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Error retrieving columns for table ' . $table[0]);
  160. }
  161. $whereClause= ($extends === 'xPDOSimpleObject' ? " WHERE `Key_name` != 'PRIMARY'" : '');
  162. $indexesStmt= $this->manager->xpdo->query('SHOW INDEXES FROM ' . $this->manager->xpdo->escape($table[0]) . $whereClause);
  163. if ($indexesStmt) {
  164. $indexes= $indexesStmt->fetchAll(PDO::FETCH_ASSOC);
  165. if ($this->manager->xpdo->getDebug() === true) $this->manager->xpdo->log(xPDO::LOG_LEVEL_DEBUG, "Indices for table {$table[0]}: " . print_r($indexes, true));
  166. if (!empty($indexes)) {
  167. $indices = array();
  168. foreach ($indexes as $index) {
  169. if (!array_key_exists($index['Key_name'], $indices)) $indices[$index['Key_name']] = array();
  170. $indices[$index['Key_name']][$index['Seq_in_index']] = $index;
  171. }
  172. foreach ($indices as $index) {
  173. $xmlIndexCols = array();
  174. if ($this->manager->xpdo->getDebug() === true) $this->manager->xpdo->log(xPDO::LOG_LEVEL_DEBUG, "Details of index: " . print_r($index, true));
  175. foreach ($index as $columnSeq => $column) {
  176. if ($columnSeq == 1) {
  177. $keyName = $column['Key_name'];
  178. $primary = $keyName == 'PRIMARY' ? 'true' : 'false';
  179. $unique = empty($column['Non_unique']) ? 'true' : 'false';
  180. $packed = empty($column['Packed']) ? 'false' : 'true';
  181. $type = $column['Index_type'];
  182. }
  183. $null = $column['Null'] == 'YES' ? 'true' : 'false';
  184. $xmlIndexCols[]= "\t\t\t<column key=\"{$column['Column_name']}\" length=\"{$column['Sub_part']}\" collation=\"{$column['Collation']}\" null=\"{$null}\" />";
  185. }
  186. $xmlIndices[]= "\t\t<index alias=\"{$keyName}\" name=\"{$keyName}\" primary=\"{$primary}\" unique=\"{$unique}\" type=\"{$type}\" >";
  187. $xmlIndices[]= implode("\n", $xmlIndexCols);
  188. $xmlIndices[]= "\t\t</index>";
  189. }
  190. } else {
  191. $this->manager->xpdo->log(xPDO::LOG_LEVEL_WARN, 'No indexes were found in table ' . $table[0]);
  192. }
  193. } else {
  194. $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Error getting indexes for table ' . $table[0]);
  195. }
  196. $xmlObject[] = "\t<object class=\"{$class}\" table=\"{$tableName}\" extends=\"{$extends}\">";
  197. $xmlObject[] = implode("\n", $xmlFields);
  198. if (!empty($xmlIndices)) {
  199. $xmlObject[] = '';
  200. $xmlObject[] = implode("\n", $xmlIndices);
  201. }
  202. $xmlObject[] = "\t</object>";
  203. $xmlContent[] = implode("\n", $xmlObject);
  204. }
  205. $xmlContent[] = "</model>";
  206. if ($this->manager->xpdo->getDebug() === true) {
  207. $this->manager->xpdo->log(xPDO::LOG_LEVEL_DEBUG, implode("\n", $xmlContent));
  208. }
  209. $file= fopen($schemaFile, 'wb');
  210. $written= fwrite($file, implode("\n", $xmlContent));
  211. fclose($file);
  212. return true;
  213. }
  214. }