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) 1997-2003 The PHP Group                                |
6
// +----------------------------------------------------------------------+
7
// | This source file is subject to version 2.0 of the PHP license,       |
8
// | that is bundled with this package in the file LICENSE, and is        |
9
// | available at through the world-wide-web at                           |
10
// | http://www.php.net/license/2_02.txt.                                 |
11
// | If you did not receive a copy of the PHP license and are unable to   |
12
// | obtain it through the world-wide-web, please send a note to          |
13
// | license@php.net so we can mail you a copy immediately.               |
14
// +----------------------------------------------------------------------+
15
// | Author: Bertrand Mansion <bmansion@mamasam.com>                      |
16
// +----------------------------------------------------------------------+
17
//
18
// $Id: IniCommented.php 306554 2010-12-21 20:04:20Z cweiske $
19
 
20
/**
21
* Config parser for PHP .ini files with comments
22
*
23
* @author      Bertrand Mansion <bmansion@mamasam.com>
24
* @package     Config
25
*/
26
class Config_Container_IniCommented {
27
 
28
    /**
29
     * Options for this class:
30
     * - linebreak - Character to use as new line break when serializing
31
     *
32
     * @var array
33
     */
34
    var $options = array(
35
        'linebreak' => "\n"
36
    );
37
 
38
    /**
39
    * Constructor
40
    *
41
    * @access public
42
    * @param    string  $options    (optional)Options to be used by renderer
43
    */
44
    function Config_Container_IniCommented($options = array())
45
    {
46
        $this->options = array_merge($this->options, $options);
47
    } // end constructor
48
 
49
    /**
50
    * Parses the data of the given configuration file
51
    *
52
    * @access public
53
    * @param string $datasrc    path to the configuration file
54
    * @param object $obj        reference to a config object
55
    * @return mixed returns a PEAR_ERROR, if error occurs or true if ok
56
    */
57
    function &parseDatasrc($datasrc, &$obj)
58
    {
59
        $return = true;
60
        if (!file_exists($datasrc)) {
61
            return PEAR::raiseError(
62
                'Datasource file does not exist.',
63
                null, PEAR_ERROR_RETURN
64
            );
65
        }
66
        $lines = file($datasrc);
67
        if ($lines === false) {
68
            return PEAR::raiseError(
69
                'File could not be read',
70
                null, PEAR_ERROR_RETURN
71
            );
72
        }
73
 
74
        $n = 0;
75
        $lastline = '';
76
        $currentSection =& $obj->container;
77
        foreach ($lines as $line) {
78
            $n++;
79
            if (preg_match('/^\s*;(.*?)\s*$/', $line, $match)) {
80
                // a comment
81
                $currentSection->createComment($match[1]);
82
            } elseif (preg_match('/^\s*$/', $line)) {
83
                // a blank line
84
                $currentSection->createBlank();
85
            } elseif (preg_match('/^\s*([a-zA-Z0-9_\-\.\s:]*)\s*=\s*(.*)\s*$/', $line, $match)) {
86
                // a directive
87
 
88
                $values = $this->_quoteAndCommaParser($match[2]);
89
                if (PEAR::isError($values)) {
90
                    return PEAR::raiseError($values);
91
                }
92
 
93
                if (count($values)) {
94
                    foreach($values as $value) {
95
                        if ($value[0] == 'normal') {
96
                            $currentSection->createDirective(trim($match[1]), $value[1]);
97
                        }
98
                        if ($value[0] == 'comment') {
99
                            $currentSection->createComment(substr($value[1], 1));
100
                        }
101
                    }
102
                }
103
            } elseif (preg_match('/^\s*\[\s*(.*)\s*\]\s*$/', $line, $match)) {
104
                // a section
105
                $currentSection =& $obj->container->createSection($match[1]);
106
            } else {
107
                return PEAR::raiseError("Syntax error in '$datasrc' at line $n.", null, PEAR_ERROR_RETURN);
108
            }
109
        }
110
        return $return;
111
    } // end func parseDatasrc
112
 
113
    /**
114
     * Quote and Comma Parser for INI files
115
     *
116
     * This function allows complex values such as:
117
     *
118
     * <samp>
119
     * mydirective = "Item, number \"1\"", Item 2 ; "This" is really, really tricky
120
     * </samp>
121
     * @param  string  $text    value of a directive to parse for quotes/multiple values
122
     * @return array   The array returned contains multiple values, if any (unquoted literals
123
     *                 to be used as is), and a comment, if any.  The format of the array is:
124
     *
125
     * <pre>
126
     * array(array('normal', 'first value'),
127
     *       array('normal', 'next value'),...
128
     *       array('comment', '; comment with leading ;'))
129
     * </pre>
130
     * @author Greg Beaver <cellog@users.sourceforge.net>
131
     * @access private
132
     */
133
    function _quoteAndCommaParser($text)
134
    {
135
        $text = trim($text);
136
        if ($text == '') {
137
            $emptyNode = array();
138
            $emptyNode[0][0] = 'normal';
139
            $emptyNode[0][1] = '';
140
            return $emptyNode;
141
        }
142
 
143
        // tokens
144
        $tokens['normal'] = array('"', ';', ',');
145
        $tokens['quote'] = array('"', '\\');
146
        $tokens['escape'] = false; // cycle
147
        $tokens['after_quote'] = array(',', ';');
148
 
149
        // events
150
        $events['normal'] = array('"' => 'quote', ';' => 'comment', ',' => 'normal');
151
        $events['quote'] = array('"' => 'after_quote', '\\' => 'escape');
152
        $events['after_quote'] = array(',' => 'normal', ';' => 'comment');
153
 
154
        // state stack
155
        $stack = array();
156
 
157
        // return information
158
        $return = array();
159
        $returnpos = 0;
160
        $returntype = 'normal';
161
 
162
        // initialize
163
        array_push($stack, 'normal');
164
        $pos = 0; // position in $text
165
 
166
        do {
167
            $char = $text{$pos};
168
            $state = $this->_getQACEvent($stack);
169
 
170
            if ($tokens[$state]) {
171
                if (in_array($char, $tokens[$state])) {
172
                    switch($events[$state][$char]) {
173
                        case 'quote' :
174
                            if ($state == 'normal' &&
175
                                isset($return[$returnpos]) &&
176
                                !empty($return[$returnpos][1])) {
177
                                return PEAR::raiseError(
178
                                    'invalid ini syntax, quotes cannot follow'
179
                                    . " text '$text'",
180
                                    null, PEAR_ERROR_RETURN
181
                                );
182
                            }
183
                            if ($returnpos >= 0 && isset($return[$returnpos])) {
184
                                // trim any unnecessary whitespace in earlier entries
185
                                $return[$returnpos][1] = trim($return[$returnpos][1]);
186
                            } else {
187
                                $returnpos++;
188
                            }
189
                            $return[$returnpos] = array('normal', '');
190
                            array_push($stack, 'quote');
191
                            continue 2;
192
                        break;
193
                        case 'comment' :
194
                            // comments go to the end of the line, so we are done
195
                            $return[++$returnpos] = array('comment', substr($text, $pos));
196
                            return $return;
197
                        break;
198
                        case 'after_quote' :
199
                            array_push($stack, 'after_quote');
200
                        break;
201
                        case 'escape' :
202
                            // don't save the first slash
203
                            array_push($stack, 'escape');
204
                            continue 2;
205
                        break;
206
                        case 'normal' :
207
                        // start a new segment
208
                            if ($state == 'normal') {
209
                                $returnpos++;
210
                                continue 2;
211
                            } else {
212
                                while ($state != 'normal') {
213
                                    array_pop($stack);
214
                                    $state = $this->_getQACEvent($stack);
215
                                }
216
                                $returnpos++;
217
                            }
218
                        break;
219
                        default :
220
                            PEAR::raiseError(
221
                                "::_quoteAndCommaParser oops, state missing",
222
                                null, PEAR_ERROR_DIE
223
                            );
224
                        break;
225
                    }
226
                } else {
227
                    if ($state != 'after_quote') {
228
                        if (!isset($return[$returnpos])) {
229
                            $return[$returnpos] = array('normal', '');
230
                        }
231
                        // add this character to the current ini segment if
232
                        // non-empty, or if in a quote
233
                        if ($state == 'quote') {
234
                            $return[$returnpos][1] .= $char;
235
                        } elseif (!empty($return[$returnpos][1]) ||
236
                                 (empty($return[$returnpos][1]) && trim($char) != '')) {
237
                            if (!isset($return[$returnpos])) {
238
                                $return[$returnpos] = array('normal', '');
239
                            }
240
                            $return[$returnpos][1] .= $char;
241
                            if (strcasecmp('true', $return[$returnpos][1]) == 0) {
242
                              $return[$returnpos][1] = '1';
243
                            } elseif (strcasecmp('false', $return[$returnpos][1]) == 0) {
244
                              $return[$returnpos][1] = '';
245
                            }
246
                        }
247
                    } else {
248
                        if (trim($char) != '') {
249
                            return PEAR::raiseError(
250
                                'invalid ini syntax, text after a quote'
251
                                . " not allowed '$text'",
252
                                null, PEAR_ERROR_RETURN
253
                            );
254
                        }
255
                    }
256
                }
257
            } else {
258
                // no tokens, so add this one and cycle to previous state
259
                $return[$returnpos][1] .= $char;
260
                array_pop($stack);
261
            }
262
        } while (++$pos < strlen($text));
263
        return $return;
264
    } // end func _quoteAndCommaParser
265
 
266
    /**
267
     * Retrieve the state off of a state stack for the Quote and Comma Parser
268
     * @param  array  $stack    The parser state stack
269
     * @author Greg Beaver <cellog@users.sourceforge.net>
270
     * @access private
271
     */
272
    function _getQACEvent($stack)
273
    {
274
        return array_pop($stack);
275
    } // end func _getQACEvent
276
 
277
    /**
278
    * Returns a formatted string of the object
279
    * @param    object  $obj    Container object to be output as string
280
    * @access   public
281
    * @return   string
282
    */
283
    function toString(&$obj)
284
    {
285
        static $childrenCount, $commaString;
286
 
287
        if (!isset($string)) {
288
            $string = '';
289
        }
290
        switch ($obj->type) {
291
            case 'blank':
292
                $string = $this->options['linebreak'];
293
                break;
294
            case 'comment':
295
                $string = ';'.$obj->content . $this->options['linebreak'];
296
                break;
297
            case 'directive':
298
                $count = $obj->parent->countChildren('directive', $obj->name);
299
                $content = $obj->content;
300
                if ($content === false) {
301
                    $content = '0';
302
                } elseif ($content === true) {
303
                    $content = '1';
304
                } elseif (strlen(trim($content)) < strlen($content) ||
305
                          strpos($content, ',') !== false ||
306
                          strpos($content, ';') !== false ||
307
                          strpos($content, '=') !== false ||
308
                          strpos($content, '"') !== false ||
309
                          strpos($content, '%') !== false ||
310
                          strpos($content, '~') !== false ||
311
													strpos($content, '!') !== false ||
312
													strpos($content, '|') !== false ||
313
													strpos($content, '&') !== false ||
314
													strpos($content, '(') !== false ||
315
													strpos($content, ')') !== false ||
316
													$content === 'none') {
317
                    $content = '"'.addslashes($content).'"';
318
                }
319
                if ($count > 1) {
320
                    // multiple values for a directive are separated by a comma
321
                    if (isset($childrenCount[$obj->name])) {
322
                        $childrenCount[$obj->name]++;
323
                    } else {
324
                        $childrenCount[$obj->name] = 0;
325
                        $commaString[$obj->name] = $obj->name.' = ';
326
                    }
327
                    if ($childrenCount[$obj->name] == $count-1) {
328
                        // Clean the static for future calls to toString
329
                        $string .= $commaString[$obj->name] . $content
330
                            . $this->options['linebreak'];
331
                        unset($childrenCount[$obj->name]);
332
                        unset($commaString[$obj->name]);
333
                    } else {
334
                        $commaString[$obj->name] .= $content.', ';
335
                    }
336
                } else {
337
                    $string = $obj->name.' = '.$content . $this->options['linebreak'];
338
                }
339
                break;
340
            case 'section':
341
                if (!$obj->isRoot()) {
342
                    $string = '[' . $obj->name . ']' . $this->options['linebreak'];
343
                }
344
                if (count($obj->children) > 0) {
345
                    for ($i = 0; $i < count($obj->children); $i++) {
346
                        $string .= $this->toString($obj->getChild($i));
347
                    }
348
                }
349
                break;
350
            default:
351
                $string = '';
352
        }
353
        return $string;
354
    } // end func toString
355
} // end class Config_Container_IniCommented
356
?>