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