source: branches/comu-ver2/data/module/adodb/adodb-xmlschema03.inc.php @ 18701

Revision 18701, 61.2 KB checked in by nanasess, 14 years ago (diff)

Copyright の更新(#601)

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