Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
// +----------------------------------------------------------------------+
3
// | PHP Version 4                                                        |
4
// +----------------------------------------------------------------------+
5
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
6
// | Stig. S. Bakken, Lukas Smith                                         |
7
// | All rights reserved.                                                 |
8
// +----------------------------------------------------------------------+
9
// | MDB is a merge of PEAR DB and Metabases that provides a unified DB   |
10
// | API as well as database abstraction for PHP applications.            |
11
// | This LICENSE is in the BSD license style.                            |
12
// |                                                                      |
13
// | Redistribution and use in source and binary forms, with or without   |
14
// | modification, are permitted provided that the following conditions   |
15
// | are met:                                                             |
16
// |                                                                      |
17
// | Redistributions of source code must retain the above copyright       |
18
// | notice, this list of conditions and the following disclaimer.        |
19
// |                                                                      |
20
// | Redistributions in binary form must reproduce the above copyright    |
21
// | notice, this list of conditions and the following disclaimer in the  |
22
// | documentation and/or other materials provided with the distribution. |
23
// |                                                                      |
24
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
25
// | Lukas Smith nor the names of his contributors may be used to endorse |
26
// | or promote products derived from this software without specific prior|
27
// | written permission.                                                  |
28
// |                                                                      |
29
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
30
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
31
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
32
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
33
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
34
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
35
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
36
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
37
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
38
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
39
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
40
// | POSSIBILITY OF SUCH DAMAGE.                                          |
41
// +----------------------------------------------------------------------+
42
// | Author: Christian Dickmann <dickmann@php.net>                        |
43
// +----------------------------------------------------------------------+
44
//
45
// $Id: Parser.php,v 1.30.4.1 2004/01/08 13:43:02 lsmith Exp $
46
//
47
 
48
require_once('XML/Parser.php');
49
 
50
/**
51
 * Parses an XML schema file
52
 *
53
 * @package MDB
54
 * @category Database
55
 * @access private
56
 * @author  Christian Dickmann <dickmann@php.net>
57
 */
58
class MDB_Parser extends XML_Parser
59
{
60
    var $database_definition = array();
61
    var $elements = array();
62
    var $element = '';
63
    var $count = 0;
64
    var $table = array();
65
    var $table_name = '';
66
    var $field = array();
67
    var $field_name = '';
68
    var $init = array();
69
    var $init_name = '';
70
    var $init_value = '';
71
    var $index = array();
72
    var $index_name = '';
73
    var $var_mode = FALSE;
74
    var $variables = array();
75
    var $seq = array();
76
    var $seq_name = '';
77
    var $error = NULL;
78
 
79
    var $invalid_names = array(
80
        'user' => array(),
81
        'is' => array(),
82
        'file' => array(
83
            'oci' => array(),
84
            'oracle' => array()
85
        ),
86
        'notify' => array(
87
            'pgsql' => array()
88
        ),
89
        'restrict' => array(
90
            'mysql' => array()
91
        ),
92
        'password' => array(
93
            'ibase' => array()
94
        )
95
    );
96
    var $fail_on_invalid_names = 1;
97
 
98
    function MDB_Parser($variables, $fail_on_invalid_names = 1)
99
    {
100
        $this->XML_Parser();
101
        $this->variables = $variables;
102
        $this->fail_on_invalid_names = $fail_on_invalid_names;
103
    }
104
 
105
    function startHandler($xp, $element, $attribs)
106
    {
107
        if (strtolower($element) == 'variable') {
108
            $this->var_mode = TRUE;
109
            return;
110
        };
111
 
112
        $this->elements[$this->count++] = strtolower($element);
113
        $this->element = implode('-', $this->elements);
114
 
115
        switch($this->element) {
116
        case 'database-table-initialization-insert':
117
            $this->init = array('type' => 'insert');
118
            break;
119
        case 'database-table-initialization-insert-field':
120
            $this->init_name = '';
121
            $this->init_value = '';
122
            break;
123
        case 'database-table':
124
            $this->table_name = '';
125
            $this->table = array();
126
            break;
127
        case 'database-table-declaration-field':
128
            $this->field_name = '';
129
            $this->field = array();
130
            break;
131
        case 'database-table-declaration-field-default':
132
            $this->field['default'] = '';
133
            break;
134
        case 'database-table-declaration-index':
135
            $this->index_name = '';
136
            $this->index = array();
137
            break;
138
        case 'database-sequence':
139
            $this->seq_name = '';
140
            $this->seq = array();
141
            break;
142
        case 'database-table-declaration-index-field':
143
            $this->field_name = '';
144
            $this->field = array();
145
            break;
146
        };
147
    }
148
 
149
    function endHandler($xp, $element)
150
    {
151
        if (strtolower($element) == 'variable') {
152
            $this->var_mode = FALSE;
153
            return;
154
        };
155
 
156
        switch($this->element) {
157
        /* Initialization */
158
        case 'database-table-initialization-insert-field':
159
            if (!$this->init_name) {
160
                $this->raiseError('field-name has to be specified', $xp);
161
            };
162
            if (isset($this->init['FIELDS'][$this->init_name])) {
163
                $this->raiseError('field "'.$this->init_name.'" already filled', $xp);
164
            };
165
            if (!isset($this->table['FIELDS'][$this->init_name])) {
166
                $this->raiseError('unkown field "'.$this->init_name.'"', $xp);
167
            };
168
            if ($this->init_value !== ''
169
                && !$this->validateFieldValue($this->init_name, $this->init_value, $xp))
170
            {
171
                $this->raiseError('field "'.$this->init_name.'" has wrong value', $xp);
172
            };
173
            $this->init['FIELDS'][$this->init_name] = $this->init_value;
174
            break;
175
        case 'database-table-initialization-insert':
176
            $this->table['initialization'][] = $this->init;
177
            break;
178
 
179
        /* Table definition */
180
        case 'database-table':
181
            if (!isset($this->table['was'])) {
182
                $this->table['was'] = $this->table_name;
183
            };
184
            if (!$this->table_name) {
185
                $this->raiseError('tables need names', $xp);
186
            };
187
            if (isset($this->database_definition['TABLES'][$this->table_name])) {
188
                $this->raiseError('table "'.$this->table_name.'" already exists', $xp);
189
            };
190
            if (!isset($this->table['FIELDS'])) {
191
                $this->raiseError('tables need one or more fields', $xp);
192
            };
193
            if (isset($this->table['INDEXES'])) {
194
                foreach($this->table['INDEXES'] as $index_name => $index) {
195
                    foreach($index['FIELDS'] as $field_name => $field) {
196
                        if (!isset($this->table['FIELDS'][$field_name])) {
197
                            $this->raiseError('index field "'.$field_name.'" does not exist', $xp);
198
                        }
199
                        if (!(isset($this->table['FIELDS'][$field_name]['notnull'])
200
                            && $this->table['FIELDS'][$field_name]['notnull'] == 1))
201
                        {
202
                            $this->raiseError('index field "'.$field_name.'" has to be "notnull"', $xp);
203
                        }
204
                    }
205
                }
206
            };
207
            $this->database_definition['TABLES'][$this->table_name] = $this->table;
208
            break;
209
 
210
        /* Field declaration */
211
        case 'database-table-declaration-field':
212
            if (!$this->field_name || !isset($this->field['type'])) {
213
                $this->raiseError('field "'.$this->field_name.'" was not properly specified', $xp);
214
            };
215
            if (isset($this->table['FIELDS'][$this->field_name])) {
216
                $this->raiseError('field "'.$this->field_name.'" already exists', $xp);
217
            };
218
            /* Invalidname check */
219
            if ($this->fail_on_invalid_names && isset($this->invalid_names[$this->field_name])) {
220
                $this->raiseError('fieldname "'.$this->field_name.'" not allowed', $xp);
221
            };
222
            /* Type check */
223
            switch($this->field['type']) {
224
            case 'integer':
225
                if (isset($this->field['unsigned'])
226
                    && $this->field['unsigned'] !== '1' && $this->field['unsigned'] !== '0')
227
                {
228
                    $this->raiseError('unsigned has to be 1 or 0', $xp);
229
                };
230
                break;
231
            case 'text':
232
            case 'clob':
233
            case 'blob':
234
                if (isset($this->field['length']) && ((int)$this->field['length']) <= 0) {
235
                    $this->raiseError('length has to be an integer greater 0', $xp);
236
                };
237
                break;
238
            case 'boolean':
239
            case 'date':
240
            case 'timestamp':
241
            case 'time':
242
            case 'float':
243
            case 'decimal':
244
                break;
245
            default:
246
                $this->raiseError('no valid field type ("'.$this->field['type'].'") specified', $xp);
247
            };
248
            if (!isset($this->field['was'])) {
249
                $this->field['was'] = $this->field_name;
250
            };
251
            if (isset($this->field['notnull']) && !$this->is_boolean($this->field['notnull'])) {
252
                $this->raiseError('field  "notnull" has to be 1 or 0', $xp);
253
            };
254
            if (isset($this->field['notnull']) && !isset($this->field['default'])) {
255
                $this->raiseError('if field is "notnull", it needs a default value', $xp);
256
            };
257
            if (isset($this->field['unsigned']) && !$this->is_boolean($this->field['unsigned'])) {
258
                $this->raiseError('field  "notnull" has to be 1 or 0', $xp);
259
            };
260
            $this->table['FIELDS'][$this->field_name] = $this->field;
261
            if (isset($this->field['default'])) {
262
                if ($this->field['type'] == 'clob' || $this->field['type'] == 'blob') {
263
                    $this->raiseError('"'.$this->field['type'].'"-fields are not allowed to have a default value', $xp);
264
                };
265
                if ($this->field['default'] !== ''
266
                    && !$this->validateFieldValue($this->field_name, $this->field['default'], $xp))
267
                {
268
                    $this->raiseError('default value of "'.$this->field_name.'" is of wrong type', $xp);
269
                };
270
            };
271
            break;
272
 
273
        /* Index declaration */
274
        case 'database-table-declaration-index':
275
            if (!$this->index_name) {
276
                $this->raiseError('an index needs a name', $xp);
277
            };
278
            if (isset($this->table['INDEXES'][$this->index_name])) {
279
                $this->raiseError('index "'.$this->index_name.'" already exists', $xp);
280
            };
281
            if (isset($this->index['unique']) && !$this->is_boolean($this->index['unique'])) {
282
                $this->raiseError('field  "unique" has to be 1 or 0', $xp);
283
            };
284
            if (!isset($this->index['was'])) {
285
                $this->index['was'] = $this->index_name;
286
            };
287
            $this->table['INDEXES'][$this->index_name] = $this->index;
288
            break;
289
        case 'database-table-declaration-index-field':
290
            if (!$this->field_name) {
291
                $this->raiseError('the index-field-name is required', $xp);
292
            };
293
            if (isset($this->field['sorting'])
294
                && $this->field['sorting'] !== 'ascending' && $this->field['sorting'] !== 'descending') {
295
                $this->raiseError('sorting type unknown', $xp);
296
            };
297
            $this->index['FIELDS'][$this->field_name] = $this->field;
298
            break;
299
 
300
        /* Sequence declaration */
301
        case 'database-sequence':
302
            if (!$this->seq_name) {
303
                $this->raiseError('a sequence has to have a name', $xp);
304
            };
305
            if (isset($this->database_definition['SEQUENCES'][$this->seq_name])) {
306
                $this->raiseError('sequence "'.$this->seq_name.'" already exists', $xp);
307
            };
308
            if (!isset($this->seq['was'])) {
309
                $this->seq['was'] = $this->seq_name;
310
            };
311
            if (isset($this->seq['on'])) {
312
                if ((!isset($this->seq['on']['table']) || !$this->seq['on']['table'])
313
                    || (!isset($this->seq['on']['field']) || !$this->seq['on']['field']))
314
                {
315
                    $this->raiseError('sequence "'.$this->seq_name.'" was not properly defined', $xp);
316
                };
317
            };
318
            $this->database_definition['SEQUENCES'][$this->seq_name] = $this->seq;
319
            break;
320
 
321
        /* End of File */
322
        case 'database':
323
            if (isset($this->database_definition['create'])
324
                && !$this->is_boolean($this->database_definition['create']))
325
            {
326
                $this->raiseError('field "create" has to be 1 or 0', $xp);
327
            };
328
            if (isset($this->database_definition['overwrite'])
329
                && !$this->is_boolean($this->database_definition['overwrite']))
330
            {
331
                $this->raiseError('field "overwrite" has to be 1 or 0', $xp);
332
            };
333
            if (!isset($this->database_definition['name']) || !$this->database_definition['name']) {
334
                $this->raiseError('database needs a name', $xp);
335
            };
336
            if (isset($this->database_definition['SEQUENCES'])) {
337
                foreach($this->database_definition['SEQUENCES'] as $seq_name => $seq) {
338
                    if (isset($seq['on'])
339
                        && !isset($this->database_definition['TABLES'][$seq['on']['table']]['FIELDS'][$seq['on']['field']]))
340
                    {
341
                        $this->raiseError('sequence "'.$seq_name.'" was assigned on unexisting field/table', $xp);
342
                    };
343
                };
344
            };
345
            if (MDB::isError($this->error)) {
346
                $this->database_definition = $this->error;
347
            };
348
            break;
349
        }
350
 
351
        unset($this->elements[--$this->count]);
352
        $this->element = implode('-', $this->elements);
353
    }
354
 
355
    function validateFieldValue($field_name, &$field_value, &$xp)
356
    {
357
        if (!isset($this->table['FIELDS'][$field_name])) {
358
            return;
359
        };
360
        $field_def = $this->table['FIELDS'][$field_name];
361
        switch($field_def['type']) {
362
        case 'text':
363
        case 'clob':
364
            if (isset($field_def['length']) && strlen($field_value) > $field_def['length']) {
365
                return($this->raiseError('"'.$field_value.'" is not of type "'.$field_def['type'].'"', $xp));
366
            };
367
            break;
368
        case 'blob':
369
            /*
370
            if (!preg_match('/^([0-9a-f]{2})*$/i', $field_value)) {
371
                return($this->raiseError('"'.$field_value.'" is not of type "'.$field_def['type'].'"', $xp));
372
            }
373
            */
374
            $field_value = pack('H*', $field_value);
375
            if (isset($field_def['length']) && strlen($field_value) > $field_def['length']) {
376
                return($this->raiseError('"'.$field_value.'" is not of type "'.$field_def['type'].'"', $xp));
377
            };
378
            break;
379
        case 'integer':
380
            if ($field_value != ((int)$field_value)) {
381
                return($this->raiseError('"'.$field_value.'" is not of type "'.$field_def['type'].'"', $xp));
382
            };
383
            $field_value = (int) $field_value;
384
            if (isset($field_def['unsigned']) && $field_def['unsigned'] && $field_value < 0) {
385
                return($this->raiseError('"'.$field_value.'" is not of type "'.$field_def['type'].'"', $xp));
386
            };
387
            break;
388
        case 'boolean':
389
            if (!$this->is_boolean($field_value)) {
390
                return($this->raiseError('"'.$field_value.'" is not of type "'.$field_def['type'].'"', $xp));
391
            }
392
            break;
393
        case 'date':
394
            if (!preg_match('/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})/', $field_value)) {
395
                return($this->raiseError('"'.$field_value.'" is not of type "'.$field_def['type'].'"', $xp));
396
            }
397
            break;
398
        case 'timestamp':
399
            if (!preg_match('/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/', $field_value)) {
400
                return($this->raiseError('"'.$field_value.'" is not of type "'.$field_def['type'].'"', $xp));
401
            }
402
            break;
403
        case 'time':
404
            if (!preg_match("/([0-9]{2}):([0-9]{2}):([0-9]{2})/", $field_value)) {
405
                return($this->raiseError('"'.$field_value.'" is not of type "'.$field_def['type'].'"', $xp));
406
            }
407
            break;
408
        case 'float':
409
        case 'double':
410
            if ($field_value != (double) $field_value) {
411
                return($this->raiseError('"'.$field_value.'" is not of type "'.$field_def['type'].'"', $xp));
412
            };
413
            $field_value = (double) $field_value;
414
            break;
415
        }
416
        return(TRUE);
417
    }
418
 
419
    function raiseError($msg, $xp = NULL)
420
    {
421
        if ($this->error === NULL) {
422
            if(is_resource($msg)) {
423
                $error = "Parser error: ";
424
                $xp = $msg;
425
            } else {
426
                $error = "Parser error: \"".$msg."\"\n";
427
            }
428
            if($xp != NULL) {
429
                $byte = @xml_get_current_byte_index($xp);
430
                $line = @xml_get_current_line_number($xp);
431
                $column = @xml_get_current_column_number($xp);
432
                $error .= "Byte: $byte; Line: $line; Col: $column\n";
433
            }
434
            $this->error = PEAR::raiseError(NULL, MDB_ERROR_MANAGER_PARSE, NULL, NULL,
435
                $error, 'MDB_Error', TRUE);
436
        };
437
        return(FALSE);
438
    }
439
 
440
    function is_boolean(&$value)
441
    {
442
        if (is_int($value) && ($value == 0 || $value == 1)) {
443
            return(TRUE);
444
        };
445
        if ($value === '1' || $value === '0') {
446
            $value = (int) $value;
447
            return(TRUE);
448
        };
449
        switch($value)
450
        {
451
        case 'N':
452
        case 'n':
453
        case 'no':
454
        case 'FALSE':
455
            $value = 0;
456
            break;
457
        case 'Y':
458
        case 'y':
459
        case 'yes':
460
        case 'TRUE':
461
            $value = 1;
462
            break;
463
        default:
464
            return(FALSE);
465
        };
466
        return(TRUE);
467
    }
468
 
469
    function cdataHandler($xp, $data)
470
    {
471
        if ($this->var_mode == TRUE) {
472
            if (!isset($this->variables[$data])) {
473
                $this->raiseError('variable "'.$data.'" not found', $xp);
474
                return;
475
            };
476
            $data = $this->variables[$data];
477
        };
478
 
479
        switch($this->element) {
480
        /* Initialization */
481
        case 'database-table-initialization-insert-field-name':
482
            @$this->init_name .= $data;
483
            break;
484
        case 'database-table-initialization-insert-field-value':
485
            @$this->init_value .= $data;
486
            break;
487
 
488
        /* Database */
489
        case 'database-name':
490
            @$this->database_definition['name'] .= $data;
491
            break;
492
        case 'database-create':
493
            @$this->database_definition['create'] .= $data;
494
            break;
495
        case 'database-overwrite':
496
            @$this->database_definition['overwrite'] .= $data;
497
            break;
498
        case 'database-table-name':
499
            @$this->table_name .= $data;
500
            break;
501
        case 'database-table-was':
502
            @$this->table['was'] .= $data;
503
            break;
504
 
505
        /* Field declaration */
506
        case 'database-table-declaration-field-name':
507
            @$this->field_name .= $data;
508
            break;
509
        case 'database-table-declaration-field-type':
510
            @$this->field['type'] .= $data;
511
            break;
512
        case 'database-table-declaration-field-was':
513
            @$this->field['was'] .= $data;
514
            break;
515
        case 'database-table-declaration-field-notnull':
516
            @$this->field['notnull'] .= $data;
517
            break;
518
        case 'database-table-declaration-field-unsigned':
519
            @$this->field['unsigned'] .= $data;
520
            break;
521
        case 'database-table-declaration-field-default':
522
            @$this->field['default'] .= $data;
523
            break;
524
        case 'database-table-declaration-field-length':
525
            @$this->field['length'] .= $data;
526
            break;
527
 
528
        /* Index declaration */
529
        case 'database-table-declaration-index-name':
530
            @$this->index_name .= $data;
531
            break;
532
        case 'database-table-declaration-index-unique':
533
            @$this->index['unique'] .= $data;
534
            break;
535
        case 'database-table-declaration-index-was':
536
            @$this->index['was'] .= $data;
537
            break;
538
        case 'database-table-declaration-index-field-name':
539
            @$this->field_name .= $data;
540
            break;
541
        case 'database-table-declaration-index-field-sorting':
542
            @$this->field['sorting'] .= $data;
543
            break;
544
 
545
        /* Sequence declaration */
546
        case 'database-sequence-name':
547
            @$this->seq_name .= $data;
548
            break;
549
        case 'database-sequence-was':
550
            @$this->seq['was'] .= $data;
551
            break;
552
        case 'database-sequence-start':
553
            @$this->seq['start'] .= $data;
554
            break;
555
        case 'database-sequence-on-table':
556
            @$this->seq['on']['table'] .= $data;
557
            break;
558
        case 'database-sequence-on-field':
559
            @$this->seq['on']['field'] .= $data;
560
            break;
561
        };
562
    }
563
};
564
 
565
?>