class CssParser in QueryPath 6
Same name and namespace in other branches
- 7.3 QueryPath/CssParser.php \CssParser
- 7.2 QueryPath/CssParser.php \CssParser
Hierarchy
- class \CssParser
Expanded class hierarchy of CssParser
File
- QueryPath/
CssParser.php, line 108
View source
class CssParser {
protected $scanner = NULL;
protected $buffer = '';
protected $handler = NULL;
protected $strict = FALSE;
protected $DEBUG = FALSE;
public function __construct($string, CssEventHandler $handler) {
$this->originalString = $string;
$is = new CssInputStream($string);
$this->scanner = new CssScanner($is);
$this->handler = $handler;
}
public function parse() {
$this->scanner
->nextToken();
while ($this->scanner->token !== FALSE) {
$position = $this->scanner
->position();
if ($this->DEBUG) {
print "PARSE " . $this->scanner->token . "\n";
}
$this
->selector();
$finalPosition = $this->scanner
->position();
if ($this->scanner->token !== FALSE && $finalPosition == $position) {
throw new CssParseException('CSS selector is not well formed.');
}
}
}
private function selector() {
if ($this->DEBUG) {
print "SELECTOR{$this->scanner->position()}\n";
}
$this
->consumeWhitespace();
$this
->simpleSelectors();
$this
->combinator();
}
private function consumeWhitespace() {
if ($this->DEBUG) {
print "CONSUME WHITESPACE\n";
}
$white = 0;
while ($this->scanner->token == CssToken::white) {
$this->scanner
->nextToken();
++$white;
}
return $white;
}
private function combinator() {
if ($this->DEBUG) {
print "COMBINATOR\n";
}
$inCombinator = FALSE;
$white = $this
->consumeWhitespace();
$t = $this->scanner->token;
if ($t == CssToken::rangle) {
$this->handler
->directDescendant();
$this->scanner
->nextToken();
$inCombinator = TRUE;
}
elseif ($t == CssToken::plus) {
$this->handler
->adjacent();
$this->scanner
->nextToken();
$inCombinator = TRUE;
}
elseif ($t == CssToken::comma) {
$this->handler
->anotherSelector();
$this->scanner
->nextToken();
$inCombinator = TRUE;
}
elseif ($t == CssToken::tilde) {
$this->handler
->sibling();
$this->scanner
->nextToken();
$inCombinator = TRUE;
}
if ($inCombinator) {
$white = 0;
if ($this->DEBUG) {
print "COMBINATOR: " . CssToken::name($t) . "\n";
}
$this
->consumeWhitespace();
if ($this
->isCombinator($this->scanner->token)) {
throw new CssParseException("Illegal combinator: Cannot have two combinators in sequence.");
}
}
elseif ($white > 0) {
if ($this->DEBUG) {
print "COMBINATOR: any descendant\n";
}
$inCombinator = TRUE;
$this->handler
->anyDescendant();
}
else {
if ($this->DEBUG) {
print "COMBINATOR: no combinator found.\n";
}
}
}
private function isCombinator($tok) {
$combinators = array(
CssToken::plus,
CssToken::rangle,
CssToken::comma,
CssToken::tilde,
);
return in_array($tok, $combinators);
}
private function simpleSelectors() {
if ($this->DEBUG) {
print "SIMPLE SELECTOR\n";
}
$this
->allElements();
$this
->elementName();
$this
->elementClass();
$this
->elementID();
$this
->pseudoClass();
$this
->attribute();
}
private function elementID() {
if ($this->DEBUG) {
print "ELEMENT ID\n";
}
if ($this->scanner->token == CssToken::octo) {
$this->scanner
->nextToken();
if ($this->scanner->token !== CssToken::char) {
throw new CssParseException("Expected string after #");
}
$id = $this->scanner
->getNameString();
$this->handler
->elementID($id);
}
}
private function elementClass() {
if ($this->DEBUG) {
print "ELEMENT CLASS\n";
}
if ($this->scanner->token == CssToken::dot) {
$this->scanner
->nextToken();
$this
->consumeWhitespace();
$cssClass = $this->scanner
->getNameString();
$this->handler
->elementClass($cssClass);
}
}
private function pseudoClass($restricted = FALSE) {
if ($this->DEBUG) {
print "PSEUDO-CLASS\n";
}
if ($this->scanner->token == CssToken::colon) {
$isPseudoElement = FALSE;
if ($this->scanner
->nextToken() === CssToken::colon) {
$isPseudoElement = TRUE;
$this->scanner
->nextToken();
}
$name = $this->scanner
->getNameString();
if ($restricted && $name == 'not') {
throw new CssParseException("The 'not' pseudo-class is illegal in this context.");
}
$value = NULL;
if ($this->scanner->token == CssToken::lparen) {
if ($isPseudoElement) {
throw new CssParseException("Illegal left paren. Pseudo-Element cannot have arguments.");
}
$value = $this
->pseudoClassValue();
}
if ($isPseudoElement) {
if ($restricted) {
throw new CssParseException("Pseudo-Elements are illegal in this context.");
}
$this->handler
->pseudoElement($name);
$this
->consumeWhitespace();
if ($this->scanner->token !== FALSE && $this->scanner->token !== CssToken::comma) {
throw new CssParseException("A Pseudo-Element must be the last item in a selector.");
}
}
else {
$this->handler
->pseudoClass($name, $value);
}
}
}
private function pseudoClassValue() {
if ($this->scanner->token == CssToken::lparen) {
$buf = '';
$buf .= $this->scanner
->getQuotedString();
return $buf;
}
}
private function elementName() {
if ($this->DEBUG) {
print "ELEMENT NAME\n";
}
if ($this->scanner->token === CssToken::pipe) {
$this->scanner
->nextToken();
$this
->consumeWhitespace();
$elementName = $this->scanner
->getNameString();
$this->handler
->element($elementName);
}
elseif ($this->scanner->token === CssToken::char) {
$elementName = $this->scanner
->getNameString();
if ($this->scanner->token == CssToken::pipe) {
$elementNS = $elementName;
$this->scanner
->nextToken();
$this
->consumeWhitespace();
if ($this->scanner->token === CssToken::star) {
$this->handler
->anyElementInNS($elementNS);
$this->scanner
->nextToken();
}
elseif ($this->scanner->token !== CssToken::char) {
$this
->throwError(CssToken::char, $this->scanner->token);
}
else {
$elementName = $this->scanner
->getNameString();
$this->handler
->elementNS($elementName, $elementNS);
}
}
else {
$this->handler
->element($elementName);
}
}
}
private function allElements() {
if ($this->scanner->token === CssToken::star) {
$this->scanner
->nextToken();
if ($this->scanner->token === CssToken::pipe) {
$this->scanner
->nextToken();
if ($this->scanner->token === CssToken::star) {
$this->scanner
->nextToken();
$this->handler
->anyElementInNS('*');
}
else {
$name = $this->scanner
->getNameString();
$this->handler
->elementNS($name, '*');
}
}
else {
$this->handler
->anyElement();
}
}
}
private function attribute() {
if ($this->scanner->token == CssToken::lsquare) {
$attrVal = $op = $ns = NULL;
$this->scanner
->nextToken();
$this
->consumeWhitespace();
if ($this->scanner->token === CssToken::at) {
if ($this->strict) {
throw new CssParseException('The @ is illegal in attributes.');
}
else {
$this->scanner
->nextToken();
$this
->consumeWhitespace();
}
}
if ($this->scanner->token === CssToken::star) {
$ns = '*';
$this->scanner
->nextToken();
}
if ($this->scanner->token === CssToken::pipe) {
$this->scanner
->nextToken();
$this
->consumeWhitespace();
}
$attrName = $this->scanner
->getNameString();
$this
->consumeWhitespace();
if ($this->scanner->token === CssToken::pipe && $this->scanner
->peek() !== '=') {
$ns = $attrName;
$this->scanner
->nextToken();
$attrName = $this->scanner
->getNameString();
$this
->consumeWhitespace();
}
switch ($this->scanner->token) {
case CssToken::eq:
$this
->consumeWhitespace();
$op = CssEventHandler::isExactly;
break;
case CssToken::tilde:
if ($this->scanner
->nextToken() !== CssToken::eq) {
$this
->throwError(CssToken::eq, $this->scanner->token);
}
$op = CssEventHandler::containsWithSpace;
break;
case CssToken::pipe:
if ($this->scanner
->nextToken() !== CssToken::eq) {
$this
->throwError(CssToken::eq, $this->scanner->token);
}
$op = CssEventHandler::containsWithHyphen;
break;
case CssToken::star:
if ($this->scanner
->nextToken() !== CssToken::eq) {
$this
->throwError(CssToken::eq, $this->scanner->token);
}
$op = CssEventHandler::containsInString;
break;
case CssToken::dollar:
if ($this->scanner
->nextToken() !== CssToken::eq) {
$this
->throwError(CssToken::eq, $this->scanner->token);
}
$op = CssEventHandler::endsWith;
break;
case CssToken::carat:
if ($this->scanner
->nextToken() !== CssToken::eq) {
$this
->throwError(CssToken::eq, $this->scanner->token);
}
$op = CssEventHandler::beginsWith;
break;
}
if (isset($op)) {
$this->scanner
->nextToken();
$this
->consumeWhitespace();
if ($this->scanner->token === CssToken::quote || $this->scanner->token === CssToken::squote) {
$attrVal = $this->scanner
->getQuotedString();
}
else {
$attrVal = $this->scanner
->getNameString();
}
if ($this->DEBUG) {
print "ATTR: {$attrVal} AND OP: {$op}\n";
}
}
$this
->consumeWhitespace();
if ($this->scanner->token != CssToken::rsquare) {
$this
->throwError(CssToken::rsquare, $this->scanner->token);
}
if (isset($ns)) {
$this->handler
->attributeNS($attrName, $ns, $attrVal, $op);
}
elseif (isset($attrVal)) {
$this->handler
->attribute($attrName, $attrVal, $op);
}
else {
$this->handler
->attribute($attrName);
}
$this->scanner
->nextToken();
}
}
private function throwError($expected, $got) {
$filter = sprintf('Expected %s, got %s', CssToken::name($expected), CssToken::name($got));
throw new CssParseException($filter);
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
CssParser:: |
protected | property | ||
CssParser:: |
protected | property | ||
CssParser:: |
protected | property | ||
CssParser:: |
protected | property | ||
CssParser:: |
protected | property | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
public | function | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
private | function | ||
CssParser:: |
public | function |