1<?php 2 3namespace plugin\struct\meta; 4 5/** 6 * Class ConfigParser 7 * 8 * Utilities to parse the configuration syntax into an array 9 * 10 * @package 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 ); 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 'title': 58 $this->config['title'] = $val; 59 break; 60 case 'head': 61 case 'header': 62 case 'headers': 63 $this->config['headers'] = $this->parseValues($val); 64 break; 65 case 'align': 66 $this->config['align'] = $this->parseAlignments($val); 67 break; 68 case 'widths': 69 $this->config['width'] = $this->parseValues($val); 70 break; 71 case 'min': 72 $this->config['min'] = abs((int) $val); 73 break; 74 case 'limit': 75 case 'max': 76 $this->config['limit'] = abs((int) $val); 77 break; 78 case 'order': 79 case 'sort': 80 // FIXME multiple values!? 81 $this->config['sort'] = $helper->parseSort($val); 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 'page': 97 case 'target': 98 $this->config['page'] = cleanID($val); 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 'sepbyheaders': 110 $this->config['sepbyheaders'] = (bool) $val; 111 break; 112 default: 113 throw new StructException("unknown option '%s'", hsc($val)); 114 } 115 } 116 // we need at least one column to display 117 // fill up headers with field names if necessary 118 $this->config['headers'] = (array) $this->config['headers']; 119 $cnth = count($this->config['headers']); 120 $cntf = count($this->config['cols']); 121 for($i = $cnth; $i < $cntf; $i++) { 122 $column = array_slice($this->config['cols'], $i, 1); 123 $columnprops = array_pop($column); 124 $this->config['headers'][] = $columnprops['title']; 125 } 126 } 127 128 /** 129 * Get the parsed configuration 130 * 131 * @return array 132 */ 133 public function getConfig() { 134 return $this->config; 135 } 136 137 /** 138 * Splits the given line into key and value 139 * 140 * @param $line 141 * @return bool|array returns false for empty lines 142 */ 143 protected function splitLine($line) { 144 // ignore comments 145 $line = preg_replace('/(?<![&\\\\])#.*$/', '', $line); 146 $line = str_replace('\\#', '#', $line); 147 $line = trim($line); 148 if(empty($line)) return false; 149 150 $line = preg_split('/\s*:\s*/', $line, 2); 151 $line[0] = strtolower($line[0]); 152 153 return $line; 154 } 155 156 /** 157 * parses schema config and aliases 158 * 159 * @param $val 160 * @return array 161 */ 162 protected function parseSchema($val){ 163 $schemas = array(); 164 $parts = explode(',', $val); 165 foreach($parts as $part) { 166 list($table, $alias) = explode(' ', $part); 167 $table = trim($table); 168 $alias = trim($alias); 169 if(!$table) continue; 170 171 $schemas[] = array($table, $alias,); 172 } 173 return $schemas; 174 } 175 176 /** 177 * Parse alignment data 178 * 179 * @param string $val 180 * @return string[] 181 */ 182 protected function parseAlignments($val) { 183 $cols = explode(',', $val); 184 $data = array(); 185 foreach($cols as $col) { 186 $col = trim(strtolower($col)); 187 if($col[0] == 'c') { 188 $align = 'center'; 189 } elseif($col[0] == 'r') { 190 $align = 'right'; 191 } else { 192 $align = 'left'; 193 } 194 $data[] = $align; 195 } 196 197 return $data; 198 } 199 200 /** 201 * Split values at the commas, 202 * - Wrap with quotes to escape comma, quotes escaped by two quotes 203 * - Within quotes spaces are stored. 204 * 205 * @param string $line 206 * @return array 207 */ 208 protected function parseValues($line) { 209 $values = array(); 210 $inQuote = false; 211 $escapedQuote = false; 212 $value = ''; 213 $len = strlen($line); 214 for($i = 0; $i < $len; $i++) { 215 if($line{$i} == '"') { 216 if($inQuote) { 217 if($escapedQuote) { 218 $value .= '"'; 219 $escapedQuote = false; 220 continue; 221 } 222 if($line{$i + 1} == '"') { 223 $escapedQuote = true; 224 continue; 225 } 226 array_push($values, $value); 227 $inQuote = false; 228 $value = ''; 229 continue; 230 } else { 231 $inQuote = true; 232 $value = ''; //don't store stuff before the opening quote 233 continue; 234 } 235 } else if($line{$i} == ',') { 236 if($inQuote) { 237 $value .= ','; 238 continue; 239 } else { 240 if(strlen($value) < 1) { 241 continue; 242 } 243 array_push($values, trim($value)); 244 $value = ''; 245 continue; 246 } 247 } 248 $value .= $line{$i}; 249 } 250 if(strlen($value) > 0) { 251 array_push($values, trim($value)); 252 } 253 return $values; 254 } 255 256} 257