diff --git a/src/Symfony/Component/CssSelector/CHANGELOG.md b/src/Symfony/Component/CssSelector/CHANGELOG.md index be10abee92..f40b8d6f20 100644 --- a/src/Symfony/Component/CssSelector/CHANGELOG.md +++ b/src/Symfony/Component/CssSelector/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +2.8.0 +----- + + * Added the ConverterInterface and the Converter implementation as a non-static API for the component. + * Deprecated the `CssSelector` static API of the component. + 2.1.0 ----- diff --git a/src/Symfony/Component/CssSelector/Converter.php b/src/Symfony/Component/CssSelector/Converter.php new file mode 100644 index 0000000000..3723c33d91 --- /dev/null +++ b/src/Symfony/Component/CssSelector/Converter.php @@ -0,0 +1,56 @@ + + * + * 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; + +/** + * @author Christophe Coevoet + * + * @api + */ +class Converter implements ConverterInterface +{ + 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()) + ; + } + + /** + * {@inheritdoc} + */ + public function toXPath($cssExpr, $prefix = 'descendant-or-self::') + { + return $this->translator->cssToXPath($cssExpr, $prefix); + } +} diff --git a/src/Symfony/Component/CssSelector/ConverterInterface.php b/src/Symfony/Component/CssSelector/ConverterInterface.php new file mode 100644 index 0000000000..3321f10d1d --- /dev/null +++ b/src/Symfony/Component/CssSelector/ConverterInterface.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector; + +/** + * ConverterInterface is the main entry point of the component and can convert CSS + * selectors to XPath expressions. + * + * 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 Christophe Coevoet + * + * @api + */ +interface ConverterInterface +{ + /** + * 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 + * + * @api + */ + public function toXPath($cssExpr, $prefix = 'descendant-or-self::'); + +} diff --git a/src/Symfony/Component/CssSelector/CssSelector.php b/src/Symfony/Component/CssSelector/CssSelector.php index 82c9283ea2..eb61c93afe 100644 --- a/src/Symfony/Component/CssSelector/CssSelector.php +++ b/src/Symfony/Component/CssSelector/CssSelector.php @@ -11,12 +11,7 @@ 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; +@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\Converter class instead.', E_USER_DEPRECATED); /** * CssSelector is the main entry point of the component and can convert CSS @@ -62,6 +57,8 @@ use Symfony\Component\CssSelector\XPath\Translator; * * @author Fabien Potencier * + * @deprecated as of 2.8, will be removed in 3.0. Use the \Symfony\Component\CssSelector\Converter class instead. + * * @api */ class CssSelector @@ -82,20 +79,9 @@ class CssSelector */ public static function toXPath($cssExpr, $prefix = 'descendant-or-self::') { - $translator = new Translator(); + $converter = new Converter(self::$html); - if (self::$html) { - $translator->registerExtension(new HtmlExtension($translator)); - } - - $translator - ->registerParserShortcut(new EmptyStringParser()) - ->registerParserShortcut(new ElementParser()) - ->registerParserShortcut(new ClassParser()) - ->registerParserShortcut(new HashParser()) - ; - - return $translator->cssToXPath($cssExpr, $prefix); + return $converter->toXPath($cssExpr, $prefix); } /** diff --git a/src/Symfony/Component/CssSelector/Node/AbstractNode.php b/src/Symfony/Component/CssSelector/Node/AbstractNode.php index b1c8e9508d..7477e9119d 100644 --- a/src/Symfony/Component/CssSelector/Node/AbstractNode.php +++ b/src/Symfony/Component/CssSelector/Node/AbstractNode.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Node; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ abstract class AbstractNode implements NodeInterface { diff --git a/src/Symfony/Component/CssSelector/Node/AttributeNode.php b/src/Symfony/Component/CssSelector/Node/AttributeNode.php index b10a4dd5b3..af872b79e9 100644 --- a/src/Symfony/Component/CssSelector/Node/AttributeNode.php +++ b/src/Symfony/Component/CssSelector/Node/AttributeNode.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Node; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class AttributeNode extends AbstractNode { diff --git a/src/Symfony/Component/CssSelector/Node/ClassNode.php b/src/Symfony/Component/CssSelector/Node/ClassNode.php index 544342f871..f965e7773e 100644 --- a/src/Symfony/Component/CssSelector/Node/ClassNode.php +++ b/src/Symfony/Component/CssSelector/Node/ClassNode.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Node; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class ClassNode extends AbstractNode { diff --git a/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php b/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php index 6d00db431c..39f6599777 100644 --- a/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php +++ b/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Node; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class CombinedSelectorNode extends AbstractNode { diff --git a/src/Symfony/Component/CssSelector/Node/ElementNode.php b/src/Symfony/Component/CssSelector/Node/ElementNode.php index 71ef121741..06e343e969 100644 --- a/src/Symfony/Component/CssSelector/Node/ElementNode.php +++ b/src/Symfony/Component/CssSelector/Node/ElementNode.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Node; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class ElementNode extends AbstractNode { diff --git a/src/Symfony/Component/CssSelector/Node/FunctionNode.php b/src/Symfony/Component/CssSelector/Node/FunctionNode.php index f94af8dafc..612f348c5e 100644 --- a/src/Symfony/Component/CssSelector/Node/FunctionNode.php +++ b/src/Symfony/Component/CssSelector/Node/FunctionNode.php @@ -20,6 +20,8 @@ use Symfony\Component\CssSelector\Parser\Token; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class FunctionNode extends AbstractNode { diff --git a/src/Symfony/Component/CssSelector/Node/HashNode.php b/src/Symfony/Component/CssSelector/Node/HashNode.php index ddbe76477a..20db465162 100644 --- a/src/Symfony/Component/CssSelector/Node/HashNode.php +++ b/src/Symfony/Component/CssSelector/Node/HashNode.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Node; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class HashNode extends AbstractNode { diff --git a/src/Symfony/Component/CssSelector/Node/NegationNode.php b/src/Symfony/Component/CssSelector/Node/NegationNode.php index 0fafb0a120..4b5aa2260d 100644 --- a/src/Symfony/Component/CssSelector/Node/NegationNode.php +++ b/src/Symfony/Component/CssSelector/Node/NegationNode.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Node; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class NegationNode extends AbstractNode { diff --git a/src/Symfony/Component/CssSelector/Node/NodeInterface.php b/src/Symfony/Component/CssSelector/Node/NodeInterface.php index dd300e23cd..d919e20c71 100644 --- a/src/Symfony/Component/CssSelector/Node/NodeInterface.php +++ b/src/Symfony/Component/CssSelector/Node/NodeInterface.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Node; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ interface NodeInterface { diff --git a/src/Symfony/Component/CssSelector/Node/PseudoNode.php b/src/Symfony/Component/CssSelector/Node/PseudoNode.php index 0e413adc54..c23ddd5912 100644 --- a/src/Symfony/Component/CssSelector/Node/PseudoNode.php +++ b/src/Symfony/Component/CssSelector/Node/PseudoNode.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Node; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class PseudoNode extends AbstractNode { diff --git a/src/Symfony/Component/CssSelector/Node/SelectorNode.php b/src/Symfony/Component/CssSelector/Node/SelectorNode.php index 4958da55af..729e0911b3 100644 --- a/src/Symfony/Component/CssSelector/Node/SelectorNode.php +++ b/src/Symfony/Component/CssSelector/Node/SelectorNode.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Node; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class SelectorNode extends AbstractNode { diff --git a/src/Symfony/Component/CssSelector/Node/Specificity.php b/src/Symfony/Component/CssSelector/Node/Specificity.php index 0ce0c3f304..a24b4fdf84 100644 --- a/src/Symfony/Component/CssSelector/Node/Specificity.php +++ b/src/Symfony/Component/CssSelector/Node/Specificity.php @@ -20,6 +20,8 @@ namespace Symfony\Component\CssSelector\Node; * @see http://www.w3.org/TR/selectors/#specificity * * @author Jean-François Simon + * + * @internal */ class Specificity { diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php index f480776d2c..a29775cab3 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php @@ -21,6 +21,8 @@ use Symfony\Component\CssSelector\Parser\TokenStream; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class CommentHandler implements HandlerInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php b/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php index 049ddd3ec4..a1297c80c0 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php @@ -21,6 +21,8 @@ use Symfony\Component\CssSelector\Parser\TokenStream; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ interface HandlerInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php index b144223fbd..f74bda5126 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php @@ -24,6 +24,8 @@ use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class HashHandler implements HandlerInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php index 86739eab99..358c7c14ad 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php @@ -24,6 +24,8 @@ use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class IdentifierHandler implements HandlerInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php index 97a9387b18..4ea5c484b2 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php @@ -23,6 +23,8 @@ use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class NumberHandler implements HandlerInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php index 9f7a5946b0..4205296016 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php @@ -26,6 +26,8 @@ use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class StringHandler implements HandlerInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php index 234bbd84cf..4c2d3354fb 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php @@ -22,6 +22,8 @@ use Symfony\Component\CssSelector\Parser\TokenStream; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class WhitespaceHandler implements HandlerInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Parser.php b/src/Symfony/Component/CssSelector/Parser/Parser.php index 9625e3e8a1..f94aea36a6 100644 --- a/src/Symfony/Component/CssSelector/Parser/Parser.php +++ b/src/Symfony/Component/CssSelector/Parser/Parser.php @@ -22,6 +22,8 @@ use Symfony\Component\CssSelector\Parser\Tokenizer\Tokenizer; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class Parser implements ParserInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/ParserInterface.php b/src/Symfony/Component/CssSelector/Parser/ParserInterface.php index 3b43a52fde..c5af20367d 100644 --- a/src/Symfony/Component/CssSelector/Parser/ParserInterface.php +++ b/src/Symfony/Component/CssSelector/Parser/ParserInterface.php @@ -20,6 +20,8 @@ use Symfony\Component\CssSelector\Node\SelectorNode; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ interface ParserInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Reader.php b/src/Symfony/Component/CssSelector/Parser/Reader.php index ba2a7f0da9..41136367d8 100644 --- a/src/Symfony/Component/CssSelector/Parser/Reader.php +++ b/src/Symfony/Component/CssSelector/Parser/Reader.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Parser; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class Reader { diff --git a/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php b/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php index 83f8d13d9c..c513de5ff1 100644 --- a/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php +++ b/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php @@ -23,6 +23,8 @@ use Symfony\Component\CssSelector\Parser\ParserInterface; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class ClassParser implements ParserInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.php b/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.php index 00e443553b..c29f5e442e 100644 --- a/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.php +++ b/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.php @@ -22,6 +22,8 @@ use Symfony\Component\CssSelector\Parser\ParserInterface; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class ElementParser implements ParserInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php b/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php index 98a08fde06..016cf0a848 100644 --- a/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php +++ b/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php @@ -26,6 +26,8 @@ use Symfony\Component\CssSelector\Parser\ParserInterface; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class EmptyStringParser implements ParserInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php b/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php index 3dbad79d5b..3f3883bb8d 100644 --- a/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php +++ b/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php @@ -23,6 +23,8 @@ use Symfony\Component\CssSelector\Parser\ParserInterface; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class HashParser implements ParserInterface { diff --git a/src/Symfony/Component/CssSelector/Parser/Token.php b/src/Symfony/Component/CssSelector/Parser/Token.php index 6f7586f612..68fac59b03 100644 --- a/src/Symfony/Component/CssSelector/Parser/Token.php +++ b/src/Symfony/Component/CssSelector/Parser/Token.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Parser; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class Token { diff --git a/src/Symfony/Component/CssSelector/Parser/TokenStream.php b/src/Symfony/Component/CssSelector/Parser/TokenStream.php index c0525d7a83..1ec727febf 100644 --- a/src/Symfony/Component/CssSelector/Parser/TokenStream.php +++ b/src/Symfony/Component/CssSelector/Parser/TokenStream.php @@ -21,6 +21,8 @@ use Symfony\Component\CssSelector\Exception\SyntaxErrorException; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class TokenStream { diff --git a/src/Symfony/Component/CssSelector/Parser/Tokenizer/Tokenizer.php b/src/Symfony/Component/CssSelector/Parser/Tokenizer/Tokenizer.php index 79fa7b77d0..aa9fc50773 100644 --- a/src/Symfony/Component/CssSelector/Parser/Tokenizer/Tokenizer.php +++ b/src/Symfony/Component/CssSelector/Parser/Tokenizer/Tokenizer.php @@ -23,6 +23,8 @@ use Symfony\Component\CssSelector\Parser\TokenStream; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class Tokenizer { diff --git a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php index bf5096be9f..af4c31e5b0 100644 --- a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php +++ b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Parser\Tokenizer; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class TokenizerEscaping { diff --git a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php index 326f020821..5b071cd090 100644 --- a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php +++ b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\Parser\Tokenizer; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class TokenizerPatterns { diff --git a/src/Symfony/Component/CssSelector/Tests/ConverterTest.php b/src/Symfony/Component/CssSelector/Tests/ConverterTest.php new file mode 100644 index 0000000000..239c92fff1 --- /dev/null +++ b/src/Symfony/Component/CssSelector/Tests/ConverterTest.php @@ -0,0 +1,36 @@ + + * + * 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 Symfony\Component\CssSelector\Converter; + +class ConverterTest extends \PHPUnit_Framework_TestCase +{ + public function testCssToXPath() + { + $converter = new Converter(); + + $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 Converter(false); + + $this->assertEquals('descendant-or-self::H1', $converter->toXPath('H1')); + } +} diff --git a/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php b/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php index 61ab80eec8..06eb0d2306 100644 --- a/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php +++ b/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php @@ -13,6 +13,9 @@ namespace Symfony\Component\CssSelector\Tests; use Symfony\Component\CssSelector\CssSelector; +/** + * @group legacy + */ class CssSelectorTest extends \PHPUnit_Framework_TestCase { public function testCssToXPath() diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/AbstractExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/AbstractExtension.php index c70609c695..026ac06c79 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/AbstractExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/AbstractExtension.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\XPath\Extension; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ abstract class AbstractExtension implements ExtensionInterface { diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php index cbe48c57a9..6ace8b5925 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php @@ -21,6 +21,8 @@ use Symfony\Component\CssSelector\XPath\XPathExpr; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class AttributeMatchingExtension extends AbstractExtension { diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php index 9ce018f13a..0d2d658b65 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php @@ -20,6 +20,8 @@ use Symfony\Component\CssSelector\XPath\XPathExpr; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class CombinationExtension extends AbstractExtension { diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php b/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php index 9b47f24f36..3607022891 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\XPath\Extension; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ interface ExtensionInterface { diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php index 41ece8a422..ea05523055 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php @@ -25,6 +25,8 @@ use Symfony\Component\CssSelector\XPath\XPathExpr; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class FunctionExtension extends AbstractExtension { diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/HtmlExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/HtmlExtension.php index 0da74d4727..de6ce41621 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/HtmlExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/HtmlExtension.php @@ -23,6 +23,8 @@ use Symfony\Component\CssSelector\XPath\XPathExpr; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class HtmlExtension extends AbstractExtension { diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php index 2b8920f1ab..9d7f8fa3f0 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php @@ -22,6 +22,8 @@ use Symfony\Component\CssSelector\XPath\XPathExpr; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class NodeExtension extends AbstractExtension { diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php index 008ec2b4b1..1c8b217e39 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php @@ -21,6 +21,8 @@ use Symfony\Component\CssSelector\XPath\XPathExpr; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class PseudoClassExtension extends AbstractExtension { diff --git a/src/Symfony/Component/CssSelector/XPath/Translator.php b/src/Symfony/Component/CssSelector/XPath/Translator.php index 5053793ea0..8c021b3bee 100644 --- a/src/Symfony/Component/CssSelector/XPath/Translator.php +++ b/src/Symfony/Component/CssSelector/XPath/Translator.php @@ -25,6 +25,8 @@ use Symfony\Component\CssSelector\Parser\ParserInterface; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class Translator implements TranslatorInterface { diff --git a/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.php b/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.php index bc19ae8634..0b5de83d57 100644 --- a/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.php +++ b/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.php @@ -20,6 +20,8 @@ use Symfony\Component\CssSelector\Node\SelectorNode; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ interface TranslatorInterface { diff --git a/src/Symfony/Component/CssSelector/XPath/XPathExpr.php b/src/Symfony/Component/CssSelector/XPath/XPathExpr.php index c7ef97cb9a..420ef3d85c 100644 --- a/src/Symfony/Component/CssSelector/XPath/XPathExpr.php +++ b/src/Symfony/Component/CssSelector/XPath/XPathExpr.php @@ -18,6 +18,8 @@ namespace Symfony\Component\CssSelector\XPath; * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. * * @author Jean-François Simon + * + * @internal */ class XPathExpr { diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 38881d2f97..043fa46a49 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -11,7 +11,7 @@ namespace Symfony\Component\DomCrawler; -use Symfony\Component\CssSelector\CssSelector; +use Symfony\Component\CssSelector\Converter; /** * Crawler eases navigation of a list of \DOMElement objects. @@ -42,6 +42,13 @@ class Crawler extends \SplObjectStorage */ private $baseHref; + /** + * Whether the Crawler contains HTML or XML content (used when converting CSS to XPath) + * + * @var bool + */ + private $isHtml = true; + /** * Constructor. * @@ -263,6 +270,8 @@ class Crawler extends \SplObjectStorage libxml_disable_entity_loader($disableEntities); $this->addDocument($dom); + + $this->isHtml = false; } /** @@ -349,11 +358,11 @@ class Crawler extends \SplObjectStorage { foreach ($this as $i => $node) { if ($i == $position) { - return new static($node, $this->uri, $this->baseHref); + return $this->createSubCrawler($node); } } - return new static(null, $this->uri, $this->baseHref); + return $this->createSubCrawler(null); } /** @@ -378,7 +387,7 @@ class Crawler extends \SplObjectStorage { $data = array(); foreach ($this as $i => $node) { - $data[] = $closure(new static($node, $this->uri, $this->baseHref), $i); + $data[] = $closure($this->createSubCrawler($node), $i); } return $data; @@ -394,7 +403,7 @@ class Crawler extends \SplObjectStorage */ public function slice($offset = 0, $length = -1) { - return new static(iterator_to_array(new \LimitIterator($this, $offset, $length)), $this->uri); + return $this->createSubCrawler(iterator_to_array(new \LimitIterator($this, $offset, $length))); } /** @@ -412,12 +421,12 @@ class Crawler extends \SplObjectStorage { $nodes = array(); foreach ($this as $i => $node) { - if (false !== $closure(new static($node, $this->uri, $this->baseHref), $i)) { + if (false !== $closure($this->createSubCrawler($node), $i)) { $nodes[] = $node; } } - return new static($nodes, $this->uri, $this->baseHref); + return $this->createSubCrawler($nodes); } /** @@ -459,7 +468,7 @@ class Crawler extends \SplObjectStorage throw new \InvalidArgumentException('The current node list is empty.'); } - return new static($this->sibling($this->getNode(0)->parentNode->firstChild), $this->uri, $this->baseHref); + return $this->createSubCrawler($this->sibling($this->getNode(0)->parentNode->firstChild)); } /** @@ -477,7 +486,7 @@ class Crawler extends \SplObjectStorage throw new \InvalidArgumentException('The current node list is empty.'); } - return new static($this->sibling($this->getNode(0)), $this->uri, $this->baseHref); + return $this->createSubCrawler($this->sibling($this->getNode(0))); } /** @@ -495,7 +504,7 @@ class Crawler extends \SplObjectStorage throw new \InvalidArgumentException('The current node list is empty.'); } - return new static($this->sibling($this->getNode(0), 'previousSibling'), $this->uri, $this->baseHref); + return $this->createSubCrawler($this->sibling($this->getNode(0), 'previousSibling')); } /** @@ -522,7 +531,7 @@ class Crawler extends \SplObjectStorage } } - return new static($nodes, $this->uri, $this->baseHref); + return $this->createSubCrawler($nodes); } /** @@ -542,7 +551,7 @@ class Crawler extends \SplObjectStorage $node = $this->getNode(0)->firstChild; - return new static($node ? $this->sibling($node) : array(), $this->uri, $this->baseHref); + return $this->createSubCrawler($node ? $this->sibling($node) : array()); } /** @@ -679,7 +688,7 @@ class Crawler extends \SplObjectStorage // If we dropped all expressions in the XPath while preparing it, there would be no match if ('' === $xpath) { - return new static(null, $this->uri, $this->baseHref); + return $this->createSubCrawler(null); } return $this->filterRelativeXPath($xpath); @@ -700,12 +709,14 @@ class Crawler extends \SplObjectStorage */ public function filter($selector) { - if (!class_exists('Symfony\\Component\\CssSelector\\CssSelector')) { - throw new \RuntimeException('Unable to filter with a CSS selector as the Symfony CssSelector is not installed (you can use filterXPath instead).'); + if (!class_exists('Symfony\\Component\\CssSelector\\Converter')) { + 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 Converter($this->isHtml); + // The CssSelector already prefixes the selector with descendant-or-self:: - return $this->filterRelativeXPath(CssSelector::toXPath($selector)); + return $this->filterRelativeXPath($converter->toXPath($selector)); } /** @@ -1019,7 +1030,7 @@ class Crawler extends \SplObjectStorage { $prefixes = $this->findNamespacePrefixes($xpath); - $crawler = new static(null, $this->uri, $this->baseHref); + $crawler = $this->createSubCrawler(null); foreach ($this as $node) { $domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes); @@ -1189,4 +1200,19 @@ class Crawler extends \SplObjectStorage 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; + + return $crawler; + } } diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index 4cfd06f5c5..cdd87dd7a8 100755 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -11,7 +11,6 @@ namespace Symfony\Component\DomCrawler\Tests; -use Symfony\Component\CssSelector\CssSelector; use Symfony\Component\DomCrawler\Crawler; class CrawlerTest extends \PHPUnit_Framework_TestCase @@ -618,16 +617,12 @@ EOF public function testFilterWithNamespace() { - CssSelector::disableHtmlExtension(); - $crawler = $this->createTestXmlCrawler()->filter('yt|accessControl'); $this->assertCount(2, $crawler, '->filter() automatically registers namespaces'); } public function testFilterWithMultipleNamespaces() { - CssSelector::disableHtmlExtension(); - $crawler = $this->createTestXmlCrawler()->filter('media|group yt|aspectRatio'); $this->assertCount(1, $crawler, '->filter() automatically registers namespaces'); $this->assertSame('widescreen', $crawler->text()); diff --git a/src/Symfony/Component/DomCrawler/composer.json b/src/Symfony/Component/DomCrawler/composer.json index 4b3b5f2df3..79b79d82a4 100644 --- a/src/Symfony/Component/DomCrawler/composer.json +++ b/src/Symfony/Component/DomCrawler/composer.json @@ -20,7 +20,7 @@ }, "require-dev": { "symfony/phpunit-bridge": "~2.7|~3.0.0", - "symfony/css-selector": "~2.3|~3.0.0" + "symfony/css-selector": "~2.8|~3.0.0" }, "suggest": { "symfony/css-selector": ""