PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/concreteOLD/libraries/3rdparty/adodb/adodb-xmlschema.inc.php

https://bitbucket.org/selfeky/xclusivescardwebsite
PHP | 2223 lines | 1035 code | 311 blank | 877 comment | 139 complexity | 9b4a329d356512abfab2a80848e24a85 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. // Copyright (c) 2004 ars Cognita Inc., all rights reserved
  3. /* ******************************************************************************
  4. Released under both BSD license and Lesser GPL library license.
  5. Whenever there is any discrepancy between the two licenses,
  6. the BSD license will take precedence.
  7. *******************************************************************************/
  8. /**
  9. * xmlschema is a class that allows the user to quickly and easily
  10. * build a database on any ADOdb-supported platform using a simple
  11. * XML schema.
  12. *
  13. * Last Editor: $Author: jlim $
  14. * @author Richard Tango-Lowy & Dan Cech
  15. * @version $Revision: 1.12 $
  16. *
  17. * @package axmls
  18. * @tutorial getting_started.pkg
  19. */
  20. function _file_get_contents($file)
  21. {
  22. if (function_exists('file_get_contents')) return file_get_contents($file);
  23. $f = fopen($file,'r');
  24. if (!$f) return '';
  25. $t = '';
  26. while ($s = fread($f,100000)) $t .= $s;
  27. fclose($f);
  28. return $t;
  29. }
  30. /**
  31. * Debug on or off
  32. */
  33. if( !defined( 'XMLS_DEBUG' ) ) {
  34. define( 'XMLS_DEBUG', FALSE );
  35. }
  36. /**
  37. * Default prefix key
  38. */
  39. if( !defined( 'XMLS_PREFIX' ) ) {
  40. define( 'XMLS_PREFIX', '%%P' );
  41. }
  42. /**
  43. * Maximum length allowed for object prefix
  44. */
  45. if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {
  46. define( 'XMLS_PREFIX_MAXLEN', 10 );
  47. }
  48. /**
  49. * Execute SQL inline as it is generated
  50. */
  51. if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {
  52. define( 'XMLS_EXECUTE_INLINE', FALSE );
  53. }
  54. /**
  55. * Continue SQL Execution if an error occurs?
  56. */
  57. if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {
  58. define( 'XMLS_CONTINUE_ON_ERROR', FALSE );
  59. }
  60. /**
  61. * Current Schema Version
  62. */
  63. if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {
  64. define( 'XMLS_SCHEMA_VERSION', '0.2' );
  65. }
  66. /**
  67. * Default Schema Version. Used for Schemas without an explicit version set.
  68. */
  69. if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {
  70. define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );
  71. }
  72. /**
  73. * Default Schema Version. Used for Schemas without an explicit version set.
  74. */
  75. if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {
  76. define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );
  77. }
  78. /**
  79. * Include the main ADODB library
  80. */
  81. if( !defined( '_ADODB_LAYER' ) ) {
  82. require( 'adodb.inc.php' );
  83. require( 'adodb-datadict.inc.php' );
  84. }
  85. /**
  86. * Abstract DB Object. This class provides basic methods for database objects, such
  87. * as tables and indexes.
  88. *
  89. * @package axmls
  90. * @access private
  91. */
  92. class dbObject {
  93. /**
  94. * var object Parent
  95. */
  96. var $parent;
  97. /**
  98. * var string current element
  99. */
  100. var $currentElement;
  101. /**
  102. * NOP
  103. */
  104. function dbObject( &$parent, $attributes = NULL ) {
  105. $this->parent = $parent;
  106. }
  107. /**
  108. * XML Callback to process start elements
  109. *
  110. * @access private
  111. */
  112. function _tag_open( &$parser, $tag, $attributes ) {
  113. }
  114. /**
  115. * XML Callback to process CDATA elements
  116. *
  117. * @access private
  118. */
  119. function _tag_cdata( &$parser, $cdata ) {
  120. }
  121. /**
  122. * XML Callback to process end elements
  123. *
  124. * @access private
  125. */
  126. function _tag_close( &$parser, $tag ) {
  127. }
  128. function create() {
  129. return array();
  130. }
  131. /**
  132. * Destroys the object
  133. */
  134. function destroy() {
  135. unset( $this );
  136. }
  137. /**
  138. * Checks whether the specified RDBMS is supported by the current
  139. * database object or its ranking ancestor.
  140. *
  141. * @param string $platform RDBMS platform name (from ADODB platform list).
  142. * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.
  143. */
  144. function supportedPlatform( $platform = NULL ) {
  145. return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;
  146. }
  147. /**
  148. * Returns the prefix set by the ranking ancestor of the database object.
  149. *
  150. * @param string $name Prefix string.
  151. * @return string Prefix.
  152. */
  153. function prefix( $name = '' ) {
  154. return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;
  155. }
  156. /**
  157. * Extracts a field ID from the specified field.
  158. *
  159. * @param string $field Field.
  160. * @return string Field ID.
  161. */
  162. function FieldID( $field ) {
  163. return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );
  164. }
  165. }
  166. /**
  167. * Creates a table object in ADOdb's datadict format
  168. *
  169. * This class stores information about a database table. As charactaristics
  170. * of the table are loaded from the external source, methods and properties
  171. * of this class are used to build up the table description in ADOdb's
  172. * datadict format.
  173. *
  174. * @package axmls
  175. * @access private
  176. */
  177. class dbTable extends dbObject {
  178. /**
  179. * @var string Table name
  180. */
  181. var $name;
  182. /**
  183. * @var array Field specifier: Meta-information about each field
  184. */
  185. var $fields = array();
  186. /**
  187. * @var array List of table indexes.
  188. */
  189. var $indexes = array();
  190. /**
  191. * @var array Table options: Table-level options
  192. */
  193. var $opts = array();
  194. /**
  195. * @var string Field index: Keeps track of which field is currently being processed
  196. */
  197. var $current_field;
  198. /**
  199. * @var boolean Mark table for destruction
  200. * @access private
  201. */
  202. var $drop_table;
  203. /**
  204. * @var boolean Mark field for destruction (not yet implemented)
  205. * @access private
  206. */
  207. var $drop_field = array();
  208. /**
  209. * Iniitializes a new table object.
  210. *
  211. * @param string $prefix DB Object prefix
  212. * @param array $attributes Array of table attributes.
  213. */
  214. function dbTable( &$parent, $attributes = NULL ) {
  215. $this->parent = $parent;
  216. $this->name = $this->prefix($attributes['NAME']);
  217. }
  218. /**
  219. * XML Callback to process start elements. Elements currently
  220. * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT.
  221. *
  222. * @access private
  223. */
  224. function _tag_open( &$parser, $tag, $attributes ) {
  225. $this->currentElement = strtoupper( $tag );
  226. switch( $this->currentElement ) {
  227. case 'INDEX':
  228. if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
  229. xml_set_object( $parser, $this->addIndex( $attributes ) );
  230. }
  231. break;
  232. case 'DATA':
  233. if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
  234. xml_set_object( $parser, $this->addData( $attributes ) );
  235. }
  236. break;
  237. case 'DROP':
  238. $this->drop();
  239. break;
  240. case 'FIELD':
  241. // Add a field
  242. $fieldName = $attributes['NAME'];
  243. $fieldType = $attributes['TYPE'];
  244. $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;
  245. $fieldOpts = isset( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;
  246. $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );
  247. break;
  248. case 'KEY':
  249. case 'NOTNULL':
  250. case 'AUTOINCREMENT':
  251. // Add a field option
  252. $this->addFieldOpt( $this->current_field, $this->currentElement );
  253. break;
  254. case 'DEFAULT':
  255. // Add a field option to the table object
  256. // Work around ADOdb datadict issue that misinterprets empty strings.
  257. if( $attributes['VALUE'] == '' ) {
  258. $attributes['VALUE'] = " '' ";
  259. }
  260. $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );
  261. break;
  262. case 'DEFDATE':
  263. case 'DEFTIMESTAMP':
  264. // Add a field option to the table object
  265. $this->addFieldOpt( $this->current_field, $this->currentElement );
  266. break;
  267. default:
  268. // print_r( array( $tag, $attributes ) );
  269. }
  270. }
  271. /**
  272. * XML Callback to process CDATA elements
  273. *
  274. * @access private
  275. */
  276. function _tag_cdata( &$parser, $cdata ) {
  277. switch( $this->currentElement ) {
  278. // Table constraint
  279. case 'CONSTRAINT':
  280. if( isset( $this->current_field ) ) {
  281. $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );
  282. } else {
  283. $this->addTableOpt( $cdata );
  284. }
  285. break;
  286. // Table option
  287. case 'OPT':
  288. $this->addTableOpt( $cdata );
  289. break;
  290. default:
  291. }
  292. }
  293. /**
  294. * XML Callback to process end elements
  295. *
  296. * @access private
  297. */
  298. function _tag_close( &$parser, $tag ) {
  299. $this->currentElement = '';
  300. switch( strtoupper( $tag ) ) {
  301. case 'TABLE':
  302. $this->parent->addSQL( $this->create( $this->parent ) );
  303. xml_set_object( $parser, $this->parent );
  304. $this->destroy();
  305. break;
  306. case 'FIELD':
  307. unset($this->current_field);
  308. break;
  309. }
  310. }
  311. /**
  312. * Adds an index to a table object
  313. *
  314. * @param array $attributes Index attributes
  315. * @return object dbIndex object
  316. */
  317. function addIndex( $attributes ) {
  318. $name = strtoupper( $attributes['NAME'] );
  319. $this->indexes[$name] = new dbIndex( $this, $attributes );
  320. return $this->indexes[$name];
  321. }
  322. /**
  323. * Adds data to a table object
  324. *
  325. * @param array $attributes Data attributes
  326. * @return object dbData object
  327. */
  328. function addData( $attributes ) {
  329. if( !isset( $this->data ) ) {
  330. $this->data = new dbData( $this, $attributes );
  331. }
  332. return $this->data;
  333. }
  334. /**
  335. * Adds a field to a table object
  336. *
  337. * $name is the name of the table to which the field should be added.
  338. * $type is an ADODB datadict field type. The following field types
  339. * are supported as of ADODB 3.40:
  340. * - C: varchar
  341. * - X: CLOB (character large object) or largest varchar size
  342. * if CLOB is not supported
  343. * - C2: Multibyte varchar
  344. * - X2: Multibyte CLOB
  345. * - B: BLOB (binary large object)
  346. * - D: Date (some databases do not support this, and we return a datetime type)
  347. * - T: Datetime or Timestamp
  348. * - L: Integer field suitable for storing booleans (0 or 1)
  349. * - I: Integer (mapped to I4)
  350. * - I1: 1-byte integer
  351. * - I2: 2-byte integer
  352. * - I4: 4-byte integer
  353. * - I8: 8-byte integer
  354. * - F: Floating point number
  355. * - N: Numeric or decimal number
  356. *
  357. * @param string $name Name of the table to which the field will be added.
  358. * @param string $type ADODB datadict field type.
  359. * @param string $size Field size
  360. * @param array $opts Field options array
  361. * @return array Field specifier array
  362. */
  363. function addField( $name, $type, $size = NULL, $opts = NULL ) {
  364. $field_id = $this->FieldID( $name );
  365. // Set the field index so we know where we are
  366. $this->current_field = $field_id;
  367. // Set the field name (required)
  368. $this->fields[$field_id]['NAME'] = $name;
  369. // Set the field type (required)
  370. $this->fields[$field_id]['TYPE'] = $type;
  371. // Set the field size (optional)
  372. if( isset( $size ) ) {
  373. $this->fields[$field_id]['SIZE'] = $size;
  374. }
  375. // Set the field options
  376. if( isset( $opts ) ) {
  377. $this->fields[$field_id]['OPTS'][] = $opts;
  378. }
  379. }
  380. /**
  381. * Adds a field option to the current field specifier
  382. *
  383. * This method adds a field option allowed by the ADOdb datadict
  384. * and appends it to the given field.
  385. *
  386. * @param string $field Field name
  387. * @param string $opt ADOdb field option
  388. * @param mixed $value Field option value
  389. * @return array Field specifier array
  390. */
  391. function addFieldOpt( $field, $opt, $value = NULL ) {
  392. if( !isset( $value ) ) {
  393. $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;
  394. // Add the option and value
  395. } else {
  396. $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );
  397. }
  398. }
  399. /**
  400. * Adds an option to the table
  401. *
  402. * This method takes a comma-separated list of table-level options
  403. * and appends them to the table object.
  404. *
  405. * @param string $opt Table option
  406. * @return array Options
  407. */
  408. function addTableOpt( $opt ) {
  409. if(isset($this->currentPlatform)) {
  410. $this->opts[$this->parent->db->databaseType] = $opt;
  411. }
  412. return $this->opts;
  413. }
  414. /**
  415. * Generates the SQL that will create the table in the database
  416. *
  417. * @param object $xmls adoSchema object
  418. * @return array Array containing table creation SQL
  419. */
  420. function create( &$xmls ) {
  421. $sql = array();
  422. // drop any existing indexes
  423. if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
  424. foreach( $legacy_indexes as $index => $index_details ) {
  425. $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
  426. }
  427. }
  428. // remove fields to be dropped from table object
  429. foreach( $this->drop_field as $field ) {
  430. unset( $this->fields[$field] );
  431. }
  432. // if table exists
  433. if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
  434. // drop table
  435. if( $this->drop_table ) {
  436. $sql[] = $xmls->dict->DropTableSQL( $this->name );
  437. return $sql;
  438. }
  439. // drop any existing fields not in schema
  440. foreach( $legacy_fields as $field_id => $field ) {
  441. if( !isset( $this->fields[$field_id] ) ) {
  442. $sql[] = $xmls->dict->DropColumnSQL( $this->name, '`'.$field->name.'`' );
  443. }
  444. }
  445. // if table doesn't exist
  446. } else {
  447. if( $this->drop_table ) {
  448. return $sql;
  449. }
  450. $legacy_fields = array();
  451. }
  452. // Loop through the field specifier array, building the associative array for the field options
  453. $fldarray = array();
  454. foreach( $this->fields as $field_id => $finfo ) {
  455. // Set an empty size if it isn't supplied
  456. if( !isset( $finfo['SIZE'] ) ) {
  457. $finfo['SIZE'] = '';
  458. }
  459. // Initialize the field array with the type and size
  460. $fldarray[$field_id] = array(
  461. 'NAME' => $finfo['NAME'],
  462. 'TYPE' => $finfo['TYPE'],
  463. 'SIZE' => $finfo['SIZE']
  464. );
  465. // Loop through the options array and add the field options.
  466. if( isset( $finfo['OPTS'] ) ) {
  467. foreach( $finfo['OPTS'] as $opt ) {
  468. // Option has an argument.
  469. if( is_array( $opt ) ) {
  470. $key = key( $opt );
  471. $value = $opt[key( $opt )];
  472. @$fldarray[$field_id][$key] .= $value;
  473. // Option doesn't have arguments
  474. } else {
  475. $fldarray[$field_id][$opt] = $opt;
  476. }
  477. }
  478. }
  479. }
  480. if( empty( $legacy_fields ) ) {
  481. // Create the new table
  482. $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
  483. logMsg( end( $sql ), 'Generated CreateTableSQL' );
  484. } else {
  485. // Upgrade an existing table
  486. logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
  487. switch( $xmls->upgrade ) {
  488. // Use ChangeTableSQL
  489. case 'ALTER':
  490. logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
  491. $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );
  492. break;
  493. case 'REPLACE':
  494. logMsg( 'Doing upgrade REPLACE (testing)' );
  495. $sql[] = $xmls->dict->DropTableSQL( $this->name );
  496. $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
  497. break;
  498. // ignore table
  499. default:
  500. return array();
  501. }
  502. }
  503. foreach( $this->indexes as $index ) {
  504. $sql[] = $index->create( $xmls );
  505. }
  506. if( isset( $this->data ) ) {
  507. $sql[] = $this->data->create( $xmls );
  508. }
  509. return $sql;
  510. }
  511. /**
  512. * Marks a field or table for destruction
  513. */
  514. function drop() {
  515. if( isset( $this->current_field ) ) {
  516. // Drop the current field
  517. logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );
  518. // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );
  519. $this->drop_field[$this->current_field] = $this->current_field;
  520. } else {
  521. // Drop the current table
  522. logMsg( "Dropping table '{$this->name}'" );
  523. // $this->drop_table = $xmls->dict->DropTableSQL( $this->name );
  524. $this->drop_table = TRUE;
  525. }
  526. }
  527. }
  528. /**
  529. * Creates an index object in ADOdb's datadict format
  530. *
  531. * This class stores information about a database index. As charactaristics
  532. * of the index are loaded from the external source, methods and properties
  533. * of this class are used to build up the index description in ADOdb's
  534. * datadict format.
  535. *
  536. * @package axmls
  537. * @access private
  538. */
  539. class dbIndex extends dbObject {
  540. /**
  541. * @var string Index name
  542. */
  543. var $name;
  544. /**
  545. * @var array Index options: Index-level options
  546. */
  547. var $opts = array();
  548. /**
  549. * @var array Indexed fields: Table columns included in this index
  550. */
  551. var $columns = array();
  552. /**
  553. * @var boolean Mark index for destruction
  554. * @access private
  555. */
  556. var $drop = FALSE;
  557. /**
  558. * Initializes the new dbIndex object.
  559. *
  560. * @param object $parent Parent object
  561. * @param array $attributes Attributes
  562. *
  563. * @internal
  564. */
  565. function dbIndex( &$parent, $attributes = NULL ) {
  566. $this->parent = $parent;
  567. $this->name = $this->prefix ($attributes['NAME']);
  568. }
  569. /**
  570. * XML Callback to process start elements
  571. *
  572. * Processes XML opening tags.
  573. * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH.
  574. *
  575. * @access private
  576. */
  577. function _tag_open( &$parser, $tag, $attributes ) {
  578. $this->currentElement = strtoupper( $tag );
  579. switch( $this->currentElement ) {
  580. case 'DROP':
  581. $this->drop();
  582. break;
  583. case 'CLUSTERED':
  584. case 'BITMAP':
  585. case 'UNIQUE':
  586. case 'FULLTEXT':
  587. case 'HASH':
  588. // Add index Option
  589. $this->addIndexOpt( $this->currentElement );
  590. break;
  591. default:
  592. // print_r( array( $tag, $attributes ) );
  593. }
  594. }
  595. /**
  596. * XML Callback to process CDATA elements
  597. *
  598. * Processes XML cdata.
  599. *
  600. * @access private
  601. */
  602. function _tag_cdata( &$parser, $cdata ) {
  603. switch( $this->currentElement ) {
  604. // Index field name
  605. case 'COL':
  606. $this->addField( $cdata );
  607. break;
  608. default:
  609. }
  610. }
  611. /**
  612. * XML Callback to process end elements
  613. *
  614. * @access private
  615. */
  616. function _tag_close( &$parser, $tag ) {
  617. $this->currentElement = '';
  618. switch( strtoupper( $tag ) ) {
  619. case 'INDEX':
  620. xml_set_object( $parser, $this->parent );
  621. break;
  622. }
  623. }
  624. /**
  625. * Adds a field to the index
  626. *
  627. * @param string $name Field name
  628. * @return string Field list
  629. */
  630. function addField( $name ) {
  631. $this->columns[$this->FieldID( $name )] = $name;
  632. // Return the field list
  633. return $this->columns;
  634. }
  635. /**
  636. * Adds options to the index
  637. *
  638. * @param string $opt Comma-separated list of index options.
  639. * @return string Option list
  640. */
  641. function addIndexOpt( $opt ) {
  642. $this->opts[] = $opt;
  643. // Return the options list
  644. return $this->opts;
  645. }
  646. /**
  647. * Generates the SQL that will create the index in the database
  648. *
  649. * @param object $xmls adoSchema object
  650. * @return array Array containing index creation SQL
  651. */
  652. function create( &$xmls ) {
  653. if( $this->drop ) {
  654. return NULL;
  655. }
  656. // eliminate any columns that aren't in the table
  657. foreach( $this->columns as $id => $col ) {
  658. if( !isset( $this->parent->fields[$id] ) ) {
  659. unset( $this->columns[$id] );
  660. }
  661. }
  662. return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
  663. }
  664. /**
  665. * Marks an index for destruction
  666. */
  667. function drop() {
  668. $this->drop = TRUE;
  669. }
  670. }
  671. /**
  672. * Creates a data object in ADOdb's datadict format
  673. *
  674. * This class stores information about table data.
  675. *
  676. * @package axmls
  677. * @access private
  678. */
  679. class dbData extends dbObject {
  680. var $data = array();
  681. var $row;
  682. /**
  683. * Initializes the new dbIndex object.
  684. *
  685. * @param object $parent Parent object
  686. * @param array $attributes Attributes
  687. *
  688. * @internal
  689. */
  690. function dbData( &$parent, $attributes = NULL ) {
  691. $this->parent = $parent;
  692. }
  693. /**
  694. * XML Callback to process start elements
  695. *
  696. * Processes XML opening tags.
  697. * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH.
  698. *
  699. * @access private
  700. */
  701. function _tag_open( &$parser, $tag, $attributes ) {
  702. $this->currentElement = strtoupper( $tag );
  703. switch( $this->currentElement ) {
  704. case 'ROW':
  705. $this->row = count( $this->data );
  706. $this->data[$this->row] = array();
  707. break;
  708. case 'F':
  709. $this->addField($attributes);
  710. default:
  711. // print_r( array( $tag, $attributes ) );
  712. }
  713. }
  714. /**
  715. * XML Callback to process CDATA elements
  716. *
  717. * Processes XML cdata.
  718. *
  719. * @access private
  720. */
  721. function _tag_cdata( &$parser, $cdata ) {
  722. switch( $this->currentElement ) {
  723. // Index field name
  724. case 'F':
  725. $this->addData( $cdata );
  726. break;
  727. default:
  728. }
  729. }
  730. /**
  731. * XML Callback to process end elements
  732. *
  733. * @access private
  734. */
  735. function _tag_close( &$parser, $tag ) {
  736. $this->currentElement = '';
  737. switch( strtoupper( $tag ) ) {
  738. case 'DATA':
  739. xml_set_object( $parser, $this->parent );
  740. break;
  741. }
  742. }
  743. /**
  744. * Adds a field to the index
  745. *
  746. * @param string $name Field name
  747. * @return string Field list
  748. */
  749. function addField( $attributes ) {
  750. if( isset( $attributes['NAME'] ) ) {
  751. $name = $attributes['NAME'];
  752. } else {
  753. $name = count($this->data[$this->row]);
  754. }
  755. // Set the field index so we know where we are
  756. $this->current_field = $this->FieldID( $name );
  757. }
  758. /**
  759. * Adds options to the index
  760. *
  761. * @param string $opt Comma-separated list of index options.
  762. * @return string Option list
  763. */
  764. function addData( $cdata ) {
  765. if( !isset( $this->data[$this->row] ) ) {
  766. $this->data[$this->row] = array();
  767. }
  768. if( !isset( $this->data[$this->row][$this->current_field] ) ) {
  769. $this->data[$this->row][$this->current_field] = '';
  770. }
  771. $this->data[$this->row][$this->current_field] .= $cdata;
  772. }
  773. /**
  774. * Generates the SQL that will create the index in the database
  775. *
  776. * @param object $xmls adoSchema object
  777. * @return array Array containing index creation SQL
  778. */
  779. function create( &$xmls ) {
  780. $table = $xmls->dict->TableName($this->parent->name);
  781. $table_field_count = count($this->parent->fields);
  782. $sql = array();
  783. // eliminate any columns that aren't in the table
  784. foreach( $this->data as $row ) {
  785. $table_fields = $this->parent->fields;
  786. $fields = array();
  787. foreach( $row as $field_id => $field_data ) {
  788. if( !array_key_exists( $field_id, $table_fields ) ) {
  789. if( is_numeric( $field_id ) ) {
  790. $field_id = reset( array_keys( $table_fields ) );
  791. } else {
  792. continue;
  793. }
  794. }
  795. $name = $table_fields[$field_id]['NAME'];
  796. switch( $table_fields[$field_id]['TYPE'] ) {
  797. case 'C':
  798. case 'C2':
  799. case 'X':
  800. case 'X2':
  801. $fields[$name] = $xmls->db->qstr( $field_data );
  802. break;
  803. case 'I':
  804. case 'I1':
  805. case 'I2':
  806. case 'I4':
  807. case 'I8':
  808. $fields[$name] = intval($field_data);
  809. break;
  810. default:
  811. $fields[$name] = $field_data;
  812. }
  813. unset($table_fields[$field_id]);
  814. }
  815. // check that at least 1 column is specified
  816. if( empty( $fields ) ) {
  817. continue;
  818. }
  819. // check that no required columns are missing
  820. if( count( $fields ) < $table_field_count ) {
  821. foreach( $table_fields as $field ) {
  822. if (isset( $field['OPTS'] ))
  823. if( ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {
  824. continue(2);
  825. }
  826. }
  827. }
  828. $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
  829. }
  830. return $sql;
  831. }
  832. }
  833. /**
  834. * Creates the SQL to execute a list of provided SQL queries
  835. *
  836. * @package axmls
  837. * @access private
  838. */
  839. class dbQuerySet extends dbObject {
  840. /**
  841. * @var array List of SQL queries
  842. */
  843. var $queries = array();
  844. /**
  845. * @var string String used to build of a query line by line
  846. */
  847. var $query;
  848. /**
  849. * @var string Query prefix key
  850. */
  851. var $prefixKey = '';
  852. /**
  853. * @var boolean Auto prefix enable (TRUE)
  854. */
  855. var $prefixMethod = 'AUTO';
  856. /**
  857. * Initializes the query set.
  858. *
  859. * @param object $parent Parent object
  860. * @param array $attributes Attributes
  861. */
  862. function dbQuerySet( &$parent, $attributes = NULL ) {
  863. $this->parent = $parent;
  864. // Overrides the manual prefix key
  865. if( isset( $attributes['KEY'] ) ) {
  866. $this->prefixKey = $attributes['KEY'];
  867. }
  868. $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';
  869. // Enables or disables automatic prefix prepending
  870. switch( $prefixMethod ) {
  871. case 'AUTO':
  872. $this->prefixMethod = 'AUTO';
  873. break;
  874. case 'MANUAL':
  875. $this->prefixMethod = 'MANUAL';
  876. break;
  877. case 'NONE':
  878. $this->prefixMethod = 'NONE';
  879. break;
  880. }
  881. }
  882. /**
  883. * XML Callback to process start elements. Elements currently
  884. * processed are: QUERY.
  885. *
  886. * @access private
  887. */
  888. function _tag_open( &$parser, $tag, $attributes ) {
  889. $this->currentElement = strtoupper( $tag );
  890. switch( $this->currentElement ) {
  891. case 'QUERY':
  892. // Create a new query in a SQL queryset.
  893. // Ignore this query set if a platform is specified and it's different than the
  894. // current connection platform.
  895. if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
  896. $this->newQuery();
  897. } else {
  898. $this->discardQuery();
  899. }
  900. break;
  901. default:
  902. // print_r( array( $tag, $attributes ) );
  903. }
  904. }
  905. /**
  906. * XML Callback to process CDATA elements
  907. */
  908. function _tag_cdata( &$parser, $cdata ) {
  909. switch( $this->currentElement ) {
  910. // Line of queryset SQL data
  911. case 'QUERY':
  912. $this->buildQuery( $cdata );
  913. break;
  914. default:
  915. }
  916. }
  917. /**
  918. * XML Callback to process end elements
  919. *
  920. * @access private
  921. */
  922. function _tag_close( &$parser, $tag ) {
  923. $this->currentElement = '';
  924. switch( strtoupper( $tag ) ) {
  925. case 'QUERY':
  926. // Add the finished query to the open query set.
  927. $this->addQuery();
  928. break;
  929. case 'SQL':
  930. $this->parent->addSQL( $this->create( $this->parent ) );
  931. xml_set_object( $parser, $this->parent );
  932. $this->destroy();
  933. break;
  934. default:
  935. }
  936. }
  937. /**
  938. * Re-initializes the query.
  939. *
  940. * @return boolean TRUE
  941. */
  942. function newQuery() {
  943. $this->query = '';
  944. return TRUE;
  945. }
  946. /**
  947. * Discards the existing query.
  948. *
  949. * @return boolean TRUE
  950. */
  951. function discardQuery() {
  952. unset( $this->query );
  953. return TRUE;
  954. }
  955. /**
  956. * Appends a line to a query that is being built line by line
  957. *
  958. * @param string $data Line of SQL data or NULL to initialize a new query
  959. * @return string SQL query string.
  960. */
  961. function buildQuery( $sql = NULL ) {
  962. if( !isset( $this->query ) OR empty( $sql ) ) {
  963. return FALSE;
  964. }
  965. $this->query .= $sql;
  966. return $this->query;
  967. }
  968. /**
  969. * Adds a completed query to the query list
  970. *
  971. * @return string SQL of added query
  972. */
  973. function addQuery() {
  974. if( !isset( $this->query ) ) {
  975. return FALSE;
  976. }
  977. $this->queries[] = $return = trim($this->query);
  978. unset( $this->query );
  979. return $return;
  980. }
  981. /**
  982. * Creates and returns the current query set
  983. *
  984. * @param object $xmls adoSchema object
  985. * @return array Query set
  986. */
  987. function create( &$xmls ) {
  988. foreach( $this->queries as $id => $query ) {
  989. switch( $this->prefixMethod ) {
  990. case 'AUTO':
  991. // Enable auto prefix replacement
  992. // Process object prefix.
  993. // Evaluate SQL statements to prepend prefix to objects
  994. $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
  995. $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
  996. $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
  997. // SELECT statements aren't working yet
  998. #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );
  999. case 'MANUAL':
  1000. // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX.
  1001. // If prefixKey is not set, we use the default constant XMLS_PREFIX
  1002. if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {
  1003. // Enable prefix override
  1004. $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );
  1005. } else {
  1006. // Use default replacement
  1007. $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );
  1008. }
  1009. }
  1010. $this->queries[$id] = trim( $query );
  1011. }
  1012. // Return the query set array
  1013. return $this->queries;
  1014. }
  1015. /**
  1016. * Rebuilds the query with the prefix attached to any objects
  1017. *
  1018. * @param string $regex Regex used to add prefix
  1019. * @param string $query SQL query string
  1020. * @param string $prefix Prefix to be appended to tables, indices, etc.
  1021. * @return string Prefixed SQL query string.
  1022. */
  1023. function prefixQuery( $regex, $query, $prefix = NULL ) {
  1024. if( !isset( $prefix ) ) {
  1025. return $query;
  1026. }
  1027. if( preg_match( $regex, $query, $match ) ) {
  1028. $preamble = $match[1];
  1029. $postamble = $match[5];
  1030. $objectList = explode( ',', $match[3] );
  1031. // $prefix = $prefix . '_';
  1032. $prefixedList = '';
  1033. foreach( $objectList as $object ) {
  1034. if( $prefixedList !== '' ) {
  1035. $prefixedList .= ', ';
  1036. }
  1037. $prefixedList .= $prefix . trim( $object );
  1038. }
  1039. $query = $preamble . ' ' . $prefixedList . ' ' . $postamble;
  1040. }
  1041. return $query;
  1042. }
  1043. }
  1044. /**
  1045. * Loads and parses an XML file, creating an array of "ready-to-run" SQL statements
  1046. *
  1047. * This class is used to load and parse the XML file, to create an array of SQL statements
  1048. * that can be used to build a database, and to build the database using the SQL array.
  1049. *
  1050. * @tutorial getting_started.pkg
  1051. *
  1052. * @author Richard Tango-Lowy & Dan Cech
  1053. * @version $Revision: 1.12 $
  1054. *
  1055. * @package axmls
  1056. */
  1057. class adoSchema {
  1058. /**
  1059. * @var array Array containing SQL queries to generate all objects
  1060. * @access private
  1061. */
  1062. var $sqlArray;
  1063. /**
  1064. * @var object ADOdb connection object
  1065. * @access private
  1066. */
  1067. var $db;
  1068. /**
  1069. * @var object ADOdb Data Dictionary
  1070. * @access private
  1071. */
  1072. var $dict;
  1073. /**
  1074. * @var string Current XML element
  1075. * @access private
  1076. */
  1077. var $currentElement = '';
  1078. /**
  1079. * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database
  1080. * @access private
  1081. */
  1082. var $upgrade = '';
  1083. /**
  1084. * @var string Optional object prefix
  1085. * @access private
  1086. */
  1087. var $objectPrefix = '';
  1088. /**
  1089. * @var long Original Magic Quotes Runtime value
  1090. * @access private
  1091. */
  1092. var $mgq;
  1093. /**
  1094. * @var long System debug
  1095. * @access private
  1096. */
  1097. var $debug;
  1098. /**
  1099. * @var string Regular expression to find schema version
  1100. * @access private
  1101. */
  1102. var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';
  1103. /**
  1104. * @var string Current schema version
  1105. * @access private
  1106. */
  1107. var $schemaVersion;
  1108. /**
  1109. * @var int Success of last Schema execution
  1110. */
  1111. var $success;
  1112. /**
  1113. * @var bool Execute SQL inline as it is generated
  1114. */
  1115. var $executeInline;
  1116. /**
  1117. * @var bool Continue SQL execution if errors occur
  1118. */
  1119. var $continueOnError;
  1120. /**
  1121. * Creates an adoSchema object
  1122. *
  1123. * Creating an adoSchema object is the first step in processing an XML schema.
  1124. * The only parameter is an ADOdb database connection object, which must already
  1125. * have been created.
  1126. *
  1127. * @param object $db ADOdb database connection object.
  1128. */
  1129. function adoSchema( &$db ) {
  1130. // Initialize the environment
  1131. $this->mgq = get_magic_quotes_runtime();
  1132. set_magic_quotes_runtime(0);
  1133. $this->db = $db;
  1134. $this->debug = $this->db->debug;
  1135. $this->dict = NewDataDictionary( $this->db );
  1136. $this->sqlArray = array();
  1137. $this->schemaVersion = XMLS_SCHEMA_VERSION;
  1138. $this->executeInline( XMLS_EXECUTE_INLINE );
  1139. $this->continueOnError( XMLS_CONTINUE_ON_ERROR );
  1140. $this->setUpgradeMethod();
  1141. }
  1142. /**
  1143. * Sets the method to be used for upgrading an existing database
  1144. *
  1145. * Use this method to specify how existing database objects should be upgraded.
  1146. * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to
  1147. * alter each database object directly, REPLACE attempts to rebuild each object
  1148. * from scratch, BEST attempts to determine the best upgrade method for each
  1149. * object, and NONE disables upgrading.
  1150. *
  1151. * This method is not yet used by AXMLS, but exists for backward compatibility.
  1152. * The ALTER method is automatically assumed when the adoSchema object is
  1153. * instantiated; other upgrade methods are not currently supported.
  1154. *
  1155. * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE)
  1156. * @returns string Upgrade method used
  1157. */
  1158. function SetUpgradeMethod( $method = '' ) {
  1159. if( !is_string( $method ) ) {
  1160. return FALSE;
  1161. }
  1162. $method = strtoupper( $method );
  1163. // Handle the upgrade methods
  1164. switch( $method ) {
  1165. case 'ALTER':
  1166. $this->upgrade = $method;
  1167. break;
  1168. case 'REPLACE':
  1169. $this->upgrade = $method;
  1170. break;
  1171. case 'BEST':
  1172. $this->upgrade = 'ALTER';
  1173. break;
  1174. case 'NONE':
  1175. $this->upgrade = 'NONE';
  1176. break;
  1177. default:
  1178. // Use default if no legitimate method is passed.
  1179. $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;
  1180. }
  1181. return $this->upgrade;
  1182. }
  1183. /**
  1184. * Enables/disables inline SQL execution.
  1185. *
  1186. * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution),
  1187. * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode
  1188. * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema()
  1189. * to apply the schema to the database.
  1190. *
  1191. * @param bool $mode execute
  1192. * @return bool current execution mode
  1193. *
  1194. * @see ParseSchema(), ExecuteSchema()
  1195. */
  1196. function ExecuteInline( $mode = NULL ) {
  1197. if( is_bool( $mode ) ) {
  1198. $this->executeInline = $mode;
  1199. }
  1200. return $this->executeInline;
  1201. }
  1202. /**
  1203. * Enables/disables SQL continue on error.
  1204. *
  1205. * Call this method to enable or disable continuation of SQL execution if an error occurs.
  1206. * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs.
  1207. * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing
  1208. * of the schema will continue.
  1209. *
  1210. * @param bool $mode execute
  1211. * @return bool current continueOnError mode
  1212. *
  1213. * @see addSQL(), ExecuteSchema()
  1214. */
  1215. function ContinueOnError( $mode = NULL ) {
  1216. if( is_bool( $mode ) ) {
  1217. $this->continueOnError = $mode;
  1218. }
  1219. return $this->continueOnError;
  1220. }
  1221. /**
  1222. * Loads an XML schema from a file and converts it to SQL.
  1223. *
  1224. * Call this method to load the specified schema (see the DTD for the proper format) from
  1225. * the filesystem and generate the SQL necessary to create the database described.
  1226. * @see ParseSchemaString()
  1227. *
  1228. * @param string $file Name of XML schema file.
  1229. * @param bool $returnSchema Return schema rather than parsing.
  1230. * @return array Array of SQL queries, ready to execute
  1231. */
  1232. function ParseSchema( $filename, $returnSchema = FALSE ) {
  1233. return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
  1234. }
  1235. /**
  1236. * Loads an XML schema from a file and converts it to SQL.
  1237. *
  1238. * Call this method to load the specified schema from a file (see the DTD for the proper format)
  1239. * and generate the SQL necessary to create the database described by the schema.
  1240. *
  1241. * @param string $file Name of XML schema file.
  1242. * @param bool $returnSchema Return schema rather than parsing.
  1243. * @return array Array of SQL queries, ready to execute.
  1244. *
  1245. * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString()
  1246. * @see ParseSchema(), ParseSchemaString()
  1247. */
  1248. function ParseSchemaFile( $filename, $returnSchema = FALSE ) {
  1249. // Open the file
  1250. if( !($fp = fopen( $filename, 'r' )) ) {
  1251. // die( 'Unable to open file' );
  1252. return FALSE;
  1253. }
  1254. // do version detection here
  1255. if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {
  1256. return FALSE;
  1257. }
  1258. if ( $returnSchema )
  1259. {
  1260. $xmlstring = '';
  1261. while( $data = fread( $fp, 100000 ) ) {
  1262. $xmlstring .= $data;
  1263. }
  1264. return $xmlstring;
  1265. }
  1266. $this->success = 2;
  1267. $xmlParser = $this->create_parser();
  1268. // Process the file
  1269. while( $data = fread( $fp, 4096 ) ) {
  1270. if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {
  1271. die( sprintf(
  1272. "XML error: %s at line %d",
  1273. xml_error_string( xml_get_error_code( $xmlParser) ),
  1274. xml_get_current_line_number( $xmlParser)
  1275. ) );
  1276. }
  1277. }
  1278. xml_parser_free( $xmlParser );
  1279. return $this->sqlArray;
  1280. }
  1281. /**
  1282. * Converts an XML schema string to SQL.
  1283. *
  1284. * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
  1285. * and generate the SQL necessary to create the database described by the schema.
  1286. * @see ParseSchema()
  1287. *
  1288. * @param string $xmlstring XML schema string.
  1289. * @param bool $returnSchema Return schema rather than parsing.
  1290. * @return array Array of SQL queries, ready to execute.
  1291. */
  1292. function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {
  1293. if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
  1294. return FALSE;
  1295. }
  1296. // do version detection here
  1297. if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {
  1298. return FALSE;
  1299. }
  1300. if ( $returnSchema )
  1301. {
  1302. return $xmlstring;
  1303. }
  1304. $this->success = 2;
  1305. $xmlParser = $this->create_parser();
  1306. if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {
  1307. die( sprintf(
  1308. "XML error: %s at line %d",
  1309. xml_error_string( xml_get_error_code( $xmlParser) ),
  1310. xml_get_current_line_number( $xmlParser)
  1311. ) );
  1312. }
  1313. xml_parser_free( $xmlParser );
  1314. return $this->sqlArray;
  1315. }
  1316. /**
  1317. * Loads an XML schema from a file and converts it to uninstallation SQL.
  1318. *
  1319. * Call this method to load the specified schema (see the DTD for the proper format) from
  1320. * the filesystem and generate the SQL necessary to remove the database described.
  1321. * @see RemoveSchemaString()
  1322. *
  1323. * @param string $file Name of XML schema file.
  1324. * @param bool $returnSchema Return schema rather than parsing.
  1325. * @return array Array of SQL queries, ready to execute
  1326. */
  1327. function RemoveSchema( $filename, $returnSchema = FALSE ) {
  1328. return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
  1329. }
  1330. /**
  1331. * Converts an XML schema string to uninstallation SQL.
  1332. *
  1333. * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
  1334. * and generate the SQL necessary to uninstall the database described by the schema.
  1335. * @see RemoveSchema()
  1336. *
  1337. * @param string $schema XML schema string.
  1338. * @param bool $returnSchema Return schema rather than parsing.
  1339. * @return array Array of SQL queries, ready to execute.
  1340. */
  1341. function RemoveSchemaString( $schema, $returnSchema = FALSE ) {
  1342. // grab current version
  1343. if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
  1344. return FALSE;
  1345. }
  1346. return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );
  1347. }
  1348. /**
  1349. * Applies the current XML schema to the database (post execution).
  1350. *
  1351. * Call this method to apply the current schema (generally created by calling
  1352. * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes,
  1353. * and executing other SQL specified in the schema) after parsing.
  1354. * @see ParseSchema(), ParseSchemaString(), ExecuteInline()
  1355. *
  1356. * @param array $sqlArray Array of SQL statements that will be applied rather than
  1357. * the current schema.
  1358. * @param boolean $continueOnErr Continue to apply the schema even if an error occurs.
  1359. * @returns integer 0 if failure, 1 if errors, 2 if successful.
  1360. */
  1361. function ExecuteSchema( $sqlArray = NULL, $continueOnErr = NULL ) {
  1362. if( !is_bool( $continueOnErr ) ) {
  1363. $continueOnErr = $this->ContinueOnError();
  1364. }
  1365. if( !isset( $sqlArray ) ) {
  1366. $sqlArray = $this->sqlArray;
  1367. }
  1368. if( !is_array( $sqlArray ) ) {
  1369. $this->success = 0;
  1370. } else {
  1371. $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );
  1372. }
  1373. return $this->success;
  1374. }
  1375. /**
  1376. * Returns the current SQL array.
  1377. *
  1378. * Call this method to fetch the array of SQL queries resulting from
  1379. * ParseSchema() or ParseSchemaString().
  1380. *
  1381. * @param string $format Format: HTML, TEXT, or NONE (PHP array)
  1382. * @return array Array of SQL statements or FALSE if an error occurs
  1383. */
  1384. function PrintSQL( $format = 'NONE' ) {
  1385. $sqlArray = null;
  1386. return $this->getSQL( $format, $sqlArray );
  1387. }
  1388. /**
  1389. * Saves the current SQL array to the local filesystem as a list of SQL queries.
  1390. *
  1391. * Call this method to save the array of SQL queries (generally resulting from a
  1392. * parsed XML schema) to the filesystem.
  1393. *
  1394. * @param string $filename Path and name where the file should be saved.
  1395. * @return boolean TRUE if save is successful, else FALSE.
  1396. */
  1397. function SaveSQL( $filename = './schema.sql' ) {
  1398. if( !isset( $sqlArray ) ) {
  1399. $sqlArray = $this->sqlArray;
  1400. }
  1401. if( !isset( $sqlArray ) ) {
  1402. return FALSE;
  1403. }
  1404. $fp = fopen( $filename, "w" );
  1405. foreach( $sqlArray as $key => $query ) {
  1406. fwrite( $fp, $query . ";\n" );
  1407. }
  1408. fclose( $fp );
  1409. }
  1410. /**
  1411. * Create an xml parser
  1412. *
  1413. * @return object PHP XML parser object
  1414. *
  1415. * @access private
  1416. */
  1417. function create_parser() {
  1418. // Create the parser
  1419. $xmlParser = xml_parser_create();
  1420. xml_set_object( $xmlParser, $this );
  1421. // Initialize the XML callback functions
  1422. xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );
  1423. xml_set_character_data_handler( $xmlParser, '_tag_cdata' );
  1424. return $xmlParser;
  1425. }
  1426. /**
  1427. * XML Callback to process start elements
  1428. *
  1429. * @access private
  1430. */
  1431. function _tag_open( &$parser, $tag, $attributes ) {
  1432. switch( strtoupper( $tag ) ) {
  1433. case 'TABLE':
  1434. $this->obj = new dbTable( $this, $attributes );
  1435. xml_set_object( $parser, $this->obj );
  1436. break;
  1437. case 'SQL':
  1438. if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
  1439. $this->obj = new dbQuerySet( $this, $attributes );
  1440. xml_set_object( $parser, $this->obj );
  1441. }
  1442. break;
  1443. default:
  1444. // print_r( array( $tag, $attributes ) );
  1445. }
  1446. }
  1447. /**
  1448. * XML Callback to process CDATA elements
  1449. *
  1450. * @access private
  1451. */
  1452. function _tag_cdata( &$parser, $cdata ) {
  1453. }
  1454. /**
  1455. * XML Callback to process end elements
  1456. *
  1457. * @access private
  1458. * @internal
  1459. */
  1460. function _tag_close( &$parser, $tag ) {
  1461. }
  1462. /**
  1463. * Converts an XML schema string to the specified DTD version.
  1464. *
  1465. * Call this method to convert a string containing an XML schema to a different AXMLS
  1466. * DTD version. For instance, to convert a schema created for an pre-1.0 version for
  1467. * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version
  1468. * parameter is specified, the schema will be converted to the current DTD version.
  1469. * If the newFile parameter is provided, the converted schema will be written to the specified
  1470. * file.
  1471. * @see ConvertSchemaFile()
  1472. *
  1473. * @param string $schema String containing XML schema that will be converted.
  1474. * @param string $newVersion DTD version to convert to.
  1475. * @param string $newFile File name of (converted) output file.
  1476. * @return string Converted XML schema or FALSE if an error occurs.
  1477. */
  1478. function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {
  1479. // grab current version
  1480. if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
  1481. return FALSE;
  1482. }
  1483. if( !isset ($newVersion) ) {
  1484. $newVersion = $this->schemaVersion;
  1485. }
  1486. if( $version == $newVersion ) {
  1487. $result = $schema;
  1488. } else {
  1489. $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);
  1490. }
  1491. if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
  1492. fwrite( $fp, $result );
  1493. fclose( $fp );
  1494. }
  1495. return $result;
  1496. }
  1497. // compat for pre-4.3 - jlim
  1498. function _file_get_contents($path)
  1499. {
  1500. if (function_exists('file_get_contents')) return file_get_contents($path);
  1501. return join('',file($path));
  1502. }
  1503. /**
  1504. * Converts an XML schema file to the specified DTD version.
  1505. *
  1506. * Call this method to convert the specified XML schema file to a different AXMLS
  1507. * DTD version. For instance, to convert a schema created for an pre-1.0 version for
  1508. * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version
  1509. * parameter is specified, the schema will be converted to the current DTD version.
  1510. * If the newFile parameter is provided, the converted schema will be written to the specified
  1511. * file.
  1512. * @see ConvertSchemaString()
  1513. *
  1514. * @param string $filename Name of XML schema file that will be converted.
  1515. * @param string $newVersion DTD version to convert to.
  1516. * @param string $newFile File name of (converted) output file.
  1517. * @return string Converted XML schema or FALSE if an error occurs.
  1518. */
  1519. function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {
  1520. // grab current version
  1521. if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {
  1522. return FALSE;
  1523. }
  1524. if( !isset ($newVersion) ) {
  1525. $newVersion = $this->schemaVersion;
  1526. }
  1527. if( $version == $newVersion ) {
  1528. $result = _file_get_contents( $filename );
  1529. // remove unicode BOM if present
  1530. if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {
  1531. $result = substr( $result, 3 );
  1532. }
  1533. } else {
  1534. $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );
  1535. }
  1536. if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
  1537. fwrite( $fp, $result );
  1538. fclose( $fp );
  1539. }
  1540. return $result;
  1541. }
  1542. function TransformSchema( $schema, $xsl, $schematype='string' )
  1543. {
  1544. // Fail if XSLT extension is not available
  1545. if( ! function_exists( 'xslt_create' ) ) {
  1546. return FALSE;
  1547. }
  1548. $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';
  1549. // look for xsl
  1550. if( !is_readable( $xsl_file ) ) {
  1551. return FALSE;
  1552. }
  1553. switch( $schematype )
  1554. {
  1555. case 'file':
  1556. if( !is_readable( $schema ) ) {
  1557. return FALSE;
  1558. }
  1559. $schema = _file_get_contents( $schema );
  1560. break;
  1561. case 'string':
  1562. default:
  1563. if( !is_string( $schema ) ) {
  1564. return FALSE;
  1565. }
  1566. }
  1567. $arguments = array (
  1568. '/_xml' => $schema,
  1569. '/_xsl' => _file_get_contents( $xsl_file )
  1570. );
  1571. // create an XSLT processor
  1572. $xh = xslt_create ();
  1573. // set error handler
  1574. xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));
  1575. // process the schema
  1576. $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments);
  1577. xslt_free ($xh);
  1578. return $result;
  1579. }
  1580. /**
  1581. * Processes XSLT transformation errors
  1582. *
  1583. * @param object $parser XML parser object
  1584. * @param integer $errno Error number
  1585. * @param integer $level Error level
  1586. * @param array $fields Error information fields
  1587. *
  1588. * @access private
  1589. */
  1590. function xslt_error_handler( $parser, $errno, $level, $fields ) {
  1591. if( is_array( $fields ) ) {
  1592. $msg = array(
  1593. 'Message Type' => ucfirst( $fields['msgtype'] ),
  1594. 'Message Code' => $fields['code'],
  1595. 'Message' => $fields['msg'],
  1596. 'Error Number' => $errno,
  1597. 'Level' => $level
  1598. );
  1599. switch( $fields['URI'] ) {
  1600. case 'arg:/_xml':
  1601. $msg['Input'] = 'XML';
  1602. break;
  1603. case 'arg:/_xsl':
  1604. $msg['Input'] = 'XSL';
  1605. break;
  1606. default:
  1607. $msg['Input'] = $fields['URI'];
  1608. }
  1609. $msg['Line'] = $fields['line'];
  1610. } else {
  1611. $msg = array(
  1612. 'Message Type' => 'Error',
  1613. 'Error Number' => $errno,
  1614. 'Level' => $level,
  1615. 'Fields' => var_export( $fields, TRUE )
  1616. );
  1617. }
  1618. $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"
  1619. . '<table>' . "\n";
  1620. foreach( $msg as $label => $details ) {
  1621. $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";
  1622. }
  1623. $error_details .= '</table>';
  1624. trigger_error( $error_details, E_USER_ERROR );
  1625. }
  1626. /**
  1627. * Returns the AXMLS Schema Version of the requested XML schema file.
  1628. *
  1629. * Call this method to obtain the AXMLS DTD version of the requested XML schema file.
  1630. * @see SchemaStringVersion()
  1631. *
  1632. * @param string $filename AXMLS schema file
  1633. * @return string Schema version number or FALSE on error
  1634. */
  1635. function SchemaFileVersion( $filename ) {
  1636. // Open the file
  1637. if( !($fp = fopen( $filename, 'r' )) ) {
  1638. // die( 'Unable to open file' );
  1639. return FALSE;
  1640. }
  1641. // Process the file
  1642. while( $data = fread( $fp, 4096 ) ) {
  1643. if( preg_match( $this->versionRegex, $data, $matches ) ) {
  1644. return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
  1645. }
  1646. }
  1647. return FALSE;
  1648. }
  1649. /**
  1650. * Returns the AXMLS Schema Version of the provided XML schema string.
  1651. *
  1652. * Call this method to obtain the AXMLS DTD version of the provided XML schema string.
  1653. * @see SchemaFileVersion()
  1654. *
  1655. * @param string $xmlstring XML schema string
  1656. * @return string Schema version number or FALSE on error
  1657. */
  1658. function SchemaStringVersion( $xmlstring ) {
  1659. if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
  1660. return FALSE;
  1661. }
  1662. if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {
  1663. return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
  1664. }
  1665. return FALSE;
  1666. }
  1667. /**
  1668. * Extracts an XML schema from an existing database.
  1669. *
  1670. * Call this method to create an XML schema string from an existing database.
  1671. * If the data parameter is set to TRUE, AXMLS will include the data from the database
  1672. * in the schema.
  1673. *
  1674. * @param boolean $data Include data in schema dump
  1675. * @return string Generated XML schema
  1676. */
  1677. function ExtractSchema( $data = FALSE ) {
  1678. $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );
  1679. $schema = '<?xml version="1.0"?>' . "\n"
  1680. . '<schema version="' . $this->schemaVersion . '">' . "\n";
  1681. if( is_array( $tables = $this->db->MetaTables( 'TABLES' ) ) ) {
  1682. foreach( $tables as $table ) {
  1683. $schema .= ' <table name="' . $table . '">' . "\n";
  1684. // grab details from database
  1685. $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE 1=1' );
  1686. $fields = $this->db->MetaColumns( $table );
  1687. $indexes = $this->db->MetaIndexes( $table );
  1688. if( is_array( $fields ) ) {
  1689. foreach( $fields as $details ) {
  1690. $extra = '';
  1691. $content = array();
  1692. if( $details->max_length > 0 ) {
  1693. $extra .= ' size="' . $details->max_length . '"';
  1694. }
  1695. if( $details->primary_key ) {
  1696. $content[] = '<KEY/>';
  1697. } elseif( $details->not_null ) {
  1698. $content[] = '<NOTNULL/>';
  1699. }
  1700. if( $details->has_default ) {
  1701. $content[] = '<DEFAULT value="' . $details->default_value . '"/>';
  1702. }
  1703. if( $details->auto_increment ) {
  1704. $content[] = '<AUTOINCREMENT/>';
  1705. }
  1706. // this stops the creation of 'R' columns,
  1707. // AUTOINCREMENT is used to create auto columns
  1708. $details->primary_key = 0;
  1709. $type = $rs->MetaType( $details );
  1710. $schema .= ' <field name="' . $details->name . '" type="' . $type . '"' . $extra . '>';
  1711. if( !empty( $content ) ) {
  1712. $schema .= "\n " . implode( "\n ", $content ) . "\n ";
  1713. }
  1714. $schema .= '</field>…

Large files files are truncated, but you can click here to view the full file