Parser2.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. <?php
  2. /**
  3. * PHP versions 4 and 5
  4. *
  5. * Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,
  6. * Stig. S. Bakken, Lukas Smith, Igor Feghali
  7. * All rights reserved.
  8. *
  9. * MDB2_Schema enables users to maintain RDBMS independant schema files
  10. * in XML that can be used to manipulate both data and database schemas
  11. * This LICENSE is in the BSD license style.
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions
  15. * are met:
  16. *
  17. * Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * Redistributions in binary form must reproduce the above copyright
  21. * notice, this list of conditions and the following disclaimer in the
  22. * documentation and/or other materials provided with the distribution.
  23. *
  24. * Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,
  25. * Lukas Smith, Igor Feghali nor the names of his contributors may be
  26. * used to endorse or promote products derived from this software
  27. * without specific prior written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  30. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  31. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  32. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  33. * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  34. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  35. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  36. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  37. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  38. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
  39. * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  40. * POSSIBILITY OF SUCH DAMAGE.
  41. *
  42. * Author: Igor Feghali <ifeghali@php.net>
  43. *
  44. * @category Database
  45. * @package MDB2_Schema
  46. * @author Igor Feghali <ifeghali@php.net>
  47. * @license BSD http://www.opensource.org/licenses/bsd-license.php
  48. * @version CVS: $Id: Parser2.php,v 1.12 2008/11/30 03:34:00 clockwerx Exp $
  49. * @link http://pear.php.net/packages/MDB2_Schema
  50. */
  51. require_once 'XML/Unserializer.php';
  52. require_once 'MDB2/Schema/Validate.php';
  53. /**
  54. * Parses an XML schema file
  55. *
  56. * @category Database
  57. * @package MDB2_Schema
  58. * @author Lukas Smith <smith@pooteeweet.org>
  59. * @author Igor Feghali <ifeghali@php.net>
  60. * @license BSD http://www.opensource.org/licenses/bsd-license.php
  61. * @link http://pear.php.net/packages/MDB2_Schema
  62. */
  63. class MDB2_Schema_Parser2 extends XML_Unserializer
  64. {
  65. var $database_definition = array();
  66. var $database_loaded = array();
  67. var $variables = array();
  68. var $error;
  69. var $structure = false;
  70. var $val;
  71. var $options = array();
  72. var $table = array();
  73. var $table_name = '';
  74. var $field = array();
  75. var $field_name = '';
  76. var $index = array();
  77. var $index_name = '';
  78. var $constraint = array();
  79. var $constraint_name = '';
  80. var $sequence = array();
  81. var $sequence_name = '';
  82. var $init = array();
  83. function __construct($variables, $fail_on_invalid_names = true, $structure = false, $valid_types = array(), $force_defaults = true)
  84. {
  85. // force ISO-8859-1 due to different defaults for PHP4 and PHP5
  86. // todo: this probably needs to be investigated some more and cleaned up
  87. $this->options['encoding'] = 'ISO-8859-1';
  88. $this->options['XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE'] = true;
  89. $this->options['XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY'] = false;
  90. $this->options['forceEnum'] = array('table', 'field', 'index', 'foreign', 'insert', 'update', 'delete', 'sequence');
  91. /*
  92. * todo: find a way to force the following items not to be parsed as arrays
  93. * as it cause problems in functions with multiple arguments
  94. */
  95. //$this->options['forceNEnum'] = array('value', 'column');
  96. $this->variables = $variables;
  97. $this->structure = $structure;
  98. $this->val =& new MDB2_Schema_Validate($fail_on_invalid_names, $valid_types, $force_defaults);
  99. parent::XML_Unserializer($this->options);
  100. }
  101. function MDB2_Schema_Parser2($variables, $fail_on_invalid_names = true, $structure = false, $valid_types = array(), $force_defaults = true)
  102. {
  103. $this->__construct($variables, $fail_on_invalid_names, $structure, $valid_types, $force_defaults);
  104. }
  105. function parse()
  106. {
  107. $result = $this->unserialize($this->filename, true);
  108. if (PEAR::isError($result)) {
  109. return $result;
  110. } else {
  111. $this->database_loaded = $this->getUnserializedData();
  112. return $this->fixDatabaseKeys($this->database_loaded);
  113. }
  114. }
  115. function setInputFile($filename)
  116. {
  117. $this->filename = $filename;
  118. return MDB2_OK;
  119. }
  120. function renameKey(&$arr, $oKey, $nKey)
  121. {
  122. $arr[$nKey] = &$arr[$oKey];
  123. unset($arr[$oKey]);
  124. }
  125. function fixDatabaseKeys($database)
  126. {
  127. $this->database_definition = array(
  128. 'name' => '',
  129. 'create' => '',
  130. 'overwrite' => '',
  131. 'charset' => '',
  132. 'description' => '',
  133. 'comments' => '',
  134. 'tables' => array(),
  135. 'sequences' => array()
  136. );
  137. if (!empty($database['name'])) {
  138. $this->database_definition['name'] = $database['name'];
  139. }
  140. if (!empty($database['create'])) {
  141. $this->database_definition['create'] = $database['create'];
  142. }
  143. if (!empty($database['overwrite'])) {
  144. $this->database_definition['overwrite'] = $database['overwrite'];
  145. }
  146. if (!empty($database['charset'])) {
  147. $this->database_definition['charset'] = $database['charset'];
  148. }
  149. if (!empty($database['description'])) {
  150. $this->database_definition['description'] = $database['description'];
  151. }
  152. if (!empty($database['comments'])) {
  153. $this->database_definition['comments'] = $database['comments'];
  154. }
  155. if (!empty($database['table']) && is_array($database['table'])) {
  156. foreach ($database['table'] as $table) {
  157. $this->fixTableKeys($table);
  158. }
  159. }
  160. if (!empty($database['sequence']) && is_array($database['sequence'])) {
  161. foreach ($database['sequence'] as $sequence) {
  162. $this->fixSequenceKeys($sequence);
  163. }
  164. }
  165. $result = $this->val->validateDatabase($this->database_definition);
  166. if (PEAR::isError($result)) {
  167. return $this->raiseError($result->getUserinfo());
  168. }
  169. return MDB2_OK;
  170. }
  171. function fixTableKeys($table)
  172. {
  173. $this->table = array(
  174. 'was' => '',
  175. 'description' => '',
  176. 'comments' => '',
  177. 'fields' => array(),
  178. 'indexes' => array(),
  179. 'constraints' => array(),
  180. 'initialization' => array()
  181. );
  182. if (!empty($table['name'])) {
  183. $this->table_name = $table['name'];
  184. } else {
  185. $this->table_name = '';
  186. }
  187. if (!empty($table['was'])) {
  188. $this->table['was'] = $table['was'];
  189. }
  190. if (!empty($table['description'])) {
  191. $this->table['description'] = $table['description'];
  192. }
  193. if (!empty($table['comments'])) {
  194. $this->table['comments'] = $table['comments'];
  195. }
  196. if (!empty($table['declaration']) && is_array($table['declaration'])) {
  197. if (!empty($table['declaration']['field']) && is_array($table['declaration']['field'])) {
  198. foreach ($table['declaration']['field'] as $field) {
  199. $this->fixTableFieldKeys($field);
  200. }
  201. }
  202. if (!empty($table['declaration']['index']) && is_array($table['declaration']['index'])) {
  203. foreach ($table['declaration']['index'] as $index) {
  204. $this->fixTableIndexKeys($index);
  205. }
  206. }
  207. if (!empty($table['declaration']['foreign']) && is_array($table['declaration']['foreign'])) {
  208. foreach ($table['declaration']['foreign'] as $constraint) {
  209. $this->fixTableConstraintKeys($constraint);
  210. }
  211. }
  212. }
  213. if (!empty($table['initialization']) && is_array($table['initialization'])) {
  214. if (!empty($table['initialization']['insert']) && is_array($table['initialization']['insert'])) {
  215. foreach ($table['initialization']['insert'] as $init) {
  216. $this->fixTableInitializationKeys($init, 'insert');
  217. }
  218. }
  219. if (!empty($table['initialization']['update']) && is_array($table['initialization']['update'])) {
  220. foreach ($table['initialization']['update'] as $init) {
  221. $this->fixTableInitializationKeys($init, 'update');
  222. }
  223. }
  224. if (!empty($table['initialization']['delete']) && is_array($table['initialization']['delete'])) {
  225. foreach ($table['initialization']['delete'] as $init) {
  226. $this->fixTableInitializationKeys($init, 'delete');
  227. }
  228. }
  229. }
  230. $result = $this->val->validateTable($this->database_definition['tables'], $this->table, $this->table_name);
  231. if (PEAR::isError($result)) {
  232. return $this->raiseError($result->getUserinfo());
  233. } else {
  234. $this->database_definition['tables'][$this->table_name] = $this->table;
  235. }
  236. return MDB2_OK;
  237. }
  238. function fixTableFieldKeys($field)
  239. {
  240. $this->field = array();
  241. if (!empty($field['name'])) {
  242. $this->field_name = $field['name'];
  243. } else {
  244. $this->field_name = '';
  245. }
  246. if (!empty($field['was'])) {
  247. $this->field['was'] = $field['was'];
  248. }
  249. if (!empty($field['type'])) {
  250. $this->field['type'] = $field['type'];
  251. }
  252. if (!empty($field['fixed'])) {
  253. $this->field['fixed'] = $field['fixed'];
  254. }
  255. if (isset($field['default'])) {
  256. $this->field['default'] = $field['default'];
  257. }
  258. if (!empty($field['notnull'])) {
  259. $this->field['notnull'] = $field['notnull'];
  260. }
  261. if (!empty($field['autoincrement'])) {
  262. $this->field['autoincrement'] = $field['autoincrement'];
  263. }
  264. if (!empty($field['unsigned'])) {
  265. $this->field['unsigned'] = $field['unsigned'];
  266. }
  267. if (!empty($field['length'])) {
  268. $this->field['length'] = $field['length'];
  269. }
  270. if (!empty($field['description'])) {
  271. $this->field['description'] = $field['description'];
  272. }
  273. if (!empty($field['comments'])) {
  274. $this->field['comments'] = $field['comments'];
  275. }
  276. $result = $this->val->validateField($this->table['fields'], $this->field, $this->field_name);
  277. if (PEAR::isError($result)) {
  278. return $this->raiseError($result->getUserinfo());
  279. } else {
  280. $this->table['fields'][$this->field_name] = $this->field;
  281. }
  282. return MDB2_OK;
  283. }
  284. function fixTableIndexKeys($index)
  285. {
  286. $this->index = array(
  287. 'was' => '',
  288. 'unique' =>'',
  289. 'primary' => '',
  290. 'fields' => array()
  291. );
  292. if (!empty($index['name'])) {
  293. $this->index_name = $index['name'];
  294. } else {
  295. $this->index_name = '';
  296. }
  297. if (!empty($index['was'])) {
  298. $this->index['was'] = $index['was'];
  299. }
  300. if (!empty($index['unique'])) {
  301. $this->index['unique'] = $index['unique'];
  302. }
  303. if (!empty($index['primary'])) {
  304. $this->index['primary'] = $index['primary'];
  305. }
  306. if (!empty($index['field'])) {
  307. foreach ($index['field'] as $field) {
  308. if (!empty($field['name'])) {
  309. $this->field_name = $field['name'];
  310. } else {
  311. $this->field_name = '';
  312. }
  313. $this->field = array(
  314. 'sorting' => '',
  315. 'length' => ''
  316. );
  317. if (!empty($field['sorting'])) {
  318. $this->field['sorting'] = $field['sorting'];
  319. }
  320. if (!empty($field['length'])) {
  321. $this->field['length'] = $field['length'];
  322. }
  323. $result = $this->val->validateIndexField($this->index['fields'], $this->field, $this->field_name);
  324. if (PEAR::isError($result)) {
  325. return $this->raiseError($result->getUserinfo());
  326. }
  327. $this->index['fields'][$this->field_name] = $this->field;
  328. }
  329. }
  330. $result = $this->val->validateIndex($this->table['indexes'], $this->index, $this->index_name);
  331. if (PEAR::isError($result)) {
  332. return $this->raiseError($result->getUserinfo());
  333. } else {
  334. $this->table['indexes'][$this->index_name] = $this->index;
  335. }
  336. return MDB2_OK;
  337. }
  338. function fixTableConstraintKeys($constraint)
  339. {
  340. $this->constraint = array(
  341. 'was' => '',
  342. 'match' => '',
  343. 'ondelete' => '',
  344. 'onupdate' => '',
  345. 'deferrable' => '',
  346. 'initiallydeferred' => '',
  347. 'foreign' => true,
  348. 'fields' => array(),
  349. 'references' => array('table' => '', 'fields' => array())
  350. );
  351. if (!empty($constraint['name'])) {
  352. $this->constraint_name = $constraint['name'];
  353. } else {
  354. $this->constraint_name = '';
  355. }
  356. if (!empty($constraint['was'])) {
  357. $this->constraint['was'] = $constraint['was'];
  358. }
  359. if (!empty($constraint['match'])) {
  360. $this->constraint['match'] = $constraint['match'];
  361. }
  362. if (!empty($constraint['ondelete'])) {
  363. $this->constraint['ondelete'] = $constraint['ondelete'];
  364. }
  365. if (!empty($constraint['onupdate'])) {
  366. $this->constraint['onupdate'] = $constraint['onupdate'];
  367. }
  368. if (!empty($constraint['deferrable'])) {
  369. $this->constraint['deferrable'] = $constraint['deferrable'];
  370. }
  371. if (!empty($constraint['initiallydeferred'])) {
  372. $this->constraint['initiallydeferred'] = $constraint['initiallydeferred'];
  373. }
  374. if (!empty($constraint['field']) && is_array($constraint['field'])) {
  375. foreach ($constraint['field'] as $field) {
  376. $result = $this->val->validateConstraintField($this->constraint['fields'], $field);
  377. if (PEAR::isError($result)) {
  378. return $this->raiseError($result->getUserinfo());
  379. }
  380. $this->constraint['fields'][$field] = '';
  381. }
  382. }
  383. if (!empty($constraint['references']) && is_array($constraint['references'])) {
  384. /**
  385. * As we forced 'table' to be enumerated
  386. * we have to fix it on the foreign-references-table context
  387. */
  388. if (!empty($constraint['references']['table']) && is_array($constraint['references']['table'])) {
  389. $this->constraint['references']['table'] = $constraint['references']['table'][0];
  390. }
  391. if (!empty($constraint['references']['field']) && is_array($constraint['references']['field'])) {
  392. foreach ($constraint['references']['field'] as $field) {
  393. $result = $this->val->validateConstraintReferencedField($this->constraint['references']['fields'], $field);
  394. if (PEAR::isError($result)) {
  395. return $this->raiseError($result->getUserinfo());
  396. }
  397. $this->constraint['references']['fields'][$field] = '';
  398. }
  399. }
  400. }
  401. $result = $this->val->validateConstraint($this->table['constraints'], $this->constraint, $this->constraint_name);
  402. if (PEAR::isError($result)) {
  403. return $this->raiseError($result->getUserinfo());
  404. } else {
  405. $this->table['constraints'][$this->constraint_name] = $this->constraint;
  406. }
  407. return MDB2_OK;
  408. }
  409. function fixTableInitializationKeys($element, $type = '')
  410. {
  411. if (!empty($element['select']) && is_array($element['select'])) {
  412. $this->fixTableInitializationDataKeys($element['select']);
  413. $this->init = array( 'select' => $this->init );
  414. } else {
  415. $this->fixTableInitializationDataKeys($element);
  416. }
  417. $this->table['initialization'][] = array( 'type' => $type, 'data' => $this->init );
  418. }
  419. function fixTableInitializationDataKeys($element)
  420. {
  421. $this->init = array();
  422. if (!empty($element['field']) && is_array($element['field'])) {
  423. foreach ($element['field'] as $field) {
  424. $name = $field['name'];
  425. unset($field['name']);
  426. $this->setExpression($field);
  427. $this->init['field'][] = array( 'name' => $name, 'group' => $field );
  428. }
  429. }
  430. /**
  431. * As we forced 'table' to be enumerated
  432. * we have to fix it on the insert-select context
  433. */
  434. if (!empty($element['table']) && is_array($element['table'])) {
  435. $this->init['table'] = $element['table'][0];
  436. }
  437. if (!empty($element['where']) && is_array($element['where'])) {
  438. $this->init['where'] = $element['where'];
  439. $this->setExpression($this->init['where']);
  440. }
  441. }
  442. function setExpression(&$arr)
  443. {
  444. $element = each($arr);
  445. $arr = array( 'type' => $element['key'] );
  446. $element = $element['value'];
  447. switch ($arr['type']) {
  448. case 'null':
  449. break;
  450. case 'value':
  451. case 'column':
  452. $arr['data'] = $element;
  453. break;
  454. case 'function':
  455. if (!empty($element)
  456. && is_array($element)
  457. ) {
  458. $arr['data'] = array( 'name' => $element['name'] );
  459. unset($element['name']);
  460. foreach ($element as $type => $value) {
  461. if (!empty($value)) {
  462. if (is_array($value)) {
  463. foreach ($value as $argument) {
  464. $argument = array( $type => $argument );
  465. $this->setExpression($argument);
  466. $arr['data']['arguments'][] = $argument;
  467. }
  468. } else {
  469. $arr['data']['arguments'][] = array( 'type' => $type, 'data' => $value );
  470. }
  471. }
  472. }
  473. }
  474. break;
  475. case 'expression':
  476. $arr['data'] = array( 'operants' => array(), 'operator' => $element['operator'] );
  477. unset($element['operator']);
  478. foreach ($element as $k => $v) {
  479. $argument = array( $k => $v );
  480. $this->setExpression($argument);
  481. $arr['data']['operants'][] = $argument;
  482. }
  483. break;
  484. }
  485. }
  486. function fixSequenceKeys($sequence)
  487. {
  488. $this->sequence = array(
  489. 'was' => '',
  490. 'start' => '',
  491. 'description' => '',
  492. 'comments' => '',
  493. 'on' => array('table' => '', 'field' => '')
  494. );
  495. if (!empty($sequence['name'])) {
  496. $this->sequence_name = $sequence['name'];
  497. } else {
  498. $this->sequence_name = '';
  499. }
  500. if (!empty($sequence['was'])) {
  501. $this->sequence['was'] = $sequence['was'];
  502. }
  503. if (!empty($sequence['start'])) {
  504. $this->sequence['start'] = $sequence['start'];
  505. }
  506. if (!empty($sequence['description'])) {
  507. $this->sequence['description'] = $sequence['description'];
  508. }
  509. if (!empty($sequence['comments'])) {
  510. $this->sequence['comments'] = $sequence['comments'];
  511. }
  512. if (!empty($sequence['on']) && is_array($sequence['on'])) {
  513. /**
  514. * As we forced 'table' to be enumerated
  515. * we have to fix it on the sequence-on-table context
  516. */
  517. if (!empty($sequence['on']['table']) && is_array($sequence['on']['table'])) {
  518. $this->sequence['on']['table'] = $sequence['on']['table'][0];
  519. }
  520. /**
  521. * As we forced 'field' to be enumerated
  522. * we have to fix it on the sequence-on-field context
  523. */
  524. if (!empty($sequence['on']['field']) && is_array($sequence['on']['field'])) {
  525. $this->sequence['on']['field'] = $sequence['on']['field'][0];
  526. }
  527. }
  528. $result = $this->val->validateSequence($this->database_definition['sequences'], $this->sequence, $this->sequence_name);
  529. if (PEAR::isError($result)) {
  530. return $this->raiseError($result->getUserinfo());
  531. } else {
  532. $this->database_definition['sequences'][$this->sequence_name] = $this->sequence;
  533. }
  534. return MDB2_OK;
  535. }
  536. function &raiseError($msg = null, $ecode = MDB2_SCHEMA_ERROR_PARSE)
  537. {
  538. if (is_null($this->error)) {
  539. $error = 'Parser error: '.$msg."\n";
  540. $this->error =& MDB2_Schema::raiseError($ecode, null, null, $error);
  541. }
  542. return $this->error;
  543. }
  544. }
  545. ?>