Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
1 <?php 2 // Copyright (c) 2004-2005 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.62 $ 16 * 17 * @package axmls 18 * @tutorial getting_started.pkg 19 */ 20 21 function _file_get_contents($file) 22 { 23 if (function_exists('file_get_contents')) return file_get_contents($file); 24 25 $f = fopen($file,'r'); 26 if (!$f) return ''; 27 $t = ''; 28 29 while ($s = fread($f,100000)) $t .= $s; 30 fclose($f); 31 return $t; 32 } 33 34 35 /** 36 * Debug on or off 37 */ 38 if( !defined( 'XMLS_DEBUG' ) ) { 39 define( 'XMLS_DEBUG', FALSE ); 40 } 41 42 /** 43 * Default prefix key 44 */ 45 if( !defined( 'XMLS_PREFIX' ) ) { 46 define( 'XMLS_PREFIX', '%%P' ); 47 } 48 49 /** 50 * Maximum length allowed for object prefix 51 */ 52 if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) { 53 define( 'XMLS_PREFIX_MAXLEN', 10 ); 54 } 55 56 /** 57 * Execute SQL inline as it is generated 58 */ 59 if( !defined( 'XMLS_EXECUTE_INLINE' ) ) { 60 define( 'XMLS_EXECUTE_INLINE', FALSE ); 61 } 62 63 /** 64 * Continue SQL Execution if an error occurs? 65 */ 66 if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) { 67 define( 'XMLS_CONTINUE_ON_ERROR', FALSE ); 68 } 69 70 /** 71 * Current Schema Version 72 */ 73 if( !defined( 'XMLS_SCHEMA_VERSION' ) ) { 74 define( 'XMLS_SCHEMA_VERSION', '0.3' ); 75 } 76 77 /** 78 * Default Schema Version. Used for Schemas without an explicit version set. 79 */ 80 if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) { 81 define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' ); 82 } 83 84 /** 85 * How to handle data rows that already exist in a database during and upgrade. 86 * Options are INSERT (attempts to insert duplicate rows), UPDATE (updates existing 87 * rows) and IGNORE (ignores existing rows). 88 */ 89 if( !defined( 'XMLS_MODE_INSERT' ) ) { 90 define( 'XMLS_MODE_INSERT', 0 ); 91 } 92 if( !defined( 'XMLS_MODE_UPDATE' ) ) { 93 define( 'XMLS_MODE_UPDATE', 1 ); 94 } 95 if( !defined( 'XMLS_MODE_IGNORE' ) ) { 96 define( 'XMLS_MODE_IGNORE', 2 ); 97 } 98 if( !defined( 'XMLS_EXISTING_DATA' ) ) { 99 define( 'XMLS_EXISTING_DATA', XMLS_MODE_INSERT ); 100 } 101 102 /** 103 * Default Schema Version. Used for Schemas without an explicit version set. 104 */ 105 if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) { 106 define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' ); 107 } 108 109 /** 110 * Include the main ADODB library 111 */ 112 if( !defined( '_ADODB_LAYER' ) ) { 113 require ( 'adodb.inc.php' ); 114 require ( 'adodb-datadict.inc.php' ); 115 } 116 117 /** 118 * Abstract DB Object. This class provides basic methods for database objects, such 119 * as tables and indexes. 120 * 121 * @package axmls 122 * @access private 123 */ 124 class dbObject { 125 126 /** 127 * var object Parent 128 */ 129 var $parent; 130 131 /** 132 * var string current element 133 */ 134 var $currentElement; 135 136 /** 137 * NOP 138 */ 139 function __construct( &$parent, $attributes = NULL ) { 140 $this->parent = $parent; 141 } 142 143 /** 144 * XML Callback to process start elements 145 * 146 * @access private 147 */ 148 function _tag_open( $parser, $tag, $attributes ) { 149 150 } 151 152 /** 153 * XML Callback to process CDATA elements 154 * 155 * @access private 156 */ 157 function _tag_cdata( $parser, $cdata ) { 158 159 } 160 161 /** 162 * XML Callback to process end elements 163 * 164 * @access private 165 */ 166 function _tag_close( $parser, $tag ) { 167 168 } 169 170 function create(&$xmls) { 171 return array(); 172 } 173 174 /** 175 * Destroys the object 176 */ 177 function destroy() { 178 } 179 180 /** 181 * Checks whether the specified RDBMS is supported by the current 182 * database object or its ranking ancestor. 183 * 184 * @param string $platform RDBMS platform name (from ADODB platform list). 185 * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE. 186 */ 187 function supportedPlatform( $platform = NULL ) { 188 return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE; 189 } 190 191 /** 192 * Returns the prefix set by the ranking ancestor of the database object. 193 * 194 * @param string $name Prefix string. 195 * @return string Prefix. 196 */ 197 function prefix( $name = '' ) { 198 return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name; 199 } 200 201 /** 202 * Extracts a field ID from the specified field. 203 * 204 * @param string $field Field. 205 * @return string Field ID. 206 */ 207 function fieldID( $field ) { 208 return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) ); 209 } 210 } 211 212 /** 213 * Creates a table object in ADOdb's datadict format 214 * 215 * This class stores information about a database table. As charactaristics 216 * of the table are loaded from the external source, methods and properties 217 * of this class are used to build up the table description in ADOdb's 218 * datadict format. 219 * 220 * @package axmls 221 * @access private 222 */ 223 class dbTable extends dbObject { 224 225 /** 226 * @var string Table name 227 */ 228 var $name; 229 230 /** 231 * @var array Field specifier: Meta-information about each field 232 */ 233 var $fields = array(); 234 235 /** 236 * @var array List of table indexes. 237 */ 238 var $indexes = array(); 239 240 /** 241 * @var array Table options: Table-level options 242 */ 243 var $opts = array(); 244 245 /** 246 * @var string Field index: Keeps track of which field is currently being processed 247 */ 248 var $current_field; 249 250 /** 251 * @var boolean Mark table for destruction 252 * @access private 253 */ 254 var $drop_table; 255 256 /** 257 * @var boolean Mark field for destruction (not yet implemented) 258 * @access private 259 */ 260 var $drop_field = array(); 261 262 /** 263 * @var array Platform-specific options 264 * @access private 265 */ 266 var $currentPlatform = true; 267 268 269 /** 270 * Iniitializes a new table object. 271 * 272 * @param string $prefix DB Object prefix 273 * @param array $attributes Array of table attributes. 274 */ 275 function __construct( &$parent, $attributes = NULL ) { 276 $this->parent = $parent; 277 $this->name = $this->prefix($attributes['NAME']); 278 } 279 280 /** 281 * XML Callback to process start elements. Elements currently 282 * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT. 283 * 284 * @access private 285 */ 286 function _tag_open( $parser, $tag, $attributes ) { 287 $this->currentElement = strtoupper( $tag ); 288 289 switch( $this->currentElement ) { 290 case 'INDEX': 291 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { 292 $index = $this->addIndex( $attributes ); 293 xml_set_object( $parser, $index ); 294 } 295 break; 296 case 'DATA': 297 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { 298 $data = $this->addData( $attributes ); 299 xml_set_object( $parser, $data ); 300 } 301 break; 302 case 'DROP': 303 $this->drop(); 304 break; 305 case 'FIELD': 306 // Add a field 307 $fieldName = $attributes['NAME']; 308 $fieldType = $attributes['TYPE']; 309 $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL; 310 $fieldOpts = !empty( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL; 311 312 $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts ); 313 break; 314 case 'KEY': 315 case 'NOTNULL': 316 case 'AUTOINCREMENT': 317 case 'DEFDATE': 318 case 'DEFTIMESTAMP': 319 case 'UNSIGNED': 320 // Add a field option 321 $this->addFieldOpt( $this->current_field, $this->currentElement ); 322 break; 323 case 'DEFAULT': 324 // Add a field option to the table object 325 326 // Work around ADOdb datadict issue that misinterprets empty strings. 327 if( $attributes['VALUE'] == '' ) { 328 $attributes['VALUE'] = " '' "; 329 } 330 331 $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] ); 332 break; 333 case 'OPT': 334 case 'CONSTRAINT': 335 // Accept platform-specific options 336 $this->currentPlatform = ( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ); 337 break; 338 default: 339 // print_r( array( $tag, $attributes ) ); 340 } 341 } 342 343 /** 344 * XML Callback to process CDATA elements 345 * 346 * @access private 347 */ 348 function _tag_cdata( $parser, $cdata ) { 349 switch( $this->currentElement ) { 350 // Table or field comment 351 case 'DESCR': 352 if( isset( $this->current_field ) ) { 353 $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata ); 354 } else { 355 $this->addTableComment( $cdata ); 356 } 357 break; 358 // Table/field constraint 359 case 'CONSTRAINT': 360 if( isset( $this->current_field ) ) { 361 $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata ); 362 } else { 363 $this->addTableOpt( $cdata ); 364 } 365 break; 366 // Table/field option 367 case 'OPT': 368 if( isset( $this->current_field ) ) { 369 $this->addFieldOpt( $this->current_field, $cdata ); 370 } else { 371 $this->addTableOpt( $cdata ); 372 } 373 break; 374 default: 375 376 } 377 } 378 379 /** 380 * XML Callback to process end elements 381 * 382 * @access private 383 */ 384 function _tag_close( $parser, $tag ) { 385 $this->currentElement = ''; 386 387 switch( strtoupper( $tag ) ) { 388 case 'TABLE': 389 $this->parent->addSQL( $this->create( $this->parent ) ); 390 xml_set_object( $parser, $this->parent ); 391 $this->destroy(); 392 break; 393 case 'FIELD': 394 unset($this->current_field); 395 break; 396 case 'OPT': 397 case 'CONSTRAINT': 398 $this->currentPlatform = true; 399 break; 400 default: 401 402 } 403 } 404 405 /** 406 * Adds an index to a table object 407 * 408 * @param array $attributes Index attributes 409 * @return object dbIndex object 410 */ 411 function addIndex( $attributes ) { 412 $name = strtoupper( $attributes['NAME'] ); 413 $this->indexes[$name] = new dbIndex( $this, $attributes ); 414 return $this->indexes[$name]; 415 } 416 417 /** 418 * Adds data to a table object 419 * 420 * @param array $attributes Data attributes 421 * @return object dbData object 422 */ 423 function addData( $attributes ) { 424 if( !isset( $this->data ) ) { 425 $this->data = new dbData( $this, $attributes ); 426 } 427 return $this->data; 428 } 429 430 /** 431 * Adds a field to a table object 432 * 433 * $name is the name of the table to which the field should be added. 434 * $type is an ADODB datadict field type. The following field types 435 * are supported as of ADODB 3.40: 436 * - C: varchar 437 * - X: CLOB (character large object) or largest varchar size 438 * if CLOB is not supported 439 * - C2: Multibyte varchar 440 * - X2: Multibyte CLOB 441 * - B: BLOB (binary large object) 442 * - D: Date (some databases do not support this, and we return a datetime type) 443 * - T: Datetime or Timestamp 444 * - L: Integer field suitable for storing booleans (0 or 1) 445 * - I: Integer (mapped to I4) 446 * - I1: 1-byte integer 447 * - I2: 2-byte integer 448 * - I4: 4-byte integer 449 * - I8: 8-byte integer 450 * - F: Floating point number 451 * - N: Numeric or decimal number 452 * 453 * @param string $name Name of the table to which the field will be added. 454 * @param string $type ADODB datadict field type. 455 * @param string $size Field size 456 * @param array $opts Field options array 457 * @return array Field specifier array 458 */ 459 function addField( $name, $type, $size = NULL, $opts = NULL ) { 460 $field_id = $this->fieldID( $name ); 461 462 // Set the field index so we know where we are 463 $this->current_field = $field_id; 464 465 // Set the field name (required) 466 $this->fields[$field_id]['NAME'] = $name; 467 468 // Set the field type (required) 469 $this->fields[$field_id]['TYPE'] = $type; 470 471 // Set the field size (optional) 472 if( isset( $size ) ) { 473 $this->fields[$field_id]['SIZE'] = $size; 474 } 475 476 // Set the field options 477 if( isset( $opts ) ) { 478 $this->fields[$field_id]['OPTS'] = array($opts); 479 } else { 480 $this->fields[$field_id]['OPTS'] = array(); 481 } 482 } 483 484 /** 485 * Adds a field option to the current field specifier 486 * 487 * This method adds a field option allowed by the ADOdb datadict 488 * and appends it to the given field. 489 * 490 * @param string $field Field name 491 * @param string $opt ADOdb field option 492 * @param mixed $value Field option value 493 * @return array Field specifier array 494 */ 495 function addFieldOpt( $field, $opt, $value = NULL ) { 496 if( $this->currentPlatform ) { 497 if( !isset( $value ) ) { 498 $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt; 499 // Add the option and value 500 } else { 501 $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value ); 502 } 503 } 504 } 505 506 /** 507 * Adds an option to the table 508 * 509 * This method takes a comma-separated list of table-level options 510 * and appends them to the table object. 511 * 512 * @param string $opt Table option 513 * @return array Options 514 */ 515 function addTableOpt( $opt ) { 516 if(isset($this->currentPlatform)) { 517 $this->opts[$this->parent->db->dataProvider] = $opt; 518 } 519 return $this->opts; 520 } 521 522 function addTableComment( $opt ) { 523 $this->opts['comment'] = $opt; 524 return $this->opts; 525 } 526 527 /** 528 * Generates the SQL that will create the table in the database 529 * 530 * @param object $xmls adoSchema object 531 * @return array Array containing table creation SQL 532 */ 533 function create( &$xmls ) { 534 $sql = array(); 535 536 // drop any existing indexes 537 if( is_array( $legacy_indexes = $xmls->dict->metaIndexes( $this->name ) ) ) { 538 foreach( $legacy_indexes as $index => $index_details ) { 539 $sql[] = $xmls->dict->dropIndexSQL( $index, $this->name ); 540 } 541 } 542 543 // remove fields to be dropped from table object 544 foreach( $this->drop_field as $field ) { 545 unset( $this->fields[$field] ); 546 } 547 548 // if table exists 549 if( is_array( $legacy_fields = $xmls->dict->metaColumns( $this->name ) ) ) { 550 // drop table 551 if( $this->drop_table ) { 552 $sql[] = $xmls->dict->dropTableSQL( $this->name ); 553 554 return $sql; 555 } 556 557 // drop any existing fields not in schema 558 foreach( $legacy_fields as $field_id => $field ) { 559 if( !isset( $this->fields[$field_id] ) ) { 560 $sql[] = $xmls->dict->dropColumnSQL( $this->name, $field->name ); 561 } 562 } 563 // if table doesn't exist 564 } else { 565 if( $this->drop_table ) { 566 return $sql; 567 } 568 569 $legacy_fields = array(); 570 } 571 572 // Loop through the field specifier array, building the associative array for the field options 573 $fldarray = array(); 574 575 foreach( $this->fields as $field_id => $finfo ) { 576 // Set an empty size if it isn't supplied 577 if( !isset( $finfo['SIZE'] ) ) { 578 $finfo['SIZE'] = ''; 579 } 580 581 // Initialize the field array with the type and size 582 $fldarray[$field_id] = array( 583 'NAME' => $finfo['NAME'], 584 'TYPE' => $finfo['TYPE'], 585 'SIZE' => $finfo['SIZE'] 586 ); 587 588 // Loop through the options array and add the field options. 589 if( isset( $finfo['OPTS'] ) ) { 590 foreach( $finfo['OPTS'] as $opt ) { 591 // Option has an argument. 592 if( is_array( $opt ) ) { 593 $key = key( $opt ); 594 $value = $opt[key( $opt )]; 595 @$fldarray[$field_id][$key] .= $value; 596 // Option doesn't have arguments 597 } else { 598 $fldarray[$field_id][$opt] = $opt; 599 } 600 } 601 } 602 } 603 604 if( empty( $legacy_fields ) ) { 605 // Create the new table 606 $sql[] = $xmls->dict->createTableSQL( $this->name, $fldarray, $this->opts ); 607 logMsg( end( $sql ), 'Generated createTableSQL' ); 608 } else { 609 // Upgrade an existing table 610 logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" ); 611 switch( $xmls->upgrade ) { 612 // Use ChangeTableSQL 613 case 'ALTER': 614 logMsg( 'Generated changeTableSQL (ALTERing table)' ); 615 $sql[] = $xmls->dict->changeTableSQL( $this->name, $fldarray, $this->opts ); 616 break; 617 case 'REPLACE': 618 logMsg( 'Doing upgrade REPLACE (testing)' ); 619 $sql[] = $xmls->dict->dropTableSQL( $this->name ); 620 $sql[] = $xmls->dict->createTableSQL( $this->name, $fldarray, $this->opts ); 621 break; 622 // ignore table 623 default: 624 return array(); 625 } 626 } 627 628 foreach( $this->indexes as $index ) { 629 $sql[] = $index->create( $xmls ); 630 } 631 632 if( isset( $this->data ) ) { 633 $sql[] = $this->data->create( $xmls ); 634 } 635 636 return $sql; 637 } 638 639 /** 640 * Marks a field or table for destruction 641 */ 642 function drop() { 643 if( isset( $this->current_field ) ) { 644 // Drop the current field 645 logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" ); 646 // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field ); 647 $this->drop_field[$this->current_field] = $this->current_field; 648 } else { 649 // Drop the current table 650 logMsg( "Dropping table '{$this->name}'" ); 651 // $this->drop_table = $xmls->dict->DropTableSQL( $this->name ); 652 $this->drop_table = TRUE; 653 } 654 } 655 } 656 657 /** 658 * Creates an index object in ADOdb's datadict format 659 * 660 * This class stores information about a database index. As charactaristics 661 * of the index are loaded from the external source, methods and properties 662 * of this class are used to build up the index description in ADOdb's 663 * datadict format. 664 * 665 * @package axmls 666 * @access private 667 */ 668 class dbIndex extends dbObject { 669 670 /** 671 * @var string Index name 672 */ 673 var $name; 674 675 /** 676 * @var array Index options: Index-level options 677 */ 678 var $opts = array(); 679 680 /** 681 * @var array Indexed fields: Table columns included in this index 682 */ 683 var $columns = array(); 684 685 /** 686 * @var boolean Mark index for destruction 687 * @access private 688 */ 689 var $drop = FALSE; 690 691 /** 692 * Initializes the new dbIndex object. 693 * 694 * @param object $parent Parent object 695 * @param array $attributes Attributes 696 * 697 * @internal 698 */ 699 function __construct( &$parent, $attributes = NULL ) { 700 $this->parent = $parent; 701 702 $this->name = $this->prefix ($attributes['NAME']); 703 } 704 705 /** 706 * XML Callback to process start elements 707 * 708 * Processes XML opening tags. 709 * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. 710 * 711 * @access private 712 */ 713 function _tag_open( $parser, $tag, $attributes ) { 714 $this->currentElement = strtoupper( $tag ); 715 716 switch( $this->currentElement ) { 717 case 'DROP': 718 $this->drop(); 719 break; 720 case 'CLUSTERED': 721 case 'BITMAP': 722 case 'UNIQUE': 723 case 'FULLTEXT': 724 case 'HASH': 725 // Add index Option 726 $this->addIndexOpt( $this->currentElement ); 727 break; 728 default: 729 // print_r( array( $tag, $attributes ) ); 730 } 731 } 732 733 /** 734 * XML Callback to process CDATA elements 735 * 736 * Processes XML cdata. 737 * 738 * @access private 739 */ 740 function _tag_cdata( $parser, $cdata ) { 741 switch( $this->currentElement ) { 742 // Index field name 743 case 'COL': 744 $this->addField( $cdata ); 745 break; 746 default: 747 748 } 749 } 750 751 /** 752 * XML Callback to process end elements 753 * 754 * @access private 755 */ 756 function _tag_close( $parser, $tag ) { 757 $this->currentElement = ''; 758 759 switch( strtoupper( $tag ) ) { 760 case 'INDEX': 761 xml_set_object( $parser, $this->parent ); 762 break; 763 } 764 } 765 766 /** 767 * Adds a field to the index 768 * 769 * @param string $name Field name 770 * @return string Field list 771 */ 772 function addField( $name ) { 773 $this->columns[$this->fieldID( $name )] = $name; 774 775 // Return the field list 776 return $this->columns; 777 } 778 779 /** 780 * Adds options to the index 781 * 782 * @param string $opt Comma-separated list of index options. 783 * @return string Option list 784 */ 785 function addIndexOpt( $opt ) { 786 $this->opts[] = $opt; 787 788 // Return the options list 789 return $this->opts; 790 } 791 792 /** 793 * Generates the SQL that will create the index in the database 794 * 795 * @param object $xmls adoSchema object 796 * @return array Array containing index creation SQL 797 */ 798 function create( &$xmls ) { 799 if( $this->drop ) { 800 return NULL; 801 } 802 803 // eliminate any columns that aren't in the table 804 foreach( $this->columns as $id => $col ) { 805 if( !isset( $this->parent->fields[$id] ) ) { 806 unset( $this->columns[$id] ); 807 } 808 } 809 810 return $xmls->dict->createIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts ); 811 } 812 813 /** 814 * Marks an index for destruction 815 */ 816 function drop() { 817 $this->drop = TRUE; 818 } 819 } 820 821 /** 822 * Creates a data object in ADOdb's datadict format 823 * 824 * This class stores information about table data, and is called 825 * when we need to load field data into a table. 826 * 827 * @package axmls 828 * @access private 829 */ 830 class dbData extends dbObject { 831 832 var $data = array(); 833 834 var $row; 835 836 /** 837 * Initializes the new dbData object. 838 * 839 * @param object $parent Parent object 840 * @param array $attributes Attributes 841 * 842 * @internal 843 */ 844 function __construct( &$parent, $attributes = NULL ) { 845 $this->parent = $parent; 846 } 847 848 /** 849 * XML Callback to process start elements 850 * 851 * Processes XML opening tags. 852 * Elements currently processed are: ROW and F (field). 853 * 854 * @access private 855 */ 856 function _tag_open( $parser, $tag, $attributes ) { 857 $this->currentElement = strtoupper( $tag ); 858 859 switch( $this->currentElement ) { 860 case 'ROW': 861 $this->row = count( $this->data ); 862 $this->data[$this->row] = array(); 863 break; 864 case 'F': 865 $this->addField($attributes); 866 default: 867 // print_r( array( $tag, $attributes ) ); 868 } 869 } 870 871 /** 872 * XML Callback to process CDATA elements 873 * 874 * Processes XML cdata. 875 * 876 * @access private 877 */ 878 function _tag_cdata( $parser, $cdata ) { 879 switch( $this->currentElement ) { 880 // Index field name 881 case 'F': 882 $this->addData( $cdata ); 883 break; 884 default: 885 886 } 887 } 888 889 /** 890 * XML Callback to process end elements 891 * 892 * @access private 893 */ 894 function _tag_close( $parser, $tag ) { 895 $this->currentElement = ''; 896 897 switch( strtoupper( $tag ) ) { 898 case 'DATA': 899 xml_set_object( $parser, $this->parent ); 900 break; 901 } 902 } 903 904 /** 905 * Adds a field to the insert 906 * 907 * @param string $name Field name 908 * @return string Field list 909 */ 910 function addField( $attributes ) { 911 // check we're in a valid row 912 if( !isset( $this->row ) || !isset( $this->data[$this->row] ) ) { 913 return; 914 } 915 916 // Set the field index so we know where we are 917 if( isset( $attributes['NAME'] ) ) { 918 $this->current_field = $this->fieldID( $attributes['NAME'] ); 919 } else { 920 $this->current_field = count( $this->data[$this->row] ); 921 } 922 923 // initialise data 924 if( !isset( $this->data[$this->row][$this->current_field] ) ) { 925 $this->data[$this->row][$this->current_field] = ''; 926 } 927 } 928 929 /** 930 * Adds options to the index 931 * 932 * @param string $opt Comma-separated list of index options. 933 * @return string Option list 934 */ 935 function addData( $cdata ) { 936 // check we're in a valid field 937 if ( isset( $this->data[$this->row][$this->current_field] ) ) { 938 // add data to field 939 $this->data[$this->row][$this->current_field] .= $cdata; 940 } 941 } 942 943 /** 944 * Generates the SQL that will add/update the data in the database 945 * 946 * @param object $xmls adoSchema object 947 * @return array Array containing index creation SQL 948 */ 949 function create( &$xmls ) { 950 $table = $xmls->dict->tableName($this->parent->name); 951 $table_field_count = count($this->parent->fields); 952 $tables = $xmls->db->metaTables(); 953 $sql = array(); 954 955 $ukeys = $xmls->db->metaPrimaryKeys( $table ); 956 if( !empty( $this->parent->indexes ) and !empty( $ukeys ) ) { 957 foreach( $this->parent->indexes as $indexObj ) { 958 if( !in_array( $indexObj->name, $ukeys ) ) $ukeys[] = $indexObj->name; 959 } 960 } 961 962 // eliminate any columns that aren't in the table 963 foreach( $this->data as $row ) { 964 $table_fields = $this->parent->fields; 965 $fields = array(); 966 $rawfields = array(); // Need to keep some of the unprocessed data on hand. 967 968 foreach( $row as $field_id => $field_data ) { 969 if( !array_key_exists( $field_id, $table_fields ) ) { 970 if( is_numeric( $field_id ) ) { 971 $field_id = reset( array_keys( $table_fields ) ); 972 } else { 973 continue; 974 } 975 } 976 977 $name = $table_fields[$field_id]['NAME']; 978 979 switch( $table_fields[$field_id]['TYPE'] ) { 980 case 'I': 981 case 'I1': 982 case 'I2': 983 case 'I4': 984 case 'I8': 985 $fields[$name] = intval($field_data); 986 break; 987 case 'C': 988 case 'C2': 989 case 'X': 990 case 'X2': 991 default: 992 $fields[$name] = $xmls->db->qstr( $field_data ); 993 $rawfields[$name] = $field_data; 994 } 995 996 unset($table_fields[$field_id]); 997 998 } 999 1000 // check that at least 1 column is specified 1001 if( empty( $fields ) ) { 1002 continue; 1003 } 1004 1005 // check that no required columns are missing 1006 if( count( $fields ) < $table_field_count ) { 1007 foreach( $table_fields as $field ) { 1008 if( isset( $field['OPTS'] ) and ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) { 1009 continue(2); 1010 } 1011 } 1012 } 1013 1014 // The rest of this method deals with updating existing data records. 1015 1016 if( !in_array( $table, $tables ) or ( $mode = $xmls->existingData() ) == XMLS_MODE_INSERT ) { 1017 // Table doesn't yet exist, so it's safe to insert. 1018 logMsg( "$table doesn't exist, inserting or mode is INSERT" ); 1019 $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')'; 1020 continue; 1021 } 1022 1023 // Prepare to test for potential violations. Get primary keys and unique indexes 1024 $mfields = array_merge( $fields, $rawfields ); 1025 $keyFields = array_intersect( $ukeys, array_keys( $mfields ) ); 1026 1027 if( empty( $ukeys ) or count( $keyFields ) == 0 ) { 1028 // No unique keys in schema, so safe to insert 1029 logMsg( "Either schema or data has no unique keys, so safe to insert" ); 1030 $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')'; 1031 continue; 1032 } 1033 1034 // Select record containing matching unique keys. 1035 $where = ''; 1036 foreach( $ukeys as $key ) { 1037 if( isset( $mfields[$key] ) and $mfields[$key] ) { 1038 if( $where ) $where .= ' AND '; 1039 $where .= $key . ' = ' . $xmls->db->qstr( $mfields[$key] ); 1040 } 1041 } 1042 $records = $xmls->db->execute( 'SELECT * FROM ' . $table . ' WHERE ' . $where ); 1043 switch( $records->recordCount() ) { 1044 case 0: 1045 // No matching record, so safe to insert. 1046 logMsg( "No matching records. Inserting new row with unique data" ); 1047 $sql[] = $xmls->db->getInsertSQL( $records, $mfields ); 1048 break; 1049 case 1: 1050 // Exactly one matching record, so we can update if the mode permits. 1051 logMsg( "One matching record..." ); 1052 if( $mode == XMLS_MODE_UPDATE ) { 1053 logMsg( "...Updating existing row from unique data" ); 1054 $sql[] = $xmls->db->getUpdateSQL( $records, $mfields ); 1055 } 1056 break; 1057 default: 1058 // More than one matching record; the result is ambiguous, so we must ignore the row. 1059 logMsg( "More than one matching record. Ignoring row." ); 1060 } 1061 } 1062 return $sql; 1063 } 1064 } 1065 1066 /** 1067 * Creates the SQL to execute a list of provided SQL queries 1068 * 1069 * @package axmls 1070 * @access private 1071 */ 1072 class dbQuerySet extends dbObject { 1073 1074 /** 1075 * @var array List of SQL queries 1076 */ 1077 var $queries = array(); 1078 1079 /** 1080 * @var string String used to build of a query line by line 1081 */ 1082 var $query; 1083 1084 /** 1085 * @var string Query prefix key 1086 */ 1087 var $prefixKey = ''; 1088 1089 /** 1090 * @var boolean Auto prefix enable (TRUE) 1091 */ 1092 var $prefixMethod = 'AUTO'; 1093 1094 /** 1095 * Initializes the query set. 1096 * 1097 * @param object $parent Parent object 1098 * @param array $attributes Attributes 1099 */ 1100 function __construct( &$parent, $attributes = NULL ) { 1101 $this->parent = $parent; 1102 1103 // Overrides the manual prefix key 1104 if( isset( $attributes['KEY'] ) ) { 1105 $this->prefixKey = $attributes['KEY']; 1106 } 1107 1108 $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : ''; 1109 1110 // Enables or disables automatic prefix prepending 1111 switch( $prefixMethod ) { 1112 case 'AUTO': 1113 $this->prefixMethod = 'AUTO'; 1114 break; 1115 case 'MANUAL': 1116 $this->prefixMethod = 'MANUAL'; 1117 break; 1118 case 'NONE': 1119 $this->prefixMethod = 'NONE'; 1120 break; 1121 } 1122 } 1123 1124 /** 1125 * XML Callback to process start elements. Elements currently 1126 * processed are: QUERY. 1127 * 1128 * @access private 1129 */ 1130 function _tag_open( $parser, $tag, $attributes ) { 1131 $this->currentElement = strtoupper( $tag ); 1132 1133 switch( $this->currentElement ) { 1134 case 'QUERY': 1135 // Create a new query in a SQL queryset. 1136 // Ignore this query set if a platform is specified and it's different than the 1137 // current connection platform. 1138 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { 1139 $this->newQuery(); 1140 } else { 1141 $this->discardQuery(); 1142 } 1143 break; 1144 default: 1145 // print_r( array( $tag, $attributes ) ); 1146 } 1147 } 1148 1149 /** 1150 * XML Callback to process CDATA elements 1151 */ 1152 function _tag_cdata( $parser, $cdata ) { 1153 switch( $this->currentElement ) { 1154 // Line of queryset SQL data 1155 case 'QUERY': 1156 $this->buildQuery( $cdata ); 1157 break; 1158 default: 1159 1160 } 1161 } 1162 1163 /** 1164 * XML Callback to process end elements 1165 * 1166 * @access private 1167 */ 1168 function _tag_close( $parser, $tag ) { 1169 $this->currentElement = ''; 1170 1171 switch( strtoupper( $tag ) ) { 1172 case 'QUERY': 1173 // Add the finished query to the open query set. 1174 $this->addQuery(); 1175 break; 1176 case 'SQL': 1177 $this->parent->addSQL( $this->create( $this->parent ) ); 1178 xml_set_object( $parser, $this->parent ); 1179 $this->destroy(); 1180 break; 1181 default: 1182 1183 } 1184 } 1185 1186 /** 1187 * Re-initializes the query. 1188 * 1189 * @return boolean TRUE 1190 */ 1191 function newQuery() { 1192 $this->query = ''; 1193 1194 return TRUE; 1195 } 1196 1197 /** 1198 * Discards the existing query. 1199 * 1200 * @return boolean TRUE 1201 */ 1202 function discardQuery() { 1203 unset( $this->query ); 1204 1205 return TRUE; 1206 } 1207 1208 /** 1209 * Appends a line to a query that is being built line by line 1210 * 1211 * @param string $data Line of SQL data or NULL to initialize a new query 1212 * @return string SQL query string. 1213 */ 1214 function buildQuery( $sql = NULL ) { 1215 if( !isset( $this->query ) OR empty( $sql ) ) { 1216 return FALSE; 1217 } 1218 1219 $this->query .= $sql; 1220 1221 return $this->query; 1222 } 1223 1224 /** 1225 * Adds a completed query to the query list 1226 * 1227 * @return string SQL of added query 1228 */ 1229 function addQuery() { 1230 if( !isset( $this->query ) ) { 1231 return FALSE; 1232 } 1233 1234 $this->queries[] = $return = trim($this->query); 1235 1236 unset( $this->query ); 1237 1238 return $return; 1239 } 1240 1241 /** 1242 * Creates and returns the current query set 1243 * 1244 * @param object $xmls adoSchema object 1245 * @return array Query set 1246 */ 1247 function create( &$xmls ) { 1248 foreach( $this->queries as $id => $query ) { 1249 switch( $this->prefixMethod ) { 1250 case 'AUTO': 1251 // Enable auto prefix replacement 1252 1253 // Process object prefix. 1254 // Evaluate SQL statements to prepend prefix to objects 1255 $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); 1256 $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); 1257 $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); 1258 1259 // SELECT statements aren't working yet 1260 #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data ); 1261 1262 case 'MANUAL': 1263 // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX. 1264 // If prefixKey is not set, we use the default constant XMLS_PREFIX 1265 if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) { 1266 // Enable prefix override 1267 $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query ); 1268 } else { 1269 // Use default replacement 1270 $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query ); 1271 } 1272 } 1273 1274 $this->queries[$id] = trim( $query ); 1275 } 1276 1277 // Return the query set array 1278 return $this->queries; 1279 } 1280 1281 /** 1282 * Rebuilds the query with the prefix attached to any objects 1283 * 1284 * @param string $regex Regex used to add prefix 1285 * @param string $query SQL query string 1286 * @param string $prefix Prefix to be appended to tables, indices, etc. 1287 * @return string Prefixed SQL query string. 1288 */ 1289 function prefixQuery( $regex, $query, $prefix = NULL ) { 1290 if( !isset( $prefix ) ) { 1291 return $query; 1292 } 1293 1294 if( preg_match( $regex, $query, $match ) ) { 1295 $preamble = $match[1]; 1296 $postamble = $match[5]; 1297 $objectList = explode( ',', $match[3] ); 1298 // $prefix = $prefix . '_'; 1299 1300 $prefixedList = ''; 1301 1302 foreach( $objectList as $object ) { 1303 if( $prefixedList !== '' ) { 1304 $prefixedList .= ', '; 1305 } 1306 1307 $prefixedList .= $prefix . trim( $object ); 1308 } 1309 1310 $query = $preamble . ' ' . $prefixedList . ' ' . $postamble; 1311 } 1312 1313 return $query; 1314 } 1315 } 1316 1317 /** 1318 * Loads and parses an XML file, creating an array of "ready-to-run" SQL statements 1319 * 1320 * This class is used to load and parse the XML file, to create an array of SQL statements 1321 * that can be used to build a database, and to build the database using the SQL array. 1322 * 1323 * @tutorial getting_started.pkg 1324 * 1325 * @author Richard Tango-Lowy & Dan Cech 1326 * @version $Revision: 1.62 $ 1327 * 1328 * @package axmls 1329 */ 1330 class adoSchema { 1331 1332 /** 1333 * @var array Array containing SQL queries to generate all objects 1334 * @access private 1335 */ 1336 var $sqlArray; 1337 1338 /** 1339 * @var object ADOdb connection object 1340 * @access private 1341 */ 1342 var $db; 1343 1344 /** 1345 * @var object ADOdb Data Dictionary 1346 * @access private 1347 */ 1348 var $dict; 1349 1350 /** 1351 * @var string Current XML element 1352 * @access private 1353 */ 1354 var $currentElement = ''; 1355 1356 /** 1357 * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database 1358 * @access private 1359 */ 1360 var $upgrade = ''; 1361 1362 /** 1363 * @var string Optional object prefix 1364 * @access private 1365 */ 1366 var $objectPrefix = ''; 1367 1368 /** 1369 * @var long System debug 1370 * @access private 1371 */ 1372 var $debug; 1373 1374 /** 1375 * @var string Regular expression to find schema version 1376 * @access private 1377 */ 1378 var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/'; 1379 1380 /** 1381 * @var string Current schema version 1382 * @access private 1383 */ 1384 var $schemaVersion; 1385 1386 /** 1387 * @var int Success of last Schema execution 1388 */ 1389 var $success; 1390 1391 /** 1392 * @var bool Execute SQL inline as it is generated 1393 */ 1394 var $executeInline; 1395 1396 /** 1397 * @var bool Continue SQL execution if errors occur 1398 */ 1399 var $continueOnError; 1400 1401 /** 1402 * @var int How to handle existing data rows (insert, update, or ignore) 1403 */ 1404 var $existingData; 1405 1406 /** 1407 * Creates an adoSchema object 1408 * 1409 * Creating an adoSchema object is the first step in processing an XML schema. 1410 * The only parameter is an ADOdb database connection object, which must already 1411 * have been created. 1412 * 1413 * @param object $db ADOdb database connection object. 1414 */ 1415 function __construct( $db ) { 1416 $this->db = $db; 1417 $this->debug = $this->db->debug; 1418 $this->dict = NewDataDictionary( $this->db ); 1419 $this->sqlArray = array(); 1420 $this->schemaVersion = XMLS_SCHEMA_VERSION; 1421 $this->executeInline( XMLS_EXECUTE_INLINE ); 1422 $this->continueOnError( XMLS_CONTINUE_ON_ERROR ); 1423 $this->existingData( XMLS_EXISTING_DATA ); 1424 $this->setUpgradeMethod(); 1425 } 1426 1427 /** 1428 * Sets the method to be used for upgrading an existing database 1429 * 1430 * Use this method to specify how existing database objects should be upgraded. 1431 * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to 1432 * alter each database object directly, REPLACE attempts to rebuild each object 1433 * from scratch, BEST attempts to determine the best upgrade method for each 1434 * object, and NONE disables upgrading. 1435 * 1436 * This method is not yet used by AXMLS, but exists for backward compatibility. 1437 * The ALTER method is automatically assumed when the adoSchema object is 1438 * instantiated; other upgrade methods are not currently supported. 1439 * 1440 * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE) 1441 * @returns string Upgrade method used 1442 */ 1443 function setUpgradeMethod( $method = '' ) { 1444 if( !is_string( $method ) ) { 1445 return FALSE; 1446 } 1447 1448 $method = strtoupper( $method ); 1449 1450 // Handle the upgrade methods 1451 switch( $method ) { 1452 case 'ALTER': 1453 $this->upgrade = $method; 1454 break; 1455 case 'REPLACE': 1456 $this->upgrade = $method; 1457 break; 1458 case 'BEST': 1459 $this->upgrade = 'ALTER'; 1460 break; 1461 case 'NONE': 1462 $this->upgrade = 'NONE'; 1463 break; 1464 default: 1465 // Use default if no legitimate method is passed. 1466 $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD; 1467 } 1468 1469 return $this->upgrade; 1470 } 1471 1472 /** 1473 * Specifies how to handle existing data row when there is a unique key conflict. 1474 * 1475 * The existingData setting specifies how the parser should handle existing rows 1476 * when a unique key violation occurs during the insert. This can happen when inserting 1477 * data into an existing table with one or more primary keys or unique indexes. 1478 * The existingData method takes one of three options: XMLS_MODE_INSERT attempts 1479 * to always insert the data as a new row. In the event of a unique key violation, 1480 * the database will generate an error. XMLS_MODE_UPDATE attempts to update the 1481 * any existing rows with the new data based upon primary or unique key fields in 1482 * the schema. If the data row in the schema specifies no unique fields, the row 1483 * data will be inserted as a new row. XMLS_MODE_IGNORE specifies that any data rows 1484 * that would result in a unique key violation be ignored; no inserts or updates will 1485 * take place. For backward compatibility, the default setting is XMLS_MODE_INSERT, 1486 * but XMLS_MODE_UPDATE will generally be the most appropriate setting. 1487 * 1488 * @param int $mode XMLS_MODE_INSERT, XMLS_MODE_UPDATE, or XMLS_MODE_IGNORE 1489 * @return int current mode 1490 */ 1491 function existingData( $mode = NULL ) { 1492 if( is_int( $mode ) ) { 1493 switch( $mode ) { 1494 case XMLS_MODE_UPDATE: 1495 $mode = XMLS_MODE_UPDATE; 1496 break; 1497 case XMLS_MODE_IGNORE: 1498 $mode = XMLS_MODE_IGNORE; 1499 break; 1500 case XMLS_MODE_INSERT: 1501 $mode = XMLS_MODE_INSERT; 1502 break; 1503 default: 1504 $mode = XMLS_EXISTING_DATA; 1505 break; 1506 } 1507 $this->existingData = $mode; 1508 } 1509 1510 return $this->existingData; 1511 } 1512 1513 /** 1514 * Enables/disables inline SQL execution. 1515 * 1516 * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution), 1517 * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode 1518 * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema() 1519 * to apply the schema to the database. 1520 * 1521 * @param bool $mode execute 1522 * @return bool current execution mode 1523 * 1524 * @see ParseSchema(), ExecuteSchema() 1525 */ 1526 function executeInline( $mode = NULL ) { 1527 if( is_bool( $mode ) ) { 1528 $this->executeInline = $mode; 1529 } 1530 1531 return $this->executeInline; 1532 } 1533 1534 /** 1535 * Enables/disables SQL continue on error. 1536 * 1537 * Call this method to enable or disable continuation of SQL execution if an error occurs. 1538 * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs. 1539 * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing 1540 * of the schema will continue. 1541 * 1542 * @param bool $mode execute 1543 * @return bool current continueOnError mode 1544 * 1545 * @see addSQL(), ExecuteSchema() 1546 */ 1547 function continueOnError( $mode = NULL ) { 1548 if( is_bool( $mode ) ) { 1549 $this->continueOnError = $mode; 1550 } 1551 1552 return $this->continueOnError; 1553 } 1554 1555 /** 1556 * Loads an XML schema from a file and converts it to SQL. 1557 * 1558 * Call this method to load the specified schema (see the DTD for the proper format) from 1559 * the filesystem and generate the SQL necessary to create the database 1560 * described. This method automatically converts the schema to the latest 1561 * axmls schema version. 1562 * @see ParseSchemaString() 1563 * 1564 * @param string $file Name of XML schema file. 1565 * @param bool $returnSchema Return schema rather than parsing. 1566 * @return array Array of SQL queries, ready to execute 1567 */ 1568 function parseSchema( $filename, $returnSchema = FALSE ) { 1569 return $this->parseSchemaString( $this->convertSchemaFile( $filename ), $returnSchema ); 1570 } 1571 1572 /** 1573 * Loads an XML schema from a file and converts it to SQL. 1574 * 1575 * Call this method to load the specified schema directly from a file (see 1576 * the DTD for the proper format) and generate the SQL necessary to create 1577 * the database described by the schema. Use this method when you are dealing 1578 * with large schema files. Otherwise, parseSchema() is faster. 1579 * This method does not automatically convert the schema to the latest axmls 1580 * schema version. You must convert the schema manually using either the 1581 * convertSchemaFile() or convertSchemaString() method. 1582 * @see parseSchema() 1583 * @see convertSchemaFile() 1584 * @see convertSchemaString() 1585 * 1586 * @param string $file Name of XML schema file. 1587 * @param bool $returnSchema Return schema rather than parsing. 1588 * @return array Array of SQL queries, ready to execute. 1589 * 1590 * @deprecated Replaced by adoSchema::parseSchema() and adoSchema::parseSchemaString() 1591 * @see parseSchema(), parseSchemaString() 1592 */ 1593 function parseSchemaFile( $filename, $returnSchema = FALSE ) { 1594 // Open the file 1595 if( !($fp = fopen( $filename, 'r' )) ) { 1596 logMsg( 'Unable to open file' ); 1597 return FALSE; 1598 } 1599 1600 // do version detection here 1601 if( $this->schemaFileVersion( $filename ) != $this->schemaVersion ) { 1602 logMsg( 'Invalid Schema Version' ); 1603 return FALSE; 1604 } 1605 1606 if( $returnSchema ) { 1607 $xmlstring = ''; 1608 while( $data = fread( $fp, 4096 ) ) { 1609 $xmlstring .= $data . "\n"; 1610 } 1611 return $xmlstring; 1612 } 1613 1614 $this->success = 2; 1615 1616 $xmlParser = $this->create_parser(); 1617 1618 // Process the file 1619 while( $data = fread( $fp, 4096 ) ) { 1620 if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) { 1621 die( sprintf( 1622 "XML error: %s at line %d", 1623 xml_error_string( xml_get_error_code( $xmlParser) ), 1624 xml_get_current_line_number( $xmlParser) 1625 ) ); 1626 } 1627 } 1628 1629 xml_parser_free( $xmlParser ); 1630 1631 return $this->sqlArray; 1632 } 1633 1634 /** 1635 * Converts an XML schema string to SQL. 1636 * 1637 * Call this method to parse a string containing an XML schema (see the DTD for the proper format) 1638 * and generate the SQL necessary to create the database described by the schema. 1639 * @see parseSchema() 1640 * 1641 * @param string $xmlstring XML schema string. 1642 * @param bool $returnSchema Return schema rather than parsing. 1643 * @return array Array of SQL queries, ready to execute. 1644 */ 1645 function parseSchemaString( $xmlstring, $returnSchema = FALSE ) { 1646 if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) { 1647 logMsg( 'Empty or Invalid Schema' ); 1648 return FALSE; 1649 } 1650 1651 // do version detection here 1652 if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) { 1653 logMsg( 'Invalid Schema Version' ); 1654 return FALSE; 1655 } 1656 1657 if( $returnSchema ) { 1658 return $xmlstring; 1659 } 1660 1661 $this->success = 2; 1662 1663 $xmlParser = $this->create_parser(); 1664 1665 if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) { 1666 die( sprintf( 1667 "XML error: %s at line %d", 1668 xml_error_string( xml_get_error_code( $xmlParser) ), 1669 xml_get_current_line_number( $xmlParser) 1670 ) ); 1671 } 1672 1673 xml_parser_free( $xmlParser ); 1674 1675 return $this->sqlArray; 1676 } 1677 1678 /** 1679 * Loads an XML schema from a file and converts it to uninstallation SQL. 1680 * 1681 * Call this method to load the specified schema (see the DTD for the proper format) from 1682 * the filesystem and generate the SQL necessary to remove the database described. 1683 * @see RemoveSchemaString() 1684 * 1685 * @param string $file Name of XML schema file. 1686 * @param bool $returnSchema Return schema rather than parsing. 1687 * @return array Array of SQL queries, ready to execute 1688 */ 1689 function removeSchema( $filename, $returnSchema = FALSE ) { 1690 return $this->removeSchemaString( $this->convertSchemaFile( $filename ), $returnSchema ); 1691 } 1692 1693 /** 1694 * Converts an XML schema string to uninstallation SQL. 1695 * 1696 * Call this method to parse a string containing an XML schema (see the DTD for the proper format) 1697 * and generate the SQL necessary to uninstall the database described by the schema. 1698 * @see removeSchema() 1699 * 1700 * @param string $schema XML schema string. 1701 * @param bool $returnSchema Return schema rather than parsing. 1702 * @return array Array of SQL queries, ready to execute. 1703 */ 1704 function removeSchemaString( $schema, $returnSchema = FALSE ) { 1705 1706 // grab current version 1707 if( !( $version = $this->schemaStringVersion( $schema ) ) ) { 1708 return FALSE; 1709 } 1710 1711 return $this->parseSchemaString( $this->transformSchema( $schema, 'remove-' . $version), $returnSchema ); 1712 } 1713 1714 /** 1715 * Applies the current XML schema to the database (post execution). 1716 * 1717 * Call this method to apply the current schema (generally created by calling 1718 * parseSchema() or parseSchemaString() ) to the database (creating the tables, indexes, 1719 * and executing other SQL specified in the schema) after parsing. 1720 * @see parseSchema(), parseSchemaString(), executeInline() 1721 * 1722 * @param array $sqlArray Array of SQL statements that will be applied rather than 1723 * the current schema. 1724 * @param boolean $continueOnErr Continue to apply the schema even if an error occurs. 1725 * @returns integer 0 if failure, 1 if errors, 2 if successful. 1726 */ 1727 function executeSchema( $sqlArray = NULL, $continueOnErr = NULL ) { 1728 if( !is_bool( $continueOnErr ) ) { 1729 $continueOnErr = $this->continueOnError(); 1730 } 1731 1732 if( !isset( $sqlArray ) ) { 1733 $sqlArray = $this->sqlArray; 1734 } 1735 1736 if( !is_array( $sqlArray ) ) { 1737 $this->success = 0; 1738 } else { 1739 $this->success = $this->dict->executeSQLArray( $sqlArray, $continueOnErr ); 1740 } 1741 1742 return $this->success; 1743 } 1744 1745 /** 1746 * Returns the current SQL array. 1747 * 1748 * Call this method to fetch the array of SQL queries resulting from 1749 * parseSchema() or parseSchemaString(). 1750 * 1751 * @param string $format Format: HTML, TEXT, or NONE (PHP array) 1752 * @return array Array of SQL statements or FALSE if an error occurs 1753 */ 1754 function printSQL( $format = 'NONE' ) { 1755 $sqlArray = null; 1756 return $this->getSQL( $format, $sqlArray ); 1757 } 1758 1759 /** 1760 * Saves the current SQL array to the local filesystem as a list of SQL queries. 1761 * 1762 * Call this method to save the array of SQL queries (generally resulting from a 1763 * parsed XML schema) to the filesystem. 1764 * 1765 * @param string $filename Path and name where the file should be saved. 1766 * @return boolean TRUE if save is successful, else FALSE. 1767 */ 1768 function saveSQL( $filename = './schema.sql' ) { 1769 1770 if( !isset( $sqlArray ) ) { 1771 $sqlArray = $this->sqlArray; 1772 } 1773 if( !isset( $sqlArray ) ) { 1774 return FALSE; 1775 } 1776 1777 $fp = fopen( $filename, "w" ); 1778 1779 foreach( $sqlArray as $key => $query ) { 1780 fwrite( $fp, $query . ";\n" ); 1781 } 1782 fclose( $fp ); 1783 } 1784 1785 /** 1786 * Create an xml parser 1787 * 1788 * @return object PHP XML parser object 1789 * 1790 * @access private 1791 */ 1792 function create_parser() { 1793 // Create the parser 1794 $xmlParser = xml_parser_create(); 1795 xml_set_object( $xmlParser, $this ); 1796 1797 // Initialize the XML callback functions 1798 xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' ); 1799 xml_set_character_data_handler( $xmlParser, '_tag_cdata' ); 1800 1801 return $xmlParser; 1802 } 1803 1804 /** 1805 * XML Callback to process start elements 1806 * 1807 * @access private 1808 */ 1809 function _tag_open( $parser, $tag, $attributes ) { 1810 switch( strtoupper( $tag ) ) { 1811 case 'TABLE': 1812 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { 1813 $this->obj = new dbTable( $this, $attributes ); 1814 xml_set_object( $parser, $this->obj ); 1815 } 1816 break; 1817 case 'SQL': 1818 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { 1819 $this->obj = new dbQuerySet( $this, $attributes ); 1820 xml_set_object( $parser, $this->obj ); 1821 } 1822 break; 1823 default: 1824 // print_r( array( $tag, $attributes ) ); 1825 } 1826 1827 } 1828 1829 /** 1830 * XML Callback to process CDATA elements 1831 * 1832 * @access private 1833 */ 1834 function _tag_cdata( $parser, $cdata ) { 1835 } 1836 1837 /** 1838 * XML Callback to process end elements 1839 * 1840 * @access private 1841 * @internal 1842 */ 1843 function _tag_close( $parser, $tag ) { 1844 1845 } 1846 1847 /** 1848 * Converts an XML schema string to the specified DTD version. 1849 * 1850 * Call this method to convert a string containing an XML schema to a different AXMLS 1851 * DTD version. For instance, to convert a schema created for an pre-1.0 version for 1852 * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version 1853 * parameter is specified, the schema will be converted to the current DTD version. 1854 * If the newFile parameter is provided, the converted schema will be written to the specified 1855 * file. 1856 * @see convertSchemaFile() 1857 * 1858 * @param string $schema String containing XML schema that will be converted. 1859 * @param string $newVersion DTD version to convert to. 1860 * @param string $newFile File name of (converted) output file. 1861 * @return string Converted XML schema or FALSE if an error occurs. 1862 */ 1863 function convertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) { 1864 1865 // grab current version 1866 if( !( $version = $this->schemaStringVersion( $schema ) ) ) { 1867 return FALSE; 1868 } 1869 1870 if( !isset ($newVersion) ) { 1871 $newVersion = $this->schemaVersion; 1872 } 1873 1874 if( $version == $newVersion ) { 1875 $result = $schema; 1876 } else { 1877 $result = $this->transformSchema( $schema, 'convert-' . $version . '-' . $newVersion); 1878 } 1879 1880 if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) { 1881 fwrite( $fp, $result ); 1882 fclose( $fp ); 1883 } 1884 1885 return $result; 1886 } 1887 1888 /* 1889 // compat for pre-4.3 - jlim 1890 function _file_get_contents($path) 1891 { 1892 if (function_exists('file_get_contents')) return file_get_contents($path); 1893 return join('',file($path)); 1894 }*/ 1895 1896 /** 1897 * Converts an XML schema file to the specified DTD version. 1898 * 1899 * Call this method to convert the specified XML schema file to a different AXMLS 1900 * DTD version. For instance, to convert a schema created for an pre-1.0 version for 1901 * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version 1902 * parameter is specified, the schema will be converted to the current DTD version. 1903 * If the newFile parameter is provided, the converted schema will be written to the specified 1904 * file. 1905 * @see convertSchemaString() 1906 * 1907 * @param string $filename Name of XML schema file that will be converted. 1908 * @param string $newVersion DTD version to convert to. 1909 * @param string $newFile File name of (converted) output file. 1910 * @return string Converted XML schema or FALSE if an error occurs. 1911 */ 1912 function convertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) { 1913 1914 // grab current version 1915 if( !( $version = $this->schemaFileVersion( $filename ) ) ) { 1916 return FALSE; 1917 } 1918 1919 if( !isset ($newVersion) ) { 1920 $newVersion = $this->schemaVersion; 1921 } 1922 1923 if( $version == $newVersion ) { 1924 $result = _file_get_contents( $filename ); 1925 1926 // remove unicode BOM if present 1927 if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) { 1928 $result = substr( $result, 3 ); 1929 } 1930 } else { 1931 $result = $this->transformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' ); 1932 } 1933 1934 if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) { 1935 fwrite( $fp, $result ); 1936 fclose( $fp ); 1937 } 1938 1939 return $result; 1940 } 1941 1942 function transformSchema( $schema, $xsl, $schematype='string' ) 1943 { 1944 // Fail if XSLT extension is not available 1945 if( ! function_exists( 'xslt_create' ) ) { 1946 return FALSE; 1947 } 1948 1949 $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl'; 1950 1951 // look for xsl 1952 if( !is_readable( $xsl_file ) ) { 1953 return FALSE; 1954 } 1955 1956 switch( $schematype ) 1957 { 1958 case 'file': 1959 if( !is_readable( $schema ) ) { 1960 return FALSE; 1961 } 1962 1963 $schema = _file_get_contents( $schema ); 1964 break; 1965 case 'string': 1966 default: 1967 if( !is_string( $schema ) ) { 1968 return FALSE; 1969 } 1970 } 1971 1972 $arguments = array ( 1973 '/_xml' => $schema, 1974 '/_xsl' => _file_get_contents( $xsl_file ) 1975 ); 1976 1977 // create an XSLT processor 1978 $xh = xslt_create (); 1979 1980 // set error handler 1981 xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler')); 1982 1983 // process the schema 1984 $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); 1985 1986 xslt_free ($xh); 1987 1988 return $result; 1989 } 1990 1991 /** 1992 * Processes XSLT transformation errors 1993 * 1994 * @param object $parser XML parser object 1995 * @param integer $errno Error number 1996 * @param integer $level Error level 1997 * @param array $fields Error information fields 1998 * 1999 * @access private 2000 */ 2001 function xslt_error_handler( $parser, $errno, $level, $fields ) { 2002 if( is_array( $fields ) ) { 2003 $msg = array( 2004 'Message Type' => ucfirst( $fields['msgtype'] ), 2005 'Message Code' => $fields['code'], 2006 'Message' => $fields['msg'], 2007 'Error Number' => $errno, 2008 'Level' => $level 2009 ); 2010 2011 switch( $fields['URI'] ) { 2012 case 'arg:/_xml': 2013 $msg['Input'] = 'XML'; 2014 break; 2015 case 'arg:/_xsl': 2016 $msg['Input'] = 'XSL'; 2017 break; 2018 default: 2019 $msg['Input'] = $fields['URI']; 2020 } 2021 2022 $msg['Line'] = $fields['line']; 2023 } else { 2024 $msg = array( 2025 'Message Type' => 'Error', 2026 'Error Number' => $errno, 2027 'Level' => $level, 2028 'Fields' => var_export( $fields, TRUE ) 2029 ); 2030 } 2031 2032 $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n" 2033 . '<table>' . "\n"; 2034 2035 foreach( $msg as $label => $details ) { 2036 $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n"; 2037 } 2038 2039 $error_details .= '</table>'; 2040 2041 trigger_error( $error_details, E_USER_ERROR ); 2042 } 2043 2044 /** 2045 * Returns the AXMLS Schema Version of the requested XML schema file. 2046 * 2047 * Call this method to obtain the AXMLS DTD version of the requested XML schema file. 2048 * @see SchemaStringVersion() 2049 * 2050 * @param string $filename AXMLS schema file 2051 * @return string Schema version number or FALSE on error 2052 */ 2053 function schemaFileVersion( $filename ) { 2054 // Open the file 2055 if( !($fp = fopen( $filename, 'r' )) ) { 2056 // die( 'Unable to open file' ); 2057 return FALSE; 2058 } 2059 2060 // Process the file 2061 while( $data = fread( $fp, 4096 ) ) { 2062 if( preg_match( $this->versionRegex, $data, $matches ) ) { 2063 return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION; 2064 } 2065 } 2066 2067 return FALSE; 2068 } 2069 2070 /** 2071 * Returns the AXMLS Schema Version of the provided XML schema string. 2072 * 2073 * Call this method to obtain the AXMLS DTD version of the provided XML schema string. 2074 * @see SchemaFileVersion() 2075 * 2076 * @param string $xmlstring XML schema string 2077 * @return string Schema version number or FALSE on error 2078 */ 2079 function schemaStringVersion( $xmlstring ) { 2080 if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) { 2081 return FALSE; 2082 } 2083 2084 if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) { 2085 return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION; 2086 } 2087 2088 return FALSE; 2089 } 2090 2091 /** 2092 * Extracts an XML schema from an existing database. 2093 * 2094 * Call this method to create an XML schema string from an existing database. 2095 * If the data parameter is set to TRUE, AXMLS will include the data from the database 2096 * tables in the schema. 2097 * 2098 * @param boolean $data include data in schema dump 2099 * @param string $indent indentation to use 2100 * @param string $prefix extract only tables with given prefix 2101 * @param boolean $stripprefix strip prefix string when storing in XML schema 2102 * @return string Generated XML schema 2103 */ 2104 function extractSchema( $data = FALSE, $indent = ' ', $prefix = '' , $stripprefix=false) { 2105 $old_mode = $this->db->setFetchMode( ADODB_FETCH_NUM ); 2106 2107 $schema = '<?xml version="1.0"?>' . "\n" 2108 . '<schema version="' . $this->schemaVersion . '">' . "\n"; 2109 if( is_array( $tables = $this->db->metaTables( 'TABLES' ,false ,($prefix) ? str_replace('_','\_',$prefix).'%' : '') ) ) { 2110 foreach( $tables as $table ) { 2111 $schema .= $indent 2112 . '<table name="' 2113 . htmlentities( $stripprefix ? str_replace($prefix, '', $table) : $table ) 2114 . '">' . "\n"; 2115 2116 // grab details from database 2117 $rs = $this->db->execute( 'SELECT * FROM ' . $table . ' WHERE -1' ); 2118 $fields = $this->db->metaColumns( $table ); 2119 $indexes = $this->db->metaIndexes( $table ); 2120 2121 if( is_array( $fields ) ) { 2122 foreach( $fields as $details ) { 2123 $extra = ''; 2124 $content = array(); 2125 2126 if( isset($details->max_length) && $details->max_length > 0 ) { 2127 $extra .= ' size="' . $details->max_length . '"'; 2128 } 2129 2130 if( isset($details->primary_key) && $details->primary_key ) { 2131 $content[] = '<KEY/>'; 2132 } elseif( isset($details->not_null) && $details->not_null ) { 2133 $content[] = '<NOTNULL/>'; 2134 } 2135 2136 if( isset($details->has_default) && $details->has_default ) { 2137 $content[] = '<DEFAULT value="' . htmlentities( $details->default_value ) . '"/>'; 2138 } 2139 2140 if( isset($details->auto_increment) && $details->auto_increment ) { 2141 $content[] = '<AUTOINCREMENT/>'; 2142 } 2143 2144 if( isset($details->unsigned) && $details->unsigned ) { 2145 $content[] = '<UNSIGNED/>'; 2146 } 2147 2148 // this stops the creation of 'R' columns, 2149 // AUTOINCREMENT is used to create auto columns 2150 $details->primary_key = 0; 2151 $type = $rs->metaType( $details ); 2152 2153 $schema .= str_repeat( $indent, 2 ) . '<field name="' . htmlentities( $details->name ) . '" type="' . $type . '"' . $extra; 2154 2155 if( !empty( $content ) ) { 2156 $schema .= ">\n" . str_repeat( $indent, 3 ) 2157 . implode( "\n" . str_repeat( $indent, 3 ), $content ) . "\n" 2158 . str_repeat( $indent, 2 ) . '</field>' . "\n"; 2159 } else { 2160 $schema .= "/>\n"; 2161 } 2162 } 2163 } 2164 2165 if( is_array( $indexes ) ) { 2166 foreach( $indexes as $index => $details ) { 2167 $schema .= str_repeat( $indent, 2 ) . '<index name="' . $index . '">' . "\n"; 2168 2169 if( $details['unique'] ) { 2170 $schema .= str_repeat( $indent, 3 ) . '<UNIQUE/>' . "\n"; 2171 } 2172 2173 foreach( $details['columns'] as $column ) { 2174 $schema .= str_repeat( $indent, 3 ) . '<col>' . htmlentities( $column ) . '</col>' . "\n"; 2175 } 2176 2177 $schema .= str_repeat( $indent, 2 ) . '</index>' . "\n"; 2178 } 2179 } 2180 2181 if( $data ) { 2182 $rs = $this->db->execute( 'SELECT * FROM ' . $table ); 2183 2184 if( is_object( $rs ) && !$rs->EOF ) { 2185 $schema .= str_repeat( $indent, 2 ) . "<data>\n"; 2186 2187 while( $row = $rs->fetchRow() ) { 2188 foreach( $row as $key => $val ) { 2189 if ( $val != htmlentities( $val ) ) { 2190 $row[$key] = '<![CDATA[' . $val . ']]>'; 2191 } 2192 } 2193 2194 $schema .= str_repeat( $indent, 3 ) . '<row><f>' . implode( '</f><f>', $row ) . "</f></row>\n"; 2195 } 2196 2197 $schema .= str_repeat( $indent, 2 ) . "</data>\n"; 2198 } 2199 } 2200 2201 $schema .= $indent . "</table>\n"; 2202 } 2203 } 2204 2205 $this->db->setFetchMode( $old_mode ); 2206 2207 $schema .= '</schema>'; 2208 return $schema; 2209 } 2210 2211 /** 2212 * Sets a prefix for database objects 2213 * 2214 * Call this method to set a standard prefix that will be prepended to all database tables 2215 * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix. 2216 * 2217 * @param string $prefix Prefix that will be prepended. 2218 * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix. 2219 * @return boolean TRUE if successful, else FALSE 2220 */ 2221 function setPrefix( $prefix = '', $underscore = TRUE ) { 2222 switch( TRUE ) { 2223 // clear prefix 2224 case empty( $prefix ): 2225 logMsg( 'Cleared prefix' ); 2226 $this->objectPrefix = ''; 2227 return TRUE; 2228 // prefix too long 2229 case strlen( $prefix ) > XMLS_PREFIX_MAXLEN: 2230 // prefix contains invalid characters 2231 case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ): 2232 logMsg( 'Invalid prefix: ' . $prefix ); 2233 return FALSE; 2234 } 2235 2236 if( $underscore AND substr( $prefix, -1 ) != '_' ) { 2237 $prefix .= '_'; 2238 } 2239 2240 // prefix valid 2241 logMsg( 'Set prefix: ' . $prefix ); 2242 $this->objectPrefix = $prefix; 2243 return TRUE; 2244 } 2245 2246 /** 2247 * Returns an object name with the current prefix prepended. 2248 * 2249 * @param string $name Name 2250 * @return string Prefixed name 2251 * 2252 * @access private 2253 */ 2254 function prefix( $name = '' ) { 2255 // if prefix is set 2256 if( !empty( $this->objectPrefix ) ) { 2257 // Prepend the object prefix to the table name 2258 // prepend after quote if used 2259 return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name ); 2260 } 2261 2262 // No prefix set. Use name provided. 2263 return $name; 2264 } 2265 2266 /** 2267 * Checks if element references a specific platform 2268 * 2269 * @param string $platform Requested platform 2270 * @returns boolean TRUE if platform check succeeds 2271 * 2272 * @access private 2273 */ 2274 function supportedPlatform( $platform = NULL ) { 2275 if( !empty( $platform ) ) { 2276 $regex = '/(^|\|)' . $this->db->databaseType . '(\||$)/i'; 2277 2278 if( preg_match( '/^- /', $platform ) ) { 2279 if (preg_match ( $regex, substr( $platform, 2 ) ) ) { 2280 logMsg( 'Platform ' . $platform . ' is NOT supported' ); 2281 return FALSE; 2282 } 2283 } else { 2284 if( !preg_match ( $regex, $platform ) ) { 2285 logMsg( 'Platform ' . $platform . ' is NOT supported' ); 2286 return FALSE; 2287 } 2288 } 2289 } 2290 2291 logMsg( 'Platform ' . $platform . ' is supported' ); 2292 return TRUE; 2293 } 2294 2295 /** 2296 * Clears the array of generated SQL. 2297 * 2298 * @access private 2299 */ 2300 function clearSQL() { 2301 $this->sqlArray = array(); 2302 } 2303 2304 /** 2305 * Adds SQL into the SQL array. 2306 * 2307 * @param mixed $sql SQL to Add 2308 * @return boolean TRUE if successful, else FALSE. 2309 * 2310 * @access private 2311 */ 2312 function addSQL( $sql = NULL ) { 2313 if( is_array( $sql ) ) { 2314 foreach( $sql as $line ) { 2315 $this->addSQL( $line ); 2316 } 2317 2318 return TRUE; 2319 } 2320 2321 if( is_string( $sql ) ) { 2322 $this->sqlArray[] = $sql; 2323 2324 // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL. 2325 if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) { 2326 $saved = $this->db->debug; 2327 $this->db->debug = $this->debug; 2328 $ok = $this->db->Execute( $sql ); 2329 $this->db->debug = $saved; 2330 2331 if( !$ok ) { 2332 if( $this->debug ) { 2333 ADOConnection::outp( $this->db->ErrorMsg() ); 2334 } 2335 2336 $this->success = 1; 2337 } 2338 } 2339 2340 return TRUE; 2341 } 2342 2343 return FALSE; 2344 } 2345 2346 /** 2347 * Gets the SQL array in the specified format. 2348 * 2349 * @param string $format Format 2350 * @return mixed SQL 2351 * 2352 * @access private 2353 */ 2354 function getSQL( $format = NULL, $sqlArray = NULL ) { 2355 if( !is_array( $sqlArray ) ) { 2356 $sqlArray = $this->sqlArray; 2357 } 2358 2359 if( !is_array( $sqlArray ) ) { 2360 return FALSE; 2361 } 2362 2363 switch( strtolower( $format ) ) { 2364 case 'string': 2365 case 'text': 2366 return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : ''; 2367 case'html': 2368 return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : ''; 2369 } 2370 2371 return $this->sqlArray; 2372 } 2373 2374 /** 2375 * Destroys an adoSchema object. 2376 * 2377 * Call this method to clean up after an adoSchema object that is no longer in use. 2378 * @deprecated adoSchema now cleans up automatically. 2379 */ 2380 function destroy() { 2381 } 2382 } 2383 2384 /** 2385 * Message logging function 2386 * 2387 * @access private 2388 */ 2389 function logMsg( $msg, $title = NULL, $force = FALSE ) { 2390 if( XMLS_DEBUG or $force ) { 2391 echo '<pre>'; 2392 2393 if( isset( $title ) ) { 2394 echo '<h3>' . htmlentities( $title ) . '</h3>'; 2395 } 2396 2397 if( @is_object( $this ) ) { 2398 echo '[' . get_class( $this ) . '] '; 2399 } 2400 2401 print_r( $msg ); 2402 2403 echo '</pre>'; 2404 } 2405 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body