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