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