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