1*5511bd5bSAndreas Gohr<?php 2*5511bd5bSAndreas Gohr 3*5511bd5bSAndreas Gohrnamespace plugin\struct\meta; 4*5511bd5bSAndreas Gohr 5*5511bd5bSAndreas Gohr/** 6*5511bd5bSAndreas Gohr * Class ConfigParser 7*5511bd5bSAndreas Gohr * 8*5511bd5bSAndreas Gohr * Utilities to parse the configuration syntax into an array 9*5511bd5bSAndreas Gohr * 10*5511bd5bSAndreas Gohr * @package plugin\struct\meta 11*5511bd5bSAndreas Gohr */ 12*5511bd5bSAndreas Gohrclass ConfigParser { 13*5511bd5bSAndreas Gohr 14*5511bd5bSAndreas Gohr 15*5511bd5bSAndreas Gohr protected $config = array(); 16*5511bd5bSAndreas Gohr 17*5511bd5bSAndreas Gohr /** 18*5511bd5bSAndreas Gohr * Parser constructor. 19*5511bd5bSAndreas Gohr * 20*5511bd5bSAndreas Gohr * parses the given configuration lines 21*5511bd5bSAndreas Gohr * 22*5511bd5bSAndreas Gohr * @param $lines 23*5511bd5bSAndreas Gohr */ 24*5511bd5bSAndreas Gohr public function __construct($lines) { 25*5511bd5bSAndreas Gohr $data = array( 26*5511bd5bSAndreas Gohr 'limit' => 0, 27*5511bd5bSAndreas Gohr 'dynfilters' => false, 28*5511bd5bSAndreas Gohr 'summarize' => false, 29*5511bd5bSAndreas Gohr 'rownumbers' => false, 30*5511bd5bSAndreas Gohr 'sepbyheaders' => false, 31*5511bd5bSAndreas Gohr 'headers' => array(), 32*5511bd5bSAndreas Gohr 'widths' => array(), 33*5511bd5bSAndreas Gohr 'filter' => array(), 34*5511bd5bSAndreas Gohr 'schemas' => array() 35*5511bd5bSAndreas Gohr ); 36*5511bd5bSAndreas Gohr // parse info 37*5511bd5bSAndreas Gohr foreach($lines as $line) { 38*5511bd5bSAndreas Gohr list($key, $val) = $this->splitLine($line); 39*5511bd5bSAndreas Gohr if(!$key) continue; 40*5511bd5bSAndreas Gohr 41*5511bd5bSAndreas Gohr $logic = 'OR'; 42*5511bd5bSAndreas Gohr // handle line commands (we allow various aliases here) 43*5511bd5bSAndreas Gohr switch($key) { 44*5511bd5bSAndreas Gohr case 'from': 45*5511bd5bSAndreas Gohr case 'schema': 46*5511bd5bSAndreas Gohr case 'tables': 47*5511bd5bSAndreas Gohr $this->config['schemas'] = array_merge($this->config['schemas'], $this->parseSchema($val)); 48*5511bd5bSAndreas Gohr break; 49*5511bd5bSAndreas Gohr case 'select': 50*5511bd5bSAndreas Gohr case 'cols': 51*5511bd5bSAndreas Gohr case 'field': 52*5511bd5bSAndreas Gohr case 'col': 53*5511bd5bSAndreas Gohr $this->config['cols'] = $this->parseValues($val); 54*5511bd5bSAndreas Gohr break; 55*5511bd5bSAndreas Gohr case 'title': 56*5511bd5bSAndreas Gohr $this->config['title'] = $val; 57*5511bd5bSAndreas Gohr break; 58*5511bd5bSAndreas Gohr case 'head': 59*5511bd5bSAndreas Gohr case 'header': 60*5511bd5bSAndreas Gohr case 'headers': 61*5511bd5bSAndreas Gohr $this->config['headers'] = $this->parseValues($val); 62*5511bd5bSAndreas Gohr break; 63*5511bd5bSAndreas Gohr case 'align': 64*5511bd5bSAndreas Gohr $this->config['align'] = $this->parseAlignments($val); 65*5511bd5bSAndreas Gohr break; 66*5511bd5bSAndreas Gohr case 'widths': 67*5511bd5bSAndreas Gohr $this->config['width'] = $this->parseValues($val); 68*5511bd5bSAndreas Gohr break; 69*5511bd5bSAndreas Gohr case 'min': 70*5511bd5bSAndreas Gohr $this->config['min'] = abs((int) $val); 71*5511bd5bSAndreas Gohr break; 72*5511bd5bSAndreas Gohr case 'limit': 73*5511bd5bSAndreas Gohr case 'max': 74*5511bd5bSAndreas Gohr $this->config['limit'] = abs((int) $val); 75*5511bd5bSAndreas Gohr break; 76*5511bd5bSAndreas Gohr case 'order': 77*5511bd5bSAndreas Gohr case 'sort': 78*5511bd5bSAndreas Gohr // FIXME multiple values!? 79*5511bd5bSAndreas Gohr if(substr($val, 0, 1) == '^') { 80*5511bd5bSAndreas Gohr $this->config['sort'] = array(substr($val, 1), 'DESC'); 81*5511bd5bSAndreas Gohr } else { 82*5511bd5bSAndreas Gohr $this->config['sort'] = array($val, 'ASC'); 83*5511bd5bSAndreas Gohr } 84*5511bd5bSAndreas Gohr break; 85*5511bd5bSAndreas Gohr case 'where': 86*5511bd5bSAndreas Gohr case 'filter': 87*5511bd5bSAndreas Gohr case 'filterand': 88*5511bd5bSAndreas Gohr /** @noinspection PhpMissingBreakStatementInspection */ 89*5511bd5bSAndreas Gohr case 'and': 90*5511bd5bSAndreas Gohr $logic = 'AND'; 91*5511bd5bSAndreas Gohr case 'filteror': 92*5511bd5bSAndreas Gohr case 'or': 93*5511bd5bSAndreas Gohr $flt = $this->parseFilter($val); 94*5511bd5bSAndreas Gohr if($flt) { 95*5511bd5bSAndreas Gohr $flt[] = $logic; 96*5511bd5bSAndreas Gohr $this->config['filter'][] = $flt; 97*5511bd5bSAndreas Gohr } 98*5511bd5bSAndreas Gohr break; 99*5511bd5bSAndreas Gohr case 'page': 100*5511bd5bSAndreas Gohr case 'target': 101*5511bd5bSAndreas Gohr $this->config['page'] = cleanID($val); 102*5511bd5bSAndreas Gohr break; 103*5511bd5bSAndreas Gohr case 'dynfilters': 104*5511bd5bSAndreas Gohr $this->config['dynfilters'] = (bool) $val; 105*5511bd5bSAndreas Gohr break; 106*5511bd5bSAndreas Gohr case 'rownumbers': 107*5511bd5bSAndreas Gohr $this->config['rownumbers'] = (bool) $val; 108*5511bd5bSAndreas Gohr break; 109*5511bd5bSAndreas Gohr case 'summarize': 110*5511bd5bSAndreas Gohr $this->config['summarize'] = (bool) $val; 111*5511bd5bSAndreas Gohr break; 112*5511bd5bSAndreas Gohr case 'sepbyheaders': 113*5511bd5bSAndreas Gohr $this->config['sepbyheaders'] = (bool) $val; 114*5511bd5bSAndreas Gohr break; 115*5511bd5bSAndreas Gohr default: 116*5511bd5bSAndreas Gohr throw new StructException("unknown option '%s'", hsc($val)); 117*5511bd5bSAndreas Gohr } 118*5511bd5bSAndreas Gohr } 119*5511bd5bSAndreas Gohr // we need at least one column to display 120*5511bd5bSAndreas Gohr // fill up headers with field names if necessary 121*5511bd5bSAndreas Gohr $this->config['headers'] = (array) $this->config['headers']; 122*5511bd5bSAndreas Gohr $cnth = count($this->config['headers']); 123*5511bd5bSAndreas Gohr $cntf = count($this->config['cols']); 124*5511bd5bSAndreas Gohr for($i = $cnth; $i < $cntf; $i++) { 125*5511bd5bSAndreas Gohr $column = array_slice($this->config['cols'], $i, 1); 126*5511bd5bSAndreas Gohr $columnprops = array_pop($column); 127*5511bd5bSAndreas Gohr $this->config['headers'][] = $columnprops['title']; 128*5511bd5bSAndreas Gohr } 129*5511bd5bSAndreas Gohr 130*5511bd5bSAndreas Gohr return $data; 131*5511bd5bSAndreas Gohr } 132*5511bd5bSAndreas Gohr 133*5511bd5bSAndreas Gohr /** 134*5511bd5bSAndreas Gohr * Get the parsed configuration 135*5511bd5bSAndreas Gohr * 136*5511bd5bSAndreas Gohr * @return array 137*5511bd5bSAndreas Gohr */ 138*5511bd5bSAndreas Gohr public function getConfig() { 139*5511bd5bSAndreas Gohr return $this->config; 140*5511bd5bSAndreas Gohr } 141*5511bd5bSAndreas Gohr 142*5511bd5bSAndreas Gohr /** 143*5511bd5bSAndreas Gohr * Splits the given line into key and value 144*5511bd5bSAndreas Gohr * 145*5511bd5bSAndreas Gohr * @param $line 146*5511bd5bSAndreas Gohr * @return bool|array returns false for empty lines 147*5511bd5bSAndreas Gohr */ 148*5511bd5bSAndreas Gohr protected function splitLine($line) { 149*5511bd5bSAndreas Gohr // ignore comments 150*5511bd5bSAndreas Gohr $line = preg_replace('/(?<![&\\\\])#.*$/', '', $line); 151*5511bd5bSAndreas Gohr $line = str_replace('\\#', '#', $line); 152*5511bd5bSAndreas Gohr $line = trim($line); 153*5511bd5bSAndreas Gohr if(empty($line)) return false; 154*5511bd5bSAndreas Gohr 155*5511bd5bSAndreas Gohr $line = preg_split('/\s*:\s*/', $line, 2); 156*5511bd5bSAndreas Gohr $line[0] = strtolower($line[0]); 157*5511bd5bSAndreas Gohr 158*5511bd5bSAndreas Gohr return $line; 159*5511bd5bSAndreas Gohr } 160*5511bd5bSAndreas Gohr 161*5511bd5bSAndreas Gohr /** 162*5511bd5bSAndreas Gohr * parses schema config and aliases 163*5511bd5bSAndreas Gohr * 164*5511bd5bSAndreas Gohr * @param $val 165*5511bd5bSAndreas Gohr * @return array 166*5511bd5bSAndreas Gohr */ 167*5511bd5bSAndreas Gohr protected function parseSchema($val){ 168*5511bd5bSAndreas Gohr $schemas = array(); 169*5511bd5bSAndreas Gohr $parts = explode(',', $val); 170*5511bd5bSAndreas Gohr foreach($parts as $part) { 171*5511bd5bSAndreas Gohr list($table, $alias) = explode(' ', $part); 172*5511bd5bSAndreas Gohr $table = trim($table); 173*5511bd5bSAndreas Gohr $alias = trim($alias); 174*5511bd5bSAndreas Gohr if(!$table) continue; 175*5511bd5bSAndreas Gohr 176*5511bd5bSAndreas Gohr $schemas[] = array($table, $alias); 177*5511bd5bSAndreas Gohr } 178*5511bd5bSAndreas Gohr return $schemas; 179*5511bd5bSAndreas Gohr } 180*5511bd5bSAndreas Gohr 181*5511bd5bSAndreas Gohr /** 182*5511bd5bSAndreas Gohr * Parse a filter 183*5511bd5bSAndreas Gohr * 184*5511bd5bSAndreas Gohr * @param string $val 185*5511bd5bSAndreas Gohr * @return array ($col, $comp, $value) 186*5511bd5bSAndreas Gohr */ 187*5511bd5bSAndreas Gohr protected function parseFilter($val) { 188*5511bd5bSAndreas Gohr 189*5511bd5bSAndreas Gohr $comps = Search::COMPARATORS; 190*5511bd5bSAndreas Gohr $comps = array_map('preg_quote_cb', $comps); 191*5511bd5bSAndreas Gohr $comps = join('|', $comps); 192*5511bd5bSAndreas Gohr 193*5511bd5bSAndreas Gohr if(!preg_match('/^(.*?)('.$comps.')(.*)$/', $val, $match)) { 194*5511bd5bSAndreas Gohr throw new StructException('Invalid search filter %s', hsc($val)); 195*5511bd5bSAndreas Gohr } 196*5511bd5bSAndreas Gohr array_shift($match); // we don't need the zeroth match 197*5511bd5bSAndreas Gohr return $match; 198*5511bd5bSAndreas Gohr } 199*5511bd5bSAndreas Gohr 200*5511bd5bSAndreas Gohr 201*5511bd5bSAndreas Gohr /** 202*5511bd5bSAndreas Gohr * Parse alignment data 203*5511bd5bSAndreas Gohr * 204*5511bd5bSAndreas Gohr * @param string $val 205*5511bd5bSAndreas Gohr * @return string[] 206*5511bd5bSAndreas Gohr */ 207*5511bd5bSAndreas Gohr protected function parseAlignments($val) { 208*5511bd5bSAndreas Gohr $cols = explode(',', $val); 209*5511bd5bSAndreas Gohr $data = array(); 210*5511bd5bSAndreas Gohr foreach($cols as $col) { 211*5511bd5bSAndreas Gohr $col = trim(strtolower($col)); 212*5511bd5bSAndreas Gohr if($col[0] == 'c') { 213*5511bd5bSAndreas Gohr $align = 'center'; 214*5511bd5bSAndreas Gohr } elseif($col[0] == 'r') { 215*5511bd5bSAndreas Gohr $align = 'right'; 216*5511bd5bSAndreas Gohr } else { 217*5511bd5bSAndreas Gohr $align = 'left'; 218*5511bd5bSAndreas Gohr } 219*5511bd5bSAndreas Gohr $data[] = $align; 220*5511bd5bSAndreas Gohr } 221*5511bd5bSAndreas Gohr 222*5511bd5bSAndreas Gohr return $data; 223*5511bd5bSAndreas Gohr } 224*5511bd5bSAndreas Gohr 225*5511bd5bSAndreas Gohr /** 226*5511bd5bSAndreas Gohr * Split values at the commas, 227*5511bd5bSAndreas Gohr * - Wrap with quotes to escape comma, quotes escaped by two quotes 228*5511bd5bSAndreas Gohr * - Within quotes spaces are stored. 229*5511bd5bSAndreas Gohr * 230*5511bd5bSAndreas Gohr * @param string $line 231*5511bd5bSAndreas Gohr * @return array 232*5511bd5bSAndreas Gohr */ 233*5511bd5bSAndreas Gohr protected function parseValues($line) { 234*5511bd5bSAndreas Gohr $values = array(); 235*5511bd5bSAndreas Gohr $inQuote = false; 236*5511bd5bSAndreas Gohr $escapedQuote = false; 237*5511bd5bSAndreas Gohr $value = ''; 238*5511bd5bSAndreas Gohr $len = strlen($line); 239*5511bd5bSAndreas Gohr for($i = 0; $i < $len; $i++) { 240*5511bd5bSAndreas Gohr if($line{$i} == '"') { 241*5511bd5bSAndreas Gohr if($inQuote) { 242*5511bd5bSAndreas Gohr if($escapedQuote) { 243*5511bd5bSAndreas Gohr $value .= '"'; 244*5511bd5bSAndreas Gohr $escapedQuote = false; 245*5511bd5bSAndreas Gohr continue; 246*5511bd5bSAndreas Gohr } 247*5511bd5bSAndreas Gohr if($line{$i + 1} == '"') { 248*5511bd5bSAndreas Gohr $escapedQuote = true; 249*5511bd5bSAndreas Gohr continue; 250*5511bd5bSAndreas Gohr } 251*5511bd5bSAndreas Gohr array_push($values, $value); 252*5511bd5bSAndreas Gohr $inQuote = false; 253*5511bd5bSAndreas Gohr $value = ''; 254*5511bd5bSAndreas Gohr continue; 255*5511bd5bSAndreas Gohr } else { 256*5511bd5bSAndreas Gohr $inQuote = true; 257*5511bd5bSAndreas Gohr $value = ''; //don't store stuff before the opening quote 258*5511bd5bSAndreas Gohr continue; 259*5511bd5bSAndreas Gohr } 260*5511bd5bSAndreas Gohr } else if($line{$i} == ',') { 261*5511bd5bSAndreas Gohr if($inQuote) { 262*5511bd5bSAndreas Gohr $value .= ','; 263*5511bd5bSAndreas Gohr continue; 264*5511bd5bSAndreas Gohr } else { 265*5511bd5bSAndreas Gohr if(strlen($value) < 1) { 266*5511bd5bSAndreas Gohr continue; 267*5511bd5bSAndreas Gohr } 268*5511bd5bSAndreas Gohr array_push($values, trim($value)); 269*5511bd5bSAndreas Gohr $value = ''; 270*5511bd5bSAndreas Gohr continue; 271*5511bd5bSAndreas Gohr } 272*5511bd5bSAndreas Gohr } 273*5511bd5bSAndreas Gohr $value .= $line{$i}; 274*5511bd5bSAndreas Gohr } 275*5511bd5bSAndreas Gohr if(strlen($value) > 0) { 276*5511bd5bSAndreas Gohr array_push($values, trim($value)); 277*5511bd5bSAndreas Gohr } 278*5511bd5bSAndreas Gohr return $values; 279*5511bd5bSAndreas Gohr } 280*5511bd5bSAndreas Gohr 281*5511bd5bSAndreas Gohr} 282