[ Index ]
 

Code source de Mantis 1.1.0rc3

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/core/adodb/ -> adodb-xmlschema.inc.php (source)

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


Généré le : Thu Nov 29 09:42:17 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics