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

Commit 3c63c60

Browse files
committed
Merge branch 'hotfix/24-random-placeholder'
Close #24
2 parents e4b807f + 31452b0 commit 3c63c60

File tree

3 files changed

+22
-8
lines changed

3 files changed

+22
-8
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
All notable changes to this project will be documented in this file, in reverse chronological order by release.
44

5-
## 2.7.1 - TBD
5+
## 2.7.1 - 2018-04-09
66

77
### Added
88

@@ -30,6 +30,10 @@ All notable changes to this project will be documented in this file, in reverse
3030
statement for the `DOMNode` class to the (deprecated) `Zend\Dom\Query` class
3131
definition.
3232

33+
- [#24](https://github.com/zendframework/zend-dom/pull/24) updates how the
34+
tokenizer marks multiple words within attribute values in order to be
35+
more robust.
36+
3337
## 2.7.0 - 2018-03-27
3438

3539
### Added

src/Document/Query.php

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,19 +86,23 @@ public static function cssToXpath($path)
8686
return implode('|', $expressions);
8787
}
8888

89+
do {
90+
$placeholder = '{' . uniqid(mt_rand(), true) . '}';
91+
} while (strpos($path, $placeholder) !== false);
92+
8993
// Arbitrary attribute value contains whitespace
9094
$path = preg_replace_callback(
91-
'/\[\S+?([\'"])((?:(?!\1)[^\\\]|\\.)*)\1\]/',
92-
function ($matches) {
93-
return str_replace($matches[2], preg_replace('/\s+/', '\s', $matches[2]), $matches[0]);
95+
'/\[\S+?([\'"])((?!\1|\\\1).*?)\1\]/',
96+
function ($matches) use ($placeholder) {
97+
return str_replace($matches[2], preg_replace('/\s+/', $placeholder, $matches[2]), $matches[0]);
9498
},
9599
$path
96100
);
97101

98102
$paths = ['//'];
99103
$path = preg_replace('|\s+>\s+|', '>', $path);
100104
$segments = preg_split('/\s+/', $path);
101-
$segments = str_replace('\s', ' ', $segments);
105+
$segments = str_replace($placeholder, ' ', $segments);
102106

103107
foreach ($segments as $key => $segment) {
104108
$pathSegment = static::_tokenize($segment);
@@ -147,7 +151,7 @@ protected static function _tokenize($expression)
147151

148152
// arbitrary attribute strict equality
149153
$expression = preg_replace_callback(
150-
'/\[@?([a-z0-9_-]+)=([\'"])((?:(?!\2)[^\\\]|\\.)*)\2\]/i',
154+
'/\[@?([a-z0-9_-]+)=([\'"])((?!\2|\\\2).*?)\2\]/i',
151155
function ($matches) {
152156
return sprintf("[@%s='%s']", strtolower($matches[1]), str_replace("'", "\\'", $matches[3]));
153157
},
@@ -156,7 +160,7 @@ function ($matches) {
156160

157161
// arbitrary attribute contains full word
158162
$expression = preg_replace_callback(
159-
'/\[([a-z0-9_-]+)~=([\'"])((?:(?!\2)[^\\\]|\\.)*)\2\]/i',
163+
'/\[([a-z0-9_-]+)~=([\'"])((?!\2|\\\2).*?)\2\]/i',
160164
function ($matches) {
161165
return "[contains(concat(' ', normalize-space(@" . strtolower($matches[1]) . "), ' '), ' "
162166
. $matches[3] . " ')]";
@@ -166,7 +170,7 @@ function ($matches) {
166170

167171
// arbitrary attribute contains specified content
168172
$expression = preg_replace_callback(
169-
'/\[([a-z0-9_-]+)\*=([\'"])((?:(?!\2)[^\\\]|\\.)*)\2\]/i',
173+
'/\[([a-z0-9_-]+)\*=([\'"])((?!\2|\\\2).*?)\2\]/i',
170174
function ($matches) {
171175
return "[contains(@" . strtolower($matches[1]) . ", '"
172176
. $matches[3] . "')]";

test/Document/QueryTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,10 @@ public function testTransformNestedAttributeSelectors($selector, $expectedXpath)
202202
{
203203
$this->assertEquals($expectedXpath, Query::cssToXpath($selector));
204204
}
205+
206+
public function testAttributeValueWithBackslash()
207+
{
208+
$test = Query::cssToXpath('select[name="\stop \start"]');
209+
$this->assertEquals("//select[@name='\stop \start']", $test);
210+
}
205211
}

0 commit comments

Comments
 (0)