xref: /plugin/struct/meta/ConfigParser.php (revision 2ff510b482a00ed3b17f054074a6b0f8c1fcfb5c)
1<?php
2
3namespace dokuwiki\plugin\struct\meta;
4
5/**
6 * Class ConfigParser
7 *
8 * Utilities to parse the configuration syntax into an array
9 *
10 * @package dokuwiki\plugin\struct\meta
11 */
12class ConfigParser {
13
14    protected $config = array();
15
16    /**
17     * Parser constructor.
18     *
19     * parses the given configuration lines
20     *
21     * @param $lines
22     */
23    public function __construct($lines) {
24        /** @var \helper_plugin_struct_config $helper */
25        $helper = plugin_load('helper', 'struct_config');
26        $this->config = array(
27            'limit' => 0,
28            'dynfilters' => false,
29            'summarize' => false,
30            'rownumbers' => false,
31            'sepbyheaders' => false,
32            'headers' => array(),
33            'widths' => array(),
34            'filter' => array(),
35            'schemas' => array(),
36            'sort' => array()
37        );
38        // parse info
39        foreach($lines as $line) {
40            list($key, $val) = $this->splitLine($line);
41            if(!$key) continue;
42
43            $logic = 'OR';
44            // handle line commands (we allow various aliases here)
45            switch($key) {
46                case 'from':
47                case 'schema':
48                case 'tables':
49                    $this->config['schemas'] = array_merge($this->config['schemas'], $this->parseSchema($val));
50                    break;
51                case 'select':
52                case 'cols':
53                case 'field':
54                case 'col':
55                    $this->config['cols'] = $this->parseValues($val);
56                    break;
57                case 'head':
58                case 'header':
59                case 'headers':
60                    $this->config['headers'] = $this->parseValues($val);
61                    break;
62                case 'align':
63                    $this->config['align'] = $this->parseAlignments($val);
64                    break;
65                case 'width':
66                case 'widths':
67                    $this->config['widths'] = $this->parseWidths($val);
68                    break;
69                case 'min':
70                    $this->config['min'] = abs((int) $val);
71                    break;
72                case 'limit':
73                case 'max':
74                    $this->config['limit'] = abs((int) $val);
75                    break;
76                case 'order':
77                case 'sort':
78                    // FIXME multiple values!?
79                    $this->config['sort'][] = $helper->parseSort($val);
80                    break;
81                case 'where':
82                case 'filter':
83                case 'filterand':
84                    /** @noinspection PhpMissingBreakStatementInspection */
85                case 'and':
86                    $logic = 'AND';
87                case 'filteror':
88                case 'or':
89                    $flt = $helper->parseFilterLine($logic, $val);
90                    if($flt) {
91                        $this->config['filter'][] = $flt;
92                    }
93                    break;
94                case 'dynfilters':
95                    $this->config['dynfilters'] = (bool) $val;
96                    break;
97                case 'rownumbers':
98                    $this->config['rownumbers'] = (bool) $val;
99                    break;
100                case 'summarize':
101                    $this->config['summarize'] = (bool) $val;
102                    break;
103                default:
104                    throw new StructException("unknown option '%s'", hsc($key));
105            }
106        }
107
108        // fill up headers - a NULL signifies that the column label is wanted
109        $this->config['headers'] = (array) $this->config['headers'];
110        $cnth = count($this->config['headers']);
111        $cntf = count($this->config['cols']);
112        for($i = $cnth; $i < $cntf; $i++) {
113            $this->config['headers'][] = null;
114        }
115    }
116
117    /**
118     * Get the parsed configuration
119     *
120     * @return array
121     */
122    public function getConfig() {
123        return $this->config;
124    }
125
126    /**
127     * Splits the given line into key and value
128     *
129     * @param $line
130     * @return bool|array returns false for empty lines
131     */
132    protected function splitLine($line) {
133        // ignore comments
134        $line = preg_replace('/(?<![&\\\\])#.*$/', '', $line);
135        $line = str_replace('\\#', '#', $line);
136        $line = trim($line);
137        if(empty($line)) return false;
138
139        $line = preg_split('/\s*:\s*/', $line, 2);
140        $line[0] = strtolower($line[0]);
141
142        return $line;
143    }
144
145    /**
146     * parses schema config and aliases
147     *
148     * @param $val
149     * @return array
150     */
151    protected function parseSchema($val) {
152        $schemas = array();
153        $parts = explode(',', $val);
154        foreach($parts as $part) {
155            list($table, $alias) = explode(' ', trim($part));
156            $table = trim($table);
157            $alias = trim($alias);
158            if(!$table) continue;
159
160            $schemas[] = array($table, $alias,);
161        }
162        return $schemas;
163    }
164
165    /**
166     * Parse alignment data
167     *
168     * @param string $val
169     * @return string[]
170     */
171    protected function parseAlignments($val) {
172        $cols = explode(',', $val);
173        $data = array();
174        foreach($cols as $col) {
175            $col = trim(strtolower($col));
176            if($col[0] == 'c') {
177                $align = 'center';
178            } elseif($col[0] == 'r') {
179                $align = 'right';
180            } else {
181                $align = 'left';
182            }
183            $data[] = $align;
184        }
185
186        return $data;
187    }
188
189    /**
190     * Parse width data
191     *
192     * @param $val
193     * @return array
194     */
195    protected function parseWidths($val) {
196        $vals = explode(',', $val);
197        $vals = array_map('trim', $vals);
198        $len = count($vals);
199        for($i = 0; $i < $len; $i++) {
200            $val = trim(strtolower($vals[$i]));
201
202            if(preg_match('/^\d+.?(\d+)?(px|em|ex|ch|rem|%|in|cm|mm|q|pt|pc)$/', $val)) {
203                // proper CSS unit?
204                $vals[$i] = $val;
205            } else if(preg_match('/^\d+$/', $val)) {
206                // decimal only?
207                $vals[$i] = $val . 'px';
208            } else {
209                // invalid
210                $vals[$i] = '';
211            }
212        }
213        return $vals;
214    }
215
216    /**
217     * Split values at the commas,
218     * - Wrap with quotes to escape comma, quotes escaped by two quotes
219     * - Within quotes spaces are stored.
220     *
221     * @param string $line
222     * @return array
223     */
224    protected function parseValues($line) {
225        $values = array();
226        $inQuote = false;
227        $escapedQuote = false;
228        $value = '';
229        $len = strlen($line);
230        for($i = 0; $i < $len; $i++) {
231            if($line{$i} == '"') {
232                if($inQuote) {
233                    if($escapedQuote) {
234                        $value .= '"';
235                        $escapedQuote = false;
236                        continue;
237                    }
238                    if($line{$i + 1} == '"') {
239                        $escapedQuote = true;
240                        continue;
241                    }
242                    array_push($values, $value);
243                    $inQuote = false;
244                    $value = '';
245                    continue;
246                } else {
247                    $inQuote = true;
248                    $value = ''; //don't store stuff before the opening quote
249                    continue;
250                }
251            } else if($line{$i} == ',') {
252                if($inQuote) {
253                    $value .= ',';
254                    continue;
255                } else {
256                    if(strlen($value) < 1) {
257                        continue;
258                    }
259                    array_push($values, trim($value));
260                    $value = '';
261                    continue;
262                }
263            }
264            $value .= $line{$i};
265        }
266        if(strlen($value) > 0) {
267            array_push($values, trim($value));
268        }
269        return $values;
270    }
271
272}
273