1--TEST-- 2Test DOMXPath::quote with various inputs 3--EXTENSIONS-- 4dom 5--SKIPIF-- 6<?php if (!class_exists('DOMXPath')) die('skip DOMXPath not available.'); ?> 7--FILE-- 8<?php 9$dom = new DOMDocument(); 10$xpath = new DOMXPath($dom); 11 12 13/** 14 * Quote a string for use in an XPath expression. 15 * 16 * Example: $xp->query("//span[contains(text()," . $xp->quote($string) . ")]") 17 * 18 * @param string $string string to quote. 19 * @return string quoted string. 20 */ 21function UserlandDOMXPathQuote(string $string): string 22{ 23 if (false === \strpos($string, '\'')) { 24 return '\'' . $string . '\''; 25 } 26 if (false === \strpos($string, '"')) { 27 return '"' . $string . '"'; 28 } 29 // if the string contains both single and double quotes, construct an 30 // expression that concatenates all non-double-quote substrings with 31 // the quotes, e.g.: 32 // 'foo'"bar => concat("'foo'", '"bar") 33 $sb = []; 34 while ($string !== '') { 35 $bytesUntilSingleQuote = \strcspn($string, '\''); 36 $bytesUntilDoubleQuote = \strcspn($string, '"'); 37 $quoteMethod = ($bytesUntilSingleQuote > $bytesUntilDoubleQuote) ? "'" : '"'; 38 $bytesUntilQuote = \max($bytesUntilSingleQuote, $bytesUntilDoubleQuote); 39 $sb[] = $quoteMethod . \substr($string, 0, $bytesUntilQuote) . $quoteMethod; 40 $string = \substr($string, $bytesUntilQuote); 41 } 42 $sb = \implode(',', $sb); 43 return 'concat(' . $sb . ')'; 44} 45 46 47 48$tests = [ 49 '' => "''", // empty string 50 'foo' => "'foo'", // no quotes 51 '"foo' => '\'"foo\'', // double quotes only 52 '\'foo' => '"\'foo"', // single quotes only 53 '\'foo"bar' => 'concat("\'foo",\'"bar\')', // both; double quotes in mid-string 54 '\'foo"bar"baz' => 'concat("\'foo",\'"bar"baz\')', // multiple double quotes in mid-string 55 '\'foo"' => 'concat("\'foo",\'"\')', // string ends with double quotes 56 '\'foo""' => 'concat("\'foo",\'""\')', // string ends with run of double quotes 57 '"\'foo' => 'concat(\'"\',"\'foo")', // string begins with double quotes 58 '""\'foo' => 'concat(\'""\',"\'foo")', // string begins with run of double quotes 59 '\'foo""bar' => 'concat("\'foo",\'""bar\')', // run of double quotes in mid-string 60]; 61 62foreach ($tests as $input => $expected) { 63 $result = $xpath->quote($input); 64 if ($result === $expected) { 65 echo "Pass: {$input} => {$result}\n"; 66 } else { 67 echo 'Fail: '; 68 var_dump([ 69 'input' => $input, 70 'expected' => $expected, 71 'result' => $result, 72 'userland_implementation_result' => UserlandDOMXPathQuote($input), 73 ]); 74 } 75} 76?> 77--EXPECT-- 78Pass: => '' 79Pass: foo => 'foo' 80Pass: "foo => '"foo' 81Pass: 'foo => "'foo" 82Pass: 'foo"bar => concat("'foo",'"bar') 83Pass: 'foo"bar"baz => concat("'foo",'"bar"baz') 84Pass: 'foo" => concat("'foo",'"') 85Pass: 'foo"" => concat("'foo",'""') 86Pass: "'foo => concat('"',"'foo") 87Pass: ""'foo => concat('""',"'foo") 88Pass: 'foo""bar => concat("'foo",'""bar') 89