PHP Classes

File: classes/css-parser/css-parser.php

Recommend this page to a friend!
  Classes of Gonzalo Chumillas   PHP CSS Parser   classes/css-parser/css-parser.php   Download  
File: classes/css-parser/css-parser.php
Role: Class source
Content type: text/plain
Description: CSSParser class
Class: PHP CSS Parser
Get HTML document nodes matching a CSS selector
Author: By
Last change: commit
commit
Merge branch 'master' of https://github.com/cequiel/cssparser
update
Date: 10 years ago
Size: 11,560 bytes
 

Contents

Class file image Download
<?php require_once dirname(__DIR__) . "/parser/parser.php"; require_once dirname(__DIR__) . "/css-parser/css-term.php"; require_once dirname(__DIR__) . "/css-parser/filters/css-attr-filter.php"; require_once dirname(__DIR__) . "/css-parser/filters/css-class-filter.php"; require_once dirname(__DIR__) . "/css-parser/filters/css-id-filter.php"; require_once dirname(__DIR__) . "/css-parser/filters/css-pseudo-first-child-filter.php"; require_once dirname(__DIR__) . "/css-parser/filters/css-pseudo-nth-child-filter.php"; /** * BNF Grammar: * * <expression> ::= <term> {"," <term>} * <term> ::= <factor> {<factor>} * <factor> ::= <operator> <element> | <element> * <element> ::= ("*" | <identifier>) {<filter>} * <filter> ::= <class-filter> | <id-filter> | <attr->filter> | <pseudo-filter> * <class-filter> ::= "." <identifier> * <id-filter> ::= "#" <identifier> * <attr-filter> ::= "[" <identifier> [<attr-operator> <value>] "]" * <pseudo-filter> ::= ":" (<pseudo-first-child-filter> | <pseudo-nth-child-filter>) * <pseudo-nth-child-filter> ::= "nth-child" "(" <number> ")" * <pseudo-first-child-filter> ::= "first-child" * <identifier> ::= ( "_" | <alphanum> ) { "_" | "-" | <alphanum> } * <attr-operator> ::= "=" | "~=" * <operator> ::= ">" | "+" * <value> ::= <quoted string> | <alphanum> {<alphanum>} */ class CSSParser extends Parser { /** * Identifier pattern. */ const IDENTIFIER = "[_a-z0-9][_\-a-z0-9]*"; /** * List of operators. * @var array of strings */ private $operators = array(">", "+"); /** * List of 'attribute operators'. * @var array of strings */ private $attr_operators = array("=", "~="); /** * A node context. * @var DOMNode */ private $node; /** * @param DOMNode $node * @param string $query CSS selector expression */ public function __construct($node, $query) { $this->node = $node; parent::__construct($query); } /** * Is the node in array? * @param DOMNode $node * @param array(DOMNode, ...) $items * @return boolean */ private function isNodeInArray($node, $items) { foreach ($items as $item) { if ($node->isSameNode($item)) { return TRUE; } } return FALSE; } /** * Is the next an operator? * <operator> ::= ">" | "+" * * @return FALSE|array(string) */ protected function operator() { return $this->in($this->operators); } /** * Is the next an attribute operator? * <attr-operator> ::= "=" | "~=" * * @return FALSE|array(string) */ protected function attrOperator() { return $this->in(CSSAttrFilter::getOperators()); } /** * Is the next an identifier? * <identifier> ::= ( "_" | <alphanum> ) { "_" | "-" | <alphanum> } * * @return FALSE|array(string) */ protected function identifier() { if (list($id) = $this->match(CSSparser::IDENTIFIER)) { return array($id); } return FALSE; } /** * Is the next a value? * <value> ::= <a quoted string> | <a list of alphanumeric characters> * * The following examples are values: * 'hello' * "hello\"man" * 'hello\'man' * 0015blah * _blah_ * * @return FALSE|array(string) */ protected function value() { if ( !(list($value) = $this->str()) && !(list($value) = $this->match(CSSParser::IDENTIFIER)) ) { return array($value); } return array($value); } /** * Is the next a nth-child pseudo filter? * <pseudo-nth-child-filter> ::= "nth-child" "(" <number> ")" * * @return FALSE|CSSPseudoNthChildFilter */ protected function pseudoNthChildFilter() { $position = 0; if (!$this->eq("nth-child")) { return FALSE; } if (!$this->eq("(")) { throw new ParserException($this, "Invalid expression"); } if (!list($position) = $this->number()) { throw new ParserException($this, "Invalid position"); } if (!$this->eq(")")) { throw new ParserException($this, "Invalid expression"); } return new CSSPseudoNthChildFilter(intval($position)); } /** * Is the next a first-child pseudo filter? * <pseudo-first-child-filter> ::= "first-child" * * @return FALSE|CSSPseudoFirstChildFilter */ protected function pseudoFirstChildFilter() { if (!$this->eq("first-child")) { return FALSE; } return new CSSPseudoFirstChildFilter(); } /** * Is the next a speudo filter? * <pseudo-filter> ::= ":" (<pseudo-first-child-filter> | <pseudo-nth-child-filter>) * * @return FALSE|CSSPseudoFilter */ protected function pseudoFilter() { $filter = NULL; if (!$this->match("/^\:/")) { return FALSE; } if ( (!$filter = $this->is("pseudoFirstChildFilter")) && (!$filter = $this->is("pseudoNthChildFilter")) ) { throw new ParserException($this, "Invalid pseudo filter"); } return $filter; } /** * Is the next an attribute filter? * <attr-filter> ::= "[" <identifier> (<attr-operator> <value>) "]" * * @return FALSE|CSSAttrFilter */ protected function attrFilter() { $attr_name = ""; $op = ""; $value = ""; if (!$this->match("/^\[/")) { return FALSE; } if (!list($attr_name) = $this->is("identifier")) { throw new ParserException($this, "Invalid identifier"); } if (list($op) = $this->is("attrOperator")) { if (!list($value) = $this->is("value")) { throw new ParserException($this, "Invalid attribute operator"); } } if (!$this->eq("]")) { throw new ParserException($this, "Invalid expression"); } return new CSSAttrFilter($attr_name, $op, $value); } /** * Is the next an id filter? * <id-filter> ::= "#" <identifier> * * @return FALSE|CSSIdFilter */ protected function idFilter() { $id = ""; if (!$this->match("/^\#/")) { return FALSE; } if (!list($id) = $this->is("identifier")) { throw new ParserException($this, "Invalid identifier"); } return new CSSIdFilter($id); } /** * Is the next a class filter? * <class-filter> ::= "." <identifier> * * @return FALSE|CSSClassFilter */ protected function classFilter() { $class_name = ""; if (!$this->match("/^\./")) { return FALSE; } if (!list($class_name) = $this->is("identifier")) { throw new ParserException($this, "Invalid identifier"); } return new CSSClassFilter($class_name); } /** * Is the next a filter? * <filter> ::= <class-filter> | <id-filter> | <attr->filter> | <pseudo-filter> * * @return FALSE|CSSFilter */ protected function filter() { $filter = NULL; if ( (!$filter = $this->is("classFilter")) && (!$filter = $this->is("idFilter")) && (!$filter = $this->is("attrFilter")) && (!$filter = $this->is("pseudoFilter")) ) { return FALSE; } return $filter; } /** * Is the next an element? * <element> ::= ("*" | <identifier> | <filter>) {<filter>} * * The following example is an element: * div.my-class[title = hello]:nth-child(1) * * In the above example, 'div' is the tag name and the following strings are filters: * .my-class // a class filter * [title = hello] // an attribute filter * :nth-child(1) // a pseudo filter * * @return FALSE|CSSElement */ protected function element() { $element = NULL; $filter = NULL; $tag_name = "*"; // ignores left spaces $this->match("\s+"); if ( (list($name) = $this->eq("*")) || (list($name) = $this->is("identifier")) ) {; $tag_name = $name? $name : "*"; } else if (!$filter = $this->is("filter")) { return FALSE; } $element = new CSSElement($tag_name); // first filter if ($filter) { $element->addFilter($filter); } // additional filters while ($filter = $this->is("filter")) { $element->addFilter($filter); } return $element; } /** * Is the next a factor? * <factor> ::= <operator> <element> | <element> * * The following example is a factor: * > div.my-class[title = hello] * * In the above example, ">" is an operator and "div.my-class[title = hello]" is an element. * * @return FALSE|CSSFactor */ protected function factor() { $op = ""; if (list($op) = $this->is("operator")) { if (!$element = $this->is("element")) { throw new ParserException($this, "Invalid expression"); } } else if (!$element = $this->is("element")) { return FALSE; } return new CSSFactor($op, $element); } /** * Is the next a term? * <term> ::= <factor> {<factor>} * * The following example is a term: * div > div.class h2[title = 'main-title'] + h3 * * In the above example, the following strings are factors: * div * > div.class * h2[title = 'main-title'] * + h3 * * @return FALSE|CSSTerm */ protected function term() { $factor = NULL; // first factor if (!$factor = $this->is("factor")) { return FALSE; } $term = new CSSTerm(); $term->addFactor($factor); // additional factors while ($factor = $this->is("factor")) { $term->addFactor($factor); } return $term; } /** * Is the next a css selector expression? * <expression> ::= <term> {"," <term>} * * The following example is an expression: * div > div.class div#id, div > p, pre * * In the above example, the following strings are terms: * div > div.class div * div > p * pre * * @return array(DOMElement, ...) */ protected function expression() { $nodes = array(); do { if (!$term = $this->is("term")) { throw new ParserException($this, "Invalid expression"); } $nodes = CSSHelper::mergeNodes($nodes, $term->filter($this->node)); } while ($this->eq(",")); return $nodes; } protected function _parse() { return $this->is("expression"); } }