2 /** @package verysimple::DB::Reflection */
5 * import supporting libraries
7 require_once("DBColumn.php");
8 require_once("DBConstraint.php");
9 require_once("DBSet.php");
10 require_once("DBKey.php");
13 * DBTable is an object representation of a MySQL Table
15 * @package verysimple::DB::Reflection
16 * @author Jason Hinkle
17 * @copyright 1997-2007 VerySimple, Inc.
18 * @license http://www.gnu.org/licenses/lgpl.html LGPL
27 public $DefaultCharacterSet;
34 public $IsView = false;
35 static $NUMERIC_TABLE_PREFIX = 'TBL_';
36 static $NUMERIC_COLUMN_PREFIX = 'COL_';
39 * Instantiate new DBTable
42 * @param DBSchema $schema
43 * @return Array $row array that is result from "show tables" statement
45 function __construct($schema, $row)
47 $this->Schema
= $schema;
48 $this->Name
= $row ["Tables_in_" . $this->Schema
->Name
];
49 $this->Columns
= array ();
50 $this->PrimaryKeys
= array ();
51 $this->ForeignKeys
= array ();
52 $this->Constraints
= array ();
53 $this->Sets
= array ();
56 $this->DiscoverColumnPrefix();
60 * Returns the number of columns involved in the primary key
64 function NumberOfPrimaryKeyColumns()
66 return count($this->PrimaryKeys
);
70 * Returns name of the primary key
71 * TODO: If there are multiple keys, this is no accurate.
72 * Only returns the first key found
75 * @param bool $remove_prefix
78 function GetPrimaryKeyName($remove_prefix = true)
80 foreach ($this->PrimaryKeys
as $key) {
81 return ($remove_prefix) ?
$this->RemovePrefix($key->KeyColumn
) : $key->KeyColumn
;
84 // views don't technically have a primary key but we will return the first column if anybody asks
86 return $this->GetColumnNameByIndex(0, $remove_prefix);
91 * Returns the name of the column at the given index
96 * @param bool $remove_prefix
99 function GetColumnNameByIndex($index, $remove_prefix = true)
102 foreach ($this->Columns
as $column) {
103 if ($count == $index) {
104 return ($remove_prefix) ?
$column->NameWithoutPrefix
: $column->Name
;
108 throw new Exception('Index out of bounds');
112 * Returns true if the primary key for this table is an auto_increment
113 * TODO: Only checks the first key if there are multiple primary keys
118 function PrimaryKeyIsAutoIncrement()
120 $pk = $this->GetPrimaryKeyName(false);
121 return $pk && $this->Columns
[$pk]->Extra
== "auto_increment";
125 * Returns name of the first varchar field which could be used as a "label"
130 function GetDescriptorName($remove_prefix = true)
132 foreach ($this->Columns
as $column) {
133 if ($column->Type
== "varchar") {
134 return ($remove_prefix) ?
$this->RemovePrefix($column->Name
) : $column->Name
;
138 // give up because there are no varchars in this table
139 return $this->GetPrimaryKeyName($remove_prefix);
143 * Inspects all columns to see if there is a common prefix in the format: XXX_
147 private function DiscoverColumnPrefix()
151 foreach ($this->Columns
as $column) {
152 $curr_prefix = substr($column->Name
, 0, strpos($column->Name
, "_") +
1);
154 if ($prev_prefix == "") {
155 // first time through the loop
156 $prev_prefix = $curr_prefix ?
$curr_prefix : "#NONE#";
157 } elseif ($prev_prefix != $curr_prefix) {
163 // set the table column prefix property
164 $this->ColumnPrefix
= $curr_prefix;
166 // update the columns to reflect the prefix as well
167 foreach ($this->Columns
as $column) {
168 $column->NameWithoutPrefix
= substr($column->Name
, strlen($curr_prefix));
172 // if a column begins with a numeric character then prepend a string to prevent generated code errors
173 if (self
::$NUMERIC_COLUMN_PREFIX) {
174 foreach ($this->Columns
as $column) {
175 if (is_numeric(substr($column->NameWithoutPrefix
, 0, 1))) {
176 $column->NameWithoutPrefix
= self
::$NUMERIC_COLUMN_PREFIX . $column->NameWithoutPrefix
;
183 * Returns a name that is acceptable to be used as the "object" name in generated code
185 public function GetObjectName()
187 if (is_numeric(substr($this->Name
, 0, 1))) {
188 return self
::$NUMERIC_TABLE_PREFIX . $this->Name
;
195 * Given a column name, removes the prefix
199 public function RemovePrefix($name)
201 // print "remove prefix $name: " . $this->ColumnPrefix . "<br>";
202 return substr($name, strlen($this->ColumnPrefix
));
206 * Inspects the current table and loads all Columns
210 private function LoadColumns()
213 $sql = "describe `" . $this->Name
. "`";
215 $rs = $this->Schema
->Server
->Connection
->Select($sql);
217 while ($row = $this->Schema
->Server
->Connection
->Next($rs)) {
218 $this->Columns
[$row ["Field"]] = new DBColumn($this, $row);
221 $this->Schema
->Server
->Connection
->Release($rs);
225 * Load the keys and constraints for this table and populate the sets for
226 * all tables on which this table is dependents
230 public function LoadKeys()
233 // get the keys and constraints
234 $sql = "show create table `" . $this->Name
. "`";
238 $rs = $this->Schema
->Server
->Connection
->Select($sql);
240 if ($row = $this->Schema
->Server
->Connection
->Next($rs)) {
241 if (isset($row ["Create Table"])) {
242 $create_table = $row ["Create Table"];
243 } else if (isset($row ["Create View"])) {
244 $this->IsView
= true;
245 $create_table = $row ["Create View"];
247 // treat the 1st column in a view as the primary key
248 $this->Columns
[$this->GetColumnNameByIndex(0, false)]->Key
= 'PRI';
250 throw new Exception("Unknown Table Type");
254 $this->Schema
->Server
->Connection
->Release($rs);
256 $lines = explode("\n", $create_table);
258 foreach ($lines as $line) {
260 if (substr($line, 0, 11) == "PRIMARY KEY") {
261 preg_match_all("/`(\w+)`/", $line, $matches, PREG_PATTERN_ORDER
);
262 // print "<pre>"; print_r($matches); die(); // DEBUG
263 $this->PrimaryKeys
[$matches [1] [0]] = new DBKey($this, "PRIMARY KEY", $matches [0] [0]);
264 } elseif (substr($line, 0, 3) == "KEY") {
265 preg_match_all("/`(\w+)`/", $line, $matches, PREG_PATTERN_ORDER
);
266 // print "<pre>"; print_r($matches); die(); // DEBUG
267 $this->ForeignKeys
[$matches [1] [0]] = new DBKey($this, $matches [1] [0], $matches [1] [1]);
269 // Add keys to the column for convenience
270 $this->Columns
[$matches [1] [1]]->Keys
[] = $matches [1] [0];
271 } elseif (substr($line, 0, 10) == "CONSTRAINT") {
272 preg_match_all("/`(\w+)`/", $line, $matches, PREG_PATTERN_ORDER
);
273 // print "<pre>"; print_r($matches); die(); // DEBUG
274 $this->Constraints
[$matches [1] [0]] = new DBConstraint($this, $matches [1]);
276 // the set is basically the reverse of the constraint, but we want to add it to the
277 // constraining table so we don't have to do reverse-lookup looking for child relationships
278 $this->Schema
->Tables
[$matches [1] [2]]->Sets
[$matches [1] [0]] = new DBSet($this, $matches [1]);
280 // print "<pre>##########################\r\n" . print_r($matches,1) . "\r\n##########################\r\n";
282 // Add constraints to the column for convenience
283 $this->Columns
[$matches [1] [1]]->Constraints
[] = $matches [1] [0];
284 } elseif (strstr($line, "COMMENT ")) {
285 // TODO: this is pretty fragile... ?
286 // table comments and column comments are seemingly differentiated by "COMMENT=" vs "COMMENT "
287 $parts = explode("`", $line);
288 $column = $parts [1];
289 $comment = strstr($line, "COMMENT ");
290 $comment = substr($comment, 9, strlen($comment) - 11);
291 $comment = str_replace("''", "'", $comment);
292 $this->Columns
[$column]->Comment
= $comment;
294 if ($this->Columns
[$column]->Default == "" && substr($comment, 0, 8) == "default=") {
295 $this->Columns
[$column]->Default = substr($comment, 9, strlen($comment) - 10);
298 // print "<pre>" . $column . "=" . htmlspecialchars( $this->Columns[$column]->Default );
301 // TODO: look for COMMENT