source: trunk/phpgwapi/inc/adodb/adodb-xmlschema03.inc.php @ 34

Revision 34, 61.2 KB checked in by niltonneto, 17 years ago (diff)

Versão nova do ADODB (4.5 para 4.95)

  • Property svn:eol-style set to native
  • Property svn:executable set to *
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$
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.