Initial Drupal 11 with DDEV setup
This commit is contained in:
22
vendor/mck89/peast/lib/Peast/Selector/Exception.php
vendored
Normal file
22
vendor/mck89/peast/lib/Peast/Selector/Exception.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector;
|
||||
|
||||
/**
|
||||
* Selector exception class. Syntax errors in selectors are thrown
|
||||
* using this exception class.
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Exception extends \Exception
|
||||
{
|
||||
}
|
||||
185
vendor/mck89/peast/lib/Peast/Selector/Matches.php
vendored
Normal file
185
vendor/mck89/peast/lib/Peast/Selector/Matches.php
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector;
|
||||
|
||||
use Peast\Syntax\Node\Node;
|
||||
|
||||
/**
|
||||
* Selector matches class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*/
|
||||
class Matches
|
||||
{
|
||||
/**
|
||||
* Matches array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $matches;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $matches Matches
|
||||
*/
|
||||
public function __construct($matches = array())
|
||||
{
|
||||
$this->matches = $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new match
|
||||
*
|
||||
* @param Node $node
|
||||
* @param Node|null $parent
|
||||
*/
|
||||
public function addMatch(Node $node, $parent = null)
|
||||
{
|
||||
$this->matches[] = array($node, $parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matches
|
||||
*
|
||||
* @returns array
|
||||
*/
|
||||
public function getMatches()
|
||||
{
|
||||
return $this->matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matched nodes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNodes() {
|
||||
return array_map(function ($m) {
|
||||
return $m[0];
|
||||
}, $this->matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the matches using the given function, if it returns
|
||||
* a false value the match will be removed. The function will
|
||||
* receive the node and its parent as arguments.
|
||||
*
|
||||
* @param callable $fn Filter function
|
||||
* @return $this
|
||||
*/
|
||||
public function filter(callable $fn)
|
||||
{
|
||||
$newMatches = array();
|
||||
foreach ($this->matches as $match) {
|
||||
if ($fn($match[0], $match[1])) {
|
||||
$newMatches[] = $match;
|
||||
}
|
||||
}
|
||||
$this->matches = $newMatches;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all the matches with the result of the given function.
|
||||
* The function will receive the node and its parent as arguments
|
||||
* and must return an array of matches
|
||||
*
|
||||
* @param callable $fn Map function
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function map(callable $fn)
|
||||
{
|
||||
$newMatches = array();
|
||||
foreach ($this->matches as $match) {
|
||||
$res = $fn($match[0], $match[1]);
|
||||
if ($res) {
|
||||
$newMatches = array_merge($newMatches, $res);
|
||||
}
|
||||
}
|
||||
$this->matches = $newMatches;
|
||||
return $this->unique();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the current object with the other given Matches objects
|
||||
*
|
||||
* @param Matches[] $matchesArr Array of Matches to merge
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function merge($matchesArr)
|
||||
{
|
||||
foreach ($matchesArr as $matches) {
|
||||
foreach ($matches->getMatches() as $match) {
|
||||
$this->addMatch($match[0], $match[1]);
|
||||
}
|
||||
}
|
||||
return $this->unique();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all duplicated matches
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function unique()
|
||||
{
|
||||
$newMatches = array();
|
||||
$newNodes = array();
|
||||
foreach ($this->matches as $match) {
|
||||
if (!in_array($match[0], $newNodes, true)) {
|
||||
$newMatches[] = $match;
|
||||
$newNodes[] = $match[0];
|
||||
}
|
||||
}
|
||||
$this->matches = $newMatches;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a clone of the current object
|
||||
*
|
||||
* @return Matches
|
||||
*/
|
||||
public function createClone()
|
||||
{
|
||||
return new self($this->matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matches
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the match at the given index
|
||||
*
|
||||
* @param int $index Index
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get($index)
|
||||
{
|
||||
$index = (int) $index;
|
||||
if (!isset($this->matches[$index])) {
|
||||
throw new \Exception("Invalid index $index");
|
||||
}
|
||||
return $this->matches[$index];
|
||||
}
|
||||
}
|
||||
139
vendor/mck89/peast/lib/Peast/Selector/Node/Combinator.php
vendored
Normal file
139
vendor/mck89/peast/lib/Peast/Selector/Node/Combinator.php
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector\Node;
|
||||
|
||||
use Peast\Selector\Matches;
|
||||
use Peast\Syntax\Utils;
|
||||
use Peast\Traverser;
|
||||
|
||||
/**
|
||||
* Selector combinator class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*/
|
||||
class Combinator
|
||||
{
|
||||
/**
|
||||
* Operator
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $operator;
|
||||
|
||||
/**
|
||||
* Selector parts
|
||||
*
|
||||
* @var Part\Part[]
|
||||
*/
|
||||
protected $parts = array();
|
||||
|
||||
/**
|
||||
* Sets the operator
|
||||
*
|
||||
* @param string $operator Operator
|
||||
* @return $this
|
||||
*/
|
||||
public function setOperator($operator)
|
||||
{
|
||||
$this->operator = $operator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new selector part
|
||||
*
|
||||
* @param Part\Part $part Part
|
||||
* @return $this
|
||||
*/
|
||||
public function addPart(Part\Part $part)
|
||||
{
|
||||
$this->parts[] = $part;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the current group on the given matches
|
||||
*
|
||||
* @param Matches $matches Matches
|
||||
*/
|
||||
public function exec(Matches $matches)
|
||||
{
|
||||
$parts = $this->parts;
|
||||
//Sort the parts by priority to execute faster checks first
|
||||
usort($parts, function ($p1, $p2) {
|
||||
$pr1 = $p1->getPriority();
|
||||
$pr2 = $p2->getPriority();
|
||||
if ($pr1 === $pr2) {
|
||||
return 0;
|
||||
} elseif ($pr1 < $pr2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
$filter = function ($node, $parent) use ($parts) {
|
||||
foreach ($parts as $part) {
|
||||
if (!$part->check($node, $parent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
switch ($this->operator) {
|
||||
case " ":
|
||||
case ">":
|
||||
$children = $this->operator === ">";
|
||||
$matches->map(function ($curNode) use ($filter, $children) {
|
||||
$ret = array();
|
||||
$curNode->traverse(
|
||||
function ($node, $parent) use ($filter, $children, &$ret) {
|
||||
if ($filter($node, $parent)) {
|
||||
$ret[] = array($node, $parent);
|
||||
}
|
||||
if ($children) {
|
||||
return Traverser::DONT_TRAVERSE_CHILD_NODES;
|
||||
}
|
||||
},
|
||||
array(
|
||||
"skipStartingNode" => true,
|
||||
"passParentNode" => true
|
||||
)
|
||||
);
|
||||
return $ret;
|
||||
});
|
||||
break;
|
||||
case "~":
|
||||
case "+":
|
||||
$adjacent = $this->operator === "+";
|
||||
$matches->map(function ($node, $parent) use ($filter, $adjacent) {
|
||||
$ret = array();
|
||||
$evaluate = false;
|
||||
$props = $parent ? Utils::getExpandedNodeProperties($parent) : array();
|
||||
foreach ($props as $propNode) {
|
||||
if ($evaluate) {
|
||||
if ($propNode && $filter($propNode, $parent)) {
|
||||
$ret[] = array($propNode, $parent);
|
||||
}
|
||||
if ($adjacent) {
|
||||
break;
|
||||
}
|
||||
} elseif ($propNode === $node) {
|
||||
$evaluate = true;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
$matches->filter($filter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
vendor/mck89/peast/lib/Peast/Selector/Node/Group.php
vendored
Normal file
52
vendor/mck89/peast/lib/Peast/Selector/Node/Group.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector\Node;
|
||||
|
||||
use Peast\Selector\Matches;
|
||||
|
||||
/**
|
||||
* Selector group class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*/
|
||||
class Group
|
||||
{
|
||||
/**
|
||||
* Selector combinators
|
||||
*
|
||||
* @var Combinator[]
|
||||
*/
|
||||
protected $combinators = array();
|
||||
|
||||
/**
|
||||
* Adds a combinator
|
||||
*
|
||||
* @param Combinator $combinators Combinator
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addCombinator(Combinator $combinators)
|
||||
{
|
||||
$this->combinators[] = $combinators;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the current group on the given matches
|
||||
*
|
||||
* @param Matches $matches Matches
|
||||
*/
|
||||
public function exec(Matches $matches)
|
||||
{
|
||||
foreach ($this->combinators as $combinator) {
|
||||
$combinator->exec($matches);
|
||||
}
|
||||
}
|
||||
}
|
||||
228
vendor/mck89/peast/lib/Peast/Selector/Node/Part/Attribute.php
vendored
Normal file
228
vendor/mck89/peast/lib/Peast/Selector/Node/Part/Attribute.php
vendored
Normal file
@ -0,0 +1,228 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector\Node\Part;
|
||||
|
||||
use Peast\Syntax\Node\Node;
|
||||
use Peast\Syntax\Utils;
|
||||
|
||||
/**
|
||||
* Selector part attribute class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*/
|
||||
class Attribute extends Part
|
||||
{
|
||||
/**
|
||||
* Priority
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $priority = 4;
|
||||
|
||||
/**
|
||||
* Attribute names
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $names = array();
|
||||
|
||||
/**
|
||||
* Attribute match operator
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $operator = null;
|
||||
|
||||
/**
|
||||
* Attribute value
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value = null;
|
||||
|
||||
/**
|
||||
* Case insensitive flag
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $caseInsensitive = false;
|
||||
|
||||
/**
|
||||
* Regex flag
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $regex = false;
|
||||
|
||||
/**
|
||||
* Adds a name
|
||||
*
|
||||
* @param string $name Name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addName($name)
|
||||
{
|
||||
$this->names[] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the operator
|
||||
*
|
||||
* @param string $operator Operator
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOperator($operator)
|
||||
{
|
||||
$this->operator = $operator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value
|
||||
*
|
||||
* @param mixed $value Value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the case insensitive flag
|
||||
*
|
||||
* @param bool $caseInsensitive Case insensitive flag
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCaseInsensitive($caseInsensitive)
|
||||
{
|
||||
$this->caseInsensitive = $caseInsensitive;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the regex flag
|
||||
*
|
||||
* @param bool $regex Regex flag
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRegex($regex)
|
||||
{
|
||||
$this->regex = $regex;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selector part matches the given node,
|
||||
* false otherwise
|
||||
*
|
||||
* @param Node $node Node
|
||||
* @param Node $parent Parent node
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check(Node $node, $parent = null)
|
||||
{
|
||||
$attr = $node;
|
||||
foreach ($this->names as $name) {
|
||||
$attrFound = false;
|
||||
if ($attr instanceof Node) {
|
||||
$props = Utils::getNodeProperties($attr);
|
||||
foreach ($props as $prop) {
|
||||
if ($prop["name"] === $name) {
|
||||
$attrFound = true;
|
||||
$attr = $attr->{$prop["getter"]}();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$attrFound) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$bothStrings = is_string($attr) && is_string($this->value);
|
||||
switch ($this->operator) {
|
||||
case "=":
|
||||
if ($bothStrings) {
|
||||
if ($this->regex) {
|
||||
return preg_match($this->value, $attr);
|
||||
}
|
||||
return $this->compareStr(
|
||||
$this->value, $attr, $this->caseInsensitive, true, true
|
||||
);
|
||||
}
|
||||
if (is_int($attr) && is_float($this->value)) {
|
||||
return (float) $attr === $this->value;
|
||||
}
|
||||
return $attr === $this->value;
|
||||
case "<":
|
||||
if (is_float($this->value) && !is_float($attr) && !is_int($attr) && !is_string($attr)) {
|
||||
return false;
|
||||
}
|
||||
return $attr < $this->value;
|
||||
case ">":
|
||||
if (is_float($this->value) && !is_float($attr) && !is_int($attr) && !is_string($attr)) {
|
||||
return false;
|
||||
}
|
||||
return $attr > $this->value;
|
||||
case "<=":
|
||||
if (is_float($this->value) && !is_float($attr) && !is_int($attr) && !is_string($attr)) {
|
||||
return false;
|
||||
}
|
||||
return $attr <= $this->value;
|
||||
case ">=":
|
||||
if (is_float($this->value) && !is_float($attr) && !is_int($attr) && !is_string($attr)) {
|
||||
return false;
|
||||
}
|
||||
return $attr >= $this->value;
|
||||
case "^=":
|
||||
case "$=":
|
||||
case "*=":
|
||||
return $this->compareStr(
|
||||
$this->value, $attr, $this->caseInsensitive,
|
||||
$this->operator === "^=",
|
||||
$this->operator === "$="
|
||||
);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two strings
|
||||
*
|
||||
* @param string $v1 Search value
|
||||
* @param string $v2 Compare value
|
||||
* @param bool $caseInsensitive True if the search must be case insensitive
|
||||
* @param bool $matchStart True if the search must be executed from the
|
||||
* beginning of the string
|
||||
* @param bool $matchEnd True if the search must be executed from the
|
||||
* end of the string
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function compareStr($v1, $v2, $caseInsensitive, $matchStart, $matchEnd)
|
||||
{
|
||||
$regex = "#" .
|
||||
($matchStart ? "^" : "") .
|
||||
preg_quote($v1) .
|
||||
($matchEnd ? "$" : "") .
|
||||
"#u" .
|
||||
($caseInsensitive ? "i" : "");
|
||||
return (bool) preg_match($regex, $v2);
|
||||
}
|
||||
}
|
||||
47
vendor/mck89/peast/lib/Peast/Selector/Node/Part/Part.php
vendored
Normal file
47
vendor/mck89/peast/lib/Peast/Selector/Node/Part/Part.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector\Node\Part;
|
||||
|
||||
use Peast\Syntax\Node\Node;
|
||||
|
||||
/**
|
||||
* Selector part base class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
abstract class Part
|
||||
{
|
||||
/**
|
||||
* Priority
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $priority = 5;
|
||||
|
||||
public function getPriority()
|
||||
{
|
||||
return $this->priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selector part matches the given node,
|
||||
* false otherwise
|
||||
*
|
||||
* @param Node $node Node
|
||||
* @param Node $parent Parent node
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
abstract public function check(Node $node, $parent = null);
|
||||
}
|
||||
40
vendor/mck89/peast/lib/Peast/Selector/Node/Part/Pseudo.php
vendored
Normal file
40
vendor/mck89/peast/lib/Peast/Selector/Node/Part/Pseudo.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector\Node\Part;
|
||||
|
||||
/**
|
||||
* Selector pseudo part base class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
abstract class Pseudo extends Part
|
||||
{
|
||||
/**
|
||||
* Selector name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Sets the name
|
||||
*
|
||||
* @param string $name Name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
108
vendor/mck89/peast/lib/Peast/Selector/Node/Part/PseudoIndex.php
vendored
Normal file
108
vendor/mck89/peast/lib/Peast/Selector/Node/Part/PseudoIndex.php
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector\Node\Part;
|
||||
|
||||
use Peast\Syntax\Node\Node;
|
||||
use Peast\Syntax\Utils;
|
||||
|
||||
/**
|
||||
* Selector part index pseudo class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*/
|
||||
class PseudoIndex extends Pseudo
|
||||
{
|
||||
/**
|
||||
* Priority
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $priority = 2;
|
||||
|
||||
/**
|
||||
* Step
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $step = 0;
|
||||
|
||||
/**
|
||||
* Offset
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $offset = 0;
|
||||
|
||||
/**
|
||||
* Sets the step
|
||||
*
|
||||
* @param int $step Step
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setStep($step)
|
||||
{
|
||||
$this->step = $step;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the offset
|
||||
*
|
||||
* @param int $offset Offset
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOffset($offset)
|
||||
{
|
||||
$this->offset = $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selector part matches the given node,
|
||||
* false otherwise
|
||||
*
|
||||
* @param Node $node Node
|
||||
* @param Node $parent Parent node
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check(Node $node, $parent = null)
|
||||
{
|
||||
$props = Utils::getExpandedNodeProperties($parent);
|
||||
$count = count($props);
|
||||
$reverse = $this->name === "nth-last-child";
|
||||
if ($reverse) {
|
||||
$start = $count - 1 - ($this->offset - 1);
|
||||
$step = $this->step * -1;
|
||||
if ($step > 0) {
|
||||
$reverse = false;
|
||||
}
|
||||
} else {
|
||||
$start = $this->offset - 1;
|
||||
$step = $this->step;
|
||||
if ($step < 0) {
|
||||
$reverse = true;
|
||||
}
|
||||
}
|
||||
//Step 0 will cause an infinite loop, so it must be set to the
|
||||
//number of props so that it will execute only one iteration
|
||||
if (!$step) {
|
||||
$step = $reverse ? -$count : $count;
|
||||
}
|
||||
for ($i = $start; ($reverse && $i >= 0) || (!$reverse && $i < $count); $i += $step) {
|
||||
if (isset($props[$i]) && $props[$i] === $node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
66
vendor/mck89/peast/lib/Peast/Selector/Node/Part/PseudoSelector.php
vendored
Normal file
66
vendor/mck89/peast/lib/Peast/Selector/Node/Part/PseudoSelector.php
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector\Node\Part;
|
||||
|
||||
use Peast\Selector\Matches;
|
||||
use Peast\Selector\Node\Selector;
|
||||
use Peast\Syntax\Node\Node;
|
||||
|
||||
/**
|
||||
* Selector part selector pseudo class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*/
|
||||
class PseudoSelector extends Pseudo
|
||||
{
|
||||
/**
|
||||
* Priority
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $priority = 1;
|
||||
|
||||
/**
|
||||
* Selector
|
||||
*
|
||||
* @var Selector
|
||||
*/
|
||||
protected $selector;
|
||||
|
||||
/**
|
||||
* Sets the selector
|
||||
*
|
||||
* @param Selector $selector Selector
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSelector(Selector $selector)
|
||||
{
|
||||
$this->selector = $selector;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selector part matches the given node,
|
||||
* false otherwise
|
||||
*
|
||||
* @param Node $node Node
|
||||
* @param Node $parent Parent node
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check(Node $node, $parent = null)
|
||||
{
|
||||
$match = new Matches();
|
||||
$match->addMatch($node, $parent);
|
||||
$res = $this->selector->exec($match)->count();
|
||||
return $this->name === "not" ? $res === 0 : $res !== 0;
|
||||
}
|
||||
}
|
||||
62
vendor/mck89/peast/lib/Peast/Selector/Node/Part/PseudoSimple.php
vendored
Normal file
62
vendor/mck89/peast/lib/Peast/Selector/Node/Part/PseudoSimple.php
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector\Node\Part;
|
||||
|
||||
use Peast\Syntax\Node\Node;
|
||||
use Peast\Syntax\Node\Pattern;
|
||||
use Peast\Syntax\Node\Statement;
|
||||
use Peast\Syntax\Node\Expression;
|
||||
use Peast\Syntax\Node\Declaration;
|
||||
use Peast\Syntax\Utils;
|
||||
|
||||
/**
|
||||
* Selector part simple pseudo class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*/
|
||||
class PseudoSimple extends Pseudo
|
||||
{
|
||||
/**
|
||||
* Priority
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $priority = 3;
|
||||
|
||||
/**
|
||||
* Returns true if the selector part matches the given node,
|
||||
* false otherwise
|
||||
*
|
||||
* @param Node $node Node
|
||||
* @param Node $parent Parent node
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check(Node $node, $parent = null)
|
||||
{
|
||||
switch ($this->name) {
|
||||
case "pattern":
|
||||
return $node instanceof Pattern;
|
||||
case "statement":
|
||||
return $node instanceof Statement;
|
||||
case "expression":
|
||||
return $node instanceof Expression;
|
||||
case "declaration":
|
||||
return $node instanceof Declaration;
|
||||
case "last-child":
|
||||
case "first-child":
|
||||
$first = $this->name === "first-child";
|
||||
$props = Utils::getExpandedNodeProperties($parent);
|
||||
return count($props) > 0 && (
|
||||
$first ? $props[0] === $node : array_pop($props) === $node
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
vendor/mck89/peast/lib/Peast/Selector/Node/Part/Type.php
vendored
Normal file
54
vendor/mck89/peast/lib/Peast/Selector/Node/Part/Type.php
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector\Node\Part;
|
||||
|
||||
use Peast\Syntax\Node\Node;
|
||||
|
||||
/**
|
||||
* Selector part type class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*/
|
||||
class Type extends Part
|
||||
{
|
||||
/**
|
||||
* Selector type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Sets the selector type
|
||||
*
|
||||
* @param string $type Type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selector part matches the given node,
|
||||
* false otherwise
|
||||
*
|
||||
* @param Node $node Node
|
||||
* @param Node $parent Parent node
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check(Node $node, $parent = null)
|
||||
{
|
||||
return $node->getType() === $this->type;
|
||||
}
|
||||
}
|
||||
61
vendor/mck89/peast/lib/Peast/Selector/Node/Selector.php
vendored
Normal file
61
vendor/mck89/peast/lib/Peast/Selector/Node/Selector.php
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector\Node;
|
||||
|
||||
use Peast\Selector\Matches;
|
||||
|
||||
/**
|
||||
* Selector class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*/
|
||||
class Selector
|
||||
{
|
||||
/**
|
||||
* Selector groups
|
||||
*
|
||||
* @var Group[]
|
||||
*/
|
||||
protected $groups = array();
|
||||
|
||||
/**
|
||||
* Adds a new group
|
||||
*
|
||||
* @param Group $group Group
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addGroup(Group $group)
|
||||
{
|
||||
$this->groups[] = $group;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the current selector on the given matches
|
||||
*
|
||||
* @param Matches $matches Matches
|
||||
*
|
||||
* @return Matches
|
||||
*/
|
||||
public function exec(Matches $matches)
|
||||
{
|
||||
$retMatches = array();
|
||||
foreach ($this->groups as $group) {
|
||||
$clonedMatches = $matches->createClone();
|
||||
$group->exec($clonedMatches);
|
||||
$retMatches[] = $clonedMatches;
|
||||
}
|
||||
if (count($retMatches) > 1) {
|
||||
$retMatches[0]->merge(array_slice($retMatches, 1));
|
||||
}
|
||||
return $retMatches[0];
|
||||
}
|
||||
}
|
||||
615
vendor/mck89/peast/lib/Peast/Selector/Parser.php
vendored
Normal file
615
vendor/mck89/peast/lib/Peast/Selector/Parser.php
vendored
Normal file
@ -0,0 +1,615 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Peast package
|
||||
*
|
||||
* (c) Marco Marchiò <marco.mm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information refer to the LICENSE file
|
||||
* distributed with this source code
|
||||
*/
|
||||
namespace Peast\Selector;
|
||||
|
||||
/**
|
||||
* Selector parser class
|
||||
*
|
||||
* @author Marco Marchiò <marco.mm89@gmail.com>
|
||||
*/
|
||||
class Parser
|
||||
{
|
||||
/**
|
||||
* Selector string
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $selector;
|
||||
|
||||
/**
|
||||
* Current parser index
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $index = 0;
|
||||
|
||||
/**
|
||||
* Selector length
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $length;
|
||||
|
||||
/**
|
||||
* Whitespaces
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $whitespaces = array(" ", "\t", "\n", "\r", "\f");
|
||||
|
||||
/**
|
||||
* Combinators
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $combinators = array(">", "+", "~");
|
||||
|
||||
/**
|
||||
* Attribute selector operator characters
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attrOperatorChars = array("=", "<", ">", "^", "$", "*");
|
||||
|
||||
/**
|
||||
* Attribute selector operators
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attrOperators = array("=", "<", ">", "<=", ">=", "^=", "$=", "*=");
|
||||
|
||||
/**
|
||||
* Valid pseudo selectors. The value indicates the argument type:
|
||||
* - 0: no arguments
|
||||
* - 1: index formula (An+B syntax)
|
||||
* - 2: selector
|
||||
* @var array
|
||||
*/
|
||||
protected $validPseudo = array(
|
||||
"pattern" => 0, "statement" => 0, "expression" => 0, "declaration" => 0,
|
||||
"first-child" => 0, "last-child" => 0,
|
||||
"nth-child" => 1, "nth-last-child" => 1,
|
||||
"has" => 2, "is" => 2, "not" => 2
|
||||
);
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $selector Selector string
|
||||
* @param array $options Options array. See Query class
|
||||
* documentation for available options
|
||||
*/
|
||||
public function __construct($selector, $options = array())
|
||||
{
|
||||
$encoding = isset($options["encoding"]) ? $options["encoding"] : null;
|
||||
if ($encoding && !preg_match("/UTF-?8/i", $encoding)) {
|
||||
$selector = mb_convert_encoding($selector, "UTF-8", $encoding);
|
||||
}
|
||||
$this->selector = $selector;
|
||||
$this->length = strlen($selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the parsing and returns the parsed selector
|
||||
*
|
||||
* @param bool $filter True if the selector must be used for a filter
|
||||
*
|
||||
* @return Node\Selector
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function parse($filter = false)
|
||||
{
|
||||
$selector = $this->parseSelector($filter);
|
||||
//Throw an exception if the end has not been reached
|
||||
if (($char = $this->getChar()) !== null) {
|
||||
throw new Exception("Invalid syntax '$char'");
|
||||
}
|
||||
return $selector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a selector
|
||||
*
|
||||
* @param bool $filter True if the selector must be used for a filter
|
||||
*
|
||||
* @return Node\Selector
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function parseSelector($filter = false)
|
||||
{
|
||||
$selector = new Node\Selector;
|
||||
do {
|
||||
$first = true;
|
||||
$group = new Node\Group;
|
||||
while (true) {
|
||||
$combinator = $this->consumeCombinator();
|
||||
if (!$first && !$combinator) {
|
||||
break;
|
||||
}
|
||||
$parts = $this->parseSelectorParts();
|
||||
if (!count($parts)) {
|
||||
throw new Exception("Missing selector after combinator");
|
||||
}
|
||||
$first = false;
|
||||
$selCombinator = new Node\Combinator;
|
||||
$selCombinator->setOperator(
|
||||
$combinator ?: ($filter ? null : " ")
|
||||
);
|
||||
foreach ($parts as $part) {
|
||||
$selCombinator->addPart($part);
|
||||
}
|
||||
$group->addCombinator($selCombinator);
|
||||
}
|
||||
$selector->addGroup($group);
|
||||
$this->consumeWhitespaces();
|
||||
} while ($this->consume(","));
|
||||
return $selector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a set of selector pats
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function parseSelectorParts()
|
||||
{
|
||||
$parts = array();
|
||||
while (true) {
|
||||
if (
|
||||
($part = $this->parseSelectorPartType()) ||
|
||||
($part = $this->parseSelectorPartAttribute()) ||
|
||||
($part = $this->parseSelectorPartPseudo())
|
||||
) {
|
||||
$parts[] = $part;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a type selector part
|
||||
*
|
||||
* @return Node\Part\Type|null
|
||||
*/
|
||||
protected function parseSelectorPartType()
|
||||
{
|
||||
$type = $this->consumeWord();
|
||||
if ($type) {
|
||||
$part = new Node\Part\Type;
|
||||
$part->setType($type);
|
||||
return $part;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an attribute selector part
|
||||
*
|
||||
* @return Node\Part\Attribute|null
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function parseSelectorPartAttribute()
|
||||
{
|
||||
if (!$this->consume("[")) {
|
||||
return null;
|
||||
}
|
||||
$this->consumeWhitespaces();
|
||||
$part = new Node\Part\Attribute;
|
||||
if (!($name = $this->consumeWord())) {
|
||||
throw new Exception("Missing attribute name");
|
||||
}
|
||||
$part->addName($name);
|
||||
while ($this->consume(".")) {
|
||||
if (!($name = $this->consumeWord())) {
|
||||
throw new Exception("Missing attribute name after dot");
|
||||
}
|
||||
$part->addName($name);
|
||||
}
|
||||
$this->consumeWhitespaces();
|
||||
$operator = $this->consumeAny($this->attrOperatorChars);
|
||||
if ($operator) {
|
||||
if (!in_array($operator, $this->attrOperators)) {
|
||||
throw new Exception("Invalid attribute operator '$operator'");
|
||||
}
|
||||
$part->setOperator($operator);
|
||||
$this->consumeWhitespaces();
|
||||
if (!($value = $this->parseLiteral())) {
|
||||
throw new Exception("Missing attribute value");
|
||||
}
|
||||
$part->setValue($value[0]);
|
||||
if ($value[1]) {
|
||||
if ($operator != "=") {
|
||||
throw new Exception(
|
||||
"Only '=' operator is valid for attribute regex match"
|
||||
);
|
||||
}
|
||||
$part->setRegex(true);
|
||||
}
|
||||
$this->consumeWhitespaces();
|
||||
if ($this->consume("i")) {
|
||||
if (!is_string($value[0]) || $value[1]) {
|
||||
throw new Exception(
|
||||
"Case insensitive flag can be used only for string values"
|
||||
);
|
||||
}
|
||||
$part->setCaseInsensitive(true);
|
||||
$this->consumeWhitespaces();
|
||||
}
|
||||
}
|
||||
if (!$this->consume("]")) {
|
||||
throw new Exception("Unterminated attribute selector");
|
||||
}
|
||||
return $part;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a pseudo selector part
|
||||
*
|
||||
* @return Node\Part\Pseudo|null
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function parseSelectorPartPseudo()
|
||||
{
|
||||
if (!$this->consume(":")) {
|
||||
return null;
|
||||
}
|
||||
$name = $this->consumeWord("-");
|
||||
if (!isset($this->validPseudo[$name])) {
|
||||
throw new Exception("Unsupported pseudo selector '$name'");
|
||||
}
|
||||
$argsType = $this->validPseudo[$name];
|
||||
$error = false;
|
||||
if ($argsType === 1) {
|
||||
$part = new Node\Part\PseudoIndex;
|
||||
if (!$this->consume("(")) {
|
||||
$error = true;
|
||||
}
|
||||
if (!$error) {
|
||||
$this->consumeWhitespaces();
|
||||
if ($indices = $this->consumeRegex("-?\d*n(?:\+\d+)?|\d+")) {
|
||||
$indices = explode("n", $indices);
|
||||
if (count($indices) === 1) {
|
||||
$part->setOffset((int) $indices[0]);
|
||||
} else {
|
||||
switch ($indices[0]) {
|
||||
case "":
|
||||
$part->setStep(1);
|
||||
break;
|
||||
case "-":
|
||||
$part->setStep(-1);
|
||||
break;
|
||||
default:
|
||||
$part->setStep((int) $indices[0]);
|
||||
break;
|
||||
}
|
||||
if ($indices[1] !== "") {
|
||||
$part->setOffset((int) $indices[1]);
|
||||
}
|
||||
}
|
||||
} elseif (
|
||||
($word = $this->consumeWord()) &&
|
||||
($word === "even" || $word === "odd")
|
||||
) {
|
||||
$part->setStep(2);
|
||||
if ($word === "odd") {
|
||||
$part->setOffset(1);
|
||||
}
|
||||
} else {
|
||||
$error = true;
|
||||
}
|
||||
$this->consumeWhitespaces();
|
||||
if (!$error && !$this->consume(")")) {
|
||||
$error = true;
|
||||
}
|
||||
}
|
||||
} elseif ($argsType === 2) {
|
||||
$part = new Node\Part\PseudoSelector;
|
||||
if (
|
||||
$this->consume("(") &&
|
||||
($selector = $this->parseSelector($name !== "has")) &&
|
||||
$this->consume(")")
|
||||
) {
|
||||
$part->setSelector($selector);
|
||||
} else {
|
||||
$error = true;
|
||||
}
|
||||
} else {
|
||||
$part = new Node\Part\PseudoSimple;
|
||||
}
|
||||
if ($error) {
|
||||
throw new Exception(
|
||||
"Invalid argument for pseudo selector '$name'"
|
||||
);
|
||||
}
|
||||
$part->setName($name);
|
||||
return $part;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a literal value
|
||||
*
|
||||
* @return array|null
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function parseLiteral()
|
||||
{
|
||||
if (
|
||||
($literal = $this->parseLiteralBoolNull()) !== 0 ||
|
||||
($literal = $this->parseLiteralString()) !== null ||
|
||||
($literal = $this->parseLiteralNumber()) !== null
|
||||
) {
|
||||
return array($literal, false);
|
||||
} elseif ($literal = $this->parseLiteralRegex()) {
|
||||
return array($literal, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a literal boolean or null value
|
||||
*
|
||||
* @return int|bool|null
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function parseLiteralBoolNull()
|
||||
{
|
||||
$word = $this->consumeWord();
|
||||
if (!$word) {
|
||||
return 0;
|
||||
} elseif ($word === "true") {
|
||||
return true;
|
||||
} elseif ($word === "false") {
|
||||
return false;
|
||||
} elseif ($word === "null") {
|
||||
return null;
|
||||
}
|
||||
throw new Exception("Invalid attribute value '$word'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a literal string
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function parseLiteralString()
|
||||
{
|
||||
if (!($quote = $this->consumeAny(array("'", '"'), true))) {
|
||||
return null;
|
||||
}
|
||||
if (($str = $this->consumeUntil($quote)) === null) {
|
||||
throw new Exception("Unterminated string in attribute value");
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a literal number
|
||||
*
|
||||
* @return int|float|null
|
||||
*/
|
||||
protected function parseLiteralNumber()
|
||||
{
|
||||
if (
|
||||
$this->getChar() === "0" &&
|
||||
($val = $this->consumeRegex("0[xX][a-fA-F]+|0[bB][01]+|0[oO][0-7]+"))
|
||||
) {
|
||||
$form = strtolower($val[1]);
|
||||
$val = substr($val, 2);
|
||||
if ($form === "x") {
|
||||
return hexdec($val);
|
||||
} elseif ($form === "o") {
|
||||
return octdec($val);
|
||||
}
|
||||
return bindec($val);
|
||||
}
|
||||
$reg = "-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|-?\.\d+(?:[eE][+-]?\d+)?";
|
||||
if (!($val = $this->consumeRegex($reg))) {
|
||||
return null;
|
||||
}
|
||||
return (float) $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a literal regex
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function parseLiteralRegex()
|
||||
{
|
||||
if (!($sep = $this->consume("/"))) {
|
||||
return null;
|
||||
}
|
||||
if (($reg = $this->consumeUntil($sep, false, true)) === null) {
|
||||
throw new Exception("Unterminated regex in attribute value");
|
||||
}
|
||||
$modifiers = $this->consumeWord();
|
||||
return $sep . $reg . ($modifiers ?: "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes the given regex
|
||||
*
|
||||
* @param string $regex Regex to consume
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function consumeRegex($regex)
|
||||
{
|
||||
if ($this->getChar() === null) {
|
||||
return null;
|
||||
}
|
||||
if (!preg_match("#^($regex)#", substr($this->selector, $this->index), $matches)) {
|
||||
return null;
|
||||
}
|
||||
$this->index += strlen($matches[1]);
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes all the characters until the given one is reached
|
||||
*
|
||||
* @param string $stop Stop character
|
||||
* @param bool $removeEscapes If false escape characters won't be removed
|
||||
* @param false $includeStop If true stop character will be returned
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function consumeUntil($stop, $removeEscapes = true, $includeStop = false)
|
||||
{
|
||||
$buffer = "";
|
||||
$escaped = false;
|
||||
while (($char = $this->getChar()) !== null) {
|
||||
$this->index += 1;
|
||||
if (!$escaped) {
|
||||
if ($char === "\\") {
|
||||
$escaped = true;
|
||||
if (!$removeEscapes) {
|
||||
$buffer .= $char;
|
||||
}
|
||||
continue;
|
||||
} elseif ($char === $stop) {
|
||||
if ($includeStop) {
|
||||
$buffer .= $char;
|
||||
}
|
||||
return $buffer;
|
||||
}
|
||||
}
|
||||
$buffer .= $char;
|
||||
$escaped = false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes a word composed by characters a-z
|
||||
*
|
||||
* @param null|string $extraChar Extra character to match
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function consumeWord($extraChar = null)
|
||||
{
|
||||
$buffer = "";
|
||||
while ($char = $this->getChar()) {
|
||||
if (
|
||||
($char >= "a" && $char <= "z") ||
|
||||
($char >= "A" && $char <= "Z") ||
|
||||
($extraChar !== null && $char === $extraChar)
|
||||
) {
|
||||
$buffer .= $char;
|
||||
$this->index += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes a combinator
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function consumeCombinator()
|
||||
{
|
||||
//Initial ws can be trimmed if followed by another combinator
|
||||
$ws = $this->consumeWhitespaces();
|
||||
if ($combinator = $this->consumeAny($this->combinators, true)) {
|
||||
$this->consumeWhitespaces();
|
||||
} elseif ($ws) {
|
||||
//If there's no other combinators use the space
|
||||
$combinator = " ";
|
||||
} else {
|
||||
$combinator = null;
|
||||
}
|
||||
return $combinator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes as much whitespaces as possible
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function consumeWhitespaces()
|
||||
{
|
||||
return $this->consumeAny($this->whitespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes the given characters
|
||||
*
|
||||
* @param array $chars Characters to consume
|
||||
* @param false $stopAtFirst If true only the first matching character
|
||||
* is consumed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function consumeAny($chars, $stopAtFirst = false)
|
||||
{
|
||||
$buffer = "";
|
||||
while (($char = $this->getChar()) !== null) {
|
||||
if (in_array($char, $chars)) {
|
||||
$buffer .= $char;
|
||||
$this->index++;
|
||||
if ($stopAtFirst) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes the current character if it is equal to the
|
||||
* given one
|
||||
*
|
||||
* @param string $char Character to compare
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function consume($char)
|
||||
{
|
||||
if ($this->getChar() === $char) {
|
||||
$this->index++;
|
||||
return $char;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current character or null if the end
|
||||
* have been reached
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getChar()
|
||||
{
|
||||
if ($this->index < $this->length) {
|
||||
return $this->selector[$this->index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user