|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 // Copyright (c) 2004 ars Cognita Inc., all rights reserved 00003 /* ****************************************************************************** 00004 Released under both BSD license and Lesser GPL library license. 00005 Whenever there is any discrepancy between the two licenses, 00006 the BSD license will take precedence. 00007 *******************************************************************************/ 00021 function _file_get_contents($file) 00022 { 00023 if (function_exists('file_get_contents')) return file_get_contents($file); 00024 00025 $f = fopen($file,'r'); 00026 if (!$f) return ''; 00027 $t = ''; 00028 00029 while ($s = fread($f,100000)) $t .= $s; 00030 fclose($f); 00031 return $t; 00032 } 00033 00034 00038 if( !defined( 'XMLS_DEBUG' ) ) { 00039 define( 'XMLS_DEBUG', FALSE ); 00040 } 00041 00045 if( !defined( 'XMLS_PREFIX' ) ) { 00046 define( 'XMLS_PREFIX', '%%P' ); 00047 } 00048 00052 if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) { 00053 define( 'XMLS_PREFIX_MAXLEN', 10 ); 00054 } 00055 00059 if( !defined( 'XMLS_EXECUTE_INLINE' ) ) { 00060 define( 'XMLS_EXECUTE_INLINE', FALSE ); 00061 } 00062 00066 if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) { 00067 define( 'XMLS_CONTINUE_ON_ERROR', FALSE ); 00068 } 00069 00073 if( !defined( 'XMLS_SCHEMA_VERSION' ) ) { 00074 define( 'XMLS_SCHEMA_VERSION', '0.2' ); 00075 } 00076 00080 if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) { 00081 define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' ); 00082 } 00083 00087 if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) { 00088 define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' ); 00089 } 00090 00094 if( !defined( '_ADODB_LAYER' ) ) { 00095 require( 'adodb.inc.php' ); 00096 require( 'adodb-datadict.inc.php' ); 00097 } 00098 00106 class dbObject { 00107 00111 var $parent; 00112 00116 var $currentElement; 00117 00121 function dbObject( &$parent, $attributes = NULL ) { 00122 $this->parent = $parent; 00123 } 00124 00130 function _tag_open( &$parser, $tag, $attributes ) { 00131 00132 } 00133 00139 function _tag_cdata( &$parser, $cdata ) { 00140 00141 } 00142 00148 function _tag_close( &$parser, $tag ) { 00149 00150 } 00151 00152 function create(&$xmls) { 00153 return array(); 00154 } 00155 00159 function destroy() { 00160 unset( $this ); 00161 } 00162 00170 function supportedPlatform( $platform = NULL ) { 00171 return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE; 00172 } 00173 00180 function prefix( $name = '' ) { 00181 return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name; 00182 } 00183 00190 function FieldID( $field ) { 00191 return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) ); 00192 } 00193 } 00194 00206 class dbTable extends dbObject { 00207 00211 var $name; 00212 00216 var $fields = array(); 00217 00221 var $indexes = array(); 00222 00226 var $opts = array(); 00227 00231 var $current_field; 00232 00237 var $drop_table; 00238 00243 var $drop_field = array(); 00244 00251 function dbTable( &$parent, $attributes = NULL ) { 00252 $this->parent = $parent; 00253 $this->name = $this->prefix($attributes['NAME']); 00254 } 00255 00262 function _tag_open( &$parser, $tag, $attributes ) { 00263 $this->currentElement = strtoupper( $tag ); 00264 00265 switch( $this->currentElement ) { 00266 case 'INDEX': 00267 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { 00268 xml_set_object( $parser, $this->addIndex( $attributes ) ); 00269 } 00270 break; 00271 case 'DATA': 00272 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { 00273 xml_set_object( $parser, $this->addData( $attributes ) ); 00274 } 00275 break; 00276 case 'DROP': 00277 $this->drop(); 00278 break; 00279 case 'FIELD': 00280 // Add a field 00281 $fieldName = $attributes['NAME']; 00282 $fieldType = $attributes['TYPE']; 00283 $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL; 00284 $fieldOpts = isset( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL; 00285 00286 $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts ); 00287 break; 00288 case 'KEY': 00289 case 'NOTNULL': 00290 case 'AUTOINCREMENT': 00291 // Add a field option 00292 $this->addFieldOpt( $this->current_field, $this->currentElement ); 00293 break; 00294 case 'DEFAULT': 00295 // Add a field option to the table object 00296 00297 // Work around ADOdb datadict issue that misinterprets empty strings. 00298 if( $attributes['VALUE'] == '' ) { 00299 $attributes['VALUE'] = " '' "; 00300 } 00301 00302 $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] ); 00303 break; 00304 case 'DEFDATE': 00305 case 'DEFTIMESTAMP': 00306 // Add a field option to the table object 00307 $this->addFieldOpt( $this->current_field, $this->currentElement ); 00308 break; 00309 default: 00310 // print_r( array( $tag, $attributes ) ); 00311 } 00312 } 00313 00319 function _tag_cdata( &$parser, $cdata ) { 00320 switch( $this->currentElement ) { 00321 // Table constraint 00322 case 'CONSTRAINT': 00323 if( isset( $this->current_field ) ) { 00324 $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata ); 00325 } else { 00326 $this->addTableOpt( $cdata ); 00327 } 00328 break; 00329 // Table option 00330 case 'OPT': 00331 $this->addTableOpt( $cdata ); 00332 break; 00333 default: 00334 00335 } 00336 } 00337 00343 function _tag_close( &$parser, $tag ) { 00344 $this->currentElement = ''; 00345 00346 switch( strtoupper( $tag ) ) { 00347 case 'TABLE': 00348 $this->parent->addSQL( $this->create( $this->parent ) ); 00349 xml_set_object( $parser, $this->parent ); 00350 $this->destroy(); 00351 break; 00352 case 'FIELD': 00353 unset($this->current_field); 00354 break; 00355 00356 } 00357 } 00358 00365 function addIndex( $attributes ) { 00366 $name = strtoupper( $attributes['NAME'] ); 00367 $this->indexes[$name] = new dbIndex( $this, $attributes ); 00368 return $this->indexes[$name]; 00369 } 00370 00377 function addData( $attributes ) { 00378 if( !isset( $this->data ) ) { 00379 $this->data = new dbData( $this, $attributes ); 00380 } 00381 return $this->data; 00382 } 00383 00413 function addField( $name, $type, $size = NULL, $opts = NULL ) { 00414 $field_id = $this->FieldID( $name ); 00415 00416 // Set the field index so we know where we are 00417 $this->current_field = $field_id; 00418 00419 // Set the field name (required) 00420 $this->fields[$field_id]['NAME'] = $name; 00421 00422 // Set the field type (required) 00423 $this->fields[$field_id]['TYPE'] = $type; 00424 00425 // Set the field size (optional) 00426 if( isset( $size ) ) { 00427 $this->fields[$field_id]['SIZE'] = $size; 00428 } 00429 00430 // Set the field options 00431 if( isset( $opts ) ) { 00432 $this->fields[$field_id]['OPTS'][] = $opts; 00433 } 00434 } 00435 00447 function addFieldOpt( $field, $opt, $value = NULL ) { 00448 if( !isset( $value ) ) { 00449 $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt; 00450 // Add the option and value 00451 } else { 00452 $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value ); 00453 } 00454 } 00455 00465 function addTableOpt( $opt ) { 00466 if(isset($this->currentPlatform)) { 00467 $this->opts[$this->parent->db->databaseType] = $opt; 00468 } 00469 return $this->opts; 00470 } 00471 00472 00479 function create( &$xmls ) { 00480 $sql = array(); 00481 00482 // drop any existing indexes 00483 if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) { 00484 foreach( $legacy_indexes as $index => $index_details ) { 00485 $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name ); 00486 } 00487 } 00488 00489 // remove fields to be dropped from table object 00490 foreach( $this->drop_field as $field ) { 00491 unset( $this->fields[$field] ); 00492 } 00493 00494 // if table exists 00495 if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) { 00496 // drop table 00497 if( $this->drop_table ) { 00498 $sql[] = $xmls->dict->DropTableSQL( $this->name ); 00499 00500 return $sql; 00501 } 00502 00503 // drop any existing fields not in schema 00504 foreach( $legacy_fields as $field_id => $field ) { 00505 if( !isset( $this->fields[$field_id] ) ) { 00506 $sql[] = $xmls->dict->DropColumnSQL( $this->name, '`'.$field->name.'`' ); 00507 } 00508 } 00509 // if table doesn't exist 00510 } else { 00511 if( $this->drop_table ) { 00512 return $sql; 00513 } 00514 00515 $legacy_fields = array(); 00516 } 00517 00518 // Loop through the field specifier array, building the associative array for the field options 00519 $fldarray = array(); 00520 00521 foreach( $this->fields as $field_id => $finfo ) { 00522 // Set an empty size if it isn't supplied 00523 if( !isset( $finfo['SIZE'] ) ) { 00524 $finfo['SIZE'] = ''; 00525 } 00526 00527 // Initialize the field array with the type and size 00528 $fldarray[$field_id] = array( 00529 'NAME' => $finfo['NAME'], 00530 'TYPE' => $finfo['TYPE'], 00531 'SIZE' => $finfo['SIZE'] 00532 ); 00533 00534 // Loop through the options array and add the field options. 00535 if( isset( $finfo['OPTS'] ) ) { 00536 foreach( $finfo['OPTS'] as $opt ) { 00537 // Option has an argument. 00538 if( is_array( $opt ) ) { 00539 $key = key( $opt ); 00540 $value = $opt[key( $opt )]; 00541 @$fldarray[$field_id][$key] .= $value; 00542 // Option doesn't have arguments 00543 } else { 00544 $fldarray[$field_id][$opt] = $opt; 00545 } 00546 } 00547 } 00548 } 00549 00550 if( empty( $legacy_fields ) ) { 00551 // Create the new table 00552 $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts ); 00553 logMsg( end( $sql ), 'Generated CreateTableSQL' ); 00554 } else { 00555 // Upgrade an existing table 00556 logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" ); 00557 switch( $xmls->upgrade ) { 00558 // Use ChangeTableSQL 00559 case 'ALTER': 00560 logMsg( 'Generated ChangeTableSQL (ALTERing table)' ); 00561 $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts ); 00562 break; 00563 case 'REPLACE': 00564 logMsg( 'Doing upgrade REPLACE (testing)' ); 00565 $sql[] = $xmls->dict->DropTableSQL( $this->name ); 00566 $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts ); 00567 break; 00568 // ignore table 00569 default: 00570 return array(); 00571 } 00572 } 00573 00574 foreach( $this->indexes as $index ) { 00575 $sql[] = $index->create( $xmls ); 00576 } 00577 00578 if( isset( $this->data ) ) { 00579 $sql[] = $this->data->create( $xmls ); 00580 } 00581 00582 return $sql; 00583 } 00584 00588 function drop() { 00589 if( isset( $this->current_field ) ) { 00590 // Drop the current field 00591 logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" ); 00592 // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field ); 00593 $this->drop_field[$this->current_field] = $this->current_field; 00594 } else { 00595 // Drop the current table 00596 logMsg( "Dropping table '{$this->name}'" ); 00597 // $this->drop_table = $xmls->dict->DropTableSQL( $this->name ); 00598 $this->drop_table = TRUE; 00599 } 00600 } 00601 } 00602 00614 class dbIndex extends dbObject { 00615 00619 var $name; 00620 00624 var $opts = array(); 00625 00629 var $columns = array(); 00630 00635 var $drop = FALSE; 00636 00645 function dbIndex( &$parent, $attributes = NULL ) { 00646 $this->parent = $parent; 00647 00648 $this->name = $this->prefix ($attributes['NAME']); 00649 } 00650 00659 function _tag_open( &$parser, $tag, $attributes ) { 00660 $this->currentElement = strtoupper( $tag ); 00661 00662 switch( $this->currentElement ) { 00663 case 'DROP': 00664 $this->drop(); 00665 break; 00666 case 'CLUSTERED': 00667 case 'BITMAP': 00668 case 'UNIQUE': 00669 case 'FULLTEXT': 00670 case 'HASH': 00671 // Add index Option 00672 $this->addIndexOpt( $this->currentElement ); 00673 break; 00674 default: 00675 // print_r( array( $tag, $attributes ) ); 00676 } 00677 } 00678 00686 function _tag_cdata( &$parser, $cdata ) { 00687 switch( $this->currentElement ) { 00688 // Index field name 00689 case 'COL': 00690 $this->addField( $cdata ); 00691 break; 00692 default: 00693 00694 } 00695 } 00696 00702 function _tag_close( &$parser, $tag ) { 00703 $this->currentElement = ''; 00704 00705 switch( strtoupper( $tag ) ) { 00706 case 'INDEX': 00707 xml_set_object( $parser, $this->parent ); 00708 break; 00709 } 00710 } 00711 00718 function addField( $name ) { 00719 $this->columns[$this->FieldID( $name )] = $name; 00720 00721 // Return the field list 00722 return $this->columns; 00723 } 00724 00731 function addIndexOpt( $opt ) { 00732 $this->opts[] = $opt; 00733 00734 // Return the options list 00735 return $this->opts; 00736 } 00737 00744 function create( &$xmls ) { 00745 if( $this->drop ) { 00746 return NULL; 00747 } 00748 00749 // eliminate any columns that aren't in the table 00750 foreach( $this->columns as $id => $col ) { 00751 if( !isset( $this->parent->fields[$id] ) ) { 00752 unset( $this->columns[$id] ); 00753 } 00754 } 00755 00756 return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts ); 00757 } 00758 00762 function drop() { 00763 $this->drop = TRUE; 00764 } 00765 } 00766 00775 class dbData extends dbObject { 00776 00777 var $data = array(); 00778 00779 var $row; 00780 00789 function dbData( &$parent, $attributes = NULL ) { 00790 $this->parent = $parent; 00791 } 00792 00801 function _tag_open( &$parser, $tag, $attributes ) { 00802 $this->currentElement = strtoupper( $tag ); 00803 00804 switch( $this->currentElement ) { 00805 case 'ROW': 00806 $this->row = count( $this->data ); 00807 $this->data[$this->row] = array(); 00808 break; 00809 case 'F': 00810 $this->addField($attributes); 00811 default: 00812 // print_r( array( $tag, $attributes ) ); 00813 } 00814 } 00815 00823 function _tag_cdata( &$parser, $cdata ) { 00824 switch( $this->currentElement ) { 00825 // Index field name 00826 case 'F': 00827 $this->addData( $cdata ); 00828 break; 00829 default: 00830 00831 } 00832 } 00833 00839 function _tag_close( &$parser, $tag ) { 00840 $this->currentElement = ''; 00841 00842 switch( strtoupper( $tag ) ) { 00843 case 'DATA': 00844 xml_set_object( $parser, $this->parent ); 00845 break; 00846 } 00847 } 00848 00855 function addField( $attributes ) { 00856 if( isset( $attributes['NAME'] ) ) { 00857 $name = $attributes['NAME']; 00858 } else { 00859 $name = count($this->data[$this->row]); 00860 } 00861 00862 // Set the field index so we know where we are 00863 $this->current_field = $this->FieldID( $name ); 00864 } 00865 00872 function addData( $cdata ) { 00873 if( !isset( $this->data[$this->row] ) ) { 00874 $this->data[$this->row] = array(); 00875 } 00876 00877 if( !isset( $this->data[$this->row][$this->current_field] ) ) { 00878 $this->data[$this->row][$this->current_field] = ''; 00879 } 00880 00881 $this->data[$this->row][$this->current_field] .= $cdata; 00882 } 00883 00890 function create( &$xmls ) { 00891 $table = $xmls->dict->TableName($this->parent->name); 00892 $table_field_count = count($this->parent->fields); 00893 $sql = array(); 00894 00895 // eliminate any columns that aren't in the table 00896 foreach( $this->data as $row ) { 00897 $table_fields = $this->parent->fields; 00898 $fields = array(); 00899 00900 foreach( $row as $field_id => $field_data ) { 00901 if( !array_key_exists( $field_id, $table_fields ) ) { 00902 if( is_numeric( $field_id ) ) { 00903 $field_id = reset( array_keys( $table_fields ) ); 00904 } else { 00905 continue; 00906 } 00907 } 00908 00909 $name = $table_fields[$field_id]['NAME']; 00910 00911 switch( $table_fields[$field_id]['TYPE'] ) { 00912 case 'C': 00913 case 'C2': 00914 case 'X': 00915 case 'X2': 00916 $fields[$name] = $xmls->db->qstr( $field_data ); 00917 break; 00918 case 'I': 00919 case 'I1': 00920 case 'I2': 00921 case 'I4': 00922 case 'I8': 00923 $fields[$name] = intval($field_data); 00924 break; 00925 default: 00926 $fields[$name] = $field_data; 00927 } 00928 00929 unset($table_fields[$field_id]); 00930 } 00931 00932 // check that at least 1 column is specified 00933 if( empty( $fields ) ) { 00934 continue; 00935 } 00936 00937 // check that no required columns are missing 00938 if( count( $fields ) < $table_field_count ) { 00939 foreach( $table_fields as $field ) { 00940 if (isset( $field['OPTS'] )) 00941 if( ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) { 00942 continue(2); 00943 } 00944 } 00945 } 00946 00947 $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')'; 00948 } 00949 00950 return $sql; 00951 } 00952 } 00953 00960 class dbQuerySet extends dbObject { 00961 00965 var $queries = array(); 00966 00970 var $query; 00971 00975 var $prefixKey = ''; 00976 00980 var $prefixMethod = 'AUTO'; 00981 00988 function dbQuerySet( &$parent, $attributes = NULL ) { 00989 $this->parent = $parent; 00990 00991 // Overrides the manual prefix key 00992 if( isset( $attributes['KEY'] ) ) { 00993 $this->prefixKey = $attributes['KEY']; 00994 } 00995 00996 $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : ''; 00997 00998 // Enables or disables automatic prefix prepending 00999 switch( $prefixMethod ) { 01000 case 'AUTO': 01001 $this->prefixMethod = 'AUTO'; 01002 break; 01003 case 'MANUAL': 01004 $this->prefixMethod = 'MANUAL'; 01005 break; 01006 case 'NONE': 01007 $this->prefixMethod = 'NONE'; 01008 break; 01009 } 01010 } 01011 01018 function _tag_open( &$parser, $tag, $attributes ) { 01019 $this->currentElement = strtoupper( $tag ); 01020 01021 switch( $this->currentElement ) { 01022 case 'QUERY': 01023 // Create a new query in a SQL queryset. 01024 // Ignore this query set if a platform is specified and it's different than the 01025 // current connection platform. 01026 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { 01027 $this->newQuery(); 01028 } else { 01029 $this->discardQuery(); 01030 } 01031 break; 01032 default: 01033 // print_r( array( $tag, $attributes ) ); 01034 } 01035 } 01036 01040 function _tag_cdata( &$parser, $cdata ) { 01041 switch( $this->currentElement ) { 01042 // Line of queryset SQL data 01043 case 'QUERY': 01044 $this->buildQuery( $cdata ); 01045 break; 01046 default: 01047 01048 } 01049 } 01050 01056 function _tag_close( &$parser, $tag ) { 01057 $this->currentElement = ''; 01058 01059 switch( strtoupper( $tag ) ) { 01060 case 'QUERY': 01061 // Add the finished query to the open query set. 01062 $this->addQuery(); 01063 break; 01064 case 'SQL': 01065 $this->parent->addSQL( $this->create( $this->parent ) ); 01066 xml_set_object( $parser, $this->parent ); 01067 $this->destroy(); 01068 break; 01069 default: 01070 01071 } 01072 } 01073 01079 function newQuery() { 01080 $this->query = ''; 01081 01082 return TRUE; 01083 } 01084 01090 function discardQuery() { 01091 unset( $this->query ); 01092 01093 return TRUE; 01094 } 01095 01102 function buildQuery( $sql = NULL ) { 01103 if( !isset( $this->query ) OR empty( $sql ) ) { 01104 return FALSE; 01105 } 01106 01107 $this->query .= $sql; 01108 01109 return $this->query; 01110 } 01111 01117 function addQuery() { 01118 if( !isset( $this->query ) ) { 01119 return FALSE; 01120 } 01121 01122 $this->queries[] = $return = trim($this->query); 01123 01124 unset( $this->query ); 01125 01126 return $return; 01127 } 01128 01135 function create( &$xmls ) { 01136 foreach( $this->queries as $id => $query ) { 01137 switch( $this->prefixMethod ) { 01138 case 'AUTO': 01139 // Enable auto prefix replacement 01140 01141 // Process object prefix. 01142 // Evaluate SQL statements to prepend prefix to objects 01143 $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); 01144 $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); 01145 $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); 01146 01147 // SELECT statements aren't working yet 01148 #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data ); 01149 01150 case 'MANUAL': 01151 // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX. 01152 // If prefixKey is not set, we use the default constant XMLS_PREFIX 01153 if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) { 01154 // Enable prefix override 01155 $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query ); 01156 } else { 01157 // Use default replacement 01158 $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query ); 01159 } 01160 } 01161 01162 $this->queries[$id] = trim( $query ); 01163 } 01164 01165 // Return the query set array 01166 return $this->queries; 01167 } 01168 01177 function prefixQuery( $regex, $query, $prefix = NULL ) { 01178 if( !isset( $prefix ) ) { 01179 return $query; 01180 } 01181 01182 if( preg_match( $regex, $query, $match ) ) { 01183 $preamble = $match[1]; 01184 $postamble = $match[5]; 01185 $objectList = explode( ',', $match[3] ); 01186 // $prefix = $prefix . '_'; 01187 01188 $prefixedList = ''; 01189 01190 foreach( $objectList as $object ) { 01191 if( $prefixedList !== '' ) { 01192 $prefixedList .= ', '; 01193 } 01194 01195 $prefixedList .= $prefix . trim( $object ); 01196 } 01197 01198 $query = $preamble . ' ' . $prefixedList . ' ' . $postamble; 01199 } 01200 01201 return $query; 01202 } 01203 } 01204 01218 class adoSchema { 01219 01224 var $sqlArray; 01225 01230 var $db; 01231 01236 var $dict; 01237 01242 var $currentElement = ''; 01243 01248 var $upgrade = ''; 01249 01254 var $objectPrefix = ''; 01255 01260 var $mgq; 01261 01266 var $debug; 01267 01272 var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/'; 01273 01278 var $schemaVersion; 01279 01283 var $success; 01284 01288 var $executeInline; 01289 01293 var $continueOnError; 01294 01304 function adoSchema( $db ) { 01305 // Initialize the environment 01306 $this->mgq = get_magic_quotes_runtime(); 01307 ini_set("magic_quotes_runtime", 0); 01308 #set_magic_quotes_runtime(0); 01309 01310 $this->db = $db; 01311 $this->debug = $this->db->debug; 01312 $this->dict = NewDataDictionary( $this->db ); 01313 $this->sqlArray = array(); 01314 $this->schemaVersion = XMLS_SCHEMA_VERSION; 01315 $this->executeInline( XMLS_EXECUTE_INLINE ); 01316 $this->continueOnError( XMLS_CONTINUE_ON_ERROR ); 01317 $this->setUpgradeMethod(); 01318 } 01319 01336 function SetUpgradeMethod( $method = '' ) { 01337 if( !is_string( $method ) ) { 01338 return FALSE; 01339 } 01340 01341 $method = strtoupper( $method ); 01342 01343 // Handle the upgrade methods 01344 switch( $method ) { 01345 case 'ALTER': 01346 $this->upgrade = $method; 01347 break; 01348 case 'REPLACE': 01349 $this->upgrade = $method; 01350 break; 01351 case 'BEST': 01352 $this->upgrade = 'ALTER'; 01353 break; 01354 case 'NONE': 01355 $this->upgrade = 'NONE'; 01356 break; 01357 default: 01358 // Use default if no legitimate method is passed. 01359 $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD; 01360 } 01361 01362 return $this->upgrade; 01363 } 01364 01378 function ExecuteInline( $mode = NULL ) { 01379 if( is_bool( $mode ) ) { 01380 $this->executeInline = $mode; 01381 } 01382 01383 return $this->executeInline; 01384 } 01385 01399 function ContinueOnError( $mode = NULL ) { 01400 if( is_bool( $mode ) ) { 01401 $this->continueOnError = $mode; 01402 } 01403 01404 return $this->continueOnError; 01405 } 01406 01418 function ParseSchema( $filename, $returnSchema = FALSE ) { 01419 return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema ); 01420 } 01421 01435 function ParseSchemaFile( $filename, $returnSchema = FALSE ) { 01436 // Open the file 01437 if( !($fp = fopen( $filename, 'r' )) ) { 01438 // die( 'Unable to open file' ); 01439 return FALSE; 01440 } 01441 01442 // do version detection here 01443 if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) { 01444 return FALSE; 01445 } 01446 01447 if ( $returnSchema ) 01448 { 01449 $xmlstring = ''; 01450 while( $data = fread( $fp, 100000 ) ) { 01451 $xmlstring .= $data; 01452 } 01453 return $xmlstring; 01454 } 01455 01456 $this->success = 2; 01457 01458 $xmlParser = $this->create_parser(); 01459 01460 // Process the file 01461 while( $data = fread( $fp, 4096 ) ) { 01462 if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) { 01463 die( sprintf( 01464 "XML error: %s at line %d", 01465 xml_error_string( xml_get_error_code( $xmlParser) ), 01466 xml_get_current_line_number( $xmlParser) 01467 ) ); 01468 } 01469 } 01470 01471 xml_parser_free( $xmlParser ); 01472 01473 return $this->sqlArray; 01474 } 01475 01487 function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) { 01488 if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) { 01489 return FALSE; 01490 } 01491 01492 // do version detection here 01493 if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) { 01494 return FALSE; 01495 } 01496 01497 if ( $returnSchema ) 01498 { 01499 return $xmlstring; 01500 } 01501 01502 $this->success = 2; 01503 01504 $xmlParser = $this->create_parser(); 01505 01506 if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) { 01507 die( sprintf( 01508 "XML error: %s at line %d", 01509 xml_error_string( xml_get_error_code( $xmlParser) ), 01510 xml_get_current_line_number( $xmlParser) 01511 ) ); 01512 } 01513 01514 xml_parser_free( $xmlParser ); 01515 01516 return $this->sqlArray; 01517 } 01518 01530 function RemoveSchema( $filename, $returnSchema = FALSE ) { 01531 return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema ); 01532 } 01533 01545 function RemoveSchemaString( $schema, $returnSchema = FALSE ) { 01546 01547 // grab current version 01548 if( !( $version = $this->SchemaStringVersion( $schema ) ) ) { 01549 return FALSE; 01550 } 01551 01552 return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema ); 01553 } 01554 01568 function ExecuteSchema( $sqlArray = NULL, $continueOnErr = NULL ) { 01569 if( !is_bool( $continueOnErr ) ) { 01570 $continueOnErr = $this->ContinueOnError(); 01571 } 01572 01573 if( !isset( $sqlArray ) ) { 01574 $sqlArray = $this->sqlArray; 01575 } 01576 01577 if( !is_array( $sqlArray ) ) { 01578 $this->success = 0; 01579 } else { 01580 $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr ); 01581 } 01582 01583 return $this->success; 01584 } 01585 01595 function PrintSQL( $format = 'NONE' ) { 01596 $sqlArray = null; 01597 return $this->getSQL( $format, $sqlArray ); 01598 } 01599 01609 function SaveSQL( $filename = './schema.sql' ) { 01610 01611 if( !isset( $sqlArray ) ) { 01612 $sqlArray = $this->sqlArray; 01613 } 01614 if( !isset( $sqlArray ) ) { 01615 return FALSE; 01616 } 01617 01618 $fp = fopen( $filename, "w" ); 01619 01620 foreach( $sqlArray as $key => $query ) { 01621 fwrite( $fp, $query . ";\n" ); 01622 } 01623 fclose( $fp ); 01624 } 01625 01633 function create_parser() { 01634 // Create the parser 01635 $xmlParser = xml_parser_create(); 01636 xml_set_object( $xmlParser, $this ); 01637 01638 // Initialize the XML callback functions 01639 xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' ); 01640 xml_set_character_data_handler( $xmlParser, '_tag_cdata' ); 01641 01642 return $xmlParser; 01643 } 01644 01650 function _tag_open( &$parser, $tag, $attributes ) { 01651 switch( strtoupper( $tag ) ) { 01652 case 'TABLE': 01653 $this->obj = new dbTable( $this, $attributes ); 01654 xml_set_object( $parser, $this->obj ); 01655 break; 01656 case 'SQL': 01657 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { 01658 $this->obj = new dbQuerySet( $this, $attributes ); 01659 xml_set_object( $parser, $this->obj ); 01660 } 01661 break; 01662 default: 01663 // print_r( array( $tag, $attributes ) ); 01664 } 01665 01666 } 01667 01673 function _tag_cdata( &$parser, $cdata ) { 01674 } 01675 01682 function _tag_close( &$parser, $tag ) { 01683 01684 } 01685 01702 function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) { 01703 01704 // grab current version 01705 if( !( $version = $this->SchemaStringVersion( $schema ) ) ) { 01706 return FALSE; 01707 } 01708 01709 if( !isset ($newVersion) ) { 01710 $newVersion = $this->schemaVersion; 01711 } 01712 01713 if( $version == $newVersion ) { 01714 $result = $schema; 01715 } else { 01716 $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion); 01717 } 01718 01719 if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) { 01720 fwrite( $fp, $result ); 01721 fclose( $fp ); 01722 } 01723 01724 return $result; 01725 } 01726 01727 // compat for pre-4.3 - jlim 01728 function _file_get_contents($path) 01729 { 01730 if (function_exists('file_get_contents')) return file_get_contents($path); 01731 return join('',file($path)); 01732 } 01733 01750 function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) { 01751 01752 // grab current version 01753 if( !( $version = $this->SchemaFileVersion( $filename ) ) ) { 01754 return FALSE; 01755 } 01756 01757 if( !isset ($newVersion) ) { 01758 $newVersion = $this->schemaVersion; 01759 } 01760 01761 if( $version == $newVersion ) { 01762 $result = _file_get_contents( $filename ); 01763 01764 // remove unicode BOM if present 01765 if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) { 01766 $result = substr( $result, 3 ); 01767 } 01768 } else { 01769 $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' ); 01770 } 01771 01772 if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) { 01773 fwrite( $fp, $result ); 01774 fclose( $fp ); 01775 } 01776 01777 return $result; 01778 } 01779 01780 function TransformSchema( $schema, $xsl, $schematype='string' ) 01781 { 01782 // Fail if XSLT extension is not available 01783 if( ! function_exists( 'xslt_create' ) ) { 01784 return FALSE; 01785 } 01786 01787 $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl'; 01788 01789 // look for xsl 01790 if( !is_readable( $xsl_file ) ) { 01791 return FALSE; 01792 } 01793 01794 switch( $schematype ) 01795 { 01796 case 'file': 01797 if( !is_readable( $schema ) ) { 01798 return FALSE; 01799 } 01800 01801 $schema = _file_get_contents( $schema ); 01802 break; 01803 case 'string': 01804 default: 01805 if( !is_string( $schema ) ) { 01806 return FALSE; 01807 } 01808 } 01809 01810 $arguments = array ( 01811 '/_xml' => $schema, 01812 '/_xsl' => _file_get_contents( $xsl_file ) 01813 ); 01814 01815 // create an XSLT processor 01816 $xh = xslt_create (); 01817 01818 // set error handler 01819 xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler')); 01820 01821 // process the schema 01822 $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); 01823 01824 xslt_free ($xh); 01825 01826 return $result; 01827 } 01828 01839 function xslt_error_handler( $parser, $errno, $level, $fields ) { 01840 if( is_array( $fields ) ) { 01841 $msg = array( 01842 'Message Type' => ucfirst( $fields['msgtype'] ), 01843 'Message Code' => $fields['code'], 01844 'Message' => $fields['msg'], 01845 'Error Number' => $errno, 01846 'Level' => $level 01847 ); 01848 01849 switch( $fields['URI'] ) { 01850 case 'arg:/_xml': 01851 $msg['Input'] = 'XML'; 01852 break; 01853 case 'arg:/_xsl': 01854 $msg['Input'] = 'XSL'; 01855 break; 01856 default: 01857 $msg['Input'] = $fields['URI']; 01858 } 01859 01860 $msg['Line'] = $fields['line']; 01861 } else { 01862 $msg = array( 01863 'Message Type' => 'Error', 01864 'Error Number' => $errno, 01865 'Level' => $level, 01866 'Fields' => var_export( $fields, TRUE ) 01867 ); 01868 } 01869 01870 $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n" 01871 . '<table>' . "\n"; 01872 01873 foreach( $msg as $label => $details ) { 01874 $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n"; 01875 } 01876 01877 $error_details .= '</table>'; 01878 01879 trigger_error( $error_details, E_USER_ERROR ); 01880 } 01881 01891 function SchemaFileVersion( $filename ) { 01892 // Open the file 01893 if( !($fp = fopen( $filename, 'r' )) ) { 01894 // die( 'Unable to open file' ); 01895 return FALSE; 01896 } 01897 01898 // Process the file 01899 while( $data = fread( $fp, 4096 ) ) { 01900 if( preg_match( $this->versionRegex, $data, $matches ) ) { 01901 return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION; 01902 } 01903 } 01904 01905 return FALSE; 01906 } 01907 01917 function SchemaStringVersion( $xmlstring ) { 01918 if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) { 01919 return FALSE; 01920 } 01921 01922 if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) { 01923 return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION; 01924 } 01925 01926 return FALSE; 01927 } 01928 01939 function ExtractSchema( $data = FALSE ) { 01940 $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM ); 01941 01942 $schema = '<?xml version="1.0"?>' . "\n" 01943 . '<schema version="' . $this->schemaVersion . '">' . "\n"; 01944 01945 if( is_array( $tables = $this->db->MetaTables( 'TABLES' ) ) ) { 01946 foreach( $tables as $table ) { 01947 $schema .= ' <table name="' . $table . '">' . "\n"; 01948 01949 // grab details from database 01950 $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE 1=1' ); 01951 $fields = $this->db->MetaColumns( $table ); 01952 $indexes = $this->db->MetaIndexes( $table ); 01953 01954 if( is_array( $fields ) ) { 01955 foreach( $fields as $details ) { 01956 $extra = ''; 01957 $content = array(); 01958 01959 if( $details->max_length > 0 ) { 01960 $extra .= ' size="' . $details->max_length . '"'; 01961 } 01962 01963 if( $details->primary_key ) { 01964 $content[] = '<KEY/>'; 01965 } elseif( $details->not_null ) { 01966 $content[] = '<NOTNULL/>'; 01967 } 01968 01969 if( $details->has_default ) { 01970 $content[] = '<DEFAULT value="' . $details->default_value . '"/>'; 01971 } 01972 01973 if( $details->auto_increment ) { 01974 $content[] = '<AUTOINCREMENT/>'; 01975 } 01976 01977 // this stops the creation of 'R' columns, 01978 // AUTOINCREMENT is used to create auto columns 01979 $details->primary_key = 0; 01980 $type = $rs->MetaType( $details ); 01981 01982 $schema .= ' <field name="' . $details->name . '" type="' . $type . '"' . $extra . '>'; 01983 01984 if( !empty( $content ) ) { 01985 $schema .= "\n " . implode( "\n ", $content ) . "\n "; 01986 } 01987 01988 $schema .= '</field>' . "\n"; 01989 } 01990 } 01991 01992 if( is_array( $indexes ) ) { 01993 foreach( $indexes as $index => $details ) { 01994 $schema .= ' <index name="' . $index . '">' . "\n"; 01995 01996 if( $details['unique'] ) { 01997 $schema .= ' <UNIQUE/>' . "\n"; 01998 } 01999 02000 foreach( $details['columns'] as $column ) { 02001 $schema .= ' <col>' . $column . '</col>' . "\n"; 02002 } 02003 02004 $schema .= ' </index>' . "\n"; 02005 } 02006 } 02007 02008 if( $data ) { 02009 $rs = $this->db->Execute( 'SELECT * FROM ' . $table ); 02010 02011 if( is_object( $rs ) ) { 02012 $schema .= ' <data>' . "\n"; 02013 02014 while( $row = $rs->FetchRow() ) { 02015 foreach( $row as $key => $val ) { 02016 $row[$key] = htmlentities($val); 02017 } 02018 02019 $schema .= ' <row><f>' . implode( '</f><f>', $row ) . '</f></row>' . "\n"; 02020 } 02021 02022 $schema .= ' </data>' . "\n"; 02023 } 02024 } 02025 02026 $schema .= ' </table>' . "\n"; 02027 } 02028 } 02029 02030 $this->db->SetFetchMode( $old_mode ); 02031 02032 $schema .= '</schema>'; 02033 return $schema; 02034 } 02035 02046 function SetPrefix( $prefix = '', $underscore = TRUE ) { 02047 switch( TRUE ) { 02048 // clear prefix 02049 case empty( $prefix ): 02050 logMsg( 'Cleared prefix' ); 02051 $this->objectPrefix = ''; 02052 return TRUE; 02053 // prefix too long 02054 case strlen( $prefix ) > XMLS_PREFIX_MAXLEN: 02055 // prefix contains invalid characters 02056 case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ): 02057 logMsg( 'Invalid prefix: ' . $prefix ); 02058 return FALSE; 02059 } 02060 02061 if( $underscore AND substr( $prefix, -1 ) != '_' ) { 02062 $prefix .= '_'; 02063 } 02064 02065 // prefix valid 02066 logMsg( 'Set prefix: ' . $prefix ); 02067 $this->objectPrefix = $prefix; 02068 return TRUE; 02069 } 02070 02079 function prefix( $name = '' ) { 02080 // if prefix is set 02081 if( !empty( $this->objectPrefix ) ) { 02082 // Prepend the object prefix to the table name 02083 // prepend after quote if used 02084 return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name ); 02085 } 02086 02087 // No prefix set. Use name provided. 02088 return $name; 02089 } 02090 02099 function supportedPlatform( $platform = NULL ) { 02100 $regex = '/^(\w*\|)*' . $this->db->databaseType . '(\|\w*)*$/'; 02101 02102 if( !isset( $platform ) OR preg_match( $regex, $platform ) ) { 02103 logMsg( "Platform $platform is supported" ); 02104 return TRUE; 02105 } else { 02106 logMsg( "Platform $platform is NOT supported" ); 02107 return FALSE; 02108 } 02109 } 02110 02116 function clearSQL() { 02117 $this->sqlArray = array(); 02118 } 02119 02128 function addSQL( $sql = NULL ) { 02129 if( is_array( $sql ) ) { 02130 foreach( $sql as $line ) { 02131 $this->addSQL( $line ); 02132 } 02133 02134 return TRUE; 02135 } 02136 02137 if( is_string( $sql ) ) { 02138 $this->sqlArray[] = $sql; 02139 02140 // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL. 02141 if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) { 02142 $saved = $this->db->debug; 02143 $this->db->debug = $this->debug; 02144 $ok = $this->db->Execute( $sql ); 02145 $this->db->debug = $saved; 02146 02147 if( !$ok ) { 02148 if( $this->debug ) { 02149 ADOConnection::outp( $this->db->ErrorMsg() ); 02150 } 02151 02152 $this->success = 1; 02153 } 02154 } 02155 02156 return TRUE; 02157 } 02158 02159 return FALSE; 02160 } 02161 02170 function getSQL( $format = NULL, $sqlArray = NULL ) { 02171 if( !is_array( $sqlArray ) ) { 02172 $sqlArray = $this->sqlArray; 02173 } 02174 02175 if( !is_array( $sqlArray ) ) { 02176 return FALSE; 02177 } 02178 02179 switch( strtolower( $format ) ) { 02180 case 'string': 02181 case 'text': 02182 return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : ''; 02183 case'html': 02184 return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : ''; 02185 } 02186 02187 return $this->sqlArray; 02188 } 02189 02196 function Destroy() { 02197 ini_set("magic_quotes_runtime", $this->mgq ); 02198 #set_magic_quotes_runtime( $this->mgq ); 02199 unset( $this ); 02200 } 02201 } 02202 02208 function logMsg( $msg, $title = NULL, $force = FALSE ) { 02209 if( XMLS_DEBUG or $force ) { 02210 echo '<pre>'; 02211 02212 if( isset( $title ) ) { 02213 echo '<h3>' . htmlentities( $title ) . '</h3>'; 02214 } 02215 02216 if( is_object( $this ) ) { 02217 echo '[' . get_class( $this ) . '] '; 02218 } 02219 02220 print_r( $msg ); 02221 02222 echo '</pre>'; 02223 } 02224 } 02225 ?>