DBTable.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <?php
  2. /** @package verysimple::DB::Reflection */
  3. /** import supporting libraries */
  4. require_once("DBColumn.php");
  5. require_once("DBConstraint.php");
  6. require_once("DBSet.php");
  7. require_once("DBKey.php");
  8. /**
  9. * DBTable is an object representation of a MySQL Table
  10. *
  11. * @package verysimple::DB::Reflection
  12. * @author Jason Hinkle
  13. * @copyright 1997-2007 VerySimple, Inc.
  14. * @license http://www.gnu.org/licenses/lgpl.html LGPL
  15. * @version 1.0
  16. */
  17. class DBTable
  18. {
  19. public $Schema;
  20. public $Name;
  21. public $Engine;
  22. public $Comment;
  23. public $DefaultCharacterSet;
  24. public $Columns;
  25. public $ColumnPrefix;
  26. public $PrimaryKeys;
  27. public $ForeignKeys;
  28. public $Constraints;
  29. public $Sets;
  30. public $IsView = false;
  31. /**
  32. * Instantiate new DBTable
  33. *
  34. * @access public
  35. * @param DBSchema $schema
  36. * @return Array $row array that is result from "show tables" statement
  37. */
  38. function __construct($schema, $row)
  39. {
  40. $this->Schema = $schema;
  41. $this->Name = $row["Tables_in_" . $this->Schema->Name];
  42. $this->Columns = Array();
  43. $this->PrimaryKeys = Array();
  44. $this->ForeignKeys = Array();
  45. $this->Constraints = Array();
  46. $this->Sets = Array();
  47. $this->LoadColumns();
  48. $this->DiscoverColumnPrefix();
  49. }
  50. /**
  51. * Returns the number of columns involved in the primary key
  52. * @return number
  53. */
  54. function NumberOfPrimaryKeyColumns()
  55. {
  56. return count($this->PrimaryKeys);
  57. }
  58. /**
  59. * Returns name of the primary key
  60. * TODO: If there are multiple keys, this is no accurate. Only returns the first key found
  61. *
  62. * @access public
  63. * @param bool $remove_prefix
  64. * @return string
  65. */
  66. function GetPrimaryKeyName($remove_prefix = true)
  67. {
  68. foreach ($this->PrimaryKeys as $key)
  69. {
  70. return ($remove_prefix) ? $this->RemovePrefix($key->KeyColumn) : $key->KeyColumn;
  71. }
  72. // views don't technically have a primary key but we will return the first column if anybody asks
  73. if ($this->IsView) return $this->GetColumnNameByIndex(0,$remove_prefix);
  74. }
  75. /**
  76. * Returns the name of the column at the given index
  77. *
  78. * @access public
  79. * @param int $index (zero based)
  80. * @param bool $remove_prefix
  81. * @return string
  82. */
  83. function GetColumnNameByIndex($index, $remove_prefix = true)
  84. {
  85. $count = 0;
  86. foreach ($this->Columns as $column)
  87. {
  88. if ($count == $index) return ($remove_prefix) ? $column->NameWithoutPrefix : $column->Name;
  89. }
  90. throw new Exception('Index out of bounds');
  91. }
  92. /**
  93. * Returns true if the primary key for this table is an auto_increment
  94. * TODO: Only checks the first key if there are multiple primary keys
  95. *
  96. * @access public
  97. * @return bool
  98. */
  99. function PrimaryKeyIsAutoIncrement()
  100. {
  101. $pk = $this->GetPrimaryKeyName(false);
  102. return $pk && $this->Columns[$pk]->Extra == "auto_increment";
  103. }
  104. /**
  105. * Returns name of the first varchar field which could be used as a "label"
  106. *
  107. * @access public
  108. * @return string
  109. */
  110. function GetDescriptorName($remove_prefix = true)
  111. {
  112. foreach ($this->Columns as $column)
  113. {
  114. if ($column->Type == "varchar")
  115. {
  116. return ($remove_prefix) ? $this->RemovePrefix($column->Name) : $column->Name;
  117. }
  118. }
  119. // give up because there are no varchars in this table
  120. return $this->GetPrimaryKeyName($remove_prefix);
  121. }
  122. /**
  123. * Inspects all columns to see if there is a common prefix in the format: XXX_
  124. *
  125. * @access private
  126. */
  127. private function DiscoverColumnPrefix()
  128. {
  129. $prev_prefix = "";
  130. $has_prefix = true;
  131. foreach ($this->Columns as $column)
  132. {
  133. $curr_prefix = substr($column->Name, 0, strpos($column->Name,"_")+1);
  134. if ($prev_prefix == "")
  135. {
  136. // first time through the loop
  137. $prev_prefix = $curr_prefix ? $curr_prefix : "#NONE#";
  138. }
  139. elseif ($prev_prefix != $curr_prefix)
  140. {
  141. $has_prefix = false;
  142. }
  143. }
  144. if ($has_prefix)
  145. {
  146. // set the table column prefix property
  147. $this->ColumnPrefix = $curr_prefix;
  148. // update the columns to reflect the prefix as well
  149. foreach ($this->Columns as $column)
  150. {
  151. $column->NameWithoutPrefix = substr($column->Name,strlen($curr_prefix));
  152. }
  153. }
  154. }
  155. /**Given a column name, removes the prefix
  156. *
  157. * @access private
  158. */
  159. public function RemovePrefix($name)
  160. {
  161. // print "remove prefix $name: " . $this->ColumnPrefix . "<br>";
  162. return substr($name,strlen($this->ColumnPrefix));
  163. }
  164. /**
  165. * Inspects the current table and loads all Columns
  166. *
  167. * @access private
  168. */
  169. private function LoadColumns()
  170. {
  171. // get the colums
  172. $sql = "describe `".$this->Name."`";
  173. $rs = $this->Schema->Server->Connection->Select($sql);
  174. while ($row = $this->Schema->Server->Connection->Next($rs))
  175. {
  176. $this->Columns[$row["Field"]] = new DBColumn($this,$row);
  177. }
  178. $this->Schema->Server->Connection->Release($rs);
  179. }
  180. /**
  181. * Load the keys and constraints for this table and populate the sets for
  182. * all tables on which this table is dependents
  183. *
  184. * @access public
  185. */
  186. public function LoadKeys()
  187. {
  188. // get the keys and constraints
  189. $sql = "show create table `".$this->Name."`";
  190. $create_table = "";
  191. $rs = $this->Schema->Server->Connection->Select($sql);
  192. if ($row = $this->Schema->Server->Connection->Next($rs))
  193. {
  194. if (isset($row["Create Table"]))
  195. {
  196. $create_table = $row["Create Table"];
  197. }
  198. else if (isset($row["Create View"]))
  199. {
  200. $this->IsView = true;
  201. $create_table = $row["Create View"];
  202. // treat the 1st column in a view as the primary key
  203. $this->Columns[$this->GetColumnNameByIndex(0,false)]->Key = 'PRI';
  204. }
  205. else
  206. {
  207. throw new Exception("Unknown Table Type");
  208. }
  209. }
  210. $this->Schema->Server->Connection->Release($rs);
  211. $lines = explode("\n", $create_table);
  212. foreach ($lines as $line)
  213. {
  214. $line = trim($line);
  215. if (substr($line,0,11) == "PRIMARY KEY")
  216. {
  217. preg_match_all("/`(\w+)`/",$line, $matches, PREG_PATTERN_ORDER);
  218. // print "<pre>"; print_r($matches); die(); // DEBUG
  219. $this->PrimaryKeys[$matches[1][0]] = new DBKey($this,"PRIMARY KEY",$matches[0][0]);
  220. }
  221. elseif (substr($line,0,3) == "KEY")
  222. {
  223. preg_match_all("/`(\w+)`/",$line, $matches, PREG_PATTERN_ORDER);
  224. // print "<pre>"; print_r($matches); die(); // DEBUG
  225. $this->ForeignKeys[$matches[1][0]] = new DBKey($this,$matches[1][0],$matches[1][1]);
  226. // Add keys to the column for convenience
  227. $this->Columns[$matches[1][1]]->Keys[] = $matches[1][0];
  228. }
  229. elseif (substr($line,0,10) == "CONSTRAINT")
  230. {
  231. preg_match_all("/`(\w+)`/",$line, $matches, PREG_PATTERN_ORDER);
  232. //print "<pre>"; print_r($matches); die(); // DEBUG
  233. $this->Constraints[$matches[1][0]] = new DBConstraint($this,$matches[1]);
  234. // the set is basically the reverse of the constraint, but we want to add it to the
  235. // constraining table so we don't have to do reverse-lookup looking for child relationships
  236. $this->Schema->Tables[$matches[1][2]]->Sets[$matches[1][0]] = new DBSet($this,$matches[1]);
  237. //print "<pre>##########################\r\n" . print_r($matches,1) . "\r\n##########################\r\n";
  238. // Add constraints to the column for convenience
  239. $this->Columns[$matches[1][1]]->Constraints[] = $matches[1][0];
  240. }
  241. elseif ( strstr( $line, "COMMENT ") )
  242. {
  243. // TODO: this is pretty fragile... ?
  244. // table comments and column comments are seemingly differentiated by "COMMENT=" vs "COMMENT "
  245. $parts = explode("`",$line);
  246. $column = $parts[1];
  247. $comment = strstr( $line, "COMMENT ");
  248. $comment = substr($comment,9,strlen($comment)-11);
  249. $comment = str_replace("''","'",$comment);
  250. $this->Columns[$column]->Comment = $comment;
  251. if ($this->Columns[$column]->Default == "" && substr($comment,0,8) == "default=")
  252. {
  253. $this->Columns[$column]->Default = substr($comment,9, strlen($comment)-10 );
  254. }
  255. // print "<pre>" . $column . "=" . htmlspecialchars( $this->Columns[$column]->Default );
  256. }
  257. // TODO: look for COMMENT
  258. }
  259. }
  260. }
  261. ?>