00001 <?php
00002
00003
00004
00005
00006
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() {
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
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
00292 $this->addFieldOpt( $this->current_field, $this->currentElement );
00293 break;
00294 case 'DEFAULT':
00295
00296
00297
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
00307 $this->addFieldOpt( $this->current_field, $this->currentElement );
00308 break;
00309 default:
00310
00311 }
00312 }
00313
00319 function _tag_cdata( &$parser, $cdata ) {
00320 switch( $this->currentElement ) {
00321
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
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
00417 $this->current_field = $field_id;
00418
00419
00420 $this->fields[$field_id]['NAME'] = $name;
00421
00422
00423 $this->fields[$field_id]['TYPE'] = $type;
00424
00425
00426 if( isset( $size ) ) {
00427 $this->fields[$field_id]['SIZE'] = $size;
00428 }
00429
00430
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
00451 } else {
00452 $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );
00453 }
00454 }
00455
00465 function addTableOpt( $opt ) {
00466 $this->opts[] = $opt;
00467
00468 return $this->opts;
00469 }
00470
00477 function create( &$xmls ) {
00478 $sql = array();
00479
00480
00481 if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
00482 foreach( $legacy_indexes as $index => $index_details ) {
00483 $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
00484 }
00485 }
00486
00487
00488 foreach( $this->drop_field as $field ) {
00489 unset( $this->fields[$field] );
00490 }
00491
00492
00493 if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
00494
00495 if( $this->drop_table ) {
00496 $sql[] = $xmls->dict->DropTableSQL( $this->name );
00497
00498 return $sql;
00499 }
00500
00501
00502 foreach( $legacy_fields as $field_id => $field ) {
00503 if( !isset( $this->fields[$field_id] ) ) {
00504 $sql[] = $xmls->dict->DropColumnSQL( $this->name, '`'.$field->name.'`' );
00505 }
00506 }
00507
00508 } else {
00509 if( $this->drop_table ) {
00510 return $sql;
00511 }
00512
00513 $legacy_fields = array();
00514 }
00515
00516
00517 $fldarray = array();
00518
00519 foreach( $this->fields as $field_id => $finfo ) {
00520
00521 if( !isset( $finfo['SIZE'] ) ) {
00522 $finfo['SIZE'] = '';
00523 }
00524
00525
00526 $fldarray[$field_id] = array(
00527 'NAME' => $finfo['NAME'],
00528 'TYPE' => $finfo['TYPE'],
00529 'SIZE' => $finfo['SIZE']
00530 );
00531
00532
00533 if( isset( $finfo['OPTS'] ) ) {
00534 foreach( $finfo['OPTS'] as $opt ) {
00535
00536 if( is_array( $opt ) ) {
00537 $key = key( $opt );
00538 $value = $opt[key( $opt )];
00539 @$fldarray[$field_id][$key] .= $value;
00540
00541 } else {
00542 $fldarray[$field_id][$opt] = $opt;
00543 }
00544 }
00545 }
00546 }
00547
00548 if( empty( $legacy_fields ) ) {
00549
00550 $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
00551 logMsg( end( $sql ), 'Generated CreateTableSQL' );
00552 } else {
00553
00554 logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
00555 switch( $xmls->upgrade ) {
00556
00557 case 'ALTER':
00558 logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
00559 $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );
00560 break;
00561 case 'REPLACE':
00562 logMsg( 'Doing upgrade REPLACE (testing)' );
00563 $sql[] = $xmls->dict->DropTableSQL( $this->name );
00564 $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
00565 break;
00566
00567 default:
00568 return array();
00569 }
00570 }
00571
00572 foreach( $this->indexes as $index ) {
00573 $sql[] = $index->create( $xmls );
00574 }
00575
00576 if( isset( $this->data ) ) {
00577 $sql[] = $this->data->create( $xmls );
00578 }
00579
00580 return $sql;
00581 }
00582
00586 function drop() {
00587 if( isset( $this->current_field ) ) {
00588
00589 logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );
00590
00591 $this->drop_field[$this->current_field] = $this->current_field;
00592 } else {
00593
00594 logMsg( "Dropping table '{$this->name}'" );
00595
00596 $this->drop_table = TRUE;
00597 }
00598 }
00599 }
00600
00612 class dbIndex extends dbObject {
00613
00617 var $name;
00618
00622 var $opts = array();
00623
00627 var $columns = array();
00628
00633 var $drop = FALSE;
00634
00643 function dbIndex( &$parent, $attributes = NULL ) {
00644 $this->parent =& $parent;
00645
00646 $this->name = $this->prefix ($attributes['NAME']);
00647 }
00648
00657 function _tag_open( &$parser, $tag, $attributes ) {
00658 $this->currentElement = strtoupper( $tag );
00659
00660 switch( $this->currentElement ) {
00661 case 'DROP':
00662 $this->drop();
00663 break;
00664 case 'CLUSTERED':
00665 case 'BITMAP':
00666 case 'UNIQUE':
00667 case 'FULLTEXT':
00668 case 'HASH':
00669
00670 $this->addIndexOpt( $this->currentElement );
00671 break;
00672 default:
00673
00674 }
00675 }
00676
00684 function _tag_cdata( &$parser, $cdata ) {
00685 switch( $this->currentElement ) {
00686
00687 case 'COL':
00688 $this->addField( $cdata );
00689 break;
00690 default:
00691
00692 }
00693 }
00694
00700 function _tag_close( &$parser, $tag ) {
00701 $this->currentElement = '';
00702
00703 switch( strtoupper( $tag ) ) {
00704 case 'INDEX':
00705 xml_set_object( $parser, $this->parent );
00706 break;
00707 }
00708 }
00709
00716 function addField( $name ) {
00717 $this->columns[$this->FieldID( $name )] = $name;
00718
00719
00720 return $this->columns;
00721 }
00722
00729 function addIndexOpt( $opt ) {
00730 $this->opts[] = $opt;
00731
00732
00733 return $this->opts;
00734 }
00735
00742 function create( &$xmls ) {
00743 if( $this->drop ) {
00744 return NULL;
00745 }
00746
00747
00748 foreach( $this->columns as $id => $col ) {
00749 if( !isset( $this->parent->fields[$id] ) ) {
00750 unset( $this->columns[$id] );
00751 }
00752 }
00753
00754 return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
00755 }
00756
00760 function drop() {
00761 $this->drop = TRUE;
00762 }
00763 }
00764
00773 class dbData extends dbObject {
00774
00775 var $data = array();
00776
00777 var $row;
00778
00787 function dbData( &$parent, $attributes = NULL ) {
00788 $this->parent =& $parent;
00789 }
00790
00799 function _tag_open( &$parser, $tag, $attributes ) {
00800 $this->currentElement = strtoupper( $tag );
00801
00802 switch( $this->currentElement ) {
00803 case 'ROW':
00804 $this->row = count( $this->data );
00805 $this->data[$this->row] = array();
00806 break;
00807 case 'F':
00808 $this->addField($attributes);
00809 default:
00810
00811 }
00812 }
00813
00821 function _tag_cdata( &$parser, $cdata ) {
00822 switch( $this->currentElement ) {
00823
00824 case 'F':
00825 $this->addData( $cdata );
00826 break;
00827 default:
00828
00829 }
00830 }
00831
00837 function _tag_close( &$parser, $tag ) {
00838 $this->currentElement = '';
00839
00840 switch( strtoupper( $tag ) ) {
00841 case 'DATA':
00842 xml_set_object( $parser, $this->parent );
00843 break;
00844 }
00845 }
00846
00853 function addField( $attributes ) {
00854 if( isset( $attributes['NAME'] ) ) {
00855 $name = $attributes['NAME'];
00856 } else {
00857 $name = count($this->data[$this->row]);
00858 }
00859
00860
00861 $this->current_field = $this->FieldID( $name );
00862 }
00863
00870 function addData( $cdata ) {
00871 if( !isset( $this->data[$this->row] ) ) {
00872 $this->data[$this->row] = array();
00873 }
00874
00875 if( !isset( $this->data[$this->row][$this->current_field] ) ) {
00876 $this->data[$this->row][$this->current_field] = '';
00877 }
00878
00879 $this->data[$this->row][$this->current_field] .= $cdata;
00880 }
00881
00888 function create( &$xmls ) {
00889 $table = $xmls->dict->TableName($this->parent->name);
00890 $table_field_count = count($this->parent->fields);
00891 $sql = array();
00892
00893
00894 foreach( $this->data as $row ) {
00895 $table_fields = $this->parent->fields;
00896 $fields = array();
00897
00898 foreach( $row as $field_id => $field_data ) {
00899 if( !array_key_exists( $field_id, $table_fields ) ) {
00900 if( is_numeric( $field_id ) ) {
00901 $field_id = reset( array_keys( $table_fields ) );
00902 } else {
00903 continue;
00904 }
00905 }
00906
00907 $name = $table_fields[$field_id]['NAME'];
00908
00909 switch( $table_fields[$field_id]['TYPE'] ) {
00910 case 'C':
00911 case 'C2':
00912 case 'X':
00913 case 'X2':
00914 $fields[$name] = $xmls->db->qstr( $field_data );
00915 break;
00916 case 'I':
00917 case 'I1':
00918 case 'I2':
00919 case 'I4':
00920 case 'I8':
00921 $fields[$name] = intval($field_data);
00922 break;
00923 default:
00924 $fields[$name] = $field_data;
00925 }
00926
00927 unset($table_fields[$field_id]);
00928 }
00929
00930
00931 if( empty( $fields ) ) {
00932 continue;
00933 }
00934
00935
00936 if( count( $fields ) < $table_field_count ) {
00937 foreach( $table_fields as $field ) {
00938 if (isset( $field['OPTS'] ))
00939 if( ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {
00940 continue(2);
00941 }
00942 }
00943 }
00944
00945 $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
00946 }
00947
00948 return $sql;
00949 }
00950 }
00951
00958 class dbQuerySet extends dbObject {
00959
00963 var $queries = array();
00964
00968 var $query;
00969
00973 var $prefixKey = '';
00974
00978 var $prefixMethod = 'AUTO';
00979
00986 function dbQuerySet( &$parent, $attributes = NULL ) {
00987 $this->parent =& $parent;
00988
00989
00990 if( isset( $attributes['KEY'] ) ) {
00991 $this->prefixKey = $attributes['KEY'];
00992 }
00993
00994 $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';
00995
00996
00997 switch( $prefixMethod ) {
00998 case 'AUTO':
00999 $this->prefixMethod = 'AUTO';
01000 break;
01001 case 'MANUAL':
01002 $this->prefixMethod = 'MANUAL';
01003 break;
01004 case 'NONE':
01005 $this->prefixMethod = 'NONE';
01006 break;
01007 }
01008 }
01009
01016 function _tag_open( &$parser, $tag, $attributes ) {
01017 $this->currentElement = strtoupper( $tag );
01018
01019 switch( $this->currentElement ) {
01020 case 'QUERY':
01021
01022
01023
01024 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
01025 $this->newQuery();
01026 } else {
01027 $this->discardQuery();
01028 }
01029 break;
01030 default:
01031
01032 }
01033 }
01034
01038 function _tag_cdata( &$parser, $cdata ) {
01039 switch( $this->currentElement ) {
01040
01041 case 'QUERY':
01042 $this->buildQuery( $cdata );
01043 break;
01044 default:
01045
01046 }
01047 }
01048
01054 function _tag_close( &$parser, $tag ) {
01055 $this->currentElement = '';
01056
01057 switch( strtoupper( $tag ) ) {
01058 case 'QUERY':
01059
01060 $this->addQuery();
01061 break;
01062 case 'SQL':
01063 $this->parent->addSQL( $this->create( $this->parent ) );
01064 xml_set_object( $parser, $this->parent );
01065 $this->destroy();
01066 break;
01067 default:
01068
01069 }
01070 }
01071
01077 function newQuery() {
01078 $this->query = '';
01079
01080 return TRUE;
01081 }
01082
01088 function discardQuery() {
01089 unset( $this->query );
01090
01091 return TRUE;
01092 }
01093
01100 function buildQuery( $sql = NULL ) {
01101 if( !isset( $this->query ) OR empty( $sql ) ) {
01102 return FALSE;
01103 }
01104
01105 $this->query .= $sql;
01106
01107 return $this->query;
01108 }
01109
01115 function addQuery() {
01116 if( !isset( $this->query ) ) {
01117 return FALSE;
01118 }
01119
01120 $this->queries[] = $return = trim($this->query);
01121
01122 unset( $this->query );
01123
01124 return $return;
01125 }
01126
01133 function create( &$xmls ) {
01134 foreach( $this->queries as $id => $query ) {
01135 switch( $this->prefixMethod ) {
01136 case 'AUTO':
01137
01138
01139
01140
01141 $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
01142 $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
01143 $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
01144
01145
01146 #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );
01147
01148 case 'MANUAL':
01149
01150
01151 if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {
01152
01153 $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );
01154 } else {
01155
01156 $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );
01157 }
01158 }
01159
01160 $this->queries[$id] = trim( $query );
01161 }
01162
01163
01164 return $this->queries;
01165 }
01166
01175 function prefixQuery( $regex, $query, $prefix = NULL ) {
01176 if( !isset( $prefix ) ) {
01177 return $query;
01178 }
01179
01180 if( preg_match( $regex, $query, $match ) ) {
01181 $preamble = $match[1];
01182 $postamble = $match[5];
01183 $objectList = explode( ',', $match[3] );
01184
01185
01186 $prefixedList = '';
01187
01188 foreach( $objectList as $object ) {
01189 if( $prefixedList !== '' ) {
01190 $prefixedList .= ', ';
01191 }
01192
01193 $prefixedList .= $prefix . trim( $object );
01194 }
01195
01196 $query = $preamble . ' ' . $prefixedList . ' ' . $postamble;
01197 }
01198
01199 return $query;
01200 }
01201 }
01202
01216 class adoSchema {
01217
01222 var $sqlArray;
01223
01228 var $db;
01229
01234 var $dict;
01235
01240 var $currentElement = '';
01241
01246 var $upgrade = '';
01247
01252 var $objectPrefix = '';
01253
01258 var $mgq;
01259
01264 var $debug;
01265
01270 var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';
01271
01276 var $schemaVersion;
01277
01281 var $success;
01282
01286 var $executeInline;
01287
01291 var $continueOnError;
01292
01302 function adoSchema( &$db ) {
01303 // Initialize the environment
01304 $this->mgq = get_magic_quotes_runtime();
01305 set_magic_quotes_runtime(0);
01306
01307 $this->db =& $db;
01308 $this->debug = $this->db->debug;
01309 $this->dict = NewDataDictionary( $this->db );
01310 $this->sqlArray = array();
01311 $this->schemaVersion = XMLS_SCHEMA_VERSION;
01312 $this->executeInline( XMLS_EXECUTE_INLINE );
01313 $this->continueOnError( XMLS_CONTINUE_ON_ERROR );
01314 $this->setUpgradeMethod();
01315 }
01316
01333 function SetUpgradeMethod( $method = '' ) {
01334 if( !is_string( $method ) ) {
01335 return FALSE;
01336 }
01337
01338 $method = strtoupper( $method );
01339
01340 // Handle the upgrade methods
01341 switch( $method ) {
01342 case 'ALTER':
01343 $this->upgrade = $method;
01344 break;
01345 case 'REPLACE':
01346 $this->upgrade = $method;
01347 break;
01348 case 'BEST':
01349 $this->upgrade = 'ALTER';
01350 break;
01351 case 'NONE':
01352 $this->upgrade = 'NONE';
01353 break;
01354 default:
01355 // Use default if no legitimate method is passed.
01356 $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;
01357 }
01358
01359 return $this->upgrade;
01360 }
01361
01375 function ExecuteInline( $mode = NULL ) {
01376 if( is_bool( $mode ) ) {
01377 $this->executeInline = $mode;
01378 }
01379
01380 return $this->executeInline;
01381 }
01382
01396 function ContinueOnError( $mode = NULL ) {
01397 if( is_bool( $mode ) ) {
01398 $this->continueOnError = $mode;
01399 }
01400
01401 return $this->continueOnError;
01402 }
01403
01415 function ParseSchema( $filename, $returnSchema = FALSE ) {
01416 return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
01417 }
01418
01432 function ParseSchemaFile( $filename, $returnSchema = FALSE ) {
01433 // Open the file
01434 if( !($fp = fopen( $filename, 'r' )) ) {
01435 // die( 'Unable to open file' );
01436 return FALSE;
01437 }
01438
01439 // do version detection here
01440 if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {
01441 return FALSE;
01442 }
01443
01444 if ( $returnSchema )
01445 {
01446 $xmlstring = '';
01447 while( $data = fread( $fp, 100000 ) ) {
01448 $xmlstring .= $data;
01449 }
01450 return $xmlstring;
01451 }
01452
01453 $this->success = 2;
01454
01455 $xmlParser = $this->create_parser();
01456
01457 // Process the file
01458 while( $data = fread( $fp, 4096 ) ) {
01459 if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {
01460 die( sprintf(
01461 "XML error: %s at line %d",
01462 xml_error_string( xml_get_error_code( $xmlParser) ),
01463 xml_get_current_line_number( $xmlParser)
01464 ) );
01465 }
01466 }
01467
01468 xml_parser_free( $xmlParser );
01469
01470 return $this->sqlArray;
01471 }
01472
01484 function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {
01485 if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
01486 return FALSE;
01487 }
01488
01489 // do version detection here
01490 if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {
01491 return FALSE;
01492 }
01493
01494 if ( $returnSchema )
01495 {
01496 return $xmlstring;
01497 }
01498
01499 $this->success = 2;
01500
01501 $xmlParser = $this->create_parser();
01502
01503 if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {
01504 die( sprintf(
01505 "XML error: %s at line %d",
01506 xml_error_string( xml_get_error_code( $xmlParser) ),
01507 xml_get_current_line_number( $xmlParser)
01508 ) );
01509 }
01510
01511 xml_parser_free( $xmlParser );
01512
01513 return $this->sqlArray;
01514 }
01515
01527 function RemoveSchema( $filename, $returnSchema = FALSE ) {
01528 return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
01529 }
01530
01542 function RemoveSchemaString( $schema, $returnSchema = FALSE ) {
01543
01544 // grab current version
01545 if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
01546 return FALSE;
01547 }
01548
01549 return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );
01550 }
01551
01565 function ExecuteSchema( $sqlArray = NULL, $continueOnErr = NULL ) {
01566 if( !is_bool( $continueOnErr ) ) {
01567 $continueOnErr = $this->ContinueOnError();
01568 }
01569
01570 if( !isset( $sqlArray ) ) {
01571 $sqlArray = $this->sqlArray;
01572 }
01573
01574 if( !is_array( $sqlArray ) ) {
01575 $this->success = 0;
01576 } else {
01577 $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );
01578 }
01579
01580 return $this->success;
01581 }
01582
01592 function PrintSQL( $format = 'NONE' ) {
01593 $sqlArray = null;
01594 return $this->getSQL( $format, $sqlArray );
01595 }
01596
01606 function SaveSQL( $filename = './schema.sql' ) {
01607
01608 if( !isset( $sqlArray ) ) {
01609 $sqlArray = $this->sqlArray;
01610 }
01611 if( !isset( $sqlArray ) ) {
01612 return FALSE;
01613 }
01614
01615 $fp = fopen( $filename, "w" );
01616
01617 foreach( $sqlArray as $key => $query ) {
01618 fwrite( $fp, $query . ";\n" );
01619 }
01620 fclose( $fp );
01621 }
01622
01630 function &create_parser() {
01631 // Create the parser
01632 $xmlParser = xml_parser_create();
01633 xml_set_object( $xmlParser, $this );
01634
01635 // Initialize the XML callback functions
01636 xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );
01637 xml_set_character_data_handler( $xmlParser, '_tag_cdata' );
01638
01639 return $xmlParser;
01640 }
01641
01647 function _tag_open( &$parser, $tag, $attributes ) {
01648 switch( strtoupper( $tag ) ) {
01649 case 'TABLE':
01650 $this->obj = new dbTable( $this, $attributes );
01651 xml_set_object( $parser, $this->obj );
01652 break;
01653 case 'SQL':
01654 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
01655 $this->obj = new dbQuerySet( $this, $attributes );
01656 xml_set_object( $parser, $this->obj );
01657 }
01658 break;
01659 default:
01660 // print_r( array( $tag, $attributes ) );
01661 }
01662
01663 }
01664
01670 function _tag_cdata( &$parser, $cdata ) {
01671 }
01672
01679 function _tag_close( &$parser, $tag ) {
01680
01681 }
01682
01699 function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {
01700
01701 // grab current version
01702 if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
01703 return FALSE;
01704 }
01705
01706 if( !isset ($newVersion) ) {
01707 $newVersion = $this->schemaVersion;
01708 }
01709
01710 if( $version == $newVersion ) {
01711 $result = $schema;
01712 } else {
01713 $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);
01714 }
01715
01716 if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
01717 fwrite( $fp, $result );
01718 fclose( $fp );
01719 }
01720
01721 return $result;
01722 }
01723
01724 // compat for pre-4.3 - jlim
01725 function _file_get_contents($path)
01726 {
01727 if (function_exists('file_get_contents')) return file_get_contents($path);
01728 return join('',file($path));
01729 }
01730
01747 function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {
01748
01749 // grab current version
01750 if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {
01751 return FALSE;
01752 }
01753
01754 if( !isset ($newVersion) ) {
01755 $newVersion = $this->schemaVersion;
01756 }
01757
01758 if( $version == $newVersion ) {
01759 $result = _file_get_contents( $filename );
01760
01761 // remove unicode BOM if present
01762 if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {
01763 $result = substr( $result, 3 );
01764 }
01765 } else {
01766 $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );
01767 }
01768
01769 if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
01770 fwrite( $fp, $result );
01771 fclose( $fp );
01772 }
01773
01774 return $result;
01775 }
01776
01777 function TransformSchema( $schema, $xsl, $schematype='string' )
01778 {
01779 // Fail if XSLT extension is not available
01780 if( ! function_exists( 'xslt_create' ) ) {
01781 return FALSE;
01782 }
01783
01784 $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';
01785
01786 // look for xsl
01787 if( !is_readable( $xsl_file ) ) {
01788 return FALSE;
01789 }
01790
01791 switch( $schematype )
01792 {
01793 case 'file':
01794 if( !is_readable( $schema ) ) {
01795 return FALSE;
01796 }
01797
01798 $schema = _file_get_contents( $schema );
01799 break;
01800 case 'string':
01801 default:
01802 if( !is_string( $schema ) ) {
01803 return FALSE;
01804 }
01805 }
01806
01807 $arguments = array (
01808 '/_xml' => $schema,
01809 '/_xsl' => _file_get_contents( $xsl_file )
01810 );
01811
01812 // create an XSLT processor
01813 $xh = xslt_create ();
01814
01815 // set error handler
01816 xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));
01817
01818 // process the schema
01819 $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments);
01820
01821 xslt_free ($xh);
01822
01823 return $result;
01824 }
01825
01836 function xslt_error_handler( $parser, $errno, $level, $fields ) {
01837 if( is_array( $fields ) ) {
01838 $msg = array(
01839 'Message Type' => ucfirst( $fields['msgtype'] ),
01840 'Message Code' => $fields['code'],
01841 'Message' => $fields['msg'],
01842 'Error Number' => $errno,
01843 'Level' => $level
01844 );
01845
01846 switch( $fields['URI'] ) {
01847 case 'arg:/_xml':
01848 $msg['Input'] = 'XML';
01849 break;
01850 case 'arg:/_xsl':
01851 $msg['Input'] = 'XSL';
01852 break;
01853 default:
01854 $msg['Input'] = $fields['URI'];
01855 }
01856
01857 $msg['Line'] = $fields['line'];
01858 } else {
01859 $msg = array(
01860 'Message Type' => 'Error',
01861 'Error Number' => $errno,
01862 'Level' => $level,
01863 'Fields' => var_export( $fields, TRUE )
01864 );
01865 }
01866
01867 $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"
01868 . '<table>' . "\n";
01869
01870 foreach( $msg as $label => $details ) {
01871 $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";
01872 }
01873
01874 $error_details .= '</table>';
01875
01876 trigger_error( $error_details, E_USER_ERROR );
01877 }
01878
01888 function SchemaFileVersion( $filename ) {
01889 // Open the file
01890 if( !($fp = fopen( $filename, 'r' )) ) {
01891 // die( 'Unable to open file' );
01892 return FALSE;
01893 }
01894
01895 // Process the file
01896 while( $data = fread( $fp, 4096 ) ) {
01897 if( preg_match( $this->versionRegex, $data, $matches ) ) {
01898 return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
01899 }
01900 }
01901
01902 return FALSE;
01903 }
01904
01914 function SchemaStringVersion( $xmlstring ) {
01915 if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
01916 return FALSE;
01917 }
01918
01919 if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {
01920 return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
01921 }
01922
01923 return FALSE;
01924 }
01925
01936 function ExtractSchema( $data = FALSE ) {
01937 $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );
01938
01939 $schema = '<?xml version="1.0"?>' . "\n"
01940 . '<schema version="' . $this->schemaVersion . '">' . "\n";
01941
01942 if( is_array( $tables = $this->db->MetaTables( 'TABLES' ) ) ) {
01943 foreach( $tables as $table ) {
01944 $schema .= ' <table name="' . $table . '">' . "\n";
01945
01946 // grab details from database
01947 $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE 1=1' );
01948 $fields = $this->db->MetaColumns( $table );
01949 $indexes = $this->db->MetaIndexes( $table );
01950
01951 if( is_array( $fields ) ) {
01952 foreach( $fields as $details ) {
01953 $extra = '';
01954 $content = array();
01955
01956 if( $details->max_length > 0 ) {
01957 $extra .= ' size="' . $details->max_length . '"';
01958 }
01959
01960 if( $details->primary_key ) {
01961 $content[] = '<KEY/>';
01962 } elseif( $details->not_null ) {
01963 $content[] = '<NOTNULL/>';
01964 }
01965
01966 if( $details->has_default ) {
01967 $content[] = '<DEFAULT value="' . $details->default_value . '"/>';
01968 }
01969
01970 if( $details->auto_increment ) {
01971 $content[] = '<AUTOINCREMENT/>';
01972 }
01973
01974
01975
01976 $details->primary_key = 0;
01977 $type = $rs->MetaType( $details );
01978
01979 $schema .= ' <field name="' . $details->name . '" type="' . $type . '"' . $extra . '>';
01980
01981 if( !empty( $content ) ) {
01982 $schema .= "\n " . implode( "\n ", $content ) . "\n ";
01983 }
01984
01985 $schema .= '</field>' . "\n";
01986 }
01987 }
01988
01989 if( is_array( $indexes ) ) {
01990 foreach( $indexes as $index => $details ) {
01991 $schema .= ' <index name="' . $index . '">' . "\n";
01992
01993 if( $details['unique'] ) {
01994 $schema .= ' <UNIQUE/>' . "\n";
01995 }
01996
01997 foreach( $details['columns'] as $column ) {
01998 $schema .= ' <col>' . $column . '</col>' . "\n";
01999 }
02000
02001 $schema .= ' </index>' . "\n";
02002 }
02003 }
02004
02005 if( $data ) {
02006 $rs = $this->db->Execute( 'SELECT * FROM ' . $table );
02007
02008 if( is_object( $rs ) ) {
02009 $schema .= ' <data>' . "\n";
02010
02011 while( $row = $rs->FetchRow() ) {
02012 foreach( $row as $key => $val ) {
02013 $row[$key] = htmlentities($val);
02014 }
02015
02016 $schema .= ' <row><f>' . implode( '</f><f>', $row ) . '</f></row>' . "\n";
02017 }
02018
02019 $schema .= ' </data>' . "\n";
02020 }
02021 }
02022
02023 $schema .= ' </table>' . "\n";
02024 }
02025 }
02026
02027 $this->db->SetFetchMode( $old_mode );
02028
02029 $schema .= '</schema>';
02030 return $schema;
02031 }
02032
02043 function SetPrefix( $prefix = '', $underscore = TRUE ) {
02044 switch( TRUE ) {
02045
02046 case empty( $prefix ):
02047 logMsg( 'Cleared prefix' );
02048 $this->objectPrefix = '';
02049 return TRUE;
02050
02051 case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:
02052
02053 case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):
02054 logMsg( 'Invalid prefix: ' . $prefix );
02055 return FALSE;
02056 }
02057
02058 if( $underscore AND substr( $prefix, -1 ) != '_' ) {
02059 $prefix .= '_';
02060 }
02061
02062
02063 logMsg( 'Set prefix: ' . $prefix );
02064 $this->objectPrefix = $prefix;
02065 return TRUE;
02066 }
02067
02076 function prefix( $name = '' ) {
02077
02078 if( !empty( $this->objectPrefix ) ) {
02079
02080
02081 return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );
02082 }
02083
02084
02085 return $name;
02086 }
02087
02096 function supportedPlatform( $platform = NULL ) {
02097 $regex = '/^(\w*\|)*' . $this->db->databaseType . '(\|\w*)*$/';
02098
02099 if( !isset( $platform ) OR preg_match( $regex, $platform ) ) {
02100 logMsg( "Platform $platform is supported" );
02101 return TRUE;
02102 } else {
02103 logMsg( "Platform $platform is NOT supported" );
02104 return FALSE;
02105 }
02106 }
02107
02113 function clearSQL() {
02114 $this->sqlArray = array();
02115 }
02116
02125 function addSQL( $sql = NULL ) {
02126 if( is_array( $sql ) ) {
02127 foreach( $sql as $line ) {
02128 $this->addSQL( $line );
02129 }
02130
02131 return TRUE;
02132 }
02133
02134 if( is_string( $sql ) ) {
02135 $this->sqlArray[] = $sql;
02136
02137
02138 if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {
02139 $saved = $this->db->debug;
02140 $this->db->debug = $this->debug;
02141 $ok = $this->db->Execute( $sql );
02142 $this->db->debug = $saved;
02143
02144 if( !$ok ) {
02145 if( $this->debug ) {
02146 ADOConnection::outp( $this->db->ErrorMsg() );
02147 }
02148
02149 $this->success = 1;
02150 }
02151 }
02152
02153 return TRUE;
02154 }
02155
02156 return FALSE;
02157 }
02158
02167 function getSQL( $format = NULL, $sqlArray = NULL ) {
02168 if( !is_array( $sqlArray ) ) {
02169 $sqlArray = $this->sqlArray;
02170 }
02171
02172 if( !is_array( $sqlArray ) ) {
02173 return FALSE;
02174 }
02175
02176 switch( strtolower( $format ) ) {
02177 case 'string':
02178 case 'text':
02179 return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';
02180 case'html':
02181 return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';
02182 }
02183
02184 return $this->sqlArray;
02185 }
02186
02193 function Destroy() {
02194 set_magic_quotes_runtime( $this->mgq );
02195 unset( $this );
02196 }
02197 }
02198
02204 function logMsg( $msg, $title = NULL, $force = FALSE ) {
02205 if( XMLS_DEBUG or $force ) {
02206 echo '<pre>';
02207
02208 if( isset( $title ) ) {
02209 echo '<h3>' . htmlentities( $title ) . '</h3>';
02210 }
02211
02212 if( is_object( $this ) ) {
02213 echo '[' . get_class( $this ) . '] ';
02214 }
02215
02216 print_r( $msg );
02217
02218 echo '</pre>';
02219 }
02220 }
02221 ?>