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