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