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