scratch

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 86  →  ?path2? @ 87
/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&quot;s Bar"/></a>
<a href="/bar"><img alt="\' Fabien&quot;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"
}
}
}