Documentation TYPO3 par Ameos |
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() { 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 $this->opts[] = $opt; 00467 00468 return $this->opts; 00469 } 00470 00477 function create( &$xmls ) { 00478 $sql = array(); 00479 00480 // drop any existing indexes 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 // remove fields to be dropped from table object 00488 foreach( $this->drop_field as $field ) { 00489 unset( $this->fields[$field] ); 00490 } 00491 00492 // if table exists 00493 if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) { 00494 // drop table 00495 if( $this->drop_table ) { 00496 $sql[] = $xmls->dict->DropTableSQL( $this->name ); 00497 00498 return $sql; 00499 } 00500 00501 // drop any existing fields not in schema 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 // if table doesn't exist 00508 } else { 00509 if( $this->drop_table ) { 00510 return $sql; 00511 } 00512 00513 $legacy_fields = array(); 00514 } 00515 00516 // Loop through the field specifier array, building the associative array for the field options 00517 $fldarray = array(); 00518 00519 foreach( $this->fields as $field_id => $finfo ) { 00520 // Set an empty size if it isn't supplied 00521 if( !isset( $finfo['SIZE'] ) ) { 00522 $finfo['SIZE'] = ''; 00523 } 00524 00525 // Initialize the field array with the type and size 00526 $fldarray[$field_id] = array( 00527 'NAME' => $finfo['NAME'], 00528 'TYPE' => $finfo['TYPE'], 00529 'SIZE' => $finfo['SIZE'] 00530 ); 00531 00532 // Loop through the options array and add the field options. 00533 if( isset( $finfo['OPTS'] ) ) { 00534 foreach( $finfo['OPTS'] as $opt ) { 00535 // Option has an argument. 00536 if( is_array( $opt ) ) { 00537 $key = key( $opt ); 00538 $value = $opt[key( $opt )]; 00539 @$fldarray[$field_id][$key] .= $value; 00540 // Option doesn't have arguments 00541 } else { 00542 $fldarray[$field_id][$opt] = $opt; 00543 } 00544 } 00545 } 00546 } 00547 00548 if( empty( $legacy_fields ) ) { 00549 // Create the new table 00550 $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts ); 00551 logMsg( end( $sql ), 'Generated CreateTableSQL' ); 00552 } else { 00553 // Upgrade an existing table 00554 logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" ); 00555 switch( $xmls->upgrade ) { 00556 // Use ChangeTableSQL 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 // ignore table 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 // Drop the current field 00589 logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" ); 00590 // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field ); 00591 $this->drop_field[$this->current_field] = $this->current_field; 00592 } else { 00593 // Drop the current table 00594 logMsg( "Dropping table '{$this->name}'" ); 00595 // $this->drop_table = $xmls->dict->DropTableSQL( $this->name ); 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 // Add index Option 00670 $this->addIndexOpt( $this->currentElement ); 00671 break; 00672 default: 00673 // print_r( array( $tag, $attributes ) ); 00674 } 00675 } 00676 00684 function _tag_cdata( &$parser, $cdata ) { 00685 switch( $this->currentElement ) { 00686 // Index field name 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 // Return the field list 00720 return $this->columns; 00721 } 00722 00729 function addIndexOpt( $opt ) { 00730 $this->opts[] = $opt; 00731 00732 // Return the options list 00733 return $this->opts; 00734 } 00735 00742 function create( &$xmls ) { 00743 if( $this->drop ) { 00744 return NULL; 00745 } 00746 00747 // eliminate any columns that aren't in the table 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 // print_r( array( $tag, $attributes ) ); 00811 } 00812 } 00813 00821 function _tag_cdata( &$parser, $cdata ) { 00822 switch( $this->currentElement ) { 00823 // Index field name 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 // Set the field index so we know where we are 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 // eliminate any columns that aren't in the table 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 // check that at least 1 column is specified 00931 if( empty( $fields ) ) { 00932 continue; 00933 } 00934 00935 // check that no required columns are missing 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 // Overrides the manual prefix key 00990 if( isset( $attributes['KEY'] ) ) { 00991 $this->prefixKey = $attributes['KEY']; 00992 } 00993 00994 $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : ''; 00995 00996 // Enables or disables automatic prefix prepending 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 // Create a new query in a SQL queryset. 01022 // Ignore this query set if a platform is specified and it's different than the 01023 // current connection platform. 01024 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { 01025 $this->newQuery(); 01026 } else { 01027 $this->discardQuery(); 01028 } 01029 break; 01030 default: 01031 // print_r( array( $tag, $attributes ) ); 01032 } 01033 } 01034 01038 function _tag_cdata( &$parser, $cdata ) { 01039 switch( $this->currentElement ) { 01040 // Line of queryset SQL data 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 // Add the finished query to the open query set. 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 // Enable auto prefix replacement 01138 01139 // Process object prefix. 01140 // Evaluate SQL statements to prepend prefix to objects 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 // SELECT statements aren't working yet 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 // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX. 01150 // If prefixKey is not set, we use the default constant XMLS_PREFIX 01151 if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) { 01152 // Enable prefix override 01153 $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query ); 01154 } else { 01155 // Use default replacement 01156 $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query ); 01157 } 01158 } 01159 01160 $this->queries[$id] = trim( $query ); 01161 } 01162 01163 // Return the query set array 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 // $prefix = $prefix . '_'; 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 // this stops the creation of 'R' columns, 01975 // AUTOINCREMENT is used to create auto columns 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 // clear prefix 02046 case empty( $prefix ): 02047 logMsg( 'Cleared prefix' ); 02048 $this->objectPrefix = ''; 02049 return TRUE; 02050 // prefix too long 02051 case strlen( $prefix ) > XMLS_PREFIX_MAXLEN: 02052 // prefix contains invalid characters 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 // prefix valid 02063 logMsg( 'Set prefix: ' . $prefix ); 02064 $this->objectPrefix = $prefix; 02065 return TRUE; 02066 } 02067 02076 function prefix( $name = '' ) { 02077 // if prefix is set 02078 if( !empty( $this->objectPrefix ) ) { 02079 // Prepend the object prefix to the table name 02080 // prepend after quote if used 02081 return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name ); 02082 } 02083 02084 // No prefix set. Use name provided. 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 // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL. 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 ?>