Open Journal Systems  3.3.0
xmlSchemaToMigration.php
1 <?php
2 
14 require(dirname(dirname(dirname(dirname(__FILE__)))) . '/tools/bootstrap.inc.php');
15 
18  protected $source;
19 
21  protected $className;
22 
26  function __construct($argv = array()) {
27  parent::__construct($argv);
28 
29  array_shift($argv); // Shift the tool name off the top
30 
31  $this->source = array_shift($argv);
32  $this->className = array_shift($argv);
33 
34  // The source file/directory must be specified and exist.
35  if (empty($this->source) || !file_exists($this->source)) {
36  $this->usage();
37  exit(2);
38  }
39 
40  if (empty($this->className) | !preg_match('/^[a-zA-Z]+$/', $this->className)) {
41  $this->usage();
42  exit(3);
43  }
44  }
45 
49  function usage() {
50  echo "Script to convert ADODB XMLSchema-based schema descriptors to Illuminate migrations\n\n"
51  . "Usage: {$this->scriptName} input-schema-file.xml GenerateClassNamed\n\n";
52  }
53 
57  function execute() {
58  $doc = new DOMDocument();
59  $doc->preserveWhiteSpace = false;
60  $doc->loadXML(file_get_contents($this->source));
61  if ($doc->documentElement->nodeName != 'schema') throw new Exception('Invalid document element ' . $this->documentElement->nodeName);
62 
63  echo "<?php
64 
76 use Illuminate\Database\Migrations\Migration;
77 use Illuminate\Database\Schema\Builder;
78 use Illuminate\Database\Schema\Blueprint;
79 use Illuminate\Database\Capsule\Manager as Capsule;
80 
81 class $this->className extends Migration {
86  public function up() {\n";
87  foreach ($doc->documentElement->childNodes as $tableNode) {
88  if ($tableNode->nodeType == XML_COMMENT_NODE) continue; // Skip comments
89  if ($tableNode->nodeName != 'table') throw new Exception('Unexpected table node name ' . $tableNode->nodeName);
90  foreach ($tableNode->childNodes as $tableChild) {
91  if ($tableChild->nodeName == 'descr') echo "\t\t// " . $tableChild->nodeValue . "\n";
92  }
93  echo "\t\tCapsule::schema()->create('" . $tableNode->getAttribute('name') . "', function (Blueprint \$table) {\n";
94  $keys = [];
95  $hasAutoIncrementNamed = null;
96  foreach ($tableNode->childNodes as $tableChild) switch (true) {
97  case $tableChild->nodeType == XML_COMMENT_NODE: throw new Exception('Unexpected comment in table ' . $tableNode->getAttribute('name') . '!');
98  case $tableChild->nodeName == 'field':
99  // Preprocess comments
100  foreach ($tableChild->childNodes as $fieldChild) switch (true) {
101  case $fieldChild->nodeType == XML_COMMENT_NODE:
102  echo "\t\t\t// " . $fieldChild->nodeValue . "\n";
103  break;
104  }
105  echo "\t\t\t";
106  $allowDefault = true;
107  switch ($tableChild->getAttribute('type')) {
108  case 'XL':
109  echo "\$table->longText('" . $tableChild->getAttribute('name') . "')";
110  $allowDefault = false;
111  break;
112  case 'X':
113  echo "\$table->text('" . $tableChild->getAttribute('name') . "')";
114  $allowDefault = false;
115  break;
116  case 'I1':
117  // This may be a boolean or a numeric constant!
118  // MySQL complains (https://github.com/laravel/framework/issues/8840)
119  // but it's better to review and fix manually rather than rely on this.
120  echo "\$table->tinyInteger('" . $tableChild->getAttribute('name') . "')";
121  break;
122  case 'I2':
123  echo "\$table->smallInteger('" . $tableChild->getAttribute('name') . "')";
124  break;
125  case 'I4':
126  echo "\$table->integer('" . $tableChild->getAttribute('name') . "')";
127  break;
128  case 'I8':
129  echo "\$table->bigInteger('" . $tableChild->getAttribute('name') . "')";
130  break;
131  case 'F':
132  echo "\$table->float('" . $tableChild->getAttribute('name') . "', 8, 2)";
133  break;
134  case 'T':
135  echo "\$table->datetime('" . $tableChild->getAttribute('name') . "')";
136  break;
137  case 'D':
138  echo "\$table->date('" . $tableChild->getAttribute('name') . "')";
139  break;
140  case 'C':
141  echo "\$table->string('" . $tableChild->getAttribute('name') . "', " . (int) $tableChild->getAttribute('size') . ")";
142  break;
143  case 'C2':
144  echo "\$table->string('" . $tableChild->getAttribute('name') . "', " . (int) $tableChild->getAttribute('size') . ")";
145  break;
146  default: throw new Exception('Unspecified or unknown table type ' . $tableChild->getAttribute('type') . ' in column ' . $tableChild->getAttribute('name') . ' of table ' . $tableNode->getAttribute('name'));
147  }
148  $nullable = true;
149  $autoIncrement = false;
150  foreach ($tableChild->childNodes as $fieldChild) switch (true) {
151  case $fieldChild->nodeType == XML_COMMENT_NODE: break; // Already processed above
152  case $fieldChild->nodeName == 'NOTNULL': $nullable = false; break;
153  case $fieldChild->nodeName == 'AUTOINCREMENT':
154  $autoIncrement = true;
155  $hasAutoIncrementNamed = $tableChild->getAttribute('name');
156  $nullable = false;
157  break;
158  case $fieldChild->nodeName == 'KEY': $keys[] = $tableChild->getAttribute('name'); break;
159  case $fieldChild->nodeName == 'DEFAULT':
160  if ($allowDefault) {
161  $value = $fieldChild->getAttribute('VALUE');
162  if (is_string($value) && ctype_digit($value)) $value = (int) $value;
163  echo "->default(" . var_export($value, true) . ")";
164  }
165  break;
166  case $fieldChild->nodeName == 'descr': echo "->comment(" . var_export($fieldChild->nodeValue, true) . ")"; break;
167  case $fieldChild->nodeType == XML_TEXT_NODE:
168  if (trim($fieldChild->nodeValue) !== '') throw new Exception('Unexpected content in field node!');
169  break;
170  default: throw new Exception('Unhandled child node (type ' . $fieldChild->nodeType . ') to column ' . $tableChild->getAttribute('name') . ' of table ' . $tableNode->getAttribute('name'));
171  }
172  if ($autoIncrement) echo "->autoIncrement()";
173  if ($nullable && !in_array($tableChild->getAttribute('name'), $keys)) echo "->nullable()";
174  echo ";\n";
175  break;
176  case $tableChild->nodeName == 'index':
177  if (!$tableChild->hasAttribute('name')) throw new Exception('Unnamed index on table ' . $tableNode->getAttribute('name'));
178  $indexType = 'index';
179  $columns = [];
180  foreach ($tableChild->childNodes as $indexChild) switch (true) {
181  case $indexChild->nodeType == XML_COMMENT_NODE:
182  echo "\t\t\t// " . $indexChild->nodeValue . "\n";
183  break;
184  case $indexChild->nodeName == 'UNIQUE': $indexType = 'unique'; break;
185  case $indexChild->nodeName == 'col': $columns[] = trim($indexChild->nodeValue);
186  break;
187  default: throw new Exception('Unhandled index node child ' . $indexChild->nodeName);
188  }
189  if (empty($columns)) throw new Exception('Empty column list for index on table ' . $tableNode->getAttribute('name') . '))!');
190  echo "\t\t\t\$table->$indexType(['" . implode("', '", $columns) . "'], '" . $tableChild->getAttribute('name') . "');\n";
191  break;
192  case $tableChild->nodeName == 'descr': break; // Handled above.
193  break;
194  default: throw new Exception('Don\'t know how to handle this table child node (' . $tableChild->nodeName . '))!');
195  }
196  echo "\t\t});\n\n";
197  if (count($keys)>1 && $hasAutoIncrementNamed !== null) {
198  echo "\t\t// Work-around for compound primary key\n";
199  echo "\t\tswitch (Capsule::connection()->getDriverName()) {\n";
200  echo "\t\t\tcase 'mysql': Capsule::connection()->unprepared(\"ALTER TABLE " . $tableNode->getAttribute('name') . " DROP PRIMARY KEY, ADD PRIMARY KEY (" . implode(", ", $keys) . ")\"); break;\n";
201  echo "\t\t\tcase 'pgsql': Capsule::connection()->unprepared(\"ALTER TABLE " . $tableNode->getAttribute('name') . " DROP CONSTRAINT " . $tableNode->getAttribute('name') . "_pkey; ALTER TABLE " . $tableNode->getAttribute('name') . " ADD PRIMARY KEY (" . implode(", ", $keys) . ");\"); break;\n";
202  echo "\t\t}\n";
203  } elseif (count($keys)==1 && $hasAutoIncrementNamed !== null) {
204  // Handled well by autoIncrement
205  } elseif ($hasAutoIncrementNamed === null) {
206  // No autoincrement specified; we're OK without further consideration
207  } elseif (count($keys)==0) {
208  // No primary keys specified; we're OK without further consideration
209  } else {
210  throw new Exception('Not sure how to handle primary key setup for table ' . $tableNode->getAttribute('name') . '.');
211  }
212  }
213  echo "\t}\n}";
214  }
215 }
216 
217 $tool = new xmlSchemaToMigration(isset($argv) ? $argv : array());
218 $tool->execute();
219 
CommandLineTool
Initialization code for command-line scripts.
Definition: CliTool.inc.php:44
xmlSchemaToMigration\usage
usage()
Definition: xmlSchemaToMigration.php:55
$tool
$tool
Definition: mergeCoverageReportTool.php:120
xmlSchemaToMigration\execute
execute()
Definition: xmlSchemaToMigration.php:63
xmlSchemaToMigration\$className
$className
Definition: xmlSchemaToMigration.php:27
xmlSchemaToMigration\__construct
__construct($argv=array())
Definition: xmlSchemaToMigration.php:32
xmlSchemaToMigration\$source
$source
Definition: xmlSchemaToMigration.php:21
xmlSchemaToMigration
Definition: xmlSchemaToMigration.php:16
CommandLineTool\$argv
$argv
Definition: CliTool.inc.php:53