source: Dev/trunk/rdfapi/util/adodb/adodb-xmlschema03.inc.php @ 12

Last change on this file since 12 was 12, checked in by basvannuland, 14 years ago

Added RAP RDF API
Added RDF reader writer for save and load survey

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