File: DOMXPath_quote.phpt

package info (click to toggle)
php8.4 8.4.11-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 208,108 kB
  • sloc: ansic: 1,060,628; php: 35,345; sh: 11,866; cpp: 7,201; pascal: 4,913; javascript: 3,091; asm: 2,810; yacc: 2,411; makefile: 689; xml: 446; python: 301; awk: 148
file content (88 lines) | stat: -rw-r--r-- 2,972 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
--TEST--
Test DOMXPath::quote with various inputs
--EXTENSIONS--
dom
--SKIPIF--
<?php if (!class_exists('DOMXPath')) die('skip DOMXPath not available.'); ?>
--FILE--
<?php
$dom = new DOMDocument();
$xpath = new DOMXPath($dom);


/**
 * Quote a string for use in an XPath expression.
 * 
 * Example: $xp->query("//span[contains(text()," . $xp->quote($string) . ")]")
 * 
 * @param string $string string to quote.
 * @return string quoted string.
 */
function UserlandDOMXPathQuote(string $string): string
{
    if (false === \strpos($string, '\'')) {
        return '\'' . $string . '\'';
    }
    if (false === \strpos($string, '"')) {
        return '"' . $string . '"';
    }
    // if the string contains both single and double quotes, construct an
    // expression that concatenates all non-double-quote substrings with
    // the quotes, e.g.:
    //  'foo'"bar => concat("'foo'", '"bar")
    $sb = [];
    while ($string !== '') {
        $bytesUntilSingleQuote = \strcspn($string, '\'');
        $bytesUntilDoubleQuote = \strcspn($string, '"');
        $quoteMethod = ($bytesUntilSingleQuote > $bytesUntilDoubleQuote) ? "'" : '"';
        $bytesUntilQuote = \max($bytesUntilSingleQuote, $bytesUntilDoubleQuote);
        $sb[] = $quoteMethod . \substr($string, 0, $bytesUntilQuote) . $quoteMethod;
        $string = \substr($string, $bytesUntilQuote);
    }
    $sb = \implode(',', $sb);
    return 'concat(' . $sb . ')';
}



$tests = [
    '' => "''", // empty string
    'foo' => "'foo'", // no quotes
    '"foo' => '\'"foo\'', // double quotes only
    '\'foo' => '"\'foo"', // single quotes only
    '\'foo"bar' => 'concat("\'foo",\'"bar\')', // both; double quotes in mid-string
    '\'foo"bar"baz' => 'concat("\'foo",\'"bar"baz\')', // multiple double quotes in mid-string
    '\'foo"' => 'concat("\'foo",\'"\')', // string ends with double quotes
    '\'foo""' => 'concat("\'foo",\'""\')', // string ends with run of double quotes
    '"\'foo' => 'concat(\'"\',"\'foo")', // string begins with double quotes
    '""\'foo' => 'concat(\'""\',"\'foo")', // string begins with run of double quotes
    '\'foo""bar' => 'concat("\'foo",\'""bar\')', // run of double quotes in mid-string
];

foreach ($tests as $input => $expected) {
    $result = $xpath->quote($input);
    if ($result === $expected) {
        echo "Pass: {$input} => {$result}\n";
    } else {
        echo 'Fail: ';
        var_dump([
            'input' => $input,
            'expected' => $expected,
            'result' => $result,
            'userland_implementation_result' => UserlandDOMXPathQuote($input),
        ]);
    }
}
?>
--EXPECT--
Pass:  => ''
Pass: foo => 'foo'
Pass: "foo => '"foo'
Pass: 'foo => "'foo"
Pass: 'foo"bar => concat("'foo",'"bar')
Pass: 'foo"bar"baz => concat("'foo",'"bar"baz')
Pass: 'foo" => concat("'foo",'"')
Pass: 'foo"" => concat("'foo",'""')
Pass: "'foo => concat('"',"'foo")
Pass: ""'foo => concat('""',"'foo")
Pass: 'foo""bar => concat("'foo",'""bar')