Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit 78e18d9

Browse files
committed
Merge branch 'hotfix/21'
Close #21
2 parents 6ff0f94 + 863e582 commit 78e18d9

File tree

3 files changed

+48
-16
lines changed

3 files changed

+48
-16
lines changed

CHANGELOG.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,21 @@ All notable changes to this project will be documented in this file, in reverse
2222

2323
### Fixed
2424

25-
- Nothing.
25+
- [#21](https://github.com/zendframework/zend-dom/pull/21) fixes an issue with
26+
matching against nested attribute selectors (e.g., `div[class="foo"] div
27+
[class="bar"]`), ensuring such syntax will transform to expected XPath.
2628

2729
## 2.7.0 - 2018-03-27
2830

2931
### Added
3032

31-
- [#20](https://github.com/zendframework/zend-dom/pull/4) adds support for
33+
- [#20](https://github.com/zendframework/zend-dom/pull/20) adds support for
3234
attribute selectors that contain spaces, such as `input[value="Marty McFly"]`.
3335
Previously, spaces within the selector value would result in a query per
3436
space-separated word; they now, correctly, result in a single query for the
3537
exact value.
3638

37-
- [#19](https://github.com/zendframework/zend-dom/pull/4) adds support for PHP
39+
- [#19](https://github.com/zendframework/zend-dom/pull/19) adds support for PHP
3840
versions 7.1 and 7.2.
3941

4042
- Adds documentation and publishes it to https://docs.zendframework.com/zend-dom/
@@ -45,12 +47,12 @@ All notable changes to this project will be documented in this file, in reverse
4547

4648
### Removed
4749

48-
- [#13](https://github.com/zendframework/zend-dom/pull/4) and
49-
[#19](https://github.com/zendframework/zend-dom/pull/4) remove support for PHP
50+
- [#13](https://github.com/zendframework/zend-dom/pull/13) and
51+
[#19](https://github.com/zendframework/zend-dom/pull/19) remove support for PHP
5052
versions prior to 5.6.
5153

52-
- [#13](https://github.com/zendframework/zend-dom/pull/4) and
53-
[#19](https://github.com/zendframework/zend-dom/pull/4) remove support for HHVM.
54+
- [#13](https://github.com/zendframework/zend-dom/pull/13) and
55+
[#19](https://github.com/zendframework/zend-dom/pull/19) remove support for HHVM.
5456

5557
### Fixed
5658

@@ -60,7 +62,7 @@ All notable changes to this project will be documented in this file, in reverse
6062

6163
### Added
6264

63-
- [#2](https://github.com/zendframework/zend-dom/pull/4) ads context node
65+
- [#2](https://github.com/zendframework/zend-dom/pull/2) adds context node
6466
support for DOMXPath->query that supports querying in the context of a
6567
specific node.
6668

src/Document/Query.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ public static function cssToXpath($path)
8888

8989
// Arbitrary attribute value contains whitespace
9090
$path = preg_replace_callback(
91-
'/\[\S+["\'](.+)["\']\]/',
91+
'/\[\S+?([\'"])((?:(?!\1)[^\\\]|\\.)*)\1\]/',
9292
function ($matches) {
93-
return str_replace($matches[1], preg_replace('/\s+/', '\s', $matches[1]), $matches[0]);
93+
return str_replace($matches[2], preg_replace('/\s+/', '\s', $matches[2]), $matches[0]);
9494
},
9595
$path
9696
);
@@ -147,29 +147,29 @@ protected static function _tokenize($expression)
147147

148148
// arbitrary attribute strict equality
149149
$expression = preg_replace_callback(
150-
'|\[@?([a-z0-9_-]+)=[\'"]([^\'"]+)[\'"]\]|i',
150+
'/\[@?([a-z0-9_-]+)=([\'"])((?:(?!\2)[^\\\]|\\.)*)\2\]/i',
151151
function ($matches) {
152-
return '[@' . strtolower($matches[1]) . "='" . $matches[2] . "']";
152+
return sprintf("[@%s='%s']", strtolower($matches[1]), str_replace("'", "\\'", $matches[3]));
153153
},
154154
$expression
155155
);
156156

157157
// arbitrary attribute contains full word
158158
$expression = preg_replace_callback(
159-
'|\[([a-z0-9_-]+)~=[\'"]([^\'"]+)[\'"]\]|i',
159+
'/\[([a-z0-9_-]+)~=([\'"])((?:(?!\2)[^\\\]|\\.)*)\2\]/i',
160160
function ($matches) {
161161
return "[contains(concat(' ', normalize-space(@" . strtolower($matches[1]) . "), ' '), ' "
162-
. $matches[2] . " ')]";
162+
. $matches[3] . " ')]";
163163
},
164164
$expression
165165
);
166166

167167
// arbitrary attribute contains specified content
168168
$expression = preg_replace_callback(
169-
'|\[([a-z0-9_-]+)\*=[\'"]([^\'"]+)[\'"]\]|i',
169+
'/\[([a-z0-9_-]+)\*=([\'"])((?:(?!\2)[^\\\]|\\.)*)\2\]/i',
170170
function ($matches) {
171171
return "[contains(@" . strtolower($matches[1]) . ", '"
172-
. $matches[2] . "')]";
172+
. $matches[3] . "')]";
173173
},
174174
$expression
175175
);

test/Document/QueryTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,34 @@ public function testCanTransformWithAttributeAndDot()
172172
$test = Query::cssToXpath('a[@href="http://example.com"]');
173173
$this->assertEquals("//a[@href='http://example.com']", $test);
174174
}
175+
176+
public function nestedAttributeSelectors()
177+
{
178+
return [
179+
'with-double-quotes' => [
180+
'select[name="foo"] option[selected="selected"]',
181+
"//select[@name='foo']//option[@selected='selected']",
182+
],
183+
'with-single-quotes' => [
184+
"select[name='foo'] option[selected='selected']",
185+
"//select[@name='foo']//option[@selected='selected']",
186+
],
187+
'double-quotes-containing-single-quotes' => [
188+
"select[name=\"f'oo\"] option[selected=\"sel'ected\"]",
189+
"//select[@name='f\'oo']//option[@selected='sel\'ected']",
190+
],
191+
'single-quotes-containing-double-quotes' => [
192+
"select[name='f\"oo'] option[selected='sel\"ected']",
193+
"//select[@name='f\"oo']//option[@selected='sel\"ected']",
194+
],
195+
];
196+
}
197+
198+
/**
199+
* @dataProvider nestedAttributeSelectors
200+
*/
201+
public function testTransformNestedAttributeSelectors($selector, $expectedXpath)
202+
{
203+
$this->assertEquals($expectedXpath, Query::cssToXpath($selector));
204+
}
175205
}

0 commit comments

Comments
 (0)