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