[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/adodb/ -> adodb-xmlschema03.inc.php (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1