protected function parse_conditions()
{
$p =& $this->parser;
$tok = $p->token;
if ($tok === CSSQueryTokenizer::TOK_NULL) {
$this->error('Invalid search pattern(1): Empty string!');
return false;
}
$conditions_all = array();
//Tags
while ($tok !== CSSQueryTokenizer::TOK_NULL) {
$conditions = array('tags' => array(), 'attributes' => array());
if ($tok === CSSQueryTokenizer::TOK_ALL) {
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_PIPE && ($tok = $p->next()) && $tok !== CSSQueryTokenizer::TOK_ALL) {
if (($tag = $this->parse_getIdentifier()) === false) {
return false;
}
$conditions['tags'][] = array('tag' => $tag, 'compare' => 'name');
$tok = $p->next_no_whitespace();
} else {
$conditions['tags'][''] = array('tag' => '', 'match' => false);
if ($tok === CSSQueryTokenizer::TOK_ALL) {
$tok = $p->next_no_whitespace();
}
}
} elseif ($tok === CSSQueryTokenizer::TOK_PIPE) {
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_ALL) {
$conditions['tags'][] = array('tag' => '', 'compare' => 'namespace');
} elseif (($tag = $this->parse_getIdentifier()) !== false) {
$conditions['tags'][] = array('tag' => $tag, 'compare' => 'total');
} else {
return false;
}
$tok = $p->next_no_whitespace();
} elseif ($tok === CSSQueryTokenizer::TOK_BRACE_OPEN) {
$tok = $p->next_no_whitespace();
$last_mode = 'or';
while (true) {
$match = true;
$compare = 'total';
if ($tok === CSSQueryTokenizer::TOK_NOT) {
$match = false;
$tok = $p->next_no_whitespace();
}
if ($tok === CSSQueryTokenizer::TOK_ALL) {
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_PIPE) {
$this->next();
$compare = 'name';
if (($tag = $this->parse_getIdentifier()) === false) {
return false;
}
}
} elseif ($tok === CSSQueryTokenizer::TOK_PIPE) {
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_ALL) {
$tag = '';
$compare = 'namespace';
} elseif (($tag = $this->parse_getIdentifier()) === false) {
return false;
}
$tok = $p->next_no_whitespace();
} else {
if (($tag = $this->parse_getIdentifier()) === false) {
return false;
}
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_PIPE) {
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_ALL) {
$compare = 'namespace';
} elseif (($tag_name = $this->parse_getIdentifier()) !== false) {
$tag = $tag . ':' . $tag_name;
} else {
return false;
}
$tok = $p->next_no_whitespace();
}
}
if ($tok === CSSQueryTokenizer::TOK_WHITESPACE) {
$tok = $p->next_no_whitespace();
}
$conditions['tags'][] = array('tag' => $tag, 'match' => $match, 'operator' => $last_mode, 'compare' => $compare);
switch ($tok) {
case CSSQueryTokenizer::TOK_COMMA:
$tok = $p->next_no_whitespace();
$last_mode = 'or';
continue 2;
case CSSQueryTokenizer::TOK_PLUS:
$tok = $p->next_no_whitespace();
$last_mode = 'and';
continue 2;
case CSSQueryTokenizer::TOK_BRACE_CLOSE:
$tok = $p->next();
break 2;
default:
$this->error('Expected closing brace or comma at pos %pos%!');
return false;
}
}
} elseif (($tag = $this->parse_getIdentifier(false)) !== false) {
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_PIPE) {
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_ALL) {
$conditions['tags'][] = array('tag' => $tag, 'compare' => 'namespace');
} elseif (($tag_name = $this->parse_getIdentifier()) !== false) {
$tag = $tag . ':' . $tag_name;
$conditions['tags'][] = array('tag' => $tag, 'match' => true);
} else {
return false;
}
$tok = $p->next();
} elseif ($tag === 'text' && $tok === CSSQueryTokenizer::TOK_BRACE_OPEN) {
$pos = $p->getPos();
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_BRACE_CLOSE) {
$conditions['tags'][] = array('tag' => '~text~', 'match' => true);
$p->next();
} else {
$p->setPos($pos);
}
} else {
$conditions['tags'][] = array('tag' => $tag, 'match' => true);
}
} else {
unset($conditions['tags']);
}
//Class
$last_mode = 'or';
if ($tok === CSSQueryTokenizer::TOK_CLASS) {
$p->next();
if (($class = $this->parse_getIdentifier()) === false) {
return false;
}
$conditions['attributes'][] = array('attribute' => 'class', 'operator_value' => 'contains_word', 'value' => $class, 'operator_result' => $last_mode);
$last_mode = 'and';
$tok = $p->next();
}
//ID
if ($tok === CSSQueryTokenizer::TOK_ID) {
$p->next();
if (($id = $this->parse_getIdentifier()) === false) {
return false;
}
$conditions['attributes'][] = array('attribute' => 'id', 'operator_value' => 'equals', 'value' => $id, 'operator_result' => $last_mode);
$last_mode = 'and';
$tok = $p->next();
}
//Attributes
if ($tok === CSSQueryTokenizer::TOK_BRACKET_OPEN) {
$tok = $p->next_no_whitespace();
while (true) {
$match = true;
$compare = 'total';
if ($tok === CSSQueryTokenizer::TOK_NOT) {
$match = false;
$tok = $p->next_no_whitespace();
}
if ($tok === CSSQueryTokenizer::TOK_ALL) {
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_PIPE) {
$tok = $p->next();
if (($attribute = $this->parse_getIdentifier()) === false) {
return false;
}
$compare = 'name';
$tok = $p->next();
} else {
$this->error('Expected pipe at pos %pos%!');
return false;
}
} elseif ($tok === CSSQueryTokenizer::TOK_PIPE) {
$tok = $p->next();
if (($tag = $this->parse_getIdentifier()) === false) {
return false;
}
$tok = $p->next_no_whitespace();
} elseif (($attribute = $this->parse_getIdentifier()) !== false) {
$tok = $p->next();
if ($tok === CSSQueryTokenizer::TOK_PIPE) {
$tok = $p->next();
if (($attribute_name = $this->parse_getIdentifier()) !== false) {
$attribute = $attribute . ':' . $attribute_name;
} else {
return false;
}
$tok = $p->next();
}
} else {
return false;
}
if ($tok === CSSQueryTokenizer::TOK_WHITESPACE) {
$tok = $p->next_no_whitespace();
}
$operator_value = '';
$val = '';
switch ($tok) {
case CSSQueryTokenizer::TOK_COMPARE_PREFIX:
case CSSQueryTokenizer::TOK_COMPARE_CONTAINS:
case CSSQueryTokenizer::TOK_COMPARE_CONTAINS_WORD:
case CSSQueryTokenizer::TOK_COMPARE_ENDS:
case CSSQueryTokenizer::TOK_COMPARE_EQUALS:
case CSSQueryTokenizer::TOK_COMPARE_NOT_EQUAL:
case CSSQueryTokenizer::TOK_COMPARE_REGEX:
case CSSQueryTokenizer::TOK_COMPARE_STARTS:
case CSSQueryTokenizer::TOK_COMPARE_BIGGER_THAN:
case CSSQueryTokenizer::TOK_COMPARE_SMALLER_THAN:
$operator_value = $p->getTokenString($tok === CSSQueryTokenizer::TOK_COMPARE_EQUALS ? 0 : -1);
$p->next_no_whitespace();
if (($val = $this->parse_getIdentifier()) === false) {
return false;
}
$tok = $p->next_no_whitespace();
break;
}
if ($operator_value && $val) {
$conditions['attributes'][] = array('attribute' => $attribute, 'operator_value' => $operator_value, 'value' => $val, 'match' => $match, 'operator_result' => $last_mode, 'compare' => $compare);
} else {
$conditions['attributes'][] = array('attribute' => $attribute, 'value' => $match, 'operator_result' => $last_mode, 'compare' => $compare);
}
switch ($tok) {
case CSSQueryTokenizer::TOK_COMMA:
$tok = $p->next_no_whitespace();
$last_mode = 'or';
continue 2;
case CSSQueryTokenizer::TOK_PLUS:
$tok = $p->next_no_whitespace();
$last_mode = 'and';
continue 2;
case CSSQueryTokenizer::TOK_BRACKET_CLOSE:
$tok = $p->next();
break 2;
default:
$this->error('Expected closing bracket or comma at pos %pos%!');
return false;
}
}
}
if (count($conditions['attributes']) < 1) {
unset($conditions['attributes']);
}
while ($tok === CSSQueryTokenizer::TOK_COLON) {
if (count($conditions) < 1) {
$conditions['tags'] = array(array('tag' => '', 'match' => false));
}
$tok = $p->next();
if (($filter = $this->parse_getIdentifier()) === false) {
return false;
}
if (($tok = $p->next()) === CSSQueryTokenizer::TOK_BRACE_OPEN) {
$start = $p->pos;
$count = 1;
while (($tok = $p->next()) !== CSSQueryTokenizer::TOK_NULL && !($tok === CSSQueryTokenizer::TOK_BRACE_CLOSE && --$count === 0)) {
if ($tok === CSSQueryTokenizer::TOK_BRACE_OPEN) {
++$count;
}
}
if ($tok !== CSSQueryTokenizer::TOK_BRACE_CLOSE) {
$this->error('Expected closing brace at pos %pos%!');
return false;
}
$len = $p->pos - 1 - $start;
$params = $len > 0 ? substr($p->doc, $start + 1, $len) : '';
$tok = $p->next();
} else {
$params = '';
}
$conditions['filters'][] = array('filter' => $filter, 'params' => $params);
}
if (count($conditions) < 1) {
$this->error('Invalid search pattern(2): No conditions found!');
return false;
}
$conditions_all[] = $conditions;
if ($tok === CSSQueryTokenizer::TOK_WHITESPACE) {
$tok = $p->next_no_whitespace();
}
if ($tok === CSSQueryTokenizer::TOK_COMMA) {
$tok = $p->next_no_whitespace();
continue;
} else {
break;
}
}
return $conditions_all;
}