source: sandbox/2.5.1-evolucao/phpgwapi/inc/adodb/adodb-xmlschema03.inc.php @ 8222

Revision 8222, 61.3 KB checked in by angelo, 11 years ago (diff)

Ticket #3491 - Compatibilizar Expresso com novas versoes do PHP

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