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