scratch
/vendor/symfony/css-selector/.gitignore |
---|
@@ -0,0 +1,3 @@ |
vendor/ |
composer.lock |
phpunit.xml |
/vendor/symfony/css-selector/CHANGELOG.md |
---|
@@ -0,0 +1,13 @@ |
CHANGELOG |
========= |
2.8.0 |
----- |
* Added the `CssSelectorConverter` class as a non-static API for the component. |
* Deprecated the `CssSelector` static API of the component. |
2.1.0 |
----- |
* none |
/vendor/symfony/css-selector/CssSelector.php |
---|
@@ -0,0 +1,98 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector; |
@trigger_error('The '.__NAMESPACE__.'\CssSelector class is deprecated since version 2.8 and will be removed in 3.0. Use directly the \Symfony\Component\CssSelector\CssSelectorConverter class instead.', E_USER_DEPRECATED); |
/** |
* CssSelector is the main entry point of the component and can convert CSS |
* selectors to XPath expressions. |
* |
* $xpath = CssSelector::toXpath('h1.foo'); |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* Copyright (c) 2007-2012 Ian Bicking and contributors. See AUTHORS |
* for more details. |
* |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions are |
* met: |
* |
* 1. Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* |
* 2. Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in |
* the documentation and/or other materials provided with the |
* distribution. |
* |
* 3. Neither the name of Ian Bicking nor the names of its contributors may |
* be used to endorse or promote products derived from this software |
* without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IAN BICKING OR |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* |
* @author Fabien Potencier <fabien@symfony.com> |
* |
* @deprecated as of 2.8, will be removed in 3.0. Use the \Symfony\Component\CssSelector\CssSelectorConverter class instead. |
*/ |
class CssSelector |
{ |
private static $html = true; |
/** |
* Translates a CSS expression to its XPath equivalent. |
* Optionally, a prefix can be added to the resulting XPath |
* expression with the $prefix parameter. |
* |
* @param mixed $cssExpr The CSS expression |
* @param string $prefix An optional prefix for the XPath expression |
* |
* @return string |
*/ |
public static function toXPath($cssExpr, $prefix = 'descendant-or-self::') |
{ |
$converter = new CssSelectorConverter(self::$html); |
return $converter->toXPath($cssExpr, $prefix); |
} |
/** |
* Enables the HTML extension. |
*/ |
public static function enableHtmlExtension() |
{ |
self::$html = true; |
} |
/** |
* Disables the HTML extension. |
*/ |
public static function disableHtmlExtension() |
{ |
self::$html = false; |
} |
} |
/vendor/symfony/css-selector/CssSelectorConverter.php |
---|
@@ -0,0 +1,65 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector; |
use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser; |
use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser; |
use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser; |
use Symfony\Component\CssSelector\Parser\Shortcut\HashParser; |
use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension; |
use Symfony\Component\CssSelector\XPath\Translator; |
/** |
* CssSelectorConverter is the main entry point of the component and can convert CSS |
* selectors to XPath expressions. |
* |
* @author Christophe Coevoet <stof@notk.org> |
*/ |
class CssSelectorConverter |
{ |
private $translator; |
/** |
* @param bool $html Whether HTML support should be enabled. Disable it for XML documents |
*/ |
public function __construct($html = true) |
{ |
$this->translator = new Translator(); |
if ($html) { |
$this->translator->registerExtension(new HtmlExtension($this->translator)); |
} |
$this->translator |
->registerParserShortcut(new EmptyStringParser()) |
->registerParserShortcut(new ElementParser()) |
->registerParserShortcut(new ClassParser()) |
->registerParserShortcut(new HashParser()) |
; |
} |
/** |
* Translates a CSS expression to its XPath equivalent. |
* |
* Optionally, a prefix can be added to the resulting XPath |
* expression with the $prefix parameter. |
* |
* @param string $cssExpr The CSS expression |
* @param string $prefix An optional prefix for the XPath expression |
* |
* @return string |
*/ |
public function toXPath($cssExpr, $prefix = 'descendant-or-self::') |
{ |
return $this->translator->cssToXPath($cssExpr, $prefix); |
} |
} |
/vendor/symfony/css-selector/Exception/ExceptionInterface.php |
---|
@@ -0,0 +1,24 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Exception; |
/** |
* Interface for exceptions. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
*/ |
interface ExceptionInterface |
{ |
} |
/vendor/symfony/css-selector/Exception/ExpressionErrorException.php |
---|
@@ -0,0 +1,24 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Exception; |
/** |
* ParseException is thrown when a CSS selector syntax is not valid. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
*/ |
class ExpressionErrorException extends ParseException |
{ |
} |
/vendor/symfony/css-selector/Exception/InternalErrorException.php |
---|
@@ -0,0 +1,24 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Exception; |
/** |
* ParseException is thrown when a CSS selector syntax is not valid. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
*/ |
class InternalErrorException extends ParseException |
{ |
} |
/vendor/symfony/css-selector/Exception/ParseException.php |
---|
@@ -0,0 +1,24 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Exception; |
/** |
* ParseException is thrown when a CSS selector syntax is not valid. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Fabien Potencier <fabien@symfony.com> |
*/ |
class ParseException extends \Exception implements ExceptionInterface |
{ |
} |
/vendor/symfony/css-selector/Exception/SyntaxErrorException.php |
---|
@@ -0,0 +1,73 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Exception; |
use Symfony\Component\CssSelector\Parser\Token; |
/** |
* ParseException is thrown when a CSS selector syntax is not valid. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
*/ |
class SyntaxErrorException extends ParseException |
{ |
/** |
* @param string $expectedValue |
* @param Token $foundToken |
* |
* @return self |
*/ |
public static function unexpectedToken($expectedValue, Token $foundToken) |
{ |
return new self(sprintf('Expected %s, but %s found.', $expectedValue, $foundToken)); |
} |
/** |
* @param string $pseudoElement |
* @param string $unexpectedLocation |
* |
* @return self |
*/ |
public static function pseudoElementFound($pseudoElement, $unexpectedLocation) |
{ |
return new self(sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation)); |
} |
/** |
* @param int $position |
* |
* @return self |
*/ |
public static function unclosedString($position) |
{ |
return new self(sprintf('Unclosed/invalid string at %s.', $position)); |
} |
/** |
* @return self |
*/ |
public static function nestedNot() |
{ |
return new self('Got nested ::not().'); |
} |
/** |
* @return self |
*/ |
public static function stringAsFunctionArgument() |
{ |
return new self('String not allowed as function argument.'); |
} |
} |
/vendor/symfony/css-selector/LICENSE |
---|
@@ -0,0 +1,19 @@ |
Copyright (c) 2004-2017 Fabien Potencier |
Permission is hereby granted, free of charge, to any person obtaining a copy |
of this software and associated documentation files (the "Software"), to deal |
in the Software without restriction, including without limitation the rights |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
copies of the Software, and to permit persons to whom the Software is furnished |
to do so, subject to the following conditions: |
The above copyright notice and this permission notice shall be included in all |
copies or substantial portions of the Software. |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
THE SOFTWARE. |
/vendor/symfony/css-selector/Node/AbstractNode.php |
---|
@@ -0,0 +1,42 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Abstract base node class. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
abstract class AbstractNode implements NodeInterface |
{ |
/** |
* @var string |
*/ |
private $nodeName; |
/** |
* @return string |
*/ |
public function getNodeName() |
{ |
if (null === $this->nodeName) { |
$this->nodeName = preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', get_called_class()); |
} |
return $this->nodeName; |
} |
} |
/vendor/symfony/css-selector/Node/AttributeNode.php |
---|
@@ -0,0 +1,126 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Represents a "<selector>[<namespace>|<attribute> <operator> <value>]" node. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class AttributeNode extends AbstractNode |
{ |
/** |
* @var NodeInterface |
*/ |
private $selector; |
/** |
* @var string |
*/ |
private $namespace; |
/** |
* @var string |
*/ |
private $attribute; |
/** |
* @var string |
*/ |
private $operator; |
/** |
* @var string |
*/ |
private $value; |
/** |
* @param NodeInterface $selector |
* @param string $namespace |
* @param string $attribute |
* @param string $operator |
* @param string $value |
*/ |
public function __construct(NodeInterface $selector, $namespace, $attribute, $operator, $value) |
{ |
$this->selector = $selector; |
$this->namespace = $namespace; |
$this->attribute = $attribute; |
$this->operator = $operator; |
$this->value = $value; |
} |
/** |
* @return NodeInterface |
*/ |
public function getSelector() |
{ |
return $this->selector; |
} |
/** |
* @return string |
*/ |
public function getNamespace() |
{ |
return $this->namespace; |
} |
/** |
* @return string |
*/ |
public function getAttribute() |
{ |
return $this->attribute; |
} |
/** |
* @return string |
*/ |
public function getOperator() |
{ |
return $this->operator; |
} |
/** |
* @return string |
*/ |
public function getValue() |
{ |
return $this->value; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getSpecificity() |
{ |
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); |
} |
/** |
* {@inheritdoc} |
*/ |
public function __toString() |
{ |
$attribute = $this->namespace ? $this->namespace.'|'.$this->attribute : $this->attribute; |
return 'exists' === $this->operator |
? sprintf('%s[%s[%s]]', $this->getNodeName(), $this->selector, $attribute) |
: sprintf("%s[%s[%s %s '%s']]", $this->getNodeName(), $this->selector, $attribute, $this->operator, $this->value); |
} |
} |
/vendor/symfony/css-selector/Node/ClassNode.php |
---|
@@ -0,0 +1,77 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Represents a "<selector>.<name>" node. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class ClassNode extends AbstractNode |
{ |
/** |
* @var NodeInterface |
*/ |
private $selector; |
/** |
* @var string |
*/ |
private $name; |
/** |
* @param NodeInterface $selector |
* @param string $name |
*/ |
public function __construct(NodeInterface $selector, $name) |
{ |
$this->selector = $selector; |
$this->name = $name; |
} |
/** |
* @return NodeInterface |
*/ |
public function getSelector() |
{ |
return $this->selector; |
} |
/** |
* @return string |
*/ |
public function getName() |
{ |
return $this->name; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getSpecificity() |
{ |
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); |
} |
/** |
* {@inheritdoc} |
*/ |
public function __toString() |
{ |
return sprintf('%s[%s.%s]', $this->getNodeName(), $this->selector, $this->name); |
} |
} |
/vendor/symfony/css-selector/Node/CombinedSelectorNode.php |
---|
@@ -0,0 +1,94 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Represents a combined node. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class CombinedSelectorNode extends AbstractNode |
{ |
/** |
* @var NodeInterface |
*/ |
private $selector; |
/** |
* @var string |
*/ |
private $combinator; |
/** |
* @var NodeInterface |
*/ |
private $subSelector; |
/** |
* @param NodeInterface $selector |
* @param string $combinator |
* @param NodeInterface $subSelector |
*/ |
public function __construct(NodeInterface $selector, $combinator, NodeInterface $subSelector) |
{ |
$this->selector = $selector; |
$this->combinator = $combinator; |
$this->subSelector = $subSelector; |
} |
/** |
* @return NodeInterface |
*/ |
public function getSelector() |
{ |
return $this->selector; |
} |
/** |
* @return string |
*/ |
public function getCombinator() |
{ |
return $this->combinator; |
} |
/** |
* @return NodeInterface |
*/ |
public function getSubSelector() |
{ |
return $this->subSelector; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getSpecificity() |
{ |
return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); |
} |
/** |
* {@inheritdoc} |
*/ |
public function __toString() |
{ |
$combinator = ' ' === $this->combinator ? '<followed>' : $this->combinator; |
return sprintf('%s[%s %s %s]', $this->getNodeName(), $this->selector, $combinator, $this->subSelector); |
} |
} |
/vendor/symfony/css-selector/Node/ElementNode.php |
---|
@@ -0,0 +1,79 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Represents a "<namespace>|<element>" node. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class ElementNode extends AbstractNode |
{ |
/** |
* @var string|null |
*/ |
private $namespace; |
/** |
* @var string|null |
*/ |
private $element; |
/** |
* @param string|null $namespace |
* @param string|null $element |
*/ |
public function __construct($namespace = null, $element = null) |
{ |
$this->namespace = $namespace; |
$this->element = $element; |
} |
/** |
* @return null|string |
*/ |
public function getNamespace() |
{ |
return $this->namespace; |
} |
/** |
* @return null|string |
*/ |
public function getElement() |
{ |
return $this->element; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getSpecificity() |
{ |
return new Specificity(0, 0, $this->element ? 1 : 0); |
} |
/** |
* {@inheritdoc} |
*/ |
public function __toString() |
{ |
$element = $this->element ?: '*'; |
return sprintf('%s[%s]', $this->getNodeName(), $this->namespace ? $this->namespace.'|'.$element : $element); |
} |
} |
/vendor/symfony/css-selector/Node/FunctionNode.php |
---|
@@ -0,0 +1,98 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
use Symfony\Component\CssSelector\Parser\Token; |
/** |
* Represents a "<selector>:<name>(<arguments>)" node. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class FunctionNode extends AbstractNode |
{ |
/** |
* @var NodeInterface |
*/ |
private $selector; |
/** |
* @var string |
*/ |
private $name; |
/** |
* @var Token[] |
*/ |
private $arguments; |
/** |
* @param NodeInterface $selector |
* @param string $name |
* @param Token[] $arguments |
*/ |
public function __construct(NodeInterface $selector, $name, array $arguments = array()) |
{ |
$this->selector = $selector; |
$this->name = strtolower($name); |
$this->arguments = $arguments; |
} |
/** |
* @return NodeInterface |
*/ |
public function getSelector() |
{ |
return $this->selector; |
} |
/** |
* @return string |
*/ |
public function getName() |
{ |
return $this->name; |
} |
/** |
* @return Token[] |
*/ |
public function getArguments() |
{ |
return $this->arguments; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getSpecificity() |
{ |
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); |
} |
/** |
* {@inheritdoc} |
*/ |
public function __toString() |
{ |
$arguments = implode(', ', array_map(function (Token $token) { |
return "'".$token->getValue()."'"; |
}, $this->arguments)); |
return sprintf('%s[%s:%s(%s)]', $this->getNodeName(), $this->selector, $this->name, $arguments ? '['.$arguments.']' : ''); |
} |
} |
/vendor/symfony/css-selector/Node/HashNode.php |
---|
@@ -0,0 +1,77 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Represents a "<selector>#<id>" node. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class HashNode extends AbstractNode |
{ |
/** |
* @var NodeInterface |
*/ |
private $selector; |
/** |
* @var string |
*/ |
private $id; |
/** |
* @param NodeInterface $selector |
* @param string $id |
*/ |
public function __construct(NodeInterface $selector, $id) |
{ |
$this->selector = $selector; |
$this->id = $id; |
} |
/** |
* @return NodeInterface |
*/ |
public function getSelector() |
{ |
return $this->selector; |
} |
/** |
* @return string |
*/ |
public function getId() |
{ |
return $this->id; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getSpecificity() |
{ |
return $this->selector->getSpecificity()->plus(new Specificity(1, 0, 0)); |
} |
/** |
* {@inheritdoc} |
*/ |
public function __toString() |
{ |
return sprintf('%s[%s#%s]', $this->getNodeName(), $this->selector, $this->id); |
} |
} |
/vendor/symfony/css-selector/Node/NegationNode.php |
---|
@@ -0,0 +1,77 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Represents a "<selector>:not(<identifier>)" node. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class NegationNode extends AbstractNode |
{ |
/** |
* @var NodeInterface |
*/ |
private $selector; |
/** |
* @var NodeInterface |
*/ |
private $subSelector; |
/** |
* @param NodeInterface $selector |
* @param NodeInterface $subSelector |
*/ |
public function __construct(NodeInterface $selector, NodeInterface $subSelector) |
{ |
$this->selector = $selector; |
$this->subSelector = $subSelector; |
} |
/** |
* @return NodeInterface |
*/ |
public function getSelector() |
{ |
return $this->selector; |
} |
/** |
* @return NodeInterface |
*/ |
public function getSubSelector() |
{ |
return $this->subSelector; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getSpecificity() |
{ |
return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); |
} |
/** |
* {@inheritdoc} |
*/ |
public function __toString() |
{ |
return sprintf('%s[%s:not(%s)]', $this->getNodeName(), $this->selector, $this->subSelector); |
} |
} |
/vendor/symfony/css-selector/Node/NodeInterface.php |
---|
@@ -0,0 +1,46 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Interface for nodes. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
interface NodeInterface |
{ |
/** |
* Returns node's name. |
* |
* @return string |
*/ |
public function getNodeName(); |
/** |
* Returns node's specificity. |
* |
* @return Specificity |
*/ |
public function getSpecificity(); |
/** |
* Returns node's string representation. |
* |
* @return string |
*/ |
public function __toString(); |
} |
/vendor/symfony/css-selector/Node/PseudoNode.php |
---|
@@ -0,0 +1,77 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Represents a "<selector>:<identifier>" node. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class PseudoNode extends AbstractNode |
{ |
/** |
* @var NodeInterface |
*/ |
private $selector; |
/** |
* @var string |
*/ |
private $identifier; |
/** |
* @param NodeInterface $selector |
* @param string $identifier |
*/ |
public function __construct(NodeInterface $selector, $identifier) |
{ |
$this->selector = $selector; |
$this->identifier = strtolower($identifier); |
} |
/** |
* @return NodeInterface |
*/ |
public function getSelector() |
{ |
return $this->selector; |
} |
/** |
* @return string |
*/ |
public function getIdentifier() |
{ |
return $this->identifier; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getSpecificity() |
{ |
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); |
} |
/** |
* {@inheritdoc} |
*/ |
public function __toString() |
{ |
return sprintf('%s[%s:%s]', $this->getNodeName(), $this->selector, $this->identifier); |
} |
} |
/vendor/symfony/css-selector/Node/SelectorNode.php |
---|
@@ -0,0 +1,77 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Represents a "<selector>(::|:)<pseudoElement>" node. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class SelectorNode extends AbstractNode |
{ |
/** |
* @var NodeInterface |
*/ |
private $tree; |
/** |
* @var null|string |
*/ |
private $pseudoElement; |
/** |
* @param NodeInterface $tree |
* @param null|string $pseudoElement |
*/ |
public function __construct(NodeInterface $tree, $pseudoElement = null) |
{ |
$this->tree = $tree; |
$this->pseudoElement = $pseudoElement ? strtolower($pseudoElement) : null; |
} |
/** |
* @return NodeInterface |
*/ |
public function getTree() |
{ |
return $this->tree; |
} |
/** |
* @return null|string |
*/ |
public function getPseudoElement() |
{ |
return $this->pseudoElement; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getSpecificity() |
{ |
return $this->tree->getSpecificity()->plus(new Specificity(0, 0, $this->pseudoElement ? 1 : 0)); |
} |
/** |
* {@inheritdoc} |
*/ |
public function __toString() |
{ |
return sprintf('%s[%s%s]', $this->getNodeName(), $this->tree, $this->pseudoElement ? '::'.$this->pseudoElement : ''); |
} |
} |
/vendor/symfony/css-selector/Node/Specificity.php |
---|
@@ -0,0 +1,105 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Node; |
/** |
* Represents a node specificity. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @see http://www.w3.org/TR/selectors/#specificity |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class Specificity |
{ |
const A_FACTOR = 100; |
const B_FACTOR = 10; |
const C_FACTOR = 1; |
/** |
* @var int |
*/ |
private $a; |
/** |
* @var int |
*/ |
private $b; |
/** |
* @var int |
*/ |
private $c; |
/** |
* Constructor. |
* |
* @param int $a |
* @param int $b |
* @param int $c |
*/ |
public function __construct($a, $b, $c) |
{ |
$this->a = $a; |
$this->b = $b; |
$this->c = $c; |
} |
/** |
* @param Specificity $specificity |
* |
* @return self |
*/ |
public function plus(Specificity $specificity) |
{ |
return new self($this->a + $specificity->a, $this->b + $specificity->b, $this->c + $specificity->c); |
} |
/** |
* Returns global specificity value. |
* |
* @return int |
*/ |
public function getValue() |
{ |
return $this->a * self::A_FACTOR + $this->b * self::B_FACTOR + $this->c * self::C_FACTOR; |
} |
/** |
* Returns -1 if the object specificity is lower than the argument, |
* 0 if they are equal, and 1 if the argument is lower. |
* |
* @param Specificity $specificity |
* |
* @return int |
*/ |
public function compareTo(Specificity $specificity) |
{ |
if ($this->a !== $specificity->a) { |
return $this->a > $specificity->a ? 1 : -1; |
} |
if ($this->b !== $specificity->b) { |
return $this->b > $specificity->b ? 1 : -1; |
} |
if ($this->c !== $specificity->c) { |
return $this->c > $specificity->c ? 1 : -1; |
} |
return 0; |
} |
} |
/vendor/symfony/css-selector/Parser/Handler/CommentHandler.php |
---|
@@ -0,0 +1,48 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Reader; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
/** |
* CSS selector comment handler. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class CommentHandler implements HandlerInterface |
{ |
/** |
* {@inheritdoc} |
*/ |
public function handle(Reader $reader, TokenStream $stream) |
{ |
if ('/*' !== $reader->getSubstring(2)) { |
return false; |
} |
$offset = $reader->getOffset('*/'); |
if (false === $offset) { |
$reader->moveToEnd(); |
} else { |
$reader->moveForward($offset + 2); |
} |
return true; |
} |
} |
/vendor/symfony/css-selector/Parser/Handler/HandlerInterface.php |
---|
@@ -0,0 +1,36 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Reader; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
/** |
* CSS selector handler interface. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
interface HandlerInterface |
{ |
/** |
* @param Reader $reader |
* @param TokenStream $stream |
* |
* @return bool |
*/ |
public function handle(Reader $reader, TokenStream $stream); |
} |
/vendor/symfony/css-selector/Parser/Handler/HashHandler.php |
---|
@@ -0,0 +1,69 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Reader; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; |
/** |
* CSS selector comment handler. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class HashHandler implements HandlerInterface |
{ |
/** |
* @var TokenizerPatterns |
*/ |
private $patterns; |
/** |
* @var TokenizerEscaping |
*/ |
private $escaping; |
/** |
* @param TokenizerPatterns $patterns |
* @param TokenizerEscaping $escaping |
*/ |
public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) |
{ |
$this->patterns = $patterns; |
$this->escaping = $escaping; |
} |
/** |
* {@inheritdoc} |
*/ |
public function handle(Reader $reader, TokenStream $stream) |
{ |
$match = $reader->findPattern($this->patterns->getHashPattern()); |
if (!$match) { |
return false; |
} |
$value = $this->escaping->escapeUnicode($match[1]); |
$stream->push(new Token(Token::TYPE_HASH, $value, $reader->getPosition())); |
$reader->moveForward(strlen($match[0])); |
return true; |
} |
} |
/vendor/symfony/css-selector/Parser/Handler/IdentifierHandler.php |
---|
@@ -0,0 +1,69 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Reader; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; |
/** |
* CSS selector comment handler. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class IdentifierHandler implements HandlerInterface |
{ |
/** |
* @var TokenizerPatterns |
*/ |
private $patterns; |
/** |
* @var TokenizerEscaping |
*/ |
private $escaping; |
/** |
* @param TokenizerPatterns $patterns |
* @param TokenizerEscaping $escaping |
*/ |
public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) |
{ |
$this->patterns = $patterns; |
$this->escaping = $escaping; |
} |
/** |
* {@inheritdoc} |
*/ |
public function handle(Reader $reader, TokenStream $stream) |
{ |
$match = $reader->findPattern($this->patterns->getIdentifierPattern()); |
if (!$match) { |
return false; |
} |
$value = $this->escaping->escapeUnicode($match[0]); |
$stream->push(new Token(Token::TYPE_IDENTIFIER, $value, $reader->getPosition())); |
$reader->moveForward(strlen($match[0])); |
return true; |
} |
} |
/vendor/symfony/css-selector/Parser/Handler/NumberHandler.php |
---|
@@ -0,0 +1,60 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Reader; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; |
/** |
* CSS selector comment handler. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class NumberHandler implements HandlerInterface |
{ |
/** |
* @var TokenizerPatterns |
*/ |
private $patterns; |
/** |
* @param TokenizerPatterns $patterns |
*/ |
public function __construct(TokenizerPatterns $patterns) |
{ |
$this->patterns = $patterns; |
} |
/** |
* {@inheritdoc} |
*/ |
public function handle(Reader $reader, TokenStream $stream) |
{ |
$match = $reader->findPattern($this->patterns->getNumberPattern()); |
if (!$match) { |
return false; |
} |
$stream->push(new Token(Token::TYPE_NUMBER, $match[0], $reader->getPosition())); |
$reader->moveForward(strlen($match[0])); |
return true; |
} |
} |
/vendor/symfony/css-selector/Parser/Handler/StringHandler.php |
---|
@@ -0,0 +1,88 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Handler; |
use Symfony\Component\CssSelector\Exception\InternalErrorException; |
use Symfony\Component\CssSelector\Exception\SyntaxErrorException; |
use Symfony\Component\CssSelector\Parser\Reader; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; |
/** |
* CSS selector comment handler. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class StringHandler implements HandlerInterface |
{ |
/** |
* @var TokenizerPatterns |
*/ |
private $patterns; |
/** |
* @var TokenizerEscaping |
*/ |
private $escaping; |
/** |
* @param TokenizerPatterns $patterns |
* @param TokenizerEscaping $escaping |
*/ |
public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) |
{ |
$this->patterns = $patterns; |
$this->escaping = $escaping; |
} |
/** |
* {@inheritdoc} |
*/ |
public function handle(Reader $reader, TokenStream $stream) |
{ |
$quote = $reader->getSubstring(1); |
if (!in_array($quote, array("'", '"'))) { |
return false; |
} |
$reader->moveForward(1); |
$match = $reader->findPattern($this->patterns->getQuotedStringPattern($quote)); |
if (!$match) { |
throw new InternalErrorException(sprintf('Should have found at least an empty match at %s.', $reader->getPosition())); |
} |
// check unclosed strings |
if (strlen($match[0]) === $reader->getRemainingLength()) { |
throw SyntaxErrorException::unclosedString($reader->getPosition() - 1); |
} |
// check quotes pairs validity |
if ($quote !== $reader->getSubstring(1, strlen($match[0]))) { |
throw SyntaxErrorException::unclosedString($reader->getPosition() - 1); |
} |
$string = $this->escaping->escapeUnicodeAndNewLine($match[0]); |
$stream->push(new Token(Token::TYPE_STRING, $string, $reader->getPosition())); |
$reader->moveForward(strlen($match[0]) + 1); |
return true; |
} |
} |
/vendor/symfony/css-selector/Parser/Handler/WhitespaceHandler.php |
---|
@@ -0,0 +1,46 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Reader; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
/** |
* CSS selector whitespace handler. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class WhitespaceHandler implements HandlerInterface |
{ |
/** |
* {@inheritdoc} |
*/ |
public function handle(Reader $reader, TokenStream $stream) |
{ |
$match = $reader->findPattern('~^[ \t\r\n\f]+~'); |
if (false === $match) { |
return false; |
} |
$stream->push(new Token(Token::TYPE_WHITESPACE, $match[0], $reader->getPosition())); |
$reader->moveForward(strlen($match[0])); |
return true; |
} |
} |
/vendor/symfony/css-selector/Parser/Parser.php |
---|
@@ -0,0 +1,401 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser; |
use Symfony\Component\CssSelector\Exception\SyntaxErrorException; |
use Symfony\Component\CssSelector\Node; |
use Symfony\Component\CssSelector\Parser\Tokenizer\Tokenizer; |
/** |
* CSS selector parser. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class Parser implements ParserInterface |
{ |
/** |
* @var Tokenizer |
*/ |
private $tokenizer; |
/** |
* Constructor. |
* |
* @param null|Tokenizer $tokenizer |
*/ |
public function __construct(Tokenizer $tokenizer = null) |
{ |
$this->tokenizer = $tokenizer ?: new Tokenizer(); |
} |
/** |
* {@inheritdoc} |
*/ |
public function parse($source) |
{ |
$reader = new Reader($source); |
$stream = $this->tokenizer->tokenize($reader); |
return $this->parseSelectorList($stream); |
} |
/** |
* Parses the arguments for ":nth-child()" and friends. |
* |
* @param Token[] $tokens |
* |
* @return array |
* |
* @throws SyntaxErrorException |
*/ |
public static function parseSeries(array $tokens) |
{ |
foreach ($tokens as $token) { |
if ($token->isString()) { |
throw SyntaxErrorException::stringAsFunctionArgument(); |
} |
} |
$joined = trim(implode('', array_map(function (Token $token) { |
return $token->getValue(); |
}, $tokens))); |
$int = function ($string) { |
if (!is_numeric($string)) { |
throw SyntaxErrorException::stringAsFunctionArgument(); |
} |
return (int) $string; |
}; |
switch (true) { |
case 'odd' === $joined: |
return array(2, 1); |
case 'even' === $joined: |
return array(2, 0); |
case 'n' === $joined: |
return array(1, 0); |
case false === strpos($joined, 'n'): |
return array(0, $int($joined)); |
} |
$split = explode('n', $joined); |
$first = isset($split[0]) ? $split[0] : null; |
return array( |
$first ? ('-' === $first || '+' === $first ? $int($first.'1') : $int($first)) : 1, |
isset($split[1]) && $split[1] ? $int($split[1]) : 0, |
); |
} |
/** |
* Parses selector nodes. |
* |
* @param TokenStream $stream |
* |
* @return array |
*/ |
private function parseSelectorList(TokenStream $stream) |
{ |
$stream->skipWhitespace(); |
$selectors = array(); |
while (true) { |
$selectors[] = $this->parserSelectorNode($stream); |
if ($stream->getPeek()->isDelimiter(array(','))) { |
$stream->getNext(); |
$stream->skipWhitespace(); |
} else { |
break; |
} |
} |
return $selectors; |
} |
/** |
* Parses next selector or combined node. |
* |
* @param TokenStream $stream |
* |
* @return Node\SelectorNode |
* |
* @throws SyntaxErrorException |
*/ |
private function parserSelectorNode(TokenStream $stream) |
{ |
list($result, $pseudoElement) = $this->parseSimpleSelector($stream); |
while (true) { |
$stream->skipWhitespace(); |
$peek = $stream->getPeek(); |
if ($peek->isFileEnd() || $peek->isDelimiter(array(','))) { |
break; |
} |
if (null !== $pseudoElement) { |
throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector'); |
} |
if ($peek->isDelimiter(array('+', '>', '~'))) { |
$combinator = $stream->getNext()->getValue(); |
$stream->skipWhitespace(); |
} else { |
$combinator = ' '; |
} |
list($nextSelector, $pseudoElement) = $this->parseSimpleSelector($stream); |
$result = new Node\CombinedSelectorNode($result, $combinator, $nextSelector); |
} |
return new Node\SelectorNode($result, $pseudoElement); |
} |
/** |
* Parses next simple node (hash, class, pseudo, negation). |
* |
* @param TokenStream $stream |
* @param bool $insideNegation |
* |
* @return array |
* |
* @throws SyntaxErrorException |
*/ |
private function parseSimpleSelector(TokenStream $stream, $insideNegation = false) |
{ |
$stream->skipWhitespace(); |
$selectorStart = count($stream->getUsed()); |
$result = $this->parseElementNode($stream); |
$pseudoElement = null; |
while (true) { |
$peek = $stream->getPeek(); |
if ($peek->isWhitespace() |
|| $peek->isFileEnd() |
|| $peek->isDelimiter(array(',', '+', '>', '~')) |
|| ($insideNegation && $peek->isDelimiter(array(')'))) |
) { |
break; |
} |
if (null !== $pseudoElement) { |
throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector'); |
} |
if ($peek->isHash()) { |
$result = new Node\HashNode($result, $stream->getNext()->getValue()); |
} elseif ($peek->isDelimiter(array('.'))) { |
$stream->getNext(); |
$result = new Node\ClassNode($result, $stream->getNextIdentifier()); |
} elseif ($peek->isDelimiter(array('['))) { |
$stream->getNext(); |
$result = $this->parseAttributeNode($result, $stream); |
} elseif ($peek->isDelimiter(array(':'))) { |
$stream->getNext(); |
if ($stream->getPeek()->isDelimiter(array(':'))) { |
$stream->getNext(); |
$pseudoElement = $stream->getNextIdentifier(); |
continue; |
} |
$identifier = $stream->getNextIdentifier(); |
if (in_array(strtolower($identifier), array('first-line', 'first-letter', 'before', 'after'))) { |
// Special case: CSS 2.1 pseudo-elements can have a single ':'. |
// Any new pseudo-element must have two. |
$pseudoElement = $identifier; |
continue; |
} |
if (!$stream->getPeek()->isDelimiter(array('('))) { |
$result = new Node\PseudoNode($result, $identifier); |
continue; |
} |
$stream->getNext(); |
$stream->skipWhitespace(); |
if ('not' === strtolower($identifier)) { |
if ($insideNegation) { |
throw SyntaxErrorException::nestedNot(); |
} |
list($argument, $argumentPseudoElement) = $this->parseSimpleSelector($stream, true); |
$next = $stream->getNext(); |
if (null !== $argumentPseudoElement) { |
throw SyntaxErrorException::pseudoElementFound($argumentPseudoElement, 'inside ::not()'); |
} |
if (!$next->isDelimiter(array(')'))) { |
throw SyntaxErrorException::unexpectedToken('")"', $next); |
} |
$result = new Node\NegationNode($result, $argument); |
} else { |
$arguments = array(); |
$next = null; |
while (true) { |
$stream->skipWhitespace(); |
$next = $stream->getNext(); |
if ($next->isIdentifier() |
|| $next->isString() |
|| $next->isNumber() |
|| $next->isDelimiter(array('+', '-')) |
) { |
$arguments[] = $next; |
} elseif ($next->isDelimiter(array(')'))) { |
break; |
} else { |
throw SyntaxErrorException::unexpectedToken('an argument', $next); |
} |
} |
if (empty($arguments)) { |
throw SyntaxErrorException::unexpectedToken('at least one argument', $next); |
} |
$result = new Node\FunctionNode($result, $identifier, $arguments); |
} |
} else { |
throw SyntaxErrorException::unexpectedToken('selector', $peek); |
} |
} |
if (count($stream->getUsed()) === $selectorStart) { |
throw SyntaxErrorException::unexpectedToken('selector', $stream->getPeek()); |
} |
return array($result, $pseudoElement); |
} |
/** |
* Parses next element node. |
* |
* @param TokenStream $stream |
* |
* @return Node\ElementNode |
*/ |
private function parseElementNode(TokenStream $stream) |
{ |
$peek = $stream->getPeek(); |
if ($peek->isIdentifier() || $peek->isDelimiter(array('*'))) { |
if ($peek->isIdentifier()) { |
$namespace = $stream->getNext()->getValue(); |
} else { |
$stream->getNext(); |
$namespace = null; |
} |
if ($stream->getPeek()->isDelimiter(array('|'))) { |
$stream->getNext(); |
$element = $stream->getNextIdentifierOrStar(); |
} else { |
$element = $namespace; |
$namespace = null; |
} |
} else { |
$element = $namespace = null; |
} |
return new Node\ElementNode($namespace, $element); |
} |
/** |
* Parses next attribute node. |
* |
* @param Node\NodeInterface $selector |
* @param TokenStream $stream |
* |
* @return Node\AttributeNode |
* |
* @throws SyntaxErrorException |
*/ |
private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream) |
{ |
$stream->skipWhitespace(); |
$attribute = $stream->getNextIdentifierOrStar(); |
if (null === $attribute && !$stream->getPeek()->isDelimiter(array('|'))) { |
throw SyntaxErrorException::unexpectedToken('"|"', $stream->getPeek()); |
} |
if ($stream->getPeek()->isDelimiter(array('|'))) { |
$stream->getNext(); |
if ($stream->getPeek()->isDelimiter(array('='))) { |
$namespace = null; |
$stream->getNext(); |
$operator = '|='; |
} else { |
$namespace = $attribute; |
$attribute = $stream->getNextIdentifier(); |
$operator = null; |
} |
} else { |
$namespace = $operator = null; |
} |
if (null === $operator) { |
$stream->skipWhitespace(); |
$next = $stream->getNext(); |
if ($next->isDelimiter(array(']'))) { |
return new Node\AttributeNode($selector, $namespace, $attribute, 'exists', null); |
} elseif ($next->isDelimiter(array('='))) { |
$operator = '='; |
} elseif ($next->isDelimiter(array('^', '$', '*', '~', '|', '!')) |
&& $stream->getPeek()->isDelimiter(array('=')) |
) { |
$operator = $next->getValue().'='; |
$stream->getNext(); |
} else { |
throw SyntaxErrorException::unexpectedToken('operator', $next); |
} |
} |
$stream->skipWhitespace(); |
$value = $stream->getNext(); |
if ($value->isNumber()) { |
// if the value is a number, it's casted into a string |
$value = new Token(Token::TYPE_STRING, (string) $value->getValue(), $value->getPosition()); |
} |
if (!($value->isIdentifier() || $value->isString())) { |
throw SyntaxErrorException::unexpectedToken('string or identifier', $value); |
} |
$stream->skipWhitespace(); |
$next = $stream->getNext(); |
if (!$next->isDelimiter(array(']'))) { |
throw SyntaxErrorException::unexpectedToken('"]"', $next); |
} |
return new Node\AttributeNode($selector, $namespace, $attribute, $operator, $value->getValue()); |
} |
} |
/vendor/symfony/css-selector/Parser/ParserInterface.php |
---|
@@ -0,0 +1,36 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
/** |
* CSS selector parser interface. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
interface ParserInterface |
{ |
/** |
* Parses given selector source into an array of tokens. |
* |
* @param string $source |
* |
* @return SelectorNode[] |
*/ |
public function parse($source); |
} |
/vendor/symfony/css-selector/Parser/Reader.php |
---|
@@ -0,0 +1,125 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser; |
/** |
* CSS selector reader. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class Reader |
{ |
/** |
* @var string |
*/ |
private $source; |
/** |
* @var int |
*/ |
private $length; |
/** |
* @var int |
*/ |
private $position = 0; |
/** |
* @param string $source |
*/ |
public function __construct($source) |
{ |
$this->source = $source; |
$this->length = strlen($source); |
} |
/** |
* @return bool |
*/ |
public function isEOF() |
{ |
return $this->position >= $this->length; |
} |
/** |
* @return int |
*/ |
public function getPosition() |
{ |
return $this->position; |
} |
/** |
* @return int |
*/ |
public function getRemainingLength() |
{ |
return $this->length - $this->position; |
} |
/** |
* @param int $length |
* @param int $offset |
* |
* @return string |
*/ |
public function getSubstring($length, $offset = 0) |
{ |
return substr($this->source, $this->position + $offset, $length); |
} |
/** |
* @param string $string |
* |
* @return int |
*/ |
public function getOffset($string) |
{ |
$position = strpos($this->source, $string, $this->position); |
return false === $position ? false : $position - $this->position; |
} |
/** |
* @param string $pattern |
* |
* @return bool |
*/ |
public function findPattern($pattern) |
{ |
$source = substr($this->source, $this->position); |
if (preg_match($pattern, $source, $matches)) { |
return $matches; |
} |
return false; |
} |
/** |
* @param int $length |
*/ |
public function moveForward($length) |
{ |
$this->position += $length; |
} |
public function moveToEnd() |
{ |
$this->position = $this->length; |
} |
} |
/vendor/symfony/css-selector/Parser/Shortcut/ClassParser.php |
---|
@@ -0,0 +1,51 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Shortcut; |
use Symfony\Component\CssSelector\Node\ClassNode; |
use Symfony\Component\CssSelector\Node\ElementNode; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
use Symfony\Component\CssSelector\Parser\ParserInterface; |
/** |
* CSS selector class parser shortcut. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class ClassParser implements ParserInterface |
{ |
/** |
* {@inheritdoc} |
*/ |
public function parse($source) |
{ |
// Matches an optional namespace, optional element, and required class |
// $source = 'test|input.ab6bd_field'; |
// $matches = array (size=4) |
// 0 => string 'test|input.ab6bd_field' (length=22) |
// 1 => string 'test' (length=4) |
// 2 => string 'input' (length=5) |
// 3 => string 'ab6bd_field' (length=11) |
if (preg_match('/^(?:([a-z]++)\|)?+([\w-]++|\*)?+\.([\w-]++)$/i', trim($source), $matches)) { |
return array( |
new SelectorNode(new ClassNode(new ElementNode($matches[1] ?: null, $matches[2] ?: null), $matches[3])), |
); |
} |
return array(); |
} |
} |
/vendor/symfony/css-selector/Parser/Shortcut/ElementParser.php |
---|
@@ -0,0 +1,47 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Shortcut; |
use Symfony\Component\CssSelector\Node\ElementNode; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
use Symfony\Component\CssSelector\Parser\ParserInterface; |
/** |
* CSS selector element parser shortcut. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class ElementParser implements ParserInterface |
{ |
/** |
* {@inheritdoc} |
*/ |
public function parse($source) |
{ |
// Matches an optional namespace, required element or `*` |
// $source = 'testns|testel'; |
// $matches = array (size=3) |
// 0 => string 'testns|testel' (length=13) |
// 1 => string 'testns' (length=6) |
// 2 => string 'testel' (length=6) |
if (preg_match('/^(?:([a-z]++)\|)?([\w-]++|\*)$/i', trim($source), $matches)) { |
return array(new SelectorNode(new ElementNode($matches[1] ?: null, $matches[2]))); |
} |
return array(); |
} |
} |
/vendor/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php |
---|
@@ -0,0 +1,46 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Shortcut; |
use Symfony\Component\CssSelector\Node\ElementNode; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
use Symfony\Component\CssSelector\Parser\ParserInterface; |
/** |
* CSS selector class parser shortcut. |
* |
* This shortcut ensure compatibility with previous version. |
* - The parser fails to parse an empty string. |
* - In the previous version, an empty string matches each tags. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class EmptyStringParser implements ParserInterface |
{ |
/** |
* {@inheritdoc} |
*/ |
public function parse($source) |
{ |
// Matches an empty string |
if ($source == '') { |
return array(new SelectorNode(new ElementNode(null, '*'))); |
} |
return array(); |
} |
} |
/vendor/symfony/css-selector/Parser/Shortcut/HashParser.php |
---|
@@ -0,0 +1,51 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Shortcut; |
use Symfony\Component\CssSelector\Node\ElementNode; |
use Symfony\Component\CssSelector\Node\HashNode; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
use Symfony\Component\CssSelector\Parser\ParserInterface; |
/** |
* CSS selector hash parser shortcut. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class HashParser implements ParserInterface |
{ |
/** |
* {@inheritdoc} |
*/ |
public function parse($source) |
{ |
// Matches an optional namespace, optional element, and required id |
// $source = 'test|input#ab6bd_field'; |
// $matches = array (size=4) |
// 0 => string 'test|input#ab6bd_field' (length=22) |
// 1 => string 'test' (length=4) |
// 2 => string 'input' (length=5) |
// 3 => string 'ab6bd_field' (length=11) |
if (preg_match('/^(?:([a-z]++)\|)?+([\w-]++|\*)?+#([\w-]++)$/i', trim($source), $matches)) { |
return array( |
new SelectorNode(new HashNode(new ElementNode($matches[1] ?: null, $matches[2] ?: null), $matches[3])), |
); |
} |
return array(); |
} |
} |
/vendor/symfony/css-selector/Parser/Token.php |
---|
@@ -0,0 +1,162 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser; |
/** |
* CSS selector token. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class Token |
{ |
const TYPE_FILE_END = 'eof'; |
const TYPE_DELIMITER = 'delimiter'; |
const TYPE_WHITESPACE = 'whitespace'; |
const TYPE_IDENTIFIER = 'identifier'; |
const TYPE_HASH = 'hash'; |
const TYPE_NUMBER = 'number'; |
const TYPE_STRING = 'string'; |
/** |
* @var int |
*/ |
private $type; |
/** |
* @var string |
*/ |
private $value; |
/** |
* @var int |
*/ |
private $position; |
/** |
* @param int $type |
* @param string $value |
* @param int $position |
*/ |
public function __construct($type, $value, $position) |
{ |
$this->type = $type; |
$this->value = $value; |
$this->position = $position; |
} |
/** |
* @return int |
*/ |
public function getType() |
{ |
return $this->type; |
} |
/** |
* @return string |
*/ |
public function getValue() |
{ |
return $this->value; |
} |
/** |
* @return int |
*/ |
public function getPosition() |
{ |
return $this->position; |
} |
/** |
* @return bool |
*/ |
public function isFileEnd() |
{ |
return self::TYPE_FILE_END === $this->type; |
} |
/** |
* @param array $values |
* |
* @return bool |
*/ |
public function isDelimiter(array $values = array()) |
{ |
if (self::TYPE_DELIMITER !== $this->type) { |
return false; |
} |
if (empty($values)) { |
return true; |
} |
return in_array($this->value, $values); |
} |
/** |
* @return bool |
*/ |
public function isWhitespace() |
{ |
return self::TYPE_WHITESPACE === $this->type; |
} |
/** |
* @return bool |
*/ |
public function isIdentifier() |
{ |
return self::TYPE_IDENTIFIER === $this->type; |
} |
/** |
* @return bool |
*/ |
public function isHash() |
{ |
return self::TYPE_HASH === $this->type; |
} |
/** |
* @return bool |
*/ |
public function isNumber() |
{ |
return self::TYPE_NUMBER === $this->type; |
} |
/** |
* @return bool |
*/ |
public function isString() |
{ |
return self::TYPE_STRING === $this->type; |
} |
/** |
* @return string |
*/ |
public function __toString() |
{ |
if ($this->value) { |
return sprintf('<%s "%s" at %s>', $this->type, $this->value, $this->position); |
} |
return sprintf('<%s at %s>', $this->type, $this->position); |
} |
} |
/vendor/symfony/css-selector/Parser/TokenStream.php |
---|
@@ -0,0 +1,184 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser; |
use Symfony\Component\CssSelector\Exception\InternalErrorException; |
use Symfony\Component\CssSelector\Exception\SyntaxErrorException; |
/** |
* CSS selector token stream. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class TokenStream |
{ |
/** |
* @var Token[] |
*/ |
private $tokens = array(); |
/** |
* @var bool |
*/ |
private $frozen = false; |
/** |
* @var Token[] |
*/ |
private $used = array(); |
/** |
* @var int |
*/ |
private $cursor = 0; |
/** |
* @var Token|null |
*/ |
private $peeked = null; |
/** |
* @var bool |
*/ |
private $peeking = false; |
/** |
* Pushes a token. |
* |
* @param Token $token |
* |
* @return $this |
*/ |
public function push(Token $token) |
{ |
$this->tokens[] = $token; |
return $this; |
} |
/** |
* Freezes stream. |
* |
* @return $this |
*/ |
public function freeze() |
{ |
$this->frozen = true; |
return $this; |
} |
/** |
* Returns next token. |
* |
* @return Token |
* |
* @throws InternalErrorException If there is no more token |
*/ |
public function getNext() |
{ |
if ($this->peeking) { |
$this->peeking = false; |
$this->used[] = $this->peeked; |
return $this->peeked; |
} |
if (!isset($this->tokens[$this->cursor])) { |
throw new InternalErrorException('Unexpected token stream end.'); |
} |
return $this->tokens[$this->cursor++]; |
} |
/** |
* Returns peeked token. |
* |
* @return Token |
*/ |
public function getPeek() |
{ |
if (!$this->peeking) { |
$this->peeked = $this->getNext(); |
$this->peeking = true; |
} |
return $this->peeked; |
} |
/** |
* Returns used tokens. |
* |
* @return Token[] |
*/ |
public function getUsed() |
{ |
return $this->used; |
} |
/** |
* Returns nex identifier token. |
* |
* @return string The identifier token value |
* |
* @throws SyntaxErrorException If next token is not an identifier |
*/ |
public function getNextIdentifier() |
{ |
$next = $this->getNext(); |
if (!$next->isIdentifier()) { |
throw SyntaxErrorException::unexpectedToken('identifier', $next); |
} |
return $next->getValue(); |
} |
/** |
* Returns nex identifier or star delimiter token. |
* |
* @return null|string The identifier token value or null if star found |
* |
* @throws SyntaxErrorException If next token is not an identifier or a star delimiter |
*/ |
public function getNextIdentifierOrStar() |
{ |
$next = $this->getNext(); |
if ($next->isIdentifier()) { |
return $next->getValue(); |
} |
if ($next->isDelimiter(array('*'))) { |
return; |
} |
throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next); |
} |
/** |
* Skips next whitespace if any. |
*/ |
public function skipWhitespace() |
{ |
$peek = $this->getPeek(); |
if ($peek->isWhitespace()) { |
$this->getNext(); |
} |
} |
} |
/vendor/symfony/css-selector/Parser/Tokenizer/Tokenizer.php |
---|
@@ -0,0 +1,80 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Tokenizer; |
use Symfony\Component\CssSelector\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Reader; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
/** |
* CSS selector tokenizer. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class Tokenizer |
{ |
/** |
* @var Handler\HandlerInterface[] |
*/ |
private $handlers; |
/** |
* Constructor. |
*/ |
public function __construct() |
{ |
$patterns = new TokenizerPatterns(); |
$escaping = new TokenizerEscaping($patterns); |
$this->handlers = array( |
new Handler\WhitespaceHandler(), |
new Handler\IdentifierHandler($patterns, $escaping), |
new Handler\HashHandler($patterns, $escaping), |
new Handler\StringHandler($patterns, $escaping), |
new Handler\NumberHandler($patterns), |
new Handler\CommentHandler(), |
); |
} |
/** |
* Tokenize selector source code. |
* |
* @param Reader $reader |
* |
* @return TokenStream |
*/ |
public function tokenize(Reader $reader) |
{ |
$stream = new TokenStream(); |
while (!$reader->isEOF()) { |
foreach ($this->handlers as $handler) { |
if ($handler->handle($reader, $stream)) { |
continue 2; |
} |
} |
$stream->push(new Token(Token::TYPE_DELIMITER, $reader->getSubstring(1), $reader->getPosition())); |
$reader->moveForward(1); |
} |
return $stream |
->push(new Token(Token::TYPE_FILE_END, null, $reader->getPosition())) |
->freeze(); |
} |
} |
/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php |
---|
@@ -0,0 +1,84 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Tokenizer; |
/** |
* CSS selector tokenizer escaping applier. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class TokenizerEscaping |
{ |
/** |
* @var TokenizerPatterns |
*/ |
private $patterns; |
/** |
* @param TokenizerPatterns $patterns |
*/ |
public function __construct(TokenizerPatterns $patterns) |
{ |
$this->patterns = $patterns; |
} |
/** |
* @param string $value |
* |
* @return string |
*/ |
public function escapeUnicode($value) |
{ |
$value = $this->replaceUnicodeSequences($value); |
return preg_replace($this->patterns->getSimpleEscapePattern(), '$1', $value); |
} |
/** |
* @param string $value |
* |
* @return string |
*/ |
public function escapeUnicodeAndNewLine($value) |
{ |
$value = preg_replace($this->patterns->getNewLineEscapePattern(), '', $value); |
return $this->escapeUnicode($value); |
} |
/** |
* @param string $value |
* |
* @return string |
*/ |
private function replaceUnicodeSequences($value) |
{ |
return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function ($match) { |
$c = hexdec($match[1]); |
if (0x80 > $c %= 0x200000) { |
return chr($c); |
} |
if (0x800 > $c) { |
return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); |
} |
if (0x10000 > $c) { |
return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); |
} |
}, $value); |
} |
} |
/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php |
---|
@@ -0,0 +1,162 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Parser\Tokenizer; |
/** |
* CSS selector tokenizer patterns builder. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class TokenizerPatterns |
{ |
/** |
* @var string |
*/ |
private $unicodeEscapePattern; |
/** |
* @var string |
*/ |
private $simpleEscapePattern; |
/** |
* @var string |
*/ |
private $newLineEscapePattern; |
/** |
* @var string |
*/ |
private $escapePattern; |
/** |
* @var string |
*/ |
private $stringEscapePattern; |
/** |
* @var string |
*/ |
private $nonAsciiPattern; |
/** |
* @var string |
*/ |
private $nmCharPattern; |
/** |
* @var string |
*/ |
private $nmStartPattern; |
/** |
* @var string |
*/ |
private $identifierPattern; |
/** |
* @var string |
*/ |
private $hashPattern; |
/** |
* @var string |
*/ |
private $numberPattern; |
/** |
* @var string |
*/ |
private $quotedStringPattern; |
/** |
* Constructor. |
*/ |
public function __construct() |
{ |
$this->unicodeEscapePattern = '\\\\([0-9a-f]{1,6})(?:\r\n|[ \n\r\t\f])?'; |
$this->simpleEscapePattern = '\\\\(.)'; |
$this->newLineEscapePattern = '\\\\(?:\n|\r\n|\r|\f)'; |
$this->escapePattern = $this->unicodeEscapePattern.'|\\\\[^\n\r\f0-9a-f]'; |
$this->stringEscapePattern = $this->newLineEscapePattern.'|'.$this->escapePattern; |
$this->nonAsciiPattern = '[^\x00-\x7F]'; |
$this->nmCharPattern = '[_a-z0-9-]|'.$this->escapePattern.'|'.$this->nonAsciiPattern; |
$this->nmStartPattern = '[_a-z]|'.$this->escapePattern.'|'.$this->nonAsciiPattern; |
$this->identifierPattern = '(?:'.$this->nmStartPattern.')(?:'.$this->nmCharPattern.')*'; |
$this->hashPattern = '#((?:'.$this->nmCharPattern.')+)'; |
$this->numberPattern = '[+-]?(?:[0-9]*\.[0-9]+|[0-9]+)'; |
$this->quotedStringPattern = '([^\n\r\f%s]|'.$this->stringEscapePattern.')*'; |
} |
/** |
* @return string |
*/ |
public function getNewLineEscapePattern() |
{ |
return '~^'.$this->newLineEscapePattern.'~'; |
} |
/** |
* @return string |
*/ |
public function getSimpleEscapePattern() |
{ |
return '~^'.$this->simpleEscapePattern.'~'; |
} |
/** |
* @return string |
*/ |
public function getUnicodeEscapePattern() |
{ |
return '~^'.$this->unicodeEscapePattern.'~i'; |
} |
/** |
* @return string |
*/ |
public function getIdentifierPattern() |
{ |
return '~^'.$this->identifierPattern.'~i'; |
} |
/** |
* @return string |
*/ |
public function getHashPattern() |
{ |
return '~^'.$this->hashPattern.'~i'; |
} |
/** |
* @return string |
*/ |
public function getNumberPattern() |
{ |
return '~^'.$this->numberPattern.'~'; |
} |
/** |
* @param string $quote |
* |
* @return string |
*/ |
public function getQuotedStringPattern($quote) |
{ |
return '~^'.sprintf($this->quotedStringPattern, $quote).'~i'; |
} |
} |
/vendor/symfony/css-selector/README.md |
---|
@@ -0,0 +1,20 @@ |
CssSelector Component |
===================== |
The CssSelector component converts CSS selectors to XPath expressions. |
Resources |
--------- |
* [Documentation](https://symfony.com/doc/current/components/css_selector.html) |
* [Contributing](https://symfony.com/doc/current/contributing/index.html) |
* [Report issues](https://github.com/symfony/symfony/issues) and |
[send Pull Requests](https://github.com/symfony/symfony/pulls) |
in the [main Symfony repository](https://github.com/symfony/symfony) |
Credits |
------- |
This component is a port of the Python cssselect library |
[v0.7.1](https://github.com/SimonSapin/cssselect/releases/tag/v0.7.1), |
which is distributed under the BSD license. |
/vendor/symfony/css-selector/Tests/CssSelectorConverterTest.php |
---|
@@ -0,0 +1,76 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\CssSelectorConverter; |
class CssSelectorConverterTest extends TestCase |
{ |
public function testCssToXPath() |
{ |
$converter = new CssSelectorConverter(); |
$this->assertEquals('descendant-or-self::*', $converter->toXPath('')); |
$this->assertEquals('descendant-or-self::h1', $converter->toXPath('h1')); |
$this->assertEquals("descendant-or-self::h1[@id = 'foo']", $converter->toXPath('h1#foo')); |
$this->assertEquals("descendant-or-self::h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]", $converter->toXPath('h1.foo')); |
$this->assertEquals('descendant-or-self::foo:h1', $converter->toXPath('foo|h1')); |
$this->assertEquals('descendant-or-self::h1', $converter->toXPath('H1')); |
} |
public function testCssToXPathXml() |
{ |
$converter = new CssSelectorConverter(false); |
$this->assertEquals('descendant-or-self::H1', $converter->toXPath('H1')); |
} |
/** |
* @expectedException \Symfony\Component\CssSelector\Exception\ParseException |
* @expectedExceptionMessage Expected identifier, but <eof at 3> found. |
*/ |
public function testParseExceptions() |
{ |
$converter = new CssSelectorConverter(); |
$converter->toXPath('h1:'); |
} |
/** @dataProvider getCssToXPathWithoutPrefixTestData */ |
public function testCssToXPathWithoutPrefix($css, $xpath) |
{ |
$converter = new CssSelectorConverter(); |
$this->assertEquals($xpath, $converter->toXPath($css, ''), '->parse() parses an input string and returns a node'); |
} |
public function getCssToXPathWithoutPrefixTestData() |
{ |
return array( |
array('h1', 'h1'), |
array('foo|h1', 'foo:h1'), |
array('h1, h2, h3', 'h1 | h2 | h3'), |
array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]"), |
array('h1 > p', 'h1/p'), |
array('h1#foo', "h1[@id = 'foo']"), |
array('h1.foo', "h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), |
array('h1[class*="foo bar"]', "h1[@class and contains(@class, 'foo bar')]"), |
array('h1[foo|class*="foo bar"]', "h1[@foo:class and contains(@foo:class, 'foo bar')]"), |
array('h1[class]', 'h1[@class]'), |
array('h1 .foo', "h1/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), |
array('h1 #foo', "h1/descendant-or-self::*/*[@id = 'foo']"), |
array('h1 [class*=foo]', "h1/descendant-or-self::*/*[@class and contains(@class, 'foo')]"), |
array('div>.foo', "div/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), |
array('div > .foo', "div/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/CssSelectorTest.php |
---|
@@ -0,0 +1,68 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\CssSelector; |
/** |
* @group legacy |
*/ |
class CssSelectorTest extends TestCase |
{ |
public function testCssToXPath() |
{ |
$this->assertEquals('descendant-or-self::*', CssSelector::toXPath('')); |
$this->assertEquals('descendant-or-self::h1', CssSelector::toXPath('h1')); |
$this->assertEquals("descendant-or-self::h1[@id = 'foo']", CssSelector::toXPath('h1#foo')); |
$this->assertEquals("descendant-or-self::h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]", CssSelector::toXPath('h1.foo')); |
$this->assertEquals('descendant-or-self::foo:h1', CssSelector::toXPath('foo|h1')); |
} |
/** @dataProvider getCssToXPathWithoutPrefixTestData */ |
public function testCssToXPathWithoutPrefix($css, $xpath) |
{ |
$this->assertEquals($xpath, CssSelector::toXPath($css, ''), '->parse() parses an input string and returns a node'); |
} |
public function testParseExceptions() |
{ |
try { |
CssSelector::toXPath('h1:'); |
$this->fail('->parse() throws an Exception if the css selector is not valid'); |
} catch (\Exception $e) { |
$this->assertInstanceOf('\Symfony\Component\CssSelector\Exception\ParseException', $e, '->parse() throws an Exception if the css selector is not valid'); |
$this->assertEquals('Expected identifier, but <eof at 3> found.', $e->getMessage(), '->parse() throws an Exception if the css selector is not valid'); |
} |
} |
public function getCssToXPathWithoutPrefixTestData() |
{ |
return array( |
array('h1', 'h1'), |
array('foo|h1', 'foo:h1'), |
array('h1, h2, h3', 'h1 | h2 | h3'), |
array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]"), |
array('h1 > p', 'h1/p'), |
array('h1#foo', "h1[@id = 'foo']"), |
array('h1.foo', "h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), |
array('h1[class*="foo bar"]', "h1[@class and contains(@class, 'foo bar')]"), |
array('h1[foo|class*="foo bar"]', "h1[@foo:class and contains(@foo:class, 'foo bar')]"), |
array('h1[class]', 'h1[@class]'), |
array('h1 .foo', "h1/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), |
array('h1 #foo', "h1/descendant-or-self::*/*[@id = 'foo']"), |
array('h1 [class*=foo]', "h1/descendant-or-self::*/*[@class and contains(@class, 'foo')]"), |
array('div>.foo', "div/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), |
array('div > .foo', "div/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Node/AbstractNodeTest.php |
---|
@@ -0,0 +1,34 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\Node\NodeInterface; |
abstract class AbstractNodeTest extends TestCase |
{ |
/** @dataProvider getToStringConversionTestData */ |
public function testToStringConversion(NodeInterface $node, $representation) |
{ |
$this->assertEquals($representation, (string) $node); |
} |
/** @dataProvider getSpecificityValueTestData */ |
public function testSpecificityValue(NodeInterface $node, $value) |
{ |
$this->assertEquals($value, $node->getSpecificity()->getValue()); |
} |
abstract public function getToStringConversionTestData(); |
abstract public function getSpecificityValueTestData(); |
} |
/vendor/symfony/css-selector/Tests/Node/AttributeNodeTest.php |
---|
@@ -0,0 +1,37 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use Symfony\Component\CssSelector\Node\AttributeNode; |
use Symfony\Component\CssSelector\Node\ElementNode; |
class AttributeNodeTest extends AbstractNodeTest |
{ |
public function getToStringConversionTestData() |
{ |
return array( |
array(new AttributeNode(new ElementNode(), null, 'attribute', 'exists', null), 'Attribute[Element[*][attribute]]'), |
array(new AttributeNode(new ElementNode(), null, 'attribute', '$=', 'value'), "Attribute[Element[*][attribute $= 'value']]"), |
array(new AttributeNode(new ElementNode(), 'namespace', 'attribute', '$=', 'value'), "Attribute[Element[*][namespace|attribute $= 'value']]"), |
); |
} |
public function getSpecificityValueTestData() |
{ |
return array( |
array(new AttributeNode(new ElementNode(), null, 'attribute', 'exists', null), 10), |
array(new AttributeNode(new ElementNode(null, 'element'), null, 'attribute', 'exists', null), 11), |
array(new AttributeNode(new ElementNode(), null, 'attribute', '$=', 'value'), 10), |
array(new AttributeNode(new ElementNode(), 'namespace', 'attribute', '$=', 'value'), 10), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Node/ClassNodeTest.php |
---|
@@ -0,0 +1,33 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use Symfony\Component\CssSelector\Node\ClassNode; |
use Symfony\Component\CssSelector\Node\ElementNode; |
class ClassNodeTest extends AbstractNodeTest |
{ |
public function getToStringConversionTestData() |
{ |
return array( |
array(new ClassNode(new ElementNode(), 'class'), 'Class[Element[*].class]'), |
); |
} |
public function getSpecificityValueTestData() |
{ |
return array( |
array(new ClassNode(new ElementNode(), 'class'), 10), |
array(new ClassNode(new ElementNode(null, 'element'), 'class'), 11), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Node/CombinedSelectorNodeTest.php |
---|
@@ -0,0 +1,35 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use Symfony\Component\CssSelector\Node\CombinedSelectorNode; |
use Symfony\Component\CssSelector\Node\ElementNode; |
class CombinedSelectorNodeTest extends AbstractNodeTest |
{ |
public function getToStringConversionTestData() |
{ |
return array( |
array(new CombinedSelectorNode(new ElementNode(), '>', new ElementNode()), 'CombinedSelector[Element[*] > Element[*]]'), |
array(new CombinedSelectorNode(new ElementNode(), ' ', new ElementNode()), 'CombinedSelector[Element[*] <followed> Element[*]]'), |
); |
} |
public function getSpecificityValueTestData() |
{ |
return array( |
array(new CombinedSelectorNode(new ElementNode(), '>', new ElementNode()), 0), |
array(new CombinedSelectorNode(new ElementNode(null, 'element'), '>', new ElementNode()), 1), |
array(new CombinedSelectorNode(new ElementNode(null, 'element'), '>', new ElementNode(null, 'element')), 2), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Node/ElementNodeTest.php |
---|
@@ -0,0 +1,35 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use Symfony\Component\CssSelector\Node\ElementNode; |
class ElementNodeTest extends AbstractNodeTest |
{ |
public function getToStringConversionTestData() |
{ |
return array( |
array(new ElementNode(), 'Element[*]'), |
array(new ElementNode(null, 'element'), 'Element[element]'), |
array(new ElementNode('namespace', 'element'), 'Element[namespace|element]'), |
); |
} |
public function getSpecificityValueTestData() |
{ |
return array( |
array(new ElementNode(), 0), |
array(new ElementNode(null, 'element'), 1), |
array(new ElementNode('namespace', 'element'), 1), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Node/FunctionNodeTest.php |
---|
@@ -0,0 +1,47 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use Symfony\Component\CssSelector\Node\ElementNode; |
use Symfony\Component\CssSelector\Node\FunctionNode; |
use Symfony\Component\CssSelector\Parser\Token; |
class FunctionNodeTest extends AbstractNodeTest |
{ |
public function getToStringConversionTestData() |
{ |
return array( |
array(new FunctionNode(new ElementNode(), 'function'), 'Function[Element[*]:function()]'), |
array(new FunctionNode(new ElementNode(), 'function', array( |
new Token(Token::TYPE_IDENTIFIER, 'value', 0), |
)), "Function[Element[*]:function(['value'])]"), |
array(new FunctionNode(new ElementNode(), 'function', array( |
new Token(Token::TYPE_STRING, 'value1', 0), |
new Token(Token::TYPE_NUMBER, 'value2', 0), |
)), "Function[Element[*]:function(['value1', 'value2'])]"), |
); |
} |
public function getSpecificityValueTestData() |
{ |
return array( |
array(new FunctionNode(new ElementNode(), 'function'), 10), |
array(new FunctionNode(new ElementNode(), 'function', array( |
new Token(Token::TYPE_IDENTIFIER, 'value', 0), |
)), 10), |
array(new FunctionNode(new ElementNode(), 'function', array( |
new Token(Token::TYPE_STRING, 'value1', 0), |
new Token(Token::TYPE_NUMBER, 'value2', 0), |
)), 10), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Node/HashNodeTest.php |
---|
@@ -0,0 +1,33 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use Symfony\Component\CssSelector\Node\HashNode; |
use Symfony\Component\CssSelector\Node\ElementNode; |
class HashNodeTest extends AbstractNodeTest |
{ |
public function getToStringConversionTestData() |
{ |
return array( |
array(new HashNode(new ElementNode(), 'id'), 'Hash[Element[*]#id]'), |
); |
} |
public function getSpecificityValueTestData() |
{ |
return array( |
array(new HashNode(new ElementNode(), 'id'), 100), |
array(new HashNode(new ElementNode(null, 'id'), 'class'), 101), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Node/NegationNodeTest.php |
---|
@@ -0,0 +1,33 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use Symfony\Component\CssSelector\Node\ClassNode; |
use Symfony\Component\CssSelector\Node\NegationNode; |
use Symfony\Component\CssSelector\Node\ElementNode; |
class NegationNodeTest extends AbstractNodeTest |
{ |
public function getToStringConversionTestData() |
{ |
return array( |
array(new NegationNode(new ElementNode(), new ClassNode(new ElementNode(), 'class')), 'Negation[Element[*]:not(Class[Element[*].class])]'), |
); |
} |
public function getSpecificityValueTestData() |
{ |
return array( |
array(new NegationNode(new ElementNode(), new ClassNode(new ElementNode(), 'class')), 10), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Node/PseudoNodeTest.php |
---|
@@ -0,0 +1,32 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use Symfony\Component\CssSelector\Node\ElementNode; |
use Symfony\Component\CssSelector\Node\PseudoNode; |
class PseudoNodeTest extends AbstractNodeTest |
{ |
public function getToStringConversionTestData() |
{ |
return array( |
array(new PseudoNode(new ElementNode(), 'pseudo'), 'Pseudo[Element[*]:pseudo]'), |
); |
} |
public function getSpecificityValueTestData() |
{ |
return array( |
array(new PseudoNode(new ElementNode(), 'pseudo'), 10), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Node/SelectorNodeTest.php |
---|
@@ -0,0 +1,34 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use Symfony\Component\CssSelector\Node\ElementNode; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
class SelectorNodeTest extends AbstractNodeTest |
{ |
public function getToStringConversionTestData() |
{ |
return array( |
array(new SelectorNode(new ElementNode()), 'Selector[Element[*]]'), |
array(new SelectorNode(new ElementNode(), 'pseudo'), 'Selector[Element[*]::pseudo]'), |
); |
} |
public function getSpecificityValueTestData() |
{ |
return array( |
array(new SelectorNode(new ElementNode()), 0), |
array(new SelectorNode(new ElementNode(), 'pseudo'), 1), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Node/SpecificityTest.php |
---|
@@ -0,0 +1,63 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Node; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\Node\Specificity; |
class SpecificityTest extends TestCase |
{ |
/** @dataProvider getValueTestData */ |
public function testValue(Specificity $specificity, $value) |
{ |
$this->assertEquals($value, $specificity->getValue()); |
} |
/** @dataProvider getValueTestData */ |
public function testPlusValue(Specificity $specificity, $value) |
{ |
$this->assertEquals($value + 123, $specificity->plus(new Specificity(1, 2, 3))->getValue()); |
} |
public function getValueTestData() |
{ |
return array( |
array(new Specificity(0, 0, 0), 0), |
array(new Specificity(0, 0, 2), 2), |
array(new Specificity(0, 3, 0), 30), |
array(new Specificity(4, 0, 0), 400), |
array(new Specificity(4, 3, 2), 432), |
); |
} |
/** @dataProvider getCompareTestData */ |
public function testCompareTo(Specificity $a, Specificity $b, $result) |
{ |
$this->assertEquals($result, $a->compareTo($b)); |
} |
public function getCompareTestData() |
{ |
return array( |
array(new Specificity(0, 0, 0), new Specificity(0, 0, 0), 0), |
array(new Specificity(0, 0, 1), new Specificity(0, 0, 1), 0), |
array(new Specificity(0, 0, 2), new Specificity(0, 0, 1), 1), |
array(new Specificity(0, 0, 2), new Specificity(0, 0, 3), -1), |
array(new Specificity(0, 4, 0), new Specificity(0, 4, 0), 0), |
array(new Specificity(0, 6, 0), new Specificity(0, 5, 11), 1), |
array(new Specificity(0, 7, 0), new Specificity(0, 8, 0), -1), |
array(new Specificity(9, 0, 0), new Specificity(9, 0, 0), 0), |
array(new Specificity(11, 0, 0), new Specificity(10, 11, 0), 1), |
array(new Specificity(12, 11, 0), new Specificity(13, 0, 0), -1), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Handler/AbstractHandlerTest.php |
---|
@@ -0,0 +1,70 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Handler; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\Parser\Reader; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
/** |
* @author Jean-François Simon <contact@jfsimon.fr> |
*/ |
abstract class AbstractHandlerTest extends TestCase |
{ |
/** @dataProvider getHandleValueTestData */ |
public function testHandleValue($value, Token $expectedToken, $remainingContent) |
{ |
$reader = new Reader($value); |
$stream = new TokenStream(); |
$this->assertTrue($this->generateHandler()->handle($reader, $stream)); |
$this->assertEquals($expectedToken, $stream->getNext()); |
$this->assertRemainingContent($reader, $remainingContent); |
} |
/** @dataProvider getDontHandleValueTestData */ |
public function testDontHandleValue($value) |
{ |
$reader = new Reader($value); |
$stream = new TokenStream(); |
$this->assertFalse($this->generateHandler()->handle($reader, $stream)); |
$this->assertStreamEmpty($stream); |
$this->assertRemainingContent($reader, $value); |
} |
abstract public function getHandleValueTestData(); |
abstract public function getDontHandleValueTestData(); |
abstract protected function generateHandler(); |
protected function assertStreamEmpty(TokenStream $stream) |
{ |
$property = new \ReflectionProperty($stream, 'tokens'); |
$property->setAccessible(true); |
$this->assertEquals(array(), $property->getValue($stream)); |
} |
protected function assertRemainingContent(Reader $reader, $remainingContent) |
{ |
if ('' === $remainingContent) { |
$this->assertEquals(0, $reader->getRemainingLength()); |
$this->assertTrue($reader->isEOF()); |
} else { |
$this->assertEquals(strlen($remainingContent), $reader->getRemainingLength()); |
$this->assertEquals(0, $reader->getOffset($remainingContent)); |
} |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Handler/CommentHandlerTest.php |
---|
@@ -0,0 +1,55 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Handler\CommentHandler; |
use Symfony\Component\CssSelector\Parser\Reader; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
class CommentHandlerTest extends AbstractHandlerTest |
{ |
/** @dataProvider getHandleValueTestData */ |
public function testHandleValue($value, Token $unusedArgument, $remainingContent) |
{ |
$reader = new Reader($value); |
$stream = new TokenStream(); |
$this->assertTrue($this->generateHandler()->handle($reader, $stream)); |
// comments are ignored (not pushed as token in stream) |
$this->assertStreamEmpty($stream); |
$this->assertRemainingContent($reader, $remainingContent); |
} |
public function getHandleValueTestData() |
{ |
return array( |
// 2nd argument only exists for inherited method compatibility |
array('/* comment */', new Token(null, null, null), ''), |
array('/* comment */foo', new Token(null, null, null), 'foo'), |
); |
} |
public function getDontHandleValueTestData() |
{ |
return array( |
array('>'), |
array('+'), |
array(' '), |
); |
} |
protected function generateHandler() |
{ |
return new CommentHandler(); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Handler/HashHandlerTest.php |
---|
@@ -0,0 +1,49 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Handler\HashHandler; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; |
class HashHandlerTest extends AbstractHandlerTest |
{ |
public function getHandleValueTestData() |
{ |
return array( |
array('#id', new Token(Token::TYPE_HASH, 'id', 0), ''), |
array('#123', new Token(Token::TYPE_HASH, '123', 0), ''), |
array('#id.class', new Token(Token::TYPE_HASH, 'id', 0), '.class'), |
array('#id element', new Token(Token::TYPE_HASH, 'id', 0), ' element'), |
); |
} |
public function getDontHandleValueTestData() |
{ |
return array( |
array('id'), |
array('123'), |
array('<'), |
array('<'), |
array('#'), |
); |
} |
protected function generateHandler() |
{ |
$patterns = new TokenizerPatterns(); |
return new HashHandler($patterns, new TokenizerEscaping($patterns)); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Handler/IdentifierHandlerTest.php |
---|
@@ -0,0 +1,49 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Handler\IdentifierHandler; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; |
class IdentifierHandlerTest extends AbstractHandlerTest |
{ |
public function getHandleValueTestData() |
{ |
return array( |
array('foo', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), ''), |
array('foo|bar', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '|bar'), |
array('foo.class', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '.class'), |
array('foo[attr]', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '[attr]'), |
array('foo bar', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), ' bar'), |
); |
} |
public function getDontHandleValueTestData() |
{ |
return array( |
array('>'), |
array('+'), |
array(' '), |
array('*|foo'), |
array('/* comment */'), |
); |
} |
protected function generateHandler() |
{ |
$patterns = new TokenizerPatterns(); |
return new IdentifierHandler($patterns, new TokenizerEscaping($patterns)); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Handler/NumberHandlerTest.php |
---|
@@ -0,0 +1,50 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Handler\NumberHandler; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; |
class NumberHandlerTest extends AbstractHandlerTest |
{ |
public function getHandleValueTestData() |
{ |
return array( |
array('12', new Token(Token::TYPE_NUMBER, '12', 0), ''), |
array('12.34', new Token(Token::TYPE_NUMBER, '12.34', 0), ''), |
array('+12.34', new Token(Token::TYPE_NUMBER, '+12.34', 0), ''), |
array('-12.34', new Token(Token::TYPE_NUMBER, '-12.34', 0), ''), |
array('12 arg', new Token(Token::TYPE_NUMBER, '12', 0), ' arg'), |
array('12]', new Token(Token::TYPE_NUMBER, '12', 0), ']'), |
); |
} |
public function getDontHandleValueTestData() |
{ |
return array( |
array('hello'), |
array('>'), |
array('+'), |
array(' '), |
array('/* comment */'), |
); |
} |
protected function generateHandler() |
{ |
$patterns = new TokenizerPatterns(); |
return new NumberHandler($patterns); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Handler/StringHandlerTest.php |
---|
@@ -0,0 +1,50 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Handler\StringHandler; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; |
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; |
class StringHandlerTest extends AbstractHandlerTest |
{ |
public function getHandleValueTestData() |
{ |
return array( |
array('"hello"', new Token(Token::TYPE_STRING, 'hello', 1), ''), |
array('"1"', new Token(Token::TYPE_STRING, '1', 1), ''), |
array('" "', new Token(Token::TYPE_STRING, ' ', 1), ''), |
array('""', new Token(Token::TYPE_STRING, '', 1), ''), |
array("'hello'", new Token(Token::TYPE_STRING, 'hello', 1), ''), |
array("'foo'bar", new Token(Token::TYPE_STRING, 'foo', 1), 'bar'), |
); |
} |
public function getDontHandleValueTestData() |
{ |
return array( |
array('hello'), |
array('>'), |
array('1'), |
array(' '), |
); |
} |
protected function generateHandler() |
{ |
$patterns = new TokenizerPatterns(); |
return new StringHandler($patterns, new TokenizerEscaping($patterns)); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Handler/WhitespaceHandlerTest.php |
---|
@@ -0,0 +1,44 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Handler; |
use Symfony\Component\CssSelector\Parser\Handler\WhitespaceHandler; |
use Symfony\Component\CssSelector\Parser\Token; |
class WhitespaceHandlerTest extends AbstractHandlerTest |
{ |
public function getHandleValueTestData() |
{ |
return array( |
array(' ', new Token(Token::TYPE_WHITESPACE, ' ', 0), ''), |
array("\n", new Token(Token::TYPE_WHITESPACE, "\n", 0), ''), |
array("\t", new Token(Token::TYPE_WHITESPACE, "\t", 0), ''), |
array(' foo', new Token(Token::TYPE_WHITESPACE, ' ', 0), 'foo'), |
array(' .foo', new Token(Token::TYPE_WHITESPACE, ' ', 0), '.foo'), |
); |
} |
public function getDontHandleValueTestData() |
{ |
return array( |
array('>'), |
array('1'), |
array('a'), |
); |
} |
protected function generateHandler() |
{ |
return new WhitespaceHandler(); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/ParserTest.php |
---|
@@ -0,0 +1,249 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\Exception\SyntaxErrorException; |
use Symfony\Component\CssSelector\Node\FunctionNode; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
use Symfony\Component\CssSelector\Parser\Parser; |
use Symfony\Component\CssSelector\Parser\Token; |
class ParserTest extends TestCase |
{ |
/** @dataProvider getParserTestData */ |
public function testParser($source, $representation) |
{ |
$parser = new Parser(); |
$this->assertEquals($representation, array_map(function (SelectorNode $node) { |
return (string) $node->getTree(); |
}, $parser->parse($source))); |
} |
/** @dataProvider getParserExceptionTestData */ |
public function testParserException($source, $message) |
{ |
$parser = new Parser(); |
try { |
$parser->parse($source); |
$this->fail('Parser should throw a SyntaxErrorException.'); |
} catch (SyntaxErrorException $e) { |
$this->assertEquals($message, $e->getMessage()); |
} |
} |
/** @dataProvider getPseudoElementsTestData */ |
public function testPseudoElements($source, $element, $pseudo) |
{ |
$parser = new Parser(); |
$selectors = $parser->parse($source); |
$this->assertCount(1, $selectors); |
/** @var SelectorNode $selector */ |
$selector = $selectors[0]; |
$this->assertEquals($element, (string) $selector->getTree()); |
$this->assertEquals($pseudo, (string) $selector->getPseudoElement()); |
} |
/** @dataProvider getSpecificityTestData */ |
public function testSpecificity($source, $value) |
{ |
$parser = new Parser(); |
$selectors = $parser->parse($source); |
$this->assertCount(1, $selectors); |
/** @var SelectorNode $selector */ |
$selector = $selectors[0]; |
$this->assertEquals($value, $selector->getSpecificity()->getValue()); |
} |
/** @dataProvider getParseSeriesTestData */ |
public function testParseSeries($series, $a, $b) |
{ |
$parser = new Parser(); |
$selectors = $parser->parse(sprintf(':nth-child(%s)', $series)); |
$this->assertCount(1, $selectors); |
/** @var FunctionNode $function */ |
$function = $selectors[0]->getTree(); |
$this->assertEquals(array($a, $b), Parser::parseSeries($function->getArguments())); |
} |
/** @dataProvider getParseSeriesExceptionTestData */ |
public function testParseSeriesException($series) |
{ |
$parser = new Parser(); |
$selectors = $parser->parse(sprintf(':nth-child(%s)', $series)); |
$this->assertCount(1, $selectors); |
/** @var FunctionNode $function */ |
$function = $selectors[0]->getTree(); |
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\CssSelector\Exception\SyntaxErrorException'); |
Parser::parseSeries($function->getArguments()); |
} |
public function getParserTestData() |
{ |
return array( |
array('*', array('Element[*]')), |
array('*|*', array('Element[*]')), |
array('*|foo', array('Element[foo]')), |
array('foo|*', array('Element[foo|*]')), |
array('foo|bar', array('Element[foo|bar]')), |
array('#foo#bar', array('Hash[Hash[Element[*]#foo]#bar]')), |
array('div>.foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), |
array('div> .foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), |
array('div >.foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), |
array('div > .foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), |
array("div \n> \t \t .foo", array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), |
array('td.foo,.bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), |
array('td.foo, .bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), |
array("td.foo\t\r\n\f ,\t\r\n\f .bar", array('Class[Element[td].foo]', 'Class[Element[*].bar]')), |
array('td.foo,.bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), |
array('td.foo, .bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), |
array("td.foo\t\r\n\f ,\t\r\n\f .bar", array('Class[Element[td].foo]', 'Class[Element[*].bar]')), |
array('div, td.foo, div.bar span', array('Element[div]', 'Class[Element[td].foo]', 'CombinedSelector[Class[Element[div].bar] <followed> Element[span]]')), |
array('div > p', array('CombinedSelector[Element[div] > Element[p]]')), |
array('td:first', array('Pseudo[Element[td]:first]')), |
array('td :first', array('CombinedSelector[Element[td] <followed> Pseudo[Element[*]:first]]')), |
array('a[name]', array('Attribute[Element[a][name]]')), |
array("a[ name\t]", array('Attribute[Element[a][name]]')), |
array('a [name]', array('CombinedSelector[Element[a] <followed> Attribute[Element[*][name]]]')), |
array('a[rel="include"]', array("Attribute[Element[a][rel = 'include']]")), |
array('a[rel = include]', array("Attribute[Element[a][rel = 'include']]")), |
array("a[hreflang |= 'en']", array("Attribute[Element[a][hreflang |= 'en']]")), |
array('a[hreflang|=en]', array("Attribute[Element[a][hreflang |= 'en']]")), |
array('div:nth-child(10)', array("Function[Element[div]:nth-child(['10'])]")), |
array(':nth-child(2n+2)', array("Function[Element[*]:nth-child(['2', 'n', '+2'])]")), |
array('div:nth-of-type(10)', array("Function[Element[div]:nth-of-type(['10'])]")), |
array('div div:nth-of-type(10) .aclass', array("CombinedSelector[CombinedSelector[Element[div] <followed> Function[Element[div]:nth-of-type(['10'])]] <followed> Class[Element[*].aclass]]")), |
array('label:only', array('Pseudo[Element[label]:only]')), |
array('a:lang(fr)', array("Function[Element[a]:lang(['fr'])]")), |
array('div:contains("foo")', array("Function[Element[div]:contains(['foo'])]")), |
array('div#foobar', array('Hash[Element[div]#foobar]')), |
array('div:not(div.foo)', array('Negation[Element[div]:not(Class[Element[div].foo])]')), |
array('td ~ th', array('CombinedSelector[Element[td] ~ Element[th]]')), |
array('.foo[data-bar][data-baz=0]', array("Attribute[Attribute[Class[Element[*].foo][data-bar]][data-baz = '0']]")), |
); |
} |
public function getParserExceptionTestData() |
{ |
return array( |
array('attributes(href)/html/body/a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '(', 10))->getMessage()), |
array('attributes(href)', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '(', 10))->getMessage()), |
array('html/body/a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '/', 4))->getMessage()), |
array(' ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 1))->getMessage()), |
array('div, ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 5))->getMessage()), |
array(' , div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, ',', 1))->getMessage()), |
array('p, , div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, ',', 3))->getMessage()), |
array('div > ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 6))->getMessage()), |
array(' > div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '>', 2))->getMessage()), |
array('foo|#bar', SyntaxErrorException::unexpectedToken('identifier or "*"', new Token(Token::TYPE_HASH, 'bar', 4))->getMessage()), |
array('#.foo', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '#', 0))->getMessage()), |
array('.#foo', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_HASH, 'foo', 1))->getMessage()), |
array(':#foo', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_HASH, 'foo', 1))->getMessage()), |
array('[*]', SyntaxErrorException::unexpectedToken('"|"', new Token(Token::TYPE_DELIMITER, ']', 2))->getMessage()), |
array('[foo|]', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_DELIMITER, ']', 5))->getMessage()), |
array('[#]', SyntaxErrorException::unexpectedToken('identifier or "*"', new Token(Token::TYPE_DELIMITER, '#', 1))->getMessage()), |
array('[foo=#]', SyntaxErrorException::unexpectedToken('string or identifier', new Token(Token::TYPE_DELIMITER, '#', 5))->getMessage()), |
array(':nth-child()', SyntaxErrorException::unexpectedToken('at least one argument', new Token(Token::TYPE_DELIMITER, ')', 11))->getMessage()), |
array('[href]a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_IDENTIFIER, 'a', 6))->getMessage()), |
array('[rel:stylesheet]', SyntaxErrorException::unexpectedToken('operator', new Token(Token::TYPE_DELIMITER, ':', 4))->getMessage()), |
array('[rel=stylesheet', SyntaxErrorException::unexpectedToken('"]"', new Token(Token::TYPE_FILE_END, '', 15))->getMessage()), |
array(':lang(fr', SyntaxErrorException::unexpectedToken('an argument', new Token(Token::TYPE_FILE_END, '', 8))->getMessage()), |
array(':contains("foo', SyntaxErrorException::unclosedString(10)->getMessage()), |
array('foo!', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '!', 3))->getMessage()), |
); |
} |
public function getPseudoElementsTestData() |
{ |
return array( |
array('foo', 'Element[foo]', ''), |
array('*', 'Element[*]', ''), |
array(':empty', 'Pseudo[Element[*]:empty]', ''), |
array(':BEfore', 'Element[*]', 'before'), |
array(':aftER', 'Element[*]', 'after'), |
array(':First-Line', 'Element[*]', 'first-line'), |
array(':First-Letter', 'Element[*]', 'first-letter'), |
array('::befoRE', 'Element[*]', 'before'), |
array('::AFter', 'Element[*]', 'after'), |
array('::firsT-linE', 'Element[*]', 'first-line'), |
array('::firsT-letteR', 'Element[*]', 'first-letter'), |
array('::Selection', 'Element[*]', 'selection'), |
array('foo:after', 'Element[foo]', 'after'), |
array('foo::selection', 'Element[foo]', 'selection'), |
array('lorem#ipsum ~ a#b.c[href]:empty::selection', 'CombinedSelector[Hash[Element[lorem]#ipsum] ~ Pseudo[Attribute[Class[Hash[Element[a]#b].c][href]]:empty]]', 'selection'), |
); |
} |
public function getSpecificityTestData() |
{ |
return array( |
array('*', 0), |
array(' foo', 1), |
array(':empty ', 10), |
array(':before', 1), |
array('*:before', 1), |
array(':nth-child(2)', 10), |
array('.bar', 10), |
array('[baz]', 10), |
array('[baz="4"]', 10), |
array('[baz^="4"]', 10), |
array('#lipsum', 100), |
array(':not(*)', 0), |
array(':not(foo)', 1), |
array(':not(.foo)', 10), |
array(':not([foo])', 10), |
array(':not(:empty)', 10), |
array(':not(#foo)', 100), |
array('foo:empty', 11), |
array('foo:before', 2), |
array('foo::before', 2), |
array('foo:empty::before', 12), |
array('#lorem + foo#ipsum:first-child > bar:first-line', 213), |
); |
} |
public function getParseSeriesTestData() |
{ |
return array( |
array('1n+3', 1, 3), |
array('1n +3', 1, 3), |
array('1n + 3', 1, 3), |
array('1n+ 3', 1, 3), |
array('1n-3', 1, -3), |
array('1n -3', 1, -3), |
array('1n - 3', 1, -3), |
array('1n- 3', 1, -3), |
array('n-5', 1, -5), |
array('odd', 2, 1), |
array('even', 2, 0), |
array('3n', 3, 0), |
array('n', 1, 0), |
array('+n', 1, 0), |
array('-n', -1, 0), |
array('5', 0, 5), |
); |
} |
public function getParseSeriesExceptionTestData() |
{ |
return array( |
array('foo'), |
array('n+'), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/ReaderTest.php |
---|
@@ -0,0 +1,102 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\Parser\Reader; |
class ReaderTest extends TestCase |
{ |
public function testIsEOF() |
{ |
$reader = new Reader(''); |
$this->assertTrue($reader->isEOF()); |
$reader = new Reader('hello'); |
$this->assertFalse($reader->isEOF()); |
$this->assignPosition($reader, 2); |
$this->assertFalse($reader->isEOF()); |
$this->assignPosition($reader, 5); |
$this->assertTrue($reader->isEOF()); |
} |
public function testGetRemainingLength() |
{ |
$reader = new Reader('hello'); |
$this->assertEquals(5, $reader->getRemainingLength()); |
$this->assignPosition($reader, 2); |
$this->assertEquals(3, $reader->getRemainingLength()); |
$this->assignPosition($reader, 5); |
$this->assertEquals(0, $reader->getRemainingLength()); |
} |
public function testGetSubstring() |
{ |
$reader = new Reader('hello'); |
$this->assertEquals('he', $reader->getSubstring(2)); |
$this->assertEquals('el', $reader->getSubstring(2, 1)); |
$this->assignPosition($reader, 2); |
$this->assertEquals('ll', $reader->getSubstring(2)); |
$this->assertEquals('lo', $reader->getSubstring(2, 1)); |
} |
public function testGetOffset() |
{ |
$reader = new Reader('hello'); |
$this->assertEquals(2, $reader->getOffset('ll')); |
$this->assertFalse($reader->getOffset('w')); |
$this->assignPosition($reader, 2); |
$this->assertEquals(0, $reader->getOffset('ll')); |
$this->assertFalse($reader->getOffset('he')); |
} |
public function testFindPattern() |
{ |
$reader = new Reader('hello'); |
$this->assertFalse($reader->findPattern('/world/')); |
$this->assertEquals(array('hello', 'h'), $reader->findPattern('/^([a-z]).*/')); |
$this->assignPosition($reader, 2); |
$this->assertFalse($reader->findPattern('/^h.*/')); |
$this->assertEquals(array('llo'), $reader->findPattern('/^llo$/')); |
} |
public function testMoveForward() |
{ |
$reader = new Reader('hello'); |
$this->assertEquals(0, $reader->getPosition()); |
$reader->moveForward(2); |
$this->assertEquals(2, $reader->getPosition()); |
} |
public function testToEnd() |
{ |
$reader = new Reader('hello'); |
$reader->moveToEnd(); |
$this->assertTrue($reader->isEOF()); |
} |
private function assignPosition(Reader $reader, $value) |
{ |
$position = new \ReflectionProperty($reader, 'position'); |
$position->setAccessible(true); |
$position->setValue($reader, $value); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Shortcut/ClassParserTest.php |
---|
@@ -0,0 +1,45 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser; |
/** |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
*/ |
class ClassParserTest extends TestCase |
{ |
/** @dataProvider getParseTestData */ |
public function testParse($source, $representation) |
{ |
$parser = new ClassParser(); |
$selectors = $parser->parse($source); |
$this->assertCount(1, $selectors); |
/** @var SelectorNode $selector */ |
$selector = $selectors[0]; |
$this->assertEquals($representation, (string) $selector->getTree()); |
} |
public function getParseTestData() |
{ |
return array( |
array('.testclass', 'Class[Element[*].testclass]'), |
array('testel.testclass', 'Class[Element[testel].testclass]'), |
array('testns|.testclass', 'Class[Element[testns|*].testclass]'), |
array('testns|*.testclass', 'Class[Element[testns|*].testclass]'), |
array('testns|testel.testclass', 'Class[Element[testns|testel].testclass]'), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Shortcut/ElementParserTest.php |
---|
@@ -0,0 +1,44 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser; |
/** |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
*/ |
class ElementParserTest extends TestCase |
{ |
/** @dataProvider getParseTestData */ |
public function testParse($source, $representation) |
{ |
$parser = new ElementParser(); |
$selectors = $parser->parse($source); |
$this->assertCount(1, $selectors); |
/** @var SelectorNode $selector */ |
$selector = $selectors[0]; |
$this->assertEquals($representation, (string) $selector->getTree()); |
} |
public function getParseTestData() |
{ |
return array( |
array('*', 'Element[*]'), |
array('testel', 'Element[testel]'), |
array('testns|*', 'Element[testns|*]'), |
array('testns|testel', 'Element[testns|testel]'), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Shortcut/EmptyStringParserTest.php |
---|
@@ -0,0 +1,36 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser; |
/** |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
*/ |
class EmptyStringParserTest extends TestCase |
{ |
public function testParse() |
{ |
$parser = new EmptyStringParser(); |
$selectors = $parser->parse(''); |
$this->assertCount(1, $selectors); |
/** @var SelectorNode $selector */ |
$selector = $selectors[0]; |
$this->assertEquals('Element[*]', (string) $selector->getTree()); |
$selectors = $parser->parse('this will produce an empty array'); |
$this->assertCount(0, $selectors); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/Shortcut/HashParserTest.php |
---|
@@ -0,0 +1,45 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
use Symfony\Component\CssSelector\Parser\Shortcut\HashParser; |
/** |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
*/ |
class HashParserTest extends TestCase |
{ |
/** @dataProvider getParseTestData */ |
public function testParse($source, $representation) |
{ |
$parser = new HashParser(); |
$selectors = $parser->parse($source); |
$this->assertCount(1, $selectors); |
/** @var SelectorNode $selector */ |
$selector = $selectors[0]; |
$this->assertEquals($representation, (string) $selector->getTree()); |
} |
public function getParseTestData() |
{ |
return array( |
array('#testid', 'Hash[Element[*]#testid]'), |
array('testel#testid', 'Hash[Element[testel]#testid]'), |
array('testns|#testid', 'Hash[Element[testns|*]#testid]'), |
array('testns|*#testid', 'Hash[Element[testns|*]#testid]'), |
array('testns|testel#testid', 'Hash[Element[testns|testel]#testid]'), |
); |
} |
} |
/vendor/symfony/css-selector/Tests/Parser/TokenStreamTest.php |
---|
@@ -0,0 +1,96 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\Parser; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\Parser\Token; |
use Symfony\Component\CssSelector\Parser\TokenStream; |
class TokenStreamTest extends TestCase |
{ |
public function testGetNext() |
{ |
$stream = new TokenStream(); |
$stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); |
$stream->push($t2 = new Token(Token::TYPE_DELIMITER, '.', 2)); |
$stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'title', 3)); |
$this->assertSame($t1, $stream->getNext()); |
$this->assertSame($t2, $stream->getNext()); |
$this->assertSame($t3, $stream->getNext()); |
} |
public function testGetPeek() |
{ |
$stream = new TokenStream(); |
$stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); |
$stream->push($t2 = new Token(Token::TYPE_DELIMITER, '.', 2)); |
$stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'title', 3)); |
$this->assertSame($t1, $stream->getPeek()); |
$this->assertSame($t1, $stream->getNext()); |
$this->assertSame($t2, $stream->getPeek()); |
$this->assertSame($t2, $stream->getPeek()); |
$this->assertSame($t2, $stream->getNext()); |
} |
public function testGetNextIdentifier() |
{ |
$stream = new TokenStream(); |
$stream->push(new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); |
$this->assertEquals('h1', $stream->getNextIdentifier()); |
} |
public function testFailToGetNextIdentifier() |
{ |
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\CssSelector\Exception\SyntaxErrorException'); |
$stream = new TokenStream(); |
$stream->push(new Token(Token::TYPE_DELIMITER, '.', 2)); |
$stream->getNextIdentifier(); |
} |
public function testGetNextIdentifierOrStar() |
{ |
$stream = new TokenStream(); |
$stream->push(new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); |
$this->assertEquals('h1', $stream->getNextIdentifierOrStar()); |
$stream->push(new Token(Token::TYPE_DELIMITER, '*', 0)); |
$this->assertNull($stream->getNextIdentifierOrStar()); |
} |
public function testFailToGetNextIdentifierOrStar() |
{ |
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\CssSelector\Exception\SyntaxErrorException'); |
$stream = new TokenStream(); |
$stream->push(new Token(Token::TYPE_DELIMITER, '.', 2)); |
$stream->getNextIdentifierOrStar(); |
} |
public function testSkipWhitespace() |
{ |
$stream = new TokenStream(); |
$stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); |
$stream->push($t2 = new Token(Token::TYPE_WHITESPACE, ' ', 2)); |
$stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'h1', 3)); |
$stream->skipWhitespace(); |
$this->assertSame($t1, $stream->getNext()); |
$stream->skipWhitespace(); |
$this->assertSame($t3, $stream->getNext()); |
} |
} |
/vendor/symfony/css-selector/Tests/XPath/Fixtures/ids.html |
---|
@@ -0,0 +1,48 @@ |
<html id="html"><head> |
<link id="link-href" href="foo" /> |
<link id="link-nohref" /> |
</head><body> |
<div id="outer-div"> |
<a id="name-anchor" name="foo"></a> |
<a id="tag-anchor" rel="tag" href="http://localhost/foo">link</a> |
<a id="nofollow-anchor" rel="nofollow" href="https://example.org"> |
link</a> |
<ol id="first-ol" class="a b c"> |
<li id="first-li">content</li> |
<li id="second-li" lang="En-us"> |
<div id="li-div"> |
</div> |
</li> |
<li id="third-li" class="ab c"></li> |
<li id="fourth-li" class="ab |
c"></li> |
<li id="fifth-li"></li> |
<li id="sixth-li"></li> |
<li id="seventh-li"> </li> |
</ol> |
<p id="paragraph"> |
<b id="p-b">hi</b> <em id="p-em">there</em> |
<b id="p-b2">guy</b> |
<input type="checkbox" id="checkbox-unchecked" /> |
<input type="checkbox" id="checkbox-disabled" disabled="" /> |
<input type="text" id="text-checked" checked="checked" /> |
<input type="hidden" /> |
<input type="hidden" disabled="disabled" /> |
<input type="checkbox" id="checkbox-checked" checked="checked" /> |
<input type="checkbox" id="checkbox-disabled-checked" |
disabled="disabled" checked="checked" /> |
<fieldset id="fieldset" disabled="disabled"> |
<input type="checkbox" id="checkbox-fieldset-disabled" /> |
<input type="hidden" /> |
</fieldset> |
</p> |
<ol id="second-ol"> |
</ol> |
<map name="dummymap"> |
<area shape="circle" coords="200,250,25" href="foo.html" id="area-href" /> |
<area shape="default" id="area-nohref" /> |
</map> |
</div> |
<div id="foobar-div" foobar="ab bc |
cde"><span id="foobar-span"></span></div> |
</body></html> |
/vendor/symfony/css-selector/Tests/XPath/Fixtures/lang.xml |
---|
@@ -0,0 +1,11 @@ |
<test> |
<a id="first" xml:lang="en">a</a> |
<b id="second" xml:lang="en-US">b</b> |
<c id="third" xml:lang="en-Nz">c</c> |
<d id="fourth" xml:lang="En-us">d</d> |
<e id="fifth" xml:lang="fr">e</e> |
<f id="sixth" xml:lang="ru">f</f> |
<g id="seventh" xml:lang="de"> |
<h id="eighth" xml:lang="zh"/> |
</g> |
</test> |
/vendor/symfony/css-selector/Tests/XPath/Fixtures/shakespear.html |
---|
@@ -0,0 +1,308 @@ |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" debug="true"> |
<head> |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> |
</head> |
<body> |
<div id="test"> |
<div class="dialog"> |
<h2>As You Like It</h2> |
<div id="playwright"> |
by William Shakespeare |
</div> |
<div class="dialog scene thirdClass" id="scene1"> |
<h3>ACT I, SCENE III. A room in the palace.</h3> |
<div class="dialog"> |
<div class="direction">Enter CELIA and ROSALIND</div> |
</div> |
<div id="speech1" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.1">Why, cousin! why, Rosalind! Cupid have mercy! not a word?</div> |
</div> |
<div id="speech2" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.2">Not one to throw at a dog.</div> |
</div> |
<div id="speech3" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.3">No, thy words are too precious to be cast away upon</div> |
<div id="scene1.3.4">curs; throw some of them at me; come, lame me with reasons.</div> |
</div> |
<div id="speech4" class="character">ROSALIND</div> |
<div id="speech5" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.8">But is all this for your father?</div> |
</div> |
<div class="dialog"> |
<div id="scene1.3.5">Then there were two cousins laid up; when the one</div> |
<div id="scene1.3.6">should be lamed with reasons and the other mad</div> |
<div id="scene1.3.7">without any.</div> |
</div> |
<div id="speech6" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.9">No, some of it is for my child's father. O, how</div> |
<div id="scene1.3.10">full of briers is this working-day world!</div> |
</div> |
<div id="speech7" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.11">They are but burs, cousin, thrown upon thee in</div> |
<div id="scene1.3.12">holiday foolery: if we walk not in the trodden</div> |
<div id="scene1.3.13">paths our very petticoats will catch them.</div> |
</div> |
<div id="speech8" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.14">I could shake them off my coat: these burs are in my heart.</div> |
</div> |
<div id="speech9" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.15">Hem them away.</div> |
</div> |
<div id="speech10" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.16">I would try, if I could cry 'hem' and have him.</div> |
</div> |
<div id="speech11" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.17">Come, come, wrestle with thy affections.</div> |
</div> |
<div id="speech12" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.18">O, they take the part of a better wrestler than myself!</div> |
</div> |
<div id="speech13" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.19">O, a good wish upon you! you will try in time, in</div> |
<div id="scene1.3.20">despite of a fall. But, turning these jests out of</div> |
<div id="scene1.3.21">service, let us talk in good earnest: is it</div> |
<div id="scene1.3.22">possible, on such a sudden, you should fall into so</div> |
<div id="scene1.3.23">strong a liking with old Sir Rowland's youngest son?</div> |
</div> |
<div id="speech14" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.24">The duke my father loved his father dearly.</div> |
</div> |
<div id="speech15" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.25">Doth it therefore ensue that you should love his son</div> |
<div id="scene1.3.26">dearly? By this kind of chase, I should hate him,</div> |
<div id="scene1.3.27">for my father hated his father dearly; yet I hate</div> |
<div id="scene1.3.28">not Orlando.</div> |
</div> |
<div id="speech16" class="character">ROSALIND</div> |
<div title="wtf" class="dialog"> |
<div id="scene1.3.29">No, faith, hate him not, for my sake.</div> |
</div> |
<div id="speech17" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.30">Why should I not? doth he not deserve well?</div> |
</div> |
<div id="speech18" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.31">Let me love him for that, and do you love him</div> |
<div id="scene1.3.32">because I do. Look, here comes the duke.</div> |
</div> |
<div id="speech19" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.33">With his eyes full of anger.</div> |
<div class="direction">Enter DUKE FREDERICK, with Lords</div> |
</div> |
<div id="speech20" class="character">DUKE FREDERICK</div> |
<div class="dialog"> |
<div id="scene1.3.34">Mistress, dispatch you with your safest haste</div> |
<div id="scene1.3.35">And get you from our court.</div> |
</div> |
<div id="speech21" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.36">Me, uncle?</div> |
</div> |
<div id="speech22" class="character">DUKE FREDERICK</div> |
<div class="dialog"> |
<div id="scene1.3.37">You, cousin</div> |
<div id="scene1.3.38">Within these ten days if that thou be'st found</div> |
<div id="scene1.3.39">So near our public court as twenty miles,</div> |
<div id="scene1.3.40">Thou diest for it.</div> |
</div> |
<div id="speech23" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.41"> I do beseech your grace,</div> |
<div id="scene1.3.42">Let me the knowledge of my fault bear with me:</div> |
<div id="scene1.3.43">If with myself I hold intelligence</div> |
<div id="scene1.3.44">Or have acquaintance with mine own desires,</div> |
<div id="scene1.3.45">If that I do not dream or be not frantic,--</div> |
<div id="scene1.3.46">As I do trust I am not--then, dear uncle,</div> |
<div id="scene1.3.47">Never so much as in a thought unborn</div> |
<div id="scene1.3.48">Did I offend your highness.</div> |
</div> |
<div id="speech24" class="character">DUKE FREDERICK</div> |
<div class="dialog"> |
<div id="scene1.3.49">Thus do all traitors:</div> |
<div id="scene1.3.50">If their purgation did consist in words,</div> |
<div id="scene1.3.51">They are as innocent as grace itself:</div> |
<div id="scene1.3.52">Let it suffice thee that I trust thee not.</div> |
</div> |
<div id="speech25" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.53">Yet your mistrust cannot make me a traitor:</div> |
<div id="scene1.3.54">Tell me whereon the likelihood depends.</div> |
</div> |
<div id="speech26" class="character">DUKE FREDERICK</div> |
<div class="dialog"> |
<div id="scene1.3.55">Thou art thy father's daughter; there's enough.</div> |
</div> |
<div id="speech27" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.56">So was I when your highness took his dukedom;</div> |
<div id="scene1.3.57">So was I when your highness banish'd him:</div> |
<div id="scene1.3.58">Treason is not inherited, my lord;</div> |
<div id="scene1.3.59">Or, if we did derive it from our friends,</div> |
<div id="scene1.3.60">What's that to me? my father was no traitor:</div> |
<div id="scene1.3.61">Then, good my liege, mistake me not so much</div> |
<div id="scene1.3.62">To think my poverty is treacherous.</div> |
</div> |
<div id="speech28" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.63">Dear sovereign, hear me speak.</div> |
</div> |
<div id="speech29" class="character">DUKE FREDERICK</div> |
<div class="dialog"> |
<div id="scene1.3.64">Ay, Celia; we stay'd her for your sake,</div> |
<div id="scene1.3.65">Else had she with her father ranged along.</div> |
</div> |
<div id="speech30" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.66">I did not then entreat to have her stay;</div> |
<div id="scene1.3.67">It was your pleasure and your own remorse:</div> |
<div id="scene1.3.68">I was too young that time to value her;</div> |
<div id="scene1.3.69">But now I know her: if she be a traitor,</div> |
<div id="scene1.3.70">Why so am I; we still have slept together,</div> |
<div id="scene1.3.71">Rose at an instant, learn'd, play'd, eat together,</div> |
<div id="scene1.3.72">And wheresoever we went, like Juno's swans,</div> |
<div id="scene1.3.73">Still we went coupled and inseparable.</div> |
</div> |
<div id="speech31" class="character">DUKE FREDERICK</div> |
<div class="dialog"> |
<div id="scene1.3.74">She is too subtle for thee; and her smoothness,</div> |
<div id="scene1.3.75">Her very silence and her patience</div> |
<div id="scene1.3.76">Speak to the people, and they pity her.</div> |
<div id="scene1.3.77">Thou art a fool: she robs thee of thy name;</div> |
<div id="scene1.3.78">And thou wilt show more bright and seem more virtuous</div> |
<div id="scene1.3.79">When she is gone. Then open not thy lips:</div> |
<div id="scene1.3.80">Firm and irrevocable is my doom</div> |
<div id="scene1.3.81">Which I have pass'd upon her; she is banish'd.</div> |
</div> |
<div id="speech32" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.82">Pronounce that sentence then on me, my liege:</div> |
<div id="scene1.3.83">I cannot live out of her company.</div> |
</div> |
<div id="speech33" class="character">DUKE FREDERICK</div> |
<div class="dialog"> |
<div id="scene1.3.84">You are a fool. You, niece, provide yourself:</div> |
<div id="scene1.3.85">If you outstay the time, upon mine honour,</div> |
<div id="scene1.3.86">And in the greatness of my word, you die.</div> |
<div class="direction">Exeunt DUKE FREDERICK and Lords</div> |
</div> |
<div id="speech34" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.87">O my poor Rosalind, whither wilt thou go?</div> |
<div id="scene1.3.88">Wilt thou change fathers? I will give thee mine.</div> |
<div id="scene1.3.89">I charge thee, be not thou more grieved than I am.</div> |
</div> |
<div id="speech35" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.90">I have more cause.</div> |
</div> |
<div id="speech36" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.91"> Thou hast not, cousin;</div> |
<div id="scene1.3.92">Prithee be cheerful: know'st thou not, the duke</div> |
<div id="scene1.3.93">Hath banish'd me, his daughter?</div> |
</div> |
<div id="speech37" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.94">That he hath not.</div> |
</div> |
<div id="speech38" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.95">No, hath not? Rosalind lacks then the love</div> |
<div id="scene1.3.96">Which teacheth thee that thou and I am one:</div> |
<div id="scene1.3.97">Shall we be sunder'd? shall we part, sweet girl?</div> |
<div id="scene1.3.98">No: let my father seek another heir.</div> |
<div id="scene1.3.99">Therefore devise with me how we may fly,</div> |
<div id="scene1.3.100">Whither to go and what to bear with us;</div> |
<div id="scene1.3.101">And do not seek to take your change upon you,</div> |
<div id="scene1.3.102">To bear your griefs yourself and leave me out;</div> |
<div id="scene1.3.103">For, by this heaven, now at our sorrows pale,</div> |
<div id="scene1.3.104">Say what thou canst, I'll go along with thee.</div> |
</div> |
<div id="speech39" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.105">Why, whither shall we go?</div> |
</div> |
<div id="speech40" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.106">To seek my uncle in the forest of Arden.</div> |
</div> |
<div id="speech41" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.107">Alas, what danger will it be to us,</div> |
<div id="scene1.3.108">Maids as we are, to travel forth so far!</div> |
<div id="scene1.3.109">Beauty provoketh thieves sooner than gold.</div> |
</div> |
<div id="speech42" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.110">I'll put myself in poor and mean attire</div> |
<div id="scene1.3.111">And with a kind of umber smirch my face;</div> |
<div id="scene1.3.112">The like do you: so shall we pass along</div> |
<div id="scene1.3.113">And never stir assailants.</div> |
</div> |
<div id="speech43" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.114">Were it not better,</div> |
<div id="scene1.3.115">Because that I am more than common tall,</div> |
<div id="scene1.3.116">That I did suit me all points like a man?</div> |
<div id="scene1.3.117">A gallant curtle-axe upon my thigh,</div> |
<div id="scene1.3.118">A boar-spear in my hand; and--in my heart</div> |
<div id="scene1.3.119">Lie there what hidden woman's fear there will--</div> |
<div id="scene1.3.120">We'll have a swashing and a martial outside,</div> |
<div id="scene1.3.121">As many other mannish cowards have</div> |
<div id="scene1.3.122">That do outface it with their semblances.</div> |
</div> |
<div id="speech44" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.123">What shall I call thee when thou art a man?</div> |
</div> |
<div id="speech45" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.124">I'll have no worse a name than Jove's own page;</div> |
<div id="scene1.3.125">And therefore look you call me Ganymede.</div> |
<div id="scene1.3.126">But what will you be call'd?</div> |
</div> |
<div id="speech46" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.127">Something that hath a reference to my state</div> |
<div id="scene1.3.128">No longer Celia, but Aliena.</div> |
</div> |
<div id="speech47" class="character">ROSALIND</div> |
<div class="dialog"> |
<div id="scene1.3.129">But, cousin, what if we assay'd to steal</div> |
<div id="scene1.3.130">The clownish fool out of your father's court?</div> |
<div id="scene1.3.131">Would he not be a comfort to our travel?</div> |
</div> |
<div id="speech48" class="character">CELIA</div> |
<div class="dialog"> |
<div id="scene1.3.132">He'll go along o'er the wide world with me;</div> |
<div id="scene1.3.133">Leave me alone to woo him. Let's away,</div> |
<div id="scene1.3.134">And get our jewels and our wealth together,</div> |
<div id="scene1.3.135">Devise the fittest time and safest way</div> |
<div id="scene1.3.136">To hide us from pursuit that will be made</div> |
<div id="scene1.3.137">After my flight. Now go we in content</div> |
<div id="scene1.3.138">To liberty and not to banishment.</div> |
<div class="direction">Exeunt</div> |
</div> |
</div> |
</div> |
</div> |
</body> |
</html> |
/vendor/symfony/css-selector/Tests/XPath/TranslatorTest.php |
---|
@@ -0,0 +1,325 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\Tests\XPath; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension; |
use Symfony\Component\CssSelector\XPath\Translator; |
class TranslatorTest extends TestCase |
{ |
/** @dataProvider getXpathLiteralTestData */ |
public function testXpathLiteral($value, $literal) |
{ |
$this->assertEquals($literal, Translator::getXpathLiteral($value)); |
} |
/** @dataProvider getCssToXPathTestData */ |
public function testCssToXPath($css, $xpath) |
{ |
$translator = new Translator(); |
$translator->registerExtension(new HtmlExtension($translator)); |
$this->assertEquals($xpath, $translator->cssToXPath($css, '')); |
} |
/** @dataProvider getXmlLangTestData */ |
public function testXmlLang($css, array $elementsId) |
{ |
$translator = new Translator(); |
$document = new \SimpleXMLElement(file_get_contents(__DIR__.'/Fixtures/lang.xml')); |
$elements = $document->xpath($translator->cssToXPath($css)); |
$this->assertEquals(count($elementsId), count($elements)); |
foreach ($elements as $element) { |
$this->assertTrue(in_array($element->attributes()->id, $elementsId)); |
} |
} |
/** @dataProvider getHtmlIdsTestData */ |
public function testHtmlIds($css, array $elementsId) |
{ |
$translator = new Translator(); |
$translator->registerExtension(new HtmlExtension($translator)); |
$document = new \DOMDocument(); |
$document->strictErrorChecking = false; |
$internalErrors = libxml_use_internal_errors(true); |
$document->loadHTMLFile(__DIR__.'/Fixtures/ids.html'); |
$document = simplexml_import_dom($document); |
$elements = $document->xpath($translator->cssToXPath($css)); |
$this->assertCount(count($elementsId), $elementsId); |
foreach ($elements as $element) { |
if (null !== $element->attributes()->id) { |
$this->assertTrue(in_array($element->attributes()->id, $elementsId)); |
} |
} |
libxml_clear_errors(); |
libxml_use_internal_errors($internalErrors); |
} |
/** @dataProvider getHtmlShakespearTestData */ |
public function testHtmlShakespear($css, $count) |
{ |
$translator = new Translator(); |
$translator->registerExtension(new HtmlExtension($translator)); |
$document = new \DOMDocument(); |
$document->strictErrorChecking = false; |
$document->loadHTMLFile(__DIR__.'/Fixtures/shakespear.html'); |
$document = simplexml_import_dom($document); |
$bodies = $document->xpath('//body'); |
$elements = $bodies[0]->xpath($translator->cssToXPath($css)); |
$this->assertCount($count, $elements); |
} |
public function getXpathLiteralTestData() |
{ |
return array( |
array('foo', "'foo'"), |
array("foo's bar", '"foo\'s bar"'), |
array("foo's \"middle\" bar", 'concat(\'foo\', "\'", \'s "middle" bar\')'), |
array("foo's 'middle' \"bar\"", 'concat(\'foo\', "\'", \'s \', "\'", \'middle\', "\'", \' "bar"\')'), |
); |
} |
public function getCssToXPathTestData() |
{ |
return array( |
array('*', '*'), |
array('e', 'e'), |
array('*|e', 'e'), |
array('e|f', 'e:f'), |
array('e[foo]', 'e[@foo]'), |
array('e[foo|bar]', 'e[@foo:bar]'), |
array('e[foo="bar"]', "e[@foo = 'bar']"), |
array('e[foo~="bar"]', "e[@foo and contains(concat(' ', normalize-space(@foo), ' '), ' bar ')]"), |
array('e[foo^="bar"]', "e[@foo and starts-with(@foo, 'bar')]"), |
array('e[foo$="bar"]', "e[@foo and substring(@foo, string-length(@foo)-2) = 'bar']"), |
array('e[foo*="bar"]', "e[@foo and contains(@foo, 'bar')]"), |
array('e[hreflang|="en"]', "e[@hreflang and (@hreflang = 'en' or starts-with(@hreflang, 'en-'))]"), |
array('e:nth-child(1)', "*/*[name() = 'e' and (position() = 1)]"), |
array('e:nth-last-child(1)', "*/*[name() = 'e' and (position() = last() - 0)]"), |
array('e:nth-last-child(2n+2)', "*/*[name() = 'e' and (last() - position() - 1 >= 0 and (last() - position() - 1) mod 2 = 0)]"), |
array('e:nth-of-type(1)', '*/e[position() = 1]'), |
array('e:nth-last-of-type(1)', '*/e[position() = last() - 0]'), |
array('div e:nth-last-of-type(1) .aclass', "div/descendant-or-self::*/e[position() = last() - 0]/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' aclass ')]"), |
array('e:first-child', "*/*[name() = 'e' and (position() = 1)]"), |
array('e:last-child', "*/*[name() = 'e' and (position() = last())]"), |
array('e:first-of-type', '*/e[position() = 1]'), |
array('e:last-of-type', '*/e[position() = last()]'), |
array('e:only-child', "*/*[name() = 'e' and (last() = 1)]"), |
array('e:only-of-type', 'e[last() = 1]'), |
array('e:empty', 'e[not(*) and not(string-length())]'), |
array('e:EmPTY', 'e[not(*) and not(string-length())]'), |
array('e:root', 'e[not(parent::*)]'), |
array('e:hover', 'e[0]'), |
array('e:contains("foo")', "e[contains(string(.), 'foo')]"), |
array('e:ConTains(foo)', "e[contains(string(.), 'foo')]"), |
array('e.warning', "e[@class and contains(concat(' ', normalize-space(@class), ' '), ' warning ')]"), |
array('e#myid', "e[@id = 'myid']"), |
array('e:not(:nth-child(odd))', 'e[not(position() - 1 >= 0 and (position() - 1) mod 2 = 0)]'), |
array('e:nOT(*)', 'e[0]'), |
array('e f', 'e/descendant-or-self::*/f'), |
array('e > f', 'e/f'), |
array('e + f', "e/following-sibling::*[name() = 'f' and (position() = 1)]"), |
array('e ~ f', 'e/following-sibling::f'), |
array('div#container p', "div[@id = 'container']/descendant-or-self::*/p"), |
); |
} |
public function getXmlLangTestData() |
{ |
return array( |
array(':lang("EN")', array('first', 'second', 'third', 'fourth')), |
array(':lang("en-us")', array('second', 'fourth')), |
array(':lang(en-nz)', array('third')), |
array(':lang(fr)', array('fifth')), |
array(':lang(ru)', array('sixth')), |
array(":lang('ZH')", array('eighth')), |
array(':lang(de) :lang(zh)', array('eighth')), |
array(':lang(en), :lang(zh)', array('first', 'second', 'third', 'fourth', 'eighth')), |
array(':lang(es)', array()), |
); |
} |
public function getHtmlIdsTestData() |
{ |
return array( |
array('div', array('outer-div', 'li-div', 'foobar-div')), |
array('DIV', array('outer-div', 'li-div', 'foobar-div')), // case-insensitive in HTML |
array('div div', array('li-div')), |
array('div, div div', array('outer-div', 'li-div', 'foobar-div')), |
array('a[name]', array('name-anchor')), |
array('a[NAme]', array('name-anchor')), // case-insensitive in HTML: |
array('a[rel]', array('tag-anchor', 'nofollow-anchor')), |
array('a[rel="tag"]', array('tag-anchor')), |
array('a[href*="localhost"]', array('tag-anchor')), |
array('a[href*=""]', array()), |
array('a[href^="http"]', array('tag-anchor', 'nofollow-anchor')), |
array('a[href^="http:"]', array('tag-anchor')), |
array('a[href^=""]', array()), |
array('a[href$="org"]', array('nofollow-anchor')), |
array('a[href$=""]', array()), |
array('div[foobar~="bc"]', array('foobar-div')), |
array('div[foobar~="cde"]', array('foobar-div')), |
array('[foobar~="ab bc"]', array('foobar-div')), |
array('[foobar~=""]', array()), |
array('[foobar~=" \t"]', array()), |
array('div[foobar~="cd"]', array()), |
array('*[lang|="En"]', array('second-li')), |
array('[lang|="En-us"]', array('second-li')), |
// Attribute values are case sensitive |
array('*[lang|="en"]', array()), |
array('[lang|="en-US"]', array()), |
array('*[lang|="e"]', array()), |
// ... :lang() is not. |
array(':lang("EN")', array('second-li', 'li-div')), |
array('*:lang(en-US)', array('second-li', 'li-div')), |
array(':lang("e")', array()), |
array('li:nth-child(3)', array('third-li')), |
array('li:nth-child(10)', array()), |
array('li:nth-child(2n)', array('second-li', 'fourth-li', 'sixth-li')), |
array('li:nth-child(even)', array('second-li', 'fourth-li', 'sixth-li')), |
array('li:nth-child(2n+0)', array('second-li', 'fourth-li', 'sixth-li')), |
array('li:nth-child(+2n+1)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')), |
array('li:nth-child(odd)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')), |
array('li:nth-child(2n+4)', array('fourth-li', 'sixth-li')), |
array('li:nth-child(3n+1)', array('first-li', 'fourth-li', 'seventh-li')), |
array('li:nth-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), |
array('li:nth-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), |
array('li:nth-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), |
array('li:nth-child(n+3)', array('third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), |
array('li:nth-child(-n)', array()), |
array('li:nth-child(-n-1)', array()), |
array('li:nth-child(-n+1)', array('first-li')), |
array('li:nth-child(-n+3)', array('first-li', 'second-li', 'third-li')), |
array('li:nth-last-child(0)', array()), |
array('li:nth-last-child(2n)', array('second-li', 'fourth-li', 'sixth-li')), |
array('li:nth-last-child(even)', array('second-li', 'fourth-li', 'sixth-li')), |
array('li:nth-last-child(2n+2)', array('second-li', 'fourth-li', 'sixth-li')), |
array('li:nth-last-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), |
array('li:nth-last-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), |
array('li:nth-last-child(n-3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), |
array('li:nth-last-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), |
array('li:nth-last-child(n+3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li')), |
array('li:nth-last-child(-n)', array()), |
array('li:nth-last-child(-n-1)', array()), |
array('li:nth-last-child(-n+1)', array('seventh-li')), |
array('li:nth-last-child(-n+3)', array('fifth-li', 'sixth-li', 'seventh-li')), |
array('ol:first-of-type', array('first-ol')), |
array('ol:nth-child(1)', array('first-ol')), |
array('ol:nth-of-type(2)', array('second-ol')), |
array('ol:nth-last-of-type(1)', array('second-ol')), |
array('span:only-child', array('foobar-span')), |
array('li div:only-child', array('li-div')), |
array('div *:only-child', array('li-div', 'foobar-span')), |
array('p:only-of-type', array('paragraph')), |
array('a:empty', array('name-anchor')), |
array('a:EMpty', array('name-anchor')), |
array('li:empty', array('third-li', 'fourth-li', 'fifth-li', 'sixth-li')), |
array(':root', array('html')), |
array('html:root', array('html')), |
array('li:root', array()), |
array('* :root', array()), |
array('*:contains("link")', array('html', 'outer-div', 'tag-anchor', 'nofollow-anchor')), |
array(':CONtains("link")', array('html', 'outer-div', 'tag-anchor', 'nofollow-anchor')), |
array('*:contains("LInk")', array()), // case sensitive |
array('*:contains("e")', array('html', 'nil', 'outer-div', 'first-ol', 'first-li', 'paragraph', 'p-em')), |
array('*:contains("E")', array()), // case-sensitive |
array('.a', array('first-ol')), |
array('.b', array('first-ol')), |
array('*.a', array('first-ol')), |
array('ol.a', array('first-ol')), |
array('.c', array('first-ol', 'third-li', 'fourth-li')), |
array('*.c', array('first-ol', 'third-li', 'fourth-li')), |
array('ol *.c', array('third-li', 'fourth-li')), |
array('ol li.c', array('third-li', 'fourth-li')), |
array('li ~ li.c', array('third-li', 'fourth-li')), |
array('ol > li.c', array('third-li', 'fourth-li')), |
array('#first-li', array('first-li')), |
array('li#first-li', array('first-li')), |
array('*#first-li', array('first-li')), |
array('li div', array('li-div')), |
array('li > div', array('li-div')), |
array('div div', array('li-div')), |
array('div > div', array()), |
array('div>.c', array('first-ol')), |
array('div > .c', array('first-ol')), |
array('div + div', array('foobar-div')), |
array('a ~ a', array('tag-anchor', 'nofollow-anchor')), |
array('a[rel="tag"] ~ a', array('nofollow-anchor')), |
array('ol#first-ol li:last-child', array('seventh-li')), |
array('ol#first-ol *:last-child', array('li-div', 'seventh-li')), |
array('#outer-div:first-child', array('outer-div')), |
array('#outer-div :first-child', array('name-anchor', 'first-li', 'li-div', 'p-b', 'checkbox-fieldset-disabled', 'area-href')), |
array('a[href]', array('tag-anchor', 'nofollow-anchor')), |
array(':not(*)', array()), |
array('a:not([href])', array('name-anchor')), |
array('ol :Not(li[class])', array('first-li', 'second-li', 'li-div', 'fifth-li', 'sixth-li', 'seventh-li')), |
// HTML-specific |
array(':link', array('link-href', 'tag-anchor', 'nofollow-anchor', 'area-href')), |
array(':visited', array()), |
array(':enabled', array('link-href', 'tag-anchor', 'nofollow-anchor', 'checkbox-unchecked', 'text-checked', 'checkbox-checked', 'area-href')), |
array(':disabled', array('checkbox-disabled', 'checkbox-disabled-checked', 'fieldset', 'checkbox-fieldset-disabled')), |
array(':checked', array('checkbox-checked', 'checkbox-disabled-checked')), |
); |
} |
public function getHtmlShakespearTestData() |
{ |
return array( |
array('*', 246), |
array('div:contains(CELIA)', 26), |
array('div:only-child', 22), // ? |
array('div:nth-child(even)', 106), |
array('div:nth-child(2n)', 106), |
array('div:nth-child(odd)', 137), |
array('div:nth-child(2n+1)', 137), |
array('div:nth-child(n)', 243), |
array('div:last-child', 53), |
array('div:first-child', 51), |
array('div > div', 242), |
array('div + div', 190), |
array('div ~ div', 190), |
array('body', 1), |
array('body div', 243), |
array('div', 243), |
array('div div', 242), |
array('div div div', 241), |
array('div, div, div', 243), |
array('div, a, span', 243), |
array('.dialog', 51), |
array('div.dialog', 51), |
array('div .dialog', 51), |
array('div.character, div.dialog', 99), |
array('div.direction.dialog', 0), |
array('div.dialog.direction', 0), |
array('div.dialog.scene', 1), |
array('div.scene.scene', 1), |
array('div.scene .scene', 0), |
array('div.direction .dialog ', 0), |
array('div .dialog .direction', 4), |
array('div.dialog .dialog .direction', 4), |
array('#speech5', 1), |
array('div#speech5', 1), |
array('div #speech5', 1), |
array('div.scene div.dialog', 49), |
array('div#scene1 div.dialog div', 142), |
array('#scene1 #speech1', 1), |
array('div[class]', 103), |
array('div[class=dialog]', 50), |
array('div[class^=dia]', 51), |
array('div[class$=log]', 50), |
array('div[class*=sce]', 1), |
array('div[class|=dialog]', 50), // ? Seems right |
array('div[class!=madeup]', 243), // ? Seems right |
array('div[class~=dialog]', 51), // ? Seems right |
); |
} |
} |
/vendor/symfony/css-selector/XPath/Extension/AbstractExtension.php |
---|
@@ -0,0 +1,65 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath\Extension; |
/** |
* XPath expression translator abstract extension. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
abstract class AbstractExtension implements ExtensionInterface |
{ |
/** |
* {@inheritdoc} |
*/ |
public function getNodeTranslators() |
{ |
return array(); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getCombinationTranslators() |
{ |
return array(); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getFunctionTranslators() |
{ |
return array(); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getPseudoClassTranslators() |
{ |
return array(); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getAttributeMatchingTranslators() |
{ |
return array(); |
} |
} |
/vendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php |
---|
@@ -0,0 +1,175 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath\Extension; |
use Symfony\Component\CssSelector\XPath\Translator; |
use Symfony\Component\CssSelector\XPath\XPathExpr; |
/** |
* XPath expression translator attribute extension. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class AttributeMatchingExtension extends AbstractExtension |
{ |
/** |
* {@inheritdoc} |
*/ |
public function getAttributeMatchingTranslators() |
{ |
return array( |
'exists' => array($this, 'translateExists'), |
'=' => array($this, 'translateEquals'), |
'~=' => array($this, 'translateIncludes'), |
'|=' => array($this, 'translateDashMatch'), |
'^=' => array($this, 'translatePrefixMatch'), |
'$=' => array($this, 'translateSuffixMatch'), |
'*=' => array($this, 'translateSubstringMatch'), |
'!=' => array($this, 'translateDifferent'), |
); |
} |
/** |
* @param XPathExpr $xpath |
* @param string $attribute |
* @param string $value |
* |
* @return XPathExpr |
*/ |
public function translateExists(XPathExpr $xpath, $attribute, $value) |
{ |
return $xpath->addCondition($attribute); |
} |
/** |
* @param XPathExpr $xpath |
* @param string $attribute |
* @param string $value |
* |
* @return XPathExpr |
*/ |
public function translateEquals(XPathExpr $xpath, $attribute, $value) |
{ |
return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value))); |
} |
/** |
* @param XPathExpr $xpath |
* @param string $attribute |
* @param string $value |
* |
* @return XPathExpr |
*/ |
public function translateIncludes(XPathExpr $xpath, $attribute, $value) |
{ |
return $xpath->addCondition($value ? sprintf( |
'%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)', |
$attribute, |
Translator::getXpathLiteral(' '.$value.' ') |
) : '0'); |
} |
/** |
* @param XPathExpr $xpath |
* @param string $attribute |
* @param string $value |
* |
* @return XPathExpr |
*/ |
public function translateDashMatch(XPathExpr $xpath, $attribute, $value) |
{ |
return $xpath->addCondition(sprintf( |
'%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))', |
$attribute, |
Translator::getXpathLiteral($value), |
Translator::getXpathLiteral($value.'-') |
)); |
} |
/** |
* @param XPathExpr $xpath |
* @param string $attribute |
* @param string $value |
* |
* @return XPathExpr |
*/ |
public function translatePrefixMatch(XPathExpr $xpath, $attribute, $value) |
{ |
return $xpath->addCondition($value ? sprintf( |
'%1$s and starts-with(%1$s, %2$s)', |
$attribute, |
Translator::getXpathLiteral($value) |
) : '0'); |
} |
/** |
* @param XPathExpr $xpath |
* @param string $attribute |
* @param string $value |
* |
* @return XPathExpr |
*/ |
public function translateSuffixMatch(XPathExpr $xpath, $attribute, $value) |
{ |
return $xpath->addCondition($value ? sprintf( |
'%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s', |
$attribute, |
strlen($value) - 1, |
Translator::getXpathLiteral($value) |
) : '0'); |
} |
/** |
* @param XPathExpr $xpath |
* @param string $attribute |
* @param string $value |
* |
* @return XPathExpr |
*/ |
public function translateSubstringMatch(XPathExpr $xpath, $attribute, $value) |
{ |
return $xpath->addCondition($value ? sprintf( |
'%1$s and contains(%1$s, %2$s)', |
$attribute, |
Translator::getXpathLiteral($value) |
) : '0'); |
} |
/** |
* @param XPathExpr $xpath |
* @param string $attribute |
* @param string $value |
* |
* @return XPathExpr |
*/ |
public function translateDifferent(XPathExpr $xpath, $attribute, $value) |
{ |
return $xpath->addCondition(sprintf( |
$value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s', |
$attribute, |
Translator::getXpathLiteral($value) |
)); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getName() |
{ |
return 'attribute-matching'; |
} |
} |
/vendor/symfony/css-selector/XPath/Extension/CombinationExtension.php |
---|
@@ -0,0 +1,95 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath\Extension; |
use Symfony\Component\CssSelector\XPath\XPathExpr; |
/** |
* XPath expression translator combination extension. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class CombinationExtension extends AbstractExtension |
{ |
/** |
* {@inheritdoc} |
*/ |
public function getCombinationTranslators() |
{ |
return array( |
' ' => array($this, 'translateDescendant'), |
'>' => array($this, 'translateChild'), |
'+' => array($this, 'translateDirectAdjacent'), |
'~' => array($this, 'translateIndirectAdjacent'), |
); |
} |
/** |
* @param XPathExpr $xpath |
* @param XPathExpr $combinedXpath |
* |
* @return XPathExpr |
*/ |
public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath) |
{ |
return $xpath->join('/descendant-or-self::*/', $combinedXpath); |
} |
/** |
* @param XPathExpr $xpath |
* @param XPathExpr $combinedXpath |
* |
* @return XPathExpr |
*/ |
public function translateChild(XPathExpr $xpath, XPathExpr $combinedXpath) |
{ |
return $xpath->join('/', $combinedXpath); |
} |
/** |
* @param XPathExpr $xpath |
* @param XPathExpr $combinedXpath |
* |
* @return XPathExpr |
*/ |
public function translateDirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath) |
{ |
return $xpath |
->join('/following-sibling::', $combinedXpath) |
->addNameTest() |
->addCondition('position() = 1'); |
} |
/** |
* @param XPathExpr $xpath |
* @param XPathExpr $combinedXpath |
* |
* @return XPathExpr |
*/ |
public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath) |
{ |
return $xpath->join('/following-sibling::', $combinedXpath); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getName() |
{ |
return 'combination'; |
} |
} |
/vendor/symfony/css-selector/XPath/Extension/ExtensionInterface.php |
---|
@@ -0,0 +1,69 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath\Extension; |
/** |
* XPath expression translator extension interface. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
interface ExtensionInterface |
{ |
/** |
* Returns node translators. |
* |
* These callables will receive the node as first argument and the translator as second argument. |
* |
* @return callable[] |
*/ |
public function getNodeTranslators(); |
/** |
* Returns combination translators. |
* |
* @return callable[] |
*/ |
public function getCombinationTranslators(); |
/** |
* Returns function translators. |
* |
* @return callable[] |
*/ |
public function getFunctionTranslators(); |
/** |
* Returns pseudo-class translators. |
* |
* @return callable[] |
*/ |
public function getPseudoClassTranslators(); |
/** |
* Returns attribute operation translators. |
* |
* @return callable[] |
*/ |
public function getAttributeMatchingTranslators(); |
/** |
* Returns extension name. |
* |
* @return string |
*/ |
public function getName(); |
} |
/vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php |
---|
@@ -0,0 +1,211 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath\Extension; |
use Symfony\Component\CssSelector\Exception\ExpressionErrorException; |
use Symfony\Component\CssSelector\Exception\SyntaxErrorException; |
use Symfony\Component\CssSelector\Node\FunctionNode; |
use Symfony\Component\CssSelector\Parser\Parser; |
use Symfony\Component\CssSelector\XPath\Translator; |
use Symfony\Component\CssSelector\XPath\XPathExpr; |
/** |
* XPath expression translator function extension. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class FunctionExtension extends AbstractExtension |
{ |
/** |
* {@inheritdoc} |
*/ |
public function getFunctionTranslators() |
{ |
return array( |
'nth-child' => array($this, 'translateNthChild'), |
'nth-last-child' => array($this, 'translateNthLastChild'), |
'nth-of-type' => array($this, 'translateNthOfType'), |
'nth-last-of-type' => array($this, 'translateNthLastOfType'), |
'contains' => array($this, 'translateContains'), |
'lang' => array($this, 'translateLang'), |
); |
} |
/** |
* @param XPathExpr $xpath |
* @param FunctionNode $function |
* @param bool $last |
* @param bool $addNameTest |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function translateNthChild(XPathExpr $xpath, FunctionNode $function, $last = false, $addNameTest = true) |
{ |
try { |
list($a, $b) = Parser::parseSeries($function->getArguments()); |
} catch (SyntaxErrorException $e) { |
throw new ExpressionErrorException(sprintf('Invalid series: %s', implode(', ', $function->getArguments())), 0, $e); |
} |
$xpath->addStarPrefix(); |
if ($addNameTest) { |
$xpath->addNameTest(); |
} |
if (0 === $a) { |
return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b)); |
} |
if ($a < 0) { |
if ($b < 1) { |
return $xpath->addCondition('false()'); |
} |
$sign = '<='; |
} else { |
$sign = '>='; |
} |
$expr = 'position()'; |
if ($last) { |
$expr = 'last() - '.$expr; |
--$b; |
} |
if (0 !== $b) { |
$expr .= ' - '.$b; |
} |
$conditions = array(sprintf('%s %s 0', $expr, $sign)); |
if (1 !== $a && -1 !== $a) { |
$conditions[] = sprintf('(%s) mod %d = 0', $expr, $a); |
} |
return $xpath->addCondition(implode(' and ', $conditions)); |
// todo: handle an+b, odd, even |
// an+b means every-a, plus b, e.g., 2n+1 means odd |
// 0n+b means b |
// n+0 means a=1, i.e., all elements |
// an means every a elements, i.e., 2n means even |
// -n means -1n |
// -1n+6 means elements 6 and previous |
} |
/** |
* @param XPathExpr $xpath |
* @param FunctionNode $function |
* |
* @return XPathExpr |
*/ |
public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function) |
{ |
return $this->translateNthChild($xpath, $function, true); |
} |
/** |
* @param XPathExpr $xpath |
* @param FunctionNode $function |
* |
* @return XPathExpr |
*/ |
public function translateNthOfType(XPathExpr $xpath, FunctionNode $function) |
{ |
return $this->translateNthChild($xpath, $function, false, false); |
} |
/** |
* @param XPathExpr $xpath |
* @param FunctionNode $function |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function) |
{ |
if ('*' === $xpath->getElement()) { |
throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.'); |
} |
return $this->translateNthChild($xpath, $function, true, false); |
} |
/** |
* @param XPathExpr $xpath |
* @param FunctionNode $function |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function translateContains(XPathExpr $xpath, FunctionNode $function) |
{ |
$arguments = $function->getArguments(); |
foreach ($arguments as $token) { |
if (!($token->isString() || $token->isIdentifier())) { |
throw new ExpressionErrorException( |
'Expected a single string or identifier for :contains(), got ' |
.implode(', ', $arguments) |
); |
} |
} |
return $xpath->addCondition(sprintf( |
'contains(string(.), %s)', |
Translator::getXpathLiteral($arguments[0]->getValue()) |
)); |
} |
/** |
* @param XPathExpr $xpath |
* @param FunctionNode $function |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function translateLang(XPathExpr $xpath, FunctionNode $function) |
{ |
$arguments = $function->getArguments(); |
foreach ($arguments as $token) { |
if (!($token->isString() || $token->isIdentifier())) { |
throw new ExpressionErrorException( |
'Expected a single string or identifier for :lang(), got ' |
.implode(', ', $arguments) |
); |
} |
} |
return $xpath->addCondition(sprintf( |
'lang(%s)', |
Translator::getXpathLiteral($arguments[0]->getValue()) |
)); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getName() |
{ |
return 'function'; |
} |
} |
/vendor/symfony/css-selector/XPath/Extension/HtmlExtension.php |
---|
@@ -0,0 +1,240 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath\Extension; |
use Symfony\Component\CssSelector\Exception\ExpressionErrorException; |
use Symfony\Component\CssSelector\Node\FunctionNode; |
use Symfony\Component\CssSelector\XPath\Translator; |
use Symfony\Component\CssSelector\XPath\XPathExpr; |
/** |
* XPath expression translator HTML extension. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class HtmlExtension extends AbstractExtension |
{ |
/** |
* Constructor. |
* |
* @param Translator $translator |
*/ |
public function __construct(Translator $translator) |
{ |
$translator |
->getExtension('node') |
->setFlag(NodeExtension::ELEMENT_NAME_IN_LOWER_CASE, true) |
->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getPseudoClassTranslators() |
{ |
return array( |
'checked' => array($this, 'translateChecked'), |
'link' => array($this, 'translateLink'), |
'disabled' => array($this, 'translateDisabled'), |
'enabled' => array($this, 'translateEnabled'), |
'selected' => array($this, 'translateSelected'), |
'invalid' => array($this, 'translateInvalid'), |
'hover' => array($this, 'translateHover'), |
'visited' => array($this, 'translateVisited'), |
); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getFunctionTranslators() |
{ |
return array( |
'lang' => array($this, 'translateLang'), |
); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateChecked(XPathExpr $xpath) |
{ |
return $xpath->addCondition( |
'(@checked ' |
."and (name(.) = 'input' or name(.) = 'command')" |
."and (@type = 'checkbox' or @type = 'radio'))" |
); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateLink(XPathExpr $xpath) |
{ |
return $xpath->addCondition("@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')"); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateDisabled(XPathExpr $xpath) |
{ |
return $xpath->addCondition( |
'(' |
.'@disabled and' |
.'(' |
."(name(.) = 'input' and @type != 'hidden')" |
." or name(.) = 'button'" |
." or name(.) = 'select'" |
." or name(.) = 'textarea'" |
." or name(.) = 'command'" |
." or name(.) = 'fieldset'" |
." or name(.) = 'optgroup'" |
." or name(.) = 'option'" |
.')' |
.') or (' |
."(name(.) = 'input' and @type != 'hidden')" |
." or name(.) = 'button'" |
." or name(.) = 'select'" |
." or name(.) = 'textarea'" |
.')' |
.' and ancestor::fieldset[@disabled]' |
); |
// todo: in the second half, add "and is not a descendant of that fieldset element's first legend element child, if any." |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateEnabled(XPathExpr $xpath) |
{ |
return $xpath->addCondition( |
'(' |
.'@href and (' |
."name(.) = 'a'" |
." or name(.) = 'link'" |
." or name(.) = 'area'" |
.')' |
.') or (' |
.'(' |
."name(.) = 'command'" |
." or name(.) = 'fieldset'" |
." or name(.) = 'optgroup'" |
.')' |
.' and not(@disabled)' |
.') or (' |
.'(' |
."(name(.) = 'input' and @type != 'hidden')" |
." or name(.) = 'button'" |
." or name(.) = 'select'" |
." or name(.) = 'textarea'" |
." or name(.) = 'keygen'" |
.')' |
.' and not (@disabled or ancestor::fieldset[@disabled])' |
.') or (' |
."name(.) = 'option' and not(" |
.'@disabled or ancestor::optgroup[@disabled]' |
.')' |
.')' |
); |
} |
/** |
* @param XPathExpr $xpath |
* @param FunctionNode $function |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function translateLang(XPathExpr $xpath, FunctionNode $function) |
{ |
$arguments = $function->getArguments(); |
foreach ($arguments as $token) { |
if (!($token->isString() || $token->isIdentifier())) { |
throw new ExpressionErrorException( |
'Expected a single string or identifier for :lang(), got ' |
.implode(', ', $arguments) |
); |
} |
} |
return $xpath->addCondition(sprintf( |
'ancestor-or-self::*[@lang][1][starts-with(concat(' |
."translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '-')" |
.', %s)]', |
'lang', |
Translator::getXpathLiteral(strtolower($arguments[0]->getValue()).'-') |
)); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateSelected(XPathExpr $xpath) |
{ |
return $xpath->addCondition("(@selected and name(.) = 'option')"); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateInvalid(XPathExpr $xpath) |
{ |
return $xpath->addCondition('0'); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateHover(XPathExpr $xpath) |
{ |
return $xpath->addCondition('0'); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateVisited(XPathExpr $xpath) |
{ |
return $xpath->addCondition('0'); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getName() |
{ |
return 'html'; |
} |
} |
/vendor/symfony/css-selector/XPath/Extension/NodeExtension.php |
---|
@@ -0,0 +1,273 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath\Extension; |
use Symfony\Component\CssSelector\Node; |
use Symfony\Component\CssSelector\XPath\Translator; |
use Symfony\Component\CssSelector\XPath\XPathExpr; |
/** |
* XPath expression translator node extension. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class NodeExtension extends AbstractExtension |
{ |
const ELEMENT_NAME_IN_LOWER_CASE = 1; |
const ATTRIBUTE_NAME_IN_LOWER_CASE = 2; |
const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4; |
/** |
* @var int |
*/ |
private $flags; |
/** |
* Constructor. |
* |
* @param int $flags |
*/ |
public function __construct($flags = 0) |
{ |
$this->flags = $flags; |
} |
/** |
* @param int $flag |
* @param bool $on |
* |
* @return $this |
*/ |
public function setFlag($flag, $on) |
{ |
if ($on && !$this->hasFlag($flag)) { |
$this->flags += $flag; |
} |
if (!$on && $this->hasFlag($flag)) { |
$this->flags -= $flag; |
} |
return $this; |
} |
/** |
* @param int $flag |
* |
* @return bool |
*/ |
public function hasFlag($flag) |
{ |
return $this->flags & $flag; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getNodeTranslators() |
{ |
return array( |
'Selector' => array($this, 'translateSelector'), |
'CombinedSelector' => array($this, 'translateCombinedSelector'), |
'Negation' => array($this, 'translateNegation'), |
'Function' => array($this, 'translateFunction'), |
'Pseudo' => array($this, 'translatePseudo'), |
'Attribute' => array($this, 'translateAttribute'), |
'Class' => array($this, 'translateClass'), |
'Hash' => array($this, 'translateHash'), |
'Element' => array($this, 'translateElement'), |
); |
} |
/** |
* @param Node\SelectorNode $node |
* @param Translator $translator |
* |
* @return XPathExpr |
*/ |
public function translateSelector(Node\SelectorNode $node, Translator $translator) |
{ |
return $translator->nodeToXPath($node->getTree()); |
} |
/** |
* @param Node\CombinedSelectorNode $node |
* @param Translator $translator |
* |
* @return XPathExpr |
*/ |
public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator) |
{ |
return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector()); |
} |
/** |
* @param Node\NegationNode $node |
* @param Translator $translator |
* |
* @return XPathExpr |
*/ |
public function translateNegation(Node\NegationNode $node, Translator $translator) |
{ |
$xpath = $translator->nodeToXPath($node->getSelector()); |
$subXpath = $translator->nodeToXPath($node->getSubSelector()); |
$subXpath->addNameTest(); |
if ($subXpath->getCondition()) { |
return $xpath->addCondition(sprintf('not(%s)', $subXpath->getCondition())); |
} |
return $xpath->addCondition('0'); |
} |
/** |
* @param Node\FunctionNode $node |
* @param Translator $translator |
* |
* @return XPathExpr |
*/ |
public function translateFunction(Node\FunctionNode $node, Translator $translator) |
{ |
$xpath = $translator->nodeToXPath($node->getSelector()); |
return $translator->addFunction($xpath, $node); |
} |
/** |
* @param Node\PseudoNode $node |
* @param Translator $translator |
* |
* @return XPathExpr |
*/ |
public function translatePseudo(Node\PseudoNode $node, Translator $translator) |
{ |
$xpath = $translator->nodeToXPath($node->getSelector()); |
return $translator->addPseudoClass($xpath, $node->getIdentifier()); |
} |
/** |
* @param Node\AttributeNode $node |
* @param Translator $translator |
* |
* @return XPathExpr |
*/ |
public function translateAttribute(Node\AttributeNode $node, Translator $translator) |
{ |
$name = $node->getAttribute(); |
$safe = $this->isSafeName($name); |
if ($this->hasFlag(self::ATTRIBUTE_NAME_IN_LOWER_CASE)) { |
$name = strtolower($name); |
} |
if ($node->getNamespace()) { |
$name = sprintf('%s:%s', $node->getNamespace(), $name); |
$safe = $safe && $this->isSafeName($node->getNamespace()); |
} |
$attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name)); |
$value = $node->getValue(); |
$xpath = $translator->nodeToXPath($node->getSelector()); |
if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) { |
$value = strtolower($value); |
} |
return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value); |
} |
/** |
* @param Node\ClassNode $node |
* @param Translator $translator |
* |
* @return XPathExpr |
*/ |
public function translateClass(Node\ClassNode $node, Translator $translator) |
{ |
$xpath = $translator->nodeToXPath($node->getSelector()); |
return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName()); |
} |
/** |
* @param Node\HashNode $node |
* @param Translator $translator |
* |
* @return XPathExpr |
*/ |
public function translateHash(Node\HashNode $node, Translator $translator) |
{ |
$xpath = $translator->nodeToXPath($node->getSelector()); |
return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId()); |
} |
/** |
* @param Node\ElementNode $node |
* |
* @return XPathExpr |
*/ |
public function translateElement(Node\ElementNode $node) |
{ |
$element = $node->getElement(); |
if ($this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) { |
$element = strtolower($element); |
} |
if ($element) { |
$safe = $this->isSafeName($element); |
} else { |
$element = '*'; |
$safe = true; |
} |
if ($node->getNamespace()) { |
$element = sprintf('%s:%s', $node->getNamespace(), $element); |
$safe = $safe && $this->isSafeName($node->getNamespace()); |
} |
$xpath = new XPathExpr('', $element); |
if (!$safe) { |
$xpath->addNameTest(); |
} |
return $xpath; |
} |
/** |
* {@inheritdoc} |
*/ |
public function getName() |
{ |
return 'node'; |
} |
/** |
* Tests if given name is safe. |
* |
* @param string $name |
* |
* @return bool |
*/ |
private function isSafeName($name) |
{ |
return 0 < preg_match('~^[a-zA-Z_][a-zA-Z0-9_.-]*$~', $name); |
} |
} |
/vendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.php |
---|
@@ -0,0 +1,164 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath\Extension; |
use Symfony\Component\CssSelector\Exception\ExpressionErrorException; |
use Symfony\Component\CssSelector\XPath\XPathExpr; |
/** |
* XPath expression translator pseudo-class extension. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class PseudoClassExtension extends AbstractExtension |
{ |
/** |
* {@inheritdoc} |
*/ |
public function getPseudoClassTranslators() |
{ |
return array( |
'root' => array($this, 'translateRoot'), |
'first-child' => array($this, 'translateFirstChild'), |
'last-child' => array($this, 'translateLastChild'), |
'first-of-type' => array($this, 'translateFirstOfType'), |
'last-of-type' => array($this, 'translateLastOfType'), |
'only-child' => array($this, 'translateOnlyChild'), |
'only-of-type' => array($this, 'translateOnlyOfType'), |
'empty' => array($this, 'translateEmpty'), |
); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateRoot(XPathExpr $xpath) |
{ |
return $xpath->addCondition('not(parent::*)'); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateFirstChild(XPathExpr $xpath) |
{ |
return $xpath |
->addStarPrefix() |
->addNameTest() |
->addCondition('position() = 1'); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateLastChild(XPathExpr $xpath) |
{ |
return $xpath |
->addStarPrefix() |
->addNameTest() |
->addCondition('position() = last()'); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function translateFirstOfType(XPathExpr $xpath) |
{ |
if ('*' === $xpath->getElement()) { |
throw new ExpressionErrorException('"*:first-of-type" is not implemented.'); |
} |
return $xpath |
->addStarPrefix() |
->addCondition('position() = 1'); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function translateLastOfType(XPathExpr $xpath) |
{ |
if ('*' === $xpath->getElement()) { |
throw new ExpressionErrorException('"*:last-of-type" is not implemented.'); |
} |
return $xpath |
->addStarPrefix() |
->addCondition('position() = last()'); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateOnlyChild(XPathExpr $xpath) |
{ |
return $xpath |
->addStarPrefix() |
->addNameTest() |
->addCondition('last() = 1'); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function translateOnlyOfType(XPathExpr $xpath) |
{ |
if ('*' === $xpath->getElement()) { |
throw new ExpressionErrorException('"*:only-of-type" is not implemented.'); |
} |
return $xpath->addCondition('last() = 1'); |
} |
/** |
* @param XPathExpr $xpath |
* |
* @return XPathExpr |
*/ |
public function translateEmpty(XPathExpr $xpath) |
{ |
return $xpath->addCondition('not(*) and not(string-length())'); |
} |
/** |
* {@inheritdoc} |
*/ |
public function getName() |
{ |
return 'pseudo-class'; |
} |
} |
/vendor/symfony/css-selector/XPath/Translator.php |
---|
@@ -0,0 +1,298 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath; |
use Symfony\Component\CssSelector\Exception\ExpressionErrorException; |
use Symfony\Component\CssSelector\Node\FunctionNode; |
use Symfony\Component\CssSelector\Node\NodeInterface; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
use Symfony\Component\CssSelector\Parser\Parser; |
use Symfony\Component\CssSelector\Parser\ParserInterface; |
/** |
* XPath expression translator interface. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class Translator implements TranslatorInterface |
{ |
/** |
* @var ParserInterface |
*/ |
private $mainParser; |
/** |
* @var ParserInterface[] |
*/ |
private $shortcutParsers = array(); |
/** |
* @var Extension\ExtensionInterface |
*/ |
private $extensions = array(); |
/** |
* @var array |
*/ |
private $nodeTranslators = array(); |
/** |
* @var array |
*/ |
private $combinationTranslators = array(); |
/** |
* @var array |
*/ |
private $functionTranslators = array(); |
/** |
* @var array |
*/ |
private $pseudoClassTranslators = array(); |
/** |
* @var array |
*/ |
private $attributeMatchingTranslators = array(); |
public function __construct(ParserInterface $parser = null) |
{ |
$this->mainParser = $parser ?: new Parser(); |
$this |
->registerExtension(new Extension\NodeExtension()) |
->registerExtension(new Extension\CombinationExtension()) |
->registerExtension(new Extension\FunctionExtension()) |
->registerExtension(new Extension\PseudoClassExtension()) |
->registerExtension(new Extension\AttributeMatchingExtension()) |
; |
} |
/** |
* @param string $element |
* |
* @return string |
*/ |
public static function getXpathLiteral($element) |
{ |
if (false === strpos($element, "'")) { |
return "'".$element."'"; |
} |
if (false === strpos($element, '"')) { |
return '"'.$element.'"'; |
} |
$string = $element; |
$parts = array(); |
while (true) { |
if (false !== $pos = strpos($string, "'")) { |
$parts[] = sprintf("'%s'", substr($string, 0, $pos)); |
$parts[] = "\"'\""; |
$string = substr($string, $pos + 1); |
} else { |
$parts[] = "'$string'"; |
break; |
} |
} |
return sprintf('concat(%s)', implode($parts, ', ')); |
} |
/** |
* {@inheritdoc} |
*/ |
public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::') |
{ |
$selectors = $this->parseSelectors($cssExpr); |
/** @var SelectorNode $selector */ |
foreach ($selectors as $index => $selector) { |
if (null !== $selector->getPseudoElement()) { |
throw new ExpressionErrorException('Pseudo-elements are not supported.'); |
} |
$selectors[$index] = $this->selectorToXPath($selector, $prefix); |
} |
return implode(' | ', $selectors); |
} |
/** |
* {@inheritdoc} |
*/ |
public function selectorToXPath(SelectorNode $selector, $prefix = 'descendant-or-self::') |
{ |
return ($prefix ?: '').$this->nodeToXPath($selector); |
} |
/** |
* Registers an extension. |
* |
* @param Extension\ExtensionInterface $extension |
* |
* @return $this |
*/ |
public function registerExtension(Extension\ExtensionInterface $extension) |
{ |
$this->extensions[$extension->getName()] = $extension; |
$this->nodeTranslators = array_merge($this->nodeTranslators, $extension->getNodeTranslators()); |
$this->combinationTranslators = array_merge($this->combinationTranslators, $extension->getCombinationTranslators()); |
$this->functionTranslators = array_merge($this->functionTranslators, $extension->getFunctionTranslators()); |
$this->pseudoClassTranslators = array_merge($this->pseudoClassTranslators, $extension->getPseudoClassTranslators()); |
$this->attributeMatchingTranslators = array_merge($this->attributeMatchingTranslators, $extension->getAttributeMatchingTranslators()); |
return $this; |
} |
/** |
* @param string $name |
* |
* @return Extension\ExtensionInterface |
* |
* @throws ExpressionErrorException |
*/ |
public function getExtension($name) |
{ |
if (!isset($this->extensions[$name])) { |
throw new ExpressionErrorException(sprintf('Extension "%s" not registered.', $name)); |
} |
return $this->extensions[$name]; |
} |
/** |
* Registers a shortcut parser. |
* |
* @param ParserInterface $shortcut |
* |
* @return $this |
*/ |
public function registerParserShortcut(ParserInterface $shortcut) |
{ |
$this->shortcutParsers[] = $shortcut; |
return $this; |
} |
/** |
* @param NodeInterface $node |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function nodeToXPath(NodeInterface $node) |
{ |
if (!isset($this->nodeTranslators[$node->getNodeName()])) { |
throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName())); |
} |
return call_user_func($this->nodeTranslators[$node->getNodeName()], $node, $this); |
} |
/** |
* @param string $combiner |
* @param NodeInterface $xpath |
* @param NodeInterface $combinedXpath |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function addCombination($combiner, NodeInterface $xpath, NodeInterface $combinedXpath) |
{ |
if (!isset($this->combinationTranslators[$combiner])) { |
throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner)); |
} |
return call_user_func($this->combinationTranslators[$combiner], $this->nodeToXPath($xpath), $this->nodeToXPath($combinedXpath)); |
} |
/** |
* @param XPathExpr $xpath |
* @param FunctionNode $function |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function addFunction(XPathExpr $xpath, FunctionNode $function) |
{ |
if (!isset($this->functionTranslators[$function->getName()])) { |
throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName())); |
} |
return call_user_func($this->functionTranslators[$function->getName()], $xpath, $function); |
} |
/** |
* @param XPathExpr $xpath |
* @param string $pseudoClass |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function addPseudoClass(XPathExpr $xpath, $pseudoClass) |
{ |
if (!isset($this->pseudoClassTranslators[$pseudoClass])) { |
throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass)); |
} |
return call_user_func($this->pseudoClassTranslators[$pseudoClass], $xpath); |
} |
/** |
* @param XPathExpr $xpath |
* @param string $operator |
* @param string $attribute |
* @param string $value |
* |
* @return XPathExpr |
* |
* @throws ExpressionErrorException |
*/ |
public function addAttributeMatching(XPathExpr $xpath, $operator, $attribute, $value) |
{ |
if (!isset($this->attributeMatchingTranslators[$operator])) { |
throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator)); |
} |
return call_user_func($this->attributeMatchingTranslators[$operator], $xpath, $attribute, $value); |
} |
/** |
* @param string $css |
* |
* @return SelectorNode[] |
*/ |
private function parseSelectors($css) |
{ |
foreach ($this->shortcutParsers as $shortcut) { |
$tokens = $shortcut->parse($css); |
if (!empty($tokens)) { |
return $tokens; |
} |
} |
return $this->mainParser->parse($css); |
} |
} |
/vendor/symfony/css-selector/XPath/TranslatorInterface.php |
---|
@@ -0,0 +1,47 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath; |
use Symfony\Component\CssSelector\Node\SelectorNode; |
/** |
* XPath expression translator interface. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
interface TranslatorInterface |
{ |
/** |
* Translates a CSS selector to an XPath expression. |
* |
* @param string $cssExpr |
* @param string $prefix |
* |
* @return string |
*/ |
public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::'); |
/** |
* Translates a parsed selector node to an XPath expression. |
* |
* @param SelectorNode $selector |
* @param string $prefix |
* |
* @return string |
*/ |
public function selectorToXPath(SelectorNode $selector, $prefix = 'descendant-or-self::'); |
} |
/vendor/symfony/css-selector/XPath/XPathExpr.php |
---|
@@ -0,0 +1,142 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\CssSelector\XPath; |
/** |
* XPath expression translator interface. |
* |
* This component is a port of the Python cssselect library, |
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. |
* |
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> |
* |
* @internal |
*/ |
class XPathExpr |
{ |
/** |
* @var string |
*/ |
private $path; |
/** |
* @var string |
*/ |
private $element; |
/** |
* @var string |
*/ |
private $condition; |
/** |
* @param string $path |
* @param string $element |
* @param string $condition |
* @param bool $starPrefix |
*/ |
public function __construct($path = '', $element = '*', $condition = '', $starPrefix = false) |
{ |
$this->path = $path; |
$this->element = $element; |
$this->condition = $condition; |
if ($starPrefix) { |
$this->addStarPrefix(); |
} |
} |
/** |
* @return string |
*/ |
public function getElement() |
{ |
return $this->element; |
} |
/** |
* @param $condition |
* |
* @return $this |
*/ |
public function addCondition($condition) |
{ |
$this->condition = $this->condition ? sprintf('%s and (%s)', $this->condition, $condition) : $condition; |
return $this; |
} |
/** |
* @return string |
*/ |
public function getCondition() |
{ |
return $this->condition; |
} |
/** |
* @return $this |
*/ |
public function addNameTest() |
{ |
if ('*' !== $this->element) { |
$this->addCondition('name() = '.Translator::getXpathLiteral($this->element)); |
$this->element = '*'; |
} |
return $this; |
} |
/** |
* @return $this |
*/ |
public function addStarPrefix() |
{ |
$this->path .= '*/'; |
return $this; |
} |
/** |
* Joins another XPathExpr with a combiner. |
* |
* @param string $combiner |
* @param XPathExpr $expr |
* |
* @return $this |
*/ |
public function join($combiner, XPathExpr $expr) |
{ |
$path = $this->__toString().$combiner; |
if ('*/' !== $expr->path) { |
$path .= $expr->path; |
} |
$this->path = $path; |
$this->element = $expr->element; |
$this->condition = $expr->condition; |
return $this; |
} |
/** |
* @return string |
*/ |
public function __toString() |
{ |
$path = $this->path.$this->element; |
$condition = null === $this->condition || '' === $this->condition ? '' : '['.$this->condition.']'; |
return $path.$condition; |
} |
} |
/vendor/symfony/css-selector/composer.json |
---|
@@ -0,0 +1,37 @@ |
{ |
"name": "symfony/css-selector", |
"type": "library", |
"description": "Symfony CssSelector Component", |
"keywords": [], |
"homepage": "https://symfony.com", |
"license": "MIT", |
"authors": [ |
{ |
"name": "Fabien Potencier", |
"email": "fabien@symfony.com" |
}, |
{ |
"name": "Jean-François Simon", |
"email": "jeanfrancois.simon@sensiolabs.com" |
}, |
{ |
"name": "Symfony Community", |
"homepage": "https://symfony.com/contributors" |
} |
], |
"require": { |
"php": ">=5.3.9" |
}, |
"autoload": { |
"psr-4": { "Symfony\\Component\\CssSelector\\": "" }, |
"exclude-from-classmap": [ |
"/Tests/" |
] |
}, |
"minimum-stability": "dev", |
"extra": { |
"branch-alias": { |
"dev-master": "2.8-dev" |
} |
} |
} |
/vendor/symfony/css-selector/phpunit.xml.dist |
---|
@@ -0,0 +1,29 @@ |
<?xml version="1.0" encoding="UTF-8"?> |
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd" |
backupGlobals="false" |
colors="true" |
bootstrap="vendor/autoload.php" |
> |
<php> |
<ini name="error_reporting" value="-1" /> |
</php> |
<testsuites> |
<testsuite name="Symfony CssSelector Component Test Suite"> |
<directory>./Tests/</directory> |
</testsuite> |
</testsuites> |
<filter> |
<whitelist> |
<directory>./</directory> |
<exclude> |
<directory>./Resources</directory> |
<directory>./Tests</directory> |
<directory>./vendor</directory> |
</exclude> |
</whitelist> |
</filter> |
</phpunit> |
/vendor/symfony/dom-crawler/.gitignore |
---|
@@ -0,0 +1,3 @@ |
vendor/ |
composer.lock |
phpunit.xml |
/vendor/symfony/dom-crawler/CHANGELOG.md |
---|
@@ -0,0 +1,45 @@ |
CHANGELOG |
========= |
2.5.0 |
----- |
* [BC BREAK] The default value for checkbox and radio inputs without a value attribute have changed |
from '1' to 'on' to match the HTML specification. |
* [BC BREAK] The typehints on the `Link`, `Form` and `FormField` classes have been changed from |
`\DOMNode` to `DOMElement`. Using any other type of `DOMNode` was triggering fatal errors in previous |
versions. Code extending these classes will need to update the typehints when overwriting these methods. |
2.4.0 |
----- |
* `Crawler::addXmlContent()` removes the default document namespace again if it's an only namespace. |
* added support for automatic discovery and explicit registration of document |
namespaces for `Crawler::filterXPath()` and `Crawler::filter()` |
* improved content type guessing in `Crawler::addContent()` |
* [BC BREAK] `Crawler::addXmlContent()` no longer removes the default document |
namespace |
2.3.0 |
----- |
* added Crawler::html() |
* [BC BREAK] Crawler::each() and Crawler::reduce() now return Crawler instances instead of DomElement instances |
* added schema relative URL support to links |
* added support for HTML5 'form' attribute |
2.2.0 |
----- |
* added a way to set raw path to the file in FileFormField - necessary for |
simulating HTTP requests |
2.1.0 |
----- |
* added support for the HTTP PATCH method |
* refactored the Form class internals to support multi-dimensional fields |
(the public API is backward compatible) |
* added a way to get parsing errors for Crawler::addHtmlContent() and |
Crawler::addXmlContent() via libxml functions |
* added support for submitting a form without a submit button |
/vendor/symfony/dom-crawler/Crawler.php |
---|
@@ -0,0 +1,1209 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler; |
use Symfony\Component\CssSelector\CssSelectorConverter; |
/** |
* Crawler eases navigation of a list of \DOMNode objects. |
* |
* @author Fabien Potencier <fabien@symfony.com> |
*/ |
class Crawler extends \SplObjectStorage |
{ |
/** |
* @var string The current URI |
*/ |
protected $uri; |
/** |
* @var string The default namespace prefix to be used with XPath and CSS expressions |
*/ |
private $defaultNamespacePrefix = 'default'; |
/** |
* @var array A map of manually registered namespaces |
*/ |
private $namespaces = array(); |
/** |
* @var string The base href value |
*/ |
private $baseHref; |
/** |
* @var \DOMDocument|null |
*/ |
private $document; |
/** |
* Whether the Crawler contains HTML or XML content (used when converting CSS to XPath). |
* |
* @var bool |
*/ |
private $isHtml = true; |
/** |
* Constructor. |
* |
* @param mixed $node A Node to use as the base for the crawling |
* @param string $currentUri The current URI |
* @param string $baseHref The base href value |
*/ |
public function __construct($node = null, $currentUri = null, $baseHref = null) |
{ |
$this->uri = $currentUri; |
$this->baseHref = $baseHref ?: $currentUri; |
$this->add($node); |
} |
/** |
* Removes all the nodes. |
*/ |
public function clear() |
{ |
parent::removeAll($this); |
$this->document = null; |
} |
/** |
* Adds a node to the current list of nodes. |
* |
* This method uses the appropriate specialized add*() method based |
* on the type of the argument. |
* |
* @param \DOMNodeList|\DOMNode|array|string|null $node A node |
* |
* @throws \InvalidArgumentException When node is not the expected type. |
*/ |
public function add($node) |
{ |
if ($node instanceof \DOMNodeList) { |
$this->addNodeList($node); |
} elseif ($node instanceof \DOMNode) { |
$this->addNode($node); |
} elseif (is_array($node)) { |
$this->addNodes($node); |
} elseif (is_string($node)) { |
$this->addContent($node); |
} elseif (null !== $node) { |
throw new \InvalidArgumentException(sprintf('Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "%s".', is_object($node) ? get_class($node) : gettype($node))); |
} |
} |
/** |
* Adds HTML/XML content. |
* |
* If the charset is not set via the content type, it is assumed |
* to be ISO-8859-1, which is the default charset defined by the |
* HTTP 1.1 specification. |
* |
* @param string $content A string to parse as HTML/XML |
* @param null|string $type The content type of the string |
*/ |
public function addContent($content, $type = null) |
{ |
if (empty($type)) { |
$type = 0 === strpos($content, '<?xml') ? 'application/xml' : 'text/html'; |
} |
// DOM only for HTML/XML content |
if (!preg_match('/(x|ht)ml/i', $type, $xmlMatches)) { |
return; |
} |
$charset = null; |
if (false !== $pos = stripos($type, 'charset=')) { |
$charset = substr($type, $pos + 8); |
if (false !== $pos = strpos($charset, ';')) { |
$charset = substr($charset, 0, $pos); |
} |
} |
// http://www.w3.org/TR/encoding/#encodings |
// http://www.w3.org/TR/REC-xml/#NT-EncName |
if (null === $charset && |
preg_match('/\<meta[^\>]+charset *= *["\']?([a-zA-Z\-0-9_:.]+)/i', $content, $matches)) { |
$charset = $matches[1]; |
} |
if (null === $charset) { |
$charset = 'ISO-8859-1'; |
} |
if ('x' === $xmlMatches[1]) { |
$this->addXmlContent($content, $charset); |
} else { |
$this->addHtmlContent($content, $charset); |
} |
} |
/** |
* Adds an HTML content to the list of nodes. |
* |
* The libxml errors are disabled when the content is parsed. |
* |
* If you want to get parsing errors, be sure to enable |
* internal errors via libxml_use_internal_errors(true) |
* and then, get the errors via libxml_get_errors(). Be |
* sure to clear errors with libxml_clear_errors() afterward. |
* |
* @param string $content The HTML content |
* @param string $charset The charset |
*/ |
public function addHtmlContent($content, $charset = 'UTF-8') |
{ |
$internalErrors = libxml_use_internal_errors(true); |
$disableEntities = libxml_disable_entity_loader(true); |
$dom = new \DOMDocument('1.0', $charset); |
$dom->validateOnParse = true; |
set_error_handler(function () { throw new \Exception(); }); |
try { |
// Convert charset to HTML-entities to work around bugs in DOMDocument::loadHTML() |
$content = mb_convert_encoding($content, 'HTML-ENTITIES', $charset); |
} catch (\Exception $e) { |
} |
restore_error_handler(); |
if ('' !== trim($content)) { |
@$dom->loadHTML($content); |
} |
libxml_use_internal_errors($internalErrors); |
libxml_disable_entity_loader($disableEntities); |
$this->addDocument($dom); |
$base = $this->filterRelativeXPath('descendant-or-self::base')->extract(array('href')); |
$baseHref = current($base); |
if (count($base) && !empty($baseHref)) { |
if ($this->baseHref) { |
$linkNode = $dom->createElement('a'); |
$linkNode->setAttribute('href', $baseHref); |
$link = new Link($linkNode, $this->baseHref); |
$this->baseHref = $link->getUri(); |
} else { |
$this->baseHref = $baseHref; |
} |
} |
} |
/** |
* Adds an XML content to the list of nodes. |
* |
* The libxml errors are disabled when the content is parsed. |
* |
* If you want to get parsing errors, be sure to enable |
* internal errors via libxml_use_internal_errors(true) |
* and then, get the errors via libxml_get_errors(). Be |
* sure to clear errors with libxml_clear_errors() afterward. |
* |
* @param string $content The XML content |
* @param string $charset The charset |
* @param int $options Bitwise OR of the libxml option constants |
* LIBXML_PARSEHUGE is dangerous, see |
* http://symfony.com/blog/security-release-symfony-2-0-17-released |
*/ |
public function addXmlContent($content, $charset = 'UTF-8', $options = LIBXML_NONET) |
{ |
// remove the default namespace if it's the only namespace to make XPath expressions simpler |
if (!preg_match('/xmlns:/', $content)) { |
$content = str_replace('xmlns', 'ns', $content); |
} |
$internalErrors = libxml_use_internal_errors(true); |
$disableEntities = libxml_disable_entity_loader(true); |
$dom = new \DOMDocument('1.0', $charset); |
$dom->validateOnParse = true; |
if ('' !== trim($content)) { |
@$dom->loadXML($content, $options); |
} |
libxml_use_internal_errors($internalErrors); |
libxml_disable_entity_loader($disableEntities); |
$this->addDocument($dom); |
$this->isHtml = false; |
} |
/** |
* Adds a \DOMDocument to the list of nodes. |
* |
* @param \DOMDocument $dom A \DOMDocument instance |
*/ |
public function addDocument(\DOMDocument $dom) |
{ |
if ($dom->documentElement) { |
$this->addNode($dom->documentElement); |
} |
} |
/** |
* Adds a \DOMNodeList to the list of nodes. |
* |
* @param \DOMNodeList $nodes A \DOMNodeList instance |
*/ |
public function addNodeList(\DOMNodeList $nodes) |
{ |
foreach ($nodes as $node) { |
if ($node instanceof \DOMNode) { |
$this->addNode($node); |
} |
} |
} |
/** |
* Adds an array of \DOMNode instances to the list of nodes. |
* |
* @param \DOMNode[] $nodes An array of \DOMNode instances |
*/ |
public function addNodes(array $nodes) |
{ |
foreach ($nodes as $node) { |
$this->add($node); |
} |
} |
/** |
* Adds a \DOMNode instance to the list of nodes. |
* |
* @param \DOMNode $node A \DOMNode instance |
*/ |
public function addNode(\DOMNode $node) |
{ |
if ($node instanceof \DOMDocument) { |
$node = $node->documentElement; |
} |
if (null !== $this->document && $this->document !== $node->ownerDocument) { |
@trigger_error('Attaching DOM nodes from multiple documents in a Crawler is deprecated as of 2.8 and will be forbidden in 3.0.', E_USER_DEPRECATED); |
} |
if (null === $this->document) { |
$this->document = $node->ownerDocument; |
} |
parent::attach($node); |
} |
// Serializing and unserializing a crawler creates DOM objects in a corrupted state. DOM elements are not properly serializable. |
public function unserialize($serialized) |
{ |
throw new \BadMethodCallException('A Crawler cannot be serialized.'); |
} |
public function serialize() |
{ |
throw new \BadMethodCallException('A Crawler cannot be serialized.'); |
} |
/** |
* Returns a node given its position in the node list. |
* |
* @param int $position The position |
* |
* @return self |
*/ |
public function eq($position) |
{ |
foreach ($this as $i => $node) { |
if ($i == $position) { |
return $this->createSubCrawler($node); |
} |
} |
return $this->createSubCrawler(null); |
} |
/** |
* Calls an anonymous function on each node of the list. |
* |
* The anonymous function receives the position and the node wrapped |
* in a Crawler instance as arguments. |
* |
* Example: |
* |
* $crawler->filter('h1')->each(function ($node, $i) { |
* return $node->text(); |
* }); |
* |
* @param \Closure $closure An anonymous function |
* |
* @return array An array of values returned by the anonymous function |
*/ |
public function each(\Closure $closure) |
{ |
$data = array(); |
foreach ($this as $i => $node) { |
$data[] = $closure($this->createSubCrawler($node), $i); |
} |
return $data; |
} |
/** |
* Slices the list of nodes by $offset and $length. |
* |
* @param int $offset |
* @param int $length |
* |
* @return self |
*/ |
public function slice($offset = 0, $length = -1) |
{ |
return $this->createSubCrawler(iterator_to_array(new \LimitIterator($this, $offset, $length))); |
} |
/** |
* Reduces the list of nodes by calling an anonymous function. |
* |
* To remove a node from the list, the anonymous function must return false. |
* |
* @param \Closure $closure An anonymous function |
* |
* @return self |
*/ |
public function reduce(\Closure $closure) |
{ |
$nodes = array(); |
foreach ($this as $i => $node) { |
if (false !== $closure($this->createSubCrawler($node), $i)) { |
$nodes[] = $node; |
} |
} |
return $this->createSubCrawler($nodes); |
} |
/** |
* Returns the first node of the current selection. |
* |
* @return self |
*/ |
public function first() |
{ |
return $this->eq(0); |
} |
/** |
* Returns the last node of the current selection. |
* |
* @return self |
*/ |
public function last() |
{ |
return $this->eq(count($this) - 1); |
} |
/** |
* Returns the siblings nodes of the current selection. |
* |
* @return self |
* |
* @throws \InvalidArgumentException When current node is empty |
*/ |
public function siblings() |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
return $this->createSubCrawler($this->sibling($this->getNode(0)->parentNode->firstChild)); |
} |
/** |
* Returns the next siblings nodes of the current selection. |
* |
* @return self |
* |
* @throws \InvalidArgumentException When current node is empty |
*/ |
public function nextAll() |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
return $this->createSubCrawler($this->sibling($this->getNode(0))); |
} |
/** |
* Returns the previous sibling nodes of the current selection. |
* |
* @return self |
* |
* @throws \InvalidArgumentException |
*/ |
public function previousAll() |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
return $this->createSubCrawler($this->sibling($this->getNode(0), 'previousSibling')); |
} |
/** |
* Returns the parents nodes of the current selection. |
* |
* @return self |
* |
* @throws \InvalidArgumentException When current node is empty |
*/ |
public function parents() |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
$node = $this->getNode(0); |
$nodes = array(); |
while ($node = $node->parentNode) { |
if (XML_ELEMENT_NODE === $node->nodeType) { |
$nodes[] = $node; |
} |
} |
return $this->createSubCrawler($nodes); |
} |
/** |
* Returns the children nodes of the current selection. |
* |
* @return self |
* |
* @throws \InvalidArgumentException When current node is empty |
*/ |
public function children() |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
$node = $this->getNode(0)->firstChild; |
return $this->createSubCrawler($node ? $this->sibling($node) : array()); |
} |
/** |
* Returns the attribute value of the first node of the list. |
* |
* @param string $attribute The attribute name |
* |
* @return string|null The attribute value or null if the attribute does not exist |
* |
* @throws \InvalidArgumentException When current node is empty |
*/ |
public function attr($attribute) |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
$node = $this->getNode(0); |
return $node->hasAttribute($attribute) ? $node->getAttribute($attribute) : null; |
} |
/** |
* Returns the node name of the first node of the list. |
* |
* @return string The node name |
* |
* @throws \InvalidArgumentException When current node is empty |
*/ |
public function nodeName() |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
return $this->getNode(0)->nodeName; |
} |
/** |
* Returns the node value of the first node of the list. |
* |
* @return string The node value |
* |
* @throws \InvalidArgumentException When current node is empty |
*/ |
public function text() |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
return $this->getNode(0)->nodeValue; |
} |
/** |
* Returns the first node of the list as HTML. |
* |
* @return string The node html |
* |
* @throws \InvalidArgumentException When current node is empty |
*/ |
public function html() |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
$html = ''; |
foreach ($this->getNode(0)->childNodes as $child) { |
$html .= $child->ownerDocument->saveHTML($child); |
} |
return $html; |
} |
/** |
* Extracts information from the list of nodes. |
* |
* You can extract attributes or/and the node value (_text). |
* |
* Example: |
* |
* $crawler->filter('h1 a')->extract(array('_text', 'href')); |
* |
* @param array $attributes An array of attributes |
* |
* @return array An array of extracted values |
*/ |
public function extract($attributes) |
{ |
$attributes = (array) $attributes; |
$count = count($attributes); |
$data = array(); |
foreach ($this as $node) { |
$elements = array(); |
foreach ($attributes as $attribute) { |
if ('_text' === $attribute) { |
$elements[] = $node->nodeValue; |
} else { |
$elements[] = $node->getAttribute($attribute); |
} |
} |
$data[] = $count > 1 ? $elements : $elements[0]; |
} |
return $data; |
} |
/** |
* Filters the list of nodes with an XPath expression. |
* |
* The XPath expression is evaluated in the context of the crawler, which |
* is considered as a fake parent of the elements inside it. |
* This means that a child selector "div" or "./div" will match only |
* the div elements of the current crawler, not their children. |
* |
* @param string $xpath An XPath expression |
* |
* @return self |
*/ |
public function filterXPath($xpath) |
{ |
$xpath = $this->relativize($xpath); |
// If we dropped all expressions in the XPath while preparing it, there would be no match |
if ('' === $xpath) { |
return $this->createSubCrawler(null); |
} |
return $this->filterRelativeXPath($xpath); |
} |
/** |
* Filters the list of nodes with a CSS selector. |
* |
* This method only works if you have installed the CssSelector Symfony Component. |
* |
* @param string $selector A CSS selector |
* |
* @return self |
* |
* @throws \RuntimeException if the CssSelector Component is not available |
*/ |
public function filter($selector) |
{ |
if (!class_exists('Symfony\\Component\\CssSelector\\CssSelectorConverter')) { |
throw new \RuntimeException('Unable to filter with a CSS selector as the Symfony CssSelector 2.8+ is not installed (you can use filterXPath instead).'); |
} |
$converter = new CssSelectorConverter($this->isHtml); |
// The CssSelector already prefixes the selector with descendant-or-self:: |
return $this->filterRelativeXPath($converter->toXPath($selector)); |
} |
/** |
* Selects links by name or alt value for clickable images. |
* |
* @param string $value The link text |
* |
* @return self |
*/ |
public function selectLink($value) |
{ |
$xpath = sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) ', static::xpathLiteral(' '.$value.' ')). |
sprintf('or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)]]', static::xpathLiteral(' '.$value.' ')); |
return $this->filterRelativeXPath($xpath); |
} |
/** |
* Selects a button by name or alt value for images. |
* |
* @param string $value The button text |
* |
* @return self |
*/ |
public function selectButton($value) |
{ |
$translate = 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")'; |
$xpath = sprintf('descendant-or-self::input[((contains(%s, "submit") or contains(%s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', $translate, $translate, static::xpathLiteral(' '.$value.' ')). |
sprintf('or (contains(%s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id=%s or @name=%s] ', $translate, static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)). |
sprintf('| descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id=%s or @name=%s]', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)); |
return $this->filterRelativeXPath($xpath); |
} |
/** |
* Returns a Link object for the first node in the list. |
* |
* @param string $method The method for the link (get by default) |
* |
* @return Link A Link instance |
* |
* @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement |
*/ |
public function link($method = 'get') |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
$node = $this->getNode(0); |
if (!$node instanceof \DOMElement) { |
throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_class($node))); |
} |
return new Link($node, $this->baseHref, $method); |
} |
/** |
* Returns an array of Link objects for the nodes in the list. |
* |
* @return Link[] An array of Link instances |
* |
* @throws \InvalidArgumentException If the current node list contains non-DOMElement instances |
*/ |
public function links() |
{ |
$links = array(); |
foreach ($this as $node) { |
if (!$node instanceof \DOMElement) { |
throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', get_class($node))); |
} |
$links[] = new Link($node, $this->baseHref, 'get'); |
} |
return $links; |
} |
/** |
* Returns a Form object for the first node in the list. |
* |
* @param array $values An array of values for the form fields |
* @param string $method The method for the form |
* |
* @return Form A Form instance |
* |
* @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement |
*/ |
public function form(array $values = null, $method = null) |
{ |
if (!count($this)) { |
throw new \InvalidArgumentException('The current node list is empty.'); |
} |
$node = $this->getNode(0); |
if (!$node instanceof \DOMElement) { |
throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_class($node))); |
} |
$form = new Form($node, $this->uri, $method, $this->baseHref); |
if (null !== $values) { |
$form->setValues($values); |
} |
return $form; |
} |
/** |
* Overloads a default namespace prefix to be used with XPath and CSS expressions. |
* |
* @param string $prefix |
*/ |
public function setDefaultNamespacePrefix($prefix) |
{ |
$this->defaultNamespacePrefix = $prefix; |
} |
/** |
* @param string $prefix |
* @param string $namespace |
*/ |
public function registerNamespace($prefix, $namespace) |
{ |
$this->namespaces[$prefix] = $namespace; |
} |
/** |
* Converts string for XPath expressions. |
* |
* Escaped characters are: quotes (") and apostrophe ('). |
* |
* Examples: |
* <code> |
* echo Crawler::xpathLiteral('foo " bar'); |
* //prints 'foo " bar' |
* |
* echo Crawler::xpathLiteral("foo ' bar"); |
* //prints "foo ' bar" |
* |
* echo Crawler::xpathLiteral('a\'b"c'); |
* //prints concat('a', "'", 'b"c') |
* </code> |
* |
* @param string $s String to be escaped |
* |
* @return string Converted string |
*/ |
public static function xpathLiteral($s) |
{ |
if (false === strpos($s, "'")) { |
return sprintf("'%s'", $s); |
} |
if (false === strpos($s, '"')) { |
return sprintf('"%s"', $s); |
} |
$string = $s; |
$parts = array(); |
while (true) { |
if (false !== $pos = strpos($string, "'")) { |
$parts[] = sprintf("'%s'", substr($string, 0, $pos)); |
$parts[] = "\"'\""; |
$string = substr($string, $pos + 1); |
} else { |
$parts[] = "'$string'"; |
break; |
} |
} |
return sprintf('concat(%s)', implode(', ', $parts)); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function attach($object, $data = null) |
{ |
$this->triggerDeprecation(__METHOD__); |
parent::attach($object, $data); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function detach($object) |
{ |
$this->triggerDeprecation(__METHOD__); |
parent::detach($object); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function contains($object) |
{ |
$this->triggerDeprecation(__METHOD__); |
return parent::contains($object); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function addAll($storage) |
{ |
$this->triggerDeprecation(__METHOD__); |
parent::addAll($storage); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function removeAll($storage) |
{ |
$this->triggerDeprecation(__METHOD__); |
parent::removeAll($storage); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function removeAllExcept($storage) |
{ |
$this->triggerDeprecation(__METHOD__); |
parent::removeAllExcept($storage); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function getInfo() |
{ |
$this->triggerDeprecation(__METHOD__); |
return parent::getInfo(); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function setInfo($data) |
{ |
$this->triggerDeprecation(__METHOD__); |
parent::setInfo($data); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function offsetExists($object) |
{ |
$this->triggerDeprecation(__METHOD__); |
return parent::offsetExists($object); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function offsetSet($object, $data = null) |
{ |
$this->triggerDeprecation(__METHOD__); |
parent::offsetSet($object, $data); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function offsetUnset($object) |
{ |
$this->triggerDeprecation(__METHOD__); |
parent::offsetUnset($object); |
} |
/** |
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0. |
*/ |
public function offsetGet($object) |
{ |
$this->triggerDeprecation(__METHOD__); |
return parent::offsetGet($object); |
} |
/** |
* Filters the list of nodes with an XPath expression. |
* |
* The XPath expression should already be processed to apply it in the context of each node. |
* |
* @param string $xpath |
* |
* @return self |
*/ |
private function filterRelativeXPath($xpath) |
{ |
$prefixes = $this->findNamespacePrefixes($xpath); |
$crawler = $this->createSubCrawler(null); |
foreach ($this as $node) { |
$domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes); |
$crawler->add($domxpath->query($xpath, $node)); |
} |
return $crawler; |
} |
/** |
* Make the XPath relative to the current context. |
* |
* The returned XPath will match elements matching the XPath inside the current crawler |
* when running in the context of a node of the crawler. |
* |
* @param string $xpath |
* |
* @return string |
*/ |
private function relativize($xpath) |
{ |
$expressions = array(); |
// An expression which will never match to replace expressions which cannot match in the crawler |
// We cannot simply drop |
$nonMatchingExpression = 'a[name() = "b"]'; |
$xpathLen = strlen($xpath); |
$openedBrackets = 0; |
$startPosition = strspn($xpath, " \t\n\r\0\x0B"); |
for ($i = $startPosition; $i <= $xpathLen; ++$i) { |
$i += strcspn($xpath, '"\'[]|', $i); |
if ($i < $xpathLen) { |
switch ($xpath[$i]) { |
case '"': |
case "'": |
if (false === $i = strpos($xpath, $xpath[$i], $i + 1)) { |
return $xpath; // The XPath expression is invalid |
} |
continue 2; |
case '[': |
++$openedBrackets; |
continue 2; |
case ']': |
--$openedBrackets; |
continue 2; |
} |
} |
if ($openedBrackets) { |
continue; |
} |
if ($startPosition < $xpathLen && '(' === $xpath[$startPosition]) { |
// If the union is inside some braces, we need to preserve the opening braces and apply |
// the change only inside it. |
$j = 1 + strspn($xpath, "( \t\n\r\0\x0B", $startPosition + 1); |
$parenthesis = substr($xpath, $startPosition, $j); |
$startPosition += $j; |
} else { |
$parenthesis = ''; |
} |
$expression = rtrim(substr($xpath, $startPosition, $i - $startPosition)); |
// BC for Symfony 2.4 and lower were elements were adding in a fake _root parent |
if (0 === strpos($expression, '/_root/')) { |
@trigger_error('XPath expressions referencing the fake root node are deprecated since version 2.8 and will be unsupported in 3.0. Please use "./" instead of "/_root/".', E_USER_DEPRECATED); |
$expression = './'.substr($expression, 7); |
} elseif (0 === strpos($expression, 'self::*/')) { |
$expression = './'.substr($expression, 8); |
} |
// add prefix before absolute element selector |
if ('' === $expression) { |
$expression = $nonMatchingExpression; |
} elseif (0 === strpos($expression, '//')) { |
$expression = 'descendant-or-self::'.substr($expression, 2); |
} elseif (0 === strpos($expression, './/')) { |
$expression = 'descendant-or-self::'.substr($expression, 3); |
} elseif (0 === strpos($expression, './')) { |
$expression = 'self::'.substr($expression, 2); |
} elseif (0 === strpos($expression, 'child::')) { |
$expression = 'self::'.substr($expression, 7); |
} elseif ('/' === $expression[0] || 0 === strpos($expression, 'self::')) { |
// the only direct child in Symfony 2.4 and lower is _root, which is already handled previously |
// so let's drop the expression entirely |
$expression = $nonMatchingExpression; |
} elseif ('.' === $expression[0]) { |
// '.' is the fake root element in Symfony 2.4 and lower, which is excluded from results |
$expression = $nonMatchingExpression; |
} elseif (0 === strpos($expression, 'descendant::')) { |
$expression = 'descendant-or-self::'.substr($expression, 12); |
} elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) { |
// the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes) |
$expression = $nonMatchingExpression; |
} elseif (0 !== strpos($expression, 'descendant-or-self::')) { |
$expression = 'self::'.$expression; |
} |
$expressions[] = $parenthesis.$expression; |
if ($i === $xpathLen) { |
return implode(' | ', $expressions); |
} |
$i += strspn($xpath, " \t\n\r\0\x0B", $i + 1); |
$startPosition = $i + 1; |
} |
return $xpath; // The XPath expression is invalid |
} |
/** |
* @param int $position |
* |
* @return \DOMElement|null |
*/ |
public function getNode($position) |
{ |
foreach ($this as $i => $node) { |
if ($i == $position) { |
return $node; |
} |
} |
} |
/** |
* @param \DOMElement $node |
* @param string $siblingDir |
* |
* @return array |
*/ |
protected function sibling($node, $siblingDir = 'nextSibling') |
{ |
$nodes = array(); |
do { |
if ($node !== $this->getNode(0) && $node->nodeType === 1) { |
$nodes[] = $node; |
} |
} while ($node = $node->$siblingDir); |
return $nodes; |
} |
/** |
* @param \DOMDocument $document |
* @param array $prefixes |
* |
* @return \DOMXPath |
* |
* @throws \InvalidArgumentException |
*/ |
private function createDOMXPath(\DOMDocument $document, array $prefixes = array()) |
{ |
$domxpath = new \DOMXPath($document); |
foreach ($prefixes as $prefix) { |
$namespace = $this->discoverNamespace($domxpath, $prefix); |
if (null !== $namespace) { |
$domxpath->registerNamespace($prefix, $namespace); |
} |
} |
return $domxpath; |
} |
/** |
* @param \DOMXPath $domxpath |
* @param string $prefix |
* |
* @return string |
* |
* @throws \InvalidArgumentException |
*/ |
private function discoverNamespace(\DOMXPath $domxpath, $prefix) |
{ |
if (isset($this->namespaces[$prefix])) { |
return $this->namespaces[$prefix]; |
} |
// ask for one namespace, otherwise we'd get a collection with an item for each node |
$namespaces = $domxpath->query(sprintf('(//namespace::*[name()="%s"])[last()]', $this->defaultNamespacePrefix === $prefix ? '' : $prefix)); |
if ($node = $namespaces->item(0)) { |
return $node->nodeValue; |
} |
} |
/** |
* @param string $xpath |
* |
* @return array |
*/ |
private function findNamespacePrefixes($xpath) |
{ |
if (preg_match_all('/(?P<prefix>[a-z_][a-z_0-9\-\.]*+):[^"\/:]/i', $xpath, $matches)) { |
return array_unique($matches['prefix']); |
} |
return array(); |
} |
/** |
* Creates a crawler for some subnodes. |
* |
* @param \DOMElement|\DOMElement[]|\DOMNodeList|null $nodes |
* |
* @return static |
*/ |
private function createSubCrawler($nodes) |
{ |
$crawler = new static($nodes, $this->uri, $this->baseHref); |
$crawler->isHtml = $this->isHtml; |
$crawler->document = $this->document; |
$crawler->namespaces = $this->namespaces; |
return $crawler; |
} |
private function triggerDeprecation($methodName, $useTrace = false) |
{ |
if ($useTrace || defined('HHVM_VERSION')) { |
if (PHP_VERSION_ID >= 50400) { |
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); |
} else { |
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); |
} |
// The SplObjectStorage class performs calls to its own methods. These |
// method calls must not lead to triggered deprecation notices. |
if (isset($trace[2]['class']) && 'SplObjectStorage' === $trace[2]['class']) { |
return; |
} |
} |
@trigger_error('The '.$methodName.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); |
} |
} |
/vendor/symfony/dom-crawler/Field/ChoiceFormField.php |
---|
@@ -0,0 +1,324 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Field; |
/** |
* ChoiceFormField represents a choice form field. |
* |
* It is constructed from a HTML select tag, or a HTML checkbox, or radio inputs. |
* |
* @author Fabien Potencier <fabien@symfony.com> |
*/ |
class ChoiceFormField extends FormField |
{ |
/** |
* @var string |
*/ |
private $type; |
/** |
* @var bool |
*/ |
private $multiple; |
/** |
* @var array |
*/ |
private $options; |
/** |
* @var bool |
*/ |
private $validationDisabled = false; |
/** |
* Returns true if the field should be included in the submitted values. |
* |
* @return bool true if the field should be included in the submitted values, false otherwise |
*/ |
public function hasValue() |
{ |
// don't send a value for unchecked checkboxes |
if (in_array($this->type, array('checkbox', 'radio')) && null === $this->value) { |
return false; |
} |
return true; |
} |
/** |
* Check if the current selected option is disabled. |
* |
* @return bool |
*/ |
public function isDisabled() |
{ |
if (parent::isDisabled() && 'select' === $this->type) { |
return true; |
} |
foreach ($this->options as $option) { |
if ($option['value'] == $this->value && $option['disabled']) { |
return true; |
} |
} |
return false; |
} |
/** |
* Sets the value of the field. |
* |
* @param string $value The value of the field |
*/ |
public function select($value) |
{ |
$this->setValue($value); |
} |
/** |
* Ticks a checkbox. |
* |
* @throws \LogicException When the type provided is not correct |
*/ |
public function tick() |
{ |
if ('checkbox' !== $this->type) { |
throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); |
} |
$this->setValue(true); |
} |
/** |
* Ticks a checkbox. |
* |
* @throws \LogicException When the type provided is not correct |
*/ |
public function untick() |
{ |
if ('checkbox' !== $this->type) { |
throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); |
} |
$this->setValue(false); |
} |
/** |
* Sets the value of the field. |
* |
* @param string $value The value of the field |
* |
* @throws \InvalidArgumentException When value type provided is not correct |
*/ |
public function setValue($value) |
{ |
if ('checkbox' === $this->type && false === $value) { |
// uncheck |
$this->value = null; |
} elseif ('checkbox' === $this->type && true === $value) { |
// check |
$this->value = $this->options[0]['value']; |
} else { |
if (is_array($value)) { |
if (!$this->multiple) { |
throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name)); |
} |
foreach ($value as $v) { |
if (!$this->containsOption($v, $this->options)) { |
throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $v, implode(', ', $this->availableOptionValues()))); |
} |
} |
} elseif (!$this->containsOption($value, $this->options)) { |
throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $value, implode(', ', $this->availableOptionValues()))); |
} |
if ($this->multiple) { |
$value = (array) $value; |
} |
if (is_array($value)) { |
$this->value = $value; |
} else { |
parent::setValue($value); |
} |
} |
} |
/** |
* Adds a choice to the current ones. |
* |
* @param \DOMElement $node |
* |
* @throws \LogicException When choice provided is not multiple nor radio |
* |
* @internal |
*/ |
public function addChoice(\DOMElement $node) |
{ |
if (!$this->multiple && 'radio' !== $this->type) { |
throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name)); |
} |
$option = $this->buildOptionValue($node); |
$this->options[] = $option; |
if ($node->hasAttribute('checked')) { |
$this->value = $option['value']; |
} |
} |
/** |
* Returns the type of the choice field (radio, select, or checkbox). |
* |
* @return string The type |
*/ |
public function getType() |
{ |
return $this->type; |
} |
/** |
* Returns true if the field accepts multiple values. |
* |
* @return bool true if the field accepts multiple values, false otherwise |
*/ |
public function isMultiple() |
{ |
return $this->multiple; |
} |
/** |
* Initializes the form field. |
* |
* @throws \LogicException When node type is incorrect |
*/ |
protected function initialize() |
{ |
if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) { |
throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName)); |
} |
if ('input' === $this->node->nodeName && 'checkbox' !== strtolower($this->node->getAttribute('type')) && 'radio' !== strtolower($this->node->getAttribute('type'))) { |
throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is %s).', $this->node->getAttribute('type'))); |
} |
$this->value = null; |
$this->options = array(); |
$this->multiple = false; |
if ('input' == $this->node->nodeName) { |
$this->type = strtolower($this->node->getAttribute('type')); |
$optionValue = $this->buildOptionValue($this->node); |
$this->options[] = $optionValue; |
if ($this->node->hasAttribute('checked')) { |
$this->value = $optionValue['value']; |
} |
} else { |
$this->type = 'select'; |
if ($this->node->hasAttribute('multiple')) { |
$this->multiple = true; |
$this->value = array(); |
$this->name = str_replace('[]', '', $this->name); |
} |
$found = false; |
foreach ($this->xpath->query('descendant::option', $this->node) as $option) { |
$optionValue = $this->buildOptionValue($option); |
$this->options[] = $optionValue; |
if ($option->hasAttribute('selected')) { |
$found = true; |
if ($this->multiple) { |
$this->value[] = $optionValue['value']; |
} else { |
$this->value = $optionValue['value']; |
} |
} |
} |
// if no option is selected and if it is a simple select box, take the first option as the value |
if (!$found && !$this->multiple && !empty($this->options)) { |
$this->value = $this->options[0]['value']; |
} |
} |
} |
/** |
* Returns option value with associated disabled flag. |
* |
* @param \DOMElement $node |
* |
* @return array |
*/ |
private function buildOptionValue(\DOMElement $node) |
{ |
$option = array(); |
$defaultDefaultValue = 'select' === $this->node->nodeName ? '' : 'on'; |
$defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : $defaultDefaultValue; |
$option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue; |
$option['disabled'] = $node->hasAttribute('disabled'); |
return $option; |
} |
/** |
* Checks whether given value is in the existing options. |
* |
* @param string $optionValue |
* @param array $options |
* |
* @return bool |
*/ |
public function containsOption($optionValue, $options) |
{ |
if ($this->validationDisabled) { |
return true; |
} |
foreach ($options as $option) { |
if ($option['value'] == $optionValue) { |
return true; |
} |
} |
return false; |
} |
/** |
* Returns list of available field options. |
* |
* @return array |
*/ |
public function availableOptionValues() |
{ |
$values = array(); |
foreach ($this->options as $option) { |
$values[] = $option['value']; |
} |
return $values; |
} |
/** |
* Disables the internal validation of the field. |
* |
* @return self |
*/ |
public function disableValidation() |
{ |
$this->validationDisabled = true; |
return $this; |
} |
} |
/vendor/symfony/dom-crawler/Field/FileFormField.php |
---|
@@ -0,0 +1,108 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Field; |
/** |
* FileFormField represents a file form field (an HTML file input tag). |
* |
* @author Fabien Potencier <fabien@symfony.com> |
*/ |
class FileFormField extends FormField |
{ |
/** |
* Sets the PHP error code associated with the field. |
* |
* @param int $error The error code (one of UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, or UPLOAD_ERR_EXTENSION) |
* |
* @throws \InvalidArgumentException When error code doesn't exist |
*/ |
public function setErrorCode($error) |
{ |
$codes = array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION); |
if (!in_array($error, $codes)) { |
throw new \InvalidArgumentException(sprintf('The error code %s is not valid.', $error)); |
} |
$this->value = array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0); |
} |
/** |
* Sets the value of the field. |
* |
* @param string $value The value of the field |
*/ |
public function upload($value) |
{ |
$this->setValue($value); |
} |
/** |
* Sets the value of the field. |
* |
* @param string $value The value of the field |
*/ |
public function setValue($value) |
{ |
if (null !== $value && is_readable($value)) { |
$error = UPLOAD_ERR_OK; |
$size = filesize($value); |
$info = pathinfo($value); |
$name = $info['basename']; |
// copy to a tmp location |
$tmp = sys_get_temp_dir().'/'.sha1(uniqid(mt_rand(), true)); |
if (array_key_exists('extension', $info)) { |
$tmp .= '.'.$info['extension']; |
} |
if (is_file($tmp)) { |
unlink($tmp); |
} |
copy($value, $tmp); |
$value = $tmp; |
} else { |
$error = UPLOAD_ERR_NO_FILE; |
$size = 0; |
$name = ''; |
$value = ''; |
} |
$this->value = array('name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size); |
} |
/** |
* Sets path to the file as string for simulating HTTP request. |
* |
* @param string $path The path to the file |
*/ |
public function setFilePath($path) |
{ |
parent::setValue($path); |
} |
/** |
* Initializes the form field. |
* |
* @throws \LogicException When node type is incorrect |
*/ |
protected function initialize() |
{ |
if ('input' !== $this->node->nodeName) { |
throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName)); |
} |
if ('file' !== strtolower($this->node->getAttribute('type'))) { |
throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is %s).', $this->node->getAttribute('type'))); |
} |
$this->setValue(null); |
} |
} |
/vendor/symfony/dom-crawler/Field/FormField.php |
---|
@@ -0,0 +1,114 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Field; |
/** |
* FormField is the abstract class for all form fields. |
* |
* @author Fabien Potencier <fabien@symfony.com> |
*/ |
abstract class FormField |
{ |
/** |
* @var \DOMElement |
*/ |
protected $node; |
/** |
* @var string |
*/ |
protected $name; |
/** |
* @var string |
*/ |
protected $value; |
/** |
* @var \DOMDocument |
*/ |
protected $document; |
/** |
* @var \DOMXPath |
*/ |
protected $xpath; |
/** |
* @var bool |
*/ |
protected $disabled; |
/** |
* Constructor. |
* |
* @param \DOMElement $node The node associated with this field |
*/ |
public function __construct(\DOMElement $node) |
{ |
$this->node = $node; |
$this->name = $node->getAttribute('name'); |
$this->xpath = new \DOMXPath($node->ownerDocument); |
$this->initialize(); |
} |
/** |
* Returns the name of the field. |
* |
* @return string The name of the field |
*/ |
public function getName() |
{ |
return $this->name; |
} |
/** |
* Gets the value of the field. |
* |
* @return string|array The value of the field |
*/ |
public function getValue() |
{ |
return $this->value; |
} |
/** |
* Sets the value of the field. |
* |
* @param string $value The value of the field |
*/ |
public function setValue($value) |
{ |
$this->value = (string) $value; |
} |
/** |
* Returns true if the field should be included in the submitted values. |
* |
* @return bool true if the field should be included in the submitted values, false otherwise |
*/ |
public function hasValue() |
{ |
return true; |
} |
/** |
* Check if the current field is disabled. |
* |
* @return bool |
*/ |
public function isDisabled() |
{ |
return $this->node->hasAttribute('disabled'); |
} |
/** |
* Initializes the form field. |
*/ |
abstract protected function initialize(); |
} |
/vendor/symfony/dom-crawler/Field/InputFormField.php |
---|
@@ -0,0 +1,45 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Field; |
/** |
* InputFormField represents an input form field (an HTML input tag). |
* |
* For inputs with type of file, checkbox, or radio, there are other more |
* specialized classes (cf. FileFormField and ChoiceFormField). |
* |
* @author Fabien Potencier <fabien@symfony.com> |
*/ |
class InputFormField extends FormField |
{ |
/** |
* Initializes the form field. |
* |
* @throws \LogicException When node type is incorrect |
*/ |
protected function initialize() |
{ |
if ('input' !== $this->node->nodeName && 'button' !== $this->node->nodeName) { |
throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName)); |
} |
if ('checkbox' === strtolower($this->node->getAttribute('type'))) { |
throw new \LogicException('Checkboxes should be instances of ChoiceFormField.'); |
} |
if ('file' === strtolower($this->node->getAttribute('type'))) { |
throw new \LogicException('File inputs should be instances of FileFormField.'); |
} |
$this->value = $this->node->getAttribute('value'); |
} |
} |
/vendor/symfony/dom-crawler/Field/TextareaFormField.php |
---|
@@ -0,0 +1,37 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Field; |
/** |
* TextareaFormField represents a textarea form field (an HTML textarea tag). |
* |
* @author Fabien Potencier <fabien@symfony.com> |
*/ |
class TextareaFormField extends FormField |
{ |
/** |
* Initializes the form field. |
* |
* @throws \LogicException When node type is incorrect |
*/ |
protected function initialize() |
{ |
if ('textarea' !== $this->node->nodeName) { |
throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName)); |
} |
$this->value = ''; |
foreach ($this->node->childNodes as $node) { |
$this->value .= $node->wholeText; |
} |
} |
} |
/vendor/symfony/dom-crawler/Form.php |
---|
@@ -0,0 +1,473 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler; |
use Symfony\Component\DomCrawler\Field\ChoiceFormField; |
use Symfony\Component\DomCrawler\Field\FormField; |
/** |
* Form represents an HTML form. |
* |
* @author Fabien Potencier <fabien@symfony.com> |
*/ |
class Form extends Link implements \ArrayAccess |
{ |
/** |
* @var \DOMElement |
*/ |
private $button; |
/** |
* @var FormFieldRegistry |
*/ |
private $fields; |
/** |
* @var string |
*/ |
private $baseHref; |
/** |
* Constructor. |
* |
* @param \DOMElement $node A \DOMElement instance |
* @param string $currentUri The URI of the page where the form is embedded |
* @param string $method The method to use for the link (if null, it defaults to the method defined by the form) |
* @param string $baseHref The URI of the <base> used for relative links, but not for empty action |
* |
* @throws \LogicException if the node is not a button inside a form tag |
*/ |
public function __construct(\DOMElement $node, $currentUri, $method = null, $baseHref = null) |
{ |
parent::__construct($node, $currentUri, $method); |
$this->baseHref = $baseHref; |
$this->initialize(); |
} |
/** |
* Gets the form node associated with this form. |
* |
* @return \DOMElement A \DOMElement instance |
*/ |
public function getFormNode() |
{ |
return $this->node; |
} |
/** |
* Sets the value of the fields. |
* |
* @param array $values An array of field values |
* |
* @return $this |
*/ |
public function setValues(array $values) |
{ |
foreach ($values as $name => $value) { |
$this->fields->set($name, $value); |
} |
return $this; |
} |
/** |
* Gets the field values. |
* |
* The returned array does not include file fields (@see getFiles). |
* |
* @return array An array of field values |
*/ |
public function getValues() |
{ |
$values = array(); |
foreach ($this->fields->all() as $name => $field) { |
if ($field->isDisabled()) { |
continue; |
} |
if (!$field instanceof Field\FileFormField && $field->hasValue()) { |
$values[$name] = $field->getValue(); |
} |
} |
return $values; |
} |
/** |
* Gets the file field values. |
* |
* @return array An array of file field values |
*/ |
public function getFiles() |
{ |
if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) { |
return array(); |
} |
$files = array(); |
foreach ($this->fields->all() as $name => $field) { |
if ($field->isDisabled()) { |
continue; |
} |
if ($field instanceof Field\FileFormField) { |
$files[$name] = $field->getValue(); |
} |
} |
return $files; |
} |
/** |
* Gets the field values as PHP. |
* |
* This method converts fields with the array notation |
* (like foo[bar] to arrays) like PHP does. |
* |
* @return array An array of field values |
*/ |
public function getPhpValues() |
{ |
$values = array(); |
foreach ($this->getValues() as $name => $value) { |
$qs = http_build_query(array($name => $value), '', '&'); |
if (!empty($qs)) { |
parse_str($qs, $expandedValue); |
$varName = substr($name, 0, strlen(key($expandedValue))); |
$values = array_replace_recursive($values, array($varName => current($expandedValue))); |
} |
} |
return $values; |
} |
/** |
* Gets the file field values as PHP. |
* |
* This method converts fields with the array notation |
* (like foo[bar] to arrays) like PHP does. |
* The returned array is consistent with the array for field values |
* (@see getPhpValues), rather than uploaded files found in $_FILES. |
* For a compound file field foo[bar] it will create foo[bar][name], |
* instead of foo[name][bar] which would be found in $_FILES. |
* |
* @return array An array of file field values |
*/ |
public function getPhpFiles() |
{ |
$values = array(); |
foreach ($this->getFiles() as $name => $value) { |
$qs = http_build_query(array($name => $value), '', '&'); |
if (!empty($qs)) { |
parse_str($qs, $expandedValue); |
$varName = substr($name, 0, strlen(key($expandedValue))); |
$values = array_replace_recursive($values, array($varName => current($expandedValue))); |
} |
} |
return $values; |
} |
/** |
* Gets the URI of the form. |
* |
* The returned URI is not the same as the form "action" attribute. |
* This method merges the value if the method is GET to mimics |
* browser behavior. |
* |
* @return string The URI |
*/ |
public function getUri() |
{ |
$uri = parent::getUri(); |
if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) { |
$query = parse_url($uri, PHP_URL_QUERY); |
$currentParameters = array(); |
if ($query) { |
parse_str($query, $currentParameters); |
} |
$queryString = http_build_query(array_merge($currentParameters, $this->getValues()), null, '&'); |
$pos = strpos($uri, '?'); |
$base = false === $pos ? $uri : substr($uri, 0, $pos); |
$uri = rtrim($base.'?'.$queryString, '?'); |
} |
return $uri; |
} |
protected function getRawUri() |
{ |
return $this->node->getAttribute('action'); |
} |
/** |
* Gets the form method. |
* |
* If no method is defined in the form, GET is returned. |
* |
* @return string The method |
*/ |
public function getMethod() |
{ |
if (null !== $this->method) { |
return $this->method; |
} |
return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET'; |
} |
/** |
* Returns true if the named field exists. |
* |
* @param string $name The field name |
* |
* @return bool true if the field exists, false otherwise |
*/ |
public function has($name) |
{ |
return $this->fields->has($name); |
} |
/** |
* Removes a field from the form. |
* |
* @param string $name The field name |
*/ |
public function remove($name) |
{ |
$this->fields->remove($name); |
} |
/** |
* Gets a named field. |
* |
* @param string $name The field name |
* |
* @return FormField The field instance |
* |
* @throws \InvalidArgumentException When field is not present in this form |
*/ |
public function get($name) |
{ |
return $this->fields->get($name); |
} |
/** |
* Sets a named field. |
* |
* @param FormField $field The field |
*/ |
public function set(FormField $field) |
{ |
$this->fields->add($field); |
} |
/** |
* Gets all fields. |
* |
* @return FormField[] |
*/ |
public function all() |
{ |
return $this->fields->all(); |
} |
/** |
* Returns true if the named field exists. |
* |
* @param string $name The field name |
* |
* @return bool true if the field exists, false otherwise |
*/ |
public function offsetExists($name) |
{ |
return $this->has($name); |
} |
/** |
* Gets the value of a field. |
* |
* @param string $name The field name |
* |
* @return FormField The associated Field instance |
* |
* @throws \InvalidArgumentException if the field does not exist |
*/ |
public function offsetGet($name) |
{ |
return $this->fields->get($name); |
} |
/** |
* Sets the value of a field. |
* |
* @param string $name The field name |
* @param string|array $value The value of the field |
* |
* @throws \InvalidArgumentException if the field does not exist |
*/ |
public function offsetSet($name, $value) |
{ |
$this->fields->set($name, $value); |
} |
/** |
* Removes a field from the form. |
* |
* @param string $name The field name |
*/ |
public function offsetUnset($name) |
{ |
$this->fields->remove($name); |
} |
/** |
* Disables validation. |
* |
* @return self |
*/ |
public function disableValidation() |
{ |
foreach ($this->fields->all() as $field) { |
if ($field instanceof Field\ChoiceFormField) { |
$field->disableValidation(); |
} |
} |
return $this; |
} |
/** |
* Sets the node for the form. |
* |
* Expects a 'submit' button \DOMElement and finds the corresponding form element, or the form element itself. |
* |
* @param \DOMElement $node A \DOMElement instance |
* |
* @throws \LogicException If given node is not a button or input or does not have a form ancestor |
*/ |
protected function setNode(\DOMElement $node) |
{ |
$this->button = $node; |
if ('button' === $node->nodeName || ('input' === $node->nodeName && in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image')))) { |
if ($node->hasAttribute('form')) { |
// if the node has the HTML5-compliant 'form' attribute, use it |
$formId = $node->getAttribute('form'); |
$form = $node->ownerDocument->getElementById($formId); |
if (null === $form) { |
throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId)); |
} |
$this->node = $form; |
return; |
} |
// we loop until we find a form ancestor |
do { |
if (null === $node = $node->parentNode) { |
throw new \LogicException('The selected node does not have a form ancestor.'); |
} |
} while ('form' !== $node->nodeName); |
} elseif ('form' !== $node->nodeName) { |
throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName)); |
} |
$this->node = $node; |
} |
/** |
* Adds form elements related to this form. |
* |
* Creates an internal copy of the submitted 'button' element and |
* the form node or the entire document depending on whether we need |
* to find non-descendant elements through HTML5 'form' attribute. |
*/ |
private function initialize() |
{ |
$this->fields = new FormFieldRegistry(); |
$xpath = new \DOMXPath($this->node->ownerDocument); |
// add submitted button if it has a valid name |
if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) { |
if ('input' == $this->button->nodeName && 'image' == strtolower($this->button->getAttribute('type'))) { |
$name = $this->button->getAttribute('name'); |
$this->button->setAttribute('value', '0'); |
// temporarily change the name of the input node for the x coordinate |
$this->button->setAttribute('name', $name.'.x'); |
$this->set(new Field\InputFormField($this->button)); |
// temporarily change the name of the input node for the y coordinate |
$this->button->setAttribute('name', $name.'.y'); |
$this->set(new Field\InputFormField($this->button)); |
// restore the original name of the input node |
$this->button->setAttribute('name', $name); |
} else { |
$this->set(new Field\InputFormField($this->button)); |
} |
} |
// find form elements corresponding to the current form |
if ($this->node->hasAttribute('id')) { |
// corresponding elements are either descendants or have a matching HTML5 form attribute |
$formId = Crawler::xpathLiteral($this->node->getAttribute('id')); |
$fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s] | //form[@id=%s]//input[not(@form)] | //form[@id=%s]//button[not(@form)] | //form[@id=%s]//textarea[not(@form)] | //form[@id=%s]//select[not(@form)]', $formId, $formId, $formId, $formId, $formId, $formId, $formId, $formId)); |
foreach ($fieldNodes as $node) { |
$this->addField($node); |
} |
} else { |
// do the xpath query with $this->node as the context node, to only find descendant elements |
// however, descendant elements with form attribute are not part of this form |
$fieldNodes = $xpath->query('descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)]', $this->node); |
foreach ($fieldNodes as $node) { |
$this->addField($node); |
} |
} |
if ($this->baseHref && '' !== $this->node->getAttribute('action')) { |
$this->currentUri = $this->baseHref; |
} |
} |
private function addField(\DOMElement $node) |
{ |
if (!$node->hasAttribute('name') || !$node->getAttribute('name')) { |
return; |
} |
$nodeName = $node->nodeName; |
if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) { |
$this->set(new Field\ChoiceFormField($node)); |
} elseif ('input' == $nodeName && 'radio' == strtolower($node->getAttribute('type'))) { |
// there may be other fields with the same name that are no choice |
// fields already registered (see https://github.com/symfony/symfony/issues/11689) |
if ($this->has($node->getAttribute('name')) && $this->get($node->getAttribute('name')) instanceof ChoiceFormField) { |
$this->get($node->getAttribute('name'))->addChoice($node); |
} else { |
$this->set(new Field\ChoiceFormField($node)); |
} |
} elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) { |
$this->set(new Field\FileFormField($node)); |
} elseif ('input' == $nodeName && !in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image'))) { |
$this->set(new Field\InputFormField($node)); |
} elseif ('textarea' == $nodeName) { |
$this->set(new Field\TextareaFormField($node)); |
} |
} |
} |
/vendor/symfony/dom-crawler/FormFieldRegistry.php |
---|
@@ -0,0 +1,217 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler; |
use Symfony\Component\DomCrawler\Field\FormField; |
/** |
* This is an internal class that must not be used directly. |
* |
* @internal |
*/ |
class FormFieldRegistry |
{ |
private $fields = array(); |
private $base; |
/** |
* Adds a field to the registry. |
* |
* @param FormField $field The field |
*/ |
public function add(FormField $field) |
{ |
$segments = $this->getSegments($field->getName()); |
$target = &$this->fields; |
while ($segments) { |
if (!is_array($target)) { |
$target = array(); |
} |
$path = array_shift($segments); |
if ('' === $path) { |
$target = &$target[]; |
} else { |
$target = &$target[$path]; |
} |
} |
$target = $field; |
} |
/** |
* Removes a field and its children from the registry. |
* |
* @param string $name The fully qualified name of the base field |
*/ |
public function remove($name) |
{ |
$segments = $this->getSegments($name); |
$target = &$this->fields; |
while (count($segments) > 1) { |
$path = array_shift($segments); |
if (!array_key_exists($path, $target)) { |
return; |
} |
$target = &$target[$path]; |
} |
unset($target[array_shift($segments)]); |
} |
/** |
* Returns the value of the field and its children. |
* |
* @param string $name The fully qualified name of the field |
* |
* @return mixed The value of the field |
* |
* @throws \InvalidArgumentException if the field does not exist |
*/ |
public function &get($name) |
{ |
$segments = $this->getSegments($name); |
$target = &$this->fields; |
while ($segments) { |
$path = array_shift($segments); |
if (!array_key_exists($path, $target)) { |
throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path)); |
} |
$target = &$target[$path]; |
} |
return $target; |
} |
/** |
* Tests whether the form has the given field. |
* |
* @param string $name The fully qualified name of the field |
* |
* @return bool Whether the form has the given field |
*/ |
public function has($name) |
{ |
try { |
$this->get($name); |
return true; |
} catch (\InvalidArgumentException $e) { |
return false; |
} |
} |
/** |
* Set the value of a field and its children. |
* |
* @param string $name The fully qualified name of the field |
* @param mixed $value The value |
* |
* @throws \InvalidArgumentException if the field does not exist |
*/ |
public function set($name, $value) |
{ |
$target = &$this->get($name); |
if ((!is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) { |
$target->setValue($value); |
} elseif (is_array($value)) { |
$fields = self::create($name, $value); |
foreach ($fields->all() as $k => $v) { |
$this->set($k, $v); |
} |
} else { |
throw new \InvalidArgumentException(sprintf('Cannot set value on a compound field "%s".', $name)); |
} |
} |
/** |
* Returns the list of field with their value. |
* |
* @return FormField[] The list of fields as array((string) Fully qualified name => (mixed) value) |
*/ |
public function all() |
{ |
return $this->walk($this->fields, $this->base); |
} |
/** |
* Creates an instance of the class. |
* |
* This function is made private because it allows overriding the $base and |
* the $values properties without any type checking. |
* |
* @param string $base The fully qualified name of the base field |
* @param array $values The values of the fields |
* |
* @return static |
*/ |
private static function create($base, array $values) |
{ |
$registry = new static(); |
$registry->base = $base; |
$registry->fields = $values; |
return $registry; |
} |
/** |
* Transforms a PHP array in a list of fully qualified name / value. |
* |
* @param array $array The PHP array |
* @param string $base The name of the base field |
* @param array $output The initial values |
* |
* @return array The list of fields as array((string) Fully qualified name => (mixed) value) |
*/ |
private function walk(array $array, $base = '', array &$output = array()) |
{ |
foreach ($array as $k => $v) { |
$path = empty($base) ? $k : sprintf('%s[%s]', $base, $k); |
if (is_array($v)) { |
$this->walk($v, $path, $output); |
} else { |
$output[$path] = $v; |
} |
} |
return $output; |
} |
/** |
* Splits a field name into segments as a web browser would do. |
* |
* <code> |
* getSegments('base[foo][3][]') = array('base', 'foo, '3', ''); |
* </code> |
* |
* @param string $name The name of the field |
* |
* @return string[] The list of segments |
*/ |
private function getSegments($name) |
{ |
if (preg_match('/^(?P<base>[^[]+)(?P<extra>(\[.*)|$)/', $name, $m)) { |
$segments = array($m['base']); |
while (!empty($m['extra'])) { |
$extra = $m['extra']; |
if (preg_match('/^\[(?P<segment>.*?)\](?P<extra>.*)$/', $extra, $m)) { |
$segments[] = $m['segment']; |
} else { |
$segments[] = $extra; |
} |
} |
return $segments; |
} |
return array($name); |
} |
} |
/vendor/symfony/dom-crawler/LICENSE |
---|
@@ -0,0 +1,19 @@ |
Copyright (c) 2004-2017 Fabien Potencier |
Permission is hereby granted, free of charge, to any person obtaining a copy |
of this software and associated documentation files (the "Software"), to deal |
in the Software without restriction, including without limitation the rights |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
copies of the Software, and to permit persons to whom the Software is furnished |
to do so, subject to the following conditions: |
The above copyright notice and this permission notice shall be included in all |
copies or substantial portions of the Software. |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
THE SOFTWARE. |
/vendor/symfony/dom-crawler/Link.php |
---|
@@ -0,0 +1,224 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler; |
/** |
* Link represents an HTML link (an HTML a, area or link tag). |
* |
* @author Fabien Potencier <fabien@symfony.com> |
*/ |
class Link |
{ |
/** |
* @var \DOMElement |
*/ |
protected $node; |
/** |
* @var string The method to use for the link |
*/ |
protected $method; |
/** |
* @var string The URI of the page where the link is embedded (or the base href) |
*/ |
protected $currentUri; |
/** |
* Constructor. |
* |
* @param \DOMElement $node A \DOMElement instance |
* @param string $currentUri The URI of the page where the link is embedded (or the base href) |
* @param string $method The method to use for the link (get by default) |
* |
* @throws \InvalidArgumentException if the node is not a link |
*/ |
public function __construct(\DOMElement $node, $currentUri, $method = 'GET') |
{ |
if (!in_array(strtolower(substr($currentUri, 0, 4)), array('http', 'file'))) { |
throw new \InvalidArgumentException(sprintf('Current URI must be an absolute URL ("%s").', $currentUri)); |
} |
$this->setNode($node); |
$this->method = $method ? strtoupper($method) : null; |
$this->currentUri = $currentUri; |
} |
/** |
* Gets the node associated with this link. |
* |
* @return \DOMElement A \DOMElement instance |
*/ |
public function getNode() |
{ |
return $this->node; |
} |
/** |
* Gets the method associated with this link. |
* |
* @return string The method |
*/ |
public function getMethod() |
{ |
return $this->method; |
} |
/** |
* Gets the URI associated with this link. |
* |
* @return string The URI |
*/ |
public function getUri() |
{ |
$uri = trim($this->getRawUri()); |
// absolute URL? |
if (null !== parse_url($uri, PHP_URL_SCHEME)) { |
return $uri; |
} |
// empty URI |
if (!$uri) { |
return $this->currentUri; |
} |
// an anchor |
if ('#' === $uri[0]) { |
return $this->cleanupAnchor($this->currentUri).$uri; |
} |
$baseUri = $this->cleanupUri($this->currentUri); |
if ('?' === $uri[0]) { |
return $baseUri.$uri; |
} |
// absolute URL with relative schema |
if (0 === strpos($uri, '//')) { |
return preg_replace('#^([^/]*)//.*$#', '$1', $baseUri).$uri; |
} |
$baseUri = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUri); |
// absolute path |
if ('/' === $uri[0]) { |
return $baseUri.$uri; |
} |
// relative path |
$path = parse_url(substr($this->currentUri, strlen($baseUri)), PHP_URL_PATH); |
$path = $this->canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri); |
return $baseUri.('' === $path || '/' !== $path[0] ? '/' : '').$path; |
} |
/** |
* Returns raw URI data. |
* |
* @return string |
*/ |
protected function getRawUri() |
{ |
return $this->node->getAttribute('href'); |
} |
/** |
* Returns the canonicalized URI path (see RFC 3986, section 5.2.4). |
* |
* @param string $path URI path |
* |
* @return string |
*/ |
protected function canonicalizePath($path) |
{ |
if ('' === $path || '/' === $path) { |
return $path; |
} |
if ('.' === substr($path, -1)) { |
$path .= '/'; |
} |
$output = array(); |
foreach (explode('/', $path) as $segment) { |
if ('..' === $segment) { |
array_pop($output); |
} elseif ('.' !== $segment) { |
$output[] = $segment; |
} |
} |
return implode('/', $output); |
} |
/** |
* Sets current \DOMElement instance. |
* |
* @param \DOMElement $node A \DOMElement instance |
* |
* @throws \LogicException If given node is not an anchor |
*/ |
protected function setNode(\DOMElement $node) |
{ |
if ('a' !== $node->nodeName && 'area' !== $node->nodeName && 'link' !== $node->nodeName) { |
throw new \LogicException(sprintf('Unable to navigate from a "%s" tag.', $node->nodeName)); |
} |
$this->node = $node; |
} |
/** |
* Removes the query string and the anchor from the given uri. |
* |
* @param string $uri The uri to clean |
* |
* @return string |
*/ |
private function cleanupUri($uri) |
{ |
return $this->cleanupQuery($this->cleanupAnchor($uri)); |
} |
/** |
* Remove the query string from the uri. |
* |
* @param string $uri |
* |
* @return string |
*/ |
private function cleanupQuery($uri) |
{ |
if (false !== $pos = strpos($uri, '?')) { |
return substr($uri, 0, $pos); |
} |
return $uri; |
} |
/** |
* Remove the anchor from the uri. |
* |
* @param string $uri |
* |
* @return string |
*/ |
private function cleanupAnchor($uri) |
{ |
if (false !== $pos = strpos($uri, '#')) { |
return substr($uri, 0, $pos); |
} |
return $uri; |
} |
} |
/vendor/symfony/dom-crawler/README.md |
---|
@@ -0,0 +1,13 @@ |
DomCrawler Component |
==================== |
The DomCrawler component eases DOM navigation for HTML and XML documents. |
Resources |
--------- |
* [Documentation](https://symfony.com/doc/current/components/dom_crawler.html) |
* [Contributing](https://symfony.com/doc/current/contributing/index.html) |
* [Report issues](https://github.com/symfony/symfony/issues) and |
[send Pull Requests](https://github.com/symfony/symfony/pulls) |
in the [main Symfony repository](https://github.com/symfony/symfony) |
/vendor/symfony/dom-crawler/Tests/CrawlerTest.php |
---|
@@ -0,0 +1,1109 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Tests; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\DomCrawler\Crawler; |
class CrawlerTest extends TestCase |
{ |
public function testConstructor() |
{ |
$crawler = new Crawler(); |
$this->assertCount(0, $crawler, '__construct() returns an empty crawler'); |
$doc = new \DOMDocument(); |
$node = $doc->createElement('test'); |
$crawler = new Crawler($node); |
$this->assertCount(1, $crawler, '__construct() takes a node as a first argument'); |
} |
public function testAdd() |
{ |
$crawler = new Crawler(); |
$crawler->add($this->createDomDocument()); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMDocument'); |
$crawler = new Crawler(); |
$crawler->add($this->createNodeList()); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNodeList'); |
$list = array(); |
foreach ($this->createNodeList() as $node) { |
$list[] = $node; |
} |
$crawler = new Crawler(); |
$crawler->add($list); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from an array of nodes'); |
$crawler = new Crawler(); |
$crawler->add($this->createNodeList()->item(0)); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNode'); |
$crawler = new Crawler(); |
$crawler->add('<html><body>Foo</body></html>'); |
$this->assertEquals('Foo', $crawler->filterXPath('//body')->text(), '->add() adds nodes from a string'); |
} |
/** |
* @expectedException \InvalidArgumentException |
*/ |
public function testAddInvalidType() |
{ |
$crawler = new Crawler(); |
$crawler->add(1); |
} |
public function testAddHtmlContent() |
{ |
$crawler = new Crawler(); |
$crawler->addHtmlContent('<html><div class="foo"></html>', 'UTF-8'); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addHtmlContent() adds nodes from an HTML string'); |
} |
public function testAddHtmlContentWithBaseTag() |
{ |
$crawler = new Crawler(); |
$crawler->addHtmlContent('<html><head><base href="http://symfony.com"></head><a href="/contact"></a></html>', 'UTF-8'); |
$this->assertEquals('http://symfony.com', $crawler->filterXPath('//base')->attr('href'), '->addHtmlContent() adds nodes from an HTML string'); |
$this->assertEquals('http://symfony.com/contact', $crawler->filterXPath('//a')->link()->getUri(), '->addHtmlContent() adds nodes from an HTML string'); |
} |
/** |
* @requires extension mbstring |
*/ |
public function testAddHtmlContentCharset() |
{ |
$crawler = new Crawler(); |
$crawler->addHtmlContent('<html><div class="foo">Tiếng Việt</html>', 'UTF-8'); |
$this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text()); |
} |
public function testAddHtmlContentInvalidBaseTag() |
{ |
$crawler = new Crawler(null, 'http://symfony.com'); |
$crawler->addHtmlContent('<html><head><base target="_top"></head><a href="/contact"></a></html>', 'UTF-8'); |
$this->assertEquals('http://symfony.com/contact', current($crawler->filterXPath('//a')->links())->getUri(), '->addHtmlContent() correctly handles a non-existent base tag href attribute'); |
} |
public function testAddHtmlContentUnsupportedCharset() |
{ |
$crawler = new Crawler(); |
$crawler->addHtmlContent(file_get_contents(__DIR__.'/Fixtures/windows-1250.html'), 'Windows-1250'); |
$this->assertEquals('Žťčýů', $crawler->filterXPath('//p')->text()); |
} |
/** |
* @requires extension mbstring |
*/ |
public function testAddHtmlContentCharsetGbk() |
{ |
$crawler = new Crawler(); |
//gbk encode of <html><p>中文</p></html> |
$crawler->addHtmlContent(base64_decode('PGh0bWw+PHA+1tDOxDwvcD48L2h0bWw+'), 'gbk'); |
$this->assertEquals('中文', $crawler->filterXPath('//p')->text()); |
} |
public function testAddHtmlContentWithErrors() |
{ |
$internalErrors = libxml_use_internal_errors(true); |
$crawler = new Crawler(); |
$crawler->addHtmlContent(<<<'EOF' |
<!DOCTYPE html> |
<html> |
<head> |
</head> |
<body> |
<nav><a href="#"><a href="#"></nav> |
</body> |
</html> |
EOF |
, 'UTF-8'); |
$errors = libxml_get_errors(); |
$this->assertCount(1, $errors); |
$this->assertEquals("Tag nav invalid\n", $errors[0]->message); |
libxml_clear_errors(); |
libxml_use_internal_errors($internalErrors); |
} |
public function testAddXmlContent() |
{ |
$crawler = new Crawler(); |
$crawler->addXmlContent('<html><div class="foo"></div></html>', 'UTF-8'); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addXmlContent() adds nodes from an XML string'); |
} |
public function testAddXmlContentCharset() |
{ |
$crawler = new Crawler(); |
$crawler->addXmlContent('<html><div class="foo">Tiếng Việt</div></html>', 'UTF-8'); |
$this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text()); |
} |
public function testAddXmlContentWithErrors() |
{ |
$internalErrors = libxml_use_internal_errors(true); |
$crawler = new Crawler(); |
$crawler->addXmlContent(<<<'EOF' |
<!DOCTYPE html> |
<html> |
<head> |
</head> |
<body> |
<nav><a href="#"><a href="#"></nav> |
</body> |
</html> |
EOF |
, 'UTF-8'); |
$this->assertTrue(count(libxml_get_errors()) > 1); |
libxml_clear_errors(); |
libxml_use_internal_errors($internalErrors); |
} |
public function testAddContent() |
{ |
$crawler = new Crawler(); |
$crawler->addContent('<html><div class="foo"></html>', 'text/html; charset=UTF-8'); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string'); |
$crawler = new Crawler(); |
$crawler->addContent('<html><div class="foo"></html>', 'text/html; charset=UTF-8; dir=RTL'); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string with extended content type'); |
$crawler = new Crawler(); |
$crawler->addContent('<html><div class="foo"></html>'); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() uses text/html as the default type'); |
$crawler = new Crawler(); |
$crawler->addContent('<html><div class="foo"></div></html>', 'text/xml; charset=UTF-8'); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string'); |
$crawler = new Crawler(); |
$crawler->addContent('<html><div class="foo"></div></html>', 'text/xml'); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string'); |
$crawler = new Crawler(); |
$crawler->addContent('foo bar', 'text/plain'); |
$this->assertCount(0, $crawler, '->addContent() does nothing if the type is not (x|ht)ml'); |
$crawler = new Crawler(); |
$crawler->addContent('<html><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><span>中文</span></html>'); |
$this->assertEquals('中文', $crawler->filterXPath('//span')->text(), '->addContent() guess wrong charset'); |
} |
/** |
* @requires extension iconv |
*/ |
public function testAddContentNonUtf8() |
{ |
$crawler = new Crawler(); |
$crawler->addContent(iconv('UTF-8', 'SJIS', '<html><head><meta charset="Shift_JIS"></head><body>日本語</body></html>')); |
$this->assertEquals('日本語', $crawler->filterXPath('//body')->text(), '->addContent() can recognize "Shift_JIS" in html5 meta charset tag'); |
} |
public function testAddDocument() |
{ |
$crawler = new Crawler(); |
$crawler->addDocument($this->createDomDocument()); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addDocument() adds nodes from a \DOMDocument'); |
} |
public function testAddNodeList() |
{ |
$crawler = new Crawler(); |
$crawler->addNodeList($this->createNodeList()); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodeList() adds nodes from a \DOMNodeList'); |
} |
public function testAddNodes() |
{ |
$list = array(); |
foreach ($this->createNodeList() as $node) { |
$list[] = $node; |
} |
$crawler = new Crawler(); |
$crawler->addNodes($list); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodes() adds nodes from an array of nodes'); |
} |
public function testAddNode() |
{ |
$crawler = new Crawler(); |
$crawler->addNode($this->createNodeList()->item(0)); |
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNode() adds nodes from a \DOMNode'); |
} |
public function testClear() |
{ |
$doc = new \DOMDocument(); |
$node = $doc->createElement('test'); |
$crawler = new Crawler($node); |
$crawler->clear(); |
$this->assertCount(0, $crawler, '->clear() removes all the nodes from the crawler'); |
} |
public function testEq() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//li'); |
$this->assertNotSame($crawler, $crawler->eq(0), '->eq() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->eq() returns a new instance of a crawler'); |
$this->assertEquals('Two', $crawler->eq(1)->text(), '->eq() returns the nth node of the list'); |
$this->assertCount(0, $crawler->eq(100), '->eq() returns an empty crawler if the nth node does not exist'); |
} |
public function testEach() |
{ |
$data = $this->createTestCrawler()->filterXPath('//ul[1]/li')->each(function ($node, $i) { |
return $i.'-'.$node->text(); |
}); |
$this->assertEquals(array('0-One', '1-Two', '2-Three'), $data, '->each() executes an anonymous function on each node of the list'); |
} |
public function testIteration() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//li'); |
$this->assertInstanceOf('Traversable', $crawler); |
$this->assertContainsOnlyInstancesOf('DOMElement', iterator_to_array($crawler), 'Iterating a Crawler gives DOMElement instances'); |
} |
public function testSlice() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); |
$this->assertNotSame($crawler->slice(), $crawler, '->slice() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler->slice(), '->slice() returns a new instance of a crawler'); |
$this->assertCount(3, $crawler->slice(), '->slice() does not slice the nodes in the list if any param is entered'); |
$this->assertCount(1, $crawler->slice(1, 1), '->slice() slices the nodes in the list'); |
} |
public function testReduce() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); |
$nodes = $crawler->reduce(function ($node, $i) { |
return $i !== 1; |
}); |
$this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler'); |
$this->assertCount(2, $nodes, '->reduce() filters the nodes in the list'); |
} |
public function testAttr() |
{ |
$this->assertEquals('first', $this->createTestCrawler()->filterXPath('//li')->attr('class'), '->attr() returns the attribute of the first element of the node list'); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->attr('class'); |
$this->fail('->attr() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->attr() throws an \InvalidArgumentException if the node list is empty'); |
} |
} |
public function testMissingAttrValueIsNull() |
{ |
$crawler = new Crawler(); |
$crawler->addContent('<html><div non-empty-attr="sample value" empty-attr=""></div></html>', 'text/html; charset=UTF-8'); |
$div = $crawler->filterXPath('//div'); |
$this->assertEquals('sample value', $div->attr('non-empty-attr'), '->attr() reads non-empty attributes correctly'); |
$this->assertEquals('', $div->attr('empty-attr'), '->attr() reads empty attributes correctly'); |
$this->assertNull($div->attr('missing-attr'), '->attr() reads missing attributes correctly'); |
} |
public function testNodeName() |
{ |
$this->assertEquals('li', $this->createTestCrawler()->filterXPath('//li')->nodeName(), '->nodeName() returns the node name of the first element of the node list'); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->nodeName(); |
$this->fail('->nodeName() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->nodeName() throws an \InvalidArgumentException if the node list is empty'); |
} |
} |
public function testText() |
{ |
$this->assertEquals('One', $this->createTestCrawler()->filterXPath('//li')->text(), '->text() returns the node value of the first element of the node list'); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->text(); |
$this->fail('->text() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->text() throws an \InvalidArgumentException if the node list is empty'); |
} |
} |
public function testHtml() |
{ |
$this->assertEquals('<img alt="Bar">', $this->createTestCrawler()->filterXPath('//a[5]')->html()); |
$this->assertEquals('<input type="text" value="TextValue" name="TextName"><input type="submit" value="FooValue" name="FooName" id="FooId"><input type="button" value="BarValue" name="BarName" id="BarId"><button value="ButtonValue" name="ButtonName" id="ButtonId"></button>', trim($this->createTestCrawler()->filterXPath('//form[@id="FooFormId"]')->html())); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->html(); |
$this->fail('->html() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->html() throws an \InvalidArgumentException if the node list is empty'); |
} |
} |
public function testExtract() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); |
$this->assertEquals(array('One', 'Two', 'Three'), $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list'); |
$this->assertEquals(array(array('One', 'first'), array('Two', ''), array('Three', '')), $crawler->extract(array('_text', 'class')), '->extract() returns an array of extracted data from the node list'); |
$this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->extract('_text'), '->extract() returns an empty array if the node list is empty'); |
} |
public function testFilterXpathComplexQueries() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//body'); |
$this->assertCount(0, $crawler->filterXPath('/input')); |
$this->assertCount(0, $crawler->filterXPath('/body')); |
$this->assertCount(1, $crawler->filterXPath('./body')); |
$this->assertCount(1, $crawler->filterXPath('.//body')); |
$this->assertCount(5, $crawler->filterXPath('.//input')); |
$this->assertCount(4, $crawler->filterXPath('//form')->filterXPath('//button | //input')); |
$this->assertCount(1, $crawler->filterXPath('body')); |
$this->assertCount(6, $crawler->filterXPath('//button | //input')); |
$this->assertCount(1, $crawler->filterXPath('//body')); |
$this->assertCount(1, $crawler->filterXPath('descendant-or-self::body')); |
$this->assertCount(1, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('./div'), 'A child selection finds only the current div'); |
$this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('descendant::div'), 'A descendant selector matches the current div and its child'); |
$this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('//div'), 'A descendant selector matches the current div and its child'); |
$this->assertCount(5, $crawler->filterXPath('(//a | //div)//img')); |
$this->assertCount(7, $crawler->filterXPath('((//a | //div)//img | //ul)')); |
$this->assertCount(7, $crawler->filterXPath('( ( //a | //div )//img | //ul )')); |
$this->assertCount(1, $crawler->filterXPath("//a[./@href][((./@id = 'Klausi|Claudiu' or normalize-space(string(.)) = 'Klausi|Claudiu' or ./@title = 'Klausi|Claudiu' or ./@rel = 'Klausi|Claudiu') or .//img[./@alt = 'Klausi|Claudiu'])]")); |
} |
public function testFilterXPath() |
{ |
$crawler = $this->createTestCrawler(); |
$this->assertNotSame($crawler, $crawler->filterXPath('//li'), '->filterXPath() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filterXPath() returns a new instance of a crawler'); |
$crawler = $this->createTestCrawler()->filterXPath('//ul'); |
$this->assertCount(6, $crawler->filterXPath('//li'), '->filterXPath() filters the node list with the XPath expression'); |
$crawler = $this->createTestCrawler(); |
$this->assertCount(3, $crawler->filterXPath('//body')->filterXPath('//button')->parents(), '->filterXpath() preserves parents when chained'); |
} |
public function testFilterRemovesDuplicates() |
{ |
$crawler = $this->createTestCrawler()->filter('html, body')->filter('li'); |
$this->assertCount(6, $crawler, 'The crawler removes duplicates when filtering.'); |
} |
public function testFilterXPathWithDefaultNamespace() |
{ |
$crawler = $this->createTestXmlCrawler()->filterXPath('//default:entry/default:id'); |
$this->assertCount(1, $crawler, '->filterXPath() automatically registers a namespace'); |
$this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); |
} |
public function testFilterXPathWithCustomDefaultNamespace() |
{ |
$crawler = $this->createTestXmlCrawler(); |
$crawler->setDefaultNamespacePrefix('x'); |
$crawler = $crawler->filterXPath('//x:entry/x:id'); |
$this->assertCount(1, $crawler, '->filterXPath() lets to override the default namespace prefix'); |
$this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); |
} |
public function testFilterXPathWithNamespace() |
{ |
$crawler = $this->createTestXmlCrawler()->filterXPath('//yt:accessControl'); |
$this->assertCount(2, $crawler, '->filterXPath() automatically registers a namespace'); |
} |
public function testFilterXPathWithMultipleNamespaces() |
{ |
$crawler = $this->createTestXmlCrawler()->filterXPath('//media:group/yt:aspectRatio'); |
$this->assertCount(1, $crawler, '->filterXPath() automatically registers multiple namespaces'); |
$this->assertSame('widescreen', $crawler->text()); |
} |
public function testFilterXPathWithManuallyRegisteredNamespace() |
{ |
$crawler = $this->createTestXmlCrawler(); |
$crawler->registerNamespace('m', 'http://search.yahoo.com/mrss/'); |
$crawler = $crawler->filterXPath('//m:group/yt:aspectRatio'); |
$this->assertCount(1, $crawler, '->filterXPath() uses manually registered namespace'); |
$this->assertSame('widescreen', $crawler->text()); |
} |
public function testFilterXPathWithAnUrl() |
{ |
$crawler = $this->createTestXmlCrawler(); |
$crawler = $crawler->filterXPath('//media:category[@scheme="http://gdata.youtube.com/schemas/2007/categories.cat"]'); |
$this->assertCount(1, $crawler); |
$this->assertSame('Music', $crawler->text()); |
} |
public function testFilterXPathWithFakeRoot() |
{ |
$crawler = $this->createTestCrawler(); |
$this->assertCount(0, $crawler->filterXPath('.'), '->filterXPath() returns an empty result if the XPath references the fake root node'); |
$this->assertCount(0, $crawler->filterXPath('self::*'), '->filterXPath() returns an empty result if the XPath references the fake root node'); |
$this->assertCount(0, $crawler->filterXPath('self::_root'), '->filterXPath() returns an empty result if the XPath references the fake root node'); |
} |
/** @group legacy */ |
public function testLegacyFilterXPathWithFakeRoot() |
{ |
$crawler = $this->createTestCrawler(); |
$this->assertCount(0, $crawler->filterXPath('/_root'), '->filterXPath() returns an empty result if the XPath references the fake root node'); |
$crawler = $this->createTestCrawler()->filterXPath('//body'); |
$this->assertCount(1, $crawler->filterXPath('/_root/body')); |
} |
public function testFilterXPathWithAncestorAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//form'); |
$this->assertCount(0, $crawler->filterXPath('ancestor::*'), 'The fake root node has no ancestor nodes'); |
} |
public function testFilterXPathWithAncestorOrSelfAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//form'); |
$this->assertCount(0, $crawler->filterXPath('ancestor-or-self::*'), 'The fake root node has no ancestor nodes'); |
} |
public function testFilterXPathWithAttributeAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//form'); |
$this->assertCount(0, $crawler->filterXPath('attribute::*'), 'The fake root node has no attribute nodes'); |
} |
public function testFilterXPathWithAttributeAxisAfterElementAxis() |
{ |
$this->assertCount(3, $this->createTestCrawler()->filterXPath('//form/button/attribute::*'), '->filterXPath() handles attribute axes properly when they are preceded by an element filtering axis'); |
} |
public function testFilterXPathWithChildAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]'); |
$this->assertCount(1, $crawler->filterXPath('child::div'), 'A child selection finds only the current div'); |
} |
public function testFilterXPathWithFollowingAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//a'); |
$this->assertCount(0, $crawler->filterXPath('following::div'), 'The fake root node has no following nodes'); |
} |
public function testFilterXPathWithFollowingSiblingAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//a'); |
$this->assertCount(0, $crawler->filterXPath('following-sibling::div'), 'The fake root node has no following nodes'); |
} |
public function testFilterXPathWithNamespaceAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//button'); |
$this->assertCount(0, $crawler->filterXPath('namespace::*'), 'The fake root node has no namespace nodes'); |
} |
public function testFilterXPathWithNamespaceAxisAfterElementAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]/namespace::*'); |
$this->assertCount(0, $crawler->filterXPath('namespace::*'), 'Namespace axes cannot be requested'); |
} |
public function testFilterXPathWithParentAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//button'); |
$this->assertCount(0, $crawler->filterXPath('parent::*'), 'The fake root node has no parent nodes'); |
} |
public function testFilterXPathWithPrecedingAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//form'); |
$this->assertCount(0, $crawler->filterXPath('preceding::*'), 'The fake root node has no preceding nodes'); |
} |
public function testFilterXPathWithPrecedingSiblingAxis() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//form'); |
$this->assertCount(0, $crawler->filterXPath('preceding-sibling::*'), 'The fake root node has no preceding nodes'); |
} |
public function testFilterXPathWithSelfAxes() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//a'); |
$this->assertCount(0, $crawler->filterXPath('self::a'), 'The fake root node has no "real" element name'); |
$this->assertCount(0, $crawler->filterXPath('self::a/img'), 'The fake root node has no "real" element name'); |
$this->assertCount(10, $crawler->filterXPath('self::*/a')); |
} |
public function testFilter() |
{ |
$crawler = $this->createTestCrawler(); |
$this->assertNotSame($crawler, $crawler->filter('li'), '->filter() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filter() returns a new instance of a crawler'); |
$crawler = $this->createTestCrawler()->filter('ul'); |
$this->assertCount(6, $crawler->filter('li'), '->filter() filters the node list with the CSS selector'); |
} |
public function testFilterWithDefaultNamespace() |
{ |
$crawler = $this->createTestXmlCrawler()->filter('default|entry default|id'); |
$this->assertCount(1, $crawler, '->filter() automatically registers namespaces'); |
$this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); |
} |
public function testFilterWithNamespace() |
{ |
$crawler = $this->createTestXmlCrawler()->filter('yt|accessControl'); |
$this->assertCount(2, $crawler, '->filter() automatically registers namespaces'); |
} |
public function testFilterWithMultipleNamespaces() |
{ |
$crawler = $this->createTestXmlCrawler()->filter('media|group yt|aspectRatio'); |
$this->assertCount(1, $crawler, '->filter() automatically registers namespaces'); |
$this->assertSame('widescreen', $crawler->text()); |
} |
public function testFilterWithDefaultNamespaceOnly() |
{ |
$crawler = new Crawler('<?xml version="1.0" encoding="UTF-8"?> |
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> |
<url> |
<loc>http://localhost/foo</loc> |
<changefreq>weekly</changefreq> |
<priority>0.5</priority> |
<lastmod>2012-11-16</lastmod> |
</url> |
<url> |
<loc>http://localhost/bar</loc> |
<changefreq>weekly</changefreq> |
<priority>0.5</priority> |
<lastmod>2012-11-16</lastmod> |
</url> |
</urlset> |
'); |
$this->assertEquals(2, $crawler->filter('url')->count()); |
} |
public function testSelectLink() |
{ |
$crawler = $this->createTestCrawler(); |
$this->assertNotSame($crawler, $crawler->selectLink('Foo'), '->selectLink() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectLink() returns a new instance of a crawler'); |
$this->assertCount(1, $crawler->selectLink('Fabien\'s Foo'), '->selectLink() selects links by the node values'); |
$this->assertCount(1, $crawler->selectLink('Fabien\'s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); |
$this->assertCount(2, $crawler->selectLink('Fabien"s Foo'), '->selectLink() selects links by the node values'); |
$this->assertCount(2, $crawler->selectLink('Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); |
$this->assertCount(1, $crawler->selectLink('\' Fabien"s Foo'), '->selectLink() selects links by the node values'); |
$this->assertCount(1, $crawler->selectLink('\' Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); |
$this->assertCount(4, $crawler->selectLink('Foo'), '->selectLink() selects links by the node values'); |
$this->assertCount(4, $crawler->selectLink('Bar'), '->selectLink() selects links by the node values'); |
} |
public function testSelectButton() |
{ |
$crawler = $this->createTestCrawler(); |
$this->assertNotSame($crawler, $crawler->selectButton('FooValue'), '->selectButton() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectButton() returns a new instance of a crawler'); |
$this->assertEquals(1, $crawler->selectButton('FooValue')->count(), '->selectButton() selects buttons'); |
$this->assertEquals(1, $crawler->selectButton('FooName')->count(), '->selectButton() selects buttons'); |
$this->assertEquals(1, $crawler->selectButton('FooId')->count(), '->selectButton() selects buttons'); |
$this->assertEquals(1, $crawler->selectButton('BarValue')->count(), '->selectButton() selects buttons'); |
$this->assertEquals(1, $crawler->selectButton('BarName')->count(), '->selectButton() selects buttons'); |
$this->assertEquals(1, $crawler->selectButton('BarId')->count(), '->selectButton() selects buttons'); |
$this->assertEquals(1, $crawler->selectButton('FooBarValue')->count(), '->selectButton() selects buttons with form attribute too'); |
$this->assertEquals(1, $crawler->selectButton('FooBarName')->count(), '->selectButton() selects buttons with form attribute too'); |
} |
public function testSelectButtonWithSingleQuotesInNameAttribute() |
{ |
$html = <<<'HTML' |
<!DOCTYPE html> |
<html lang="en"> |
<body> |
<div id="action"> |
<a href="/index.php?r=site/login">Login</a> |
</div> |
<form id="login-form" action="/index.php?r=site/login" method="post"> |
<button type="submit" name="Click 'Here'">Submit</button> |
</form> |
</body> |
</html> |
HTML; |
$crawler = new Crawler($html); |
$this->assertCount(1, $crawler->selectButton('Click \'Here\'')); |
} |
public function testSelectButtonWithDoubleQuotesInNameAttribute() |
{ |
$html = <<<'HTML' |
<!DOCTYPE html> |
<html lang="en"> |
<body> |
<div id="action"> |
<a href="/index.php?r=site/login">Login</a> |
</div> |
<form id="login-form" action="/index.php?r=site/login" method="post"> |
<button type="submit" name='Click "Here"'>Submit</button> |
</form> |
</body> |
</html> |
HTML; |
$crawler = new Crawler($html); |
$this->assertCount(1, $crawler->selectButton('Click "Here"')); |
} |
public function testLink() |
{ |
$crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $crawler->link(), '->link() returns a Link instance'); |
$this->assertEquals('POST', $crawler->link('post')->getMethod(), '->link() takes a method as its argument'); |
$crawler = $this->createTestCrawler('http://example.com/bar')->selectLink('GetLink'); |
$this->assertEquals('http://example.com/bar?get=param', $crawler->link()->getUri(), '->link() returns a Link instance'); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->link(); |
$this->fail('->link() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->link() throws an \InvalidArgumentException if the node list is empty'); |
} |
} |
/** |
* @expectedException \InvalidArgumentException |
* @expectedExceptionMessage The selected node should be instance of DOMElement |
*/ |
public function testInvalidLink() |
{ |
$crawler = $this->createTestCrawler('http://example.com/bar/'); |
$crawler->filterXPath('//li/text()')->link(); |
} |
/** |
* @expectedException \InvalidArgumentException |
* @expectedExceptionMessage The selected node should be instance of DOMElement |
*/ |
public function testInvalidLinks() |
{ |
$crawler = $this->createTestCrawler('http://example.com/bar/'); |
$crawler->filterXPath('//li/text()')->link(); |
} |
public function testSelectLinkAndLinkFiltered() |
{ |
$html = <<<'HTML' |
<!DOCTYPE html> |
<html lang="en"> |
<body> |
<div id="action"> |
<a href="/index.php?r=site/login">Login</a> |
</div> |
<form id="login-form" action="/index.php?r=site/login" method="post"> |
<button type="submit">Submit</button> |
</form> |
</body> |
</html> |
HTML; |
$crawler = new Crawler($html); |
$filtered = $crawler->filterXPath("descendant-or-self::*[@id = 'login-form']"); |
$this->assertCount(0, $filtered->selectLink('Login')); |
$this->assertCount(1, $filtered->selectButton('Submit')); |
$filtered = $crawler->filterXPath("descendant-or-self::*[@id = 'action']"); |
$this->assertCount(1, $filtered->selectLink('Login')); |
$this->assertCount(0, $filtered->selectButton('Submit')); |
$this->assertCount(1, $crawler->selectLink('Login')->selectLink('Login')); |
$this->assertCount(1, $crawler->selectButton('Submit')->selectButton('Submit')); |
} |
public function testChaining() |
{ |
$crawler = new Crawler('<div name="a"><div name="b"><div name="c"></div></div></div>'); |
$this->assertEquals('a', $crawler->filterXPath('//div')->filterXPath('div')->filterXPath('div')->attr('name')); |
} |
public function testLinks() |
{ |
$crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo'); |
$this->assertInternalType('array', $crawler->links(), '->links() returns an array'); |
$this->assertCount(4, $crawler->links(), '->links() returns an array'); |
$links = $crawler->links(); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $links[0], '->links() returns an array of Link instances'); |
$this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->links(), '->links() returns an empty array if the node selection is empty'); |
} |
public function testForm() |
{ |
$testCrawler = $this->createTestCrawler('http://example.com/bar/'); |
$crawler = $testCrawler->selectButton('FooValue'); |
$crawler2 = $testCrawler->selectButton('FooBarValue'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler->form(), '->form() returns a Form instance'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler2->form(), '->form() returns a Form instance'); |
$this->assertEquals($crawler->form()->getFormNode()->getAttribute('id'), $crawler2->form()->getFormNode()->getAttribute('id'), '->form() works on elements with form attribute'); |
$this->assertEquals(array('FooName' => 'FooBar', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form(array('FooName' => 'FooBar'))->getValues(), '->form() takes an array of values to submit as its first argument'); |
$this->assertEquals(array('FooName' => 'FooValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form()->getValues(), '->getValues() returns correct form values'); |
$this->assertEquals(array('FooBarName' => 'FooBarValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler2->form()->getValues(), '->getValues() returns correct form values'); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->form(); |
$this->fail('->form() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->form() throws an \InvalidArgumentException if the node list is empty'); |
} |
} |
/** |
* @expectedException \InvalidArgumentException |
* @expectedExceptionMessage The selected node should be instance of DOMElement |
*/ |
public function testInvalidForm() |
{ |
$crawler = $this->createTestCrawler('http://example.com/bar/'); |
$crawler->filterXPath('//li/text()')->form(); |
} |
public function testLast() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); |
$this->assertNotSame($crawler, $crawler->last(), '->last() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->last() returns a new instance of a crawler'); |
$this->assertEquals('Three', $crawler->last()->text()); |
} |
public function testFirst() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//li'); |
$this->assertNotSame($crawler, $crawler->first(), '->first() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->first() returns a new instance of a crawler'); |
$this->assertEquals('One', $crawler->first()->text()); |
} |
public function testSiblings() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1); |
$this->assertNotSame($crawler, $crawler->siblings(), '->siblings() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->siblings() returns a new instance of a crawler'); |
$nodes = $crawler->siblings(); |
$this->assertEquals(2, $nodes->count()); |
$this->assertEquals('One', $nodes->eq(0)->text()); |
$this->assertEquals('Three', $nodes->eq(1)->text()); |
$nodes = $this->createTestCrawler()->filterXPath('//li')->eq(0)->siblings(); |
$this->assertEquals(2, $nodes->count()); |
$this->assertEquals('Two', $nodes->eq(0)->text()); |
$this->assertEquals('Three', $nodes->eq(1)->text()); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->siblings(); |
$this->fail('->siblings() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->siblings() throws an \InvalidArgumentException if the node list is empty'); |
} |
} |
public function testNextAll() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1); |
$this->assertNotSame($crawler, $crawler->nextAll(), '->nextAll() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->nextAll() returns a new instance of a crawler'); |
$nodes = $crawler->nextAll(); |
$this->assertEquals(1, $nodes->count()); |
$this->assertEquals('Three', $nodes->eq(0)->text()); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->nextAll(); |
$this->fail('->nextAll() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->nextAll() throws an \InvalidArgumentException if the node list is empty'); |
} |
} |
public function testPreviousAll() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//li')->eq(2); |
$this->assertNotSame($crawler, $crawler->previousAll(), '->previousAll() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->previousAll() returns a new instance of a crawler'); |
$nodes = $crawler->previousAll(); |
$this->assertEquals(2, $nodes->count()); |
$this->assertEquals('Two', $nodes->eq(0)->text()); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->previousAll(); |
$this->fail('->previousAll() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->previousAll() throws an \InvalidArgumentException if the node list is empty'); |
} |
} |
public function testChildren() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//ul'); |
$this->assertNotSame($crawler, $crawler->children(), '->children() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->children() returns a new instance of a crawler'); |
$nodes = $crawler->children(); |
$this->assertEquals(3, $nodes->count()); |
$this->assertEquals('One', $nodes->eq(0)->text()); |
$this->assertEquals('Two', $nodes->eq(1)->text()); |
$this->assertEquals('Three', $nodes->eq(2)->text()); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->children(); |
$this->fail('->children() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->children() throws an \InvalidArgumentException if the node list is empty'); |
} |
try { |
$crawler = new Crawler('<p></p>'); |
$crawler->filter('p')->children(); |
$this->assertTrue(true, '->children() does not trigger a notice if the node has no children'); |
} catch (\PHPUnit\Framework\Error\Notice $e) { |
$this->fail('->children() does not trigger a notice if the node has no children'); |
} catch (\PHPUnit_Framework_Error_Notice $e) { |
$this->fail('->children() does not trigger a notice if the node has no children'); |
} |
} |
public function testParents() |
{ |
$crawler = $this->createTestCrawler()->filterXPath('//li[1]'); |
$this->assertNotSame($crawler, $crawler->parents(), '->parents() returns a new instance of a crawler'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->parents() returns a new instance of a crawler'); |
$nodes = $crawler->parents(); |
$this->assertEquals(3, $nodes->count()); |
$nodes = $this->createTestCrawler()->filterXPath('//html')->parents(); |
$this->assertEquals(0, $nodes->count()); |
try { |
$this->createTestCrawler()->filterXPath('//ol')->parents(); |
$this->fail('->parents() throws an \InvalidArgumentException if the node list is empty'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->parents() throws an \InvalidArgumentException if the node list is empty'); |
} |
} |
/** |
* @dataProvider getBaseTagData |
*/ |
public function testBaseTag($baseValue, $linkValue, $expectedUri, $currentUri = null, $description = null) |
{ |
$crawler = new Crawler('<html><base href="'.$baseValue.'"><a href="'.$linkValue.'"></a></html>', $currentUri); |
$this->assertEquals($expectedUri, $crawler->filterXPath('//a')->link()->getUri(), $description); |
} |
public function getBaseTagData() |
{ |
return array( |
array('http://base.com', 'link', 'http://base.com/link'), |
array('//base.com', 'link', 'https://base.com/link', 'https://domain.com', '<base> tag can use a schema-less URL'), |
array('path/', 'link', 'https://domain.com/path/link', 'https://domain.com', '<base> tag can set a path'), |
array('http://base.com', '#', 'http://base.com#', 'http://domain.com/path/link', '<base> tag does work with links to an anchor'), |
array('http://base.com', '', 'http://base.com', 'http://domain.com/path/link', '<base> tag does work with empty links'), |
); |
} |
/** |
* @dataProvider getBaseTagWithFormData |
*/ |
public function testBaseTagWithForm($baseValue, $actionValue, $expectedUri, $currentUri = null, $description = null) |
{ |
$crawler = new Crawler('<html><base href="'.$baseValue.'"><form method="post" action="'.$actionValue.'"><button type="submit" name="submit"/></form></html>', $currentUri); |
$this->assertEquals($expectedUri, $crawler->filterXPath('//button')->form()->getUri(), $description); |
} |
public function getBaseTagWithFormData() |
{ |
return array( |
array('https://base.com/', 'link/', 'https://base.com/link/', 'https://base.com/link/', '<base> tag does work with a path and relative form action'), |
array('/basepath', '/registration', 'http://domain.com/registration', 'http://domain.com/registration', '<base> tag does work with a path and form action'), |
array('/basepath', '', 'http://domain.com/registration', 'http://domain.com/registration', '<base> tag does work with a path and empty form action'), |
array('http://base.com/', '/registration', 'http://base.com/registration', 'http://domain.com/registration', '<base> tag does work with a URL and form action'), |
array('http://base.com', '', 'http://domain.com/path/form', 'http://domain.com/path/form', '<base> tag does work with a URL and an empty form action'), |
array('http://base.com/path', '/registration', 'http://base.com/registration', 'http://domain.com/path/form', '<base> tag does work with a URL and form action'), |
); |
} |
public function testCountOfNestedElements() |
{ |
$crawler = new Crawler('<html><body><ul><li>List item 1<ul><li>Sublist item 1</li><li>Sublist item 2</ul></li></ul></body></html>'); |
$this->assertCount(1, $crawler->filter('li:contains("List item 1")')); |
} |
public function createTestCrawler($uri = null) |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(' |
<html> |
<body> |
<a href="foo">Foo</a> |
<a href="/foo"> Fabien\'s Foo </a> |
<a href="/foo">Fabien"s Foo</a> |
<a href="/foo">\' Fabien"s Foo</a> |
<a href="/bar"><img alt="Bar"/></a> |
<a href="/bar"><img alt=" Fabien\'s Bar "/></a> |
<a href="/bar"><img alt="Fabien"s Bar"/></a> |
<a href="/bar"><img alt="\' Fabien"s Bar"/></a> |
<a href="?get=param">GetLink</a> |
<a href="/example">Klausi|Claudiu</a> |
<form action="foo" id="FooFormId"> |
<input type="text" value="TextValue" name="TextName" /> |
<input type="submit" value="FooValue" name="FooName" id="FooId" /> |
<input type="button" value="BarValue" name="BarName" id="BarId" /> |
<button value="ButtonValue" name="ButtonName" id="ButtonId" /> |
</form> |
<input type="submit" value="FooBarValue" name="FooBarName" form="FooFormId" /> |
<input type="text" value="FooTextValue" name="FooTextName" form="FooFormId" /> |
<ul class="first"> |
<li class="first">One</li> |
<li>Two</li> |
<li>Three</li> |
</ul> |
<ul> |
<li>One Bis</li> |
<li>Two Bis</li> |
<li>Three Bis</li> |
</ul> |
<div id="parent"> |
<div id="child"></div> |
<div id="child2" xmlns:foo="http://example.com"></div> |
</div> |
<div id="sibling"><img /></div> |
</body> |
</html> |
'); |
return new Crawler($dom, $uri); |
} |
protected function createTestXmlCrawler($uri = null) |
{ |
$xml = '<?xml version="1.0" encoding="UTF-8"?> |
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/2007"> |
<id>tag:youtube.com,2008:video:kgZRZmEc9j4</id> |
<yt:accessControl action="comment" permission="allowed"/> |
<yt:accessControl action="videoRespond" permission="moderated"/> |
<media:group> |
<media:title type="plain">Chordates - CrashCourse Biology #24</media:title> |
<yt:aspectRatio>widescreen</yt:aspectRatio> |
</media:group> |
<media:category label="Music" scheme="http://gdata.youtube.com/schemas/2007/categories.cat">Music</media:category> |
</entry>'; |
return new Crawler($xml, $uri); |
} |
protected function createDomDocument() |
{ |
$dom = new \DOMDocument(); |
$dom->loadXML('<html><div class="foo"></div></html>'); |
return $dom; |
} |
protected function createNodeList() |
{ |
$dom = new \DOMDocument(); |
$dom->loadXML('<html><div class="foo"></div></html>'); |
$domxpath = new \DOMXPath($dom); |
return $domxpath->query('//div'); |
} |
} |
/vendor/symfony/dom-crawler/Tests/Field/ChoiceFormFieldTest.php |
---|
@@ -0,0 +1,404 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Tests\Field; |
use Symfony\Component\DomCrawler\Field\ChoiceFormField; |
class ChoiceFormFieldTest extends FormFieldTestCase |
{ |
public function testInitialize() |
{ |
$node = $this->createNode('textarea', ''); |
try { |
$field = new ChoiceFormField($node); |
$this->fail('->initialize() throws a \LogicException if the node is not an input or a select'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input or a select'); |
} |
$node = $this->createNode('input', '', array('type' => 'text')); |
try { |
$field = new ChoiceFormField($node); |
$this->fail('->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio'); |
} |
} |
public function testGetType() |
{ |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo')); |
$field = new ChoiceFormField($node); |
$this->assertEquals('radio', $field->getType(), '->getType() returns radio for radio buttons'); |
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo')); |
$field = new ChoiceFormField($node); |
$this->assertEquals('checkbox', $field->getType(), '->getType() returns radio for a checkbox'); |
$node = $this->createNode('select', ''); |
$field = new ChoiceFormField($node); |
$this->assertEquals('select', $field->getType(), '->getType() returns radio for a select'); |
} |
public function testIsMultiple() |
{ |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo')); |
$field = new ChoiceFormField($node); |
$this->assertFalse($field->isMultiple(), '->isMultiple() returns false for radio buttons'); |
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo')); |
$field = new ChoiceFormField($node); |
$this->assertFalse($field->isMultiple(), '->isMultiple() returns false for checkboxes'); |
$node = $this->createNode('select', ''); |
$field = new ChoiceFormField($node); |
$this->assertFalse($field->isMultiple(), '->isMultiple() returns false for selects without the multiple attribute'); |
$node = $this->createNode('select', '', array('multiple' => 'multiple')); |
$field = new ChoiceFormField($node); |
$this->assertTrue($field->isMultiple(), '->isMultiple() returns true for selects with the multiple attribute'); |
$node = $this->createNode('select', '', array('multiple' => '')); |
$field = new ChoiceFormField($node); |
$this->assertTrue($field->isMultiple(), '->isMultiple() returns true for selects with an empty multiple attribute'); |
} |
public function testSelects() |
{ |
$node = $this->createSelectNode(array('foo' => false, 'bar' => false)); |
$field = new ChoiceFormField($node); |
$this->assertTrue($field->hasValue(), '->hasValue() returns true for selects'); |
$this->assertEquals('foo', $field->getValue(), '->getValue() returns the first option if none are selected'); |
$this->assertFalse($field->isMultiple(), '->isMultiple() returns false when no multiple attribute is defined'); |
$node = $this->createSelectNode(array('foo' => false, 'bar' => true)); |
$field = new ChoiceFormField($node); |
$this->assertEquals('bar', $field->getValue(), '->getValue() returns the selected option'); |
$field->setValue('foo'); |
$this->assertEquals('foo', $field->getValue(), '->setValue() changes the selected option'); |
try { |
$field->setValue('foobar'); |
$this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the selected options'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the selected options'); |
} |
try { |
$field->setValue(array('foobar')); |
$this->fail('->setValue() throws an \InvalidArgumentException if the value is an array'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is an array'); |
} |
} |
public function testSelectWithEmptyBooleanAttribute() |
{ |
$node = $this->createSelectNode(array('foo' => false, 'bar' => true), array(), ''); |
$field = new ChoiceFormField($node); |
$this->assertEquals('bar', $field->getValue()); |
} |
public function testSelectIsDisabled() |
{ |
$node = $this->createSelectNode(array('foo' => false, 'bar' => true), array('disabled' => 'disabled')); |
$field = new ChoiceFormField($node); |
$this->assertTrue($field->isDisabled(), '->isDisabled() returns true for selects with a disabled attribute'); |
} |
public function testMultipleSelects() |
{ |
$node = $this->createSelectNode(array('foo' => false, 'bar' => false), array('multiple' => 'multiple')); |
$field = new ChoiceFormField($node); |
$this->assertEquals(array(), $field->getValue(), '->setValue() returns an empty array if multiple is true and no option is selected'); |
$field->setValue('foo'); |
$this->assertEquals(array('foo'), $field->getValue(), '->setValue() returns an array of options if multiple is true'); |
$field->setValue('bar'); |
$this->assertEquals(array('bar'), $field->getValue(), '->setValue() returns an array of options if multiple is true'); |
$field->setValue(array('foo', 'bar')); |
$this->assertEquals(array('foo', 'bar'), $field->getValue(), '->setValue() returns an array of options if multiple is true'); |
$node = $this->createSelectNode(array('foo' => true, 'bar' => true), array('multiple' => 'multiple')); |
$field = new ChoiceFormField($node); |
$this->assertEquals(array('foo', 'bar'), $field->getValue(), '->getValue() returns the selected options'); |
try { |
$field->setValue(array('foobar')); |
$this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the options'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the options'); |
} |
} |
public function testRadioButtons() |
{ |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo')); |
$field = new ChoiceFormField($node); |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar')); |
$field->addChoice($node); |
$this->assertFalse($field->hasValue(), '->hasValue() returns false when no radio button is selected'); |
$this->assertNull($field->getValue(), '->getValue() returns null if no radio button is selected'); |
$this->assertFalse($field->isMultiple(), '->isMultiple() returns false for radio buttons'); |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo')); |
$field = new ChoiceFormField($node); |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar', 'checked' => 'checked')); |
$field->addChoice($node); |
$this->assertTrue($field->hasValue(), '->hasValue() returns true when a radio button is selected'); |
$this->assertEquals('bar', $field->getValue(), '->getValue() returns the value attribute of the selected radio button'); |
$field->setValue('foo'); |
$this->assertEquals('foo', $field->getValue(), '->setValue() changes the selected radio button'); |
try { |
$field->setValue('foobar'); |
$this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the radio button values'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the radio button values'); |
} |
} |
public function testRadioButtonsWithEmptyBooleanAttribute() |
{ |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo')); |
$field = new ChoiceFormField($node); |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar', 'checked' => '')); |
$field->addChoice($node); |
$this->assertTrue($field->hasValue(), '->hasValue() returns true when a radio button is selected'); |
$this->assertEquals('bar', $field->getValue(), '->getValue() returns the value attribute of the selected radio button'); |
} |
public function testRadioButtonIsDisabled() |
{ |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo', 'disabled' => 'disabled')); |
$field = new ChoiceFormField($node); |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar')); |
$field->addChoice($node); |
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'baz', 'disabled' => '')); |
$field->addChoice($node); |
$field->select('foo'); |
$this->assertEquals('foo', $field->getValue(), '->getValue() returns the value attribute of the selected radio button'); |
$this->assertTrue($field->isDisabled()); |
$field->select('bar'); |
$this->assertEquals('bar', $field->getValue(), '->getValue() returns the value attribute of the selected radio button'); |
$this->assertFalse($field->isDisabled()); |
$field->select('baz'); |
$this->assertEquals('baz', $field->getValue(), '->getValue() returns the value attribute of the selected radio button'); |
$this->assertTrue($field->isDisabled()); |
} |
public function testCheckboxes() |
{ |
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name')); |
$field = new ChoiceFormField($node); |
$this->assertFalse($field->hasValue(), '->hasValue() returns false when the checkbox is not checked'); |
$this->assertNull($field->getValue(), '->getValue() returns null if the checkbox is not checked'); |
$this->assertFalse($field->isMultiple(), '->hasValue() returns false for checkboxes'); |
try { |
$field->addChoice(new \DOMElement('input')); |
$this->fail('->addChoice() throws a \LogicException for checkboxes'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->initialize() throws a \LogicException for checkboxes'); |
} |
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked')); |
$field = new ChoiceFormField($node); |
$this->assertTrue($field->hasValue(), '->hasValue() returns true when the checkbox is checked'); |
$this->assertEquals('on', $field->getValue(), '->getValue() returns 1 if the checkbox is checked and has no value attribute'); |
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo')); |
$field = new ChoiceFormField($node); |
$this->assertEquals('foo', $field->getValue(), '->getValue() returns the value attribute if the checkbox is checked'); |
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo')); |
$field = new ChoiceFormField($node); |
$field->setValue(false); |
$this->assertNull($field->getValue(), '->setValue() unchecks the checkbox is value is false'); |
$field->setValue(true); |
$this->assertEquals('foo', $field->getValue(), '->setValue() checks the checkbox is value is true'); |
try { |
$field->setValue('bar'); |
$this->fail('->setValue() throws an \InvalidArgumentException if the value is not one from the value attribute'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one from the value attribute'); |
} |
} |
public function testCheckboxWithEmptyBooleanAttribute() |
{ |
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo', 'checked' => '')); |
$field = new ChoiceFormField($node); |
$this->assertTrue($field->hasValue(), '->hasValue() returns true when the checkbox is checked'); |
$this->assertEquals('foo', $field->getValue()); |
} |
public function testTick() |
{ |
$node = $this->createSelectNode(array('foo' => false, 'bar' => false)); |
$field = new ChoiceFormField($node); |
try { |
$field->tick(); |
$this->fail('->tick() throws a \LogicException for select boxes'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->tick() throws a \LogicException for select boxes'); |
} |
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name')); |
$field = new ChoiceFormField($node); |
$field->tick(); |
$this->assertEquals('on', $field->getValue(), '->tick() ticks checkboxes'); |
} |
public function testUntick() |
{ |
$node = $this->createSelectNode(array('foo' => false, 'bar' => false)); |
$field = new ChoiceFormField($node); |
try { |
$field->untick(); |
$this->fail('->untick() throws a \LogicException for select boxes'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->untick() throws a \LogicException for select boxes'); |
} |
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked')); |
$field = new ChoiceFormField($node); |
$field->untick(); |
$this->assertNull($field->getValue(), '->untick() unticks checkboxes'); |
} |
public function testSelect() |
{ |
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked')); |
$field = new ChoiceFormField($node); |
$field->select(true); |
$this->assertEquals('on', $field->getValue(), '->select() changes the value of the field'); |
$field->select(false); |
$this->assertNull($field->getValue(), '->select() changes the value of the field'); |
$node = $this->createSelectNode(array('foo' => false, 'bar' => false)); |
$field = new ChoiceFormField($node); |
$field->select('foo'); |
$this->assertEquals('foo', $field->getValue(), '->select() changes the selected option'); |
} |
public function testOptionWithNoValue() |
{ |
$node = $this->createSelectNodeWithEmptyOption(array('foo' => false, 'bar' => false)); |
$field = new ChoiceFormField($node); |
$this->assertEquals('foo', $field->getValue()); |
$node = $this->createSelectNodeWithEmptyOption(array('foo' => false, 'bar' => true)); |
$field = new ChoiceFormField($node); |
$this->assertEquals('bar', $field->getValue()); |
$field->select('foo'); |
$this->assertEquals('foo', $field->getValue(), '->select() changes the selected option'); |
} |
public function testDisableValidation() |
{ |
$node = $this->createSelectNode(array('foo' => false, 'bar' => false)); |
$field = new ChoiceFormField($node); |
$field->disableValidation(); |
$field->setValue('foobar'); |
$this->assertEquals('foobar', $field->getValue(), '->disableValidation() allows to set a value which is not in the selected options.'); |
$node = $this->createSelectNode(array('foo' => false, 'bar' => false), array('multiple' => 'multiple')); |
$field = new ChoiceFormField($node); |
$field->disableValidation(); |
$field->setValue(array('foobar')); |
$this->assertEquals(array('foobar'), $field->getValue(), '->disableValidation() allows to set a value which is not in the selected options.'); |
} |
public function testSelectWithEmptyValue() |
{ |
$node = $this->createSelectNodeWithEmptyOption(array('' => true, 'Female' => false, 'Male' => false)); |
$field = new ChoiceFormField($node); |
$this->assertSame('', $field->getValue()); |
} |
protected function createSelectNode($options, $attributes = array(), $selectedAttrText = 'selected') |
{ |
$document = new \DOMDocument(); |
$node = $document->createElement('select'); |
foreach ($attributes as $name => $value) { |
$node->setAttribute($name, $value); |
} |
$node->setAttribute('name', 'name'); |
foreach ($options as $value => $selected) { |
$option = $document->createElement('option', $value); |
$option->setAttribute('value', $value); |
if ($selected) { |
$option->setAttribute('selected', $selectedAttrText); |
} |
$node->appendChild($option); |
} |
return $node; |
} |
protected function createSelectNodeWithEmptyOption($options, $attributes = array()) |
{ |
$document = new \DOMDocument(); |
$node = $document->createElement('select'); |
foreach ($attributes as $name => $value) { |
$node->setAttribute($name, $value); |
} |
$node->setAttribute('name', 'name'); |
foreach ($options as $value => $selected) { |
$option = $document->createElement('option', $value); |
if ($selected) { |
$option->setAttribute('selected', 'selected'); |
} |
$node->appendChild($option); |
} |
return $node; |
} |
} |
/vendor/symfony/dom-crawler/Tests/Field/FileFormFieldTest.php |
---|
@@ -0,0 +1,114 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Tests\Field; |
use Symfony\Component\DomCrawler\Field\FileFormField; |
class FileFormFieldTest extends FormFieldTestCase |
{ |
public function testInitialize() |
{ |
$node = $this->createNode('input', '', array('type' => 'file')); |
$field = new FileFormField($node); |
$this->assertEquals(array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0), $field->getValue(), '->initialize() sets the value of the field to no file uploaded'); |
$node = $this->createNode('textarea', ''); |
try { |
$field = new FileFormField($node); |
$this->fail('->initialize() throws a \LogicException if the node is not an input field'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input field'); |
} |
$node = $this->createNode('input', '', array('type' => 'text')); |
try { |
$field = new FileFormField($node); |
$this->fail('->initialize() throws a \LogicException if the node is not a file input field'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a file input field'); |
} |
} |
/** |
* @dataProvider getSetValueMethods |
*/ |
public function testSetValue($method) |
{ |
$node = $this->createNode('input', '', array('type' => 'file')); |
$field = new FileFormField($node); |
$field->$method(null); |
$this->assertEquals(array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0), $field->getValue(), "->$method() clears the uploaded file if the value is null"); |
$field->$method(__FILE__); |
$value = $field->getValue(); |
$this->assertEquals(basename(__FILE__), $value['name'], "->$method() sets the name of the file field"); |
$this->assertEquals('', $value['type'], "->$method() sets the type of the file field"); |
$this->assertInternalType('string', $value['tmp_name'], "->$method() sets the tmp_name of the file field"); |
$this->assertFileExists($value['tmp_name'], "->$method() creates a copy of the file at the tmp_name path"); |
$this->assertEquals(0, $value['error'], "->$method() sets the error of the file field"); |
$this->assertEquals(filesize(__FILE__), $value['size'], "->$method() sets the size of the file field"); |
$origInfo = pathinfo(__FILE__); |
$tmpInfo = pathinfo($value['tmp_name']); |
$this->assertEquals( |
$origInfo['extension'], |
$tmpInfo['extension'], |
"->$method() keeps the same file extension in the tmp_name copy" |
); |
$field->$method(__DIR__.'/../Fixtures/no-extension'); |
$value = $field->getValue(); |
$this->assertArrayNotHasKey( |
'extension', |
pathinfo($value['tmp_name']), |
"->$method() does not add a file extension in the tmp_name copy" |
); |
} |
public function getSetValueMethods() |
{ |
return array( |
array('setValue'), |
array('upload'), |
); |
} |
public function testSetErrorCode() |
{ |
$node = $this->createNode('input', '', array('type' => 'file')); |
$field = new FileFormField($node); |
$field->setErrorCode(UPLOAD_ERR_FORM_SIZE); |
$value = $field->getValue(); |
$this->assertEquals(UPLOAD_ERR_FORM_SIZE, $value['error'], '->setErrorCode() sets the file input field error code'); |
try { |
$field->setErrorCode('foobar'); |
$this->fail('->setErrorCode() throws a \InvalidArgumentException if the error code is not valid'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->setErrorCode() throws a \InvalidArgumentException if the error code is not valid'); |
} |
} |
public function testSetRawFilePath() |
{ |
$node = $this->createNode('input', '', array('type' => 'file')); |
$field = new FileFormField($node); |
$field->setFilePath(__FILE__); |
$this->assertEquals(__FILE__, $field->getValue()); |
} |
} |
/vendor/symfony/dom-crawler/Tests/Field/FormFieldTest.php |
---|
@@ -0,0 +1,38 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Tests\Field; |
use Symfony\Component\DomCrawler\Field\InputFormField; |
class FormFieldTest extends FormFieldTestCase |
{ |
public function testGetName() |
{ |
$node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value')); |
$field = new InputFormField($node); |
$this->assertEquals('name', $field->getName(), '->getName() returns the name of the field'); |
} |
public function testGetSetHasValue() |
{ |
$node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value')); |
$field = new InputFormField($node); |
$this->assertEquals('value', $field->getValue(), '->getValue() returns the value of the field'); |
$field->setValue('foo'); |
$this->assertEquals('foo', $field->getValue(), '->setValue() sets the value of the field'); |
$this->assertTrue($field->hasValue(), '->hasValue() always returns true'); |
} |
} |
/vendor/symfony/dom-crawler/Tests/Field/FormFieldTestCase.php |
---|
@@ -0,0 +1,29 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Tests\Field; |
use PHPUnit\Framework\TestCase; |
class FormFieldTestCase extends TestCase |
{ |
protected function createNode($tag, $value, $attributes = array()) |
{ |
$document = new \DOMDocument(); |
$node = $document->createElement($tag, $value); |
foreach ($attributes as $name => $value) { |
$node->setAttribute($name, $value); |
} |
return $node; |
} |
} |
/vendor/symfony/dom-crawler/Tests/Field/InputFormFieldTest.php |
---|
@@ -0,0 +1,49 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Tests\Field; |
use Symfony\Component\DomCrawler\Field\InputFormField; |
class InputFormFieldTest extends FormFieldTestCase |
{ |
public function testInitialize() |
{ |
$node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value')); |
$field = new InputFormField($node); |
$this->assertEquals('value', $field->getValue(), '->initialize() sets the value of the field to the value attribute value'); |
$node = $this->createNode('textarea', ''); |
try { |
$field = new InputFormField($node); |
$this->fail('->initialize() throws a \LogicException if the node is not an input'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input'); |
} |
$node = $this->createNode('input', '', array('type' => 'checkbox')); |
try { |
$field = new InputFormField($node); |
$this->fail('->initialize() throws a \LogicException if the node is a checkbox'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is a checkbox'); |
} |
$node = $this->createNode('input', '', array('type' => 'file')); |
try { |
$field = new InputFormField($node); |
$this->fail('->initialize() throws a \LogicException if the node is a file'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is a file'); |
} |
} |
} |
/vendor/symfony/dom-crawler/Tests/Field/TextareaFormFieldTest.php |
---|
@@ -0,0 +1,46 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Tests\Field; |
use Symfony\Component\DomCrawler\Field\TextareaFormField; |
class TextareaFormFieldTest extends FormFieldTestCase |
{ |
public function testInitialize() |
{ |
$node = $this->createNode('textarea', 'foo bar'); |
$field = new TextareaFormField($node); |
$this->assertEquals('foo bar', $field->getValue(), '->initialize() sets the value of the field to the textarea node value'); |
$node = $this->createNode('input', ''); |
try { |
$field = new TextareaFormField($node); |
$this->fail('->initialize() throws a \LogicException if the node is not a textarea'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a textarea'); |
} |
// Ensure that valid HTML can be used on a textarea. |
$node = $this->createNode('textarea', 'foo bar <h1>Baz</h1>'); |
$field = new TextareaFormField($node); |
$this->assertEquals('foo bar <h1>Baz</h1>', $field->getValue(), '->initialize() sets the value of the field to the textarea node value'); |
// Ensure that we don't do any DOM manipulation/validation by passing in |
// "invalid" HTML. |
$node = $this->createNode('textarea', 'foo bar <h1>Baz</h2>'); |
$field = new TextareaFormField($node); |
$this->assertEquals('foo bar <h1>Baz</h2>', $field->getValue(), '->initialize() sets the value of the field to the textarea node value'); |
} |
} |
/vendor/symfony/dom-crawler/Tests/Fixtures/no-extension |
---|
@@ -0,0 +1 @@ |
Test |
/vendor/symfony/dom-crawler/Tests/Fixtures/windows-1250.html |
---|
@@ -0,0 +1,8 @@ |
<html> |
<head> |
<meta http-equiv="content-type" content="text/html;charset=windows-1250"> |
</head> |
<body> |
<p>Žèýù</p> |
</body> |
</html> |
/vendor/symfony/dom-crawler/Tests/FormTest.php |
---|
@@ -0,0 +1,941 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Tests; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\DomCrawler\Form; |
use Symfony\Component\DomCrawler\FormFieldRegistry; |
class FormTest extends TestCase |
{ |
public static function setUpBeforeClass() |
{ |
// Ensure that the private helper class FormFieldRegistry is loaded |
class_exists('Symfony\\Component\\DomCrawler\\Form'); |
} |
public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(' |
<html> |
<input type="submit" /> |
<form> |
<input type="foo" /> |
</form> |
<button /> |
</html> |
'); |
$nodes = $dom->getElementsByTagName('input'); |
try { |
$form = new Form($nodes->item(0), 'http://example.com'); |
$this->fail('__construct() throws a \\LogicException if the node has no form ancestor'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor'); |
} |
try { |
$form = new Form($nodes->item(1), 'http://example.com'); |
$this->fail('__construct() throws a \\LogicException if the input type is not submit, button, or image'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '__construct() throws a \\LogicException if the input type is not submit, button, or image'); |
} |
$nodes = $dom->getElementsByTagName('button'); |
try { |
$form = new Form($nodes->item(0), 'http://example.com'); |
$this->fail('__construct() throws a \\LogicException if the node has no form ancestor'); |
} catch (\LogicException $e) { |
$this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor'); |
} |
} |
/** |
* __construct() should throw \\LogicException if the form attribute is invalid. |
* |
* @expectedException \LogicException |
*/ |
public function testConstructorThrowsExceptionIfNoRelatedForm() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(' |
<html> |
<form id="bar"> |
<input type="submit" form="nonexistent" /> |
</form> |
<input type="text" form="nonexistent" /> |
<button /> |
</html> |
'); |
$nodes = $dom->getElementsByTagName('input'); |
$form = new Form($nodes->item(0), 'http://example.com'); |
$form = new Form($nodes->item(1), 'http://example.com'); |
} |
public function testConstructorLoadsOnlyFieldsOfTheRightForm() |
{ |
$dom = $this->createTestMultipleForm(); |
$nodes = $dom->getElementsByTagName('form'); |
$buttonElements = $dom->getElementsByTagName('button'); |
$form = new Form($nodes->item(0), 'http://example.com'); |
$this->assertCount(3, $form->all()); |
$form = new Form($buttonElements->item(1), 'http://example.com'); |
$this->assertCount(5, $form->all()); |
} |
public function testConstructorHandlesFormAttribute() |
{ |
$dom = $this->createTestHtml5Form(); |
$inputElements = $dom->getElementsByTagName('input'); |
$buttonElements = $dom->getElementsByTagName('button'); |
// Tests if submit buttons are correctly assigned to forms |
$form1 = new Form($buttonElements->item(1), 'http://example.com'); |
$this->assertSame($dom->getElementsByTagName('form')->item(0), $form1->getFormNode(), 'HTML5-compliant form attribute handled incorrectly'); |
$form1 = new Form($inputElements->item(3), 'http://example.com'); |
$this->assertSame($dom->getElementsByTagName('form')->item(0), $form1->getFormNode(), 'HTML5-compliant form attribute handled incorrectly'); |
$form2 = new Form($buttonElements->item(0), 'http://example.com'); |
$this->assertSame($dom->getElementsByTagName('form')->item(1), $form2->getFormNode(), 'HTML5-compliant form attribute handled incorrectly'); |
} |
public function testConstructorHandlesFormValues() |
{ |
$dom = $this->createTestHtml5Form(); |
$inputElements = $dom->getElementsByTagName('input'); |
$buttonElements = $dom->getElementsByTagName('button'); |
$form1 = new Form($inputElements->item(3), 'http://example.com'); |
$form2 = new Form($buttonElements->item(0), 'http://example.com'); |
// Tests if form values are correctly assigned to forms |
$values1 = array( |
'apples' => array('1', '2'), |
'form_name' => 'form-1', |
'button_1' => 'Capture fields', |
'outer_field' => 'success', |
); |
$values2 = array( |
'oranges' => array('1', '2', '3'), |
'form_name' => 'form_2', |
'button_2' => '', |
'app_frontend_form_type_contact_form_type' => array('contactType' => '', 'firstName' => 'John'), |
); |
$this->assertEquals($values1, $form1->getPhpValues(), 'HTML5-compliant form attribute handled incorrectly'); |
$this->assertEquals($values2, $form2->getPhpValues(), 'HTML5-compliant form attribute handled incorrectly'); |
} |
public function testMultiValuedFields() |
{ |
$form = $this->createForm('<form> |
<input type="text" name="foo[4]" value="foo" disabled="disabled" /> |
<input type="text" name="foo" value="foo" disabled="disabled" /> |
<input type="text" name="foo[2]" value="foo" disabled="disabled" /> |
<input type="text" name="foo[]" value="foo" disabled="disabled" /> |
<input type="text" name="bar[foo][]" value="foo" disabled="disabled" /> |
<input type="text" name="bar[foo][foobar]" value="foo" disabled="disabled" /> |
<input type="submit" /> |
</form> |
'); |
$this->assertEquals( |
array_keys($form->all()), |
array('foo[2]', 'foo[3]', 'bar[foo][0]', 'bar[foo][foobar]') |
); |
$this->assertEquals($form->get('foo[2]')->getValue(), 'foo'); |
$this->assertEquals($form->get('foo[3]')->getValue(), 'foo'); |
$this->assertEquals($form->get('bar[foo][0]')->getValue(), 'foo'); |
$this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foo'); |
$form['foo[2]'] = 'bar'; |
$form['foo[3]'] = 'bar'; |
$this->assertEquals($form->get('foo[2]')->getValue(), 'bar'); |
$this->assertEquals($form->get('foo[3]')->getValue(), 'bar'); |
$form['bar'] = array('foo' => array('0' => 'bar', 'foobar' => 'foobar')); |
$this->assertEquals($form->get('bar[foo][0]')->getValue(), 'bar'); |
$this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foobar'); |
} |
/** |
* @dataProvider provideInitializeValues |
*/ |
public function testConstructor($message, $form, $values) |
{ |
$form = $this->createForm('<form>'.$form.'</form>'); |
$this->assertEquals( |
$values, |
array_map( |
function ($field) { |
$class = get_class($field); |
return array(substr($class, strrpos($class, '\\') + 1), $field->getValue()); |
}, |
$form->all() |
), |
'->getDefaultValues() '.$message |
); |
} |
public function provideInitializeValues() |
{ |
return array( |
array( |
'does not take into account input fields without a name attribute', |
'<input type="text" value="foo" /> |
<input type="submit" />', |
array(), |
), |
array( |
'does not take into account input fields with an empty name attribute value', |
'<input type="text" name="" value="foo" /> |
<input type="submit" />', |
array(), |
), |
array( |
'takes into account disabled input fields', |
'<input type="text" name="foo" value="foo" disabled="disabled" /> |
<input type="submit" />', |
array('foo' => array('InputFormField', 'foo')), |
), |
array( |
'appends the submitted button value', |
'<input type="submit" name="bar" value="bar" />', |
array('bar' => array('InputFormField', 'bar')), |
), |
array( |
'appends the submitted button value for Button element', |
'<button type="submit" name="bar" value="bar">Bar</button>', |
array('bar' => array('InputFormField', 'bar')), |
), |
array( |
'appends the submitted button value but not other submit buttons', |
'<input type="submit" name="bar" value="bar" /> |
<input type="submit" name="foobar" value="foobar" />', |
array('foobar' => array('InputFormField', 'foobar')), |
), |
array( |
'turns an image input into x and y fields', |
'<input type="image" name="bar" />', |
array('bar.x' => array('InputFormField', '0'), 'bar.y' => array('InputFormField', '0')), |
), |
array( |
'returns textareas', |
'<textarea name="foo">foo</textarea> |
<input type="submit" />', |
array('foo' => array('TextareaFormField', 'foo')), |
), |
array( |
'returns inputs', |
'<input type="text" name="foo" value="foo" /> |
<input type="submit" />', |
array('foo' => array('InputFormField', 'foo')), |
), |
array( |
'returns checkboxes', |
'<input type="checkbox" name="foo" value="foo" checked="checked" /> |
<input type="submit" />', |
array('foo' => array('ChoiceFormField', 'foo')), |
), |
array( |
'returns not-checked checkboxes', |
'<input type="checkbox" name="foo" value="foo" /> |
<input type="submit" />', |
array('foo' => array('ChoiceFormField', false)), |
), |
array( |
'returns radio buttons', |
'<input type="radio" name="foo" value="foo" /> |
<input type="radio" name="foo" value="bar" checked="bar" /> |
<input type="submit" />', |
array('foo' => array('ChoiceFormField', 'bar')), |
), |
array( |
'returns file inputs', |
'<input type="file" name="foo" /> |
<input type="submit" />', |
array('foo' => array('FileFormField', array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), |
), |
); |
} |
public function testGetFormNode() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML('<html><form><input type="submit" /></form></html>'); |
$form = new Form($dom->getElementsByTagName('input')->item(0), 'http://example.com'); |
$this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); |
} |
public function testGetFormNodeFromNamedForm() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML('<html><form name="my_form"><input type="submit" /></form></html>'); |
$form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com'); |
$this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); |
} |
public function testGetMethod() |
{ |
$form = $this->createForm('<form><input type="submit" /></form>'); |
$this->assertEquals('GET', $form->getMethod(), '->getMethod() returns get if no method is defined'); |
$form = $this->createForm('<form method="post"><input type="submit" /></form>'); |
$this->assertEquals('POST', $form->getMethod(), '->getMethod() returns the method attribute value of the form'); |
$form = $this->createForm('<form method="post"><input type="submit" /></form>', 'put'); |
$this->assertEquals('PUT', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); |
$form = $this->createForm('<form method="post"><input type="submit" /></form>', 'delete'); |
$this->assertEquals('DELETE', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); |
$form = $this->createForm('<form method="post"><input type="submit" /></form>', 'patch'); |
$this->assertEquals('PATCH', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); |
} |
public function testGetSetValue() |
{ |
$form = $this->createForm('<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>'); |
$this->assertEquals('foo', $form['foo']->getValue(), '->offsetGet() returns the value of a form field'); |
$form['foo'] = 'bar'; |
$this->assertEquals('bar', $form['foo']->getValue(), '->offsetSet() changes the value of a form field'); |
try { |
$form['foobar'] = 'bar'; |
$this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); |
} |
try { |
$form['foobar']; |
$this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); |
} |
} |
public function testDisableValidation() |
{ |
$form = $this->createForm('<form> |
<select name="foo[bar]"> |
<option value="bar">bar</option> |
</select> |
<select name="foo[baz]"> |
<option value="foo">foo</option> |
</select> |
<input type="submit" /> |
</form>'); |
$form->disableValidation(); |
$form['foo[bar]']->select('foo'); |
$form['foo[baz]']->select('bar'); |
$this->assertEquals('foo', $form['foo[bar]']->getValue(), '->disableValidation() disables validation of all ChoiceFormField.'); |
$this->assertEquals('bar', $form['foo[baz]']->getValue(), '->disableValidation() disables validation of all ChoiceFormField.'); |
} |
public function testOffsetUnset() |
{ |
$form = $this->createForm('<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>'); |
unset($form['foo']); |
$this->assertFalse(isset($form['foo']), '->offsetUnset() removes a field'); |
} |
public function testOffsetExists() |
{ |
$form = $this->createForm('<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>'); |
$this->assertTrue(isset($form['foo']), '->offsetExists() return true if the field exists'); |
$this->assertFalse(isset($form['bar']), '->offsetExists() return false if the field does not exist'); |
} |
public function testGetValues() |
{ |
$form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><select multiple="multiple" name="baz[]"></select><input type="submit" /></form>'); |
$this->assertEquals(array('foo[bar]' => 'foo', 'bar' => 'bar', 'baz' => array()), $form->getValues(), '->getValues() returns all form field values'); |
$form = $this->createForm('<form><input type="checkbox" name="foo" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include not-checked checkboxes'); |
$form = $this->createForm('<form><input type="file" name="foo" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include file input fields'); |
$form = $this->createForm('<form><input type="text" name="foo" value="foo" disabled="disabled" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include disabled fields'); |
} |
public function testSetValues() |
{ |
$form = $this->createForm('<form><input type="checkbox" name="foo" value="foo" checked="checked" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$form->setValues(array('foo' => false, 'bar' => 'foo')); |
$this->assertEquals(array('bar' => 'foo'), $form->getValues(), '->setValues() sets the values of fields'); |
} |
public function testMultiselectSetValues() |
{ |
$form = $this->createForm('<form><select multiple="multiple" name="multi"><option value="foo">foo</option><option value="bar">bar</option></select><input type="submit" /></form>'); |
$form->setValues(array('multi' => array('foo', 'bar'))); |
$this->assertEquals(array('multi' => array('foo', 'bar')), $form->getValues(), '->setValue() sets the values of select'); |
} |
public function testGetPhpValues() |
{ |
$form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), '->getPhpValues() converts keys with [] to arrays'); |
$form = $this->createForm('<form><input type="text" name="fo.o[ba.r]" value="foo" /><input type="text" name="ba r" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array('fo.o' => array('ba.r' => 'foo'), 'ba r' => 'bar'), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names'); |
$form = $this->createForm('<form><input type="text" name="fo.o[ba.r][]" value="foo" /><input type="text" name="fo.o[ba.r][ba.z]" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array('fo.o' => array('ba.r' => array('foo', 'ba.z' => 'bar'))), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names recursively'); |
$form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><select multiple="multiple" name="baz[]"></select><input type="submit" /></form>'); |
$this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), "->getPhpValues() doesn't return empty values"); |
} |
public function testGetFiles() |
{ |
$form = $this->createForm('<form><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array(), $form->getFiles(), '->getFiles() returns an empty array if method is get'); |
$form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for POST'); |
$form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>', 'put'); |
$this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PUT'); |
$form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>', 'delete'); |
$this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for DELETE'); |
$form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>', 'patch'); |
$this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PATCH'); |
$form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" disabled="disabled" /><input type="submit" /></form>'); |
$this->assertEquals(array(), $form->getFiles(), '->getFiles() does not include disabled file fields'); |
} |
public function testGetPhpFiles() |
{ |
$form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array('foo' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() converts keys with [] to arrays'); |
$form = $this->createForm('<form method="post"><input type="file" name="f.o o[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array('f.o o' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names'); |
$form = $this->createForm('<form method="post"><input type="file" name="f.o o[bar][ba.z]" /><input type="file" name="f.o o[bar][]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertEquals(array('f.o o' => array('bar' => array('ba.z' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0), array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names recursively'); |
} |
/** |
* @dataProvider provideGetUriValues |
*/ |
public function testGetUri($message, $form, $values, $uri, $method = null) |
{ |
$form = $this->createForm($form, $method); |
$form->setValues($values); |
$this->assertEquals('http://example.com'.$uri, $form->getUri(), '->getUri() '.$message); |
} |
public function testGetBaseUri() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML('<form method="post" action="foo.php"><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$nodes = $dom->getElementsByTagName('input'); |
$form = new Form($nodes->item($nodes->length - 1), 'http://www.foo.com/'); |
$this->assertEquals('http://www.foo.com/foo.php', $form->getUri()); |
} |
public function testGetUriWithAnchor() |
{ |
$form = $this->createForm('<form action="#foo"><input type="submit" /></form>', null, 'http://example.com/id/123'); |
$this->assertEquals('http://example.com/id/123#foo', $form->getUri()); |
} |
public function testGetUriActionAbsolute() |
{ |
$formHtml = '<form id="login_form" action="https://login.foo.com/login.php?login_attempt=1" method="POST"><input type="text" name="foo" value="foo" /><input type="submit" /></form>'; |
$form = $this->createForm($formHtml); |
$this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); |
$form = $this->createForm($formHtml, null, 'https://login.foo.com'); |
$this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); |
$form = $this->createForm($formHtml, null, 'https://login.foo.com/bar/'); |
$this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); |
// The action URI haven't the same domain Host have an another domain as Host |
$form = $this->createForm($formHtml, null, 'https://www.foo.com'); |
$this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); |
$form = $this->createForm($formHtml, null, 'https://www.foo.com/bar/'); |
$this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); |
} |
public function testGetUriAbsolute() |
{ |
$form = $this->createForm('<form action="foo"><input type="submit" /></form>', null, 'http://localhost/foo/'); |
$this->assertEquals('http://localhost/foo/foo', $form->getUri(), '->getUri() returns absolute URIs'); |
$form = $this->createForm('<form action="/foo"><input type="submit" /></form>', null, 'http://localhost/foo/'); |
$this->assertEquals('http://localhost/foo', $form->getUri(), '->getUri() returns absolute URIs'); |
} |
public function testGetUriWithOnlyQueryString() |
{ |
$form = $this->createForm('<form action="?get=param"><input type="submit" /></form>', null, 'http://localhost/foo/bar'); |
$this->assertEquals('http://localhost/foo/bar?get=param', $form->getUri(), '->getUri() returns absolute URIs only if the host has been defined in the constructor'); |
} |
public function testGetUriWithoutAction() |
{ |
$form = $this->createForm('<form><input type="submit" /></form>', null, 'http://localhost/foo/bar'); |
$this->assertEquals('http://localhost/foo/bar', $form->getUri(), '->getUri() returns path if no action defined'); |
} |
public function provideGetUriValues() |
{ |
return array( |
array( |
'returns the URI of the form', |
'<form action="/foo"><input type="submit" /></form>', |
array(), |
'/foo', |
), |
array( |
'appends the form values if the method is get', |
'<form action="/foo"><input type="text" name="foo" value="foo" /><input type="submit" /></form>', |
array(), |
'/foo?foo=foo', |
), |
array( |
'appends the form values and merges the submitted values', |
'<form action="/foo"><input type="text" name="foo" value="foo" /><input type="submit" /></form>', |
array('foo' => 'bar'), |
'/foo?foo=bar', |
), |
array( |
'does not append values if the method is post', |
'<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>', |
array(), |
'/foo', |
), |
array( |
'does not append values if the method is patch', |
'<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>', |
array(), |
'/foo', |
'PUT', |
), |
array( |
'does not append values if the method is delete', |
'<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>', |
array(), |
'/foo', |
'DELETE', |
), |
array( |
'does not append values if the method is put', |
'<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>', |
array(), |
'/foo', |
'PATCH', |
), |
array( |
'appends the form values to an existing query string', |
'<form action="/foo?bar=bar"><input type="text" name="foo" value="foo" /><input type="submit" /></form>', |
array(), |
'/foo?bar=bar&foo=foo', |
), |
array( |
'replaces query values with the form values', |
'<form action="/foo?bar=bar"><input type="text" name="bar" value="foo" /><input type="submit" /></form>', |
array(), |
'/foo?bar=foo', |
), |
array( |
'returns an empty URI if the action is empty', |
'<form><input type="submit" /></form>', |
array(), |
'/', |
), |
array( |
'appends the form values even if the action is empty', |
'<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>', |
array(), |
'/?foo=foo', |
), |
array( |
'chooses the path if the action attribute value is a sharp (#)', |
'<form action="#" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>', |
array(), |
'/#', |
), |
); |
} |
public function testHas() |
{ |
$form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertFalse($form->has('foo'), '->has() returns false if a field is not in the form'); |
$this->assertTrue($form->has('bar'), '->has() returns true if a field is in the form'); |
} |
public function testRemove() |
{ |
$form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$form->remove('bar'); |
$this->assertFalse($form->has('bar'), '->remove() removes a field'); |
} |
public function testGet() |
{ |
$form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Field\\InputFormField', $form->get('bar'), '->get() returns the field object associated with the given name'); |
try { |
$form->get('foo'); |
$this->fail('->get() throws an \InvalidArgumentException if the field does not exist'); |
} catch (\InvalidArgumentException $e) { |
$this->assertTrue(true, '->get() throws an \InvalidArgumentException if the field does not exist'); |
} |
} |
public function testAll() |
{ |
$form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>'); |
$fields = $form->all(); |
$this->assertCount(1, $fields, '->all() return an array of form field objects'); |
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Field\\InputFormField', $fields['bar'], '->all() return an array of form field objects'); |
} |
public function testSubmitWithoutAFormButton() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(' |
<html> |
<form> |
<input type="foo" /> |
</form> |
</html> |
'); |
$nodes = $dom->getElementsByTagName('form'); |
$form = new Form($nodes->item(0), 'http://example.com'); |
$this->assertSame($nodes->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); |
} |
public function testTypeAttributeIsCaseInsensitive() |
{ |
$form = $this->createForm('<form method="post"><input type="IMAGE" name="example" /></form>'); |
$this->assertTrue($form->has('example.x'), '->has() returns true if the image input was correctly turned into an x and a y fields'); |
$this->assertTrue($form->has('example.y'), '->has() returns true if the image input was correctly turned into an x and a y fields'); |
} |
public function testFormFieldRegistryAcceptAnyNames() |
{ |
$field = $this->getFormFieldMock('[t:dbt%3adate;]data_daterange_enddate_value'); |
$registry = new FormFieldRegistry(); |
$registry->add($field); |
$this->assertEquals($field, $registry->get('[t:dbt%3adate;]data_daterange_enddate_value')); |
$registry->set('[t:dbt%3adate;]data_daterange_enddate_value', null); |
$form = $this->createForm('<form><input type="text" name="[t:dbt%3adate;]data_daterange_enddate_value" value="bar" /><input type="submit" /></form>'); |
$form['[t:dbt%3adate;]data_daterange_enddate_value'] = 'bar'; |
$registry->remove('[t:dbt%3adate;]data_daterange_enddate_value'); |
} |
/** |
* @expectedException \InvalidArgumentException |
*/ |
public function testFormFieldRegistryGetThrowAnExceptionWhenTheFieldDoesNotExist() |
{ |
$registry = new FormFieldRegistry(); |
$registry->get('foo'); |
} |
/** |
* @expectedException \InvalidArgumentException |
*/ |
public function testFormFieldRegistrySetThrowAnExceptionWhenTheFieldDoesNotExist() |
{ |
$registry = new FormFieldRegistry(); |
$registry->set('foo', null); |
} |
public function testFormFieldRegistryHasReturnsTrueWhenTheFQNExists() |
{ |
$registry = new FormFieldRegistry(); |
$registry->add($this->getFormFieldMock('foo[bar]')); |
$this->assertTrue($registry->has('foo')); |
$this->assertTrue($registry->has('foo[bar]')); |
$this->assertFalse($registry->has('bar')); |
$this->assertFalse($registry->has('foo[foo]')); |
} |
public function testFormRegistryFieldsCanBeRemoved() |
{ |
$registry = new FormFieldRegistry(); |
$registry->add($this->getFormFieldMock('foo')); |
$registry->remove('foo'); |
$this->assertFalse($registry->has('foo')); |
} |
public function testFormRegistrySupportsMultivaluedFields() |
{ |
$registry = new FormFieldRegistry(); |
$registry->add($this->getFormFieldMock('foo[]')); |
$registry->add($this->getFormFieldMock('foo[]')); |
$registry->add($this->getFormFieldMock('bar[5]')); |
$registry->add($this->getFormFieldMock('bar[]')); |
$registry->add($this->getFormFieldMock('bar[baz]')); |
$this->assertEquals( |
array('foo[0]', 'foo[1]', 'bar[5]', 'bar[6]', 'bar[baz]'), |
array_keys($registry->all()) |
); |
} |
public function testFormRegistrySetValues() |
{ |
$registry = new FormFieldRegistry(); |
$registry->add($f2 = $this->getFormFieldMock('foo[2]')); |
$registry->add($f3 = $this->getFormFieldMock('foo[3]')); |
$registry->add($fbb = $this->getFormFieldMock('foo[bar][baz]')); |
$f2 |
->expects($this->exactly(2)) |
->method('setValue') |
->with(2) |
; |
$f3 |
->expects($this->exactly(2)) |
->method('setValue') |
->with(3) |
; |
$fbb |
->expects($this->exactly(2)) |
->method('setValue') |
->with('fbb') |
; |
$registry->set('foo[2]', 2); |
$registry->set('foo[3]', 3); |
$registry->set('foo[bar][baz]', 'fbb'); |
$registry->set('foo', array( |
2 => 2, |
3 => 3, |
'bar' => array( |
'baz' => 'fbb', |
), |
)); |
} |
/** |
* @expectedException \InvalidArgumentException |
* @expectedExceptionMessage Cannot set value on a compound field "foo[bar]". |
*/ |
public function testFormRegistrySetValueOnCompoundField() |
{ |
$registry = new FormFieldRegistry(); |
$registry->add($this->getFormFieldMock('foo[bar][baz]')); |
$registry->set('foo[bar]', 'fbb'); |
} |
/** |
* @expectedException \InvalidArgumentException |
* @expectedExceptionMessage Unreachable field "0" |
*/ |
public function testFormRegistrySetArrayOnNotCompoundField() |
{ |
$registry = new FormFieldRegistry(); |
$registry->add($this->getFormFieldMock('bar')); |
$registry->set('bar', array('baz')); |
} |
public function testDifferentFieldTypesWithSameName() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(' |
<html> |
<body> |
<form action="/"> |
<input type="hidden" name="option" value="default"> |
<input type="radio" name="option" value="A"> |
<input type="radio" name="option" value="B"> |
<input type="hidden" name="settings[1]" value="0"> |
<input type="checkbox" name="settings[1]" value="1" id="setting-1"> |
<button>klickme</button> |
</form> |
</body> |
</html> |
'); |
$form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com'); |
$this->assertInstanceOf('Symfony\Component\DomCrawler\Field\ChoiceFormField', $form->get('option')); |
} |
protected function getFormFieldMock($name, $value = null) |
{ |
$field = $this |
->getMockBuilder('Symfony\\Component\\DomCrawler\\Field\\FormField') |
->setMethods(array('getName', 'getValue', 'setValue', 'initialize')) |
->disableOriginalConstructor() |
->getMock() |
; |
$field |
->expects($this->any()) |
->method('getName') |
->will($this->returnValue($name)) |
; |
$field |
->expects($this->any()) |
->method('getValue') |
->will($this->returnValue($value)) |
; |
return $field; |
} |
protected function createForm($form, $method = null, $currentUri = null) |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML('<html>'.$form.'</html>'); |
$xPath = new \DOMXPath($dom); |
$nodes = $xPath->query('//input | //button'); |
if (null === $currentUri) { |
$currentUri = 'http://example.com/'; |
} |
return new Form($nodes->item($nodes->length - 1), $currentUri, $method); |
} |
protected function createTestHtml5Form() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(' |
<html> |
<h1>Hello form</h1> |
<form id="form-1" action="" method="POST"> |
<div><input type="checkbox" name="apples[]" value="1" checked /></div> |
<input form="form_2" type="checkbox" name="oranges[]" value="1" checked /> |
<div><label></label><input form="form-1" type="hidden" name="form_name" value="form-1" /></div> |
<input form="form-1" type="submit" name="button_1" value="Capture fields" /> |
<button form="form_2" type="submit" name="button_2">Submit form_2</button> |
</form> |
<input form="form-1" type="checkbox" name="apples[]" value="2" checked /> |
<form id="form_2" action="" method="POST"> |
<div><div><input type="checkbox" name="oranges[]" value="2" checked /> |
<input type="checkbox" name="oranges[]" value="3" checked /></div></div> |
<input form="form_2" type="hidden" name="form_name" value="form_2" /> |
<input form="form-1" type="hidden" name="outer_field" value="success" /> |
<button form="form-1" type="submit" name="button_3">Submit from outside the form</button> |
<div> |
<label for="app_frontend_form_type_contact_form_type_contactType">Message subject</label> |
<div> |
<select name="app_frontend_form_type_contact_form_type[contactType]" id="app_frontend_form_type_contact_form_type_contactType"><option selected="selected" value="">Please select subject</option><option id="1">Test type</option></select> |
</div> |
</div> |
<div> |
<label for="app_frontend_form_type_contact_form_type_firstName">Firstname</label> |
<input type="text" name="app_frontend_form_type_contact_form_type[firstName]" value="John" id="app_frontend_form_type_contact_form_type_firstName"/> |
</div> |
</form> |
<button /> |
</html>'); |
return $dom; |
} |
protected function createTestMultipleForm() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(' |
<html> |
<h1>Hello form</h1> |
<form action="" method="POST"> |
<div><input type="checkbox" name="apples[]" value="1" checked /></div> |
<input type="checkbox" name="oranges[]" value="1" checked /> |
<div><label></label><input type="hidden" name="form_name" value="form-1" /></div> |
<input type="submit" name="button_1" value="Capture fields" /> |
<button type="submit" name="button_2">Submit form_2</button> |
</form> |
<form action="" method="POST"> |
<div><div><input type="checkbox" name="oranges[]" value="2" checked /> |
<input type="checkbox" name="oranges[]" value="3" checked /></div></div> |
<input type="hidden" name="form_name" value="form_2" /> |
<input type="hidden" name="outer_field" value="success" /> |
<button type="submit" name="button_3">Submit from outside the form</button> |
</form> |
<button /> |
</html>'); |
return $dom; |
} |
public function testgetPhpValuesWithEmptyTextarea() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(' |
<html> |
<form> |
<textarea name="example"></textarea> |
</form> |
</html> |
'); |
$nodes = $dom->getElementsByTagName('form'); |
$form = new Form($nodes->item(0), 'http://example.com'); |
$this->assertEquals($form->getPhpValues(), array('example' => '')); |
} |
} |
/vendor/symfony/dom-crawler/Tests/LinkTest.php |
---|
@@ -0,0 +1,161 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Component\DomCrawler\Tests; |
use PHPUnit\Framework\TestCase; |
use Symfony\Component\DomCrawler\Link; |
class LinkTest extends TestCase |
{ |
/** |
* @expectedException \LogicException |
*/ |
public function testConstructorWithANonATag() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML('<html><div><div></html>'); |
new Link($dom->getElementsByTagName('div')->item(0), 'http://www.example.com/'); |
} |
/** |
* @expectedException \InvalidArgumentException |
*/ |
public function testConstructorWithAnInvalidCurrentUri() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML('<html><a href="/foo">foo</a></html>'); |
new Link($dom->getElementsByTagName('a')->item(0), 'example.com'); |
} |
public function testGetNode() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML('<html><a href="/foo">foo</a></html>'); |
$node = $dom->getElementsByTagName('a')->item(0); |
$link = new Link($node, 'http://example.com/'); |
$this->assertEquals($node, $link->getNode(), '->getNode() returns the node associated with the link'); |
} |
public function testGetMethod() |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML('<html><a href="/foo">foo</a></html>'); |
$node = $dom->getElementsByTagName('a')->item(0); |
$link = new Link($node, 'http://example.com/'); |
$this->assertEquals('GET', $link->getMethod(), '->getMethod() returns the method of the link'); |
$link = new Link($node, 'http://example.com/', 'post'); |
$this->assertEquals('POST', $link->getMethod(), '->getMethod() returns the method of the link'); |
} |
/** |
* @dataProvider getGetUriTests |
*/ |
public function testGetUri($url, $currentUri, $expected) |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(sprintf('<html><a href="%s">foo</a></html>', $url)); |
$link = new Link($dom->getElementsByTagName('a')->item(0), $currentUri); |
$this->assertEquals($expected, $link->getUri()); |
} |
/** |
* @dataProvider getGetUriTests |
*/ |
public function testGetUriOnArea($url, $currentUri, $expected) |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(sprintf('<html><map><area href="%s" /></map></html>', $url)); |
$link = new Link($dom->getElementsByTagName('area')->item(0), $currentUri); |
$this->assertEquals($expected, $link->getUri()); |
} |
/** |
* @dataProvider getGetUriTests |
*/ |
public function testGetUriOnLink($url, $currentUri, $expected) |
{ |
$dom = new \DOMDocument(); |
$dom->loadHTML(sprintf('<html><head><link href="%s" /></head></html>', $url)); |
$link = new Link($dom->getElementsByTagName('link')->item(0), $currentUri); |
$this->assertEquals($expected, $link->getUri()); |
} |
public function getGetUriTests() |
{ |
return array( |
array('/foo', 'http://localhost/bar/foo/', 'http://localhost/foo'), |
array('/foo', 'http://localhost/bar/foo', 'http://localhost/foo'), |
array(' |
/foo', 'http://localhost/bar/foo/', 'http://localhost/foo'), |
array('/foo |
', 'http://localhost/bar/foo', 'http://localhost/foo'), |
array('foo', 'http://localhost/bar/foo/', 'http://localhost/bar/foo/foo'), |
array('foo', 'http://localhost/bar/foo', 'http://localhost/bar/foo'), |
array('', 'http://localhost/bar/', 'http://localhost/bar/'), |
array('#', 'http://localhost/bar/', 'http://localhost/bar/#'), |
array('#bar', 'http://localhost/bar?a=b', 'http://localhost/bar?a=b#bar'), |
array('#bar', 'http://localhost/bar/#foo', 'http://localhost/bar/#bar'), |
array('?a=b', 'http://localhost/bar#foo', 'http://localhost/bar?a=b'), |
array('?a=b', 'http://localhost/bar/', 'http://localhost/bar/?a=b'), |
array('http://login.foo.com/foo', 'http://localhost/bar/', 'http://login.foo.com/foo'), |
array('https://login.foo.com/foo', 'https://localhost/bar/', 'https://login.foo.com/foo'), |
array('mailto:foo@bar.com', 'http://localhost/foo', 'mailto:foo@bar.com'), |
// tests schema relative URL (issue #7169) |
array('//login.foo.com/foo', 'http://localhost/bar/', 'http://login.foo.com/foo'), |
array('//login.foo.com/foo', 'https://localhost/bar/', 'https://login.foo.com/foo'), |
array('?foo=2', 'http://localhost?foo=1', 'http://localhost?foo=2'), |
array('?foo=2', 'http://localhost/?foo=1', 'http://localhost/?foo=2'), |
array('?foo=2', 'http://localhost/bar?foo=1', 'http://localhost/bar?foo=2'), |
array('?foo=2', 'http://localhost/bar/?foo=1', 'http://localhost/bar/?foo=2'), |
array('?bar=2', 'http://localhost?foo=1', 'http://localhost?bar=2'), |
array('foo', 'http://login.foo.com/bar/baz?/query/string', 'http://login.foo.com/bar/foo'), |
array('.', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/'), |
array('./', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/'), |
array('./foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/foo'), |
array('..', 'http://localhost/foo/bar/baz', 'http://localhost/foo/'), |
array('../', 'http://localhost/foo/bar/baz', 'http://localhost/foo/'), |
array('../foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo/foo'), |
array('../..', 'http://localhost/foo/bar/baz', 'http://localhost/'), |
array('../../', 'http://localhost/foo/bar/baz', 'http://localhost/'), |
array('../../foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo'), |
array('../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'), |
array('../bar/../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'), |
array('../bar/./../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'), |
array('../../', 'http://localhost/', 'http://localhost/'), |
array('../../', 'http://localhost', 'http://localhost/'), |
array('/foo', 'http://localhost?bar=1', 'http://localhost/foo'), |
array('/foo', 'http://localhost#bar', 'http://localhost/foo'), |
array('/foo', 'file:///', 'file:///foo'), |
array('/foo', 'file:///bar/baz', 'file:///foo'), |
array('foo', 'file:///', 'file:///foo'), |
array('foo', 'file:///bar/baz', 'file:///bar/foo'), |
); |
} |
} |
/vendor/symfony/dom-crawler/composer.json |
---|
@@ -0,0 +1,40 @@ |
{ |
"name": "symfony/dom-crawler", |
"type": "library", |
"description": "Symfony DomCrawler Component", |
"keywords": [], |
"homepage": "https://symfony.com", |
"license": "MIT", |
"authors": [ |
{ |
"name": "Fabien Potencier", |
"email": "fabien@symfony.com" |
}, |
{ |
"name": "Symfony Community", |
"homepage": "https://symfony.com/contributors" |
} |
], |
"require": { |
"php": ">=5.3.9", |
"symfony/polyfill-mbstring": "~1.0" |
}, |
"require-dev": { |
"symfony/css-selector": "~2.8|~3.0.0" |
}, |
"suggest": { |
"symfony/css-selector": "" |
}, |
"autoload": { |
"psr-4": { "Symfony\\Component\\DomCrawler\\": "" }, |
"exclude-from-classmap": [ |
"/Tests/" |
] |
}, |
"minimum-stability": "dev", |
"extra": { |
"branch-alias": { |
"dev-master": "2.8-dev" |
} |
} |
} |
/vendor/symfony/dom-crawler/phpunit.xml.dist |
---|
@@ -0,0 +1,29 @@ |
<?xml version="1.0" encoding="UTF-8"?> |
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd" |
backupGlobals="false" |
colors="true" |
bootstrap="vendor/autoload.php" |
> |
<php> |
<ini name="error_reporting" value="-1" /> |
</php> |
<testsuites> |
<testsuite name="Symfony DomCrawler Component Test Suite"> |
<directory>./Tests/</directory> |
</testsuite> |
</testsuites> |
<filter> |
<whitelist> |
<directory>./</directory> |
<exclude> |
<directory>./Resources</directory> |
<directory>./Tests</directory> |
<directory>./vendor</directory> |
</exclude> |
</whitelist> |
</filter> |
</phpunit> |
/vendor/symfony/polyfill-mbstring/LICENSE |
---|
@@ -0,0 +1,19 @@ |
Copyright (c) 2014-2016 Fabien Potencier |
Permission is hereby granted, free of charge, to any person obtaining a copy |
of this software and associated documentation files (the "Software"), to deal |
in the Software without restriction, including without limitation the rights |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
copies of the Software, and to permit persons to whom the Software is furnished |
to do so, subject to the following conditions: |
The above copyright notice and this permission notice shall be included in all |
copies or substantial portions of the Software. |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
THE SOFTWARE. |
/vendor/symfony/polyfill-mbstring/Mbstring.php |
---|
@@ -0,0 +1,650 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
namespace Symfony\Polyfill\Mbstring; |
/** |
* Partial mbstring implementation in PHP, iconv based, UTF-8 centric. |
* |
* Implemented: |
* - mb_chr - Returns a specific character from its Unicode code point |
* - mb_convert_encoding - Convert character encoding |
* - mb_convert_variables - Convert character code in variable(s) |
* - mb_decode_mimeheader - Decode string in MIME header field |
* - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED |
* - mb_convert_case - Perform case folding on a string |
* - mb_get_info - Get internal settings of mbstring |
* - mb_http_input - Detect HTTP input character encoding |
* - mb_http_output - Set/Get HTTP output character encoding |
* - mb_internal_encoding - Set/Get internal character encoding |
* - mb_list_encodings - Returns an array of all supported encodings |
* - mb_ord - Returns the Unicode code point of a character |
* - mb_output_handler - Callback function converts character encoding in output buffer |
* - mb_scrub - Replaces ill-formed byte sequences with substitute characters |
* - mb_strlen - Get string length |
* - mb_strpos - Find position of first occurrence of string in a string |
* - mb_strrpos - Find position of last occurrence of a string in a string |
* - mb_strtolower - Make a string lowercase |
* - mb_strtoupper - Make a string uppercase |
* - mb_substitute_character - Set/Get substitution character |
* - mb_substr - Get part of string |
* - mb_stripos - Finds position of first occurrence of a string within another, case insensitive |
* - mb_stristr - Finds first occurrence of a string within another, case insensitive |
* - mb_strrchr - Finds the last occurrence of a character in a string within another |
* - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive |
* - mb_strripos - Finds position of last occurrence of a string within another, case insensitive |
* - mb_strstr - Finds first occurrence of a string within anothers |
* - mb_strwidth - Return width of string |
* - mb_substr_count - Count the number of substring occurrences |
* |
* Not implemented: |
* - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) |
* - mb_decode_numericentity - Decode HTML numeric string reference to character |
* - mb_encode_numericentity - Encode character to HTML numeric string reference |
* - mb_ereg_* - Regular expression with multibyte support |
* - mb_parse_str - Parse GET/POST/COOKIE data and set global variable |
* - mb_preferred_mime_name - Get MIME charset string |
* - mb_regex_encoding - Returns current encoding for multibyte regex as string |
* - mb_regex_set_options - Set/Get the default options for mbregex functions |
* - mb_send_mail - Send encoded mail |
* - mb_split - Split multibyte string using regular expression |
* - mb_strcut - Get part of string |
* - mb_strimwidth - Get truncated string with specified width |
* |
* @author Nicolas Grekas <p@tchwork.com> |
* |
* @internal |
*/ |
final class Mbstring |
{ |
const MB_CASE_FOLD = PHP_INT_MAX; |
private static $encodingList = array('ASCII', 'UTF-8'); |
private static $language = 'neutral'; |
private static $internalEncoding = 'UTF-8'; |
private static $caseFold = array( |
array('µ','ſ',"\xCD\x85",'ς',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"), |
array('μ','s','ι', 'σ','β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1",'ι'), |
); |
public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) |
{ |
if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { |
$fromEncoding = self::mb_detect_encoding($s, $fromEncoding); |
} else { |
$fromEncoding = self::getEncoding($fromEncoding); |
} |
$toEncoding = self::getEncoding($toEncoding); |
if ('BASE64' === $fromEncoding) { |
$s = base64_decode($s); |
$fromEncoding = $toEncoding; |
} |
if ('BASE64' === $toEncoding) { |
return base64_encode($s); |
} |
if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { |
if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { |
$fromEncoding = 'Windows-1252'; |
} |
if ('UTF-8' !== $fromEncoding) { |
$s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); |
} |
return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); |
} |
if ('HTML-ENTITIES' === $fromEncoding) { |
$s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); |
$fromEncoding = 'UTF-8'; |
} |
return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); |
} |
public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) |
{ |
$vars = array(&$a, &$b, &$c, &$d, &$e, &$f); |
$ok = true; |
array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { |
if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { |
$ok = false; |
} |
}); |
return $ok ? $fromEncoding : false; |
} |
public static function mb_decode_mimeheader($s) |
{ |
return iconv_mime_decode($s, 2, self::$internalEncoding); |
} |
public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) |
{ |
trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); |
} |
public static function mb_convert_case($s, $mode, $encoding = null) |
{ |
if ('' === $s .= '') { |
return ''; |
} |
$encoding = self::getEncoding($encoding); |
if ('UTF-8' === $encoding) { |
$encoding = null; |
} else { |
$s = iconv($encoding, 'UTF-8//IGNORE', $s); |
} |
if (MB_CASE_TITLE == $mode) { |
$s = preg_replace_callback('/\b\p{Ll}/u', array(__CLASS__, 'title_case_upper'), $s); |
$s = preg_replace_callback('/\B[\p{Lu}\p{Lt}]+/u', array(__CLASS__, 'title_case_lower'), $s); |
} else { |
if (MB_CASE_UPPER == $mode) { |
static $upper = null; |
if (null === $upper) { |
$upper = self::getData('upperCase'); |
} |
$map = $upper; |
} else { |
if (self::MB_CASE_FOLD === $mode) { |
$s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); |
} |
static $lower = null; |
if (null === $lower) { |
$lower = self::getData('lowerCase'); |
} |
$map = $lower; |
} |
static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); |
$i = 0; |
$len = strlen($s); |
while ($i < $len) { |
$ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; |
$uchr = substr($s, $i, $ulen); |
$i += $ulen; |
if (isset($map[$uchr])) { |
$uchr = $map[$uchr]; |
$nlen = strlen($uchr); |
if ($nlen == $ulen) { |
$nlen = $i; |
do { |
$s[--$nlen] = $uchr[--$ulen]; |
} while ($ulen); |
} else { |
$s = substr_replace($s, $uchr, $i - $ulen, $ulen); |
$len += $nlen - $ulen; |
$i += $nlen - $ulen; |
} |
} |
} |
} |
if (null === $encoding) { |
return $s; |
} |
return iconv('UTF-8', $encoding.'//IGNORE', $s); |
} |
public static function mb_internal_encoding($encoding = null) |
{ |
if (null === $encoding) { |
return self::$internalEncoding; |
} |
$encoding = self::getEncoding($encoding); |
if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) { |
self::$internalEncoding = $encoding; |
return true; |
} |
return false; |
} |
public static function mb_language($lang = null) |
{ |
if (null === $lang) { |
return self::$language; |
} |
switch ($lang = strtolower($lang)) { |
case 'uni': |
case 'neutral': |
self::$language = $lang; |
return true; |
} |
return false; |
} |
public static function mb_list_encodings() |
{ |
return array('UTF-8'); |
} |
public static function mb_encoding_aliases($encoding) |
{ |
switch (strtoupper($encoding)) { |
case 'UTF8': |
case 'UTF-8': |
return array('utf8'); |
} |
return false; |
} |
public static function mb_check_encoding($var = null, $encoding = null) |
{ |
if (null === $encoding) { |
if (null === $var) { |
return false; |
} |
$encoding = self::$internalEncoding; |
} |
return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var); |
} |
public static function mb_detect_encoding($str, $encodingList = null, $strict = false) |
{ |
if (null === $encodingList) { |
$encodingList = self::$encodingList; |
} else { |
if (!is_array($encodingList)) { |
$encodingList = array_map('trim', explode(',', $encodingList)); |
} |
$encodingList = array_map('strtoupper', $encodingList); |
} |
foreach ($encodingList as $enc) { |
switch ($enc) { |
case 'ASCII': |
if (!preg_match('/[\x80-\xFF]/', $str)) { |
return $enc; |
} |
break; |
case 'UTF8': |
case 'UTF-8': |
if (preg_match('//u', $str)) { |
return 'UTF-8'; |
} |
break; |
default: |
if (0 === strncmp($enc, 'ISO-8859-', 9)) { |
return $enc; |
} |
} |
} |
return false; |
} |
public static function mb_detect_order($encodingList = null) |
{ |
if (null === $encodingList) { |
return self::$encodingList; |
} |
if (!is_array($encodingList)) { |
$encodingList = array_map('trim', explode(',', $encodingList)); |
} |
$encodingList = array_map('strtoupper', $encodingList); |
foreach ($encodingList as $enc) { |
switch ($enc) { |
default: |
if (strncmp($enc, 'ISO-8859-', 9)) { |
return false; |
} |
case 'ASCII': |
case 'UTF8': |
case 'UTF-8': |
} |
} |
self::$encodingList = $encodingList; |
return true; |
} |
public static function mb_strlen($s, $encoding = null) |
{ |
switch ($encoding = self::getEncoding($encoding)) { |
case 'ASCII': |
case 'CP850': |
return strlen($s); |
} |
return @iconv_strlen($s, $encoding); |
} |
public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) |
{ |
$encoding = self::getEncoding($encoding); |
if ('' === $needle .= '') { |
trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); |
return false; |
} |
return iconv_strpos($haystack, $needle, $offset, $encoding); |
} |
public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) |
{ |
$encoding = self::getEncoding($encoding); |
if ($offset != (int) $offset) { |
$offset = 0; |
} elseif ($offset = (int) $offset) { |
if ($offset < 0) { |
$haystack = self::mb_substr($haystack, 0, $offset, $encoding); |
$offset = 0; |
} else { |
$haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); |
} |
} |
$pos = iconv_strrpos($haystack, $needle, $encoding); |
return false !== $pos ? $offset + $pos : false; |
} |
public static function mb_strtolower($s, $encoding = null) |
{ |
return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); |
} |
public static function mb_strtoupper($s, $encoding = null) |
{ |
return self::mb_convert_case($s, MB_CASE_UPPER, $encoding); |
} |
public static function mb_substitute_character($c = null) |
{ |
if (0 === strcasecmp($c, 'none')) { |
return true; |
} |
return null !== $c ? false : 'none'; |
} |
public static function mb_substr($s, $start, $length = null, $encoding = null) |
{ |
$encoding = self::getEncoding($encoding); |
if ($start < 0) { |
$start = iconv_strlen($s, $encoding) + $start; |
if ($start < 0) { |
$start = 0; |
} |
} |
if (null === $length) { |
$length = 2147483647; |
} elseif ($length < 0) { |
$length = iconv_strlen($s, $encoding) + $length - $start; |
if ($length < 0) { |
return ''; |
} |
} |
return iconv_substr($s, $start, $length, $encoding).''; |
} |
public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) |
{ |
$haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); |
$needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); |
return self::mb_strpos($haystack, $needle, $offset, $encoding); |
} |
public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) |
{ |
$pos = self::mb_stripos($haystack, $needle, 0, $encoding); |
return self::getSubpart($pos, $part, $haystack, $encoding); |
} |
public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) |
{ |
$encoding = self::getEncoding($encoding); |
$needle = self::mb_substr($needle, 0, 1, $encoding); |
$pos = iconv_strrpos($haystack, $needle, $encoding); |
return self::getSubpart($pos, $part, $haystack, $encoding); |
} |
public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) |
{ |
$needle = self::mb_substr($needle, 0, 1, $encoding); |
$pos = self::mb_strripos($haystack, $needle, $encoding); |
return self::getSubpart($pos, $part, $haystack, $encoding); |
} |
public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) |
{ |
$haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); |
$needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); |
return self::mb_strrpos($haystack, $needle, $offset, $encoding); |
} |
public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) |
{ |
$pos = strpos($haystack, $needle); |
if (false === $pos) { |
return false; |
} |
if ($part) { |
return substr($haystack, 0, $pos); |
} |
return substr($haystack, $pos); |
} |
public static function mb_get_info($type = 'all') |
{ |
$info = array( |
'internal_encoding' => self::$internalEncoding, |
'http_output' => 'pass', |
'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', |
'func_overload' => 0, |
'func_overload_list' => 'no overload', |
'mail_charset' => 'UTF-8', |
'mail_header_encoding' => 'BASE64', |
'mail_body_encoding' => 'BASE64', |
'illegal_chars' => 0, |
'encoding_translation' => 'Off', |
'language' => self::$language, |
'detect_order' => self::$encodingList, |
'substitute_character' => 'none', |
'strict_detection' => 'Off', |
); |
if ('all' === $type) { |
return $info; |
} |
if (isset($info[$type])) { |
return $info[$type]; |
} |
return false; |
} |
public static function mb_http_input($type = '') |
{ |
return false; |
} |
public static function mb_http_output($encoding = null) |
{ |
return null !== $encoding ? 'pass' === $encoding : 'pass'; |
} |
public static function mb_strwidth($s, $encoding = null) |
{ |
$encoding = self::getEncoding($encoding); |
if ('UTF-8' !== $encoding) { |
$s = iconv($encoding, 'UTF-8//IGNORE', $s); |
} |
$s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); |
return ($wide << 1) + iconv_strlen($s, 'UTF-8'); |
} |
public static function mb_substr_count($haystack, $needle, $encoding = null) |
{ |
return substr_count($haystack, $needle); |
} |
public static function mb_output_handler($contents, $status) |
{ |
return $contents; |
} |
public static function mb_chr($code, $encoding = null) |
{ |
if (0x80 > $code %= 0x200000) { |
$s = chr($code); |
} elseif (0x800 > $code) { |
$s = chr(0xC0 | $code >> 6).chr(0x80 | $code & 0x3F); |
} elseif (0x10000 > $code) { |
$s = chr(0xE0 | $code >> 12).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F); |
} else { |
$s = chr(0xF0 | $code >> 18).chr(0x80 | $code >> 12 & 0x3F).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F); |
} |
if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { |
$s = mb_convert_encoding($s, $encoding, 'UTF-8'); |
} |
return $s; |
} |
public static function mb_ord($s, $encoding = null) |
{ |
if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { |
$s = mb_convert_encoding($s, 'UTF-8', $encoding); |
} |
$code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; |
if (0xF0 <= $code) { |
return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; |
} |
if (0xE0 <= $code) { |
return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; |
} |
if (0xC0 <= $code) { |
return (($code - 0xC0) << 6) + $s[2] - 0x80; |
} |
return $code; |
} |
private static function getSubpart($pos, $part, $haystack, $encoding) |
{ |
if (false === $pos) { |
return false; |
} |
if ($part) { |
return self::mb_substr($haystack, 0, $pos, $encoding); |
} |
return self::mb_substr($haystack, $pos, null, $encoding); |
} |
private static function html_encoding_callback($m) |
{ |
$i = 1; |
$entities = ''; |
$m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8')); |
while (isset($m[$i])) { |
if (0x80 > $m[$i]) { |
$entities .= chr($m[$i++]); |
continue; |
} |
if (0xF0 <= $m[$i]) { |
$c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; |
} elseif (0xE0 <= $m[$i]) { |
$c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; |
} else { |
$c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; |
} |
$entities .= '&#'.$c.';'; |
} |
return $entities; |
} |
private static function title_case_lower($s) |
{ |
return self::mb_convert_case($s[0], MB_CASE_LOWER, 'UTF-8'); |
} |
private static function title_case_upper($s) |
{ |
return self::mb_convert_case($s[0], MB_CASE_UPPER, 'UTF-8'); |
} |
private static function getData($file) |
{ |
if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { |
return require $file; |
} |
return false; |
} |
private static function getEncoding($encoding) |
{ |
if (null === $encoding) { |
return self::$internalEncoding; |
} |
$encoding = strtoupper($encoding); |
if ('8BIT' === $encoding || 'BINARY' === $encoding) { |
return 'CP850'; |
} |
if ('UTF8' === $encoding) { |
return 'UTF-8'; |
} |
return $encoding; |
} |
} |
/vendor/symfony/polyfill-mbstring/README.md |
---|
@@ -0,0 +1,13 @@ |
Symfony Polyfill / Mbstring |
=========================== |
This component provides a partial, native PHP implementation for the |
[Mbstring](http://php.net/mbstring) extension. |
More information can be found in the |
[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). |
License |
======= |
This library is released under the [MIT license](LICENSE). |
/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php |
---|
@@ -0,0 +1,1101 @@ |
<?php |
static $data = array ( |
'A' => 'a', |
'B' => 'b', |
'C' => 'c', |
'D' => 'd', |
'E' => 'e', |
'F' => 'f', |
'G' => 'g', |
'H' => 'h', |
'I' => 'i', |
'J' => 'j', |
'K' => 'k', |
'L' => 'l', |
'M' => 'm', |
'N' => 'n', |
'O' => 'o', |
'P' => 'p', |
'Q' => 'q', |
'R' => 'r', |
'S' => 's', |
'T' => 't', |
'U' => 'u', |
'V' => 'v', |
'W' => 'w', |
'X' => 'x', |
'Y' => 'y', |
'Z' => 'z', |
'À' => 'à', |
'Á' => 'á', |
'Â' => 'â', |
'Ã' => 'ã', |
'Ä' => 'ä', |
'Å' => 'å', |
'Æ' => 'æ', |
'Ç' => 'ç', |
'È' => 'è', |
'É' => 'é', |
'Ê' => 'ê', |
'Ë' => 'ë', |
'Ì' => 'ì', |
'Í' => 'í', |
'Î' => 'î', |
'Ï' => 'ï', |
'Ð' => 'ð', |
'Ñ' => 'ñ', |
'Ò' => 'ò', |
'Ó' => 'ó', |
'Ô' => 'ô', |
'Õ' => 'õ', |
'Ö' => 'ö', |
'Ø' => 'ø', |
'Ù' => 'ù', |
'Ú' => 'ú', |
'Û' => 'û', |
'Ü' => 'ü', |
'Ý' => 'ý', |
'Þ' => 'þ', |
'Ä€' => 'Ä', |
'Ă' => 'ă', |
'Ą' => 'ą', |
'Ć' => 'ć', |
'Ĉ' => 'ĉ', |
'ÄŠ' => 'Ä‹', |
'Č' => 'č', |
'Ď' => 'ď', |
'Ä' => 'Ä‘', |
'Ä’' => 'Ä“', |
'Ä”' => 'Ä•', |
'Ä–' => 'Ä—', |
'Ę' => 'ę', |
'Ě' => 'ě', |
'Ĝ' => 'ĝ', |
'Äž' => 'ÄŸ', |
'Ä ' => 'Ä¡', |
'Ä¢' => 'Ä£', |
'Ĥ' => 'ĥ', |
'Ħ' => 'ħ', |
'Ĩ' => 'ĩ', |
'Ī' => 'ī', |
'Ĭ' => 'Ä', |
'Į' => 'į', |
'Ä°' => 'i', |
'IJ' => 'ij', |
'Ĵ' => 'ĵ', |
'Ķ' => 'ķ', |
'Ĺ' => 'ĺ', |
'Ļ' => 'ļ', |
'Ľ' => 'ľ', |
'Ä¿' => 'Å€', |
'Ł' => 'ł', |
'Ń' => 'ń', |
'Ņ' => 'ņ', |
'Ň' => 'ň', |
'ÅŠ' => 'Å‹', |
'Ō' => 'ō', |
'ÅŽ' => 'Å', |
'Ő' => 'ő', |
'Å’' => 'Å“', |
'Å”' => 'Å•', |
'Å–' => 'Å—', |
'Ř' => 'ř', |
'Ś' => 'ś', |
'Ŝ' => 'ŝ', |
'Åž' => 'ÅŸ', |
'Š' => 'š', |
'Å¢' => 'Å£', |
'Ť' => 'ť', |
'Ŧ' => 'ŧ', |
'Ũ' => 'ũ', |
'Ū' => 'ū', |
'Ŭ' => 'Å', |
'Ů' => 'ů', |
'Ű' => 'ű', |
'Ų' => 'ų', |
'Ŵ' => 'ŵ', |
'Ŷ' => 'ŷ', |
'Ÿ' => 'ÿ', |
'Ź' => 'ź', |
'Ż' => 'ż', |
'Ž' => 'ž', |
'Æ' => 'É“', |
'Ƃ' => 'ƃ', |
'Æ„' => 'Æ…', |
'Ɔ' => 'ɔ', |
'Ƈ' => 'ƈ', |
'Ɖ' => 'ɖ', |
'ÆŠ' => 'É—', |
'Ƌ' => 'ƌ', |
'ÆŽ' => 'Ç', |
'Ə' => 'ə', |
'Ɛ' => 'ɛ', |
'Æ‘' => 'Æ’', |
'Æ“' => 'É ', |
'Æ”' => 'É£', |
'Æ–' => 'É©', |
'Ɨ' => 'ɨ', |
'Ƙ' => 'ƙ', |
'Ɯ' => 'ɯ', |
'Æ' => 'ɲ', |
'Ɵ' => 'ɵ', |
'Æ ' => 'Æ¡', |
'Æ¢' => 'Æ£', |
'Ƥ' => 'ƥ', |
'Ʀ' => 'ʀ', |
'Ƨ' => 'ƨ', |
'Ʃ' => 'ʃ', |
'Ƭ' => 'Æ', |
'Ʈ' => 'ʈ', |
'Ư' => 'ư', |
'Ʊ' => 'ʊ', |
'Ʋ' => 'ʋ', |
'Ƴ' => 'ƴ', |
'Ƶ' => 'ƶ', |
'Æ·' => 'Ê’', |
'Ƹ' => 'ƹ', |
'Ƽ' => 'ƽ', |
'DŽ' => 'dž', |
'Dž' => 'dž', |
'LJ' => 'lj', |
'Lj' => 'lj', |
'NJ' => 'nj', |
'Nj' => 'nj', |
'Ç' => 'ÇŽ', |
'Ǐ' => 'ǐ', |
'Ç‘' => 'Ç’', |
'Ç“' => 'Ç”', |
'Ç•' => 'Ç–', |
'Ǘ' => 'ǘ', |
'Ç™' => 'Çš', |
'Ǜ' => 'ǜ', |
'Çž' => 'ÇŸ', |
'Ç ' => 'Ç¡', |
'Ç¢' => 'Ç£', |
'Ǥ' => 'ǥ', |
'Ǧ' => 'ǧ', |
'Ǩ' => 'ǩ', |
'Ǫ' => 'ǫ', |
'Ǭ' => 'Ç', |
'Ǯ' => 'ǯ', |
'DZ' => 'dz', |
'Dz' => 'dz', |
'Ǵ' => 'ǵ', |
'Ƕ' => 'ƕ', |
'Ç·' => 'Æ¿', |
'Ǹ' => 'ǹ', |
'Ǻ' => 'ǻ', |
'Ǽ' => 'ǽ', |
'Ǿ' => 'ǿ', |
'È€' => 'È', |
'Ȃ' => 'ȃ', |
'È„' => 'È…', |
'Ȇ' => 'ȇ', |
'Ȉ' => 'ȉ', |
'ÈŠ' => 'È‹', |
'Ȍ' => 'ȍ', |
'ÈŽ' => 'È', |
'È' => 'È‘', |
'È’' => 'È“', |
'È”' => 'È•', |
'È–' => 'È—', |
'Ș' => 'ș', |
'Èš' => 'È›', |
'Ȝ' => 'ȝ', |
'Èž' => 'ÈŸ', |
'È ' => 'Æž', |
'È¢' => 'È£', |
'Ȥ' => 'ȥ', |
'Ȧ' => 'ȧ', |
'Ȩ' => 'ȩ', |
'Ȫ' => 'ȫ', |
'Ȭ' => 'È', |
'Ȯ' => 'ȯ', |
'Ȱ' => 'ȱ', |
'Ȳ' => 'ȳ', |
'Ⱥ' => 'ⱥ', |
'Ȼ' => 'ȼ', |
'Ƚ' => 'ƚ', |
'Ⱦ' => 'ⱦ', |
'É' => 'É‚', |
'Ƀ' => 'ƀ', |
'Ʉ' => 'ʉ', |
'Ʌ' => 'ʌ', |
'Ɇ' => 'ɇ', |
'Ɉ' => 'ɉ', |
'ÉŠ' => 'É‹', |
'Ɍ' => 'ɍ', |
'ÉŽ' => 'É', |
'Ͱ' => 'ͱ', |
'Ͳ' => 'ͳ', |
'Ͷ' => 'ͷ', |
'Ϳ' => 'ϳ', |
'Ά' => 'ά', |
'Έ' => 'έ', |
'Ή' => 'ή', |
'Ί' => 'ί', |
'Ό' => 'ό', |
'Ύ' => 'ύ', |
'Ώ' => 'ώ', |
'Α' => 'α', |
'Β' => 'β', |
'Γ' => 'γ', |
'Δ' => 'δ', |
'Ε' => 'ε', |
'Ζ' => 'ζ', |
'Η' => 'η', |
'Θ' => 'θ', |
'Ι' => 'ι', |
'Κ' => 'κ', |
'Λ' => 'λ', |
'Μ' => 'μ', |
'Ν' => 'ν', |
'Ξ' => 'ξ', |
'Ο' => 'ο', |
'Π' => 'π', |
'Ρ' => 'ρ', |
'Σ' => 'σ', |
'Τ' => 'τ', |
'Υ' => 'υ', |
'Φ' => 'φ', |
'Χ' => 'χ', |
'Ψ' => 'ψ', |
'Ω' => 'ω', |
'Ϊ' => 'ϊ', |
'Ϋ' => 'ϋ', |
'Ϗ' => 'ϗ', |
'Ϙ' => 'ϙ', |
'Ϛ' => 'ϛ', |
'Ϝ' => 'ϝ', |
'Ϟ' => 'ϟ', |
'Ϡ' => 'ϡ', |
'Ϣ' => 'ϣ', |
'Ϥ' => 'ϥ', |
'Ϧ' => 'ϧ', |
'Ϩ' => 'ϩ', |
'Ϫ' => 'ϫ', |
'Ϭ' => 'ϭ', |
'Ϯ' => 'ϯ', |
'ϴ' => 'θ', |
'Ϸ' => 'ϸ', |
'Ϲ' => 'ϲ', |
'Ϻ' => 'ϻ', |
'Ͻ' => 'ͻ', |
'Ͼ' => 'ͼ', |
'Ͽ' => 'ͽ', |
'Ѐ' => 'ѐ', |
'Ё' => 'ё', |
'Ђ' => 'ђ', |
'Ѓ' => 'ѓ', |
'Є' => 'є', |
'Ѕ' => 'ѕ', |
'І' => 'і', |
'Ї' => 'ї', |
'Ј' => 'ј', |
'Љ' => 'љ', |
'Њ' => 'њ', |
'Ћ' => 'ћ', |
'Ќ' => 'ќ', |
'Ѝ' => 'ѝ', |
'Ў' => 'ў', |
'Џ' => 'џ', |
'А' => 'а', |
'Б' => 'б', |
'В' => 'в', |
'Г' => 'г', |
'Д' => 'д', |
'Е' => 'е', |
'Ж' => 'ж', |
'З' => 'з', |
'И' => 'и', |
'Й' => 'й', |
'К' => 'к', |
'Л' => 'л', |
'М' => 'м', |
'Н' => 'н', |
'О' => 'о', |
'П' => 'п', |
'Р' => 'р', |
'С' => 'с', |
'Т' => 'т', |
'У' => 'у', |
'Ф' => 'ф', |
'Х' => 'х', |
'Ц' => 'ц', |
'Ч' => 'ч', |
'Ш' => 'ш', |
'Щ' => 'щ', |
'Ъ' => 'ъ', |
'Ы' => 'ы', |
'Ь' => 'ь', |
'Э' => 'э', |
'Ю' => 'ю', |
'Я' => 'я', |
'Ѡ' => 'ѡ', |
'Ѣ' => 'ѣ', |
'Ѥ' => 'ѥ', |
'Ѧ' => 'ѧ', |
'Ѩ' => 'ѩ', |
'Ѫ' => 'ѫ', |
'Ѭ' => 'ѭ', |
'Ѯ' => 'ѯ', |
'Ѱ' => 'ѱ', |
'Ѳ' => 'ѳ', |
'Ѵ' => 'ѵ', |
'Ѷ' => 'ѷ', |
'Ѹ' => 'ѹ', |
'Ѻ' => 'ѻ', |
'Ѽ' => 'ѽ', |
'Ѿ' => 'ѿ', |
'Ҁ' => 'ҁ', |
'Ҋ' => 'ҋ', |
'Ҍ' => 'ҍ', |
'Ҏ' => 'ҏ', |
'Ґ' => 'ґ', |
'Ғ' => 'ғ', |
'Ҕ' => 'ҕ', |
'Җ' => 'җ', |
'Ҙ' => 'ҙ', |
'Қ' => 'қ', |
'Ҝ' => 'ҝ', |
'Ҟ' => 'ҟ', |
'Ҡ' => 'ҡ', |
'Ң' => 'ң', |
'Ҥ' => 'ҥ', |
'Ҧ' => 'ҧ', |
'Ҩ' => 'ҩ', |
'Ҫ' => 'ҫ', |
'Ҭ' => 'ҭ', |
'Ү' => 'ү', |
'Ұ' => 'ұ', |
'Ҳ' => 'ҳ', |
'Ҵ' => 'ҵ', |
'Ҷ' => 'ҷ', |
'Ҹ' => 'ҹ', |
'Һ' => 'һ', |
'Ҽ' => 'ҽ', |
'Ҿ' => 'ҿ', |
'Ӏ' => 'ӏ', |
'Ӂ' => 'ӂ', |
'Ӄ' => 'ӄ', |
'Ӆ' => 'ӆ', |
'Ӈ' => 'ӈ', |
'Ӊ' => 'ӊ', |
'Ӌ' => 'ӌ', |
'Ӎ' => 'ӎ', |
'Ӑ' => 'ӑ', |
'Ӓ' => 'ӓ', |
'Ӕ' => 'ӕ', |
'Ӗ' => 'ӗ', |
'Ә' => 'ә', |
'Ӛ' => 'ӛ', |
'Ӝ' => 'ӝ', |
'Ӟ' => 'ӟ', |
'Ӡ' => 'ӡ', |
'Ӣ' => 'ӣ', |
'Ӥ' => 'ӥ', |
'Ӧ' => 'ӧ', |
'Ө' => 'ө', |
'Ӫ' => 'ӫ', |
'Ӭ' => 'ӭ', |
'Ӯ' => 'ӯ', |
'Ӱ' => 'ӱ', |
'Ӳ' => 'ӳ', |
'Ӵ' => 'ӵ', |
'Ӷ' => 'ӷ', |
'Ӹ' => 'ӹ', |
'Ӻ' => 'ӻ', |
'Ӽ' => 'ӽ', |
'Ӿ' => 'ӿ', |
'Ô€' => 'Ô', |
'Ô‚' => 'Ôƒ', |
'Ô„' => 'Ô…', |
'Ô†' => 'Ô‡', |
'Ԉ' => 'ԉ', |
'ÔŠ' => 'Ô‹', |
'Ԍ' => 'ԍ', |
'ÔŽ' => 'Ô', |
'Ô' => 'Ô‘', |
'Ô’' => 'Ô“', |
'Ô”' => 'Ô•', |
'Ô–' => 'Ô—', |
'Ԙ' => 'ԙ', |
'Ôš' => 'Ô›', |
'Ԝ' => 'ԝ', |
'Ôž' => 'ÔŸ', |
'Ô ' => 'Ô¡', |
'Ô¢' => 'Ô£', |
'Ô¤' => 'Ô¥', |
'Ô¦' => 'Ô§', |
'Ô¨' => 'Ô©', |
'Ôª' => 'Ô«', |
'Ô¬' => 'Ô', |
'Ô®' => 'Ô¯', |
'Ô±' => 'Õ¡', |
'Ô²' => 'Õ¢', |
'Ô³' => 'Õ£', |
'Ô´' => 'Õ¤', |
'Ôµ' => 'Õ¥', |
'Ô¶' => 'Õ¦', |
'Ô·' => 'Õ§', |
'Ô¸' => 'Õ¨', |
'Ô¹' => 'Õ©', |
'Ôº' => 'Õª', |
'Ô»' => 'Õ«', |
'Ô¼' => 'Õ¬', |
'Ô½' => 'Õ', |
'Ô¾' => 'Õ®', |
'Ô¿' => 'Õ¯', |
'Õ€' => 'Õ°', |
'Õ' => 'Õ±', |
'Õ‚' => 'Õ²', |
'Õƒ' => 'Õ³', |
'Õ„' => 'Õ´', |
'Õ…' => 'Õµ', |
'Õ†' => 'Õ¶', |
'Õ‡' => 'Õ·', |
'Õˆ' => 'Õ¸', |
'Õ‰' => 'Õ¹', |
'ÕŠ' => 'Õº', |
'Õ‹' => 'Õ»', |
'Ռ' => 'ռ', |
'Õ' => 'Õ½', |
'ÕŽ' => 'Õ¾', |
'Õ' => 'Õ¿', |
'Õ' => 'Ö€', |
'Õ‘' => 'Ö', |
'Õ’' => 'Ö‚', |
'Õ“' => 'Öƒ', |
'Õ”' => 'Ö„', |
'Õ•' => 'Ö…', |
'Õ–' => 'Ö†', |
'á‚ ' => 'â´€', |
'á‚¡' => 'â´', |
'á‚¢' => 'â´‚', |
'á‚£' => 'â´ƒ', |
'Ⴄ' => 'ⴄ', |
'á‚¥' => 'â´…', |
'Ⴆ' => 'ⴆ', |
'Ⴇ' => 'ⴇ', |
'Ⴈ' => 'ⴈ', |
'á‚©' => 'â´‰', |
'Ⴊ' => 'ⴊ', |
'á‚«' => 'â´‹', |
'Ⴌ' => 'ⴌ', |
'Ⴍ' => 'ⴍ', |
'á‚®' => 'â´Ž', |
'Ⴏ' => 'â´', |
'á‚°' => 'â´', |
'Ⴑ' => 'ⴑ', |
'Ⴒ' => 'ⴒ', |
'Ⴓ' => 'ⴓ', |
'á‚´' => 'â´”', |
'Ⴕ' => 'ⴕ', |
'Ⴖ' => 'ⴖ', |
'á‚·' => 'â´—', |
'Ⴘ' => 'ⴘ', |
'Ⴙ' => 'ⴙ', |
'Ⴚ' => 'ⴚ', |
'á‚»' => 'â´›', |
'Ⴜ' => 'ⴜ', |
'Ⴝ' => 'â´', |
'Ⴞ' => 'ⴞ', |
'á‚¿' => 'â´Ÿ', |
'Ⴠ' => 'ⴠ', |
'Ⴡ' => 'ⴡ', |
'Ⴢ' => 'ⴢ', |
'Ⴣ' => 'ⴣ', |
'Ⴤ' => 'ⴤ', |
'Ⴥ' => 'ⴥ', |
'Ⴧ' => 'ⴧ', |
'Ⴭ' => 'ⴭ', |
'Ḁ' => 'á¸', |
'Ḃ' => 'ḃ', |
'Ḅ' => 'ḅ', |
'Ḇ' => 'ḇ', |
'Ḉ' => 'ḉ', |
'Ḋ' => 'ḋ', |
'Ḍ' => 'ḍ', |
'Ḏ' => 'á¸', |
'á¸' => 'ḑ', |
'Ḓ' => 'ḓ', |
'Ḕ' => 'ḕ', |
'Ḗ' => 'ḗ', |
'Ḙ' => 'ḙ', |
'Ḛ' => 'ḛ', |
'Ḝ' => 'ḝ', |
'Ḟ' => 'ḟ', |
'Ḡ' => 'ḡ', |
'Ḣ' => 'ḣ', |
'Ḥ' => 'ḥ', |
'Ḧ' => 'ḧ', |
'Ḩ' => 'ḩ', |
'Ḫ' => 'ḫ', |
'Ḭ' => 'á¸', |
'Ḯ' => 'ḯ', |
'Ḱ' => 'ḱ', |
'Ḳ' => 'ḳ', |
'Ḵ' => 'ḵ', |
'Ḷ' => 'ḷ', |
'Ḹ' => 'ḹ', |
'Ḻ' => 'ḻ', |
'Ḽ' => 'ḽ', |
'Ḿ' => 'ḿ', |
'á¹€' => 'á¹', |
'Ṃ' => 'ṃ', |
'Ṅ' => 'ṅ', |
'Ṇ' => 'ṇ', |
'Ṉ' => 'ṉ', |
'Ṋ' => 'ṋ', |
'Ṍ' => 'ṍ', |
'Ṏ' => 'á¹', |
'á¹' => 'ṑ', |
'Ṓ' => 'ṓ', |
'Ṕ' => 'ṕ', |
'á¹–' => 'á¹—', |
'Ṙ' => 'ṙ', |
'Ṛ' => 'ṛ', |
'Ṝ' => 'ṝ', |
'Ṟ' => 'ṟ', |
'Ṡ' => 'ṡ', |
'á¹¢' => 'á¹£', |
'Ṥ' => 'ṥ', |
'Ṧ' => 'ṧ', |
'Ṩ' => 'ṩ', |
'Ṫ' => 'ṫ', |
'Ṭ' => 'á¹', |
'Ṯ' => 'ṯ', |
'á¹°' => 'á¹±', |
'á¹²' => 'á¹³', |
'á¹´' => 'á¹µ', |
'Ṷ' => 'ṷ', |
'Ṹ' => 'ṹ', |
'Ṻ' => 'ṻ', |
'á¹¼' => 'á¹½', |
'Ṿ' => 'ṿ', |
'Ẁ' => 'áº', |
'Ẃ' => 'ẃ', |
'Ẅ' => 'ẅ', |
'Ẇ' => 'ẇ', |
'Ẉ' => 'ẉ', |
'Ẋ' => 'ẋ', |
'Ẍ' => 'ẍ', |
'Ẏ' => 'áº', |
'áº' => 'ẑ', |
'Ẓ' => 'ẓ', |
'Ẕ' => 'ẕ', |
'ẞ' => 'ß', |
'Ạ' => 'ạ', |
'Ả' => 'ả', |
'Ấ' => 'ấ', |
'Ầ' => 'ầ', |
'Ẩ' => 'ẩ', |
'Ẫ' => 'ẫ', |
'Ậ' => 'áº', |
'Ắ' => 'ắ', |
'Ằ' => 'ằ', |
'Ẳ' => 'ẳ', |
'Ẵ' => 'ẵ', |
'Ặ' => 'ặ', |
'Ẹ' => 'ẹ', |
'Ẻ' => 'ẻ', |
'Ẽ' => 'ẽ', |
'Ế' => 'ế', |
'Ề' => 'á»', |
'Ể' => 'ể', |
'Ễ' => 'ễ', |
'Ệ' => 'ệ', |
'Ỉ' => 'ỉ', |
'Ị' => 'ị', |
'Ọ' => 'ọ', |
'Ỏ' => 'á»', |
'á»' => 'ố', |
'Ồ' => 'ồ', |
'Ổ' => 'ổ', |
'á»–' => 'á»—', |
'Ộ' => 'ộ', |
'Ớ' => 'ớ', |
'Ờ' => 'ờ', |
'Ở' => 'ở', |
'Ỡ' => 'ỡ', |
'Ợ' => 'ợ', |
'Ụ' => 'ụ', |
'Ủ' => 'ủ', |
'Ứ' => 'ứ', |
'Ừ' => 'ừ', |
'Ử' => 'á»', |
'Ữ' => 'ữ', |
'á»°' => 'á»±', |
'Ỳ' => 'ỳ', |
'Ỵ' => 'ỵ', |
'Ỷ' => 'ỷ', |
'Ỹ' => 'ỹ', |
'Ỻ' => 'ỻ', |
'Ỽ' => 'ỽ', |
'Ỿ' => 'ỿ', |
'Ἀ' => 'ἀ', |
'Ἁ' => 'ἁ', |
'Ἂ' => 'ἂ', |
'Ἃ' => 'ἃ', |
'Ἄ' => 'ἄ', |
'á¼' => 'á¼…', |
'Ἆ' => 'ἆ', |
'á¼' => 'ἇ', |
'Ἐ' => 'ἐ', |
'Ἑ' => 'ἑ', |
'Ἒ' => 'ἒ', |
'Ἓ' => 'ἓ', |
'Ἔ' => 'ἔ', |
'á¼' => 'ἕ', |
'Ἠ' => 'ἠ', |
'Ἡ' => 'ἡ', |
'Ἢ' => 'ἢ', |
'Ἣ' => 'ἣ', |
'Ἤ' => 'ἤ', |
'á¼' => 'á¼¥', |
'Ἦ' => 'ἦ', |
'Ἧ' => 'ἧ', |
'Ἰ' => 'ἰ', |
'á¼¹' => 'á¼±', |
'Ἲ' => 'ἲ', |
'á¼»' => 'á¼³', |
'á¼¼' => 'á¼´', |
'á¼½' => 'á¼µ', |
'Ἶ' => 'ἶ', |
'Ἷ' => 'ἷ', |
'Ὀ' => 'ὀ', |
'Ὁ' => 'ὁ', |
'Ὂ' => 'ὂ', |
'Ὃ' => 'ὃ', |
'Ὄ' => 'ὄ', |
'á½' => 'á½…', |
'Ὑ' => 'ὑ', |
'Ὓ' => 'ὓ', |
'á½' => 'ὕ', |
'Ὗ' => 'ὗ', |
'Ὠ' => 'ὠ', |
'Ὡ' => 'ὡ', |
'Ὢ' => 'ὢ', |
'Ὣ' => 'ὣ', |
'Ὤ' => 'ὤ', |
'á½' => 'á½¥', |
'Ὦ' => 'ὦ', |
'Ὧ' => 'ὧ', |
'ᾈ' => 'ᾀ', |
'ᾉ' => 'ᾁ', |
'ᾊ' => 'ᾂ', |
'ᾋ' => 'ᾃ', |
'ᾌ' => 'ᾄ', |
'á¾' => 'á¾…', |
'ᾎ' => 'ᾆ', |
'á¾' => 'ᾇ', |
'ᾘ' => 'ᾐ', |
'ᾙ' => 'ᾑ', |
'ᾚ' => 'ᾒ', |
'ᾛ' => 'ᾓ', |
'ᾜ' => 'ᾔ', |
'á¾' => 'ᾕ', |
'ᾞ' => 'ᾖ', |
'ᾟ' => 'ᾗ', |
'ᾨ' => 'ᾠ', |
'ᾩ' => 'ᾡ', |
'ᾪ' => 'ᾢ', |
'ᾫ' => 'ᾣ', |
'ᾬ' => 'ᾤ', |
'á¾' => 'á¾¥', |
'ᾮ' => 'ᾦ', |
'ᾯ' => 'ᾧ', |
'Ᾰ' => 'ᾰ', |
'á¾¹' => 'á¾±', |
'Ὰ' => 'ὰ', |
'á¾»' => 'á½±', |
'á¾¼' => 'á¾³', |
'Ὲ' => 'ὲ', |
'Έ' => 'έ', |
'á¿Š' => 'á½´', |
'á¿‹' => 'á½µ', |
'ῌ' => 'ῃ', |
'Ῐ' => 'ῐ', |
'á¿™' => 'á¿‘', |
'Ὶ' => 'ὶ', |
'á¿›' => 'á½·', |
'Ῠ' => 'ῠ', |
'á¿©' => 'á¿¡', |
'Ὺ' => 'ὺ', |
'á¿«' => 'á½»', |
'Ῥ' => 'ῥ', |
'Ὸ' => 'ὸ', |
'Ό' => 'ό', |
'Ὼ' => 'ὼ', |
'á¿»' => 'á½½', |
'ῼ' => 'ῳ', |
'Ω' => 'ω', |
'K' => 'k', |
'â„«' => 'Ã¥', |
'Ⅎ' => 'ⅎ', |
'Ⅰ' => 'ⅰ', |
'â…¡' => 'â…±', |
'â…¢' => 'â…²', |
'â…£' => 'â…³', |
'â…¤' => 'â…´', |
'â…¥' => 'â…µ', |
'â…¦' => 'â…¶', |
'â…§' => 'â…·', |
'â…¨' => 'â…¸', |
'â…©' => 'â…¹', |
'â…ª' => 'â…º', |
'â…«' => 'â…»', |
'â…¬' => 'â…¼', |
'Ⅽ' => 'ⅽ', |
'â…®' => 'â…¾', |
'â…¯' => 'â…¿', |
'Ↄ' => 'ↄ', |
'â’¶' => 'â“', |
'â’·' => 'â“‘', |
'â’¸' => 'â“’', |
'â’¹' => 'â““', |
'â’º' => 'â“”', |
'â’»' => 'â“•', |
'â’¼' => 'â“–', |
'â’½' => 'â“—', |
'Ⓘ' => 'ⓘ', |
'â’¿' => 'â“™', |
'â“€' => 'â“š', |
'Ⓛ' => 'ⓛ', |
'Ⓜ' => 'ⓜ', |
'Ⓝ' => 'ⓝ', |
'â“„' => 'â“ž', |
'â“…' => 'â“Ÿ', |
'Ⓠ' => 'ⓠ', |
'Ⓡ' => 'ⓡ', |
'Ⓢ' => 'ⓢ', |
'Ⓣ' => 'ⓣ', |
'Ⓤ' => 'ⓤ', |
'â“‹' => 'â“¥', |
'Ⓦ' => 'ⓦ', |
'â“' => 'ⓧ', |
'Ⓨ' => 'ⓨ', |
'â“' => 'â“©', |
'â°€' => 'â°°', |
'â°' => 'â°±', |
'â°‚' => 'â°²', |
'â°ƒ' => 'â°³', |
'â°„' => 'â°´', |
'â°…' => 'â°µ', |
'â°†' => 'â°¶', |
'â°‡' => 'â°·', |
'â°ˆ' => 'â°¸', |
'â°‰' => 'â°¹', |
'â°Š' => 'â°º', |
'â°‹' => 'â°»', |
'Ⰼ' => 'ⰼ', |
'â°' => 'â°½', |
'â°Ž' => 'â°¾', |
'â°' => 'â°¿', |
'â°' => 'â±€', |
'â°‘' => 'â±', |
'Ⱂ' => 'ⱂ', |
'Ⱃ' => 'ⱃ', |
'Ⱄ' => 'ⱄ', |
'â°•' => 'â±…', |
'Ⱆ' => 'ⱆ', |
'Ⱇ' => 'ⱇ', |
'Ⱈ' => 'ⱈ', |
'Ⱉ' => 'ⱉ', |
'Ⱊ' => 'ⱊ', |
'Ⱋ' => 'ⱋ', |
'Ⱌ' => 'ⱌ', |
'Ⱍ' => 'ⱍ', |
'Ⱎ' => 'ⱎ', |
'Ⱏ' => 'ⱏ', |
'Ⱐ' => 'ⱐ', |
'Ⱑ' => 'ⱑ', |
'â°¢' => 'â±’', |
'Ⱓ' => 'ⱓ', |
'â°¤' => 'â±”', |
'Ⱕ' => 'ⱕ', |
'â°¦' => 'â±–', |
'â°§' => 'â±—', |
'Ⱘ' => 'ⱘ', |
'â°©' => 'â±™', |
'Ⱚ' => 'ⱚ', |
'â°«' => 'â±›', |
'Ⱜ' => 'ⱜ', |
'Ⱝ' => 'ⱝ', |
'Ⱞ' => 'ⱞ', |
'Ⱡ' => 'ⱡ', |
'â±¢' => 'É«', |
'â±£' => 'áµ½', |
'Ɽ' => 'ɽ', |
'Ⱨ' => 'ⱨ', |
'Ⱪ' => 'ⱪ', |
'Ⱬ' => 'ⱬ', |
'â±' => 'É‘', |
'Ɱ' => 'ɱ', |
'Ɐ' => 'É', |
'â±°' => 'É’', |
'â±²' => 'â±³', |
'Ⱶ' => 'ⱶ', |
'â±¾' => 'È¿', |
'Ɀ' => 'ɀ', |
'â²€' => 'â²', |
'Ⲃ' => 'ⲃ', |
'Ⲅ' => 'ⲅ', |
'Ⲇ' => 'ⲇ', |
'Ⲉ' => 'ⲉ', |
'Ⲋ' => 'ⲋ', |
'Ⲍ' => 'ⲍ', |
'Ⲏ' => 'â²', |
'â²' => 'ⲑ', |
'Ⲓ' => 'ⲓ', |
'Ⲕ' => 'ⲕ', |
'â²–' => 'â²—', |
'Ⲙ' => 'ⲙ', |
'Ⲛ' => 'ⲛ', |
'Ⲝ' => 'ⲝ', |
'Ⲟ' => 'ⲟ', |
'Ⲡ' => 'ⲡ', |
'â²¢' => 'â²£', |
'Ⲥ' => 'ⲥ', |
'Ⲧ' => 'ⲧ', |
'Ⲩ' => 'ⲩ', |
'Ⲫ' => 'ⲫ', |
'Ⲭ' => 'â²', |
'Ⲯ' => 'ⲯ', |
'â²°' => 'â²±', |
'â²²' => 'â²³', |
'â²´' => 'â²µ', |
'Ⲷ' => 'ⲷ', |
'Ⲹ' => 'ⲹ', |
'Ⲻ' => 'ⲻ', |
'â²¼' => 'â²½', |
'Ⲿ' => 'ⲿ', |
'â³€' => 'â³', |
'Ⳃ' => 'ⳃ', |
'Ⳅ' => 'ⳅ', |
'Ⳇ' => 'ⳇ', |
'Ⳉ' => 'ⳉ', |
'Ⳋ' => 'ⳋ', |
'Ⳍ' => 'ⳍ', |
'Ⳏ' => 'â³', |
'â³' => 'ⳑ', |
'Ⳓ' => 'ⳓ', |
'Ⳕ' => 'ⳕ', |
'â³–' => 'â³—', |
'Ⳙ' => 'ⳙ', |
'Ⳛ' => 'ⳛ', |
'Ⳝ' => 'ⳝ', |
'Ⳟ' => 'ⳟ', |
'Ⳡ' => 'ⳡ', |
'â³¢' => 'â³£', |
'Ⳬ' => 'ⳬ', |
'â³' => 'â³®', |
'â³²' => 'â³³', |
'Ꙁ' => 'ꙁ', |
'Ꙃ' => 'ꙃ', |
'Ꙅ' => 'ꙅ', |
'Ꙇ' => 'ꙇ', |
'Ꙉ' => 'ꙉ', |
'Ꙋ' => 'ꙋ', |
'Ꙍ' => 'ꙍ', |
'Ꙏ' => 'ꙏ', |
'Ꙑ' => 'ꙑ', |
'Ꙓ' => 'ꙓ', |
'Ꙕ' => 'ꙕ', |
'Ꙗ' => 'ꙗ', |
'Ꙙ' => 'ꙙ', |
'Ꙛ' => 'ꙛ', |
'Ꙝ' => 'ꙝ', |
'Ꙟ' => 'ꙟ', |
'Ꙡ' => 'ꙡ', |
'Ꙣ' => 'ꙣ', |
'Ꙥ' => 'ꙥ', |
'Ꙧ' => 'ꙧ', |
'Ꙩ' => 'ꙩ', |
'Ꙫ' => 'ꙫ', |
'Ꙭ' => 'ꙭ', |
'Ꚁ' => 'êš', |
'Ꚃ' => 'ꚃ', |
'êš„' => 'êš…', |
'Ꚇ' => 'ꚇ', |
'Ꚉ' => 'ꚉ', |
'Ꚋ' => 'ꚋ', |
'Ꚍ' => 'ꚍ', |
'Ꚏ' => 'êš', |
'êš' => 'êš‘', |
'êš’' => 'êš“', |
'êš”' => 'êš•', |
'êš–' => 'êš—', |
'Ꚙ' => 'ꚙ', |
'êšš' => 'êš›', |
'Ꜣ' => 'ꜣ', |
'Ꜥ' => 'ꜥ', |
'Ꜧ' => 'ꜧ', |
'Ꜩ' => 'ꜩ', |
'Ꜫ' => 'ꜫ', |
'Ꜭ' => 'ꜭ', |
'Ꜯ' => 'ꜯ', |
'Ꜳ' => 'ꜳ', |
'Ꜵ' => 'ꜵ', |
'Ꜷ' => 'ꜷ', |
'Ꜹ' => 'ꜹ', |
'Ꜻ' => 'ꜻ', |
'Ꜽ' => 'ꜽ', |
'Ꜿ' => 'ꜿ', |
'Ꝁ' => 'ꝁ', |
'Ꝃ' => 'ꝃ', |
'Ꝅ' => 'ꝅ', |
'Ꝇ' => 'ꝇ', |
'Ꝉ' => 'ꝉ', |
'Ꝋ' => 'ꝋ', |
'Ꝍ' => 'ꝍ', |
'Ꝏ' => 'ꝏ', |
'Ꝑ' => 'ꝑ', |
'Ꝓ' => 'ꝓ', |
'Ꝕ' => 'ꝕ', |
'Ꝗ' => 'ꝗ', |
'Ꝙ' => 'ꝙ', |
'Ꝛ' => 'ꝛ', |
'Ꝝ' => 'ꝝ', |
'Ꝟ' => 'ꝟ', |
'Ꝡ' => 'ꝡ', |
'Ꝣ' => 'ꝣ', |
'Ꝥ' => 'ꝥ', |
'Ꝧ' => 'ꝧ', |
'Ꝩ' => 'ꝩ', |
'Ꝫ' => 'ꝫ', |
'Ꝭ' => 'ꝭ', |
'Ꝯ' => 'ꝯ', |
'Ꝺ' => 'ꝺ', |
'Ꝼ' => 'ꝼ', |
'ê½' => 'áµ¹', |
'Ꝿ' => 'ꝿ', |
'Ꞁ' => 'êž', |
'Ꞃ' => 'ꞃ', |
'êž„' => 'êž…', |
'Ꞇ' => 'ꞇ', |
'Ꞌ' => 'ꞌ', |
'êž' => 'É¥', |
'êž' => 'êž‘', |
'êž’' => 'êž“', |
'êž–' => 'êž—', |
'Ꞙ' => 'ꞙ', |
'êžš' => 'êž›', |
'Ꞝ' => 'ꞝ', |
'Ꞟ' => 'ꞟ', |
'êž ' => 'êž¡', |
'Ꞣ' => 'ꞣ', |
'Ꞥ' => 'ꞥ', |
'Ꞧ' => 'ꞧ', |
'Ꞩ' => 'ꞩ', |
'Ɦ' => 'ɦ', |
'Ɜ' => 'ɜ', |
'Ɡ' => 'ɡ', |
'êž' => 'ɬ', |
'êž°' => 'Êž', |
'Ʇ' => 'ʇ', |
'A' => 'a', |
'B' => 'b', |
'C' => 'c', |
'D' => 'd', |
'E' => 'e', |
'F' => 'f', |
'G' => 'g', |
'H' => 'h', |
'I' => 'i', |
'J' => 'j', |
'K' => 'k', |
'L' => 'l', |
'M' => 'm', |
'N' => 'n', |
'O' => 'o', |
'P' => 'p', |
'Q' => 'q', |
'R' => 'r', |
'S' => 's', |
'T' => 't', |
'U' => 'u', |
'V' => 'v', |
'W' => 'w', |
'X' => 'x', |
'Y' => 'y', |
'Z' => 'z', |
'𐐀' => '𐐨', |
'𐐁' => '𐐩', |
'𐐂' => '𐐪', |
'𐐃' => '𐐫', |
'𐐄' => '𐐬', |
'𐐅' => '𐐭', |
'𐐆' => '𐐮', |
'𐐇' => '𐐯', |
'𐐈' => '𐐰', |
'𐐉' => '𐐱', |
'𐐊' => '𐐲', |
'𐐋' => '𐐳', |
'𐐌' => '𐐴', |
'𐐍' => '𐐵', |
'𐐎' => '𐐶', |
'𐐏' => '𐐷', |
'𐐐' => '𐐸', |
'𐐑' => '𐐹', |
'𐐒' => '𐐺', |
'𐐓' => '𐐻', |
'𐐔' => '𐐼', |
'𐐕' => '𐐽', |
'𐐖' => '𐐾', |
'𐐗' => '𐐿', |
'𐐘' => '𐑀', |
'𐐙' => '𐑁', |
'𐐚' => '𐑂', |
'𐐛' => '𐑃', |
'𐐜' => '𐑄', |
'𐐝' => '𐑅', |
'𐐞' => '𐑆', |
'𐐟' => '𐑇', |
'𐐠' => '𐑈', |
'𐐡' => '𐑉', |
'𐐢' => '𐑊', |
'𐐣' => '𐑋', |
'𐐤' => '𐑌', |
'𐐥' => '𐑍', |
'𐐦' => '𐑎', |
'𐐧' => '𐑏', |
'ð‘¢ ' => 'ð‘£€', |
'𑢡' => 'ð‘£', |
'𑢢' => '𑣂', |
'𑢣' => '𑣃', |
'𑢤' => '𑣄', |
'ð‘¢¥' => 'ð‘£…', |
'𑢦' => '𑣆', |
'𑢧' => '𑣇', |
'𑢨' => '𑣈', |
'𑢩' => '𑣉', |
'𑢪' => '𑣊', |
'𑢫' => '𑣋', |
'𑢬' => '𑣌', |
'ð‘¢' => 'ð‘£', |
'𑢮' => '𑣎', |
'𑢯' => 'ð‘£', |
'ð‘¢°' => 'ð‘£', |
'𑢱' => '𑣑', |
'ð‘¢²' => 'ð‘£’', |
'𑢳' => '𑣓', |
'ð‘¢´' => 'ð‘£”', |
'𑢵' => '𑣕', |
'𑢶' => '𑣖', |
'ð‘¢·' => 'ð‘£—', |
'𑢸' => '𑣘', |
'ð‘¢¹' => 'ð‘£™', |
'𑢺' => '𑣚', |
'ð‘¢»' => 'ð‘£›', |
'𑢼' => '𑣜', |
'ð‘¢½' => 'ð‘£', |
'𑢾' => '𑣞', |
'𑢿' => '𑣟', |
); |
$result =& $data; |
unset($data); |
return $result; |
/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php |
---|
@@ -0,0 +1,1109 @@ |
<?php |
static $data = array ( |
'a' => 'A', |
'b' => 'B', |
'c' => 'C', |
'd' => 'D', |
'e' => 'E', |
'f' => 'F', |
'g' => 'G', |
'h' => 'H', |
'i' => 'I', |
'j' => 'J', |
'k' => 'K', |
'l' => 'L', |
'm' => 'M', |
'n' => 'N', |
'o' => 'O', |
'p' => 'P', |
'q' => 'Q', |
'r' => 'R', |
's' => 'S', |
't' => 'T', |
'u' => 'U', |
'v' => 'V', |
'w' => 'W', |
'x' => 'X', |
'y' => 'Y', |
'z' => 'Z', |
'µ' => 'Μ', |
'à' => 'À', |
'á' => 'Á', |
'â' => 'Â', |
'ã' => 'Ã', |
'ä' => 'Ä', |
'å' => 'Å', |
'æ' => 'Æ', |
'ç' => 'Ç', |
'è' => 'È', |
'é' => 'É', |
'ê' => 'Ê', |
'ë' => 'Ë', |
'ì' => 'Ì', |
'í' => 'Í', |
'î' => 'Î', |
'ï' => 'Ï', |
'ð' => 'Ð', |
'ñ' => 'Ñ', |
'ò' => 'Ò', |
'ó' => 'Ó', |
'ô' => 'Ô', |
'õ' => 'Õ', |
'ö' => 'Ö', |
'ø' => 'Ø', |
'ù' => 'Ù', |
'ú' => 'Ú', |
'û' => 'Û', |
'ü' => 'Ü', |
'ý' => 'Ý', |
'þ' => 'Þ', |
'ÿ' => 'Ÿ', |
'Ä' => 'Ä€', |
'ă' => 'Ă', |
'ą' => 'Ą', |
'ć' => 'Ć', |
'ĉ' => 'Ĉ', |
'Ä‹' => 'ÄŠ', |
'č' => 'Č', |
'ď' => 'Ď', |
'Ä‘' => 'Ä', |
'Ä“' => 'Ä’', |
'Ä•' => 'Ä”', |
'Ä—' => 'Ä–', |
'ę' => 'Ę', |
'ě' => 'Ě', |
'ĝ' => 'Ĝ', |
'ÄŸ' => 'Äž', |
'Ä¡' => 'Ä ', |
'Ä£' => 'Ä¢', |
'ĥ' => 'Ĥ', |
'ħ' => 'Ħ', |
'ĩ' => 'Ĩ', |
'ī' => 'Ī', |
'Ä' => 'Ĭ', |
'į' => 'Į', |
'ı' => 'I', |
'ij' => 'IJ', |
'ĵ' => 'Ĵ', |
'ķ' => 'Ķ', |
'ĺ' => 'Ĺ', |
'ļ' => 'Ļ', |
'ľ' => 'Ľ', |
'Å€' => 'Ä¿', |
'ł' => 'Ł', |
'ń' => 'Ń', |
'ņ' => 'Ņ', |
'ň' => 'Ň', |
'Å‹' => 'ÅŠ', |
'ō' => 'Ō', |
'Å' => 'ÅŽ', |
'ő' => 'Ő', |
'Å“' => 'Å’', |
'Å•' => 'Å”', |
'Å—' => 'Å–', |
'ř' => 'Ř', |
'ś' => 'Ś', |
'ŝ' => 'Ŝ', |
'ÅŸ' => 'Åž', |
'š' => 'Š', |
'Å£' => 'Å¢', |
'ť' => 'Ť', |
'ŧ' => 'Ŧ', |
'ũ' => 'Ũ', |
'ū' => 'Ū', |
'Å' => 'Ŭ', |
'ů' => 'Ů', |
'ű' => 'Ű', |
'ų' => 'Ų', |
'ŵ' => 'Ŵ', |
'ŷ' => 'Ŷ', |
'ź' => 'Ź', |
'ż' => 'Ż', |
'ž' => 'Ž', |
'Å¿' => 'S', |
'ƀ' => 'Ƀ', |
'ƃ' => 'Ƃ', |
'Æ…' => 'Æ„', |
'ƈ' => 'Ƈ', |
'ƌ' => 'Ƌ', |
'Æ’' => 'Æ‘', |
'ƕ' => 'Ƕ', |
'ƙ' => 'Ƙ', |
'ƚ' => 'Ƚ', |
'Æž' => 'È ', |
'Æ¡' => 'Æ ', |
'Æ£' => 'Æ¢', |
'ƥ' => 'Ƥ', |
'ƨ' => 'Ƨ', |
'Æ' => 'Ƭ', |
'ư' => 'Ư', |
'ƴ' => 'Ƴ', |
'ƶ' => 'Ƶ', |
'ƹ' => 'Ƹ', |
'ƽ' => 'Ƽ', |
'Æ¿' => 'Ç·', |
'Ç…' => 'Ç„', |
'dž' => 'DŽ', |
'Lj' => 'LJ', |
'lj' => 'LJ', |
'Ç‹' => 'ÇŠ', |
'nj' => 'NJ', |
'ÇŽ' => 'Ç', |
'ǐ' => 'Ǐ', |
'Ç’' => 'Ç‘', |
'Ç”' => 'Ç“', |
'Ç–' => 'Ç•', |
'ǘ' => 'Ǘ', |
'Çš' => 'Ç™', |
'ǜ' => 'Ǜ', |
'Ç' => 'ÆŽ', |
'ÇŸ' => 'Çž', |
'Ç¡' => 'Ç ', |
'Ç£' => 'Ç¢', |
'ǥ' => 'Ǥ', |
'ǧ' => 'Ǧ', |
'ǩ' => 'Ǩ', |
'ǫ' => 'Ǫ', |
'Ç' => 'Ǭ', |
'ǯ' => 'Ǯ', |
'Dz' => 'DZ', |
'dz' => 'DZ', |
'ǵ' => 'Ǵ', |
'ǹ' => 'Ǹ', |
'ǻ' => 'Ǻ', |
'ǽ' => 'Ǽ', |
'ǿ' => 'Ǿ', |
'È' => 'È€', |
'ȃ' => 'Ȃ', |
'È…' => 'È„', |
'ȇ' => 'Ȇ', |
'ȉ' => 'Ȉ', |
'È‹' => 'ÈŠ', |
'ȍ' => 'Ȍ', |
'È' => 'ÈŽ', |
'È‘' => 'È', |
'È“' => 'È’', |
'È•' => 'È”', |
'È—' => 'È–', |
'ș' => 'Ș', |
'È›' => 'Èš', |
'ȝ' => 'Ȝ', |
'ÈŸ' => 'Èž', |
'È£' => 'È¢', |
'ȥ' => 'Ȥ', |
'ȧ' => 'Ȧ', |
'ȩ' => 'Ȩ', |
'ȫ' => 'Ȫ', |
'È' => 'Ȭ', |
'ȯ' => 'Ȯ', |
'ȱ' => 'Ȱ', |
'ȳ' => 'Ȳ', |
'ȼ' => 'Ȼ', |
'È¿' => 'â±¾', |
'ɀ' => 'Ɀ', |
'É‚' => 'É', |
'ɇ' => 'Ɇ', |
'ɉ' => 'Ɉ', |
'É‹' => 'ÉŠ', |
'ɍ' => 'Ɍ', |
'É' => 'ÉŽ', |
'É' => 'Ɐ', |
'É‘' => 'â±', |
'É’' => 'â±°', |
'É“' => 'Æ', |
'ɔ' => 'Ɔ', |
'ɖ' => 'Ɖ', |
'É—' => 'ÆŠ', |
'ə' => 'Ə', |
'ɛ' => 'Ɛ', |
'ɜ' => 'Ɜ', |
'É ' => 'Æ“', |
'ɡ' => 'Ɡ', |
'É£' => 'Æ”', |
'É¥' => 'êž', |
'ɦ' => 'Ɦ', |
'ɨ' => 'Ɨ', |
'É©' => 'Æ–', |
'É«' => 'â±¢', |
'ɬ' => 'êž', |
'ɯ' => 'Ɯ', |
'ɱ' => 'Ɱ', |
'ɲ' => 'Æ', |
'ɵ' => 'Ɵ', |
'ɽ' => 'Ɽ', |
'ʀ' => 'Ʀ', |
'ʃ' => 'Ʃ', |
'ʇ' => 'Ʇ', |
'ʈ' => 'Ʈ', |
'ʉ' => 'Ʉ', |
'ʊ' => 'Ʊ', |
'ʋ' => 'Ʋ', |
'ʌ' => 'Ʌ', |
'Ê’' => 'Æ·', |
'Êž' => 'êž°', |
'ͅ' => 'Ι', |
'ͱ' => 'Ͱ', |
'ͳ' => 'Ͳ', |
'ͷ' => 'Ͷ', |
'ͻ' => 'Ͻ', |
'ͼ' => 'Ͼ', |
'ͽ' => 'Ͽ', |
'ά' => 'Ά', |
'έ' => 'Έ', |
'ή' => 'Ή', |
'ί' => 'Ί', |
'α' => 'Α', |
'β' => 'Β', |
'γ' => 'Γ', |
'δ' => 'Δ', |
'ε' => 'Ε', |
'ζ' => 'Ζ', |
'η' => 'Η', |
'θ' => 'Θ', |
'ι' => 'Ι', |
'κ' => 'Κ', |
'λ' => 'Λ', |
'μ' => 'Μ', |
'ν' => 'Ν', |
'ξ' => 'Ξ', |
'ο' => 'Ο', |
'π' => 'Π', |
'ρ' => 'Ρ', |
'ς' => 'Σ', |
'σ' => 'Σ', |
'τ' => 'Τ', |
'υ' => 'Υ', |
'φ' => 'Φ', |
'χ' => 'Χ', |
'ψ' => 'Ψ', |
'ω' => 'Ω', |
'ϊ' => 'Ϊ', |
'ϋ' => 'Ϋ', |
'ό' => 'Ό', |
'ύ' => 'Ύ', |
'ώ' => 'Ώ', |
'ϐ' => 'Β', |
'ϑ' => 'Θ', |
'ϕ' => 'Φ', |
'ϖ' => 'Π', |
'ϗ' => 'Ϗ', |
'ϙ' => 'Ϙ', |
'ϛ' => 'Ϛ', |
'ϝ' => 'Ϝ', |
'ϟ' => 'Ϟ', |
'ϡ' => 'Ϡ', |
'ϣ' => 'Ϣ', |
'ϥ' => 'Ϥ', |
'ϧ' => 'Ϧ', |
'ϩ' => 'Ϩ', |
'ϫ' => 'Ϫ', |
'ϭ' => 'Ϭ', |
'ϯ' => 'Ϯ', |
'ϰ' => 'Κ', |
'ϱ' => 'Ρ', |
'ϲ' => 'Ϲ', |
'ϳ' => 'Ϳ', |
'ϵ' => 'Ε', |
'ϸ' => 'Ϸ', |
'ϻ' => 'Ϻ', |
'а' => 'А', |
'б' => 'Б', |
'в' => 'В', |
'г' => 'Г', |
'д' => 'Д', |
'е' => 'Е', |
'ж' => 'Ж', |
'з' => 'З', |
'и' => 'И', |
'й' => 'Й', |
'к' => 'К', |
'л' => 'Л', |
'м' => 'М', |
'н' => 'Н', |
'о' => 'О', |
'п' => 'П', |
'р' => 'Р', |
'с' => 'С', |
'т' => 'Т', |
'у' => 'У', |
'ф' => 'Ф', |
'х' => 'Х', |
'ц' => 'Ц', |
'ч' => 'Ч', |
'ш' => 'Ш', |
'щ' => 'Щ', |
'ъ' => 'Ъ', |
'ы' => 'Ы', |
'ь' => 'Ь', |
'э' => 'Э', |
'ю' => 'Ю', |
'я' => 'Я', |
'ѐ' => 'Ѐ', |
'ё' => 'Ё', |
'ђ' => 'Ђ', |
'ѓ' => 'Ѓ', |
'є' => 'Є', |
'ѕ' => 'Ѕ', |
'і' => 'І', |
'ї' => 'Ї', |
'ј' => 'Ј', |
'љ' => 'Љ', |
'њ' => 'Њ', |
'ћ' => 'Ћ', |
'ќ' => 'Ќ', |
'ѝ' => 'Ѝ', |
'ў' => 'Ў', |
'џ' => 'Џ', |
'ѡ' => 'Ѡ', |
'ѣ' => 'Ѣ', |
'ѥ' => 'Ѥ', |
'ѧ' => 'Ѧ', |
'ѩ' => 'Ѩ', |
'ѫ' => 'Ѫ', |
'ѭ' => 'Ѭ', |
'ѯ' => 'Ѯ', |
'ѱ' => 'Ѱ', |
'ѳ' => 'Ѳ', |
'ѵ' => 'Ѵ', |
'ѷ' => 'Ѷ', |
'ѹ' => 'Ѹ', |
'ѻ' => 'Ѻ', |
'ѽ' => 'Ѽ', |
'ѿ' => 'Ѿ', |
'ҁ' => 'Ҁ', |
'ҋ' => 'Ҋ', |
'ҍ' => 'Ҍ', |
'ҏ' => 'Ҏ', |
'ґ' => 'Ґ', |
'ғ' => 'Ғ', |
'ҕ' => 'Ҕ', |
'җ' => 'Җ', |
'ҙ' => 'Ҙ', |
'қ' => 'Қ', |
'ҝ' => 'Ҝ', |
'ҟ' => 'Ҟ', |
'ҡ' => 'Ҡ', |
'ң' => 'Ң', |
'ҥ' => 'Ҥ', |
'ҧ' => 'Ҧ', |
'ҩ' => 'Ҩ', |
'ҫ' => 'Ҫ', |
'ҭ' => 'Ҭ', |
'ү' => 'Ү', |
'ұ' => 'Ұ', |
'ҳ' => 'Ҳ', |
'ҵ' => 'Ҵ', |
'ҷ' => 'Ҷ', |
'ҹ' => 'Ҹ', |
'һ' => 'Һ', |
'ҽ' => 'Ҽ', |
'ҿ' => 'Ҿ', |
'ӂ' => 'Ӂ', |
'ӄ' => 'Ӄ', |
'ӆ' => 'Ӆ', |
'ӈ' => 'Ӈ', |
'ӊ' => 'Ӊ', |
'ӌ' => 'Ӌ', |
'ӎ' => 'Ӎ', |
'ӏ' => 'Ӏ', |
'ӑ' => 'Ӑ', |
'ӓ' => 'Ӓ', |
'ӕ' => 'Ӕ', |
'ӗ' => 'Ӗ', |
'ә' => 'Ә', |
'ӛ' => 'Ӛ', |
'ӝ' => 'Ӝ', |
'ӟ' => 'Ӟ', |
'ӡ' => 'Ӡ', |
'ӣ' => 'Ӣ', |
'ӥ' => 'Ӥ', |
'ӧ' => 'Ӧ', |
'ө' => 'Ө', |
'ӫ' => 'Ӫ', |
'ӭ' => 'Ӭ', |
'ӯ' => 'Ӯ', |
'ӱ' => 'Ӱ', |
'ӳ' => 'Ӳ', |
'ӵ' => 'Ӵ', |
'ӷ' => 'Ӷ', |
'ӹ' => 'Ӹ', |
'ӻ' => 'Ӻ', |
'ӽ' => 'Ӽ', |
'ӿ' => 'Ӿ', |
'Ô' => 'Ô€', |
'Ôƒ' => 'Ô‚', |
'Ô…' => 'Ô„', |
'Ô‡' => 'Ô†', |
'ԉ' => 'Ԉ', |
'Ô‹' => 'ÔŠ', |
'ԍ' => 'Ԍ', |
'Ô' => 'ÔŽ', |
'Ô‘' => 'Ô', |
'Ô“' => 'Ô’', |
'Ô•' => 'Ô”', |
'Ô—' => 'Ô–', |
'ԙ' => 'Ԙ', |
'Ô›' => 'Ôš', |
'ԝ' => 'Ԝ', |
'ÔŸ' => 'Ôž', |
'Ô¡' => 'Ô ', |
'Ô£' => 'Ô¢', |
'Ô¥' => 'Ô¤', |
'Ô§' => 'Ô¦', |
'Ô©' => 'Ô¨', |
'Ô«' => 'Ôª', |
'Ô' => 'Ô¬', |
'Ô¯' => 'Ô®', |
'Õ¡' => 'Ô±', |
'Õ¢' => 'Ô²', |
'Õ£' => 'Ô³', |
'Õ¤' => 'Ô´', |
'Õ¥' => 'Ôµ', |
'Õ¦' => 'Ô¶', |
'Õ§' => 'Ô·', |
'Õ¨' => 'Ô¸', |
'Õ©' => 'Ô¹', |
'Õª' => 'Ôº', |
'Õ«' => 'Ô»', |
'Õ¬' => 'Ô¼', |
'Õ' => 'Ô½', |
'Õ®' => 'Ô¾', |
'Õ¯' => 'Ô¿', |
'Õ°' => 'Õ€', |
'Õ±' => 'Õ', |
'Õ²' => 'Õ‚', |
'Õ³' => 'Õƒ', |
'Õ´' => 'Õ„', |
'Õµ' => 'Õ…', |
'Õ¶' => 'Õ†', |
'Õ·' => 'Õ‡', |
'Õ¸' => 'Õˆ', |
'Õ¹' => 'Õ‰', |
'Õº' => 'ÕŠ', |
'Õ»' => 'Õ‹', |
'ռ' => 'Ռ', |
'Õ½' => 'Õ', |
'Õ¾' => 'ÕŽ', |
'Õ¿' => 'Õ', |
'Ö€' => 'Õ', |
'Ö' => 'Õ‘', |
'Ö‚' => 'Õ’', |
'Öƒ' => 'Õ“', |
'Ö„' => 'Õ”', |
'Ö…' => 'Õ•', |
'Ö†' => 'Õ–', |
'áµ¹' => 'ê½', |
'áµ½' => 'â±£', |
'á¸' => 'Ḁ', |
'ḃ' => 'Ḃ', |
'ḅ' => 'Ḅ', |
'ḇ' => 'Ḇ', |
'ḉ' => 'Ḉ', |
'ḋ' => 'Ḋ', |
'ḍ' => 'Ḍ', |
'á¸' => 'Ḏ', |
'ḑ' => 'á¸', |
'ḓ' => 'Ḓ', |
'ḕ' => 'Ḕ', |
'ḗ' => 'Ḗ', |
'ḙ' => 'Ḙ', |
'ḛ' => 'Ḛ', |
'ḝ' => 'Ḝ', |
'ḟ' => 'Ḟ', |
'ḡ' => 'Ḡ', |
'ḣ' => 'Ḣ', |
'ḥ' => 'Ḥ', |
'ḧ' => 'Ḧ', |
'ḩ' => 'Ḩ', |
'ḫ' => 'Ḫ', |
'á¸' => 'Ḭ', |
'ḯ' => 'Ḯ', |
'ḱ' => 'Ḱ', |
'ḳ' => 'Ḳ', |
'ḵ' => 'Ḵ', |
'ḷ' => 'Ḷ', |
'ḹ' => 'Ḹ', |
'ḻ' => 'Ḻ', |
'ḽ' => 'Ḽ', |
'ḿ' => 'Ḿ', |
'á¹' => 'á¹€', |
'ṃ' => 'Ṃ', |
'ṅ' => 'Ṅ', |
'ṇ' => 'Ṇ', |
'ṉ' => 'Ṉ', |
'ṋ' => 'Ṋ', |
'ṍ' => 'Ṍ', |
'á¹' => 'Ṏ', |
'ṑ' => 'á¹', |
'ṓ' => 'Ṓ', |
'ṕ' => 'Ṕ', |
'á¹—' => 'á¹–', |
'ṙ' => 'Ṙ', |
'ṛ' => 'Ṛ', |
'ṝ' => 'Ṝ', |
'ṟ' => 'Ṟ', |
'ṡ' => 'Ṡ', |
'á¹£' => 'á¹¢', |
'ṥ' => 'Ṥ', |
'ṧ' => 'Ṧ', |
'ṩ' => 'Ṩ', |
'ṫ' => 'Ṫ', |
'á¹' => 'Ṭ', |
'ṯ' => 'Ṯ', |
'á¹±' => 'á¹°', |
'á¹³' => 'á¹²', |
'á¹µ' => 'á¹´', |
'ṷ' => 'Ṷ', |
'ṹ' => 'Ṹ', |
'ṻ' => 'Ṻ', |
'á¹½' => 'á¹¼', |
'ṿ' => 'Ṿ', |
'áº' => 'Ẁ', |
'ẃ' => 'Ẃ', |
'ẅ' => 'Ẅ', |
'ẇ' => 'Ẇ', |
'ẉ' => 'Ẉ', |
'ẋ' => 'Ẋ', |
'ẍ' => 'Ẍ', |
'áº' => 'Ẏ', |
'ẑ' => 'áº', |
'ẓ' => 'Ẓ', |
'ẕ' => 'Ẕ', |
'ẛ' => 'Ṡ', |
'ạ' => 'Ạ', |
'ả' => 'Ả', |
'ấ' => 'Ấ', |
'ầ' => 'Ầ', |
'ẩ' => 'Ẩ', |
'ẫ' => 'Ẫ', |
'áº' => 'Ậ', |
'ắ' => 'Ắ', |
'ằ' => 'Ằ', |
'ẳ' => 'Ẳ', |
'ẵ' => 'Ẵ', |
'ặ' => 'Ặ', |
'ẹ' => 'Ẹ', |
'ẻ' => 'Ẻ', |
'ẽ' => 'Ẽ', |
'ế' => 'Ế', |
'á»' => 'Ề', |
'ể' => 'Ể', |
'ễ' => 'Ễ', |
'ệ' => 'Ệ', |
'ỉ' => 'Ỉ', |
'ị' => 'Ị', |
'ọ' => 'Ọ', |
'á»' => 'Ỏ', |
'ố' => 'á»', |
'ồ' => 'Ồ', |
'ổ' => 'Ổ', |
'á»—' => 'á»–', |
'ộ' => 'Ộ', |
'ớ' => 'Ớ', |
'ờ' => 'Ờ', |
'ở' => 'Ở', |
'ỡ' => 'Ỡ', |
'ợ' => 'Ợ', |
'ụ' => 'Ụ', |
'ủ' => 'Ủ', |
'ứ' => 'Ứ', |
'ừ' => 'Ừ', |
'á»' => 'Ử', |
'ữ' => 'Ữ', |
'á»±' => 'á»°', |
'ỳ' => 'Ỳ', |
'ỵ' => 'Ỵ', |
'ỷ' => 'Ỷ', |
'ỹ' => 'Ỹ', |
'ỻ' => 'Ỻ', |
'ỽ' => 'Ỽ', |
'ỿ' => 'Ỿ', |
'ἀ' => 'Ἀ', |
'ἁ' => 'Ἁ', |
'ἂ' => 'Ἂ', |
'ἃ' => 'Ἃ', |
'ἄ' => 'Ἄ', |
'á¼…' => 'á¼', |
'ἆ' => 'Ἆ', |
'ἇ' => 'á¼', |
'ἐ' => 'Ἐ', |
'ἑ' => 'Ἑ', |
'ἒ' => 'Ἒ', |
'ἓ' => 'Ἓ', |
'ἔ' => 'Ἔ', |
'ἕ' => 'á¼', |
'ἠ' => 'Ἠ', |
'ἡ' => 'Ἡ', |
'ἢ' => 'Ἢ', |
'ἣ' => 'Ἣ', |
'ἤ' => 'Ἤ', |
'á¼¥' => 'á¼', |
'ἦ' => 'Ἦ', |
'ἧ' => 'Ἧ', |
'ἰ' => 'Ἰ', |
'á¼±' => 'á¼¹', |
'ἲ' => 'Ἲ', |
'á¼³' => 'á¼»', |
'á¼´' => 'á¼¼', |
'á¼µ' => 'á¼½', |
'ἶ' => 'Ἶ', |
'ἷ' => 'Ἷ', |
'ὀ' => 'Ὀ', |
'ὁ' => 'Ὁ', |
'ὂ' => 'Ὂ', |
'ὃ' => 'Ὃ', |
'ὄ' => 'Ὄ', |
'á½…' => 'á½', |
'ὑ' => 'Ὑ', |
'ὓ' => 'Ὓ', |
'ὕ' => 'á½', |
'ὗ' => 'Ὗ', |
'ὠ' => 'Ὠ', |
'ὡ' => 'Ὡ', |
'ὢ' => 'Ὢ', |
'ὣ' => 'Ὣ', |
'ὤ' => 'Ὤ', |
'á½¥' => 'á½', |
'ὦ' => 'Ὦ', |
'ὧ' => 'Ὧ', |
'ὰ' => 'Ὰ', |
'á½±' => 'á¾»', |
'ὲ' => 'Ὲ', |
'έ' => 'Έ', |
'á½´' => 'á¿Š', |
'á½µ' => 'á¿‹', |
'ὶ' => 'Ὶ', |
'á½·' => 'á¿›', |
'ὸ' => 'Ὸ', |
'ό' => 'Ό', |
'ὺ' => 'Ὺ', |
'á½»' => 'á¿«', |
'ὼ' => 'Ὼ', |
'á½½' => 'á¿»', |
'ᾀ' => 'ᾈ', |
'ᾁ' => 'ᾉ', |
'ᾂ' => 'ᾊ', |
'ᾃ' => 'ᾋ', |
'ᾄ' => 'ᾌ', |
'á¾…' => 'á¾', |
'ᾆ' => 'ᾎ', |
'ᾇ' => 'á¾', |
'ᾐ' => 'ᾘ', |
'ᾑ' => 'ᾙ', |
'ᾒ' => 'ᾚ', |
'ᾓ' => 'ᾛ', |
'ᾔ' => 'ᾜ', |
'ᾕ' => 'á¾', |
'ᾖ' => 'ᾞ', |
'ᾗ' => 'ᾟ', |
'ᾠ' => 'ᾨ', |
'ᾡ' => 'ᾩ', |
'ᾢ' => 'ᾪ', |
'ᾣ' => 'ᾫ', |
'ᾤ' => 'ᾬ', |
'á¾¥' => 'á¾', |
'ᾦ' => 'ᾮ', |
'ᾧ' => 'ᾯ', |
'ᾰ' => 'Ᾰ', |
'á¾±' => 'á¾¹', |
'á¾³' => 'á¾¼', |
'ι' => 'Ι', |
'ῃ' => 'ῌ', |
'ῐ' => 'Ῐ', |
'á¿‘' => 'á¿™', |
'ῠ' => 'Ῠ', |
'á¿¡' => 'á¿©', |
'ῥ' => 'Ῥ', |
'ῳ' => 'ῼ', |
'ⅎ' => 'Ⅎ', |
'ⅰ' => 'Ⅰ', |
'â…±' => 'â…¡', |
'â…²' => 'â…¢', |
'â…³' => 'â…£', |
'â…´' => 'â…¤', |
'â…µ' => 'â…¥', |
'â…¶' => 'â…¦', |
'â…·' => 'â…§', |
'â…¸' => 'â…¨', |
'â…¹' => 'â…©', |
'â…º' => 'â…ª', |
'â…»' => 'â…«', |
'â…¼' => 'â…¬', |
'ⅽ' => 'Ⅽ', |
'â…¾' => 'â…®', |
'â…¿' => 'â…¯', |
'ↄ' => 'Ↄ', |
'â“' => 'â’¶', |
'â“‘' => 'â’·', |
'â“’' => 'â’¸', |
'â““' => 'â’¹', |
'â“”' => 'â’º', |
'â“•' => 'â’»', |
'â“–' => 'â’¼', |
'â“—' => 'â’½', |
'ⓘ' => 'Ⓘ', |
'â“™' => 'â’¿', |
'â“š' => 'â“€', |
'ⓛ' => 'Ⓛ', |
'ⓜ' => 'Ⓜ', |
'ⓝ' => 'Ⓝ', |
'â“ž' => 'â“„', |
'â“Ÿ' => 'â“…', |
'ⓠ' => 'Ⓠ', |
'ⓡ' => 'Ⓡ', |
'ⓢ' => 'Ⓢ', |
'ⓣ' => 'Ⓣ', |
'ⓤ' => 'Ⓤ', |
'â“¥' => 'â“‹', |
'ⓦ' => 'Ⓦ', |
'ⓧ' => 'â“', |
'ⓨ' => 'Ⓨ', |
'â“©' => 'â“', |
'â°°' => 'â°€', |
'â°±' => 'â°', |
'â°²' => 'â°‚', |
'â°³' => 'â°ƒ', |
'â°´' => 'â°„', |
'â°µ' => 'â°…', |
'â°¶' => 'â°†', |
'â°·' => 'â°‡', |
'â°¸' => 'â°ˆ', |
'â°¹' => 'â°‰', |
'â°º' => 'â°Š', |
'â°»' => 'â°‹', |
'ⰼ' => 'Ⰼ', |
'â°½' => 'â°', |
'â°¾' => 'â°Ž', |
'â°¿' => 'â°', |
'â±€' => 'â°', |
'â±' => 'â°‘', |
'ⱂ' => 'Ⱂ', |
'ⱃ' => 'Ⱃ', |
'ⱄ' => 'Ⱄ', |
'â±…' => 'â°•', |
'ⱆ' => 'Ⱆ', |
'ⱇ' => 'Ⱇ', |
'ⱈ' => 'Ⱈ', |
'ⱉ' => 'Ⱉ', |
'ⱊ' => 'Ⱊ', |
'ⱋ' => 'Ⱋ', |
'ⱌ' => 'Ⱌ', |
'ⱍ' => 'Ⱍ', |
'ⱎ' => 'Ⱎ', |
'ⱏ' => 'Ⱏ', |
'ⱐ' => 'Ⱐ', |
'ⱑ' => 'Ⱑ', |
'â±’' => 'â°¢', |
'ⱓ' => 'Ⱓ', |
'â±”' => 'â°¤', |
'ⱕ' => 'Ⱕ', |
'â±–' => 'â°¦', |
'â±—' => 'â°§', |
'ⱘ' => 'Ⱘ', |
'â±™' => 'â°©', |
'ⱚ' => 'Ⱚ', |
'â±›' => 'â°«', |
'ⱜ' => 'Ⱜ', |
'ⱝ' => 'Ⱝ', |
'ⱞ' => 'Ⱞ', |
'ⱡ' => 'Ⱡ', |
'ⱥ' => 'Ⱥ', |
'ⱦ' => 'Ⱦ', |
'ⱨ' => 'Ⱨ', |
'ⱪ' => 'Ⱪ', |
'ⱬ' => 'Ⱬ', |
'â±³' => 'â±²', |
'ⱶ' => 'Ⱶ', |
'â²' => 'â²€', |
'ⲃ' => 'Ⲃ', |
'ⲅ' => 'Ⲅ', |
'ⲇ' => 'Ⲇ', |
'ⲉ' => 'Ⲉ', |
'ⲋ' => 'Ⲋ', |
'ⲍ' => 'Ⲍ', |
'â²' => 'Ⲏ', |
'ⲑ' => 'â²', |
'ⲓ' => 'Ⲓ', |
'ⲕ' => 'Ⲕ', |
'â²—' => 'â²–', |
'ⲙ' => 'Ⲙ', |
'ⲛ' => 'Ⲛ', |
'ⲝ' => 'Ⲝ', |
'ⲟ' => 'Ⲟ', |
'ⲡ' => 'Ⲡ', |
'â²£' => 'â²¢', |
'ⲥ' => 'Ⲥ', |
'ⲧ' => 'Ⲧ', |
'ⲩ' => 'Ⲩ', |
'ⲫ' => 'Ⲫ', |
'â²' => 'Ⲭ', |
'ⲯ' => 'Ⲯ', |
'â²±' => 'â²°', |
'â²³' => 'â²²', |
'â²µ' => 'â²´', |
'ⲷ' => 'Ⲷ', |
'ⲹ' => 'Ⲹ', |
'ⲻ' => 'Ⲻ', |
'â²½' => 'â²¼', |
'ⲿ' => 'Ⲿ', |
'â³' => 'â³€', |
'ⳃ' => 'Ⳃ', |
'ⳅ' => 'Ⳅ', |
'ⳇ' => 'Ⳇ', |
'ⳉ' => 'Ⳉ', |
'ⳋ' => 'Ⳋ', |
'ⳍ' => 'Ⳍ', |
'â³' => 'Ⳏ', |
'ⳑ' => 'â³', |
'ⳓ' => 'Ⳓ', |
'ⳕ' => 'Ⳕ', |
'â³—' => 'â³–', |
'ⳙ' => 'Ⳙ', |
'ⳛ' => 'Ⳛ', |
'ⳝ' => 'Ⳝ', |
'ⳟ' => 'Ⳟ', |
'ⳡ' => 'Ⳡ', |
'â³£' => 'â³¢', |
'ⳬ' => 'Ⳬ', |
'â³®' => 'â³', |
'â³³' => 'â³²', |
'â´€' => 'á‚ ', |
'â´' => 'á‚¡', |
'â´‚' => 'á‚¢', |
'â´ƒ' => 'á‚£', |
'ⴄ' => 'Ⴄ', |
'â´…' => 'á‚¥', |
'ⴆ' => 'Ⴆ', |
'ⴇ' => 'Ⴇ', |
'ⴈ' => 'Ⴈ', |
'â´‰' => 'á‚©', |
'ⴊ' => 'Ⴊ', |
'â´‹' => 'á‚«', |
'ⴌ' => 'Ⴌ', |
'ⴍ' => 'Ⴍ', |
'â´Ž' => 'á‚®', |
'â´' => 'Ⴏ', |
'â´' => 'á‚°', |
'ⴑ' => 'Ⴑ', |
'ⴒ' => 'Ⴒ', |
'ⴓ' => 'Ⴓ', |
'â´”' => 'á‚´', |
'ⴕ' => 'Ⴕ', |
'ⴖ' => 'Ⴖ', |
'â´—' => 'á‚·', |
'ⴘ' => 'Ⴘ', |
'ⴙ' => 'Ⴙ', |
'ⴚ' => 'Ⴚ', |
'â´›' => 'á‚»', |
'ⴜ' => 'Ⴜ', |
'â´' => 'Ⴝ', |
'ⴞ' => 'Ⴞ', |
'â´Ÿ' => 'á‚¿', |
'ⴠ' => 'Ⴠ', |
'ⴡ' => 'Ⴡ', |
'ⴢ' => 'Ⴢ', |
'ⴣ' => 'Ⴣ', |
'ⴤ' => 'Ⴤ', |
'ⴥ' => 'Ⴥ', |
'ⴧ' => 'Ⴧ', |
'ⴭ' => 'Ⴭ', |
'ꙁ' => 'Ꙁ', |
'ꙃ' => 'Ꙃ', |
'ꙅ' => 'Ꙅ', |
'ꙇ' => 'Ꙇ', |
'ꙉ' => 'Ꙉ', |
'ꙋ' => 'Ꙋ', |
'ꙍ' => 'Ꙍ', |
'ꙏ' => 'Ꙏ', |
'ꙑ' => 'Ꙑ', |
'ꙓ' => 'Ꙓ', |
'ꙕ' => 'Ꙕ', |
'ꙗ' => 'Ꙗ', |
'ꙙ' => 'Ꙙ', |
'ꙛ' => 'Ꙛ', |
'ꙝ' => 'Ꙝ', |
'ꙟ' => 'Ꙟ', |
'ꙡ' => 'Ꙡ', |
'ꙣ' => 'Ꙣ', |
'ꙥ' => 'Ꙥ', |
'ꙧ' => 'Ꙧ', |
'ꙩ' => 'Ꙩ', |
'ꙫ' => 'Ꙫ', |
'ꙭ' => 'Ꙭ', |
'êš' => 'Ꚁ', |
'ꚃ' => 'Ꚃ', |
'êš…' => 'êš„', |
'ꚇ' => 'Ꚇ', |
'ꚉ' => 'Ꚉ', |
'ꚋ' => 'Ꚋ', |
'ꚍ' => 'Ꚍ', |
'êš' => 'Ꚏ', |
'êš‘' => 'êš', |
'êš“' => 'êš’', |
'êš•' => 'êš”', |
'êš—' => 'êš–', |
'ꚙ' => 'Ꚙ', |
'êš›' => 'êšš', |
'ꜣ' => 'Ꜣ', |
'ꜥ' => 'Ꜥ', |
'ꜧ' => 'Ꜧ', |
'ꜩ' => 'Ꜩ', |
'ꜫ' => 'Ꜫ', |
'ꜭ' => 'Ꜭ', |
'ꜯ' => 'Ꜯ', |
'ꜳ' => 'Ꜳ', |
'ꜵ' => 'Ꜵ', |
'ꜷ' => 'Ꜷ', |
'ꜹ' => 'Ꜹ', |
'ꜻ' => 'Ꜻ', |
'ꜽ' => 'Ꜽ', |
'ꜿ' => 'Ꜿ', |
'ꝁ' => 'Ꝁ', |
'ꝃ' => 'Ꝃ', |
'ꝅ' => 'Ꝅ', |
'ꝇ' => 'Ꝇ', |
'ꝉ' => 'Ꝉ', |
'ꝋ' => 'Ꝋ', |
'ꝍ' => 'Ꝍ', |
'ꝏ' => 'Ꝏ', |
'ꝑ' => 'Ꝑ', |
'ꝓ' => 'Ꝓ', |
'ꝕ' => 'Ꝕ', |
'ꝗ' => 'Ꝗ', |
'ꝙ' => 'Ꝙ', |
'ꝛ' => 'Ꝛ', |
'ꝝ' => 'Ꝝ', |
'ꝟ' => 'Ꝟ', |
'ꝡ' => 'Ꝡ', |
'ꝣ' => 'Ꝣ', |
'ꝥ' => 'Ꝥ', |
'ꝧ' => 'Ꝧ', |
'ꝩ' => 'Ꝩ', |
'ꝫ' => 'Ꝫ', |
'ꝭ' => 'Ꝭ', |
'ꝯ' => 'Ꝯ', |
'ꝺ' => 'Ꝺ', |
'ꝼ' => 'Ꝼ', |
'ꝿ' => 'Ꝿ', |
'êž' => 'Ꞁ', |
'ꞃ' => 'Ꞃ', |
'êž…' => 'êž„', |
'ꞇ' => 'Ꞇ', |
'ꞌ' => 'Ꞌ', |
'êž‘' => 'êž', |
'êž“' => 'êž’', |
'êž—' => 'êž–', |
'ꞙ' => 'Ꞙ', |
'êž›' => 'êžš', |
'ꞝ' => 'Ꞝ', |
'ꞟ' => 'Ꞟ', |
'êž¡' => 'êž ', |
'ꞣ' => 'Ꞣ', |
'ꞥ' => 'Ꞥ', |
'ꞧ' => 'Ꞧ', |
'ꞩ' => 'Ꞩ', |
'a' => 'A', |
'b' => 'B', |
'c' => 'C', |
'd' => 'D', |
'e' => 'E', |
'f' => 'F', |
'g' => 'G', |
'h' => 'H', |
'i' => 'I', |
'j' => 'J', |
'k' => 'K', |
'l' => 'L', |
'm' => 'M', |
'n' => 'N', |
'o' => 'O', |
'p' => 'P', |
'q' => 'Q', |
'r' => 'R', |
's' => 'S', |
't' => 'T', |
'u' => 'U', |
'v' => 'V', |
'w' => 'W', |
'x' => 'X', |
'y' => 'Y', |
'z' => 'Z', |
'𐐨' => '𐐀', |
'𐐩' => '𐐁', |
'𐐪' => '𐐂', |
'𐐫' => '𐐃', |
'𐐬' => '𐐄', |
'𐐭' => '𐐅', |
'𐐮' => '𐐆', |
'𐐯' => '𐐇', |
'𐐰' => '𐐈', |
'𐐱' => '𐐉', |
'𐐲' => '𐐊', |
'𐐳' => '𐐋', |
'𐐴' => '𐐌', |
'𐐵' => '𐐍', |
'𐐶' => '𐐎', |
'𐐷' => '𐐏', |
'𐐸' => '𐐐', |
'𐐹' => '𐐑', |
'𐐺' => '𐐒', |
'𐐻' => '𐐓', |
'𐐼' => '𐐔', |
'𐐽' => '𐐕', |
'𐐾' => '𐐖', |
'𐐿' => '𐐗', |
'𐑀' => '𐐘', |
'𐑁' => '𐐙', |
'𐑂' => '𐐚', |
'𐑃' => '𐐛', |
'𐑄' => '𐐜', |
'𐑅' => '𐐝', |
'𐑆' => '𐐞', |
'𐑇' => '𐐟', |
'𐑈' => '𐐠', |
'𐑉' => '𐐡', |
'𐑊' => '𐐢', |
'𐑋' => '𐐣', |
'𐑌' => '𐐤', |
'𐑍' => '𐐥', |
'𐑎' => '𐐦', |
'𐑏' => '𐐧', |
'ð‘£€' => 'ð‘¢ ', |
'ð‘£' => '𑢡', |
'𑣂' => '𑢢', |
'𑣃' => '𑢣', |
'𑣄' => '𑢤', |
'ð‘£…' => 'ð‘¢¥', |
'𑣆' => '𑢦', |
'𑣇' => '𑢧', |
'𑣈' => '𑢨', |
'𑣉' => '𑢩', |
'𑣊' => '𑢪', |
'𑣋' => '𑢫', |
'𑣌' => '𑢬', |
'ð‘£' => 'ð‘¢', |
'𑣎' => '𑢮', |
'ð‘£' => '𑢯', |
'ð‘£' => 'ð‘¢°', |
'𑣑' => '𑢱', |
'ð‘£’' => 'ð‘¢²', |
'𑣓' => '𑢳', |
'ð‘£”' => 'ð‘¢´', |
'𑣕' => '𑢵', |
'𑣖' => '𑢶', |
'ð‘£—' => 'ð‘¢·', |
'𑣘' => '𑢸', |
'ð‘£™' => 'ð‘¢¹', |
'𑣚' => '𑢺', |
'ð‘£›' => 'ð‘¢»', |
'𑣜' => '𑢼', |
'ð‘£' => 'ð‘¢½', |
'𑣞' => '𑢾', |
'𑣟' => '𑢿', |
); |
$result =& $data; |
unset($data); |
return $result; |
/vendor/symfony/polyfill-mbstring/bootstrap.php |
---|
@@ -0,0 +1,56 @@ |
<?php |
/* |
* This file is part of the Symfony package. |
* |
* (c) Fabien Potencier <fabien@symfony.com> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
use Symfony\Polyfill\Mbstring as p; |
if (!function_exists('mb_strlen')) { |
define('MB_CASE_UPPER', 0); |
define('MB_CASE_LOWER', 1); |
define('MB_CASE_TITLE', 2); |
function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } |
function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } |
function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } |
function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } |
function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } |
function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } |
function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } |
function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } |
function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } |
function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } |
function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } |
function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } |
function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } |
function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } |
function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } |
function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } |
function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } |
function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } |
function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } |
function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } |
function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } |
function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } |
function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } |
function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } |
function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } |
function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } |
function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } |
function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } |
function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } |
function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } |
function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } |
function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); } |
} |
if (!function_exists('mb_chr')) { |
function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); } |
function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); } |
function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } |
} |
/vendor/symfony/polyfill-mbstring/composer.json |
---|
@@ -0,0 +1,34 @@ |
{ |
"name": "symfony/polyfill-mbstring", |
"type": "library", |
"description": "Symfony polyfill for the Mbstring extension", |
"keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"], |
"homepage": "https://symfony.com", |
"license": "MIT", |
"authors": [ |
{ |
"name": "Nicolas Grekas", |
"email": "p@tchwork.com" |
}, |
{ |
"name": "Symfony Community", |
"homepage": "https://symfony.com/contributors" |
} |
], |
"require": { |
"php": ">=5.3.3" |
}, |
"autoload": { |
"psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, |
"files": [ "bootstrap.php" ] |
}, |
"suggest": { |
"ext-mbstring": "For best performance" |
}, |
"minimum-stability": "dev", |
"extra": { |
"branch-alias": { |
"dev-master": "1.3-dev" |
} |
} |
} |