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